如用Scheme语言写成的经典的helloworld程序是如下样子的:
根据类型判断来实现自省功能,下面代码判断给定的参数是否为字符串:
如执行 (fun 123) 则返回值为"not a string",这样的功能在C++或JAVA中实现的话可能会很费力气。
Scheme语言中的cond结构类似于C语言中的switch结构,cond的格式为:
(cond ((测试) 操作) … (else 操作))
如下是在Guile中的操作:
上面程序代码中,我们定义了过程w,它有一个参数x,如果x的值大于0,则返回符号upper,如x的值小于0则返回符号lower,如x 的值为0则返回符号equal。
cond可以用if形式来写,上面的过程可以如下定义:
这在功能上是和cond一样的,可以看出cond实际上是实现了if的一种多重嵌套。
(case (表达式) ((值) 操作)) ... (else 操作)))
case结构中的值可以是复合类型数据,如列表,向量表等,只要列表中含有表达式的这个结果,则进行相应的操作,如下面的代码:
上面的例子返回结果是composite,因为列表(1 4 6 8 9)中含有表达式(* 2 3)的结果6;下面是在Guile中定义的func过程,用到了case结构:
如果表达式的值都不是boolean型的话,返回最后一个表达式的值,如下面的操作:
我们还可以用and和or结构来实现较复杂的判断表达式,如在C语言中的表达式:
((x > 100) && (y <
100
)) 和 ((x > 100) || (y > 100))
在Scheme中可以表示为:
Scheme语言中只有if结构是系统原始提供的,其它的cond,case,and,or,另外还有do,when,unless等都是可以用宏定义的方式来定义的,这一点充分体现了Scheme的元语言特性。
(define factoral (lambda (x)
(if (<= x 1) 1
(* x (factoral (- x 1))))))
我们可以将下面的调用(factoral 4),即4的阶乘的运算过程图示如下:
以下为factoral过程在Guile中的运行情况:
> (define factoral (lambda (x) (if (<= x 1) 1 (* x (factoral (- x 1))))))
> factoral
#
> (factoral 4)
24
下面是一另一种递归方式的定义:
(define (factoral n)
(define (iter product counter)
(if (> counter n)
product
(iter (* counter product) (+ counter 1))))
(iter 1 1))
(display (factoral 4))
这个定义的功能和上面的完全相同,只是实现的方法不一样了,我们在过程内部实现了一个过程iter,它用counter参数来计数,调用时从1开始累计,这样它的展开过程正好和我们上面的递归过程的从4到1相反,而是从1到4。
(do ((vec (make-vector 5))
(i 0 (+ i 1)))
((= i 5) vec)
(vector-set! vec i i))
循环结构可以用递归来很轻松的实现(在Scheme语言中只有通过递归才能实现循环)。对于用惯了C语言循环的朋友,在Scheme中可以用递归简单实现:
(define loop
(lambda(x y)
(when (<= x y)
(begin (display x) (display #space) (set! x (+ x 1))
(loop x y)))))
这只是一种简单的循环定义,过程有两个参数,第一个参数是循环的初始值,第二个参数是循环终止值,每次增加1。相信读者朋友一定会写出更漂亮更实用的循环操作来的。
用let可以将变量或过程绑定在过程的内部,即实现局部变量。下面的代码显示了let的用法(注意多了一层括号):
它的格式是:(let ((…)…) …),下面是稍复杂的用法:
以上是Scheme中的代码实现情况。它的实现过程大致是:(foo 8) 展开后形成 (bar 5 8),再展开后形成 (+ (* 5 8) 5) ,最后其值为45。
再看下面的操作:
let的绑定在过程内有效,过程外则无效,这和上面提到的过程的嵌套定是一样的,上面的iszero?过程在操作过程内定义并使用的,操作结束后再另行引用则无效,显示过程未定义出错信息。
下面操作演示了let*的用法:
还有letrec,看下面的操作过程:
上面的操作过程中,内部定义了两个判断过程even?和odd?,这两个过程是互相递归引用的,如果将letrec换成let或let*都会不正常,因为letrec是将内部定义的过程或变量间进行相互引用的。看下面的操作:
letrec帮助局部过程实现递归的操作,这不仅在letrec绑定的过程内,而且还包括所有初始化的东西,这使得在编写较复杂的过程中经常用到letrec,也成了理解它的一个难点。
以上定义了求和过程sum和求平均的过程avg,其中求和的过程sum中用到了apply来绑定"+"过程操作到列表,结果返回列表中所有数的总和。
除了apply,map以外,Scheme语言中还有很多,诸如:eval,delay,for-each,force,call-with-current-continuation等过程绑定的操作定义,它们都无一例外的提供了相当灵活的数据处理能力,也就是另初学者望而生畏的算法,当你仔细的体会了运算过程中用到的简直妙不可言的算法后,你就会发现Scheme语言设计者的思想是多么伟大。
END
本篇文章来源于微信公众号: CFD之道
评论前必须登录!
注册