0

我最近开始在 Logo 中编写非平凡的程序(在没有海龟图形的意义上非平凡)。我遇到的主要障碍之一是动态范围界定。例如考虑以下程序:

to foldl :f :acc :list [:index 1]
    output ifelse empty? :list [:acc] [
        (foldl :f (invoke :f :acc first :list :index) butfirst :list :index + 1)
    ]
end

to permute :list
    output ifelse empty? :list [[[]]] [
        foldl [[permutations item index]
            sentence :permutations map [[list]
                fput :item :list
            ] permute delete :index :list
        ] [] :list
    ]
end

permute函数适用于[]它产生输出的空列表和它产生输出[[]]的带有单个项目[a] 的列表[[a]]。但是,对于具有两个或更多元素的列表,它会失败。

猜猜为什么会失败?传递给foldlfrom的 lambda 函数permute访问了自由变量list,并且因为foldl还有一个名为它的局部变量list,所以它访问了错误的变量。因为foldl是递归定义的,所以list变量会随着每次迭代而不断缩小。

我通过在函数中保存原始列表的副本解决了这个问题,foldl如下所示:

to foldl :f :acc :list [:index 1] [:original :list]
    output ifelse empty? :list [:acc] [
        (foldl :f (invoke :f :acc first :list :index :original)
            butfirst :list :index + 1 :original)
    ]
end

to permute :list
    output ifelse empty? :list [[[]]] [
        foldl [[permutations item index list]
            sentence :permutations map [[list]
                fput :item :list
            ] permute delete :index :list
        ] [] :list
    ]
end

然而,我花了晚上的大部分时间才弄清楚是什么导致了这个奇怪的错误。我以前从未使用具有动态范围的语言进行编程(保存一小段 bash 脚本)。

因此我的问题如下:用具有动态作用域的语言编写函数时应该记住什么?最佳实践是什么?如何避免常见的陷阱?

4

2 回答 2

4

我会记住,这些语言没有闭包。

也许他们有,但有一个额外的结构(就像几十年前的一些 Lisp 语言)。更糟糕的是,有时解释器和编译器有不同的语义——就像几十年前的一些古老的 Lisp 方言一样。

Lisp 主要转向词法绑定是有原因的(Scheme 在 70 年代中期对其进行了探索,Common Lisp 在 80 年代中期得到了它,Emacs Lisp 刚刚获得了对它的支持。)。

基本上,如果您想做高级函数式编程,请远离动态范围的语言。

请改用 SML、Scheme、CL、Haskell、Racket、OCAML 等。

于 2013-10-04T18:40:03.613 回答
3

Minimize the use of free variables.

于 2013-10-04T17:54:35.510 回答