常见的lisp:KEY参数使用

Spe*_*uex 4 lisp common-lisp sequence

:KEY参数包含在Common Lisp附带的一些函数中.我发现它们的所有描述都是无益的,并且:KEY很难在搜索引擎中搜索,因为":"通常被忽略.

例如,如何在member允许两者:TEST:KEY?的功能中使用它?

Dou*_*rie 7

所述:key参数是一个参数的函数; 它应用于序列的每个元素以生成用于测试的值.如果省略,则使用标识函数.

这是CLHS的一个例子:

(member 2 '((1 . 2) (3 . 4)) :test-not #'= :key #'cdr) =>  ((3 . 4))
Run Code Online (Sandbox Code Playgroud)


Kaz*_*Kaz 6

17.2.1满足双参数测试以及17.2.2满足单参数测试的情况下,在Common Lisp HyperSpec :key序列库(第17节)的介绍部分中,有点隐秘地记录了这个论点.这是因为它的行为在整个库中是一致的.

很简单,:key指定应用于正在处理的序列或序列的元素的函数.函数的返回值用于代替这些元素.在一些函数语言的术语中,这称为"投影".元素通过关键功能进行投影.identity如果不提供此参数,您可以想象默认键功能是.

要理解的一件重要事情是,在接受一些对象参数和一个序列的函数中(例如,搜索序列中出现一个对象的函数),键函数不会应用于输入对象; 只对序列的元素.

第二个重要的是:key不能替代项目,仅用于标识项目的值.例如,搜索序列中的项目的函数将从序列中检索原始项目,即使序列的项目被投影到替代密钥:key.key函数检索的值仅用于比较.

例如,如果obj-list是具有可通过调用函数访问的名称的对象列表obj-name,我们可能会查找名为"foo"using 的对象(find "foo" obj-list :key #'obj-name).该函数obj-name应用于每个元素,并将其结果与字符串"foo"(未应用该函数)进行比较.如果存在至少一个该名称的obj-list对象,则返回第一个这样的对象.


Rai*_*wig 2

想象一下我们有一个城市列表:

(defparameter *cities*
   ; City       Population  Area km^2
  '((Paris      2265886     105.4)
    (Mislata    43756       2.06)
    (Macau      643100      30.3)
    (Kallithea  100050      4.75)
    (Nea-Smyrni 73090       3.52)
    (Howrah     1072161     51.74)))
Run Code Online (Sandbox Code Playgroud)

现在我们可以计算人口密度(人/平方公里^2)

(defun city-density (city)
  "the density is the population number divided by the area"
  (/ (second city) (third city)))
Run Code Online (Sandbox Code Playgroud)

现在我们要计算密度小于 21000 人/km^2 的所有城市的列表。

我们从列表中删除所有较大的并提供一个:test-not功能。我们需要提供一个匿名函数来进行测试并计算要比较的城市的密度。

CL-USER 85 > (remove 21000 *cities*
                     :test-not (lambda (a b)
                                 (>= a (city-density b))))

((NEA-SMYRNI 73090 3.52) (HOWRAH 1072161 51.74))
Run Code Online (Sandbox Code Playgroud)

我们可以通过提供数字函数来更简单地编写它,而无需匿名函数:test-not>=并使用该city-density函数作为键来计算每个提供的城市的值:

CL-USER 86 > (remove 21000 *cities* :test-not #'>= :key #'city-density)

((NEA-SMYRNI 73090 3.52) (HOWRAH 1072161 51.74))
Run Code Online (Sandbox Code Playgroud)

因此,同时拥有测试谓词和关键函数可以更轻松地为序列计算提供构建块......

现在想象我们使用 CLOS 和城市 CLOS 对象列表:

(defclass city ()
  ((name :initarg :name :reader city-name)
   (population :initarg :population :reader city-population)
   (area :initarg :area :reader city-area)))

(defparameter *city-objects*
  (loop for (name population area) in *cities*
        collect (make-instance 'city
                               :name name
                               :population population
                               :area area)))

(defmethod density ((c city))
  (with-slots (population area)
      c
    (/ population area)))
Run Code Online (Sandbox Code Playgroud)

现在我们按上面的方法计算列表:

CL-USER 100 > (remove 21000 *city-objects* :test-not #'>= :key #'density)
(#<CITY 42D020DDFB> #<CITY 42D020DF23>)

CL-USER 101 > (mapcar #'city-name *)
(NEA-SMYRNI HOWRAH)
Run Code Online (Sandbox Code Playgroud)

如果我们将密度作为带有吸气剂的插槽,我们可以这样做:

(defclass city ()
  ((name :initarg :name :reader city-name)
   (population :initarg :population :reader city-population)
   (area :initarg :area :reader city-area)
   (density :reader city-density)))

(defmethod initialize-instance :after ((c city) &key)
  (with-slots (density)
      c
    (setf density (density c))))

(defparameter *city-objects*
  (loop for (name population area) in *cities*
        collect (make-instance 'city
                               :name name
                               :population population
                               :area area)))
Run Code Online (Sandbox Code Playgroud)

现在我们按上面的方法计算列表,但关键是密度槽的 getter :

CL-USER 102 > (remove 21000 *city-objects* :test-not #'>= :key #'city-density)
(#<CITY 42D026D7EB> #<CITY 42D026D913>)

CL-USER 103 > (mapcar #'city-name *)
(NEA-SMYRNI HOWRAH)
Run Code Online (Sandbox Code Playgroud)

  • 感谢您对我三年前的问题的深入回答。 (3认同)