Skip to content

The Name Game

Make substitutions in a string or list of strings

Two solutions, using respectively, ssr and Amend At. Both use projections.

Both use the Do iterator to both apply and not-apply a function. One uses the Over iterator to consolidate successive operations.

Both solutions have five code lines, no loops, no control structures.

Write a function that takes a name and returns the lyrics to the Shirley Ellis song ”The Name Game”.

from Rosetta Code

We shall try two approaches to this problem and see how they compare. First, string search and replacement. In the second we treat the songs as a list of strings and use Amend At to customize it.

String search and replace

The core of this is pretty simple. Perhaps no more than inserting a name into a template.

q)s:"Name, Name, bo-bName\nBanana-fana-fo-fName\nFee-fimo-mName\nName!\n\n"
q)1 ssr[s;"Name";]"Stephen";
Stephen, Stephen, bo-bStephen
Banana-fana-fo-fStephen
Fee-fimo-mStephen
Stephen!

Not quite: have to drop the first letter of the name. Unless it’s a vowel. In fact, all the leading consonants. Is Y a vowel –Yvette, Yvonne? Let’s suppose so.

q)V:raze 1 upper\"aeiouy"  / vowels
q)s2:"$1, $1, bo-b$2\nBanana-fana-fo-f$2\nFee-fimo-m$2\n$1!\n\n"
q)1 {ssr/[s2;("$1";"$2");(x;((x in V)?1b)_x)]}"Stephen";
Stephen, Stephen, bo-bephen
Banana-fana-fo-fephen
Fee-fimo-mephen
Stephen!
"$1" and "$2" have no special significance here

Although they resemble tokens in Posix regular expression syntax, here they are just substrings that are easy to spot.

Note the use of Do \ to get the upper- and lower-case vowels.

Here we have used the Over iterator to make successive substititions. Breaking that down, it is equivalent to

q)1 ssr[;"$2";"ephen"] ssr[;"$1";"Stephen"] s2;
Stephen, Stephen, bo-bephen
Banana-fana-fo-fephen
Fee-fimo-mephen
Stephen!

And with a leading vowel?

q)1 {ssr/[s2;("$1";"$2");(x;((x in V)?1b)_x)]}"Anne";
Anne, Anne, bo-bAnne
Banana-fana-fo-fAnne
Fee-fimo-mAnne
Anne!

That A should be in lower case.

q)1 {ssr/[s2;("$1";"$2");(x;lower((x in V)?1b)_x)]}"Anne";
Anne, Anne, bo-banne
Banana-fana-fo-fanne
Fee-fimo-manne
Anne!

But we have one more rule still to go. When the name begins with B, F, or M, the corresponding bo-b, fo-f, and mo-m loses its last letter. We could treat this as a possible third string replacement.

Lightbulb moment. We do not need to test the first letter to see if the third replacement is needed. If it is not, the replacement, e.g. so-s with so-, is harmless: a no-op.

If we define the third substition

s3:{1(-1_)\x,"o-",x}lower first Name

then our result is

ssr[;"$2";tn] ssr[;"$1";Name] ssr[s;;] . s3 

The replacements are all made in the last line:

  • xo- for xo-x for some letter x
  • Name for "$1"
  • tn for "$2"

Should the successive calls to ssr be refactored with Over?

They could be. The syntax would be ssr/[s;f;t], where f and t are the lists of from- and to-strings. But rather than construct those variables, let’s apply ssr/ to a 3-list of arguments. The syntax for that would be (ssr/).(s;f;t).

game_ssr:{[Name]
  V:raze 1 lower\"AEIOUY";                                       / vowels
  tn:lower((Name in V)?1b) _ Name;                               / truncate Name
  s3:{1(-1_)\x,"o-",x}lower first Name;                          / 3rd ssr
  s:"$1, $1, bo-b$2\nBanana-fana-fo-f$2\nFee-fimo-m$2\n$1!\n\n";
  (ssr/).(s;("$1";"$2";s3 0);(Name;tn;s3 1)) }
q)1 raze game_ssr each string`Stephen`Anne`Yvonne`Brenda;
Stephen, Stephen, bo-bephen
Banana-fana-fo-fephen
Fee-fimo-mephen
Stephen!

Anne, Anne, bo-banne
Banana-fana-fo-fanne
Fee-fimo-manne
Anne!

Yvonne, Yvonne, bo-byvonne
Banana-fana-fo-fyvonne
Fee-fimo-myvonne
Yvonne!

Brenda, Brenda, bo-enda
Banana-fana-fo-fenda
Fee-fimo-menda
Brenda!

Amending a list of strings

In this approach we treat the song as a list of strings and amend a template list. The template list:

q)show s:("bo-b";"Banana-fana fo-f";"Fee-fimo-m";"!";"")
"bo-b"
"Banana-fana fo-f"
"Fee-fimo-m"
"!"
""

Prefix lines 0 and 3 with the name.

pfx:Name,", ",Name,", " / prefix

Suffix each of the first three lines with the truncated name.

n:lower Name
v:"aeiouy"
sfx:((n in v)?1b)_ n / suffix

but first drop the last letter from lines of s

where n[0]=last each s

The ternary form of Amend At (syntax @[x;yz]) applies (unary) z to to each item of x y. So

@[s;where n[0]=last each s;-1_]

does that. Successive substitutions:

@[;0;pfx,] @[;3;Name,] @[;0 1 2;,[;sfx]] @[;where n[0]=last each s;-1_] s 

We could use the Over iterator to refactor the successive calls to Amend At as a call to @/. The refactored syntax would be @/[s;i;u] where i is a nested list of indexes and u is a list of unaries.

@/[; ((0;3;0 1 2;where n[0]=last each s)); (pfx,;Name,;,[;sfx];-1_)] s 

A step too far.

The syntax of the successive applications keeps the index-unaries as pairs. In the refactored line no improvement in legibility warrants the extra cognitive load of pairing unaries and indexes. Absent some other decisive factor such as evaluation time, the earlier version is better here.

But it is good to to be able to spot opportunities to refactor!

Putting it all together:

game_amend:{[Name]
  pfx:Name,", ",Name,", ";                                    / prefix
  n:lower Name;
  sfx:((n in "aeiouy")?1b)_n;                                 / suffix
  s:("bo-b";"Banana-fana fo-f";"Fee-fimo-m";"!";"");          / song template
  @[;0;pfx,] @[;3;Name,] @[;0 1 2;,[;sfx]]
    @[;where n[0]=last each s;-1_] s }

Exercise

@[;0 1 2;,[;sfx]] uses the ternary form of Amend At. Rewrite it using the quaternary form.

Answer

@[;0 1 2;,;3#enlist sfx]

In the ternary form @[x;y;z], x[y] becomes z each x[y].

In the quaternary form @[x;y;z;zz], x[y] becomes x[y] z'zz.

Bracket notation for derived functions may help here. Equivalent to the above, x[y] becomes

ternary      @[x;y;z]         z'[x y]
quaternary   @[x;y;z;zz]      z'[x y;zz]

In this case, s[0 1 2] becomes in the ternary

,[;sfx]each s[0 1 2]
and in the quaternary
s[0 1 2],'3#enlist sfx

Review

Both approaches found solutions of similar length and legibility.

Both used projections. Both culminated in a last line that successively applied similar operations, respectively ssr and Amend At.

Using the Do iterator to refactor the last line of game_ssr improved legibility; refactoring the last line of game_amend did not.

An earlier version of this article appeared on code.kx.com.