2013-05-26 21 views
6

Chcę przekonwertować [1,2,3,4] na [[1 2] [2 3] [3 4]] lub [(1 2) (2 3) (3 4)]. W clojure mam (partition 2 1 [1,2,3,4]). Jak mogę to zrobić w haskell? Podejrzewam, że jest taka funkcja w standardowym API, ale nie mogę jej znaleźć.Pary elementów z listy

+3

Dlaczego wezwanie Clojure to 'partition'? Jeśli coś podzielisz, podzielisz w taki sposób, że jest to suma części. – AndrewC

+0

Och - znaleziono [clojure docs] (http://clojuredocs.org/clojure_core/clojure.core/partition) opisujące "partition n step coll: zwraca leniwą sekwencję list o n elementach każda, w krokach przesunięcia oddzielnie.Jeśli krok nie zostanie podany, domyślnie n, partycje nie zachodzą na siebie. " – AndrewC

Odpowiedz

19

Standardowa Sztuką jest to, aby zip lista z własnym tail:

> let xs = [1,2,3,4] in zip xs (tail xs) 
[(1,2),(2,3),(3,4)] 

Aby zrozumieć, dlaczego to działa, w kolejce na liście i ogonem wizualnie.

 xs = 1 : 2 : 3 : 4 : [] 
tail xs = 2 : 3 : 4 : [] 

i zauważ, że zip czyni krotki z każdej kolumny.

Istnieją dwa powody, dla których bardziej subtelne to zawsze robi to, co trzeba:

  • zip przystanki gdy albo lista zabraknie elementów. Ma to sens, ponieważ nie możemy mieć "niepełnej pary" na końcu i zapewnia to, że nie otrzymamy żadnych par z listy pojedynczych elementów.
  • Gdy xs jest pusty, można oczekiwać, że tail xs wyrzuci wyjątek. Jednakże, ponieważ zip najpierw sprawdza swój pierwszy argument, gdy widzi, że jest to pusta lista, drugi argument nigdy nie jest oceniany.

Wszystko powyżej odnosi się również do zipWith, więc możesz użyć tej samej metody, gdy chcesz zastosować funkcję parowania do sąsiednich elementów.

Dla standardowego rozwiązania, takiego jak Clojure's partition, nie ma nic w standardowych bibliotekach. Można jednak spróbować czegoś takiego:

partition' :: Int -> Int -> [a] -> [[a]] 
partition' size offset 
    | size <= 0 = error "partition': size must be positive" 
    | offset <= 0 = error "partition': offset must be positive" 
    | otherwise = loop 
    where 
    loop :: [a] -> [[a]] 
    loop xs = case splitAt size xs of 
       -- If the second part is empty, we're at the end. But we might 
       -- have gotten less than we asked for, hence the check. 
       (ys, []) -> if length ys == size then [ys] else [] 
       (ys, _) -> ys : loop (drop offset xs) 
+0

, więc nie ma w nim żadnej standardowej funkcji w standardowej bibliotece, np. Jeśli nie chcesz mieć krotek, ale trzykrotnie lub jeśli chcę powtórzyć lub pominąć pewne wartości, tak jak http://clojuredocs.org/clojure_core/clojure.core/partition – piotrek

+0

@piotrek: Nic w standardowych bibliotekach, nie, jednak nie jest to trudne do wdrożenia. Dodałem przykładową implementację do mojej odpowiedzi – hammar

1

Wystarczy rzucić kolejną odpowiedź się tam przy użyciu innego podejścia:

Dla n = 2 chcesz po prostu zip listy z ogonem. Dla n = 3 chcesz spakować listę ogonem i ogonem. Ten wzór kontynuuje dalej, więc wszystko, co musimy zrobić, to uogólnić go:

partition n = sequence . take n . iterate tail 

Ale to działa tylko na przesunięcie 1. Aby uogólnić przesunięcia musimy tylko spojrzeć na genrated listy. Zawsze będzie miał postać:

[[1..something],[2..something+1],..] 

Więc wszystko pozostało do zrobienia, to wybrać każdą offset th element i powinniśmy być w porządku. shamelessy Wygrałem tę wersję z @ertes z this question:

everyNth :: Int -> [a] -> [a] 
everyNth n = map head . takeWhile (not . null) . iterate (drop n) 

Cała funkcja staje się teraz:

partition size offset = everyNth offset . sequence . take size . iterate tail 
+0

Niestety twoja generalizacja daje jakąś dziwną permutację – flq

Powiązane problemy