binding - 如何刷新、重制 lambda 上的词法绑定(bind)?

标签 binding scheme lisp racket

我正在尝试了解如何重新绑定(bind)词法绑定(bind),或者重新定义 lambda 的闭包。 next-noun 的预期用法只是在不带参数的情况下根据需要多次调用它。它应该从列表中返回一个随机名词,但在列表耗尽之前尚未返回。

这是我正在使用的玩具示例:

#lang racket

(define nouns `(time
                year
                people
                way
                day
                man))

(define (next-noun)
  (let* ([lst-nouns (shuffle nouns)]
         [func-syn 
          `(λ ()
             (let* ([n (car lst-nouns)]
                    [lst-nouns (if (null? (cdr lst-nouns))
                                   (shuffle nouns)
                                   (cdr lst-nouns))])
               (set! next-noun (eval func-syn))
               n))])
    ((eval func-syn))))

当尝试运行它时,我收到此错误:

main.rkt> 
main.rkt> (next-noun)
; lst-nouns: undefined;
;  cannot reference an identifier before its definition
;   in module: "/home/joel/projects/racket/ad_lib/main.rkt"

这让我很困惑,因为 lst 名词 any 应该有一个绑定(bind) time (eval func-syn) 运行。这是怎么回事?

最佳答案

您根本不需要在这里使用eval。它使解决方案比所需的更加复杂(和 insecure )。此外,“循环”逻辑是不正确的,因为您没有更新 lst-nouns 中的位置,而且每次调用过程时它都会被重新定义。另请参阅link由 Sorawee 分享,以了解为什么 eval 无法看到本地绑定(bind)。

在Scheme中,我们尽可能避免改变状态,但对于这个过程,我认为这是合理的。诀窍是将需要更新的状态保留在闭包内;这是一种方法:

(define nouns '(time
                year
                people
                way
                day
                man))

; notice that `next-noun` gets bound to a `lambda`
; and that `lst-nouns` was defined outside of it
; so it's the same for all procedure invocations
(define next-noun
  ; store list position in a closure outside lambda
  (let ((lst-nouns '()))
    ; define `next-noun` as a no-args procedure
    (λ ()
      ; if list is empty, reset with shuffled original
      (when (null? lst-nouns)
        (set! lst-nouns (shuffle nouns)))
      ; obtain current element
      (let ((noun (car lst-nouns)))
        ; advance to next element
        (set! lst-nouns (cdr lst-nouns))
        ; return current element
        noun))))

@PetSerAl 在评论中提出了更惯用的解决方案。我的猜测是,出于学习目的,您想从头开始实现这一点 - 但在现实生活中,我们会使用 Racket 的 generators 来做类似的事情。 :

(require racket/generator)

(define next-noun
  (infinite-generator
   (for-each yield (shuffle nouns))))

无论哪种方式,它都会按预期工作 - 重复调用 next-noun 将返回 nouns 中的所有元素,直到耗尽,此时列表将重新洗牌并进行迭代将重新启动:

(next-noun)
=> 'day
(next-noun)
=> 'time
...

关于binding - 如何刷新、重制 lambda 上的词法绑定(bind)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54384930/

相关文章:

c# - 绑定(bind)到资源 key ,WPF

c# - 如何将一个对象的成员绑定(bind)到数据网格

scheme - 是什么原因导致Scheme中出现未绑定(bind)变量错误?

scheme - 用 Racket 输入的最佳方法?

.net - 如何选择 WCF 绑定(bind)?

c# - 将 WPF DataGrid 绑定(bind)到 DataTable

scheme - PLT方案: Converting one of the macros in 'Casting SPELs in LISP'

types - 我的快速排序不适用于负数 (Common Lisp)

Emacs - slime - 将当前函数保存到文件

lisp - 约束满足问题中的启发式是否确保没有回溯? (当存在解决方案时)