Lisp良好的做法

时间:2015-10-05 09:53:49

标签: algorithm lisp common-lisp ansi-common-lisp

我在2天前开始学习Lisp,并且我正在阅读Paul Graham的ANSI Common List,它以非常有趣的方式公开了语言结构。它对于初学者来说并不是太多理论上的,也不是太浅薄(就像Sierra-Bate的头一个Java,我个人讨厌)。在简短的一般语言介绍之后,他开始讨论回合列表并提出简单列表压缩的示例。基本上,让el成为重复n次的元素。您可以用一个(n el)列表替换所有这些。为此,他给出了一个代码实现,但我尝试自己做,显然是有效的。我想,如果可能的话,有人会分析我的代码并向我展示其实现的关键点,我确信它有很多,因为它是我第一次接触Lisp。谢谢大家!

(defun compress (x)
"compress a list replacing repeated sequential values for (<n> <value>)"
(let ((lst '()) (fst t) (lt nil) (n 0)) 
    (dolist (el x)
        (if fst
            (progn
                (setq fst nil)
                (setq lt el)
                (setq n 1))
            (progn 
                (if (equal el lt)
                    (setq n (+ n 1))
                    (progn
                        (setq lst (cons (if (= n 1)
                                    lt
                                    (list n lt)) 
                                lst))
                        (setq lt el)
                        (setq n 1)
                    )))))
    (setq lst (cons (if (and (not (= n 0)) (= n 1))
                        lt
                        (list n lt)) 
            lst))
(reverse lst)))

2 个答案:

答案 0 :(得分:2)

算法似乎很合理。我只发现fst变量是多余的。只有在进入循环时才使用它,所以你也可以将第一组赋值移到前导码上并迭代列表的其余元素。

现在问题是如何在Lisp中表达算法。

最重要的一点是,您可以使用nreverse代替reversenreverse效率更高,但它会破坏其论证的结构。一般来说,由于所谓的共享结构,你不希望这样:一个cons单元可以是几个列表的一部分,所以修改一个缺点卖掉你可以在明显不相关的列表中带来意想不到的变化。 (PCL很好地处理了这个问题。)但是,在你的函数中,你从头开始构造lst,手动将新元素推送到它上面,所以它无法与其他列表共享结构。所以你可以放心地免除结构。这是常见的push/nreverse成语。 reverse只是提出一个新列表,lst变成垃圾。

为了使代码更加惯用,您可以使用incf的{​​{1}}和+=的{​​{1}}等标准快捷方式。如今,通用push似乎比cons=更受欢迎。就个人而言,我更喜欢使用setf,否则会出现setq。所以你最终会得到这样的东西:

cond

答案 1 :(得分:2)

除了Stanislav's answer 我想展示另一种可能的实现方式。 压缩算法是REDUCE的完美用例,也称为fold。 reduce函数到目前为止采用了一个新元素,并将它们组合起来产生下一个结果。你想要的结果是夫妻名单。初始元素是空列表。

(defun compress (sequence)
  (reduce (lambda (number list &aux (head (first list)))
            (if (eql number (first head))
                (prog1 list
                  (incf (second head)))
                (cons (list number 1) list)))
          sequence
          :from-end t
          :initial-value nil))

从末尾应用,以便您可以在保持现有元素顺序的同时,轻松cons在当前结果之上新建一对。如果输入为'(a b b c),则将按如下方式调用匿名还原函数:

number  list           new list
---------------------------------------------
c       nil            ((c 1))
b       ((c 1))        ((b 1)(c 1))
b       ((b 1)(c 1))   ((b 2)(c 1))
a       ((b 2)(c 1))   ((a 1)(b 2)(c 1))
为序列定义

REDUCE,压缩函数是通用的:

;; string
(compress "aaddzadzaaaddb")
=> ((#\a 2) (#\d 2) (#\z 1) (#\a 1) (#\d 1) (#\z 1) (#\a 3) (#\d 2) (#\b 1))

;; vector
(compress #(1 2 3 3 3 3 4 4 4 55 5 5 5 5 5 5 ))
=> ((1 1) (2 1) (3 4) (4 3) (55 1) (5 6))

;; list
(compress '(1 2 3 3 3 3 4 4 4 55 5 5 5 5 5 5 ))
=> ((1 1) (2 1) (3 4) (4 3) (55 1) (5 6))