zoukankan      html  css  js  c++  java
  • (译)Scheme简明教程7词法变量

    第五章 词法变量

    Scheme的变量有一定的词法作用域,即,它们在程序代码中只对特定范围的代码结构可见。迄今为止我们所见过的全局变量也没有例外的:它们的作用域是整个程序,这也是一种特定的作用范围。

    我们也碰见过一些示例包含局部变量。它们都是lambda过程的参数,当过程被调用时这些变量会被赋值,而它们的作用域仅限于在过程的内部。例如:

    (define x 9)
    (define add2 (lambda (x) (+ x 2)))
     
    x  =>  9
     
    (add2 3) =>  5
    (add2 x) =>  11
     
    x  =>  9

    这里有一个全局变量x,还有一个局部变量x,就是在过程add2中那个字母x。全局变量x的值一直是9。第一次调用add2过程时,局部的x会被赋值为3,而第二次调用add2时,局部变量x的会被赋值为全局变量x的值,即,9。当过程的调用结束时,全部变量x仍然是9

    set!代码结构可修改变量的赋值。

    (set! x 20)

    上面代码将全局变量x的值9修改为20,因为对于set!全局变量是可见的。如果set!是在add2过程体内被调用,那修改的就是局部变量x

    (define add2
      (lambda (x)
        (set! x (+ x 2))
        x))

    这里set!在局部变量x上加上2,并且会返回局部变量x的新值。(从结果来看,我们无法区分这个过程和先前的add2过程)。我们可以像先前一样使用全局的x做参数值来调用add2

    (add2 x) =>  22

    (记住全局变量x的值现在是20,而不是9!)

    add2过程内的set!调用仅会影响局部变量x。尽管局部变量x被赋了全局变量x的值,但后者不会因为set!为局部变量x赋值而受影响。

    x =>  20

    注意我们做这些讨论是因为我们为局部变量和全局变量使用了同样的标识x。在某些代码中,这个叫x的标识符指的是语法闭包中的局部x变量,这会暂时隐藏闭包外或全局变量x的值。例如,

    (define counter 0)
     
    (define bump-counter
      (lambda ()
        (set! counter (+ counter 1))
        counter))

    bump-counter是一个没有参数的过程(没有参数的过程也称作thunk). 它没有引入局部变量和参数,这样就不会隐藏任何值。在每次调用时,它会修改全局变量counter的值,让它增加1,然后返回它当前的值。下面是一些bump-counter的成功调用示例:

    (bump-counter) =>  1
    (bump-counter) =>  2
    (bump-counter) =>  3

    5.1          let let*

    并不是一定要显式的创建过程才可以创建局部变量。有个特殊的代码结构let可以创建一列局部变量以便在其结构体中使用:

    (let ((x 1)
          (y 2)
          (z 3))
      (list x y z))
    =>  (1 2 3)

    lambda一样,在let结构体中,局部变量x(赋值为1)会暂时隐藏全局变量x(赋值为20)。

    局部变量xyz分别被赋值为123,这个初始化的过程并不作为let过程结构体的一部分。因此,在初始化时对x的引用都指向了全局变量x,而不是局部变量x

    (let ((x 1)
          (y x))
      (+ x y))
    =>  21

    上面代码中,因为局部变量x被赋值为1,而y被赋上了值为20的全局变量x

    有时候,用let依次的创建局变量非常的方便,如果在初始化区域中可以用先创建的变量来为后创建的变量赋值也会非常方便。let*结构就可以这样做:

    (let* ((x 1)
           (y x))
      (+ x y))
    =>  2

    在初始化y变量时的x,指的是前面刚创建好的变量x。这个例子完全等价于下面这个let嵌套的程序,更深了说,实际上就是let嵌套的缩写。

    (let ((x 1))
      (let ((y x))
        (+ x y)))
    =>  2

    我们也可以把一个过程做为值赋给变量:

    (let ((cons (lambda (x y) (+ x y))))
      (cons 1 2))
    =>  3

    在这个let构结体中,变量cons将它的参数进行相加。而在let结构的外面,cons还是用来创建点值对。

    5.2          fluid-let

    一个词法变量如果没有被隐藏,在它的作用域内一直都为可见状态。有时候,我们有必要装一个词法变量临时的设置为一个固定的值。为此我们可使用fluid-let结构(fluid-let是一个非标准的特殊结构。可参见8.3,在Scheme中定义fluid-let)

    (fluid-let ((counter 99))
      (display (bump-counter)) (newline)
      (display (bump-counter)) (newline)
      (display (bump-counter)) (newline))

    这和let看起来非常相像,但并不是暂时的隐藏了全局变量counter的值,而是在fluid-let执行体中临时的将全局变量counter的值设置为了99直到执行体结束。因此执行体中的三句display产生了结果

    100 
    101 
    102 

    fluid-let表达式计算结束后,全局变量counter会恢复成之前的的值。

    counter =>  3

    注意fluid-letlet的效果完全不同。fluid-let不会和let一样产生一个新的变量。它会修改已经存的变量的值绑定,当fluid-let结束时这个修改也会结束。

    为了清楚的说明这一些,可以思考这个根据前一个示例用let替换fluid-let后的程序。这次的输出是

    4
    5
    6

    即,初始值为3的全局变量counter,被每一次bump-counter的调用更新。而新创建的初始值为99的词法变量counter并没有影响到bump-counter的执行,因为尽管bump-counter是在局部变量counter的作用域内被调用的,但bump-counter的结构体并不在这个作用域内。所以bump-counter中的counter仍然指的是全局变量counter,最后的值为6

    counter =>  6

     

  • 相关阅读:
    [转]Linq to SQL中的实体继承
    [转贴][WCF Security] 3. X509 身份验证
    [转贴]十大网站设计错误
    [转载].NET设计模式(1):开篇
    [转自JeffreyZhao]不妨来做个尝试:UpdatePanel for ASP.NET MVC
    [转]如何快速生成100万不重复的8位均匀分布的随机编号?
    [转贴]X.509 & RSA
    [c#]Webservice中如何实现方法重载(overload)以及如何传送不能序列化的对象作参数
    (Head First 设计模式)学习笔记(2) 观察者模式(气象站实例)
    (Head First 设计模式)学习笔记(1)
  • 原文地址:https://www.cnblogs.com/heros/p/1626464.html
Copyright © 2011-2022 走看看