2012-02-01 22 views
10

Szukam scalić 2 listy w F # w czysto funkcjonalny sposób. Mam trudności ze zrozumieniem składni.Scalenie dwóch list

powiedzmy mam krotki ([5;3;8],[2;9;4])

Kiedy wywołujemy funkcję, powinien on powrócić [5;2;3;9;8;4]

Oto dlaczego mam tak daleko, co jest nie tak jestem pewien. Gdyby ktoś mógł wyjaśnić to w prosty sposób, byłbym wdzięczny.

let rec interleave (xs,ys) = function 
|([], ys) -> ys 
|(x::xs, y::ys) -> x :: y:: interleave (xs,ys) 

Odpowiedz

11

Twoja funkcja jest prawie rację. let f = function jest skrótem dla let f x = match x with, więc nie potrzebujesz jawnych argumentów. Ponadto Twój algorytm wymaga pewnych zmian.

let rec interleave = function //same as: let rec interleave (xs, ys) = match xs, ys with 
    |([], ys) -> ys 
    |(xs, []) -> xs 
    |(x::xs, y::ys) -> x :: y :: interleave (xs,ys) 

interleave ([5;3;8],[2;9;4]) //output: [5; 2; 3; 9; 8; 4] 
+1

Dzięki za szybką odpowiedź, ale nie bardzo rozumiem, dlaczego nie jest żaden argument. >] Jak nazwałbym tę funkcję? [< – user1072706

+1

Funkcję tę normalnie wywołujesz. Ostatni wiersz kodu demonstruje użycie. Zobacz [ten artykuł MSDN] (http://msdn.microsoft.com/en-us/library/dd233242.aspx) (u góry strony). Pokazuje dwie formy (równoważnej) deklaracji funkcji. – Daniel

8

Ważną kwestią jest to, że funkcja nie jest poprawna. Nie powiodło się z wprowadzeniem ([1;2;3], []), ponieważ pominięto przypadek (xs, []) w dopasowywaniu wzorca. Co więcej, argumenty są lepsze w formie curry, aby łatwiej było korzystać z częściowego zastosowania. Oto poprawiona wersja:

let rec interleave xs ys = 
    match xs, ys with 
    | [], ys -> ys 
    | xs, [] -> xs 
    | x::xs', y::ys' -> x::y::interleave xs' ys' 

Widać, że funkcja jest nie ogon rekurencyjnej ponieważ dwukrotnie dotyczy CONS (::) konstruktora po rekurencyjne wywołanie zwrócone. Jeden ciekawy sposób, aby ogon rekurencyjnej jest za pomocą ekspresji sekwencji:

let interleave xs ys = 
    let rec loop xs ys = 
     seq { 
      match xs, ys with 
      | [], ys -> yield! ys 
      | xs, [] -> yield! xs 
      | x::xs', y::ys' -> 
        yield x 
        yield y 
        yield! loop xs' ys' 
      } 
    loop xs ys |> List.ofSeq 
+3

+1 za podanie rekurencyjnego rozwiązania, chociaż osobiście użyłbym kontynuacji lub akumulatora + 'List.reverse' zamiast wyrażenia sekwencji. – ildjarn

+1

@ildjarn: Możesz zainteresować się wynikami w [tej odpowiedzi] (http://stackoverflow.com/a/7199989/162396) (zazwyczaj są one spójne niezależnie od algo). W skrócie, używanie akumulatora + List.rev zazwyczaj działa znacznie lepiej niż kontynuacje. – Daniel

+0

Fajnie, dziękuję za link @Daniel. Kontynuacje i akumulatory + 'List.rev' są interesującymi możliwościami, ale napisałem tę wersję za pomocą' Seq', aby zachować ją blisko rekurencyjnego. – pad

2

Można wykorzystać tę okazję do zdefiniowania bardziej ogólną funkcję zamówienia wyzsze - zipWith, a następnie wdrożyć interleave go używać.

let rec zipWith f xlist ylist = 
    match f, xlist, ylist with 
    | f, (x :: xs), (y :: ys) -> f x y :: zipWith f xs ys 
    | _, _, _ -> [] 

let interleave xs ys = zipWith (fun a b -> [a; b]) xs ys |> List.concat 

Edit:

Jak @pad powiedział poniżej, F # ma już zipWith pod nazwą List.map2. Więc można przepisać interleave następująco:

let interleave xs ys = List.map2 (fun a b -> [a; b]) xs ys |> List.concat 
+0

'List.map2' robi to samo, co' zipWith' w Haskell. Lista F # nie jest leniwą, więc użycie 'zipWith' w twoim rozwiązaniu stworzy tymczasową listę. – pad

+0

@pad, ah, dzięki. Widziałem wcześniej 'List.map2', ale jakoś o tym zapomniałem. Jeśli chodzi o tworzenie kolekcji pośrednich, tak, jestem tego świadomy, ale jest to prawdą dla prawie każdej funkcji wyższego rzędu na "liście". :-) – missingfaktor

0

Z OP nie jest jasne, co powinno się zdarzyć, jeżeli wykazy mają różne długości, ale tu jest uniwersalne, a realizacja ogon rekurencyjnej, że całkowicie pochłania zarówno list:

// 'a list -> 'a list -> 'a list 
let interleave xs ys = 
    let rec imp xs ys acc = 
     match xs, ys with 
     | [], [] -> acc 
     | x::xs, [] -> imp xs [] (x::acc) 
     | [], y::ys -> imp [] ys (y::acc) 
     | x::xs, y::ys -> imp xs ys (y::x::acc) 
    imp xs ys [] |> List.rev 

Przykłady:

> interleave [5;3;8] [2;9;4];; 
val it : int list = [5; 2; 3; 9; 8; 4] 
> interleave [] [1..3];; 
val it : int list = [1; 2; 3] 
> interleave [1..3] [42];; 
val it : int list = [1; 42; 2; 3] 
> interleave [1..3] [42;1337];; 
val it : int list = [1; 42; 2; 1337; 3] 
> interleave [42; 1337] [1..3];; 
val it : int list = [42; 1; 1337; 2; 3]