2013-07-22 3 views
6

У меня есть два списка:застегивать два список различной длины в Elisp

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

Как я могу создать список минусов клеток (("a" . "1") ("b" . "2") ("c" . "3") ("a" . "4")) с коротким списком переработан?

+0

Я действительно думал, что круговой список был полезен в этом случае; он не превращается ... – RNA

ответ

6

Вот мое взятие:

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

I добавьте чек, какой из них больше, но это испортит краткость :).

+0

отличное решение - именно то, что я ищу. благодаря! – RNA

+0

Но если вы не хотите, чтобы исходные списки * изменены *, то используйте 'copy-sequence' в более коротких' x' и 'y', а затем используйте' setcdr' в этом новом списке. – Drew

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

принять первые n элементы из бесконечного cycled списка, сделайте (-take n (-cycle xs)). Поэтому, чтобы создать ассоциативный список, в котором элементы из меньшего списка циклически, написать:

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

Я пошел с рекурсивным подходом, который, казался естественным для шепелявости.

(defun zip (xs ys) 
    (cond 
    ((or (null xs) (null ys))()) 
    (t (cons (cons (car xs) (car ys)) (zip (cdr xs) (cdr ys)))))) 
Смежные вопросы