如何在函数式语言中实现循环

时间:2018-03-13 23:02:28

标签: functional-programming scheme lisp paradigms purely-functional

SchemeLisp等函数式语言中,存在forfor-all循环。但是for循环需要变异,因为它不是每次迭代的新堆栈帧。由于这些语言中没有明确的变异,这些函数式语言如何实现各自的迭代循环?

2 个答案:

答案 0 :(得分:4)

使用引擎下的递归实现Scheme循环;诸如do之类的构造只是被转换为递归过程的宏。例如,这个循环采用典型的过程语言:

void print(int n) {
    for (int i = 0; i < n; i++) {
        display(i);
    }
}

......相当于Scheme中的以下程序;在这里你可以看到循环的每个部分(初始化,退出条件,增量,正文)都有一个相应的表达式:

(define (print n)
  (define (loop i)     ; helper procedure, a "named let" would be better
    (when (< i n)      ; exit condition, if this is false the recursion ends
      (display i)      ; body
      (loop (+ i 1)))) ; increment
  (loop 0))            ; initialization

您是否注意到在调用递归后没有什么可做的?编译器非常智能,可以优化它以使用单个堆栈帧,从而有效地使其与for循环一样高效 - 有关更多详细信息,请阅读tail recursion。只是为了澄清一下,在Scheme中, 明确可用,请阅读set!指令。

答案 1 :(得分:2)

这个问题实际上是两个问题,而且是一个混乱。

Scheme

中的迭代

在Scheme中,迭代是通过递归和语言的语义一起实现的,该语言要求某些类型的递归不消耗内存,特别是尾递归。请注意,这并不意味着突变。因此,例如,这里是while循环i Racket的定义。

(define-syntax-rule (while test form ...)
  (let loop ([val test])
    (if val
        (begin
          form ...
          (loop test))
        (void))))

正如您所看到的,对loop的递归调用处于尾部位置,因此不会消耗内存。

传统Lisps中的迭代

传统的Lisp不要求尾部调用消除,因此需要迭代结构:这些通常由语言提供,但通常可以用较低级别的结构实现,例如GO TO。以下是Common Lisp中while的定义:

(defmacro while (test &body forms)
  (let ((lname (make-symbol "LOOP")))
    `(tagbody
      ,lname
      (if ,test
          (progn
            ,@forms
            (go ,lname))))))

关于突变的混淆

Scheme和传统Lisps都提供了变异操作符:你可能认为它们都不是纯函数式语言。 Scheme更接近于one,但它仍然不是很接近。