收集多个最大值

时间:2013-10-07 14:19:36

标签: loops lisp max common-lisp

我有一个元素列表。每个元素的结构如下:

('symbol "string" int-score)

示例列表:

(list (list 'object1 "wabadu" 0.5)
      (list 'object2 "xezulu" 0.6)
      (list 'object1 "yebasi" 0.5)
      (list 'object1 "tesora" 0.2))

我想检索特定符号的最大值。当我用符号object2搜索时,我应该回来:

('object2 "xezulu" 0.6)

如果我使用object1进行搜索,我应该回来:

(('object1 "wabadu" 0.5) ('object1 "yebasi" 0.5))

我想收集特定对象的所有最高元素。我能做的是:假设上面的列表是下面使用的列表,我正在搜索object1。我可以检索特定对象的所有元素:

(loop for element in list
     when (equal 'object1 (first element))
     collect element)

我还可以检索列表中的一个最高元素:

(loop for element in list
     when (equal 'object1 (first element))
     maximize (third element))

但是,这只会返回一个元素。我想要的是所有最大元素。我尝试过使用collectmaximize的一些组合,但我对语法的了解很少。有没有办法收集“简单”功能中的所有最高元素?

7 个答案:

答案 0 :(得分:2)

基于LOOP版本的草图:

(defun mymax (target list &aux result max)
  (loop for (item name value) in list
        when (eql item target)
        do (cond ((or (null result)
                      (> value max))
                  (setf result (list (list item name value))
                        max value))
                 ((= value max)
                  (push (list item name value) result))))
  result)

答案 1 :(得分:1)

这将创建一个hash-table,其中键是符号,值的排列方式为(maximum . (list of strings corresponding to maximum))

(let ((data (list (list 'object1 "wabadu" 0.5)
                  (list 'object2 "xezulu" 0.6)
                  (list 'object1 "yebasi" 0.5)
                  (list 'object1 "tesora" 0.2))))
  (loop
     :with table := (make-hash-table)
     :for (item string num) :in data :do
     (destructuring-bind (&optional max strings)
         (gethash item table)
       (cond
         ((or (null max) (< max num))
          (setf (gethash item table) (list num (list string))))
         ((= max num)
          (setf (cdr strings) (cons string (cdr strings))))))
     :finally (return table)))

;; #<HASH-TABLE {1005C6BE93}>
;; --------------------
;; Count: 2
;; Size: 16
;; Test: EQL
;; Rehash size: 1.5
;; Rehash threshold: 1.0
;; [clear hashtable]
;; Contents: 
;; OBJECT1 = (0.5 ("wabadu" "yebasi")) [remove entry]
;; OBJECT2 = (0.6 ("xezulu")) [remove entry]

我认为使用此哈希表然后使用您当前拥有的数据结构,您的生活将更加轻松。

答案 2 :(得分:0)

maximize只返回一个元素。您可以按第3个组件对所有列表进行排序,然后获取前面的组件。像这样:

;;; suppose a copy of the data is stored in l

;; get all 'object1 and sort them
(setf l (sort (remove-if-not
                (lambda (x) (equal (first x) 'object1)) l)
              #'> :key #'third))
;; remove the ones with smaller value than the first one
(setf l (remove-if
          (lambda (x) (< (third x) (third (first l)))) l))

答案 3 :(得分:0)

提取您的数据以创建基本构建块;将构建块组合到您所需的功能中:

(defun make-foo (type name score)
   (list type name score))

(defun foo-type (foo) (elt foo 0))
;; ...

(defun make-foos (&rest foos)
  foos)

(defun foos-find-if (foos predicate)
  ;; return all foos satisfying predicate
  )

(defun foos-maximize (foos orderer)
  ;; return the maximum foo (any one)
  )

(defun foos-find-if-maximized (foos)
  (foos-find-if foos 
    (let ((max (foos-maximize foos #'foo-score)))
      (lambda (foo)
        (= (foo-score max) (foo-score foo))))))

答案 4 :(得分:0)

这是一种方法,首先保存只包含搜索对象列表的symbol-list。然后我们可以轻松获得最大值并删除那些值较小的列表。

(defun foo (symbol list)
  (let* ((symbol-list (remove-if-not #'(lambda (l) (eq (first l) symbol))
                       list))
         (max (apply #'max (mapcar #'third symbol-list))))
    (remove-if-not #'(lambda (l) (= (third l) max))
     symbol-list)))

我们可以称之为:(foo 'object1 l)

答案 5 :(得分:0)

您可以loop在列表中执行此操作,以选择具有正确第一个元素的所有子列表并确定最大值(您可以使用intoloop累积多个值),然后loop子句中的第二个finally进行选择,现在只选择那些得分最高的那些:

(loop for triple in *l*
      for (key nil score) = triple
      when (eq key 'object1)
        collect triple into selection
        and maximize score into max-score
      finally (return (loop for triple in selection
                            when (eql (third triple) max-score)
                              collect triple)))

编辑:或者,代替第二个循环,可以非常简洁地使用delete函数:

(loop for triple in *l*
      for (key name score) = triple
      when (eq key 'object1)
        collect triple into selection
        and maximize score into max-score
      finally (return (delete max-score selection
                              :test #'/=
                              :key #'third)))

答案 6 :(得分:0)

根据经验,如果你真的想将一系列事物简化为一个结果,那么应该有一个很好的方法来实现这一点。

还有:

(defun collect-maxima-by-third (list)
  (reduce
   #'(lambda (max-list next-element)
       (let ((max-value (third (first max-list)))
             (next-value (third next-element)))
         (cond ((< max-value next-value)
                (list next-element))
                ((= max-value next-value)
                 (cons next-element max-list))
                (t max-list)))) ; the greater-than case
   (rest list)
   :initial-value (list (first list))))

它并不完美,就好像你给它一个空列表一样,它会给你一个包含空列表的列表,而不仅仅是一个空列表,但如果你认为这种情况经常发生,你可以轻松地为此添加一个案例。 / p>

这种技术(可能不是这个确切的例子)详细介绍了函数式编程的各种文本;一些Haskell文本做得特别好(想想你是一个Haskell)。