4

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

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

但是,这只会返回一个元素。我想要的是所有最大的元素。我尝试了一些与collectand的组合maximize,但我对语法的了解很少。有没有办法在“简单”函数中收集所有最高元素?

4

7 回答 7

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)
于 2013-10-07T14:41:48.863 回答
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]

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

于 2013-10-07T15:42:41.083 回答
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))))))
于 2013-10-07T16:01:09.153 回答
0

您可以loop通过列表一次来选择具有正确第一个元素的所有子列表并确定最大值(您可以使用intoloop多个值累积),然后loopfinally子句中的第二个通过选择,现在只选择得分最高的人:

(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)))
于 2013-10-08T13:53:55.980 回答
0

根据经验,如果您真的想将一系列事物归结为一个结果,那么使用 reduce 应该有一个很好的方法来做到这一点。

还有:

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

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

这种技术(可能不是这个确切的例子)在函数式编程的各种文本中都有详细说明;一些 Haskell 文本做得特别好(Learn You a Haskell 浮现在脑海中)。

于 2013-10-10T22:13:17.797 回答
0

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

;;; 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))
于 2013-10-07T14:37:22.797 回答
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)

于 2013-10-07T19:35:37.413 回答