[Closed] Lambdas in Maxscript – First Attempt
I tried to implement some way to allow a function in maxscript to call another function for which it doesn’t
know its parameters. In C# you can use Lambdas to do that.
Here an example in C#:
CheckAge(person,
onAdult: () => Checkout(person, shoppingCardItems)
onChild () => PrintNotAllowedError() )
/// Here the code of the CheckAge method
public void CheckAge(Person p, Action onAdult, Action onChild)
{
if(p.Age > 18)
onAdult();
else
onChild();
}
EDIT: igonore this solution, it is crappy. Please scroll down to my answer to this post.
——————————— old ————————————-
Here is my attempt to mimic a same behavior in maxscript.
I just post it here in case someone is interessed or has some own ideas how to
implement Lambdas in maxscript. I would be eager to know it.
(
global LambdaObject
global lambda1, lambda2, lambda3, lambda4
global Call
(
fn Call fPointer =
(
if classof fPointer == LambdaObject then
fPointer.exec()
else if classof fPointer == MAXScriptFunction then
fPointer()
)
struct LambdaObject
(
functionPointer,
lambdaParameter= #(),
fn CallOrGet arg =
(
if classof arg == LambdaObject then
arg.exec
else
arg
),
fn exec =
(
if lambdaParameter.count == 0 then
functionPointer()
else if lambdaParameter.count == 1 then
functionPointer (CallOrGet lambdaParameter[1])
else if lambdaParameter.count == 2 then
functionPointer (CallOrGet lambdaParameter[1]) (CallOrGet lambdaParameter[2])
else if lambdaParameter.count == 3 then
functionPointer (CallOrGet lambdaParameter[1]) (CallOrGet lambdaParameter[2]) (CallOrGet lambdaParameter[3])
else if lambdaParameter.count == 4 then
functionPointer (CallOrGet lambdaParameter[1]) (CallOrGet lambdaParameter[2]) (CallOrGet lambdaParameter[3]) (CallOrGet lambdaParameter[4])
)
)
-- // lambda
fn lambda1 c p1 =
(
l = LambdaObject()
l.functionPointer = c
l.lambdaParameter = #()
l.lambdaParameter[1] = p1
l
)
fn lambda2 c p1 p2 =
(
l = LambdaObject()
l.functionPointer = c
l.lambdaParameter = #()
l.lambdaParameter[1] = p1
l.lambdaParameter[2] = p2
l
)
fn lambda3 c p1 p2 p3 =
(
l = LambdaObject()
l.functionPointer = c
l.lambdaParameter = #()
l.lambdaParameter[1] = p1
l.lambdaParameter[2] = p2
l.lambdaParameter[3] = p3
l
)
fn lambda4 c p1 p2 p3 p4 =
(
l = LambdaObject()
l.functionPointer = c
l.lambdaParameter = #()
l.lambdaParameter[1] = p1
l.lambdaParameter[2] = p2
l.lambdaParameter[3] = p3
l.lambdaParameter[4] = p4
l
)
OK
)
fn CheckAge person isAdult: isChild: =
(
if person.age > 18 then
Call isAdult
else
Call isChild
OK
)
fn CheckName str isPeter isNotPeter =
(
if str == "Peter" then
Call isPeter
else
Call isNotPeter
OK
)
fn CheckHasEnoughMoney person isRich isPoor =
(
if person.money > 1000 then
Call isRich
else
Call isPoor
OK
)
fn PrintchildError =
(
print "This product is not for children"
)
fn PrintString str = print str
struct Person
(
name,age,money
)
fn checkandprint p =
(
successmessage = "You can buy this"
CheckAge p \
isAdult:(lambda3 CheckHasEnoughMoney p \
/*is rich*/ (lambda3 CheckName p.name \
/*is peter*/ (lambda1 PrintString "Peter you are not allowed to shop here") \
/*is not peter*/ (lambda1 PrintString successmessage)) \
/*is poor*/ (lambda1 PrintString ("Sorry you are poor"))) \
isChild:PrintchildError
)
p = Person()
p.name = "Peter"
p.age = 120
p.money = 200
checkandprint p
)
The readability of the code isn’t that good yet, because of two reasons:
- no function overloading ( so i needed lambda1, lambda2 for differenct parameter counts)
- and the syntax of maxscript wouldn’t allow me to use named arguments ( so i used comment blocks but its ugly)
Also this is not working correctly and i dont’t know why:
fn printallStrings s1 s2 s3 = (print s1+s2+s3)
test = (lambda3 printallStrings "hi" "my" "name" )
Call test
-- NOT WORKING: just calls first lambaparameter
CheckAge p \
isAdult:test \
isChild:PrintchildError
CheckAge p \
isAdult:(lambda1 PrintString "hi") \
isChild:PrintchildError
PS: Yes i am aware of the performance overhead. But for many scipts performance is not the
problem but maintainablity/readability is more important.
Ok, found a solution for the missing overloading of functions.
I forgot that i can use the struct initializing to get the parameters.
Also the code should now work. So ignore the solution above.
Only thing i am still missing is a way to use named arguments in function calls inside lambda
-- test calls some with 1 parameter and some with 2 parameter
CheckAge p1 \
isNotAlive: (lambda print "you arent't even alive, c'mon man!")\
isAdult: (lambda PrintGreeting "welcome" p1)\
isChild: (lambda print "is child")
-- test lambda inside of lambda
CheckAge p1 \
isAdult:(lambda
CheckMoney p1 400 \
(lambda print "you can buy it") \
(lambda print "you don't have enough money to buy this"))\
isChild:(lambda print "you are not old enough to buy this") \
isNotAlive: (lambda print "you arent't even alive, c'mon man!")
Full code:
struct lambda
(
functionpointer,
p1,p2,p3,p4,
fn exec =
(
if p4 != undefined then
functionpointer p1 p2 p3 p4
else if p3 != undefined then
functionpointer p1 p2 p3
else if p2 != undefined then
functionpointer p1 p2
else if p1 != undefined then
functionpointer p1
else
functionpointer()
)
)
fn Call fPointer =
(
if classof fPointer == Lambda then
fPointer.exec()
else if classof fPointer == MAXScriptFunction then
fPointer()
)
-- Test lambdas
fn CheckMoney person price hasMoney notEnoughMoney =
(
if person.money >= price then
Call hasMoney
else
Call notEnoughMoney
)
fn CheckAge person isAdult: isChild: isNotAlive: =
(
if person.age > 18 then
Call isAdult
else if person.age > 0 then
Call isChild
else
Call isNotAlive
)
fn PrintGreeting greeting person = (print (greeting + " " +person.name))
struct Person (name, age , money)
p1 = Person "peter" 30 200
-- test calls some with 1 parameter and some with 2 parameter
CheckAge p1 \
isNotAlive: (lambda print "you arent't even alive, c'mon man!")\
isAdult: (lambda PrintGreeting "welcome" p1)\
isChild: (lambda print "is child")
-- test lambda inside of lambda
CheckAge p1 \
isAdult:(lambda CheckMoney p1 400 \
(lambda print "you can buy it") \
(lambda print "you don't have enough money to buy this"))\
isChild:(lambda print "you are not old enough to buy this") \
isNotAlive: (lambda print "you arent't even alive, c'mon man!")
why is this ‘complexity’?
maxscript has well done ‘function’ passing mechanics…
fn average method a b power: = (method a b power:power)
fn linear a b = (a+b)/2.0
fn square a b = sqrt(a*a + b*b)
fn powered a b power:4 = pow (pow a power + pow b power) (1.0/power)
/*
average linear 2 4
average square 2 4
average powered 2 4 power:3
*/
to allow a function in maxscript to call another function for which it doesn’t
know its parameters
your average can just execute methods that takes two parameters. So you restrict what kind of function gets executed next.
Then I can just execute functions that take one array as argument.
Also I don’t think that solution will be more readable than my last solution.
I want to achieve that function ‘A’ calls function ‘B’ without knowing anything about function ‘B’.
edit: the goal is to get loose coupling
No, just an action, not the agruments.
My C# example from above:
public void main ()
{
var shoppingcarditems = new List<string>();
shoppingcarditem.Add("Apple");
shoppingcarditem.Add("Orange");
CheckAge(person,
onAdult: () => Checkout(person, shoppingcarditems)
onChild () => PrintNotAllowedError() )
}
public void CheckAge(Person p, Action onAdult, Action onChild)
{
if(p.Age > 18)
onAdult();
else
onChild();
}
CheckAge calls Checkout without knowing that it also gets passed shoppingcarditems into it from outside ( here the main function). Only the main function knows the Checkout function. The CheckAge function doens’t know other functions.
fn stringUnion method data &out: =
(
out = method data
)
fn easyadd data =
(
s = ""
for d in data do s += d
s
)
fn camelcase data =
(
s = ""
for d in data do
(
d = tolower d
d[1] = toupper d[1]
s += d
)
s
)
/*
stringUnion easyadd #("dogs", "and", "cats", "are", "not", "friends") out:&s0
stringUnion camelcase #("dogs", "and", "cats", "are", "not", "friends") out:&s1
s0
s1
*/
struct Job1
(
data,
result,
fn action = (result = (data[1] + data[2]))
)
struct Job2
(
data,
result,
fn action = (result = (data[1] as string + " + " + data[2] as string + " = " + (data[1] + data[2]) as string))
)
fn doAJob job =
(
job.action()
ok
)
j1 = Job1 data:#(1,4)
doAJob j1
j1.result
j2 = Job2 data:#(1,4)
doAJob j2
j2.result
fn doAJobExt job data: =
(
if data != unsupplied do job.data = data
job.action()
ok
)
struct Job3
(
data,
result,
fn action =
(
act = if data[2] >= 0 then " + " else " - "
result = (data[1] as string + act + (abs data[2]) as string + " = " + (data[1] + data[2]) as string)
)
)
j3 = Job3 data:#(0,0)
doAJobExt j3
j3.result
doAJobExt j3 data:#(1.3, -0.8)
j3.result