`cond`也是`let`?

时间:2019-05-09 16:02:13

标签: scheme lisp conditional-statements let

经常,我发现我想根据先前值的值有条件地计算一系列值。例如,我们将这些值称为xyz。首先,我计算x。如果x符合某些条件,则我将计算y的函数x,依此类推。

;; compute value x
;; if x =? #f -> #f
;; else compute value y = f(x)
;; if y =? #f -> #f
;; else compute value z = f(y)
;; et cetera

您如何在Scheme中做到这一点?我认为通常会使用cond,但是cond会丢弃测试结果,因此在这种情况下没有用。

3 个答案:

答案 0 :(得分:6)

使用let*,它在简短绑定的范围内顺序评估初始化形式。在初始化表格中,使用and使计算成为条件。

(let* ((x (compute-x))
       (y (and x (f1 x)))
       (z (and y (f2 y))))
  ;; code that uses the variables
)

答案 1 :(得分:3)

您可以在=>子句中使用cond设备,例如:

(define (testing p x)
   (if (p x) 
      x 
      #f))

(display
   (cond
      ((testing even? 1) => (lambda (x)          ; the clause is skipped
                              (list 10 x)))
      ((testing even? 2) => (lambda (x)          ; the clause is entered and
                              (list 20 x)))))    ;   `x` is bound to 2

您将在相应的子句中嵌套cond,以表示您描述的嵌套条件。

这样,代码结构显然遵循您的逻辑,这总是很好。

答案 2 :(得分:1)

因此,您的代码可以在标准Common Lisp中这样编写:

(let ((it x-expression))
  (if it
      (let ((it (f it)))
        (if it
            it))))

注意,我没有给出else形式(替代形式),因为它在CL中是可选的。 Paul Graham引入了Anaphoric macros,该方式可以自动缓存测试值。

(defmacro aif (test-form then-form &optional else-form)
  `(let ((it ,test-form))
     (if it ,then-form ,else-form)))

(aif x-expression
  (aif (f it)
    (aif (f it)
         it)

这在CL中效果很好,但在Scheme中存在卫生方面的问题。我知道有人为绑定名称创建了带有额外参数的内容,但这使它失去了优雅:

(aif x x-expression
  (aif y (f x)
    (aif z (f y)
         z)))

我一直在尝试cond,在创建评估程序时,我通常会在剥离之前进行测试,最后以iflet嵌套为例。我的第一次迭代看起来像:

(define (ev expr env)
  (defcond 
    ((symbol? expr) (symbol->value expr env))
    ((not (pair? expr)) expr => (operator (ev (car expr) env)))
    ((macro? operator) (macro-apply expr (cdr expr) env) => (args (map (lambda (e) (ev e env)) (cdr expr))))
    (else (fun-apply operator args env))))

它还支持另一种方法,因为我发现重用=>不太好:

(define (ev expr env)
  (defcond 
    ((symbol? expr) (symbol->value expr env))
    ((not (pair? expr)) expr)
    (define operator (ev (car expr) env))
    ((macro? operator) (macro-apply expr (cdr expr) env))
    (define args (map (lambda (e) (ev e env)) (cdr expr)))
    (else (fun-apply operator args env))))

现在,它也可以与aif相同的方式使用。如果您对Scheme宏感兴趣:

(define-syntax defcond
  (syntax-rules (else bind define =>)
    ((_ "build" terms ())
     terms)
    ((_ "build" alternative ((bind (b e) ...) . rest))
     (defcond "build" (let ((b e) ...) alternative) rest))
    ((_ "build" alternative ((bind name (b e) ...) . rest))
     (defcond "build" (let name ((b e) ...) alternative) rest))
    ((_ "build" alternative ((define b e) . rest))
     (defcond "build" (letrec ((b e)) alternative) rest))
    ((_ "build" alternative ((predicate consequent) . rest))
     (defcond "build" (if predicate consequent alternative) rest))
    ((_ "build" alternative ((predicate consequent => (b e) ...) . rest))
     (defcond "build" (if predicate consequent (let ((b e) ...) alternative)) rest))
    ((_ "build" alternative ((predicate consequent) . rest))
     (defcond "build" (if predicate consequent alternative) rest))
    ((_ "maybe-else" ((else expression) . rest))
     (defcond "build" expression rest))
    ((_ "maybe-else" ((something expression) . rest))
     (defcond "build" #f ((something expression) . rest)))
    ((_ "reverse" terms ())
     (defcond "maybe-else" terms))
    ((_ "reverse" (fterms ...) (term1 terms ...))
     (defcond "reverse" (term1 fterms ...) (terms ...)))
    ((_ terms ...)
     (defcond "reverse" () (terms ...)))))

不是很优雅的实现,但是可以。如您所见,它支持bind,并且也命名为bind。例如。

(defcond 
  ((not (pair? lst)) #f)
  (bind loop ((lst lst) (acc 0)))
  ((null? lst) acc)
  (else (loop (cdr lst) (+ acc (car lst))))

虽然我喜欢这个主意,但我仍然不认为它应该是神圣而优雅的。在出现更好的语法之前,我将为提高可读性而编写它。例如:

(if (not (pair? lst))
    #f
    (let loop ((lst lst) (acc 0))
      (if (null? lst)
          acc
          (loop (cdr lst) (+ acc (car lst))))))