在elisp中压缩两个不同长度的列表

时间:2013-07-22 22:10:59

标签: elisp

我有两个清单:

(setq x (list "a" "b" "c"))
(setq y (list "1" "2" "3" "4"))

如何创建一个使用较短列表回收的净值单元格(("a" . "1") ("b" . "2") ("c" . "3") ("a" . "4"))列表?

4 个答案:

答案 0 :(得分:6)

这是我的看法:

(require 'cl-lib)
(cl-mapcar #'list (setcdr (last x) x) y)

我会检查哪一个更大,但这会破坏简洁:)。

答案 1 :(得分:1)

肯定有一种更简单的方法,但是这里有一个版本可以将输入序列转换为无限列表并将它们拉到一起:

(defun* cycle-iterator (xs &optional (idx 0) (len (length xs)))
  "Create an iterator that will cycle over the elements in XS.
Return a cons, where the car is the current value and the cdr is
a function to continue the iteration."
  (cons (nth (mod idx len) xs)
        (eval `(lambda () (cycle-iterator ',xs ,(1+ idx) ,len)))))

(defun cycle-take (xs n)
  "Take N elements from XS, cycling the elements if N exceeds the length of XS."
  (loop
   when (plusp n)
   ;; Creating the iterator returns the first value. Subsequent calls can then
   ;; be processed in a loop.
   with (value . iterator) = (cycle-iterator xs)
   with acc = (list value)
   repeat (1- n) do (destructuring-bind (val . next) (funcall iterator)
                      (setq iterator next)
                      (setq acc (cons val acc)))
   finally (return (nreverse acc))))

(defun cycling-zip (xs ys)
  "Zip XS and YS together, cycling elements to ensure the result
  is as long as the longest input list."
  (loop
   with limit = (max (length xs) (length ys))
   for x in (cycle-take xs limit)
   for y in (cycle-take ys limit)
   collect (cons x y)))


;; Usage:
(cycling-zip '("a" "b" "c") '("1" "2" "3" "4"))
; => (("a" . "1") ("b" . "2") ("c" . "3") ("a" . "4"))

答案 2 :(得分:1)

这个答案需要dash列表操作库。在攻击您的问题之前,找到最长列表的长度是件好事。我提出的第一种方式是:

(require 'dash)
(require 'dash-functional)
(length (-max-by (-on '> 'length) (list x y))) ; 4

-on是来自包dash-functional的智能函数,它接受一个比较器,一个要比较的键,并返回一个比较该键的函数。因此,(-max-by (-on '> 'length) xs)会在xs中找到一个元素,其长度最大。但是这个表达式本身太聪明了,dash-functional只能在Emacs 24中使用词法作用域。让我们重写一下,灵感来自Python solution

(-max (-map 'length (list x y))) ; 4

要从无限cycled列表中获取第一个n元素,请执行(-take n (-cycle xs))。因此,要创建一个alist,其中循环显示较小列表中的元素,请写:

(let ((len (-max (-map 'length (list x y)))))
  (flet ((cycle (xs) (-take len (-cycle xs))))
    (-zip (cycle x) (cycle y)))) ; (("a" . "1") ("b" . "2") ("c" . "3") ("a" . "4"))

答案 3 :(得分:0)

我采用了递归方法,这对于lisp来说似乎很自然。

(defun zip (xs ys)
  (cond
   ((or (null xs) (null ys)) ())
   (t (cons (cons (car xs) (car ys)) (zip (cdr xs) (cdr ys))))))