Lisp 请批评我的口齿不清

Lisp 请批评我的口齿不清,lisp,common-lisp,Lisp,Common Lisp,我创建了一个很好的小程序: (defun unzip (seq) "Takes an even-length list and breaks it apart by evens/odd index" (let ((oddresult '()) (evenresult '())) (loop for n from 0 to (- (length seq) 1) do (if (oddp n) (push (nth n seq) oddresu

我创建了一个很好的小程序:

(defun unzip (seq)
  "Takes an even-length list and breaks it apart by evens/odd index"
  (let ((oddresult '())
    (evenresult '()))
    (loop for n from 0 to (- (length seq) 1) do
      (if (oddp n)
          (push (nth n seq) oddresult)
        (push (nth n seq) evenresult)))
    (list (reverse oddresult) (reverse evenresult))))
使用它:

CL-USER> (unzip '(1 2 3 4 5 6))
((2 4 6) (1 3 5))
但是,我很清楚我在任何语言中编写BOTCH C++的能力,希望能对我的代码<未解压缩< /代码>进行分析,以获得良好的通用LISP风格。
  • 不要在列表的元素上循环,应该对每个或
    car
    cdr
    使用
    ,在列表上循环,直到列表为空
  • 我不会叫它
    unzip
    ,因为它不是
    zip
    的反面
  • 编辑:更具体地说,我希望
    zip
    获取一对列表并返回一对列表,因此
    unzip
    应该获取一对列表并返回一对列表。我希望它看起来像我发现的这个例子:

    (defun unzip (list)
      (let ((a '())
            (b '()))
        (for-each (lambda (i) (push (first i) a) (push (second i) b)) list)
        (values (nreverse a) (nreverse b))))
    
    首先要注意的是,
    '()
    ()
    是等价的,因为空列表是自评估的,等于
    NIL
    ,并且在任何情况下都不需要它们,因为
    NIL
    由`(let(变量)…)语法暗示,这就是为什么在给定起始值时需要在每个绑定周围加一个括号的原因

    对于这种情况,使用
    LET
    并不是必需的。更广泛地使用功能此函数可以写成:

    (defun unzip (seq)
      "Takes an even-length list and breaks it apart by evens/odd index"
      (loop for n from 0
            for element in seq
            if (oddp n)
              collect element into oddresult
            else
              collect element into evenresult
            finally (return (list oddresult evenresult))))
    
    (defun unzip (seq)
      "Takes an even-length list and breaks it apart by evens/odd index"
      (iter (for element in seq)
            (for n from 0)
            (if (oddp n)
                (collect element into oddresult)
                (collect element into evenresult))
            (finally (return (list oddresult evenresult)))))
    
    就我个人而言,我更喜欢大多数迭代,使用它可以写成:

    (defun unzip (seq)
      "Takes an even-length list and breaks it apart by evens/odd index"
      (loop for n from 0
            for element in seq
            if (oddp n)
              collect element into oddresult
            else
              collect element into evenresult
            finally (return (list oddresult evenresult))))
    
    (defun unzip (seq)
      "Takes an even-length list and breaks it apart by evens/odd index"
      (iter (for element in seq)
            (for n from 0)
            (if (oddp n)
                (collect element into oddresult)
                (collect element into evenresult))
            (finally (return (list oddresult evenresult)))))
    
    甚至:

    (defun unzip (seq)
      "Takes an even-length list and breaks it apart by evens/odd index"
      (iter (generate element in seq)
            (collect (next element) into evenresult)
            (collect (next element) into oddresult)
            (finally (return (list oddresult evenresult)))))
    

    编辑:附加说明:名称
    unzip
    通常表示一个稍有不同的功能。参数名称实际上应该是
    list
    ,因为
    seq
    会建议函数也采用向量。虽然可以在广义序列上运行函数,但通常不建议这样做,因为列表和向量具有不同的性能特征。特别是,通过
    NTH
    的随机访问对于列表来说是线性时间,这意味着您几乎不应该使用它。即使时间成本微不足道,它通常也表明您应该使用不同的数据结构。

    我能看到的唯一不好的事情是
    nth
    。您应该遍历列表,因为
    n
    可能需要遍历列表。忘记数组和数组索引:)

    不幸的是,我不知道如何在Lisp中做到这一点,但在Scheme中,您只需要使用一个名为
    let

    递归解决方案:

    (defun unzip (seq &optional oddresult evenresult)
      (if (null seq)
          (list (reverse oddresult) (reverse evenresult))
        (if (oddp (car seq))
            (unzip (cdr seq) (cons (car seq) oddresult) evenresult)
          (unzip (cdr seq) oddresult (cons (car seq) evenresult)))))
    

    我会用循环或递归函数来解决这个问题

    (define (unzip l)
      (define (iter e v)
        (list (not (car v)) (caddr v) (cons e (cadr v))))
      (define (swap-if p a b)
        (if p (list b a) (list a b)))
      (map reverse
           (apply swap-if (fold iter '(#t () ()) l))))
    
    对于一个基于循环的解决方案,@Ramarren已经基本解决了这个问题

    如果知道什么来自偶数索引和什么来自奇数索引并不重要,我会使用以下方法:

    (defun unzip (list &optional acc1 acc2)
       (if seq
           (unzip (cdr list) acc2 (cons (car list) acc1)))
           (list (nreverse acc1) (nreverse acc2))))
    

    如果有必要知道奇数或偶数索引的结果,我会将解压包在一个助手函数中,该函数比较列表的CAR和返回值的CAAR。如果它们相同,请将其传递回,否则交换结果列表中的两个列表。

    让我们看看您的代码:

    (defun unzip (seq)
      "Takes an even-length list and breaks it apart by evens/odd index"
    
     ; you name the variable seq (sequence), but the documentation mentions
     ; only lists. Sequence is in CL an abstraction over lists and vectors. 
    
     ; also nothing in the code really says that the list needs to be even-length  
    
      (let ((oddresult  '())
            (evenresult '()))
        (loop for n from 0 to (- (length seq) 1) do  ; you can iterate BELOW         
          (if (oddp n)
              (push (nth n seq) oddresult)    ; <- NTH is inefficient for lists
            (push (nth n seq) evenresult)))   ; <- NTH is inefficient for lists
        (list (reverse oddresult)             ; <- return multiple values
              (reverse evenresult))))
    
    示例(请注意,CL中的索引是基于零的,我更改了示例以减少混淆):

    以下是一个期望长度为偶数的列表并使用循环的版本:

    (defun unzip (list)
      "Takes an even-length list and breaks it apart by evens/odd index"
      (loop for (even odd) on list by #'cddr
            collect even into evenresult
            collect odd  into oddresult
            finally (return (values oddresult evenresult))))
    
    函数
    (第n个n列表)
    需要遍历列表以访问第n个元素,这是一个O(n)操作,在您的实现中它被调用了O(n)次,使整个过程成为O(n^2)。您可以在O(n)中完成相同的操作:

    (解压缩(列表) (通过#的cddr为列表中的(x y)循环 将x收集到一个 将y收集到b中 最后(返回(列表a和b)))
    方案版本,仅用于目标实践。:-)(需要SRFI 1的
    折叠功能。)


    要更改返回列表的顺序(即,首先是偶数索引元素),只需将
    #t
    更改为
    #f

    以下是另一种可能的解决方案,使用递归:

    (defun unzip (l)
      (labels ((every-other (l) (if l (cons (car l) (every-other (cddr l))))))
        (list (every-other (cdr l)) (every-other l))))
    
    首先,我们每隔一个定义一个helper函数,它从列表中获取其他元素。然后,我们在原始列表上运行此函数,跳过第一项,并在原始列表上运行此函数


    缺点:由于递归,它可能会为大型列表堆栈溢出。如果它必须处理大型列表,请参阅@Vatine以获得尾部递归解决方案。

    这里有一个循环版本,对奇数长度输入不敏感,它只是@huaiyuan答案的一个小变化,并添加了一个when子句:

    (defun unzip (list)
      (loop for (x y) on list by #'cddr
         collect x into a
         when y
         collect y into b
         finally (return (list a b))))
    

    嘲笑别人的言语冲动是不礼貌的。@Paul Nathan结果应该是
    ((1 3 5)(2 4 6))
    ,对吗?@ring0:在任何编程语言中,都有一种习惯用法和正确的风格,而且有人对学习和讨论它感兴趣。甚至还有一个新词“pythonic”。括号太多了。@Frank:我想Dave是开玩笑的。你应该在奇数或偶数索引上拆分,而不是奇数或偶数元素。使用递归不是一个好主意,因为需要对尾部递归进行优化。它不是普通标准CL的一部分,在支持它的每个实现中调用不同。给定plist,它与
    zip
    相反,不是吗?但在一般情况下,没有。提到n次是坏的,你会得到+1
    (defun unzip (list)
      (loop for (x y) on list by #'cddr
         collect x into a
         when y
         collect y into b
         finally (return (list a b))))