• Scheme r5rs letrec的用法


    说明,这是r5rs的用法. 

    (letrec ((<variable> <init>) ...) <body>) 

    假设((<variable> <init>) ...)是变量定义块V,<body>是执行块B.

    letrec最常见的用法就是用于绑定函数对象,让V里面定义的所有变量可以在运行时相互引用,不受位置前后的限制.比如:

    > (letrec ((x (lambda () (+ y y)))
             (y 100))
        (+ (x) y))
    300

    这说明运行(+ (x) y)时,函数对象x可以读取y对象的值,尽管y在x之后才绑定的. 这一点letrec很像顶层的运作模式:

    > (define x (lambda () (+ y y)))
    > (define y 100)
    > (+ (x) y)
    300

    只不过letrec创建的是一个本地作用域,而且语法上更简单.

    将letrec替换为let*或let将出错:

    > (let* ((x (lambda () (+ y y)))
             (y 100))
        (+ (x) y))
    . . y: undefined;
     cannot reference an identifier before its definition
    > (let ((x (lambda () (+ y y)))
             (y 100))
        (+ (x) y))
    . . y: undefined;
     cannot reference an identifier before its definition
    > 

    let*最多只能让靠后的variable引用靠前的variable.交换一下x,y的定义位置,就正常了:

    > (let* ((y 100)
             (x (lambda () (+ y y))))
        (+ (x) y))
    300

    而let限制更严格,各variable只能在body中被引用:

    > (let ((y 100)
            (x (lambda () (+ y y))))
        (+ (x) y))
    . . y: undefined;
     cannot reference an identifier before its definition

    当你表达式里含有一些相互递归的函数时,letrec非常合适.例如下面这个判断奇偶数的函数:

    > (letrec ((ieven?
                (lambda (n)
                  (if (zero? n)
                      #t
                      (iodd? (- n 1)))))
               (iodd?
                (lambda (n)
                  (if (zero? n)
                      #f
                      (ieven? (- n 1))))))
        (ieven? 3))  
    #f

    看起来letrec很强大的样子,那么,letrec的限制是什么呢?(准确说是r5rs的限制.Racket不存在这种限制)

    letrec要求<init>必须能够独立成值,否则letrec绑定就会出问题.以下摘自r5rs:

    One restriction on letrec is very important: it must be possible to evaluate each <init> without assigning or referring to the value of any <variable>.In the most common uses of letrec, all the <init>s are lambda expressions and the restriction is satisfied automatically.

     比如下面这个,b绑定不了2:

    > (letrec ((a 2)(b a)) b)
    #<undefined>

    对比顶层运作,不存在这种限制:

    > (define a 2)
    > (define b a)
    > b
    2

    那为什么lambda表达式能够自动地满足这个要求呢?

    因为一个lambda表达式是一个函数对象,它本身就是一个值.相当于100这种整数对象.

    Scheme不会在定义时严格检查lambda.比如里面的某变量是否已绑定对象,lambda被执行时才知道会不会出问题.

    > (lambda (n)(xxx? (- n 1)))
    #<procedure>
    > ((lambda (n)(xxx? (- n 1))) 3)
    . . xxx?: undefined;
     cannot reference undefined identifier
    > 

    那let*存在的意义是什么? 看这种情况:

    > (letrec ((a 2)(b a)) b)
    #<undefined>
    > (let* ((a 2)(b a)) b)
    2
    > 

    let*能让(b a)读取前面的定义(a 2),从而让b等于2.letrec就不行.

    而let对比let*限制更多,因此性能应该是更好的.在let和let*都能正常运行的时候,显然应该选择let.

    这应该就是let,let*和letrec各自存在的意义吧.

    注:方言Racket的letrec没有此限制.

    > (letrec ((a 2)(b a)) b)
    2
  • 相关阅读:
    2013软考最新大纲
    安卓的第二堂课
    安卓第一堂课
    VS2010的快捷键;VS2012变化的快捷键
    漂亮的表格样式大全
    ASCII说明和ASCII对照表
    Cookie学习笔记
    JavaScript实现功能全集
    1073-成绩排名
    1071-数字的空洞
  • 原文地址:https://www.cnblogs.com/xiangnan/p/3387146.html
Copyright © 2020-2023  润新知