Lisp 带有一个或多个符号的用例
我仍然有一些问题需要真正理解Lisp 带有一个或多个符号的用例,lisp,common-lisp,Lisp,Common Lisp,我仍然有一些问题需要真正理解案例。我知道您提供的符号是使用eq进行内部比较的,但我不确定是否正确获得了一件事: 我已经看到我会写作,例如: (case n (23 'foo) (42 'bar) (otherwise 'something-else)) 我还可以写: (case n ((23 42) 'foo-or-bar) (otherwise 'something-else)) 那么,如果我指定一个case的列表,然后检查变量n是否与列表中的一个元素匹配,但是如果我指
案例
。我知道您提供的符号是使用eq
进行内部比较的,但我不确定是否正确获得了一件事:
我已经看到我会写作,例如:
(case n
(23 'foo)
(42 'bar)
(otherwise 'something-else))
我还可以写:
(case n
((23 42) 'foo-or-bar)
(otherwise 'something-else))
那么,如果我指定一个case
的列表,然后检查变量n
是否与列表中的一个元素匹配,但是如果我指定一个值,那么case
直接与该值匹配,这对吗
换句话说:如果我只有一个值,可以使用非列表版本吗?键在概念上是键列表 从概念上讲,每个子句都使用一个键列表。测试密钥将与密钥列表中的每个密钥进行比较 键列表
(foo-bar-baz)
正是(foo-bar-baz)
键foo
被认为表示(foo)
。它有助于缩短代码
(case x
(foo 41)
((bar baz) 42)))
T和其他情况的例外
请注意,否则
和(否则)
,t
和(t)
是例外情况。如果要匹配符号,则需要编写(否则)
:
(case 'otherwise
((otherwise) 'the-symbol-otherwise)
(otherwise 'the-otherwise-clause))
EQ与EQL的对比
还要注意的是,公共Lisp中的大多数比较默认使用
EQL
,而不是EQ
EQ
是指针相等,EQL
也适用于数字和字符。键在概念上是键列表
从概念上讲,每个子句都使用一个键列表。测试密钥将与密钥列表中的每个密钥进行比较
键列表(foo-bar-baz)
正是(foo-bar-baz)
键foo
被认为表示(foo)
。它有助于缩短代码
(case x
(foo 41)
((bar baz) 42)))
T和其他情况的例外
请注意,否则
和(否则)
,t
和(t)
是例外情况。如果要匹配符号,则需要编写(否则)
:
(case 'otherwise
((otherwise) 'the-symbol-otherwise)
(otherwise 'the-otherwise-clause))
EQ与EQL的对比
还要注意的是,公共Lisp中的大多数比较默认使用
EQL
,而不是EQ
EQ
是指针等式,EQL
也适用于数字和字符。这不是直接回答您的问题,但我们可以使用lisp查看它在做什么
如果您在CLHS页面中查找案例
,我们可以看到它是一个宏,这意味着我们可以对它进行宏扩展以查看它变成了什么
让我们来看第一个例子
(case n
(23 'foo)
(42 'bar)
(otherwise 'something-else))
这扩展到
(LET ((#:G1246 N))
(COND ((EQL #:G1246 '23) NIL 'FOO)
((EQL #:G1246 '42) NIL 'BAR)
(T NIL 'SOMETHING-ELSE)))
#:G1246
是一个gensym,我们可以将其视为一个由lisp保证唯一的符号。我现在将其重命名为tmp
(let ((tmp n))
(cond ((eql tmp '23) nil 'foo)
((eql tmp '42) nil 'bar)
(t nil 'something-else)))
另外,cond
是一个宏。。让我们看看它是如何扩展的(我简化了它,有点像上面)
现在我们可以看到所有的逻辑
现在让我们展开下一个
(case n
((23 42) 'foo-or-bar)
(otherwise 'something-else))
变成
(let ((tmp n))
(if (or (eql tmp '23) (eql tmp '42))
'foo-or-bar
'something-else))
宏扩展非常有用。希望这对您有所帮助这不是直接回答您的问题,但我们可以使用lisp查看它在做什么 如果您在CLHS页面中查找
案例
,我们可以看到它是一个宏,这意味着我们可以对它进行宏扩展以查看它变成了什么
让我们来看第一个例子
(case n
(23 'foo)
(42 'bar)
(otherwise 'something-else))
这扩展到
(LET ((#:G1246 N))
(COND ((EQL #:G1246 '23) NIL 'FOO)
((EQL #:G1246 '42) NIL 'BAR)
(T NIL 'SOMETHING-ELSE)))
#:G1246
是一个gensym,我们可以将其视为一个由lisp保证唯一的符号。我现在将其重命名为tmp
(let ((tmp n))
(cond ((eql tmp '23) nil 'foo)
((eql tmp '42) nil 'bar)
(t nil 'something-else)))
另外,cond
是一个宏。。让我们看看它是如何扩展的(我简化了它,有点像上面)
现在我们可以看到所有的逻辑
现在让我们展开下一个
(case n
((23 42) 'foo-or-bar)
(otherwise 'something-else))
变成
(let ((tmp n))
(if (or (eql tmp '23) (eql tmp '42))
'foo-or-bar
'something-else))
宏扩展非常有用。希望这有帮助你为什么不试试呢?转到lisp提示符并输入一些内容,如
(let((n3))(case n((23)'foo)(4'bar))
。但是,是的,那是正确的。你为什么不试试呢?转到lisp提示符并输入一些内容,如(let((n3))(case n((23)'foo)(4'bar))
。但是,是的,这是正确的。好吧,那么请写出否则
的特例总是使用括号可能是个好主意,对吗?@GoloRoden:大多数代码不使用括号来表示单个项目的键列表。好吧,那么请写出否则
的特例总是使用括号可能是个好主意,是吗?@GoloRoden:大多数代码不使用括号来表示单个项目的键列表。这确实非常有用,感谢您的详细解释:-)哦,如果您使用的是带slime的emacs,那么将光标粘贴在开始部分,并Ctrl+Return
这将扩展到一个新的缓冲区,然后可以展开内部宏或使用“撤消”来“取消展开”它们。macroexpand的这种探索性使用非常有启发性。(Vim with slime也会有一个键绑定,但我不知道它是什么)这确实非常有用,感谢您的详细解释:-)哦,如果您将emacs与slime一起使用,那么将光标粘贴在开始部分,并Ctrl+Return
这将扩展到一个新的缓冲区,然后可以展开内部宏或使用“撤消”来“取消展开”它们。macroexpand的这种探索性使用非常有启发性。(带slimv的Vim也将有一个键绑定,但我不知道它是什么)