文档章节

【Scheme归纳】6 赋值

NoMasp
 NoMasp
发布于 2015/09/08 21:45
字数 1487
阅读 37
收藏 1

赋值

因为Scheme是函数式语言,通常来说,你可以编写不使用赋值的语句。然后如果使用赋值的话,有些算法就可以轻易实现了。尤其是内部状态和继续(continuations)需要赋值。

R5RS中规定的用于赋值的特殊形式是set!,set-car!,set-cdr!,string-set!,vector-set!等。

因为赋值改变了参数的值,因此它具有破坏性(destructive)。
在Scheme中,具有破坏性的方法都以!结尾,以警示程序员。

set!可以为一个参数赋值。与Common Lisp不同,set!无法给一个S-表达式赋值。另外,在赋值前参数应该被定义。例如:

(define num 10) ;Value: num (set! num (* num num)) ;Value: 100 (let ((n 0)) (set!n (+ n 3)) n) ;Value: 3

赋值和内部状态

Scheme中变量的作用被限定在了源码中定义其的那个括号里。作用域与源代码书写方式一致的作用域称为“词法闭包(Lexical closure)”或“静态作用域(Static scope)”。

另一方面,还有一种被称为“动态作用域(Dynamic scope)”的作用域。这种作用域仅在程序运行时确定。但由于会在调试时带来种种问题,这种作用域现在已经不再使用。

特殊形式let,lambda,letrec生成闭包。lambda表达式的参数仅在函数定义内部有效。let只是lambda的语法糖,因此二者无异。

你可以使用词法闭包来实现带有内部状态的过程。我们通过一个简单的银行账户的例子来展示。

(define bank-account (let ((balance 100)) (lambda (n) (set!balance (+ balance n)) balance)))

该过程将存款赋值为(+ balance n),下面是调用这个过程的结果。

(bank-account 100)
;Value: 200
(bank-account -50)
;Value: 150

因为在Scheme中,你可以编写返回过程的过程,因此你可以编写一个创建银行账号的函数。这个例子预示着使用函数式程序设计语言可以很容易实现面向对象程序设计语言。

(define (make-bank-account balance) (lambda(n) (set!balance (+ balance n)) balance))
(define bill-bank-account (make-account 100))
;Value: bill-bank-account
(bill-bank-account 50)
;Value: 150
(bill-bank-account -100 ;Value: 50 (define nomasp-bank-account (make-bank-account200)) ;Value: nomasp-bank-account (nomasp-bank-account -50) ;Value: 150 (nomasp-bank-account 250) ;Value: 400

副作用

Scheme过程的主要目的是返回一个值,而另一个目的则称为副作用(Side Effect)。赋值和IO操作就是副作用。

表的破坏性操作(set-car!,set-cdr!)
函数set-car!和set-cdr!分别为一个cons单元的car部分和cdr部分赋新值。和set!不同,这两个操作都可以为S-表达式赋值。

(define list1 ‘((1 2) (3 4 5) (6 7 8 9)))
;Value: list1
(set-cdr! (third list1) ‘(a b c))
;Unspecified return value
list1
;Value: ((1 2) (3 4 5) (6 a b c))

队列

队列可以用set-car!和set-cdr!实现。队列是一种先进先出(First in first out, FIFO)的数据结构,表则是先进后出(Firstin last out, FILO)。
队列的Scheme实现

(define (make-queue)
 (cons'() '()))

(define (enqueue! queue obj)
 (let((lobj (cons obj '())))
   (if(null? (car queue))
      (begin
        (set-car! queue lobj)
        (set-cdr! queue lobj))
      (begin
        (set-cdr! (cdr queue) lobj)
        (set-cdr! queue lobj)))
   (carqueue)))

(define (dequeue! queue)
 (let((obj (car (car queue))))
   (set-car! queue (cdr (car queue)))
   obj))
(define q (make-queue))
;Value: q

(enqueue! q 'a)
;Value 12: (a)

(enqueue! q 'b)
;Value 12: (a b)

(enqueue! q 'c)
;Value 12: (a b c)

(dequeue! q)
;Value: a

q
;Value 13: ((b c) c)


定义语法,宏

用户定义语法称作宏(Macro)。Lisp/Scheme中的宏比C语言中的宏更加强大。宏可以使你的程序优美而紧凑。

宏是代码的变换。代码在被求值或编译前进行变换,and the procedure continues as if the transformed codes are writtenfrom the beginning.

你可以在Scheme中通过用符合R5RS规范的syntax-rules轻易地定义简单宏,相比之下,在Common Lisp中自定义语法就复杂多了。使用syntax-rules可以直接定义宏而不用担心变量的捕获(Variable Capture)。On the other hand,defining complicated macros that cannot be defined using the syntax-rules ismore difficult than that of the Common Lisp.

我将以一个简单的宏作为例子。
[代码片段 1]一个将变量赋值为’()的宏

(define-syntax nil! (syntax-rules () ((_x) (set! x '()))))

syntax-reuls的第二个参数由是变换前表达式构成的表。_代表宏的名字。简言之,代码片段1表示表达式(nil! x)会变换为(set!x ‘()).

这类过程不能通过函数来实现,这是因为函数的闭包性质限制它不能影响外部变量。让我们来用函数实现代码片段1,并观察效果。

(define (f-nil! x)
  (set!x '()))
(define a 1)
;Value: a

(f-nil! a)
;Value: 1

a
;Value: 1          ; the value of a dose not change

(nil! a)
;Value: 1

a
;Value: ()         ; a becomes '()

我会演示另外一个例子。我们编写宏when,其语义为:当谓词求值为真时,求值相应语句。

(define-syntax when
 (syntax-rules ()
   ((_pred b1 ...)
    (ifpred (begin b1 ...)))))

代码片段2中的…代表了任意多个数的表达式(包括0个表达式)。代码片段2揭示了诸如表达式(whenpred b1 …)会变换为(if pred (begin b1 …))。
由于这个宏是将表达式变换为if特殊形式,因此它不能使用函数来实现。下面的例子演示了如何使用when。

(let ((i 0))
 (when(= i 0)
   (display "i == 0")
   (newline)))
i == 0
;Unspecified return value

我会演示两个实宏:while和for。只要谓词部分求值为真,while就会对语句体求值。而数字在指定的范围中,for就会对语句体求值。

(define-syntax while
 (syntax-rules ()
   ((_pred b1 ...)
    (let loop () (when pred b1 ... (loop))))))


(define-syntax for
 (syntax-rules ()
   ((_(i from to) b1 ...)
    (let loop((i from))
      (when (< i to)
        b1 ...
        (loop (1+ i)))))))

下面演示了如何实用它们:

(define-syntax while
 (syntax-rules ()
   ((_pred b1 ...)
    (let loop () (when pred b1 ... (loop))))))


(define-syntax for
 (syntax-rules ()
   ((_(i from to) b1 ...)
    (let loop((i from))
      (when (< i to)
        b1 ...
        (loop (1+ i)))))))



感谢访问,希望对您有所帮助。 欢迎关注或收藏、评论或点赞。


为使本文得到斧正和提问,转载请注明出处:
http://blog.csdn.net/nomasp


版权声明:本文为 NoMasp柯于旺 原创文章,未经许可严禁转载!欢迎访问我的博客:http://blog.csdn.net/nomasp

本文转载自:http://blog.csdn.net/nomasp/article/details/44221255

NoMasp
粉丝 7
博文 334
码字总数 0
作品 0
镇江
程序员
私信 提问
Common Lisp 和 Scheme 的函数命名空间不同

最近在 <> 看到 Common Lisp 和 Scheme 的不同: 1. 在Common Lisp眼中,一个符号的symbol-value和symbol-function是不一样的,而Scheme 对两者不作区分。在Scheme里面,变量只有唯一对应的值...

沙枣
2013/08/18
1K
4
Swift:在Safari中打开App

Swift:在Safari中打开App 打开之前会发生什么呢,先看看这个图: 我这里只是简单模拟了一下。当你输入一个特殊的“url”之后,Safari弹出一个提示,问你是否继续打开这个App。如果你这个时候...

微笑的江豚
2016/08/08
32
0
人人都能学会的python编程教程6:列表(list)

上期编程题的答案如上图。 列表(list) list是一种有序的集合,可以随时添加和删除其中的元素。 当索引超出了范围时,Python会报一个IndexError错误,所以,要确保索引不要越界,记得最后一...

编程老司机
2018/05/10
0
0
编程语言--Scheme

Scheme语言是LISP语言的一个方言(或说成变种),它诞生于1975年的MIT,对于这个有近三十年历史的编程语言来说,它并没有象 C++,java,C#那样受到商业领域的青睐,在国内更是鲜为人知。但它在...

匿名
2009/03/25
17.2K
1
nomasp 博客导读:Lisp/Emacs、Algorithm、Android

版权声明:转载请联系本人,感谢配合!本站地址:http://blog.csdn.net/nomasp https://blog.csdn.net/NoMasp/article/details/44966625 Profile Introduction to Blog 您能看到这篇博客导读...

nomasp
2015/09/17
0
0

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周五乱弹 —— 你已经是个成熟的熊猫了

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @Sharon啊 :#今日歌曲推荐# 分享黑鸭子的单曲《羞答答的玫瑰静悄悄的开》 《羞答答的玫瑰静悄悄的开》- 黑鸭子 手机党少年们想听歌,请使劲儿...

小小编辑
42分钟前
216
6
结合Spring Security进行web应用会话安全管理

在本文中,将为大家说明如何结合Spring Security 和Spring Session管理web应用的会话。 一、Spring Security创建使用session的方法 Spring Security提供4种方式精确的控制会话的创建: alwa...

fightinging
47分钟前
4
0
83、Mybatis和Hibernate重要区别

Mybatis;入门简单,程序容易上手开发,节省开发成本。Mybatis需要程序猿自己编写sql语句,是一个不完全的ORM框架,对sql修改和优化非常容易实现。 Mybatis适合开发需求变更频繁的系统,比如...

lianbang_W
今天
6
0
设计模式之状态模式

定义 Allow an object to alter its behavior when its internal state changes.The object will appear to change its class.(当一个对象内在状态改变时允许其改变行为,这个对象看起来像改...

陈年之后是青葱
今天
6
0
Python常用模块之os.path

os.path.abspath(path) 输入相对路径,返回绝对路径 Python 3.7.0 (v3.7.0:1bf9cc5093, Jun 27 2018, 04:59:51) [MSC v.1914 64 bit (AMD64)] on win32Type "copyright", "credits" or "lic......

松鼠大帝
今天
11
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部