2011-12-13 10 views
8

Mam następujący list (jest to lista długość 2, ale w moim przypisania mam długość + n lista)„zastąpić” 3-krotka

xxs = [(11,22,[(33,33,33),(44,44,44)]),(55,66,[(77,77,77),(88,88,88)])] 

próbuję „zastąpić” jeden 3-krotnie (p1 lub p2 lub p3 lub p4 z obrazu poniżej) według indeksu listy (n) i według indeksu spisu podrzędnego (p).

Visual of list breakdown

tę funkcję, w tym celu, powinna być jak:

fooo newtuple n p = (…) 

Na przykład: (wymienić P3 dla (98,98,98)

fooo (98,98,98) 2 1 
[(11, 22, [(33,33,33) , (44,44,44)]) , (55, 66, [(98,98,98),(88,88,88)])] 

I Zaplanowano kod, wykonując następujące czynności:

  1. Uzyskaj dostęp do parametru pn, który chcę zmienić. Udaje mi się to osiągnąć przez:

    fob n p = ((aux2 xxs)!!n)!!p 
        where aux2 [] = [] 
         aux2 ((_,_,c):xs) = c:aux2 xs 
    
  2. "Zamień" 3-tkę. Naprawdę potrzebuję tutaj pomocy. Utknąłem. najlepszy kod (w mojej głowie to ma jakiś sens), który zrobiłem: (pamiętaj: nie bądź taki zły na moim kodzie, uczę się tylko Haskella tylko przez 5 tygodni)

    foo n p newtuple = fooAux newtuple fob 
        where fooAux _ [] = [] 
          fooAux m ((_):ds) = m:ds 
          fob n p = ((aux2 xxs)!!n)!!p 
           where aux2 [] = [] 
            aux2 ((_,_,c):xs) = c:aux2 xs 
    
  3. Wreszcie odłożę wszystko razem, używając splitAt.

Czy moje podejście do problemu jest prawidłowe? Naprawdę byłbym wdzięczny za pomoc w kroku 2.

Odpowiedz

7

Jestem też trochę nowy dla Haskella, ale zobaczmy, czy nie możemy wymyślić przyzwoitego sposobu robienia tego.

Zasadniczo próbujemy zmodyfikować coś na liście. Korzystając z programowania funkcjonalnego, chciałbym, żeby było trochę ogólne, więc poczyńmy funkcję update.

update :: Int -> (a -> a) -> [a] -> [a] 
update n f xs = pre ++ (f val) : post 
    where (pre, val:post) = splitAt n xs 

To będzie teraz podjąć indeks, funkcję oraz listę i wymienić element na liście z wynikiem funkcji nth stosowane do niego.

W naszym większym problemie musimy jednak aktualizować w kontekście zagnieżdżonym. Na szczęście nasza funkcja update przyjmuje funkcję jako argument, więc możemy również wywołać update w tym!

type Triple a = (a,a,a) 
type Item = (Int, Int, [Triple Int]) 

fooo :: Triple Int -> Int -> Int -> [Item] -> [Item] 
fooo new n p = update (n-1) upFn 
    where upFn (x,y,ps) = (x,y, update (p-1) objFn ps) 
     objFn _ = new 

Wszystko fooo musi zrobić, to zmiana połączenia dwa razy (raz w drugiej rozmowy) i zrobić trochę pracy „gospodarowanie” (wprowadzenie wynik w krotce poprawnie). Modele (n-1) i (p-1) były spowodowane indeksowaniem zaczynającym się od 1, natomiast Haskell rozpoczyna się od 0.

Pozwala to sprawdzić, czy działa z naszego testu:

*Main> fooo (98,98,98) 2 1 [(11,22,[(33,33,33),(44,44,44)]),(55,66,[(77,77,77),(88,88,88)])] 
[(11,22,[(33,33,33),(44,44,44)]),(55,66,[(98,98,98),(88,88,88)])] 
+0

Po pierwsze, twoje wyjaśnienie było naprawdę niezwykłe.Po drugie: wciąż mam wiele do nauczenia się, ale wiele nauczyłem się z twoją odpowiedzią. Po trzecie: spędzam połowę dnia próbując rozwiązać ten problem, a ty rozwiązujesz go w ciągu kilku minut (naprawdę muszę dużo się uczyć i studiować). Wreszcie, wielkie dzięki! _o_ – Nomics

4

Więc spróbował wykorzystać jakąś gotową funkcję (!!). Może uzyskać dostęp do elementu na liście, ale zapomniał o miejscu na liście, więc nie można go zaktualizować. Masz oferowane rozwiązanie, używając innej gotowej funkcji split, która rozdziera listę na dwie części i (++), która skleja je z powrotem w jedną.

Ale żeby naprawdę poczuć to, co podejrzewam, że twoje zadanie miało na celu, (łatwo zapomnieć nazwę funkcji i równie łatwo można napisać sobie nową), ty może spróbować samemu napisać pierwszy, (!!). Wtedy zobaczysz, że możesz go łatwo zmodyfikować, aby mógł również zaktualizować listę.

napisać funkcję, najlepiej myśleć o nim jako równanie równoważności:

myAt 1 (x:xs) = x 
myAt n (x:xs) | n > 1 = ... 

gdy n wynosi zero, po prostu zabrać elementu head. Co robimy, gdy nie jest? Próbujemy zbliżyć się do zera. Możesz wypełnić puste miejsca.

Tutaj zwróciliśmy znaleziony element. Co jeśli chcemy go zastąpić? Zastąp to czym? - to wywołuje kolejny parametr,

myRepl 1 (x:xs) y = (y:xs) 
myRepl n (x:xs) y | n > 1 = x : myRepl ... 

Teraz możesz wykonać resztę, tak myślę.

Wreszcie Haskell jest leniwym językiem. Oznacza to, że w końcu wywołuje on jedynie elementy listy, które są potrzebne. Co się stanie, jeśli wymienisz 7-ty element, ale dopiero pierwsze 3 zostaną później zapytane? Kod używający split będzie faktycznie wymagał 7 elementów, więc może zwrócić pierwsze 3, gdy później o nie poprosi.

Teraz w twoim przypadku chcesz zastąpić w trybie zagnieżdżonym, a wartość, aby zastąpić starą na , zależy od starej wartości: newVal = let (a,b,ls)=oldVal in (a,b,myRepl p ls newtuple). Więc rzeczywiście trzeba ponownie pisać używając funkcji zamiast wartości (tak, że gdzie y był używany wcześniej, const y pójdzie):

myUpd 1 (x:xs) f = (f x:xs) 
myUpd n ... = ... 

i cała rozmowa staje myUpd n xxs (\(a,b,c)->(a,b,myUpd ... (const ...))).

+0

To rozwiązanie najbliżej tego, którego próbowałem. Spróbuję ukończyć (...). Wyjaśnienie to wszystko. :) Dziękuję Ci!! – Nomics

+1

@Nomycy chodziło mi o to, aby napisać swoją funkcję, myśleć o niej tak, jakby już ją napisałeś, tak jakby już była dostępna dla twojego użytku, i po prostu zapisać równania równoważności w oparciu o cechy, które ma mają prawa, które ma przestrzegać. Równania te staną się definicją samej funkcji - o ile przestrzegasz jej praw, zachowuj niezmienniki przed i po użyciu, np .: lista z 1. eltem jest po prostu taka; lista z _n_th elt jest zastąpiona pierwszym eltem przedrostkiem do reszty z _ (n-1) _ -tym eltem zastąpionym. _ To wszystko. –

4

Po pierwsze, musimy ogólną funkcję mapowania pewien element listy, np .:

mapN :: (a -> a) -> Int -> [a] -> [a] 
mapN f index list = zipWith replace list [1..] where 
    replace x i | i == index = f x 
       | otherwise = x 

Możemy wykorzystać tę funkcję dwukrotnie, na zewnętrznej i wewnętrznej listy list. Jest trochę powikłaniem jako lista wewnętrzna jest częścią krotki, więc musimy inną funkcję pomocniczą:

mapTuple3 :: (c -> c) -> (a,b,c) -> (a,b,c) 
mapTuple3 f (x,y,z) = (x,y,f z) 

Teraz mamy wszystko, czego potrzebujemy, aby zastosować funkcję Replace do naszego przypadku użycia:

fooo :: Int -> Int -> (Int,Int,Int) -> [(Int,Int,[(Int,Int,Int)])] 
fooo n p newTuple = mapN (mapTuple3 (mapN (const newTuple) p)) n xxs 

Oczywiście na wewnętrznej liście nie musimy brać pod uwagę starej wartości, więc możemy użyć const :: a -> (b -> a), aby zignorować ten argument.