我正在尝试使用Common Lisp从列表中获取最大的子列表。
(defun maxlist (list)
(setq maxlen (loop for x in list maximize (list-length x)))
(loop for x in list (when (equalp maxlen (list-length x)) (return-from maxlist x)))
)
我们的想法是遍历列表两次:第一个循环获取最大子列表的大小,第二个循环获取所需的列表。但由于某些原因,我一直在return-from
行中收到错误。我错过了什么?
答案 0 :(得分:6)
loop
这里有一些问题。首先,您可以按如下方式编写循环。 Common Lisp中有return-from
和while
个表单,但loop
定义了自己的小语言,也识别while
和return
,所以你可以使用那些:
(loop for x in list
when (equalp maxlen (list-length x))
return x)
这样的循环实际上可以用find
更简洁地编写。这只是
(find maxlen list :key list-length :test 'equalp)
但请注意,list-length
应始终返回一个数字或nil
,因此equalp
过度杀伤。你可以使用eql
,这是find
的默认值,所以你甚至可以写
(find maxlen list :key list-length)
list-length
和maximize
list-length
与length
非常相似,只是如果列表具有循环结构,则返回nil
,而使用不正确的列表调用length
是错误的。但是,如果您使用(loop ... maximize ...)
,则不能拥有nil
个值,因此list-length
处理length
的唯一情况不会是你错了例如,
CL-USER> (loop for x in '(4 3 nil) maximize x)
; Evaluation aborted on #<TYPE-ERROR expected-type: REAL datum: NIL>.
(实际上,length
也适用于其他类型的序列,因此如果您传递了一个向量,list-length
会出错,但length
则不会。)所以,如果你知道那个他们都是合适的名单,你可以
(loop for x in list
maximizing (length x))
如果它们不一定都是正确的列表(这样你需要list-length
),那么你需要保护:
(loop for x in list
for len = (list-length x)
unless (null len) maximize len)
但是,现在你在列表上进行两次传递,并且你计算每个子列表的长度两次。一旦你计算最大长度,另一个是你找到一个最大值。如果你一次性完成这项工作,你将节省时间。 argmax
没有明显优雅的解决方案,但这里的实现基于reduce
,loop
和do*
。
(defun argmax (fn list &key (predicate '>) (key 'identity))
(destructuring-bind (first &rest rest) list
(car (reduce (lambda (maxxv x)
(destructuring-bind (maxx . maxv) maxxv
(declare (ignore maxx))
(let ((v (funcall fn (funcall key x))))
(if (funcall predicate v maxv)
(cons x v)
maxxv))))
rest
:initial-value (cons first (funcall fn (funcall key first)))))))
(defun argmax (function list &key (predicate '>) (key 'identity))
(loop
for x in list
for v = (funcall function (funcall key x))
for maxx = x then maxx
for maxv = v then maxv
when (funcall predicate v maxv)
do (setq maxx x
maxv v)
finally (return maxx)))
(defun argmax (function list &key (predicate '>) (key 'identity))
(do* ((x (pop list)
(pop list))
(v (funcall function (funcall key x))
(funcall function (funcall key x)))
(maxx x)
(maxv v))
((endp list) maxx)
(when (funcall predicate v maxv)
(setq maxx x
maxv v))))
他们产生相同的结果:
CL-USER> (argmax 'length '((1 2 3) (4 5) (6 7 8 9)))
(6 7 8 9)
CL-USER> (argmax 'length '((1 2 3) (6 7 8 9) (4 5)))
(6 7 8 9)
CL-USER> (argmax 'length '((6 7 8 9) (1 2 3) (4 5)))
(6 7 8 9)
答案 1 :(得分:3)
CL-USER> (defparameter *test* '((1 2 3) (4 5) (6 7 8 9)))
*TEST*
CL-USER> (car (sort *test* '> :key #'length))
(6 7 8 9)
most
请考虑保罗格雷厄姆的most
功能:
(defun most (fn lst)
(if (null lst)
(values nil nil)
(let* ((wins (car lst))
(max (funcall fn wins)))
(dolist (obj (cdr lst))
(let ((score (funcall fn obj)))
(when (> score max)
(setq wins obj
max score))))
(values wins max))))
这是测试的结果(它还返回由提供的函数返回的最佳&#39;元素的值):
CL-USER> (most #'length *test*)
(6 7 8 9)
4
extreme
实用程序过了一会儿,我想出了extreme
效用,部分基于保罗格雷厄姆的功能。这是有效且非常普遍的:
(declaim (inline use-key))
(defun use-key (key arg)
(if key (funcall key arg) arg))
(defun extreme (fn lst &key key)
(let* ((win (car lst))
(rec (use-key key win)))
(dolist (obj (cdr lst))
(let ((test (use-key key obj)))
(when (funcall fn test rec)
(setq win obj rec test))))
(values win rec)))
它需要比较谓词fn
,元素列表和(可选)key
参数。可以很容易地找到具有指定质量极值的对象:
CL-USER> (extreme #'> '(4 9 2 1 5 6))
9
9
CL-USER> (extreme #'< '(4 9 2 1 5 6))
1
1
CL-USER> (extreme #'> '((1 2 3) (4 5) (6 7 8 9)) :key #'length)
(6 7 8 9)
4
CL-USER> (extreme #'> '((1 2 3) (4 5) (6 7 8 9)) :key #'cadr)
(6 7 8 9)
7
请注意,这件事在亚历山大里亚被称为extremum
。它也可以用于序列。