一个"康德"也是一个"让"?



通常,我发现我想根据先前值的值有条件地计算一系列值。例如,我们将这些值称为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

你如何在方案中做到这一点?我认为通常人们会使用condcond会丢弃测试结果,因此在这种情况下没有用。

使用let*,它在 brevious 绑定的范围内按顺序计算初始化形式。在初始化窗体中,使用and使计算具有条件。

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

您可以在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,以表示您描述的嵌套条件。

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

所以你的代码可以用标准的Common Lisp这样写:

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

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

(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 中效果很好,但它在 Plan 中带来了卫生问题。我知道有人为绑定名称创建了一个带有额外参数的东西,但它失去了它的优雅:

(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相同的方式使用。如果您有兴趣,方案宏是:

(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))))))

相关内容

  • 没有找到相关文章

最新更新