问题描述
我正在考虑一个扭曲的条件
* {
background: lightcoral;
}
button {
background-color: lightblue;
border: none;
color: white;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
border-radius: 25px;
outline: none;
margin: 2rem;
transition: 0.3s ease-in-out;
cursor: pointer;
&:hover {
background: blue;
}
}
//Home button behavior is here
.home-button {
margin: 1rem;
border: none;
outline: none;
}
.home-pic {
border: none;
width: 2.5rem;
}
其中 (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 块限制为仅一个测试表单及其主体。
但我想知道我是不是走错了路。使用 when-let 的假想变体表明沿着这条路走下去没有任何好处。
尝试条件
使用 cond 的版本看起来更紧凑。
let
一切都归结为 let* 中定义的变量,在现实生活中,这些变量将用于避免两次计算相同的值并提高可读性。我该怎么办?
解决方法
我更愿意更多地考虑块和从它们返回值,而不是使用 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)))
,
使用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)))