公共Lisp中最大的子列表

公共Lisp中最大的子列表,lisp,common-lisp,Lisp,Common Lisp,我正在尝试使用CommonLisp从列表中获取最大的子列表 (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))) ) 其思想是迭代列表两次:第一个循环获取最大子列表的大小,第二个循环检索所需列表。但是由于某种原因,我在从

我正在尝试使用CommonLisp从列表中获取最大的子列表

(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)))
)
其思想是迭代列表两次:第一个循环获取最大子列表的大小,第二个循环检索所需列表。但是由于某种原因,我在从行返回的
中不断收到一个错误。我错过了什么

循环的主要问题 这里有一些问题。首先,您可以按如下方式编写循环。在Common Lisp中有
return from
while
表单,但定义了自己的小语言,它也可以识别
while
return
,因此您可以使用它们:

(列表中x的循环
当(相等最大长度(列表长度x))
返回x)
不过,使用
find
实际上可以更简洁地编写这样的循环。只是

(查找最大列表:键列表长度:测试'equalp》)
但是,请注意,
list length
应始终返回一个数字或
nil
,因此
equalp
是多余的。您只需使用
eql
,这是
find
的默认值,因此您甚至可以编写

(查找最大列表:键列表长度)
列表长度
最大化
list length
length
非常相似,不同的是,如果列表具有循环结构,则返回
nil
,而使用不正确的列表调用
length
则是错误的。但是如果使用的是
(循环…最大化…
),则不能有
nil
值,因此
列表长度
处理
长度
的唯一情况是仍然会给您一个错误。例如:

CL-USER>(循环用于x英寸(4-3零)最大化x)
; 对#的评估已中止。
(实际上,
length
也适用于其他类型的序列,因此如果传递向量,
list length
将出错,但
length
不会)。因此,如果您知道它们都是正确的列表,您可以

(列表中x的循环
最大化(长度x))
如果它们不一定都是正确的列表(因此您确实需要
列表长度
),那么您需要进行如下保护:

(列表中x的循环
对于len=(列表长度x)
除非(空len)最大化len)
更高效的argmax 但是,现在您要对列表进行两次遍历,并且要计算每个子列表的长度两次。一次是计算最大长度,另一次是查找具有最大值的长度。如果你一次完成,你会节省时间
argmax
没有一个明显的优雅解决方案,但这里有一些基于
reduce
loop
do*
的实现

(defun argmax(fn列表和键(谓词“>)(键”标识))
(解构绑定(第一个和第二个)列表
(轿厢(减小(λ)(最大xv x)
(解构绑定(maxx.maxv)maxxxv
(声明(忽略maxx))
(let((v(funcall-fn(funcall-key x)))
(如果(funcall谓词v maxv)
(十、五)
maxxv)))
休息
:初始值(cons优先(funcall fn(funcall键优先‘‘‘‘‘‘‘)’))
(defun argmax(函数列表和键(谓词“>)(键标识))
(环路
对于列表中的x
对于v=(funcall函数(funcall键x))
对于maxx=x,则为maxx
对于maxv=v,则为maxv
when(funcall谓词v maxv)
do(setq最大值x)
最大值(v)
最后(返回maxx)))
(defun argmax(函数列表和键(谓词“>)(键标识))
(do*((x(流行音乐列表)
(流行音乐列表)
(v(funcall函数(funcall键x))
(funcall函数(funcall键x)))
(最大值x)
(maxv)
((endp列表)maxx)
(当(funcall谓词v maxv)
(setq最大值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'((6789)(12345)))
(6 7 8 9)
循环的主要问题
这里有一些问题。首先,您可以按如下方式编写循环。在Common Lisp中有
return from
while
表单,但定义了自己的小语言,它也可以识别
while
return
,因此您可以使用它们:

(列表中x的循环
当(相等最大长度(列表长度x))
返回x)
不过,使用
find
实际上可以更简洁地编写这样的循环。只是

(查找最大列表:键列表长度:测试'equalp》)
但是,请注意,
list length
应始终返回一个数字或
nil
,因此
equalp
是多余的。您只需使用
eql
,这是
find
的默认值,因此您甚至可以编写

(查找最大列表:键列表长度)
列表长度
最大化
list length
length
非常相似,不同的是,如果列表具有循环结构,则返回
nil
,而使用不正确的列表调用
length
则是错误的。但是如果使用的是
(循环…最大化…
),则不能有
nil
值,因此
列表长度
处理
长度
的唯一情况是仍然会给您一个错误。例如:

CL-USER>(循环用于x英寸(4-3零)最大化x)
; 对#的评估已中止。
(实际上,
length
也适用于其他类型的序列,因此如果传递向量,
list length
将出错,但
length
不会)。因此,如果您知道它们是
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)
(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))))
CL-USER> (most #'length *test*)
(6 7 8 9)
4
(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)))
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
(defun maxim-list (l)
  (flet ((max-list (a b) (if (> (length a) (length b)) a b)))
    (if (null l)
    nil
    (max-list (car l) (maxim-list (cdr l))))))