{} Lambda
Defines user function
Syntax: {<...>}
; {[x〔; ..〕] 〔<...>〕}
Function declaration consists of two phases - defining lambda expression and binding it to some name. Lambdas include one or several expressions separated by semicolons.
Lambda arguments
Lambda arguments are defined using [ ]:
o).n.sum:{[arr] a:+/arr; a}
{[arr] a:+/arr; a}
o)ff:{[a;b] */a+!1+b-a}
{[a;b] */a+!1+b-a}
o)
Lambda arguments can be defined implicitly via using pre-defined names x
, y
and z
.
o)f:{x+y+z}; f[1;2;3]
6
o)f:{x%y}; f[10;5]
2
o)
Lambda result
The last expression in the lambda is the result of that lambda. To return the result early, you can use the verb return
.
If the lambda is terminated by ;
, the result will be a generic null.
return <expr>; // <comment>
}
Locals
Local variables are defined implicitly via assignment to bindings inside lambdas that are not defined yet.
o)f:{local1:x; local2:y; local1+local2}
{local1:x; local2:y; local1+local2}
o)f[10;20]
30
o)
o).n.sum:{a:+/x; a}
{a:+/x; a}
o).n.sum [!10]
45
o)
y
and z
. In this case the lambda expects more than 1 argument.o)fxz: {z:2; x*z};
o)fxz[3] //projection
{{z:2; x*z}[3;;]}
o)fxz: {[x] z:2; x*z};
o)fxz[3]
6
o)
o)a:42;
o){a:2; show a; {show a; {show a}[ ] }[ ] }[ ];
2
2
42
Using ::
you can create/change variables in non-local lambdas scope.
o)(a;b):42 24;
o){a:2; b::3; show a,b; {show a,b; {show a,b}[ ] }[ ] }[ ];
2 3
2 3
42 3
o)
::
must also be used if you want to change the vаlue of a local variable using this local variable. o)(a;b):42 24;
o){a:2; b::3; show a,b; {a::a*a; show a,b; {show a,b}[ ] }[ ] }[ ];
2 3
4 3
42 3
o)
o)a:42;
o){ show a; }[]
42
o){ show a; a:2; show a }[]
0N0
2
o){ show a; a:2; show a; {show a; a:3; show a;}[]; show a; }[]
0N0
2
0N0
3
2
o)a
42
o)
Projection
When lambda or verb expects 2 or more arguments but gets less - the result is a lambda projection on the arguments provided.
o)add2: +[2;]
{+[2;]}
o)add2 10
12
o)f: {[a;b;c] a*a+b-c};
o)g: f[3;;4]
{{[a;b;c] a*a+b-c}[3;;4]}
o)g 5
10
o)
Sometimes the projections help to indicate the specific arrity of the verb.
o)r: reagent[`async];
o)r [10]
o)// get r - monad
o)get r
10
o)//to get from chennel without lock use get[100;r] - dyad
o)//to catch error use trap
o)//the next trap uses the default monadic get, and we have an "invalid type" error
o).[get;(100;r);{x`message}]
"invalid type: [`s`long]"
o)//the next trap uses the projection of dyadic get, and we catch an correct "timeout" error
o).[get[;];(100;r);{x`message}]
"timeout elapsed"
o)
Function/lambda application
Insert arguments in [ ] after the function name or omit brackets if there is only one argument.
o).n.sum:{a:+/x; a}
{a:+/x; a}
o)arr:!3
0 1 2
o).n.sum arr
3
o).n.sum[arr]
3
You can also apply arguments to lambdas without binding the latter to a name:
o){x*2}1
2
Recursive lambdas
o
binding is special in lambdas body. It defines reference to enclosing lambda itself.Thus, it allows creating recursive lambdas:
o)fibo:{[x] $[x<2;x;o[x-1]+o[x-2]]}; fibo[6]
8
... fibonacci with memoization:
o)d:0 1!0 1; fib: {$[d[x]=0N;d[x]:o[x-2]+o[x-1];()]; d[x]};
o)fib[6]
8
o)
However, pay attention to clashes with locals/arguments:
o){[o] o:1; o}[1]
1
o){[o] .[`o;();+;1]; o}[1]
2
o){[o] o+:1; o}[1]
2
Closures
Closures are another kind of functions - they capture parent local variables. They can be used everywhеre instead of simple functions:
o)parent: { upval: x; {upval + x} }; closure: parent[2]; closure[3]
5
The fibonacci example given above can be rewritten to avoid creating global state like:
o)fibo: { d:0 1!0 1; { $[d[x]=0N;d[x]:o[x-2]+o[x-1];()]; d[x] }}[];
o)fibo[6]
8
o)