The Case iterator
DRAFT
Perhaps the least-known map iterator is Case '
.
Functions derived by Case iterate through their arguments in a special case of scattered indexing.
Case takes a non-negative integor vector i
as argument and derives a function of rank max[i]+1
.
q)i:3 0 1
q)df:i' /derived function
Above, derived function df
has rank 4.
Each argument of df
must conform to i
; that is, be a list of three items or an atom.
List and double-flip the arguments to visualise this.
q)df["abc";99;("quick";"brown";"fox");42 43 44]
42
"b"
99
The result of df
has the same length as i
and consists of successive picks from the argument lists.
Specifically, for r:df . args
r[j] <==> args . (i j;j)
So, above, r
is the first, second and third items of (respectively) the fourth, first and second arguments.
What use is this?
Vector Condtional
Case is a more general form of the Vector Conditional ?
.
Recall that Vector Conditional is a ternary with three conforming arguments, say x
, y
and z
.
The result is y
with items replaced from z
wherever boolean x
is false.
q)?[01b;2#`true;2#`false]
`false`true
q)?[0b;`true;`false]
`false
q)?[1b;2#`true;2#`false]
`true`true`
The left (first) argument of Vector Conditional must be a boolean atom or vector. The argument of Case must be an integer vector.
q)0 1'[2#`true;2#`false]
`true`false
Cond
Case also resembles Cond. Cond is sometimes called the “ternary conditional”, which is slightly misleading, because Cond is not a function. Even though it returns a result, it is a control structure; so it is not applied to an argument list, but followed by an expression list. And this matters because all the items of an argument list are evaluated, but only the first item of the expression list is sure to be evaluated.
q)$[1b;2#`true;2#`false]
`true`true
Above, only 1b
and 2#`true
were evaluated, not 2#`false
.
The first expression must be an atom but need not be boolean; any integer-based type will do.
q)$["c"$0;2#`true;2#`false]
`false`false
Case statements
The name of Case suggests a resemblance to case statements in some other programming languages: a control structure that maps values of a variable to lists of expressions to evaluate.
switch(foo) {
case 'abc':
tom = 42;
dick = 53;
harry = 7;
break;
case 'def':
tom = 17;
dick = 99;
harry = 4;
break;
default:
tom = 27;
dick = 19;
harry = 44;
}
In JavaScript the code lines for each case are quite arbitrary. But where, as above, the code blocks simply assign different values to the same names in each case, q can map succinctly:
(tom;dick;harry):(42 53 7;17 99 4;27 19 44) ("abc";"def")?foo
And if only a single variable is being assigned
switch(foo) {
case 'abc':
tom = 42;
break;
case 'def':
tom = 17;
break;
default:
tom = 27;
}
Then q is even more succinct.
tom:42 17 27 ("abc";"def")?foo
And if foo
is a list, then so too is tom
.
The Case iterator in q corresponds to a case statement inside a loop in another language, in which the values assigned in each case vary with each iteration of the loop.
Not a common construct!
Scattered indexing
Another way to visualise the operation of Case is as a special case of scattered indexing.
The arguments of the derived function must conform to the left argument i
of Case.
They therefore correspond to the rows of a matrix M
with count i
columns.
In the derived function i'
each i[j]
specifies M[i j;j]
.
Exercises
FIXME: Write answers
-
Compare Case, Vector Conditional and Cond: describe how they are alike and how they differ.
-
Write a ternary lambda
case
such thatcase[x;y;z]
returnsx'[y;z]
for suitable arguments. -
Adapt
case
so that it also handles the Vector Conditional; i.e. for booleanx
,case[x;y;z]
returns?[x;y;z]
.