有人写过 when-let-cond 吗?
Did anybody write when-let-cond?
我在想一个有点扭曲的条件
(let ((a 0))
(let* ((result nil))
(tagbody
(let ((b1 (+ 0 a)))
(when (eq b1 1)
(print "1")
(setf result b1)
(go finish)))
(let ((b2 (+ 0 a)))
(when (eq b2 2)
(print "2")
(setf result b2)
(go finish)))
(when T
(print "else")
(setf result a))
(format t "=== ~A~%" a)
finish)
result))
其中 when
测试表单包含在 let
中。一方面,这似乎适合我正在处理的问题,但也似乎过于复杂。可以用宏简化吗?如果我有很多测试表格,简化它的最佳方法是什么?
尝试这样做的部分问题是将 let 块限制为仅一个测试表单及其主体。
但我想知道我是否走错了路。玩 when-let 的假想变体表明沿着这条路走下去没有任何好处。
正在尝试条件
使用 cond 的版本看起来更紧凑。
(let ((a 3))
(let* ((b1 (+ 0 a))
(b2 (+ 0 a)))
(cond
((eq b1 1)
(print "1")
b1)
((eq b2 2)
(print "2")
b2)
(T (print "else")
a))))
所有归结为 let* 中定义的变量,在现实生活中的示例中将用于避免两次计算相同的值并提高可读性。我该怎么办?
到目前为止,使用 macrolet 是最好的解决方案。这使我能够绕过 when-let 的限制,并且并非 let 形式中的所有绑定都必须评估为真。
(let ((a 3))
(let ((result nil))
(macrolet ((ret-go (res)
`(progn
(setf result ,res)
(go finish))))
(tagbody
(let ((b1 (+ 0 a)))
(when (eq b1 1)
(print "1")
(ret-go b1)))
(let ((b2 (+ 0 a)))
(when (eq b2 2)
(print "2")
(ret-go b2)))
(when T
(print "else")
(setf result a))
(format t "=== ~A~%" a)
finish)
result)))
我宁愿更多地考虑块和它们的返回值,而不是使用 goto 和变量。如果真的需要单独的 let-bound 变量和它们自己的作用域:
(prog ((a 0))
(let ((b1 (+ 0 a)))
(when (eql b1 1)
(print "1")
(return b1)))
(let ((b2 (+ 0 a)))
(when (eql b2 2)
(print "2")
(return b2)))
(return
(progn
(print "else")
(return a))))
现在有人做了。我希望它与 cond
兼容,这会引发一个问题:如果您希望绑定子句像
(cond/binding
...
((var expr) <use var>)
...)
但是你只想允许通用测试子句,那么只有一个参数的函数是不明确的:应该
(cond/binding
...
((car x) ...)
...)
调用car
还是绑定car
?为了使这项工作正常进行,您需要在这种情况下绑定一个无用的变量:
(cond/binding
...
((useless (car x)) <useless not used here>)
...)
这意味着您要么需要在各处插入 ignore
或 ignorable
声明,要么忍受编译器警告。
所以,好吧,我决定换一种方式更好:当你想绑定一个变量时,你必须说。你可以通过这样的子句来做到这一点:
(cond/binding
...
((bind var expr) <var is bound here>)
...)
注意 bind
在语法上很神奇(所以这意味着你不能调用一个名为 bind
的函数,但这没关系,因为我已经使用 bind
作为other macros.
中的关键字
宏也很努力(好吧,很难,因为我基本上只是输入它并且没有测试)实际上表现得像 cond
:返回多个值,例如。
所以这个:
(cond/binding
((f x y z) t)
((bind x 3) (print x) (values x t))
(t (values nil nil))
(1))
扩展到
(block #:cond/binding
(when (f x y z)
(return-from #:cond/binding (progn t)))
(let ((x 3))
(when x
(return-from #:cond/binding
(progn (print x) (values x t)))))
(when t
(return-from #:cond/binding (progn (values nil nil))))
(let ((r 1))
(when r
(return-from #:cond/binding r))))
(所有方块都是同一个方块)。
所以,这里:
(defmacro cond/binding (&body clauses)
;; Like COND but it can bind variables. All clauses are (should be)
;; like COND, except that a clause of the form ((bind var <expr>)
;; ...) will bind a variable. Note that bind has to be literally
;; the symbol BIND: it's magic in the syntax.
(let ((bn (make-symbol "COND/BINDING")))
`(block ,bn
,@(mapcar
(lambda (clause)
(unless (consp clause)
(error "bad clause ~S" clause))
(case (length clause)
(1
`(let ((r ,(car clause)))
(when r (return-from ,bn r))))
(otherwise
(destructuring-bind (test/binding &body forms) clause
(typecase test/binding
(cons
(case (car test/binding)
((bind)
(unless (and (= (length test/binding) 3)
(symbolp (second test/binding)))
(error "bad binding clause ~S" test/binding))
(destructuring-bind (var expr) (rest test/binding)
`(let ((,var ,expr))
(when ,var
(return-from ,bn
(progn ,@forms))))))
(otherwise
`(when ,test/binding
(return-from ,bn
(progn ,@forms))))))
(t
`(when ,test/binding
(return-from ,bn
(progn ,@forms)))))))))
clauses))))
买者自负。
如果我对你的问题理解正确,那么你可以使用 or
并依赖于 when
在条件不成立时计算为 nil
的事实,例如,
(defun example (a)
(or
(let ((b1 (+ 0 a)))
(when (eql b1 1)
(print "1")
b1))
(let ((b2 (+ 0 a)))
(when (eql b2 2)
(print "2")
b2))
(progn
(print "else")
a)))
我在想一个有点扭曲的条件
(let ((a 0))
(let* ((result nil))
(tagbody
(let ((b1 (+ 0 a)))
(when (eq b1 1)
(print "1")
(setf result b1)
(go finish)))
(let ((b2 (+ 0 a)))
(when (eq b2 2)
(print "2")
(setf result b2)
(go finish)))
(when T
(print "else")
(setf result a))
(format t "=== ~A~%" a)
finish)
result))
其中 when
测试表单包含在 let
中。一方面,这似乎适合我正在处理的问题,但也似乎过于复杂。可以用宏简化吗?如果我有很多测试表格,简化它的最佳方法是什么?
尝试这样做的部分问题是将 let 块限制为仅一个测试表单及其主体。
但我想知道我是否走错了路。玩 when-let 的假想变体表明沿着这条路走下去没有任何好处。
正在尝试条件
使用 cond 的版本看起来更紧凑。
(let ((a 3))
(let* ((b1 (+ 0 a))
(b2 (+ 0 a)))
(cond
((eq b1 1)
(print "1")
b1)
((eq b2 2)
(print "2")
b2)
(T (print "else")
a))))
所有归结为 let* 中定义的变量,在现实生活中的示例中将用于避免两次计算相同的值并提高可读性。我该怎么办?
到目前为止,使用 macrolet 是最好的解决方案。这使我能够绕过 when-let 的限制,并且并非 let 形式中的所有绑定都必须评估为真。
(let ((a 3))
(let ((result nil))
(macrolet ((ret-go (res)
`(progn
(setf result ,res)
(go finish))))
(tagbody
(let ((b1 (+ 0 a)))
(when (eq b1 1)
(print "1")
(ret-go b1)))
(let ((b2 (+ 0 a)))
(when (eq b2 2)
(print "2")
(ret-go b2)))
(when T
(print "else")
(setf result a))
(format t "=== ~A~%" a)
finish)
result)))
我宁愿更多地考虑块和它们的返回值,而不是使用 goto 和变量。如果真的需要单独的 let-bound 变量和它们自己的作用域:
(prog ((a 0))
(let ((b1 (+ 0 a)))
(when (eql b1 1)
(print "1")
(return b1)))
(let ((b2 (+ 0 a)))
(when (eql b2 2)
(print "2")
(return b2)))
(return
(progn
(print "else")
(return a))))
现在有人做了。我希望它与 cond
兼容,这会引发一个问题:如果您希望绑定子句像
(cond/binding
...
((var expr) <use var>)
...)
但是你只想允许通用测试子句,那么只有一个参数的函数是不明确的:应该
(cond/binding
...
((car x) ...)
...)
调用car
还是绑定car
?为了使这项工作正常进行,您需要在这种情况下绑定一个无用的变量:
(cond/binding
...
((useless (car x)) <useless not used here>)
...)
这意味着您要么需要在各处插入 ignore
或 ignorable
声明,要么忍受编译器警告。
所以,好吧,我决定换一种方式更好:当你想绑定一个变量时,你必须说。你可以通过这样的子句来做到这一点:
(cond/binding
...
((bind var expr) <var is bound here>)
...)
注意 bind
在语法上很神奇(所以这意味着你不能调用一个名为 bind
的函数,但这没关系,因为我已经使用 bind
作为other macros.
宏也很努力(好吧,很难,因为我基本上只是输入它并且没有测试)实际上表现得像 cond
:返回多个值,例如。
所以这个:
(cond/binding
((f x y z) t)
((bind x 3) (print x) (values x t))
(t (values nil nil))
(1))
扩展到
(block #:cond/binding
(when (f x y z)
(return-from #:cond/binding (progn t)))
(let ((x 3))
(when x
(return-from #:cond/binding
(progn (print x) (values x t)))))
(when t
(return-from #:cond/binding (progn (values nil nil))))
(let ((r 1))
(when r
(return-from #:cond/binding r))))
(所有方块都是同一个方块)。
所以,这里:
(defmacro cond/binding (&body clauses)
;; Like COND but it can bind variables. All clauses are (should be)
;; like COND, except that a clause of the form ((bind var <expr>)
;; ...) will bind a variable. Note that bind has to be literally
;; the symbol BIND: it's magic in the syntax.
(let ((bn (make-symbol "COND/BINDING")))
`(block ,bn
,@(mapcar
(lambda (clause)
(unless (consp clause)
(error "bad clause ~S" clause))
(case (length clause)
(1
`(let ((r ,(car clause)))
(when r (return-from ,bn r))))
(otherwise
(destructuring-bind (test/binding &body forms) clause
(typecase test/binding
(cons
(case (car test/binding)
((bind)
(unless (and (= (length test/binding) 3)
(symbolp (second test/binding)))
(error "bad binding clause ~S" test/binding))
(destructuring-bind (var expr) (rest test/binding)
`(let ((,var ,expr))
(when ,var
(return-from ,bn
(progn ,@forms))))))
(otherwise
`(when ,test/binding
(return-from ,bn
(progn ,@forms))))))
(t
`(when ,test/binding
(return-from ,bn
(progn ,@forms)))))))))
clauses))))
买者自负。
如果我对你的问题理解正确,那么你可以使用 or
并依赖于 when
在条件不成立时计算为 nil
的事实,例如,
(defun example (a)
(or
(let ((b1 (+ 0 a)))
(when (eql b1 1)
(print "1")
b1))
(let ((b2 (+ 0 a)))
(when (eql b2 2)
(print "2")
b2))
(progn
(print "else")
a)))