Article Series

This article series discuss more than 30 different programming languages. Please read overview before you read any of the details.

Playing with Records Related Articles.

Preface

Goal: Finishing the task.

We can solve, these tasks below, by using both imperative approach, such as for loop. And functional approach, such as recursive subroutine. Later we can go beyond that using concurrency.

  • Extract, Flatten, Unique

4: Imperative Approach

We are going to use λ, just like any cool scheme kids.

Extract and Flatten

We can simply utilize filter-map and gain shorter code.

#lang racket
(require "my-songs.rkt")

(define tagss
  (filter-map (λ (song)
    (and (hash-has-key? song "tags")
    (hash-ref song "tags"))
  ) songs))

(displayln (string-join (flatten tagss) ":"))

Racket: For Each

The result will be as below.

❯ racket 05-flatten-a.rkt
60s:jazz:60s:rock:70s:rock:70s:pop

The Helper Extract Module

Just copy paste to a new file

#lang racket
(provide extract-songs)

(define (extract-songs songs)
  (filter-map (λ (song)
    (and (hash-has-key? song "tags")
    (hash-ref song "tags"))
  ) songs))

Racket: The Helper Extract Module

Using Extract Module

Now we can have a shorter code as shown below.

#lang racket
(require "my-songs.rkt")
(require "my-extract.rkt")

(displayln (string-join
  (flatten (extract-songs songs)) ":"))

With the result exactly the same as previous code.

Racket: Using Extract Module

Extracting Unique Tags

We can use build in remove-duplicates subroutine.

#lang racket
(require "my-songs.rkt")
(require "my-extract.rkt")

(displayln (string-join
  (remove-duplicates
  (flatten
  (extract-songs songs))) ":"))

With the result as shown below.

❯ racket 06-unique.rkt
60s:jazz:rock:70s:pop

Racket: Extracting Records


5: Functional Approach in Solving Unique

A custom example, that you can rewrite.

An Algorithm Case

Why reinvent the wheel?

The same reason as any other articles.

x:xs Pattern Matching

The same with any other articles as well.

Remove Element from List

Racket has this built in remove.

(define tags (list
  "rock" "jazz" "rock" "pop" "pop"))

(displayln (string-join
  (remove* (list "rock") tags) ":"))

With the result as below list:

❯ racket 11-exclude.rkt
jazz:pop:pop

Racket: Exclude Function

You don’t need this exclude function. It is just a demonstration of writing a function, along with its argument. We are going to understand the basic, before writing recursive unique function later.

Unique Subroutine

Official Documentation has this subroutine in this reference:

Recursive Unique Value

Our recursive function require this variable:

  • Head and Tail
  • Excluded list, based on head and tail above.

After head:tail, we can go further getting unique value.

#lang racket

(define tags (list
  "rock" "jazz" "rock" "pop" "pop"))

(define (unique tags)
  (if (<= (length tags) 1)
    tags
    (let ( 
        (head (first tags))
        (tail (rest tags)) )
      (append (list head)
        (unique (remove* (list head) tail)))
)))

(displayln (string-join
  (unique tags) ":"))

We can examine the result carefully:

❯ racket 12-unique-a.rkt
rock:jazz:pop

Racket: Preparing Unique Function: Recursive Value

We already have our unique value. We need to clean up the subroutine.

Recursive Unique Module

Since we are going to reuse the unique approach in other script. It is better to bundle the script in its own racket module.

#lang racket
(provide unique-tags)

(define (unique-tags tags)
  (if (<= (length tags) 1)
    tags
    (let ( 
        (head (first tags))
        (tail (rest tags)) )
      (append (list head)
        (unique-tags (remove* (list head) tail)))
)))

The return values is a list.

(append (list head)
  (unique-tags
    (remove* (list head) tail)))

Racket: The Unique Function: Module

Using Unique Module

There is nothing to say in this code below. Just apply unique function to our song records.

#lang racket
(require "my-songs.rkt")
(require "my-extract.rkt")
(require "my-unique.rkt")

(displayln (string-join
  (unique-tags
  (flatten
  (extract-songs songs))) ":"))

With the result as below list:

❯ racket 12-unique-b.rkt
60s:jazz:rock:70s:pop

Racket: Using Recursive Unique Function

We are done solving our challenge.

Style Guide

Forgive my indentation.

For those who care a lot about style, you can read this material below:


6: Concurrency with Channel

Racket support channel. We can rewrite previous process through channel, using sender and receiver.

Reference

Racket has this powerful go channel like.

For my learning purpose, I just need the simpler version.

Simple Example

From example above I reduce the code to something as below:

(define chan (make-channel))

(define (receiver)
  (define item (channel-get chan))
  (if (null? item)
      (display (format "Thread done\n"))
      (begin
        (display (format
          "Thread processed ~a\n" item))
        (receiver))))

(define my-thread
  (thread (λ () (receiver))))
(for ([item '(A B C D E F G H)])
  (channel-put chan item))
(channel-put chan null)
(thread-wait my-thread)

With the result as below list:

❯ racket 15-channel.rkt
Thread processed A
Thread processed B
Thread processed C
Thread processed D
Thread processed E
Thread processed F
Thread processed G
Thread processed H
Thread done

Racket: Channel

The Skeleton

We should be ready for modre complex demo. This is only consist of this one file.

#lang racket

(define (receiver chan) (...))

(define (sender chan songs) (...))

;; program entry point
...
  1. Producer: (receiver chan),
  2. Consumer: (sender chan songs),
  3. Program entry point.

Producer and Consumer

We should prepare two functions:

  1. One for Sender that put into channel,
  2. And the other one for Receiver that get from channel.

Sender that put into channel,.

(define (sender chan songs)
  (begin
    (for-each (λ (song)
      (when
        (hash-has-key? song "tags")
        (for-each (λ (tag)
          (channel-put chan tag))
          (hash-ref song "tags")))
      ) songs)
    (channel-put chan null)))

Racket: Concurreny with Channel: Sender

Receiver that get from channel.

(define (receiver chan)
  (define tags (list))
  (let loop ()
    (define item (channel-get chan))
    (if (null? item)
        tags
        (begin
          (set! tags
            (append tags (list item)))
          (loop)))))

Racket: Concurreny with Channel: Sender

Running Both Routine

Pretty short entry point right?

No need to bundle into function.

;; program entry point
(require "my-songs.rkt")

(define my-chan (make-channel))

(define my-thread (thread
  (λ () (displayln (string-join
    (remove-duplicates
    (flatten
      (receiver my-chan))) ":")
))))
(sender my-chan songs)
(thread-wait my-thread

Racket: Concurreny with Channel: Main

Result

The result will be as below list:

❯ racket 16-channel.rkt
60s:jazz:rock:70s:pop

Summary

For convenience, I put all the code in just one screenshot figure as below:

Racket: Solving Unique Song: Wide Summary

This is the end of our racket journey in this article. We shall meet again in other article.


What is Next 🤔?

Consider continue reading [ Pascal - Playing with Records - Part One ].