2012-02-01 10 views
10

F #에서 2 개의 목록을 병합하는 방법을 찾고 있습니다. 구문을 이해하는 데 어려움을 겪고 있습니다.두 목록을 병합

하자, 그것은 내가 확신 잘못이다, 내가 지금까지 왜 [5;2;3;9;8;4] 여기

가 반환해야 내가 함수를 호출 할 때 내가 튜플 ([5;3;8],[2;9;4])

을 말한다. 누군가가 간단한 방식으로 설명 할 수 있다면 감사 할 것입니다.

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

답변

11

함수는 거의 권리입니다. let f = functionlet f x = match x with의 줄임말이므로 명시적인 arg가 필요하지 않습니다. 또한 알고리즘에 약간의 조정이 필요합니다.

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

감사하지만 인수가없는 이유는 아주 이해가 안 돼요. >] 어떻게 함수를 호출하겠습니까? [< – user1072706

+1

> 평소와 같이 함수를 호출합니다. 코드의 마지막 줄은 사용법을 보여줍니다. [이 MSDN 문서] (http://msdn.microsoft.com/en-us/library/dd233242.aspx) (페이지 위쪽)를 참조하십시오. 그것은 두 가지 형태의 (동등한) 함수 선언을 보여줍니다. – Daniel

8

한 가지 중요한 점은 함수가 정확하지 않은 것입니다. 패턴 일치에서 (xs, [])의 사례를 놓친 이후로 입력 ([1;2;3], [])으로 실패합니다. 또한, 부분 적용으로 사용하기 쉽도록 카레 폼의 인수가 더 좋습니다.

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

당신은 재귀 호출이 반환 후 두 번 단점을 (::) 생성자를 적용하기 때문에 함수가하지 꼬리 재귀 것을 볼 수 있습니다 여기에 수정 된 버전입니다.

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 꼬리 재귀 솔루션을 제공하기 위해, 개인적으로 연속 표현식이 아닌 연속 또는 누적 기 + List.reverse를 사용했을 것입니다. – ildjarn

+1

@ildjarn : [이 답변] (http://stackoverflow.com/a/7199989/162396) (알 고에 관계없이 일관성이있는 경향이 있음)의 결과에 관심이있을 수 있습니다. 요약하면, 누산기 + List.rev를 사용하면 일반적으로 연속보다 훨씬 더 효과적입니다. – Daniel

+0

@Daniel 링크를 사용해 주셔서 감사합니다. Continuations와 accumulator +'List.rev'는 흥미로운 가능성이 있습니다. 그러나이 버전을'Seq'를 사용하여 비 꼬리 재귀 적 (non-tail recursive)에 가깝도록 유지했습니다. – pad

2

당신은보다 일반적인 고차 함수를 정의하는이 기회를 사용할 수 있습니다 - zipWith을하고 그것을 사용 interleave을 구현 : 한 가지 흥미로운 방법은 시퀀스 표현을 사용하고이 꼬리 재귀 확인합니다.

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 

편집 :

@pad 아래 말했듯이, F 번호가 이미 이름 List.map2에서 zipWith 있습니다. 이 목록은 길이가 다른 경우 어떻게해야하는지 분명하지 않다 영업에서

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

'List.map2'는 하스켈에서'zipWith'와 같은 일을합니다. 그리고 F # list는 게으름이 아니므로 솔루션에서와 같이'zipWith'를 사용하면 임시 목록이 생성됩니다. – pad

+0

@pad, 아, 고마워. 이전에'List.map2'를 본 적이 있지만, 어쨌든 잊어 버렸습니다. 중간 수집의 생성에 관해서는 그렇습니다. 그러나 이것은 List에있는 거의 모든 고차 함수에 적용됩니다. :-) – missingfaktor

0

하지만, 여기에 완전히 두 목록 소비 일반, 꼬리 재귀 구현의 :

// '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 
다음과 같이 그래서 당신은 interleave를 다시 작성할 수 있습니다

예 : 신속한 응답을

> 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]