常见的Lisp身份组

时间:2018-10-16 08:56:52

标签: grouping common-lisp

我是一个精打细算的初学者,我编写了一个函数来将列表中的equal个相邻项目分组。如果Lisp的专家可以给我一些建议,让我对此功能进行更好的简洁编写,我将不胜感激。预先感谢!

(defun identity-groups (lst)
  (labels ((travel (tail group groups)
         (cond ((endp tail) (cons group groups))
           ((equal (car tail) (car (last group)))
            (travel (cdr tail) (cons (car tail) group) groups))
           (t (travel (cdr tail) (list (car tail)) (cons group groups))))))
    (reverse (travel (cdr lst) (list (car lst)) nil))))

(identity-groups '(1 3 5 4 4 4 4 5 1 2 2 2 1 2 3 3 3 3 3 4 5 6 7))
;; => ((1) (3) (5) (4 4 4 4) (5) (1) (2 2 2) (1) (2) (3 3 3 3 3) (4) (5) (6) (7))

3 个答案:

答案 0 :(得分:3)

看起来不错!

  • int似乎等同于const component = ({name, email, id}) => { const gotoSecondContent = () => { window.location = "secondContent.html"; } return ( <button onclick={ gotoSecondContent } id="component"> <div>header</div> </button>); }

  • 要使元素保持原始顺序,请反转每个组的项目。

  • 在您自己构建结果列表(equal (car tail) (car (last group)))时,使用(equal (car tail) (car group))代替groups是安全,高效的。

  • 使用nreverse作为参数而不是reverse时没有名称冲突,因为变量和函数位于不同的命名空间(“ Lisp-2”)中。

    < / li>
  • 给予类似list这样的实用函数这样的好方法,以便调用者可以决定何时将列表元素视为相等(请参见Common lisp :KEY parameter use),以加入通用函数的范畴。例如lst&key test keymember

  • 还有一个文档字符串! :)

更新版本:

find

测试:

sort

答案 1 :(得分:3)

期望的函数适合以下模式:从已知子结果G1和新值中构建值G0,并可以使用REDUCE来实现。

匿名归约函数的第一个参数是累加器,这里是组的列表。第二个参数是新值。

(reduce (lambda (groups value)
           (let ((most-recent-group (first groups)))
              (if (equal (first most-recent-group) value)
                  (list* (cons value most-recent-group) (rest groups))
                  (list* (list value) groups))))
        '(1 3 5 4 4 4 4 5 1 2 2 2 1 2 3 3 3 3 3 4 5 6 7)
        :initial-value ())

结果是:

((7) (6) (5) (4) (3 3 3 3 3) (2) (1) (2 2 2) (1) (5) (4 4 4 4) (5) (3) (1))

您的代码中的一个问题是调用last以访问最后一个组,这使代码一次又一次遍历列表。通常,您应避免将列表视为数组,而应将它们用作堆栈(仅manipualte最上面的元素)。

如果需要反转元素,则可以在每个组的末尾(等效值之间的顺序)或整个函数的末尾(组之间的顺序)使用它。

答案 2 :(得分:1)

“经典”递归解决方案

(defun identity-groups (l &key (test #'eql))
  (labels ((group (l last-group acc)
              (cond ((null l) (cons last-group acc))
                    ((and last-group (funcall test (car l) (car last-group)))
                     (group (cdr l) (cons (car l) last-group) acc))
                    (t
                     (group (cdr l) (list (car l)) (cons last-group acc))))))
    (cdr (reverse (group l '() '())))))

较旧的版本(要求初始值不等于第一个列表元素)

因此上述版本摆脱了这个关键论点。

(defun identity-groups (l &key (test #'eql) (initial-value '(0))) 
  (labels ((group (l last-group acc)
              (cond ((null l) (cons last-group acc))
                    ((funcall test (car l) (car last-group))
                     (group (cdr l) (cons (car l) last-group) acc))
                    (t
                     (group (cdr l) (list (car l)) (cons last-group acc))))))
    (cdr (reverse (group l initial-value '())))))

即兴式循环结构

还尝试了do的循环构造。

(defun group-identicals (l &key (test #'eql))
  (let ((lx) (tmp) (res))                       ;; initiate variables
    (dolist (x l (reverse (cons tmp res)))      ;; var list return/result-value
      (cond ((or (null lx) (funcall test x lx)) ;; if first round or 
             (push x tmp)            ;; if last x (lx) equal to current `x`,
             (setf lx x))          ;; collect it in tmp and set lx to x
            (t (push tmp res)      ;; if x not equal to lastx, push tmp to result
               (setf tmp (list x)) ;; and begin new tmp list with x
               (setf lx x))))))    ;; and set last x value to current x
    (cdr (reverse (group l initial-value '()))))) 
    ;; cdr removes initial last-group value

;; test:
(group-identicals '(1 2 3 3 4 4 4 4 5 5 6 3 3 3 3))
;; ((1) (2) (3 3) (4 4 4 4) (5 5) (6) (3 3 3 3))
(group-identicals '("a" "b" "b" "c" "d" "d" "d" "e") :test #'string=)
;; (("a") ("b" "b") ("c") ("d" "d" "d") ("e"))