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, 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.

Guile: 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)

Guile: Map and Filter

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)

Guile: Filter Map Shortcut

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)

Guile: Concatenate

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)

Guile: Append List Using Set!

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)

Guile: Going Further with Let

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.

Guile: Extract The Tags List

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)

Guile: Bundle to Subroutine

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)))

Guile: The Helper Flatten Module

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.

Guile: Using Flatten Module


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

Guile: Extracting Records

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 ].