代码之家  ›  专栏  ›  技术社区  ›  Paul Nathan

请评论我的口齿不清[关闭]

  •  12
  • Paul Nathan  · 技术社区  · 15 年前

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

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

    然而,我敏锐地意识到我有能力 unzip 对于良好的通用Lisp样式。

    10 回复  |  直到 15 年前
        1
  •  13
  •   Ramarren    15 年前

    首先要注意的是 '() () 是等效的,因为空列表是自评估的,并且等于 NIL LET 因为

    不是用 LET LOOP 功能更广泛此功能可以写成:

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

    就我个人而言,我更喜欢 iterate

    (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 是列表的线性时间,这意味着您几乎不应该使用它。即使时间成本微不足道,它通常也表明您应该使用不同的数据结构。

        2
  •  8
  •   huaiyuan    15 年前

    功能 (nth n list) 需要遍历列表才能访问第n个元素,这是一个O(n)操作,在您的实现中它被调用了O(n)次,使整个过程成为O(n^2)。您可以在O(n)中完成相同的操作:

    (defun unzip (list)
      (loop for (x y) on list by #'cddr
            collect x into a
            collect y into b
            finally (return (list a b))))
    
        3
  •  3
  •   Rainer Joswig    15 年前

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

    以下是列表的版本:

    (defun unzip (list &aux oddresult evenresult (odd t))
      "Takes a list and breaks it apart by evens/odd index"
      (dolist (element list (values (reverse oddresult) (reverse evenresult)))
        (if (setf odd (not odd))
            (push element oddresult)
          (push element evenresult))))
    

    示例(请注意,CL中的索引是基于零的,我更改了示例以减少混淆):

    CL-USER 10 > (unzip '(0 1 2 3 4 5))
    (1 3 5)
    (0 2 4)
    

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

    (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))))
    
        4
  •  2
  •   leppie    15 年前

    我能看到的唯一不好的事情是 nth . 您应该在列表中迭代,而不是 N

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

        5
  •  1
  •   Vatine    15 年前

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

    对于一个基于循环的解决方案,@Ramarren已经基本解决了这个问题。

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

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

        6
  •  1
  •   C. K. Young    15 年前

    方案版本,仅用于目标实践。:-)(需要SRFI 1 fold 功能。)

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

    #t #f .

        7
  •  1
  •   Gabe Timothy Khouri    15 年前
    • for-each car cdr 对列表进行迭代,直到其为空。
    • unzip 因为它与 zip .

    编辑:更具体地说,我希望 拉链 要获取一对列表并返回一对列表,请 应该获取一对列表并返回一对列表。我希望它看起来像我发现的这个例子:

    (defun unzip (list)
      (let ((a '())
            (b '()))
        (for-each (lambda (i) (push (first i) a) (push (second i) b)) list)
        (values (nreverse a) (nreverse b))))
    
        8
  •  1
  •   lpetru    15 年前

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

    (defun unzip (l)
      (labels ((every-other (l) (if l (cons (car l) (every-other (cddr l))))))
        (list (every-other (cdr l)) (every-other l))))
    

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

        9
  •  1
  •   nowbumja    14 年前

    这是一个对奇数长度输入不敏感的循环版本,它只是@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))))
    
        10
  •  0
  •   Vijay Mathew    15 年前

    递归解决方案:

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