Common lisp :KEY 参数使用
Common lisp :KEY parameter use
:KEY
参数包含在 Common Lisp 附带的一些函数中。我找到的所有关于它们的描述都没有帮助,并且 :KEY
很难在搜索引擎中搜索,因为“:”通常被忽略。
如何使用它,例如,在允许 :TEST
和 :KEY
的 member
函数中?
:key
论证在 Sequences Library (Section 17) in the Common Lisp HyperSpec, under 17.2.1 Satisfying a Two-Argument Test as well as 17.2.2 Satisfying a One-Argument Test 的介绍部分中有所记载,有些隐晦。这是因为它的行为在整个库中是一致的。
很简单,:key
指定应用于正在处理的一个或多个序列的元素的函数。函数的 return 值用于代替这些元素。在某些函数式语言的术语中,这称为 "projection"。元素通过键函数投影。你可以想象默认的键函数是 identity
,如果你不提供这个参数。
需要理解的一件重要事情是,在接受一些对象参数和序列的函数中(例如搜索序列以查找对象出现的函数),关键函数是 not应用于输入对象;仅对序列的元素。
第二个重要的事情是 :key
不替代项目,仅替代用于标识项目的值。例如,在序列中搜索项目的函数将从序列中检索原始项目,即使序列的项目通过 :key
投影到替代键也是如此。 key函数取回的值仅用于比较
例如如果 obj-list
是一个对象列表,其名称可通过名为 obj-name
的函数访问,我们可能会使用 (find "foo" obj-list :key #'obj-name)
查找名为 "foo"
的对象。函数 obj-name
应用于每个元素,并将其结果与字符串 "foo"
(未应用该函数)进行比较。如果 obj-list
中至少存在一个同名对象,则第一个这样的对象是 returned.
:key
参数是一个参数的函数;它应用于序列的每个元素以生成用于测试的值。如果省略,则使用恒等函数。
这是来自 CLHS 的示例:
(member 2 '((1 . 2) (3 . 4)) :test-not #'= :key #'cdr) => ((3 . 4))
假设我们有一个城市列表:
(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)))
现在我们可以计算people/km^2
的人口密度
(defun city-density (city)
"the density is the population number divided by the area"
(/ (second city) (third city)))
现在我们要计算密度小于 21000 的所有城市的列表 people/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))
我们可以通过提供数字 :test-not
函数 >=
并使用 city-density
函数作为键来计算每个提供的城市的值,从而在没有匿名函数的情况下将其编写得更简单:
CL-USER 86 > (remove 21000 *cities* :test-not #'>= :key #'city-density)
((NEA-SMYRNI 73090 3.52) (HOWRAH 1072161 51.74))
因此,同时拥有测试谓词和键函数可以更轻松地为序列计算提供构建块...
现在假设我们使用 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)))
现在我们计算上面的列表:
CL-USER 100 > (remove 21000 *city-objects* :test-not #'>= :key #'density)
(#<CITY 42D020DDFB> #<CITY 42D020DF23>)
CL-USER 101 > (mapcar #'city-name *)
(NEA-SMYRNI HOWRAH)
如果我们将密度作为一个插槽 getter,我们可以这样做:
(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)))
现在我们计算列表如上,但是key是density槽的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)
:KEY
参数包含在 Common Lisp 附带的一些函数中。我找到的所有关于它们的描述都没有帮助,并且 :KEY
很难在搜索引擎中搜索,因为“:”通常被忽略。
如何使用它,例如,在允许 :TEST
和 :KEY
的 member
函数中?
:key
论证在 Sequences Library (Section 17) in the Common Lisp HyperSpec, under 17.2.1 Satisfying a Two-Argument Test as well as 17.2.2 Satisfying a One-Argument Test 的介绍部分中有所记载,有些隐晦。这是因为它的行为在整个库中是一致的。
很简单,:key
指定应用于正在处理的一个或多个序列的元素的函数。函数的 return 值用于代替这些元素。在某些函数式语言的术语中,这称为 "projection"。元素通过键函数投影。你可以想象默认的键函数是 identity
,如果你不提供这个参数。
需要理解的一件重要事情是,在接受一些对象参数和序列的函数中(例如搜索序列以查找对象出现的函数),关键函数是 not应用于输入对象;仅对序列的元素。
第二个重要的事情是 :key
不替代项目,仅替代用于标识项目的值。例如,在序列中搜索项目的函数将从序列中检索原始项目,即使序列的项目通过 :key
投影到替代键也是如此。 key函数取回的值仅用于比较
例如如果 obj-list
是一个对象列表,其名称可通过名为 obj-name
的函数访问,我们可能会使用 (find "foo" obj-list :key #'obj-name)
查找名为 "foo"
的对象。函数 obj-name
应用于每个元素,并将其结果与字符串 "foo"
(未应用该函数)进行比较。如果 obj-list
中至少存在一个同名对象,则第一个这样的对象是 returned.
:key
参数是一个参数的函数;它应用于序列的每个元素以生成用于测试的值。如果省略,则使用恒等函数。
这是来自 CLHS 的示例:
(member 2 '((1 . 2) (3 . 4)) :test-not #'= :key #'cdr) => ((3 . 4))
假设我们有一个城市列表:
(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)))
现在我们可以计算people/km^2
的人口密度(defun city-density (city)
"the density is the population number divided by the area"
(/ (second city) (third city)))
现在我们要计算密度小于 21000 的所有城市的列表 people/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))
我们可以通过提供数字 :test-not
函数 >=
并使用 city-density
函数作为键来计算每个提供的城市的值,从而在没有匿名函数的情况下将其编写得更简单:
CL-USER 86 > (remove 21000 *cities* :test-not #'>= :key #'city-density)
((NEA-SMYRNI 73090 3.52) (HOWRAH 1072161 51.74))
因此,同时拥有测试谓词和键函数可以更轻松地为序列计算提供构建块...
现在假设我们使用 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)))
现在我们计算上面的列表:
CL-USER 100 > (remove 21000 *city-objects* :test-not #'>= :key #'density)
(#<CITY 42D020DDFB> #<CITY 42D020DF23>)
CL-USER 101 > (mapcar #'city-name *)
(NEA-SMYRNI HOWRAH)
如果我们将密度作为一个插槽 getter,我们可以这样做:
(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)))
现在我们计算列表如上,但是key是density槽的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)