| Closure Guide for LPC |
| |
| Table of Contents |
| |
| 1 Indroduction, Overview and Efun-Closures |
| |
| 2 Lfun-, Inline and Lambda-Closures |
| 2.1 Lfun-Closures |
| 2.2 Inline-Closures |
| 2.3 Lambda-Closures |
| 2.3.1 Advantages of Lambda-Closures |
| 2.3.2 Free Variables in Lambda-Closure Constructs |
| 2.3.3 Special Efun-Closures and Operator-Closures for Lambdas |
| 2.4 Closures with Strange Names |
| 2.5 Operator-Closures |
| 2.6 Variable-Closures |
| |
| 3 Examples |
| 3.1 Lfun-Closure |
| 3.2 Lambda-Closure |
| |
| 1 Introduction, Overview and Efun-Closures |
| |
| A closure is a pointer to a function. That means that it is data like an |
| int or a string are. It may be assigned to a variable or given to anoth- |
| er function as argument. |
| |
| To create a closure that points to an efun like write() you can write |
| the name of the efun prepended with "hash-tick": #'. #'write is a clo- |
| sure that points to the efun write(). |
| |
| I very often put parentheses around such a closure-notation because |
| otherwise my editor gets confused by the hashmark: (#'write). This is |
| especially of interest within lambda-closures (see below). |
| |
| A closure can be evaluated (which means that the function it points to |
| is called) using the efuns funcall() or apply(), which also allow to |
| give arguments to the function. Example: |
| |
| funcall(#'write,"hello"); |
| |
| This will result in the same as write("hello"); alone. The string |
| "hello" is given as first (and only) argument to the function the clo- |
| sure #'write points to. |
| |
| The return value of the function the closure points to is returned by |
| the efun funcall() or apply(). (Since write() always returns 0 the re- |
| turn value of the example above will be 0.) |
| |
| What are closures good for? With closures you can make much more univer- |
| sally usable functions. A good example is the function filter(). |
| It gets an array and a closure as arguments. Then it calls the function |
| the closure points to for each element of the array: |
| |
| filter(({ "bla","foo","bar" }),#'write); |
| |
| This will call write("bla"), write("foo") and write("bar") in any order; |
| the order is undefined. |
| (In the current implementation the given closure is evaluated for all |
| elements from the first to the last, so the output will be "blafoobar".) |
| |
| Furthermore the efun filter() examines the return value of each |
| call of the function the closure points to (the return value of the |
| write()s). If the value is true (not 0) then this element is put into |
| another array which filter() builds up. If the return value is |
| false (== 0) then this element is _not_ put into this array. When all |
| calls are done the slowly built up array is returned. Thus, |
| filter() filters from the given array all elements that the given |
| closure evaluates "true" for and returns an array of those. (The array |
| given to filter() itself is _not_ changed!) |
| |
| A more sensical example for filterwould be this: |
| |
| x = filter(users(),#'query_is_wizard); |
| |
| users() is an efun that gets no arguments and returns an array of all |
| logged in players (wizards and players). query_is_wizard() is a |
| simul_efun that gets an object as first (and only) argument and returns |
| true (1) if this object is a wizard and 0 otherwise. |
| |
| So, for each element of the array returned by users() the function |
| query_is_wizard() is called and only those for which 1 was returned are |
| collected into the result and then put into the variable x. |
| |
| We now have all logged in wizards stored as array in the variable x. |
| |
| Another example: We want to filter out all numbers that are greater than |
| 42 from the array a of integers: |
| |
| x = filter(({ 10,50,30,70 }),#'>,42); |
| |
| (x will now be ({ 50,70 }).) |
| |
| Here two things are new: first: we create a closure that points to an |
| operator; second: we use the possibility to give extra arguments to |
| filter(). |
| |
| Like all efuns the usual operators can be pointed to with a closure by |
| prepending #' to them. funcall(#'>,4,5) is exactly the same as (4>5). |
| |
| The extra arguments given as third to last argument (as many as you |
| like) to filter() are given as second to last argument to the |
| function pointed to by the closure each time it is called. |
| |
| Thus we now call (({ 10,50,30,70 })[0]>42), (({ 10,50,30,70 })[1]>42) ... |
| (which is (10>42), (50>42) ...) and return an array of all elements this |
| returns true for and store it into x. |
| |
| If you want to create a closure to an efun of which you have the name |
| stored in a string you can create such an efun-closure with the efun |
| symbol_function(): |
| |
| symbol_function("write") // this will return #'write |
| |
| funcall(symbol_function("write"),"foobar"); // == write("foobar"); |
| |
| This function does not very often occur in normal code but it is very |
| useful for tool-programming (eg the robe uses symbol_function() to allow |
| you call any efun you give). |
| |
| 2 Lfun- and Lambda-Closures |
| |
| Very often the possibilities closures to efuns offer are not sufficient |
| for the purpose one has. In nearly all cases three possibilities exist in |
| such cases: use an lfun- or inline-closure, or a lambda-closure. |
| |
| 2.1 Lfun-Closures |
| |
| The first possibility is rather easy: like with the efun-closures you |
| can create a pointer to a function in the same object you are by using |
| the #' to prepend it to a function name of a function declared above. |
| Example: |
| |
| status foo(int x) { |
| return ((x*2) > 42); |
| } |
| |
| int *bar() { |
| return filter(({ 10,50,30,70 }),#'foo); |
| } |
| |
| Thus, #'foo is used like there was an efun of this name and doing the |
| job that is done in foo(). |
| |
| 2.2 Inline Closure |
| |
| Inline closures are a variant of lfun closures, the difference being |
| that the function text is written right where the closure is used, |
| enclosed in a pair of '(:' and ':)'. The compiler will then take care |
| of creating a proper lfun and lfun-closure. The arguments passed to |
| such an inline closure are accessible by position: $1 would be the |
| first argument, $2 the second, and so on. With this, the |
| above example would read: |
| |
| int * bar() { |
| return filter(({ 10,50,30,70 }), (: ($1 * 2) > 42 :)); |
| } |
| |
| or alternatively: |
| |
| int * bar() { |
| return filter(({ 10,50,30,70 }), (: return ($1 * 2) > 42; :)); |
| } |
| |
| The difference between the two versions is that in the first form the text |
| of the inline closure must be an expression only, whereas in the second |
| form any legal statement is allowed. The compiler distinguishes the two |
| forms by the last character before the ':)': if it's a ';' or '}', the |
| compiler treats the closure as statement(s), otherwise as expression. |
| |
| Inline closures may also nested, so that the following (not very useful) |
| example is legal, too: |
| |
| return filter( ({ 10, 50, 30, 70 }) |
| , (: string *s; |
| s = map(users(), (: $1->query_name() :)); |
| return s[random(sizeof(s))] + ($1 * 2); |
| :)); |
| |
| The notation of inline closures is modelled after the MudOS functionals, |
| but there are a few important differences in behaviour. |
| |
| |
| 2.3 Lambda-Closures |
| |
| Lambda-Closures take the idea of 'define it where you use it' one step |
| further. On first glance they may look like inline closures with an uglier |
| notation, but they offer a few increased possibilities. But first things |
| first. |
| |
| The efun lambda() creates a function temporarily and returns a closure |
| pointing to this function. lambda() therefor gets two arrays as |
| arguments, the first is a list of all arguments the function shall expect |
| and the second array is the code of the function (in a more or less |
| complicated form; at least not in C- or LPC-syntax). The closure #'foo |
| from the example above could be notated as lambda-closure: |
| |
| lambda(({ 'x }),({ (#'>), |
| ({ (#'*),'x,2 }), |
| 42 |
| })) |
| |
| Now, the first argument is ({ 'x }), an array of all arguments the |
| function shall expect: 1 argument (called 'x) is expected. Notice the |
| strange notation for this argument with one single leading tick. Like |
| The hash-tick to denote closures the leading tick is used to denote |
| things called "symbols". They do not differ much from strings and if |
| you do not want to have a deeper look into closures you can leave it |
| this way. |
| |
| The second argument is an array. The first element of such an array |
| must be an efun- or an lfun-closure, the further elements are the |
| arguments for the function this closure points to. If such an argu- |
| ment is an array, it is treated alike; the first element must be a |
| closure and the remaining elements are arguments (which of course |
| also might be arrays ...). |
| |
| This leads to a problem: sometimes you want to give an array as an |
| argument to a function. But arrays in an array given to lambda() are |
| interpreted as code-arrays. To allow you to give an array as an argu- |
| ment within an array given to lambda(), you can use the function |
| quote() to make your array to a quoted array (a quoted array is for |
| an array what a symbol is for a string): |
| |
| lambda(0,({ (#'sizeof), |
| quote(({ 10,50,30,70 })) |
| })) |
| |
| For array constants, you can also use a single quote to the same |
| effect: |
| |
| lambda(0,({ (#'sizeof), |
| '({ 10,50,30,70 }) |
| })) |
| |
| This lambda-closure points to a function that will return 4 (it will |
| call sizeof() for the array ({ 10,50,30,70 })). Another thing: if |
| we want to create a function that expects no arguments, we can give |
| an empty array as first argument to lambda() but we can give 0 as |
| well to attain this. This is just an abbreviation. |
| |
| Lambda-closure constructs can become quite large and hard to read. The |
| larger they become the harder the code is to read and you should avoid |
| extreme cases. Very often the possibility to use an lfun or an inline |
| instead of a large lambda shortens the code dramatically. Example: |
| |
| status foo(object o) { |
| return environment(o)->query_level()>WL_APPRENTICE; |
| } |
| |
| x=filter(a,#'foo); |
| |
| does the same as |
| |
| x=filter(a,lambda(({ 'o }), |
| ({ (#'>), |
| ({ (#'call_other), |
| ({ (#'environment),'o }), |
| "query_level" |
| }), |
| WL_APPRENTICE |
| }))); |
| |
| (Note that the syntax with the arrow "->" for call_other()s cannot be |
| used, #'-> does not exist. You have to use #'call_other for this and |
| give the name of the lfun to be called as a string.) |
| |
| This example also demonstrates the two disadvantages of lambda closures. |
| First, they are very difficult to read, even for a simple example like |
| this. Second, the lambda closure is re-created everytime the |
| filter() is executed, even though the created code is always the |
| same. |
| |
| 'Why use lambdas at all then?' you may ask now. Well, read on. |
| |
| |
| 2.3.1 Advantages of Lambda Closures |
| |
| The advantages of lambdas stem from the fact that they are created |
| at runtime from normal arrays. |
| |
| This means that the behaviour of a lambda can be made dependant on data |
| available only at runtime. For example: |
| |
| closure c; |
| c = lambda(0, ({#'-, ({ #'time }), time() }) ); |
| |
| Whenever you now call this closure ('funcall(c)') it will return the |
| elapsed time since the closure was created. |
| |
| The second advantage of lambdas is that the arrays from which they |
| are compiled can be constructed at runtime. Imagine a customizable prompt |
| which can be configured to display the time, the environment, or both: |
| |
| mixed code; |
| |
| code = ({ "> " }); |
| if (user_wants_time) |
| code = ({ #'+, ({ #'ctime }), code }); |
| if (user_wants_environment) |
| code = ({ #'+, ({#'to_string, ({#'environment, ({#'this_player }) }) }) |
| , code }); |
| set_prompt(lambda(0, code)); |
| |
| |
| 2.3.2 Free Variables in Lambda-Closure Constructs |
| |
| You can use local variables in lambda constructs without declaring |
| them, just use them. The only limitation is that you at first have |
| to assign something to them. Give them as symbols like you do with |
| the arguments. This feature does not make much sense without the use |
| of complexer flow controlling features described below. |
| |
| The closure #'= is used to assign a value to something (like the |
| LPC-operator = is). |
| |
| 2.3.3 Special Efun-Closures and Operator-Closures for Lambdas |
| |
| There are some special closures that are supposed to be used only |
| within a lambda construct. With them you can create nearly all code |
| you can with regular LPC-code like loops and conditions. |
| |
| #'? acts like the "if" statement in LPC. The first argument is the |
| condition, the second is the code to be executed if the condition |
| returns true. The following arguments can also be such couples of |
| code-arrays that state a condition and a possible result. If at |
| the end there is a single argument, it is used as the else-case |
| if no condition returned true. |
| |
| lambda(({ 'x }),({ (#'?), // if |
| ({ (#'>),'x,5 }), // (x > 5) |
| ({ (#'*),'x,2 }), // result is x * 2; |
| ({ (#'<),'x,-5 }), // else if (x < -5) |
| ({ (#'/),'x,2 }), // result is x/2; |
| 'x // else result is x; |
| })) |
| |
| #'?! is like the #'? but it negates all conditions after evaluation |
| and thus is like an ifnot in LPC (if there were one). |
| |
| #', (which looks a bit strange) is the equivalent of the comma-operator |
| in LPC and says: evaluate all arguments and return the value of |
| the last. It is used to do several things inside a lambda-closure. |
| |
| lambda(({ 'x }),({ (#',), // two commas necessary! |
| // one for the closure and one as |
| // delimiter in the array |
| ({ (#'write),"hello world!" }), |
| ({ (#'say),"Foobar." }) |
| })) |
| |
| #'while acts like the LPC statement "while" and repeats executing one |
| code-array while another returns true. |
| #'while expects two or more arguments: the condition as first |
| argument, then the result the whole expression shall have after |
| the condition turns false (this is in many cases of no interest) |
| and as third to last argument the body of the loop. |
| |
| lambda(0,({ (#',), // several things to do ... |
| ({ (#'=),'i,0 }), // i is a local variable of this |
| // lambda-closure and is |
| // initialized with 0 now. |
| ({ (#'while), |
| ({ (#'<),'i,10 }), // condition: i < 10 |
| 42, // result is not interesting, |
| // but we must give one |
| ({ (#'write),'i }), // give out i |
| ({ (#'+=),'i,1 }) // increase i |
| }) |
| })) |
| |
| The function this closure points to will give out the |
| numbers from 0 to 9 and then return 42. |
| |
| #'do is like the do-while statement in LPC and is very much like the |
| #'while. The difference is that #'while tests the condition al- |
| ready before the body is evaluated for the first time, this means |
| that the body might not be evaluated even once. #'do evaluates |
| the body first and then the condition, thus the body is evaluated |
| at least one time. |
| Furthermore, the arguments for #'do are changed in order. #'do |
| expects as first to last but two the body of the loop, then the |
| condition (as last-but-one'th argument) and the result value as |
| last argument. So #'do must have at least two arguments: the |
| condition and the result. |
| |
| lambda(0,({ (#',), // several things to do ... |
| ({ (#'=),'i,0 }), // i is a local variable of this |
| // lambda-closure and is initialized |
| // with 0 now. |
| ({ (#'do), |
| ({ (#'write),'i }), // give out i |
| ({ (#'+=),'i,1 }), // increase i |
| ({ (#'<),'i,10 }), // condition: i < 10 |
| 42 // result is not interesting |
| }) |
| })) |
| |
| NOTE: There is no #'for in LPC, you should use #'while for this. |
| |
| #'foreach is like the foreach() statement in LPC. It evaluates one or |
| more bodies repeatedly for every value in a giving string, array |
| or mapping. The result of the closure is 0. |
| |
| #'foreach expects two or more arguments: |
| - a single variable symbol, or an array with several variable |
| symbols |
| - the value to iterate over |
| - zero or more bodes to evaluate in each iteration. |
| |
| The single values retrieved from the given value are assigned |
| one after another to the variable(s), then the bodies are executed |
| for each assignment. |
| |
| lambda(0, ({#'foreach, 'o, ({#'users}) |
| , ({#'call_other, 'o, "die" }) |
| })); |
| |
| lambda(0, ({#'foreach, ({'k, 'v}), ({ ...mapping...}) |
| , ({#'printf, "%O:%O\n", 'k, 'v }) |
| })); |
| |
| #'return gets one argument and acts like the "return" statement in LPC |
| in the function that is created by lambda(). It aborts the |
| execution of this function and returns the argument. |
| |
| lambda(0,({ (#'while),// loop |
| 1, // condition is 1 ==> endles loop |
| 42, // return value (which will never be used) |
| ({ (#'write),"grin" }), |
| ({ (#'?!), // ifnot |
| ({ (#'random),10 }), // (random(10)) |
| ({ (#'return),100 }) // return 100; |
| }) |
| })) |
| |
| This function will enter an endles loop that will in each |
| turn give out "grin" and if random(10) returns 0 (which will |
| of course happen very soon) it will leave the function with |
| "return 100". The value 42 that is given as result of the |
| loop would be returned if the condition would evaluate to 0 |
| which cannot be. (1 is never 0 ;-) |
| |
| #'break is used like the "break" statement in LPC and aborts the exe- |
| cution of loops and switches. |
| It must not appear outside a loop or switch of the lambda |
| closure itself, it cannot abort the execution of the function |
| the closure points to! |
| |
| lambda(0,({ (#'?), |
| ({ (#'random),2 }), |
| ({ (#'break) }), // this will cause the error |
| // "Unimplemented operator break |
| // for lambda()" |
| "random was false!" |
| })); |
| |
| You can use ({ #'return,0 }) instead of ({ #'break }) in such |
| cases. |
| |
| #'continue is used like the "continue" statement in LPC and jumps to |
| the end of the current loop and continues with the loop |
| condition. |
| |
| #'default may be used within a #'switch-construct but be careful! |
| To call symbol_function("default") (which is done usually |
| by tools that allow closure-creation) might crash the |
| driver! So please do only use it within your LPC-files. |
| (NOTE: This driver bug is fixed somewhere below 3.2.1@131.) |
| |
| #'.. may be used within a #'switch-construct but is not implemented |
| yet (3.2.1@131). But #'[..] works well instead of it. |
| |
| #'switch is used to create closures which behave very much like the |
| switch-construct in LPC. To understand the following you |
| should already know the syntax and possibilities of the |
| latter one (which is mightier than the C-version of switch). |
| |
| I will confront some LPC versions and the corresponding clo- |
| sure versions below. |
| |
| LPC: Closure: |
| switch (x) { lambda(0,({ (#'switch), x, |
| case 5: ({ 5 }), |
| return "five"; ({ (#'return),"five" }), |
| (#',), |
| case 6..9: ({ 6, (#'[..]), 9 }), |
| return "six to nine"; ({ (#'return), |
| "six to nine" }), |
| (#',), |
| case 1: ({ 1 }), |
| write("one"); ({ (#'write),"one" }), |
| // fall through (#',), |
| case 2: ({ 2, |
| case 10: 10 }), |
| return "two or ten"; ({ (#'return), |
| "two or ten" }), |
| (#',), |
| case 3..4: ({ 3, (#'[..]), 4 }), |
| write("three to four"); ({ (#'write), |
| "three to four" }), |
| break; // leave switch (#'break), |
| default: ({ (#'default) }), |
| write("something else"); ({ (#'write), |
| "something else" }), |
| break; (#'break) |
| } })) |
| |
| #'&& evaluates the arguments from the first on and stops if one evalu- |
| ates to 0 and returns 0. If none evaluates to 0 it returns the |
| result of the last argument. |
| |
| #'|| evaluates the arguments from the first on and stops if one evalu- |
| ates to true (not 0) and returns it. If all evaluate to 0 it |
| returns 0. |
| |
| #'catch executes the closure given as argument, but catches any |
| runtime error (see catch(E)). Optionally the symbols 'nolog, |
| 'publish and 'reserve may be given as additional arguments to |
| modify the behaviour of the catch. |
| |
| #'sscanf acts similar to how a funcall would, but passes the third |
| and following arguments as lvalues, that is, values which can |
| be assigned to. |
| |
| #'= and the #'<op>= variants are also special because the first |
| argument has to be an lvalue. |
| |
| 2.4 Closures with Strange Names |
| |
| #'negate is the unary minus that returns -x for the argument x. |
| |
| map(({ 1,2,3 }),#'negate) |
| |
| This returns ({ -1,-2,-3 }). |
| |
| #'[ is used for the things that in LPC are done with |
| the []-operator (it indexes an array or a mapping). |
| |
| lambda(0,({ #'[,quote(({ 10,50,30,70 })),2 })) ==> 30 |
| lambda(0,({ #'[,([ "x":10;50, "y":30;70 ]),"x",1 })) ==> 50 |
| |
| #'[< is the same as #'[ but counts the elements from the |
| end (like indexing with [] and the "<"). |
| |
| #'[..] returns a subarray of the argument from the one |
| given index to the other given index, both counted from the |
| beginning of the array. |
| |
| #'[..<] |
| #'[<..] |
| #'[<..<] same as above, but the indexes are counted from the end, |
| |
| lambda(0,({ #'[..<], |
| quote(({ 0,1,2,3,4,5,6,7 })),2,3 |
| })) |
| |
| This will return ({ 2,3,4,5 }). |
| #'[.. |
| #'[<.. same as above, but only the first index is given, the |
| subarray will go till the end of the original (like with |
| [x..]). |
| |
| #'({ is used to create arrays (as with ({ }) in LPC). All arguments |
| become the elements of the array. |
| |
| lambda(0,({ #'({, |
| ({ (#'random),10 }), |
| ({ (#'random),50 }), |
| ({ (#'random),30 }), |
| ({ (#'random),70 }) |
| })) |
| |
| This returns ({ random(10),random(50),random(30),random(70) }). |
| |
| #'([ is used to create mappings out of single entries (with seve- |
| ral values) like the ([ ]) in LPC. Very unusual is the fact |
| that this closure gets arrays as argument that are not eval- |
| uated, although they are not quoted. |
| |
| lambda(0,({ #'([, |
| ({ "x",1,2,3 }), |
| ({ "y",4,5,6 }) |
| })); |
| |
| This returns ([ "x": 1;2;3, |
| "y": 4;5;6 ]). |
| |
| However, the elements of the arrays are evaluated as lambda |
| expressions, so if you want to create a mapping from values |
| evaluated at call time, write them as lambda closures: |
| |
| lambda(0, ({ #'([, ({ 1, ({ #'ctime }) }) }) ) |
| |
| will return ([ 1: <result of ctime() at call time ]). |
| |
| Arrays can be put into the mapping by quoting: |
| |
| lambda(0, ({ #'([, ({ 1, '({ 2 }) }) }) ) |
| |
| will return ([ 1: ({ 2 }) ]) |
| |
| |
| #'[,] is nearly the same as #'[. The only difference |
| shows up if you want to index a mapping with a width |
| greater than 1 (with more than just one value per |
| key) directly with funcall(). Example: |
| funcall(#'[,([ 0:1;2, 3:4;5 ]),0,1) |
| This will not work. Use #'[,] and it will |
| work. If you want to use it in a lambda closure you |
| do not have to use #'[,] and #'[ will |
| do fine. On the other hand, #'[,] cannot |
| work with arrays, so in nearly all cases use #'[ |
| and just in the described special case, use |
| #'[,]. |
| This is a strange thing and I deem it a bug, so it |
| might change in the future. |
| |
| 2.5 Operator-Closures |
| |
| Most of the closures that are used for things which are done by opera- |
| tors are in fact not operator-closures but efun-closures. But there are |
| a few which do not have the state of efun-closures but are called |
| "operator-closures". #'return is an example, a complete list of them is |
| given below. |
| |
| These closures cannot be called directly using funcall() or apply() (or |
| other efuns like filter()), but must appear only in lambda-con- |
| structs. |
| |
| funcall(#'return,4); // does not work! This will raise an |
| // Uncallable-closure error. |
| funcall(lambda(0, // this is a correct example |
| ({ (#'return),4 }) |
| )); |
| |
| All operator-closures: |
| #'&& |
| #'|| |
| #', |
| #'? |
| #'?! |
| #'= |
| #'<op>= |
| #'++ |
| #'-- |
| #'break |
| #'catch |
| #'continue |
| #'default |
| #'do |
| #'foreach |
| #'return |
| #'sscanf |
| #'switch |
| #'while |
| #'({ |
| #'([ |
| |
| #'.. is very likely to be an operator closure too, but since it is |
| not implemented yet, I cannot say for sure. |
| |
| 2.6 Variable-Closures |
| |
| All object-global variables might be "closured" by prepending a #' to |
| them to allow access and/or manipulation of them. So if your object has |
| a global variable x you can use #'x within a closure. |
| |
| Normally you will treat those expressions like lfun-closures: put them |
| into an array to get the value: |
| |
| object.c: |
| int x; |
| int foo() { |
| return lambda(0,({ (#'write),({ (#'x) }) })); |
| } |
| |
| Anybody who now calls object->foo() will get a closure which will, when |
| evaluated, write the actual value of object's global variable x. |
| |
| Variable closures do not accept arguments. |
| |
| 3 Examples |
| |
| In this section I will give and explain some examples coming out of |
| praxis. If the explanation seems to be in some cases too detailed this |
| can be explained by the trial to allow the reader to read the examples |
| section first ;-) |
| |
| 3.1 Lfun-Closure |
| |
| An item with a complex long-description like a watch that shall always |
| show the actual time will usually base upon the complex/item-class and |
| give an lfun-closure as argument to the set_long()-method. |
| |
| watch.c: |
| inherit "complex/item"; |
| |
| string my_long() { |
| return ("The watch is small and has a strange otherworldly" |
| " aura about it.\n" |
| "The current time is: "+ctime()+".\n"); |
| } |
| |
| void create() { |
| set_short("a little watch"); |
| set_id(({ "watch","little watch" })); |
| set_long(#'my_long); // the lfun-closure to the lfun my_long() |
| } |
| |
| 3.2 Lambda-Closure |
| |
| The example from 3.1 can also be written using a lambda-closure. |
| |
| watch.c: |
| inherit "complex/item"; |
| |
| void create() { |
| set_short("a little watch"); |
| set_id(({ "watch","little watch" })); |
| set_long(lambda(0,({ (#'+), |
| "The watch is small and has a strange" |
| " otherworldly aura about it.\n" |
| "The current time is: ", |
| ({ (#'+), |
| ({ (#'ctime) }), |
| ".\n" |
| }) |
| }))); |
| } |