计算物品的优雅方式

时间:2011-05-18 19:29:26

标签: emacs lisp elisp

我有一个这样的清单:

  '(("Alpha" .  1538)
    ("Beta"  .  8036)
    ("Gamma" .  8990)
    ("Beta"  .  10052)
    ("Alpha" .  12837)
    ("Beta"  .  13634)
    ("Beta"  .  14977)
    ("Beta"  .  15719)
    ("Alpha" .  17075)
    ("Rho"   .  18949)
    ("Gamma" .  21118)
    ("Gamma" .  26923)
    ("Alpha" .  31609))

如何计算列表中每个元素的汽车中术语的总出现次数?基本上我想要:

(("Alpha" . 4)
 ("Beta" . 5)
 ("Gamma" . 3)
 ("Rho" . 1))

不,这不是功课。我只是没有在Lisp"中思考。事情还没有。

在C#中,我会使用LINQ来执行此操作。我也可以在lisp中使用while循环等,但我想这样做的方式似乎过于复杂。


修改

这就是我所拥有的:

(defun count-uniq (list)
  "Returns an alist, each item is a cons cell where the car is
a unique element of LIST, and the cdr is the number of occurrences of that
unique element in the list. "
  (flet ((helper (list new)
                 (if (null list)
                     new
                   (let ((elt (assoc (car list) new)))
                     (helper (cdr list)
                             (if elt
                                 (progn (incf (cdr elt)) new)
                               (cons (cons (car list) 1) new)))))))
    (nreverse (helper list nil))))

9 个答案:

答案 0 :(得分:5)

(defun freqs (list &optional test key)
  (let ((h (make-hash-table :test test)))
    (dolist (x list)
      (let ((key (if key (funcall key x) x)))
        (puthash key (1+ (gethash key h 0)) h)))
    (let ((r nil))
      (maphash #'(lambda (k v) (push (cons k v) r)) h)
      (sort r #'(lambda (x y) (< (cdr x) (cdr y)))))))

(freqs '(("Alpha" .  1538)
         ("Beta"  .  8036)
         ("Gamma" .  8990)
         ("Beta"  .  10052)
         ("Alpha" .  12837)
         ("Beta"  .  13634)
         ("Beta"  .  14977)
         ("Beta"  .  15719)
         ("Alpha" .  17075)
         ("Rho"   .  18949)
         ("Gamma" .  21118)
         ("Gamma" .  26923)
         ("Alpha" .  31609))
       #'equal #'car)

答案 1 :(得分:4)

结合更高级别的Common Lisp函数:

(defun count-unique (alist) 
  (mapcar
    (lambda (item)
      (cons (car item)
            (count (car item) alist :test #'equal :key #'car)))
    (remove-duplicates alist :test #'equal :key #'car)))

虽然它不能扩展到大型列表。如果您需要O(n)性能,请使用基于哈希表的解决方案,例如不太优雅:

(defun count-unique (alist)
  (loop
     with hash = (make-hash-table :test #'equal)
     for (key . nil) in alist
     do (incf (gethash key hash 0))
     finally (return
               (loop for key being each hash-key of hash
                  using (hash-value value)
                  collect (cons key value)))))

答案 2 :(得分:2)

我不知道这是最优雅的,但似乎是合理的:

(defun add-for-cheeso (data)
  (let (result)
    (dolist (elt data result)
      (let ((sofar (assoc (car elt) result)))
        (if sofar
            (setcdr sofar (1+ (cdr sofar)))
          (push (cons (car elt) 1) result))))))

答案 3 :(得分:2)

使用Common Lisp扩展:

(require 'cl)
(loop with result = nil
      for (key . dummy) in original-list
      do (incf (cdr (or (assoc key result)
                        (first (push (cons key 0) result)))))
      finally return (sort result
                           (lambda (a b) (string< (car a) (car b)))))

如果您不关心对最终结果进行排序,可以说finally return result

答案 4 :(得分:1)

(require 'cl)
(defun count-uniq (list)
  (let ((k 1) (list (sort (mapcar #'car list) #'string<)))
    (loop for (i . j) on list
          when (string= i (car j)) do (incf k)
          else collect (cons i k) and do (setf k 1))))

答案 5 :(得分:1)

使用高阶函数排序 reduce

首先排序(使用 string&lt; )然后减少(计算cons单元格中连续的 string = 值):

(reduce (lambda (r e)
          (if (and r (string= (caar r) e))
              (cons
               (cons (caar r) (1+ (cdar r)))
               (cdr r))
            (cons (cons e  1) r)))
        (sort (mapcar 'car alist) 'string<)
        :initial-value nil)

答案 6 :(得分:0)

以下是我认为使用Emacs的alist函数的优雅功能解决方案,产生类似于Eli答案的可重用frequencies函数:

(defun frequencies (vals)
  (reduce
   (lambda (freqs key)
     (cons (cons key (+ 1 (or (cdr (assoc key freqs)) 0)))
           (assq-delete-all-with-test key freqs 'equal)))
   vals
   :initial-value nil)))

(frequencies (mapcar 'car
                     '(("Alpha" .  1538)
                       ("Beta"  .  8036)
                       ("Gamma" .  8990)
                       ("Beta"  .  10052)
                       ("Alpha" .  12837)
                       ("Beta"  .  13634)
                       ("Beta"  .  14977)
                       ("Beta"  .  15719)
                       ("Alpha" .  17075)
                       ("Rho"   .  18949)
                       ("Gamma" .  21118)
                       ("Gamma" .  26923)
                       ("Alpha" .  31609))))
=> (("Alpha" . 4) ("Gamma" . 3) ("Rho" . 1) ("Beta" . 5))

答案 7 :(得分:0)

使用dash library

,这非常容易且非常简单
(require 'dash)

(defun frequencies (values)
  "Return an alist indicating the frequency of values in VALUES list."
  (mapcar (-lambda ((value . items))
          (cons value (length items)))
        (-group-by #'identity
                   values)))

(frequencies (mapcar #'car my-list))

答案 8 :(得分:0)

感谢cl-incfalist-get的支持:

;; (require 'cl-lib)
(defun simple-count (seq)
  "Count each unique element in SEQ."
  (let (counts)
    (dolist (element seq)
      (cl-incf (alist-get element counts 0 nil 'equal)))
    counts))

示例:

(let ((data '(("Alpha" .  1538)
              ("Beta"  .  8036)
              ("Gamma" .  8990)
              ("Beta"  .  10052)
              ("Alpha" .  12837)
              ("Beta"  .  13634)
              ("Beta"  .  14977)
              ("Beta"  .  15719)
              ("Alpha" .  17075)
              ("Rho"   .  18949)
              ("Gamma" .  21118)
              ("Gamma" .  26923)
              ("Alpha" .  31609))))
  (simple-count (mapcar 'car data)))
=> (("Rho" . 1) ("Gamma" . 3) ("Beta" . 5) ("Alpha" . 4))