2010-10-09 7 views
12

Stworzyłem miły mały rutyna:Krytyka mój Lisp, proszę

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

I z niego korzystać:

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

Jestem jednak uwrażliwione moją zdolność do zapisu Pech C++ w dowolnym języku i chciałbym dokonać analizy mojego unzip dla dobrego stylu Common Lisp.

+5

Nie byłoby uprzejmy drażnić ktoś o ich wymowie w wymowie. –

+0

Lisp przypomina mi starożytną grekę, gdzie nie ma prawdziwej użyteczności (poza Emacsem), tylko kilka osób potrafi posługiwać się językiem, a jednocześnie ci sami ludzie, najbardziej wykształceni, lubią o tym rozmawiać i jego styl przez wiele godzin :-) –

+1

@ ring0: W każdym języku programowania istnieje idiom i odpowiedni styl, a są osoby zainteresowane nauką i dyskusją. Istnieje nawet neologizm - "pytoniczny". –

Odpowiedz

0
  • Zamiast pętli na elementach listy, należy użyć for-each lub car i cdr iteracyjne nad listy, dopóki nie jest pusta.
  • Nie nazwałbym tego unzip, ponieważ nie jest przeciwieństwem zip.

Edit: A dokładniej, spodziewam zip wziąć parę list i zwraca listę par, więc unzip powinien wziąć listę par i powrócić parę list. Spodziewam się, że wyglądało to przykład znalazłem:

(defun unzip (list) 
    (let ((a '()) 
     (b '())) 
    (for-each (lambda (i) (push (first i) a) (push (second i) b)) list) 
    (values (nreverse a) (nreverse b)))) 
+0

Biorąc pod uwagę paczkę, jest przeciwieństwem 'zip', nieprawdaż? Ale w ogólnym przypadku nie. –

14

Pierwsza uwaga, że ​​'() i () są równoważne, ponieważ lista jest pusta siebie oceny i równe NIL, aw każdym razie nie trzeba w tych LET ponieważ NIL jest implikowane przez składnię `(let (variable) ...), co jest powodem, dla którego potrzebujesz nawiasu wokół każdego wiązania podczas podawania wartości początkowej.

Niewykonanie tego przy pomocy LET jest konieczne w tym przypadku. Korzystanie LOOP możliwości szerzej tej funkcji można zapisać jako:

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

Osobiście wolę iterate dla większości iteracji, przy użyciu których można zapisać jako:

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

lub nawet:

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

EDYCJA: Dodatkowe uwagi: Nazwa unzip tradycyjnie oznacza nieco inną funkcję. Nazwa argumentu powinna być w rzeczywistości list, ponieważ seq sugerowałaby, że funkcja pobiera także wektory. Chociaż możliwe jest posiadanie funkcji działających na uogólnionych sekwencjach, zwykle nie jest to zalecane, ponieważ listy i wektory mają różne charakterystyki wydajności. W szczególności, losowy dostęp przez NTH jest liniowy dla list, co oznacza, że ​​prawie nigdy nie należy go używać. Nawet jeśli koszt czasu jest nieistotny, zwykle oznacza to, że powinieneś używać innej bazy danych.

+1

Pretty. Wzmianka * nth * jest zły i dostajesz +1 – user359996

2

Jedyna zła rzecz, jaką widzę, to nth. Powinieneś przetestować tę listę zamiast tego, ponieważ nth prawdopodobnie i tak będzie musiał powtórzyć listę. Zapomnij o tablicach i indeksowaniu tablic :)

Niestety, nie wiem, jak to zrobić w Lisp, ale w Scheme, wystarczy użyć nazwanego let.

0

cykliczna rozwiązanie:

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

Powinieneś podzielić na nieparzyste lub parzyste indeksy, nie dziwne, a nawet elementy. – Vatine

1

by rozwiązać ten problem zarówno z pętlą lub funkcji rekurencyjnej.

W przypadku rozwiązania opartego na LOOP @Ramarren ma je prawie nabija.

Jeśli nie jest krytyczny wiedzieć, co pochodziło z parzystych indeksach i, co pochodziło z dziwne, że mogę używać coś takiego:

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

Jeśli jest to konieczne, aby wiedzieć, co pochodziło z parzysty, czy nieparzysty indeksów, I” d zawiń UNZIP w funkcję pomocnika, która porównuje CAR listy i CAAR wartości zwracanej. Jeśli są takie same, przekaż je z powrotem, w przeciwnym razie zamień dwie listy na liście wyników.

+1

Korzystanie z rekursji nie jest dobrym pomysłem, ponieważ potrzebna byłaby optymalizacja rekurencji ogona. Która nie jest częścią zwykłego standardowego CL i jest wywoływana inaczej w każdej implementacji, która ją obsługuje. –

3

Spójrzmy na kod:

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

Oto wersja dla list:

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

Przykład (zauważ, że indeksowanie jest zerowy siedzibą w CL, zmieniłem przykład aby uczynić go mniej mylące):

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

Tutaj jest wersja, że ​​spodziewa się nawet listy długości i używa LOOP:

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

Funkcją (nth n list) musi przechodzić listę dostęp do elementu nta o O (N) do obsługi, i jest zwany O (n) razy w implementacji, co sprawia, że ​​cały proces O (N^2) . Możesz wykonać to samo w O (n):

0

Wersja programu, tylko dla treningu docelowego. :-) (Wymaga fold funkcji Srfl 1-ek.)

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

Aby zmienić kolejność zwróconych liście (to znaczy, nawet indeksowanych pierwsze elementy), po prostu zmianę #t do #f.

0

Oto kolejna z możliwych rozwiązań, za pomocą rekurencji:

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

Po pierwsze, możemy zdefiniować funkcję pomocnika co drugi, który trwa co innego elementu z listy. Następnie uruchamiamy tę funkcję na pierwotnej liście przy pierwszym pomijanym elemencie i na oryginalnej liście.

Wadliwość: Ze względu na rekursję może być stosem przepełnienia dużych list. Jeśli musi obsługiwać duże listy, zobacz @Vatine dla rekurencyjnego rozwiązania typu tail.

0

Oto wersja pętli, który nie jest wrażliwy na wejściach dziwne długości, to tylko niewielka zmienność użytkownika @ Huaiyuan odpowiedź, z dodatkowym gdy klauzula:

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