(defvar x '((5 . a) (3 . b) (1 . c) (9 . d)))
> X
(loop for i in x minimize (car i))
> 1
我想要的是获得C而不是1.我尝试使用值,因为它仍将使用第一个返回值进行最小化,但我不知道是否有使用多值的方法 - 在这种情况下绑定?
(loop for i in x
minimize (values (car i) (cdr i)) into ans
finally (return ans))
答案 0 :(得分:4)
我担心你必须自己编写代码:
(let ((best ()))
(dolist (pair x (cdr best))
(when (or (null best) (< (car x) (car best)))
(setq best pair))))
这只扫描一次列表。
然而,这会多次调用键(car
)(如评论中所述)。
这可以优化:
(defun find-best (list &key (key #'identity) (test #'<))
(when list
(let* ((best (first list))
(best-key (funcall key best)))
(dolist (o (rest list) best)
(let ((k (funcall key o)))
(when (funcall test k best-key)
(setq best o best-key k)))))))
答案 1 :(得分:4)
(defvar x '((5 . a) (3 . b) (1 . c) (9 . d)))
(loop for (num . sym) in x
with min-num = nil
with min-sym = nil
do (when (or (null min-num)
(< num min-num))
(setf min-num num
min-sym sym))
finally (return (values min-sym min-num)))
;=> C, 1
;; see. http://common-lisp.net/project/iterate/doc/Finders.html#Finders
(ql:quickload :iterate)
(use-package :iterate)
(iter (for (num . sym) in x)
(finding sym minimizing num))
;=> C
答案 2 :(得分:1)
CL-USER 15 > (defparameter *x* '((5 . a) (3 . b) (1 . c) (2 . d) (9 . e)))
*X*
CL-USER 16 > (loop with (min-n . min-v) = (first *x*)
for (n . v) in (rest *x*)
if (< n min-n) do (setf min-n n min-v v)
finally (return min-v))
C
作为一个功能:
(defun minimize (list &key (pred #'<) (key #'identity))
"returns values: the minimum value and if there was one"
(if (null list)
(values nil nil)
(values (loop with min-e = (first list)
with min-v = (funcall key min-e)
initially (pop list)
for e in list
for v = (funcall key e)
if (funcall pred v min-v) do (setf min-e e min-v v)
finally (return min-e))
t)))
CL-USER 35 > (minimize '((5 . a) (3 . b) (1 . c) (2 . d) (9 . e)) :key #'car)
(1 . C)
T
答案 3 :(得分:1)
由于这是一个最小化问题,你只需要走一次列表。您可以直接使用loop
,dotimes
或其他一些迭代构造直接执行此操作。如果你想要一个更实用的方法,你可以使用这样的东西:
(defun keyed-predicate (predicate key)
(lambda (x y)
(if (funcall predicate
(funcall key x)
(funcall key y))
x
y)))
(cdr (reduce (keyed-predicate '< 'car) '((5 . a) (3 . b) (1 . c) (9 . d))))
;=> C
然而,问题在于,它在某些点“当前最佳值”的元素上多次调用键函数。这也发生在sds's answer中(car
多次调用best
), though it's not an issue with
汽车, since
car`没有任何副作用。例如,
(cdr (reduce (keyed-predicate '< (lambda (x)
(format t "visiting ~a~%" x)
(car x)))
'((5 . a) (3 . b) (1 . c) (9 . d))))
; visiting (3 . B)
; visiting (3 . B) ; repeat
; visiting (1 . C)
; visiting (1 . C) ; repeat
; visiting (9 . D)
;=> C
最好确保在每个元素上只调用一次键函数,这在迭代实现中更容易。例如,
(defun optimum (predicate list &key key)
(flet ((key (x)
(if (null key) x
(funcall key x))))
(if (endp list)
(funcall predicate)
(let* ((best (first list))
(best-key (key best)))
(dolist (item (rest list) best)
(let ((item-key (key item)))
(when (funcall predicate item-key best-key)
(setf best item
best-key item-key))))))))
(cdr (optimum '< '((5 . a) (3 . b) (1 . c) (9 . d)) :key 'car))
;=> C
在此,每个项目只调用一次密钥:
(cdr (optimum '< '((5 . a) (3 . b) (1 . c) (9 . d))
:key (lambda (x)
(format t "visiting ~a~%" x)
(car x))))
; visiting (5 . A)
; visiting (3 . B)
; visiting (1 . C)
; visiting (9 . D)
;=> C