2012-02-01 45 views
10

Tôi đang tìm cách hợp nhất 2 danh sách trong F # theo cách hoàn toàn có chức năng. Tôi gặp khó khăn trong việc hiểu cú pháp.Kết hợp hai danh sách

Hãy nói rằng tôi có một tuple ([5;3;8],[2;9;4])

Khi tôi gọi hàm, nó sẽ trả về [5;2;3;9;8;4]

Đây là lý do tại sao tôi có cho đến nay, đó là sai Tôi chắc chắn. Nếu ai đó có thể giải thích nó một cách đơn giản, tôi sẽ biết ơn.

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

Trả lời

11

Chức năng của bạn là gần như. let f = function là viết tắt của let f x = match x with vì vậy bạn không cần args rõ ràng. Ngoài ra, thuật toán của bạn cần một số tinh chỉnh.

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

Cảm ơn đã phản ứng nhanh chóng nhưng tôi không hoàn toàn hiểu được lý do tại sao không có đối số. >] Tôi sẽ gọi hàm như thế nào? [< – user1072706

+1

Bạn gọi hàm này như bình thường. Dòng mã cuối cùng thể hiện cách sử dụng. Xem [bài viết MSDN này] (http://msdn.microsoft.com/en-us/library/dd233242.aspx) (đầu trang). Nó cho thấy hai dạng khai báo hàm (tương đương). – Daniel

8

Một điểm quan trọng là chức năng không chính xác. Không thành công với đầu vào ([1;2;3], []) vì bạn đã bỏ lỡ trường hợp (xs, []) trong đối sánh mẫu. Hơn nữa, các đối số tốt hơn ở dạng đã được xử lý để dễ sử dụng hơn với ứng dụng một phần. Dưới đây là phiên bản chỉnh:

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

Bạn có thể thấy rằng các chức năng là không đuôi-đệ quy vì nó áp dụng nhược điểm (::) constructor hai lần sau khi cuộc gọi đệ quy quay trở lại. Một cách thú vị để làm cho nó đuôi-đệ quy được sử dụng biểu hiện trình tự:

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 cho việc đưa ra một giải pháp đuôi đệ quy, mặc dù cá nhân tôi đã có thể sử dụng tiếp tục hoặc một accumulator + 'List.reverse' chứ không phải là một biểu thức trình tự. – ildjarn

+1

@ildjarn: Bạn có thể quan tâm đến những phát hiện trong [câu trả lời này] (http://stackoverflow.com/a/7199989/162396) (chúng có xu hướng nhất quán bất kể bản ngã). Trong ngắn hạn, sử dụng một ắc quy + 'List.rev' thường thực hiện tốt hơn nhiều so với tiếp tục. – Daniel

+0

Tuyệt vời, cảm ơn vì liên kết @Daniel. Continuations và accumulator + 'List.rev' là những khả năng thú vị, nhưng tôi đã viết phiên bản này bằng cách sử dụng' Seq' để giữ nó gần với cái đuôi không đệ quy. – pad

2

Bạn có thể sử dụng cơ hội này để xác định một hàm bậc cao tổng quát hơn - zipWith, và sau đó thực hiện interleave sử dụng nó.

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:

Như @pad nói dưới đây, F # đã có zipWith dưới cái tên List.map2. Vì vậy, bạn có thể viết lại interleave như sau:

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

'List.map2' thực hiện tương tự như' zipWith' trong Haskell. Và danh sách F # không phải là lười biếng, do đó, bằng cách sử dụng 'zipWith' như trong giải pháp của bạn sẽ tạo ra một danh sách tạm thời. – pad

+0

@pad, ah, cảm ơn. Tôi đã thấy 'List.map2' trước đây, nhưng bằng cách nào đó đã quên nó. Liên quan đến việc tạo ra bộ sưu tập trung gian, vâng tôi biết điều đó, nhưng đây là điều gì đó đúng với hầu hết mọi chức năng bậc cao hơn trong 'Danh sách'. :-) – missingfaktor

0

Từ OP nó không rõ ràng những gì sẽ xảy ra nếu danh sách có độ dài khác nhau, nhưng đây là một generic, đuôi-đệ quy thực hiện tiêu thụ đầy đủ cả hai danh sách:

// '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 

Ví dụ:

> 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] 
Các vấn đề liên quan