1,333
edits
No edit summary |
|||
| Line 3: | Line 3: | ||
<syntaxhighlight lang="nasal"> | <syntaxhighlight lang="nasal"> | ||
(func() { | (func() { | ||
print("Hello World"); | |||
}) | })(); | ||
(); | |||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 13: | Line 12: | ||
<syntaxhighlight lang="nasal"> | <syntaxhighlight lang="nasal"> | ||
var square = func(number) { | var square = func(number) { | ||
return number * number; | |||
}; | }; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 38: | Line 37: | ||
<syntaxhighlight lang="nasal"> | <syntaxhighlight lang="nasal"> | ||
var myFunction = func() { | var myFunction = func() { | ||
print("Hello World!"); | print("Hello World!"); | ||
} | }; | ||
myFunction (); | myFunction (); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
The main difference being that the invocation of the myFunction function can be replaced with the anonymous function body, i.e. without any function name associated with it.}} | The main difference being that the invocation of the myFunction function can be replaced with the anonymous function body, i.e. without any function name associated with it.}} | ||
In its most basic form, a function does not even take any arguments. Let's imagine a function named '''hello''': | In its most basic form, a function does not even take any arguments. Let's imagine a function named '''hello''': | ||
| Line 51: | Line 49: | ||
<syntaxhighlight lang="nasal"> | <syntaxhighlight lang="nasal"> | ||
var hello = func() { | var hello = func() { | ||
print("Hello !"); | |||
}; | }; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 61: | Line 59: | ||
<syntaxhighlight lang="nasal"> | <syntaxhighlight lang="nasal"> | ||
var hello = func() { | var hello = func() { | ||
print("Hello !"); | |||
}; | }; | ||
call( hello,[] ); | call( hello,[] ); | ||
| Line 88: | Line 86: | ||
<syntaxhighlight lang="nasal"> | <syntaxhighlight lang="nasal"> | ||
hello( hello() ); | hello(hello()); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 99: | Line 97: | ||
<syntaxhighlight lang="nasal"> | <syntaxhighlight lang="nasal"> | ||
hello( "FlightGear" ); | hello("FlightGear"); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 107: | Line 105: | ||
<syntaxhighlight lang="nasal"> | <syntaxhighlight lang="nasal"> | ||
var hello = func( name ) { | var hello = func(name) { | ||
print("Hello ", name); | print("Hello ", name); | ||
}; | }; | ||
| Line 139: | Line 137: | ||
var log_message = func() {} | var log_message = func() {} | ||
var log_message = func {return;} # the semicolon here is also optional | var log_message = func { return; } # the semicolon here is also optional | ||
var log_message = func return; | var log_message = func return; | ||
var log_message = func {nil;} # and here too, of course | var log_message = func { nil; } # and here too, of course | ||
var log_message = func {}; | var log_message = func {}; | ||
| Line 167: | Line 165: | ||
var log_message = func { | var log_message = func { | ||
print(arg[0]); | print(arg[0]); | ||
} | }; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 175: | Line 173: | ||
var log_message = func() { | var log_message = func() { | ||
print(arg[0]); | print(arg[0]); | ||
} | }; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 191: | Line 189: | ||
var log_message = func(msg) { | var log_message = func(msg) { | ||
print(msg); | print(msg); | ||
} | }; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 203: | Line 201: | ||
var log_message = func(msg = "error") { | var log_message = func(msg = "error") { | ||
print(msg); | print(msg); | ||
} | }; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 211: | Line 209: | ||
#Incorrect: | #Incorrect: | ||
var log_message = func(msg = "error", line, object = "ground") { | var log_message = func(msg = "error", line, object = "ground") { | ||
#some code | # some code | ||
} | }; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 218: | Line 216: | ||
#Correct: | #Correct: | ||
var log_message = func(line, msg = "error", object = "ground") { | var log_message = func(line, msg = "error", object = "ground") { | ||
#some code | # some code | ||
} | }; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 239: | Line 237: | ||
<syntaxhighlight lang="nasal" enclose="div"> | <syntaxhighlight lang="nasal" enclose="div"> | ||
# Repeated from above: a simple function with a defaulted argument | # Repeated from above: a simple function with a defaulted argument | ||
var log_message = func(msg="error") { | var log_message = func(msg = "error") { | ||
print(msg); | print(msg); | ||
} | }; | ||
log_message("Hello!"); # the first specified argument gets mapped to the first declared argument, thus: msg="Hello!" | log_message("Hello!"); # the first specified argument gets mapped to the first declared argument, thus: msg="Hello!" | ||
log_message(); # no specified arguments, so the default is used: msg="error" | log_message(); # no specified arguments, so the default is used: msg="error" | ||
var print_arg = func { | var print_arg = func { | ||
foreach (var argument; arg) | foreach (var argument; arg) { | ||
print(argument); | print(argument); | ||
} | } | ||
print_arg("Hello!", "How are you today?") # there are now two arguments and they are absorbed into the implicit "arg" vector, thus: arg=["Hello!", "How are you today?"] | }; | ||
print_arg() # no arguments, so arg=[] and nothing is done: print(argument) is not even run | print_arg("Hello!", "How are you today?"); # there are now two arguments and they are absorbed into the implicit "arg" vector, thus: arg=["Hello!", "How are you today?"] | ||
print_arg(); # no arguments, so arg=[] and nothing is done: print(argument) is not even run | |||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 260: | Line 259: | ||
Examples: | Examples: | ||
<syntaxhighlight lang="nasal"> | <syntaxhighlight lang="nasal"> | ||
#if we have functions defined: | # if we have functions defined: | ||
var log_message = func (msg="") { | var log_message = func(msg = "") { | ||
#some code to log variable msg | # some code to log variable msg | ||
} | }; | ||
var lookat = func (heading=0, pitch=0, roll=0, x=nil, y=nil, z=nil, time=nil, fov=20) { | var lookat = func (heading = 0, pitch = 0, roll = 0, x = nil, y = nil, z = nil, time = nil, fov = 20) { | ||
#some code using those variables | # some code using those variables | ||
} | }; | ||
#we can use them them the usual way with comma separated list of arguments: | # we can use them them the usual way with comma separated list of arguments: | ||
log_message("Hello World!"); | log_message("Hello World!"); | ||
lookat(180, 20, 0, XO, YO, ZO, now, 55); | lookat(180, 20, 0, XO, YO, ZO, now, 55); | ||
#or we can use the hash literal arguments instead: | # or we can use the hash literal arguments instead: | ||
log_message(msg:"Hello World!"); | log_message(msg: "Hello World!"); | ||
lookat(heading:180, pitch:20, roll:0, x:X0, y:Y0, z:Z0, time:now, fov:55); | lookat(heading: 180, pitch: 20, roll: 0, x: X0, y: Y0, z: Z0, time: now, fov: 55); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 282: | Line 281: | ||
<syntaxhighlight lang="nasal"> | <syntaxhighlight lang="nasal"> | ||
var setPosition = func (latitude_deg, longitude_deg, altitude_ft) { | var setPosition = func(latitude_deg, longitude_deg, altitude_ft) { | ||
# do something here | # do something here | ||
} | }; | ||
# the actual function call: | # the actual function call: | ||
setPosition(latitude_deg:34.00, longitude_deg:7.00, alt_ft:10000); | setPosition(latitude_deg: 34.00, longitude_deg: 7.00, alt_ft: 10000); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 292: | Line 291: | ||
This is a good practice, as you may eventually have to take a longer break, away from your code - and then even you yourself will come to appreciate such small things that make code more intuitive to work with. | This is a good practice, as you may eventually have to take a longer break, away from your code - and then even you yourself will come to appreciate such small things that make code more intuitive to work with. | ||
Declared arguments are checked and defaulted as would be expected: it's an error if you fail to pass a value for a mandatory/undefaulted argument, missing default arguments get assigned as usual, and any rest parameter (e.g. <tt>func(a,b=2,rest...){}</tt>) will be assigned with an empty vector. | Declared arguments are checked and defaulted as would be expected: it's an error if you fail to pass a value for a mandatory/undefaulted argument, missing default arguments get assigned as usual, and any rest parameter (e.g. <tt>func(a, b = 2, rest...){}</tt>) will be assigned with an empty vector. | ||
=== Specifying parameters at call-time === | === Specifying parameters at call-time === | ||
| Line 300: | Line 299: | ||
<syntaxhighlight lang="nasal"> | <syntaxhighlight lang="nasal"> | ||
var print_pos = func { | var print_pos = func { | ||
print('lat:', lat, ' lon:', lon, '\n'); | print('lat: ', lat, ' lon: ', lon, '\n'); | ||
} | }; | ||
print_pos(lat:43.94, lon:23.88); # these show up perfectly fine as lat=43.94 and lon=23.88 | print_pos(lat: 43.94, lon: 23.88); # these show up perfectly fine as lat = 43.94 and lon = 23.88 | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 321: | Line 320: | ||
<syntaxhighlight lang="nasal"> | <syntaxhighlight lang="nasal"> | ||
var add1 = func(myScalar) { | var add1 = func(myScalar) { | ||
myScalar = myScalar + 1; | |||
return myScalar; | |||
} | }; | ||
var set42 = func(myVector) { | var set42 = func(myVector) { | ||
myVector[0] = 42; | |||
} | }; | ||
var a = 1; | var a = 1; | ||
| Line 334: | Line 333: | ||
print(a); # variable a outside the function shall be still 1 | print(a); # variable a outside the function shall be still 1 | ||
var b = [1,2,3]; | var b = [1, 2, 3]; | ||
print(debug.string(b)); | print(debug.string(b)); | ||
print(set42(b)); # print the return value which is 42 | print(set42(b)); # print the return value which is 42 | ||
print(debug.string(b)); # now b[0] has changed | print(debug.string(b)); # now b[0] has changed | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 345: | Line 343: | ||
<syntaxhighlight lang="nasal"> | <syntaxhighlight lang="nasal"> | ||
var calculate = func(param1,param2,operator) { | var calculate = func(param1, param2, operator) { | ||
var add = func(p1,p2) {p1+p2;} | var add = func(p1, p2) { p1 + p2; } | ||
var sub = func(p1,p2) {p1-p2;} | var sub = func(p1, p2) { p1 - p2; } | ||
var mul = func(p1,p2) {p1*p2;} | var mul = func(p1, p2) { p1 * p2; } | ||
var div = func(p1,p2) {p1/p2;} | var div = func(p1, p2) { p1 / p2; } | ||
if (operator=="+") return add(param1,param2); | if (operator == "+") return add(param1, param2); | ||
if (operator=="-") return sub(param1,param2); | if (operator == "-") return sub(param1, param2); | ||
if (operator=="*") return mul(param1,param2); | if (operator == "*") return mul(param1, param2); | ||
if (operator=="/") return div(param1,param2); | if (operator == "/") return div(param1, param2); | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 362: | Line 360: | ||
<syntaxhighlight lang="nasal"> | <syntaxhighlight lang="nasal"> | ||
var add = func(p1,p2) p1+p2; | var add = func(p1, p2) p1 + p2; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 377: | Line 375: | ||
<syntaxhighlight lang="nasal"> | <syntaxhighlight lang="nasal"> | ||
var multiply2 = func (params) { | var multiply2 = func (params) { | ||
if (typeof(params)=="scalar") return params*arg[0]; | if (typeof(params) == "scalar") return params * arg[0]; | ||
if (typeof(params)=="vector") return params[0]*params[1]; | if (typeof(params) == "vector") return params[0] * params[1]; | ||
if (typeof(params)=="hash") return params.x*params.y; | if (typeof(params) == "hash") return params.x * params.y; | ||
die("cannot do what you want me to do"); | die("cannot do what you want me to do"); | ||
} | }; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 387: | Line 385: | ||
<syntaxhighlight lang="nasal"> | <syntaxhighlight lang="nasal"> | ||
multiply2( | multiply2(2, 6); # multiply two scalars | ||
multiply2( [5,7] ); # multiply two scalars stored in a vector | multiply2([5, 7]); # multiply two scalars stored in a vector | ||
multiply2( {x:8, y:9} ); # multiply two scalars stored in a hash | multiply2({ x: 8, y: 9 }); # multiply two scalars stored in a hash | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 404: | Line 402: | ||
var hash = {}; | var hash = {}; | ||
hash.hello = func { | hash.hello = func { | ||
print ("Hello !"); | print("Hello !"); | ||
} | }; | ||
var vector = []; | var vector = []; | ||
| Line 435: | Line 433: | ||
debug.dump(local); | debug.dump(local); | ||
} | } | ||
} | }; | ||
generator([[],[]])(99); # prints {} [[],[]] 99 | generator([[], []])(99); # prints {} [[],[]] 99 | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 446: | Line 444: | ||
<syntaxhighlight lang="nasal"> | <syntaxhighlight lang="nasal"> | ||
var result = []; | var result = []; | ||
for (var i=0; i<10; i+=1) | for (var i = 0; i < 10; i += 1) { | ||
append(result, func print(i)); | append(result, func print(i)); | ||
foreach (var fn; result) | } | ||
foreach (var fn; result) { | |||
fn(); # should print the numbers 0-9 | fn(); # should print the numbers 0-9 | ||
} | |||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 457: | Line 457: | ||
<syntaxhighlight lang="nasal"> | <syntaxhighlight lang="nasal"> | ||
for (var i=0; i<10; i+=1) | for (var i = 0; i < 10; i += 1) | ||
(func { | (func { | ||
# this is an anonymous closure that will be able to "save" the value of i | # this is an anonymous closure that will be able to "save" the value of i | ||
| Line 464: | Line 464: | ||
})(); # call immediately | })(); # call immediately | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Or one could write it like this (which I prefer for minute reasons related to the garbage collector/GC): | Or one could write it like this (which I prefer for minute reasons related to the garbage collector/GC): | ||
<syntaxhighlight lang="nasal"> | <syntaxhighlight lang="nasal"> | ||
# arguments are local variables, so this form can do the same thing: | # arguments are local variables, so this form can do the same thing: | ||
var generator = func(i) return func print(i); | var generator = func(i) return func print(i); | ||
for (var i=0; i<10; i+=1) | for (var i = 0; i < 10; i += 1) { | ||
append(result, generator(i)); | append(result, generator(i)); | ||
} | |||
</syntaxhighlight> | </syntaxhighlight> | ||
The other way (which produces a nearly identical result) uses the builtin bind() function. When a func {...} expression is evaluated, there are two Nasal variables that are involved: one is a naCode and the other is a naFunc. The naCode is stored as a constant for the function and it stores information about the arguments and the body; the naFunc is simply a wrapper that makes the naCode executable and gives it a closure. Each time the VM evaluates the func expression, this creates another naFunc from the naCode base with a unique closure. If you call <tt>bind(fn, caller(0)[0], caller(0)[1])</tt> (where fn is the function you just created), you essentially do nothing – as the naFunc is, by default, bound to the caller like so. However, changing the second argument to a different hash, say a unique one for each iteration, will change the outer namespace of the function while keeping the rest of the closure intact (this last part is important for keeping the global namespace as a closure, so that the print function can be found). For example, using an empty hash with our first example snippet and bind() will produce an undefined symbol error, since we effectively removed the closure that contained the i variable. But if we create a hash such as <tt>{i:i}</tt>, then we will create a faux-namespace with the i variable – but this variable won't be affected by the for loop, and thus we will have accomplished our goal. This is the completed code: | The other way (which produces a nearly identical result) uses the builtin bind() function. When a func {...} expression is evaluated, there are two Nasal variables that are involved: one is a naCode and the other is a naFunc. The naCode is stored as a constant for the function and it stores information about the arguments and the body; the naFunc is simply a wrapper that makes the naCode executable and gives it a closure. Each time the VM evaluates the func expression, this creates another naFunc from the naCode base with a unique closure. If you call <tt>bind(fn, caller(0)[0], caller(0)[1])</tt> (where fn is the function you just created), you essentially do nothing – as the naFunc is, by default, bound to the caller like so. However, changing the second argument to a different hash, say a unique one for each iteration, will change the outer namespace of the function while keeping the rest of the closure intact (this last part is important for keeping the global namespace as a closure, so that the print function can be found). For example, using an empty hash with our first example snippet and bind() will produce an undefined symbol error, since we effectively removed the closure that contained the i variable. But if we create a hash such as <tt>{i:i}</tt>, then we will create a faux-namespace with the i variable – but this variable won't be affected by the for loop, and thus we will have accomplished our goal. This is the completed code: | ||
<syntaxhighlight lang="nasal"> | <syntaxhighlight lang="nasal"> | ||
for (var i=0; i<10; i+=1) | for (var i = 0; i < 10; i += 1) { | ||
append(result, bind(func print(i), {i:i}, caller(0)[1]); | append(result, bind(func print(i), { i: i }, caller(0)[1]); | ||
} | |||
</syntaxhighlight> | </syntaxhighlight> | ||
(for the record, bind returns the function it just rebound, even though it works via mutation, so essentially it is a convenience return of the first argument.) | (for the record, bind returns the function it just rebound, even though it works via mutation, so essentially it is a convenience return of the first argument.) | ||
| Line 488: | Line 494: | ||
var i18n_hello = func(hello) { | var i18n_hello = func(hello) { | ||
return func(name) { # returns an anonymous/unnamed function | return func(name) { # returns an anonymous/unnamed function | ||
print(hello,name); | print(hello, name); | ||
} | }; | ||
} | }; | ||
# create three new functions | # create three new functions | ||
| Line 511: | Line 517: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
So, you could just as well create a small helper function named"thermalLift.new_from_ev(ev)": | So, you could just as well create a small helper function named "thermalLift.new_from_ev(ev)": | ||
<syntaxhighlight lang="nasal"> | <syntaxhighlight lang="nasal"> | ||
thermalLift.new_from_ev = func (ev) { | thermalLift.new_from_ev = func (ev) { | ||
thermalLift.new(ev.lat, ev.lon, ev.radius, ev.height, ev.cn, ev.sh, ev.max_lift, ev.f_lift_radius); | thermalLift.new(ev.lat, ev.lon, ev.radius, ev.height, ev.cn, ev.sh, ev.max_lift, ev.f_lift_radius); | ||
} | }; | ||
var l=thermalLift.new_from_ev(ev); | var l = thermalLift.new_from_ev(ev); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Note that the expression to invoke your code would then also become less complicated and much more comprehensible. | Note that the expression to invoke your code would then also become less complicated and much more comprehensible. | ||
When you have expressions of nested method calls, such as: | When you have expressions of nested method calls, such as: | ||
| Line 534: | Line 539: | ||
<syntaxhighlight lang="nasal"> | <syntaxhighlight lang="nasal"> | ||
var copyNode = func(t,f,path) t.getNode(path).setValue(f.getNode(path).getValue()); | var copyNode = func(t, f, path) t.getNode(path).setValue(f.getNode(path).getValue()); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 540: | Line 545: | ||
<syntaxhighlight lang="nasal"> | <syntaxhighlight lang="nasal"> | ||
copyNode(t,f,"latitude-deg"); | copyNode(t, f, "latitude-deg"); | ||
copyNode(t,f,"longitude-deg"); | copyNode(t, f, "longitude-deg"); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 547: | Line 552: | ||
<syntaxhighlight lang="nasal"> | <syntaxhighlight lang="nasal"> | ||
foreach(var p; ["latitude-deg", "longitude-deg","generated-flag"]) | foreach(var p; ["latitude-deg", "longitude-deg","generated-flag"]) { | ||
copyNode(t,f,p); | copyNode(t, f, p); | ||
} | |||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 554: | Line 560: | ||
<syntaxhighlight lang="nasal"> | <syntaxhighlight lang="nasal"> | ||
var copyNode = func(target,source,properties) { | var copyNode = func(target, source, properties) { | ||
if (typeof(properties)!="vector") properties=[properties]; | if (typeof(properties) != "vector") properties = [properties]; | ||
if (typeof(target)!="hash") target=props.globals.getNode(target); | if (typeof(target) != "hash") target = props.globals.getNode(target); | ||
if (typeof(source)!="hash") target=props.globals.getNode(source) | if (typeof(source) != "hash") target = props.globals.getNode(source) | ||
foreach(var path; properties) | foreach (var path; properties) { | ||
target.getNode(path).setValue( source.getNode(path).getValue() ); | target.getNode(path).setValue( source.getNode(path).getValue() ); | ||
} | } | ||
}; | |||
copyNode("/temp/test", "/position", ["latitude-deg", "longitude-deg", "altitude-ft"]); | copyNode("/temp/test", "/position", ["latitude-deg", "longitude-deg", "altitude-ft"]); | ||
edits