Common Lisp Loop accumulator:最小化为多值绑定?

时间:2013-11-27 18:52:59

标签: loops common-lisp sbcl

(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))

4 个答案:

答案 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)

由于这是一个最小化问题,你只需要走一次列表。您可以直接使用loopdotimes或其他一些迭代构造直接执行此操作。如果你想要一个更实用的方法,你可以使用这样的东西:

(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