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) ":"))
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))
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.
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
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
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
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)))
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
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
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
...
- Producer:
(receiver chan)
, - Consumer:
(sender chan songs)
, - Program entry point.
Producer and Consumer
We should prepare two functions:
- One for
Sender
thatput
intochannel
, - And the other one for
Receiver
thatget
fromchannel
.
Sender
thatput
intochannel
,.
(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)))
Receiver
thatget
fromchannel
.
(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)))))
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
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:
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 ].