首页 > 解决方案 > 可以在 if/cond 中使用 let 吗?

问题描述

我经常发现自己想要使用列表中的当前元素,而不是总是需要在其上运行一个过程来获取它,例如使用elem而不是(car sequence)每次我想引用序列中的当前元素时。这有效......除非序列为空,所以我不能把它放在检查let之前null。这是我试图做的“模式”:

(define (filter sequence)
  ; return a list of only those items where the filter predicate returns true
  (cond ((null? sequence) nil)
        ; now that we know we have an active sequence, I want to short-hand 'elem' and 'rest'
        (let (elem car sequence) 
              (rest cdr sequence))
          (display elem)))

但后来我得到一个错误说let: bad syntax。有没有办法以某种方式完成上述模式?

Upate:也许最好的方法就是let通过简单的检查将 a 放在顶部if?就像是:

(define (filter sequence)
  (let ((elem (if (null? sequence) nil (car sequence)))
       (rest (cdr sequence)))
    (cond ((null? sequence) nil)
          (display elem))))
  

标签: schemelispracketsicp

解决方案


[这是我昨晚开始写的 coredump 答案的附录(我认为这是正确的答案),所以我会把它留给后代。]

从您的代码开始:

(define (filter sequence)
  (cond ((null? sequence) nil)
        (let (elem car sequence) 
              (rest cdr sequence))
          (display elem)))

这里至少存在三个问题:首先,您搞砸了 的语法cond,即(部分)

(cond (<test> <expr> ...)
      ...
      (else <expr> ...))

因此,要修复此问题,您需要添加一个显式else子句:

(define (filter sequence)
  (cond ((null? sequence) nil)
        (else
         (let (elem car sequence) 
           (rest cdr sequence))
         (display elem))))

现在的问题(这个问题的错误是cond由于某种原因掩盖了错误)是你搞砸了 的语法let,它是(再次,部分地):

(let ((<var> <val>) ...) <expr> ...)

您在这里犯了两个单独的错误:您错过了绑定表单周围的括号,并且表达式根本不在正文中let。所以,修复那些:

(define (filter sequence)
  (cond ((null? sequence) nil)
        (else
         (let ((elem car sequence)
               (rest cdr sequence))
           (display elem)))))

car我们现在得到了最终的错误:对&的调用没有括号cdr。所以修复那些:

(define (filter sequence)
  (cond ((null? sequence) nil)
        (else
         (let ((elem car sequence)
               (rest cdr sequence))
         (display elem))))

好吧,现在这仍然不正确,那是因为您错过了对carand的调用的括号cdr

(define (filter sequence)
  (cond ((null? sequence) nil)
        (else
         (let ((elem (car sequence))
               (rest (cdr sequence)))
           (display elem)))))

这现在有效,但它实际上并没有过滤序列。为此,它需要递归:

(define (filter sequence)
  (cond ((null? sequence) nil)
        (else
         (let ((elem (car sequence))
               (rest (cdr sequence)))
           (if (null? elem)
               (filter rest)
               (cons elem (filter rest)))))))

将内部条件提升到cond

(define (filter sequence)
  (cond ((null? sequence) nil)
        ((null (car sequence)) (filter (cdr sequence)))
        (else (cons (car sequence) (filter (cdr sequence))))))

尽管现在您遇到了多次调用car&cdr我认为您想避免的问题。

如果您使用完整的 Racket,一种很好的表达方式是使用匹配,它可以以一种很好的方式结合检查和绑定:

(define (filter sequence)
  (match sequence
    ['() '()]
    [(cons '() tail)
     (filter tail)]
    [(cons head tail)
     (cons head (filter tail))]))

推荐阅读