Preface
Goal: Finishing the task, using imperative approach.
We can solve, these tasks below,
by using imperative approach,
such as for
loop.
- Extract, Flatten, Unique
And later we can build,
using functional appproach,
utilizing map
and filter
.
4: Extract
We are going to use λ
,
just like any cool scheme kids.
For Each
We can utilize for-each
to get side effect.
(for-each (λ (song)
(if
(assoc "tags" song)
(begin
(display (assoc-ref song "tags"))
(newline)
)
)
) songs)
Guile is loose, so you can use map
instead of for-each
.
The result will be as below.
❯ guile 05-extract-a.scm
(60s jazz)
(60s rock)
(70s rock)
(70s pop)
Map and Filter
Gather all the result.
We can take each result into just one list using map
.
And print each result using map
.
(define tagss
(map
(λ (song) (assoc-ref song "tags"))
(filter
(λ (song) (assoc "tags" song))
songs))
)
(map (λ (tags)
(format #t "~a\n" tags)
) tagss)
The result is, exactly the same as previous code.
Filter Map
Even simpler shortcut
We can also use filter-map
.
But we need additional cdr
extraction.
(define tagss
(filter-map
(λ (song) (assoc "tags" song))
songs)
)
(map (λ (tags)
(format #t "~a\n" (cdr tags))
) tagss)
The result is, exactly the same as previous code.
5: Flatten
Wehn it comes to flatten,
we can utilize built in concatenate
subroutine,
or using imperative approach,
oe even functional approach later.
I make a few example, not just for practice purpose. But also explore different possibility, until I find most suitable for my needs.
Concatenate
This concatenate
is th easiest one.
(define tagss
(map
(λ (song) (cdr (assoc "tags" song)))
(filter
(λ (song) (assoc "tags" song))
songs))
)
(display tagss)
(newline)
(define tags-flatten
(concatenate tagss))
(display (string-join
tags-flatten ":"))
(newline)
The result will be as below.
❯ guile 06-flatten-a.scm
((60s jazz) (60s rock) (70s rock) (70s pop))
60s:jazz:60s:rock:70s:rock:70s:pop
Append List Using Set!
There is another approach however, such as growing list from empty list until the last recorded.
(define tags-flatten (list))
(for-each (λ (song)
(if
(assoc "tags" song)
(set! tags-flatten (append tags-flatten
(assoc-ref song "tags")))
)
) songs)
(display (string-join
tags-flatten ":"))
(newline)
The result will be as below.
❯ guile 06-flatten-b.scm
60s:jazz:60s:rock:70s:rock:70s:pop
Notice the set!
keyword, instead of define
.
Going Further with Let
This let
construct is rather confusing for beginner,
so I provide simple example, before we utilize more let
later.
(define tags-flatten (list))
(for-each (λ (song)
(if
(assoc "tags" song)
(let
((tagss (assoc-ref song "tags")))
(set! tags-flatten
(append tags-flatten tagss))
)
)
) songs)
(display (string-join
tags-flatten ":"))
(newline)
The result is, exactly the same as previous code.
Extract The Tags List
Instead of append array directly, we can append for each tags, so we have true imperative approach.
(define tags-flatten (list))
(for-each (λ (song)
(if
(assoc "tags" song)
(let
((song-tags (assoc-ref song "tags")))
(for-each (λ (tag)
(set! tags-flatten
(append tags-flatten (list tag)))
) song-tags)
)
)
) songs)
(display (string-join
tags-flatten ":"))
(newline)
The result is, exactly the same as previous code.
Why would I want this complex approach? Because this is suitable to find unique tags, as shown later.
6: Flatten Module
It is actually just extracting module, with contatenation
I have to decide which approach to extract song structure. And I choose the imperative module.
Bundle to Subroutine
Extract and Concatenate
We can utilize map
then filter
to gather a new list
.
(define
(flatten my-songs)
(let
( (tagss
(map
(λ (song) (cdr (assoc "tags" song)))
(filter
(λ (song) (assoc "tags" song))
my-songs))
) )
(concatenate tagss)
))
(display (string-join (flatten songs) ":"))
(newline)
The result will be as below.
❯ guile 07-flatten-e.scm
60s:jazz:60s:rock:70s:rock:70s:pop
It is still, exactly the same as previous code.
The Helper Flatten Module
Just copy paste to a new file
(define-module (my-helper-flatten))
(use-modules (srfi srfi-1))
(define-public (flatten my-songs)
(let ((tagss
(map (λ (song) (cdr (assoc "tags" song)))
(filter (λ (song) (assoc "tags" song))
my-songs))
))
(concatenate tagss)))
Using Flatten Module
Now we can have a shorter code as shown below.
(add-to-load-path
(dirname (current-filename)))
(use-modules (my-songs))
(use-modules (my-helper-flatten))
(display (string-join (flatten songs) ":"))
(newline)
With the result exactly the same as previous code.
7: Unique
We can go further with previous code.
Extracting Unique Tags
Filtering list, and append to list
Consider adding unique filter with this long code:
(define tags-unique (list))
(for-each (λ (song)
(if (assoc "tags" song)
(let ((song-tags (assoc-ref song "tags")))
(for-each (λ (tag)
(unless (member tag tags-unique)
(set! tags-unique
(append tags-unique
(list tag))))
) song-tags))
)) songs)
(display (string-join tags-unique ":"))
(newline)
The list filtered by using just this code
(unless (member tag tags-unique) (...))
With the result as shown below.
❯ guile 08-unique-a.scm
60s:jazz:rock:70s:pop
Using Flatten Module
We can also have compacted version of above code,
using previous flatten
module,
and filter the unique
tags later.
(add-to-load-path
(dirname (current-filename)))
(use-modules (my-songs))
(use-modules (my-helper-flatten))
(define tags-flatten (flatten songs))
(define tags-unique (list))
(for-each (λ (tag)
(unless
(member tag tags-unique)
(set! tags-unique
(append tags-unique (list tag)))
)
) tags-flatten)
(display (string-join tags-unique ":"))
(newline)
With exactly the same result.
What is Next 🤔?
We have alternative way to do get unique list using functional approach.
Consider continue reading [ Guile - Playing with Records - Part Three ].