1

我目前正在学习 common lisp,偶然发现了一个我无法回答的问题:

(defun find-all (item seq &rest keyword-args &key (test #'eql)
                     test-not &allow-other-keys)
 (if test-not
   (apply #'remove item seq
          :test-not (complement test-not) keyword-args)
   (apply #'remove item seq
          :test (complement test) keyword-args)))

该函数用于根据测试函数查找 seq 匹配项中的每个元素。可悲的是,我不明白为什么这里使用了“应用”功能。难道不应该只调用remove而不应用吗?如果我在没有应用的情况下调用删除,则警告说:“该函数在关键字部分有奇数个参数”。

我希望你能帮助我,在此先感谢!

4

2 回答 2

4

让我们以签名为例REMOVE

remove item sequence &key from-end test test-not start end count key 
=> result-sequence

上面的意思是该函数接受 2 个强制参数,itemsequence,以及应该与:key value语法一起给出的附加关键字参数。如果您只提供键或值,例如:

(remove x list :count)

那么它是无效的。一个简单的测试是查看关键字位置的参数数量并检查给定参数的数量是否为偶数。当它很奇怪时,你就知道出了点问题。

如果你打电话:

(remove item seq args)

你是同样的情况。args在您的具体情况下,这并不重要。

申请

APPLY是一种更通用的调用函数的方式:它的最后一个参数是附加参数的列表

假设args(3 4),则:

(apply #'+ 1 args)

相当于:

(+ 1 3 4)

这也适用于关键字参数;如果args是列表(:count 1),则:

(apply #'remove item seq args)

相当于:

(remove item seq :count 1)

在这里,关键字位置的参数数量是偶数。

于 2018-02-06T05:19:08.040 回答
4

删除并查找所有

REMOVE接受一堆关键字参数:from-end、test、test-not、start、end、count 和 key。

现在在FIND-ALL你想要修改的函数中只修改一个:要么test要么test-not然后调用REMOVE.

FIND-ALL的参数列表怎么写

现在您基本上有三个选项来编写 的参数列表,因为它与仅更改一个参数FIND-ALL基本相同。REMOVE

  1. 列出每个关键字参数及其默认值,然后将它们传递给必要的更改为REMOVE.

  2. 仅列出一个剩余参数列表,操作此参数列表并通过APPLYto传递新的参数列表REMOVE

  3. 1. 和 2. 的混合,如上例所示。仅列出所需的参数和要修改的关键字参数,以及调用时提供的其他关键字参数的其余列表。REMOVE通过调用APPLY

这三种可能性有多好?

现在 1. 的优点是您可以看到完整的参数列表,FIND-ALL并且它不需要 cons 参数列表。Lisp 可以检查其中的一些。但是您确实需要复制参数列表中的所有参数,然后在调用REMOVE. 可能但不是那么好。

然后 2. 的缺点是没有可见的参数列表FIND-ALL,但使用一些函数来操作参数列表可能更容易编写。3.比较容易写,但也缺少完整的参数列表。

你的例子

因此,在您的示例中,它是上述版本 3:

  • 首先传入所需的参数
  • 接下来是修改后的参数
  • 最后是所有关键字参数的列表

如果要将现有参数列表作为参数的一部分传递给函数,则需要APPLY. 这就是它在那里使用的原因。APPLY使用从列表中获取的参数调用函数。

CL-USER 1 > (apply #'+ 1 2 '(3 4 5))
15

CL-USER 2 > (let ((numbers '(3 4 5)))
              (apply #'+ 1 2 numbers))
15


CL-USER 3 > (+ 1 2 3 4 5)
15
于 2018-02-06T09:19:36.277 回答