吾生有涯 学海无涯
析模有界 知识无界

【Fluent GUI】05:Scheme[下]

内容纲要

以下内容来自https://www.ibm.com/developerworks/cn/linux/l-schm/index2.html。


1

常用结构

1.1 顺序结构
也可以说成由多个form组成的form,用begin来将多个form放在一对小括号内,最终形成一个form。格式为:(begin form1 form2 …)

如用Scheme语言写成的经典的helloworld程序是如下样子的:

1.2 if结构
Scheme语言的if结构有两种格式,一种格式为:(if 测试 过程1 过程2),即测试条件成立则执行过程1,否则执行过程2。例如下面代码:

根据类型判断来实现自省功能,下面代码判断给定的参数是否为字符串:

如执行 (fun 123) 则返回值为"not a string",这样的功能在C++或JAVA中实现的话可能会很费力气。

1.3 cond结构

Scheme语言中的cond结构类似于C语言中的switch结构,cond的格式为:

(cond ((测试) 操作) … (else 操作))

如下是在Guile中的操作:

上面程序代码中,我们定义了过程w,它有一个参数x,如果x的值大于0,则返回符号upper,如x的值小于0则返回符号lower,如x 的值为0则返回符号equal。

cond可以用if形式来写,上面的过程可以如下定义:

这在功能上是和cond一样的,可以看出cond实际上是实现了if的一种多重嵌套。

1.4 case结构
case结构和cond结构有点类似,它的格式为:

(case (表达式) ((值) 操作))    ... (else 操作)))

case结构中的值可以是复合类型数据,如列表,向量表等,只要列表中含有表达式的这个结果,则进行相应的操作,如下面的代码:

上面的例子返回结果是composite,因为列表(1 4 6 8 9)中含有表达式(* 2 3)的结果6;下面是在Guile中定义的func过程,用到了case结构:

1.5 and结构
and结构与逻辑与运算操作类似,and后可以有多个参数,只有它后面的参数的表达式的值都为#t时,它的返回值才为#t,否则为#f。看下面的操作:

如果表达式的值都不是boolean型的话,返回最后一个表达式的值,如下面的操作:

1.6 or结构
or结构与逻辑或运算操作类似,or后可以有多个参数,只要其中有一个参数的表达式值为#t,其结果就为#t,只有全为#f时其结果才为#f。如下面的操作:

我们还可以用and和or结构来实现较复杂的判断表达式,如在C语言中的表达式:

((x > 100) && (y < 100)) 和 ((x > 100) || (y > 100))

在Scheme中可以表示为:

Scheme语言中只有if结构是系统原始提供的,其它的cond,case,and,or,另外还有do,when,unless等都是可以用宏定义的方式来定义的,这一点充分体现了Scheme的元语言特性。

2

递归调用

2.1 用递归实现阶乘
在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。

2.2 循环的实现
Scheme语言中循环采用do来实现,如:
(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。相信读者朋友一定会写出更漂亮更实用的循环操作来的。

3

变量和过程的绑定

3.1 let,let*,letrec
在多数编程语言中都有关于变量的存在的时限问题,Scheme语言中用let,let*和letrec来确定变量的存在的时限问题,即局部变量和全局变量,一般情况下,全局变量都用define来定义,并放在过程代码的外部;而局部变量则用let等绑定到过程内部使用。

用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,也成了理解它的一个难点。

3.2 apply
apply的功能是为数据赋予某一操作过程,它的第一个参数必需是一个过程,随后的其它参数必需是列表,如:

以上定义了求和过程sum和求平均的过程avg,其中求和的过程sum中用到了apply来绑定"+"过程操作到列表,结果返回列表中所有数的总和。

3.3 map
map的功能和apply有些相似,它的第一个参数也必需是一个过程,随后的参数必需是多个列表,返回的结果是此过程来操作列表后的值,如下面的操作:

除了apply,map以外,Scheme语言中还有很多,诸如:eval,delay,for-each,force,call-with-current-continuation等过程绑定的操作定义,它们都无一例外的提供了相当灵活的数据处理能力,也就是另初学者望而生畏的算法,当你仔细的体会了运算过程中用到的简直妙不可言的算法后,你就会发现Scheme语言设计者的思想是多么伟大。

END


本篇文章来源于微信公众号: CFD之道

赞(1) 打赏
版权声明:未经允许,请勿随意用于商业用途。
文章名称:《【Fluent GUI】05:Scheme[下]》
文章链接:https://www.topcfd.cn/6039/
本站资源仅供个人学习交流,请于下载后24小时内删除,不允许用于商业用途,否则法律问题自行承担。
分享到

说两句 抢沙发

评论前必须登录!

 

觉得文章有用就打赏一下文章作者吧

非常感谢你的打赏,我们将继续给力更多优质内容,让我们一起创建更加美好的网络世界!

支付宝扫一扫

微信扫一扫

登录

找回密码

注册