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.

Where to Discuss?

Local Group

Preface

Goal: Continue Part Two


6: 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.

Exclude Function

The exclude function is just a filter with below details:

#!/usr/bin/raku

my @tags = ('rock',
  'jazz', 'rock', 'pop', 'pop');

sub exclude($value, @tags) {
  @tags.grep(none /$value/);
}

join(':', exclude('rock', @tags)).say;

With the result as below array:

❯ ./21-exclude.raku
jazz:pop:pop

Raku: Exclude Function

Recursive Unique Function

With exclude function above, we can build unique function recursively.

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

unit module MyHelperUnique;

sub unique(@tags) is export {
  if (@tags <= 1) {
    @tags;
  } else {
    # array[$head:@tail]
    my ($head, @tail) = @tags;

    # recursive unique
    my @xcld = @tail.grep(none /$head/);
    my @uniq = unique(@xcld);

    # returned array
    @uniq.unshift: $head;
  }
}

The return values is in array.

Raku: The Unique Function

Using Unique Module

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

use MyHelperUnique;

my @tags = ('rock',
  'jazz', 'rock', 'pop', 'pop');

say join(':', unique(@tags));

With the result as below array:

❯ ./22-unique-a.raku
rock:jazz:pop

Raku: Using Recursive Unique Function

Applying Unique to Songs Records

Now we can apply the method to our unique song challenge.

#!/usr/bin/raku
use lib $*PROGRAM.dirname;
use MySongs;
use MyHelperFlatten;
use MyHelperUnique;

say join(':', unique(flatten(@songs)));

With the same result as below

Raku: Solving Unique Song

❯ ./22-unique-b.raku
60s:jazz:rock:70s:pop

7: Generator

Yield

With procedural approach, we can also achieve the same thing.

We are going to consider this approach for concurrency topic.

Gather Take

Consider this function.

sub produce(@songs) {
  gather for (@songs) { 
    @( %$_<tags> ).Slip.map: *.take
    if ('tags'  %$_.keys);
}}

Entry Point

Then we can call later:

# main: entry point
use lib $*PROGRAM.dirname;
use MySongs;

join(":", produce(@songs).unique).say;

Raku: Generator: Gather Take (Yield)

With the result of

❯ ./25-gather.raku
60s:jazz:rock:70s:pop

8: Concurrency with Channel

Raku support Actor Model pattern. We can rewrite previous process through channel, using sender and receiver.

Reference

The Simple Skeleton

We should be ready for the simple demo. This is only consist of one short file.

#!/usr/bin/raku
my $c = Channel.new;

my @tags = ('rock',
  'jazz', 'rock', 'pop', 'pop');

start { ... }

loop { ... }
  1. Producer: start { ... }
  2. Consumer: loop { ... }

Producer and Consumer

We should prepare two functions:

  1. One for Producer that send to channel,
  2. And the other one for Consumer that receive from channel.

Producer that send to channel.

start {
  @tags.map: -> $tag { $c.send: $tag; }
  $c.send: Nil;
  $c.close;
}

Raku: Concurreny with Channel: Producer

Consumer that get from channel.

my $item;
loop {
  $item = $c.receive;
  if ($item) {
    $item.say;
  } else {
    last;
  }
}

Raku: Concurreny with Channel: Consumer

Running Both Routine

The result will be as below list:

❯ ./31-channel.raku
rock
jazz
rock
pop
pop

Real Skeleton

How about the real demo. This is also consist only one short file.

#!/usr/bin/raku

sub produce($c, @songs) { ... }

sub consume($c) { ... }

# main: entry point
...
start { produce($chan, @songs); }
consume($chan)
  1. Sender: sub produce { ... }
  2. Receiver: sub consume { ... }
  3. Program entry point.

Sender

Producer that send to channel.

Consider bundle into a function.

sub produce($c, @songs) {
  for (@songs) { 
    if ('tags'  %$_.keys) {
      @( %$_<tags> ).Slip
        .map: -> $tag { $c.send: $tag; }
      next; # dummy
    }
  }

  $c.send: Nil;
  $c.close;
}

Raku: Concurreny with Channel: Producer (Sender)

Receiver

Consumer that get from channel.

Consider bundle into a function.

sub consume($c) {
  my $item;
  my @tags = ();

  loop {
    $item = $c.receive;
    if ($item) {
      @tags.push: $item;
    } else {
      join(":", @tags.unique).say;
      last;
    }
  }
}

Raku: Concurreny with Channel: Consumer (Receiver)

Running Both Routine

Pretty short entry point right?

No need to bundle into function.

# main: entry point
use lib $*PROGRAM.dirname;
use MySongs;

my $chan = Channel.new;

start { produce($chan, @songs); }
consume($chan)

Raku: Concurreny with Channel: Running Both Process

Result

The result will be as below list:

❯ ./32-channel.raku
60s:jazz:rock:70s:pop

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


What is Next 🤔?

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