Compose out spaces
Agenda
- write a simple lambda to remove multiple embedded blanks from a string
- rewrite the lambda as a composition.
Firing blanks
We start with a good example of how vector solutions differ from looping solutions.
A classic looping solution would iterate through the characters, keeping a blank only if the previous character was not blank.
A vector solution starts by flagging the blanks.
q)str:"The quick brown fox jumps."
q)1 null\str
T h e q u i c k b r o w n f o x j u m p s .
0 0 0 1 0 0 0 0 0 1 1 1 1 0 0 0 0 0 1 0 0 0 1 1 0 0 0 0 0 0
null
returns a vector the same length as its argument.
It is often helpful to compare corresponding argument and result items.
(str;null str)
would do that.
The ‘Zen monks’ pattern1 1 f\
is slightly easier to type.
(But make friends with the monks: they will help us later.)
We want to flag every position which is not a blank preceded by a blank,
q)(str;null str;not prior[and] null str)
T h e q u i c k b r o w n f o x j u m p s .
0 0 0 1 0 0 0 0 0 1 1 1 1 0 0 0 0 0 1 0 0 0 1 1 0 0 0 0 0 0
1 1 1 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1
q){x where not prior[and] null x}str
"The quick brown fox jumps."
The condition of unaries
All art constantly aspires towards the condition of music.
— Walter Pater, “The School of Georgione” (1888)Walter Horatio Pater was an English essayist, art and literary critic, and fiction writer, regarded as one of the great stylists. — Wikipedia
Ken Iverson (the mathematician who devised the notation from which q’s ancestor languages APL and J derived) saw even binary operations as sequences of unaries.
Thus
2+3-4*5%6
could be read as
2+ 3- 4* 5% 6
- Taylor’s Aphorism:
-
All q expressions constantly aspire to be sequences of unaries.
Get composed
There is nothing wrong with the eminently legible lambda, but we can improve our grasp of q syntax by rewriting it as a composition.
First of all we note that where not prior[and] null
is a sequence of unaries, so
where not prior[and] null ::
where not prior[and] " "=
f
.
f:where not prior[and] " "=
str f str
.
Consider some other ways to write that.
str f str
str@f str
@[str;f str]
.[@;(str;f str)]
.[@] (str;f str)
.[@] 1 f\str
To be precise, can 1 \f
be written as a unary and so composed?
It’s slightly tricky.
Recall that all derived functions are variadic, so in x f\
the x
could be its left argument or the next unary along; i.e. x@f\
.
Composing 1 f\
requires us to disambiguate.
We do that by writing f\[1;]
making it clear that variadic f\
is being projected as a binary onto left argument 1
.
q)g:.[@] f\[1;] ::
q)g str
"The quick brown fox jumps."
f
q)rmeb:.[@] (where not prior[and]" "=)\[1;] ::
q)rmeb str
"The quick brown fox jumps."
Exercises
-
Good q style prefers keywords
prior
to the Each Prior iterator':
andand
to&
.Never mind: rewrite the composition using
&
and':
. Enlightenment awaits!Answer
Recall that iterators use postfix syntax, so the following are equivalent.
The parentheses make a function atom of the variadic derived function And Each Prior (prior[and] (and) prior (&':)
&':
).A function atom has noun syntax: it can be applied with prefix syntax – i.e. as a unary – so
(&':)
can be composed as a unary.q)rmeb:.[@] (where not (&':) " "=)\[1;] :: / remove multiple embedded blanks q)rmeb str "The quick brown fox jumps."
-
The posed problem is to remove duplicate embedded spaces. The solution given also removes duplicate trailing spaces, and all leading spaces.
Notice the difference below between
b&prev b
and&':[b]
.In the lambda, useq)(b;prev b;b&prev b;&':[b]) 1100010000011110000010001100000011b 0110001000001111000001000110000001b 0100000000001110000000000100000001b 1100000000001110000000000100000001b
and
andprev
instead ofprior[and]
to make a lambda that collapses all duplicate spaces.Answer
q)str:" The quick brown fox jumps. " q){x where not n and prev n:null x}str " The quick brown fox jumps. "
-
Write the previous lambda as a composition.
Answer
q)cws:.[@] (where not .[&] prev\[1;] " "=)\[1;] :: / collapse white space q)cws str " The quick brown fox jumps. "