3

查看 HyperSpec中#'adjoin的文档,我在示例部分看到以下内容:

(setq slist '()) =>  NIL
(setq slist (adjoin '(test-item 1) slist)) =>  ((TEST-ITEM 1))
(adjoin '(new-test-item 1) slist :key #'cadr) =>  ((TEST-ITEM 1))

相反,我会期待以下内容:

(adjoin '(new-test-item 1) slist :key #'cadr) =>  ((NEW-TEST-ITEM 1) (TEST-ITEM 1))

我的期望是由于HyperSpec (17.2.1)中的以下文本:

当下图中列出的运算符 F 对序列 S 的每个元素 Ei 迭代地考虑对象 O 时,有时控制在 S 中测试 O 的存在的方式由 F 测试是有用的。这控制是基于使用 :test 或 :test-not 参数指定的函数提供的。

并进一步:

对象 O 可能不会直接与 Ei 进行比较。如果提供了 :key 参数,则它是一个参数的函数的指示符,该函数以每个 Ei 作为参数调用,并产生一个用于比较的对象 Zi。(如果没有 :key 参数,Zi 就是 Ei。)

:key 参数指定的函数永远不会在 O 本身上调用。但是,如果该函数对多个序列进行操作(例如,在 set-difference 中发生),则 O 将是在另一个序列的元素上调用 :key 函数的结果。

所以我们有 slist (序列,S) as'((TEST-ITEM 1))和 O as '(new-test-item 1)。为了检查 O 是否应该相邻,函数#'cadr应用于 S 的元素,第一个是'(test-item 1)。因此,该测试给出:

(cadr '(test-item 1)) => 1

现在,当 O, , 与应用于S 的 E1'(new-test-item 1)的结果进行检查时(当没有提供 时使用的相等函数),结果应该意味着 O 是邻接的。至少我会这么认为。我有什么误解?#'cadr#'eql:testfalse

4

1 回答 1

6

这是 HyperSpec 中的一个错误,并且问题 ADJOIN-SPECIFICATION已在 CLiki 上写过。相关部分是:

问题描述:

CLHS 通过参考第 17.2.1 节满足两个参数测试来指定存在 :KEY 参数时的 ADJOIN 行为。这是不正确的,因为第 17.2.1 节指定不对 ITEM 参数调用 key 函数,但 ADJOIN 会调用,如 pushnew 中所指定。提案(附加:澄清):

代替:

test、test-not 和 key 影响如何确定 item 是否与 list 的元素相同。有关详细信息,请参阅第 17.2.1 节(满足两个参数测试)。

经过:

item 是否已经是列表的成员是通过使用 :test 或 :test-not 进行比较来确定的。:test 或 :test-not 函数的第一个参数是 :key 函数(如果提供)返回的结果,应用于项目;第二个参数是 :key 函数(如果提供)返回的列表元素。如果 :key 提供,它用于从项目和列表元素中提取要测试的部分。

理由:

澄清 ADJOIN 的规范。

目前的做法:

所有实现都按照 pushnew 上的指定实现 ADJOIN,而不是按照 adjoin 上的指定实现。

于 2014-03-12T18:20:40.377 回答