在递归调用Scheme期间,`let`创建的局部变量的状态是否会发生变化?

时间:2011-04-24 04:50:12

标签: scheme

例如,

我想检查一个元素是否在列表中。该算法很简单,让我们在C ++中做到这一点

bool element_of( const std::vector<int>& lst, int elem ) {
    for( int i( 0 ), ie = lst.size(); i < ie; ++i ) 
        if( elem == lst[i] )
            return true;
    return false;
}

由于Scheme不允许我使用单if语句,我不能做类似上面的C ++代码。然后我想出了一个临时变量,即resultresult的初始值为#f,接下来我递归调用函数来检查列表中的下一个项目,即cdr lst ...所以我的问题是,创建的变量是否为let每次进入新的函数调用时还原其初始值,或者它的值在最后一次调用之前保持不变?

另一方面,使用fold,我的解决方案是,

(define (element-of x lst)
  (fold (lambda (elem result)
          (if (eq? elem x) (or result #t) result)) 
        #f
        lst))

谢谢,

2 个答案:

答案 0 :(得分:2)

每个Let调用都会在Let的主体正在评估的环境中创建一组新的变量。Let语法是一个“语法糖”。 lambda通过传递给它的参数进行评估已经过评估。例如

(let ((a (func object))
      (b (func object2)))
     (cons a b))

与写作相同

((lambda (a b) (cons a b)) (func object) (func object2))

因此,您可以在Let语法中看到,首先评估参数,然后评估正文,并在本地使用ab的定义环境范围。因此,如果递归调用Let,每次进入Let调用的主体时,您正在新环境中评估主体(因为主体位于新定义的lambda内),并且定义在本地Let范围中定义的参数将是不同的(它们实际上是由新lambda设置的嵌套环境中的新变量,而不仅仅是已经变异或“重新定义”的变量,就像您在一个C ++循环)。

另一种说法是,你的变量将类似于C ++递归函数中的局部范围变量...对于每个函数的堆栈帧,本地范围的变量将有自己的定义,以及它们自己的内存location ...它们不是像在循环中看到的变异变量,它在本地范围内重用相同的内存变量。

希望这有帮助,

杰森

答案 1 :(得分:1)

let总是重新初始化变量;很明显,因为你必须始终提供新的绑定值。如,

(let ((a 42))
  ...)

...内,a始终为42。它不会“保留”以前调用的值。


顺便说一句,我认为你打算写(or result (equal? elem x))而不是(if (eq? elem x) (or result #t) result)。 : - )

(or result (equal? elem x))转换为以下C ++代码:

return result || elem == x;

(假设==运算符已被重载以执行equal?所执行的操作。)这样做的好处是,如果result已经为真,则无需进一步比较进行。