无法在 Racket 中使用 let

Not able to use let in Racket

我正在尝试修改 http://home.adelphi.edu/~siegfried/cs270/270rl10.html 上的排序代码,其中我使用 let 作为插入函数:

(define (mysort alon ) 
  (let insert ((n n) (alon  alon)) 
    (cond 
      [(empty? alon) (cons n empty)] 
      [else (cond 
              [(< n (first alon)) (cons n alon)] 
              [else (cons (first alon) 
                          (insert n (rest alon))])])
  (cond 
    [(empty? alon) empty] 
    [(cons? alon) (insert (first alon) 
                           (mysort (rest alon)))])))

(mysort (list 1 2 3 4 5 6 2 3 1 4 5 2 10))

但是,它在 'let' 变量声明级别不起作用:

n: unbound identifier in module in: n

我在这里 (https://docs.racket-lang.org/reference/let.html) 看到 'let' 需要有变量的初始值。我们可以在不初始化变量的情况下使用 'let' 吗?如何更正以上代码?


编辑:我尝试使用 lambda 但它不起作用:

(define (mysort4 alon ) 
  (let ([insert4
         (lambda (n alon) 
           (cond 
             [(empty? alon) (cons n empty)]
             [(< n (first alon)) (cons n alon)] 
             [else (cons (first alon) 
                         (insert4 n (rest alon) ))]))])
    (cond 
      [(empty? alon) empty] 
      [(cons? alon) (insert4 (first alon) 
                             (mysort4 (rest alon) ) )])))

(mysort4 (list 1 2 3 4 5 6 2 3 1 4 5 2 10))

错误是:

insert4: unbound identifier in module in: insert4

当您需要内部辅助函数时,请使用内部定义而不是 let。 通过最小的更改(使用 define 而不是 let),您将获得:

#lang racket
(define (mysort alon )
  (define (insert n alon)
    (cond 
      [(empty? alon) (cons n empty)]
      [else          (cond 
                       [(< n (first alon)) (cons n alon)] 
                       [else               (cons (first alon) 
                                                 (insert n (rest alon)))])]))
  (cond 
    [(empty? alon) empty]
    [(cons?  alon) (insert (first alon) 
                           (mysort (rest alon)))]))

(mysort (list 1 2 3 4 5 6 2 3 1 4 5 2 10))

当您使用 let

创建内容时
(define test 10)

(let ((test (lambda (x) 
              (list x test))))
  (test 'result))
; ==> (result 10)

显然 lambda 内部的 test 是全局的而不是 "itself",但为什么。 let 只是立即调用的匿名函数的语法糖,因此我们可以将其重写为:

(define test 10)
((lambda (test)
   (test 'result))
 (lambda (x) 
   (list x test)))

在这里您看到第一个 lambda 具有 test 作为绑定变量,因此始终是第一个操作数,但第二个 lambda 没有 test 除了全局绑定。

在递归过程中不能使用 let,因为在创建闭包时绑定不在环境中(计算 lambda)。为了解决这个问题,我们使用 letrec 解决了这个问题:

(define test 10)    
(letrec ((test (lambda (x) 
              (list x test))))
  (test 'result))
; ==> (result #<procedure:test>)

要看为什么可以展开这个表格看看:

(let ((test 'undefined)) 
  (let ((newtemp (lambda (x) (list x test)))) 
    (set! test newtemp)) 
  (test 'result))))
; ==> (result #<procedure:test>)

我不会在这里展开 let 形式,但请注意在创建 lambda 时 test 存在的事实。 lambda 成为值,但绑定是相同的。

不是顶层的

define 被重写*为 letrec 因此下面的代码完全相同:

(let () ; this is to make `define` local and not global
  (define test (lambda (x) 
                 (list x test))
  (test 10))
; ==> (result #<procedure:test>)

在命名的 let 中,名称绑定在 letrec 中,但其他值不是。要修复第一个:

(define (mysort alon)   
  (cond 
    [(empty? alon) empty] 
    [(cons? alon)
     (let insert ((n (first alon))
                  (alon  (rest alon))) 
       (cond 
         [(empty? alon) (cons n empty)] 
         [(< n (first alon)) (cons n alon)] 
         [else (cons (first alon) (insert n (rest alon)))]))]))

请注意,我将嵌套的 cond 展平,因为 cond 的重点是不必嵌套它们。它等同于其他语言中的 if-elseif*-else。该代码不起作用,因为它只放置了第一个元素。也许将所有元素插入一个以空列表开头的列表中是可行的。

第二个只需将 let 更改为 letrec 即可。该代码与相同的功能相同,它只根据其余部分插入第一个元素,但递归将适用于该元素。

如果您查看链接到的页面,您会发现您 insert 进入的列表已经排序。 IE。你在那里错过了一些东西..

insertion-sort 不是一种有效的算法。 #lang racket 对较小的列表使用合并排序和调整。试图在速度上击败它是徒劳的。

* 反之亦然。球拍使用letrec(准确地说是letrec-values