You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Lisp中SEARCH函数:KEY用EVENP/ODDP的对比逻辑及规范查询

Common Lisp SEARCH函数示例与KEY参数行为解析

一、SEARCH示例的对比逻辑

先看规范中的示例:

(search '(0 1) '(2 4 6 1 3 5) :key #'oddp) => 2

这个表达式的执行逻辑分三步:

  1. 应用KEY函数转换元素::key #'oddp表示对两个序列的每个元素都调用oddp(判断奇数),得到转换后的序列:
    • 模式序列(0 1)转换后:(nil t)(0是偶数,oddp返回nil;1是奇数,返回t)
    • 目标序列(2 4 6 1 3 5)转换后:(nil nil nil t t t)(2、4、6是偶数→nil;1、3、5是奇数→t)
  2. 匹配转换后的子序列:SEARCH用默认的eql测试,在转换后的目标序列中查找第一个匹配转换后模式的子序列。转换后的目标序列中,从索引2开始的连续两个元素是(nil t),正好与转换后的模式完全匹配。
  3. 返回起始位置:匹配的子序列在原目标序列中的起始索引是2,所以最终返回2。

二、关于默认TEST与隐式转换的疑问

在SBCL中测试发现默认的eql无法直接对比nil和0,但这个示例不存在隐式转换,也不是示例自定义行为——因为整个对比过程是基于KEY函数转换后的结果,而非原元素。原元素0和1并没有直接参与对比,参与对比的是它们经过oddp转换后的广义布尔值nil和t,而eql可以正常对比nil和t,所以示例行为完全符合规范。

三、SEARCH与ASSOC的KEY行为差异

你提到的ASSOC与SEARCH的key参数行为差异,核心原因是两个函数对key的应用场景不同:

  • SEARCH:对模式序列和目标序列的每个元素都应用key函数,然后对比转换后的子序列是否匹配。
  • ASSOC:仅对关联列表中每个关联项的car部分应用key函数,然后将转换结果与第一个参数(要查找的目标值)用test函数对比。

看你的测试示例:

CL-USER> (assoc 1 '((2 . A) (4 . B) (5 . C)) :key #'oddp)
NIL
CL-USER> (assoc t '((2 . A) (4 . B) (5 . C)) :key #'oddp)
(5 . C)

第一个表达式中,assoc对每个关联项的car(2、4、5)应用oddp,得到nil、nil、t,然后用eql对比目标值1和这三个结果,没有匹配项,所以返回nil;第二个表达式中,目标值是t,与5转换后的t匹配,所以返回(5 . C)。

四、序列函数KEY参数的规范定义

Common Lisp规范(CLHS)中,序列函数的:key参数行为统一定义在17.2.1 序列函数的关键字参数章节。该章节明确:

  • :key参数接收一个单参数函数,默认是#'identity(返回元素本身)。
  • 函数会被应用于序列的每个元素(或关联类函数的特定部分,如assoc的关联项car),转换后的结果才会参与后续的测试、匹配逻辑。
  • 不同序列函数的具体应用细节,会在各自的函数文档中补充说明(比如SEARCH需同时处理模式和目标序列,ASSOC仅处理关联项的car)。

内容的提问来源于stack exchange,提问作者Jérôme Radix

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 10:33:22