您的位置:首页 > 其它

作者自述CSE语言设计思想(四)----用CSE模拟LISP语言(中)

2011-12-26 22:01 337 查看
first-class函数

在计算机科学中,一门拥有first-class函数的语言,是要求将函数看成first-class对象,具体来说,这意味着函数既可以作为参数传递给其它函数,也可以从其它函数返回,还可以将它赋值给一个变量,或保存到一个列表数据中。first-class可译为“首类”,它最早由Christopher Strachey在一篇“函数作为一等公民”(“functions as first-class citizens”)中提及,但这个词现在有点用滥了,甚至以讹传讹,网上不少文章都说“FP编程”是一等公民了。

前面我们举例的loop函数就表现了“first-class函数”特性,下面进一步举例CSE函数的“自身数据”与“计算值”含义。

$(as,vFunc,$(print,3)@0)


“@0”是CSE中一个比较特殊的操作符,“@0”含义为取表达式自身,“@1”是将表达式计算一次,“@2”计算两次,“@N”计算N次。上面我们取“$(print,3)”表达式自身赋给变量vFunc。然后计算“vFunc@1”我们将得到vFunc变量的值(即“$(print,3)”表达式自身),计算“vFunc@2”,print函数才被调用。

把上面print替换成lambda,我们会看到一个有趣的应用:

$(as,vFunc,$(lambda,i,$(+,i,1))@0)


把“用lambda定义一个匿名函数”的语句自身,赋给vFunc,然后使用它:

$(as,test,
$(lambda,v,$(v@2,3))
)


我们定义test函数,传入参数为v,如果这么调用:

$(test,vFunc)


您将看到结果值是4。因为test函数执行时,在计算“v@ 2”时CSE系统才用“$(lambda,i,$(+,i,1))”动态的定义一个函数,然后传入3调用它,3 + 1结果是4。

把上面的变量vFunc与test去掉,作等作替换,写成如下样子:

$($(lambda,v,$(v@2,3)),$(lambda,i,$(+,i,1))@0)


运行看看,计算结果是否为4?

让FP编程更加自然

有点晕了吧?FP编程的特点与人的思维习惯不是很吻合,我们常说,3加上4(即“3+4”),但不说“加上:3,4”(即“$(+,3,4)”),我们容易理解:先定义一个某某函数,再用这个函数调用,但用FP风格描述是:“调用:定义一个函数,传入参数”,很容易让人糊涂。

现在我用命令式风格,把上面语句复述一遍:

testas
lambda: v,
return v@2 (3);
end;

test(
lambda: i,
return i + 1;
end @ 0
);


第一条语句是定义一个test函数,第二条是调用test函数,传入参数是lambda定义匿名函数的表达式,注意:是表达式自身,而不是该表达式的计算结果(一个匿名函数)。

上一篇我们介绍过操作运算与函数调用具有等价性,为方便大家以FP风格使用CSE,我推荐大家这么写代码:

3+ 4
return 3 + 4
test(vFunc)


而不宜写成这样:

$(+,3,4)
$(return,$(+,3,4))
$(test,vFunc)


后一写法没错,但未迎合人们思考习惯,前一写法更加自然,且在CSE语言环境中,仍算作合格的FP编码风格。

还记得上一篇介绍过CSE的FP与Common Lisp的差异吗?CL用空格分隔各参数,CSE用逗号,CL用括号,CSE用“$()”,看似CSE繁琐一点,但换来上述更直观的表达形式。像“test(vFunc)”在CL中是非法的,CSE则用它表示常规函数调用。

高阶函数

Highter-order function是与First-order function相对存在的一个概念,前者是我们常说的高阶函数,后者我们可理解为普通函数。为澄清这个概念,我摘录wikipedia.org上的定义:

In mathematics and computer science, higher-orderfunctions, functional forms, or functionals are functions which do at least oneof the following:

1. take oneor more functions as an input

2. output a function

All other functions are first-order functions.

意思是说,以一个(或多个)函数用作传入参数,或者返回值是函数的函数,是Highter-orderfunction,所有其它称为first-orderfunctions。

除了前面已举例的几个例子,下面我再补充介绍FP编程中常用的reduce、filter、currying调用。

reduce用于依次运算参数列表,比如定义一个两数相加操作,计算[1,2,3,4]的过程为:

((1 + 2) + 3 ) + 4


我们先用CSE定义reduce函数:

reduceas lambda:
declare(Func,v,#_);
switch( #_.len(),
@(#_.insert(0,Func(v,(#_.pop(0)) @-1));
#_.insert(0,Func);
#_.insert(0,reduce@0);
#_.insert(0,`$()`@0);
setRet(#notator(#_));
);
setRet(#notator(PASS@0,v));
);
end;


然后调用:

$($(reduce,lambda:v1,v2, v1 + v2 end,1,2,3,4))


计算结果为10,定义reduce函数与前面举列过的loop类似,每次取前两个数值做运算,然后如果有下一次循环,就返回下轮reduce调用的算式,否则借用PASS调用返回结果值。

filter函数通过传入一个过滤条件,将指定列表的数据筛选一遍。

filteras lambda:
declare(Func,bList);
i as bList.len();
$($(loop, --i >= 0,
Func(bList[i]) or bList.pop(i);
));
return bList;
end;


这么调用filter:

filter(lambda:i, i <= 3 end,[1,2,3,4,5])


该调用的结果值为[1,2,3]。

curry也是众多FP语言支持的函数,它能依次组装函数参数,我们先看一下这个函数怎么用:

addingas lambda: i1,i2,i3, i1 + i2 + i3; end;
fill_0 as curry(adding);
fill_1 as fill_0(5);
fill_2 as fill_1(6);
fill_3 as fill_2(7);
result as fill_3();


我们用CSE的class类定义curry:

classcurry:
declare args as TObjArray;
declare fun as TCseObj;

func curry(me,vFunc):
me.fun = vFunc;
me.args = [];
end;

func `operator()`(me,vArg=CseNull) asTCseObj:
if CseNull != vArg:
me.args.append(vArg);
return me;
end else:
bFunc as [me.fun];
bFunc.join(me.args);
return #notator(bFunc) @ 2;
end;
end;
end;


利用operator()重载函数调用操作,每次函数调用传入一个参数后,都记录到args类成员中,最后一次不带参数调用时(即使用缺省参数CseNull),系统才构造运算表达式实施调用。

试验一下上述定义:

curry(lambda: i1,i2,i3, i1 + i2 + i3; end)(5)(6)(7)()


看看结果值是不是18。curry函数具有分散组装函数参数的功能,可用来构造某种并行计算机制。另外还有一种需求,为简化书写或维持某种一致的接口需要,我们可能要隐藏特定函数参数,这时,在CSE中可改用更简便的“列表调用”方式去实现。比如有一个remoteEval函数提供远程计算功能,要求传入两个参数:指定远程机器、指定待计算的表达式,常规调用如下:

remoteEval(MachineA,sExpression);


当大量存在这种让MachineA实现计算的调用时,我们改用如下方式更简洁些:

machineA_evalas [remoteEval,MachineA];
machineA_eval("dir()");
machineA_eval("3 + 5");


这里remoteEval是我们假设的函数,还不存在,我们改用print演示一下这种用法:

logMsgas [print,"log>"];
logMsg("It is so good!");

errMsg as [print,"err>"];
errMsg("It seems something wrong");


打印输出分别是“log>It is so good!”与“err> It seems something wrong”。

(未完,待续)

相关文章:

作者自述CSE语言设计思想(三)----用CSE模拟LISP语言(上)

作者自述CSE语言设计思想(五)----用CSE模拟LISP语言(下)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: