经常,我发现我想根据先前值的值有条件地计算一系列值。例如,我们将这些值称为x
,y
和z
。首先,我计算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
会丢弃测试结果,因此在这种情况下没有用。
答案 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
,在创建评估程序时,我通常会在剥离之前进行测试,最后以if
和let
嵌套为例。我的第一次迭代看起来像:
(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))))))