by Arne Sommer

# Count Twice with Raku

[301] Published 4. August 2024.

This is my response to The Weekly Challenge #280.

## Challenge #280.1: Twice Appearance

You are given a string, `\$str`, containing lowercase English letters only.

Write a script to print the first letter that appears twice.

Example 1: ```Input: \$str = "acbddbca" Output: "d" ``` Example 2: ```Input: \$str = "abccd" Output: "c" ``` Example 3: ```Input: \$str = "abcdabbb" Output: "a" ```

Brute force, i.e. a loop, is probably the best approach:

File: twice-appearance ```#! /usr/bin/env raku unit sub MAIN (\$str where \$str ~~ /^<[a..z]>+\$/); # [1] my %seen; # [2] for \$str.comb -> \$letter # [3] { if %seen{\$letter} # [4] { say \$letter; # [4a] last; # [4b] } %seen{\$letter} = True; # [5] } ```

[1] Ensure that the string contains lowercase English letters only, one or more.

[2] Already encountered letters will be stored here.

[3] Iterate over each letter in the word.

[4] Have we seen the current letter before? If so, print it [4a] and exit [4b].

[5] Mark the current letter as seen.

Running it:

```\$ ./twice-appearance acbddbca d \$ ./twice-appearance abccd c \$ ./twice-appearance abcdabbb a ```

Looking good.

No verbose mode, but here are some additional examples:

```\$ ./twice-appearance 12 Usage: ./twice-appearance [-v|--verbose[=Any]] <str> \$ ./twice-appearance abcdefg ```

We can make it shorter, almost a one liner, with `map` instead of the explicit loop:

File: twice-appearance-map ```#! /usr/bin/env raku unit sub MAIN (\$str where \$str ~~ /^<[a..z]>+\$/); \$str.comb.map({ state %seen; (say \$_; last) if %seen{\$_}++ }); ```

Note the `state` variable declared inside the `map`.

See docs.raku.org/syntax/state for more information about the variable declarator `state`.

See docs.raku.org/routine/map for more information about `map`.

Running this program gives the expected result:

```\$ ./twice-appearance-map acbddbca d \$ ./twice-appearance-map abccd c \$ ./twice-appearance-map abcdabbb a ```

## Challenge #280.2:

You are given a string, `\$str`, where every two consecutive vertical bars are grouped into a `pair`.

Write a script to return the number of asterisks, `*`, excluding any between each pair of vertical bars.

Example 1: ```Input: \$str = "p|*e*rl|w**e|*ekly|" Ouput: 2 The characters we are looking here are "p" and "w**e". ``` Example 2: ```Input: \$str = "perl" Ouput: 0 ``` Example 3: ```Input: \$str = "th|ewe|e**|k|l***ych|alleng|e" Ouput: 5 The characters we are looking here are "th", "e**", "l***ych" and "e". ```

If we split a string on, let us say a comma, we get the parts without the commas. This is indeed what happens on parsing a CSV (comma separated values) file.

An example:

```> "This,does,not,make,sense".split(",").raku ("This", "does", "not", "make", "sense").Seq ```

We can split on regular expressions instead of single characters (or strings).

```> "This,does not;make,any sense".split(/<[\ ,;.]>+/).raku ("This", "does", "not", "make", "any", "sense").Seq ```

So it should not come as a big surprise that we can use a pattern matching the pairs of vertical bars, and split on that.

File: count-asterisks ```#! /usr/bin/env raku unit sub MAIN (\$str, :v(:\$verbose)); # [1] my @parts = \$str.split(/\|.*?\|/); # [2] say ": Parts: { @parts.raku }" if \$verbose; @parts.join.comb.grep( * eq '*').elems.say; # [3] ```

[1] No restrictions on the string this time.

[2] Split on pairs of vertical bars (with some non-«vertical bars» between them).

[3] Join the resulting array, split it into individual characters, get rid of non-asterisks, and count the result (i.e. the asterisks).

Running it:

```\$ ./count-asterisks "p|*e*rl|w**e|*ekly|" 2 \$ ./count-asterisks "perl" 0 \$ ./count-asterisks "th|ewe|e**|k|l***ych|alleng|e" 5 ```

Looking good.

With verbose mode:

```\$ ./count-asterisks -v "p|*e*rl|w**e|*ekly|" : Parts: ["p", "w**e", ""] 2 \$ ./count-asterisks -v "perl" : Parts: ["perl"] 0 \$ ./count-asterisks -v "th|ewe|e**|k|l***ych|alleng|e" : Parts: ["th", "e**", "l***ych", "e"] 5 ```

And that's it.