我希望以纯粹的功能方式合并F#中的2个列表。我很难理解语法。
假设我有一个元组([5;3;8],[2;9;4])
当我调用该函数时,它应该返回[5;2;3;9;8;4]
这就是我到目前为止的原因,我确信这是错误的。如果有人能以简单的方式解释,我将不胜感激。
let rec interleave (xs,ys) = function
|([], ys) -> ys
|(x::xs, y::ys) -> x :: y:: interleave (xs,ys)
答案 0 :(得分:12)
您的功能几乎正确。 let f = function
是let f x = match x with
的简写,因此您不需要显式参数。此外,您的算法需要一些调整。
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 :(得分:8)
重要的一点是功能不正确。输入([1;2;3], [])
失败,因为您在模式匹配中错过了(xs, [])
的情况。此外,参数在咖喱形式中更好,以便更容易与部分应用一起使用。这是更正后的版本:
let rec interleave xs ys =
match xs, ys with
| [], ys -> ys
| xs, [] -> xs
| x::xs', y::ys' -> x::y::interleave xs' ys'
您可以看到该函数不是尾递归,因为它在返回递归调用后两次应用cons (::)
构造函数。使尾部递归的一种有趣方法是使用序列表达式:
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
答案 2 :(得分: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#已经名为zipWith
List.map2
。因此,您可以按如下方式重写interleave
:
let interleave xs ys = List.map2 (fun a b -> [a; b]) xs ys |> List.concat
答案 3 :(得分:1)
从OP中不清楚如果列表具有不同的长度会发生什么,但这里是一个完全消耗两个列表的通用尾递归实现:
// '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 [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]
答案 4 :(得分:1)
从F#4.5开始(我认为),假设您想在用尽较短的字符时继续从较长的序列中产生元素,则可以执行以下操作:
let interleave = Seq.transpose >> Seq.concat >> Seq.toList
> interleave [ [5;3;8]; [2;9;4] ];;
val it : int list = [5; 2; 3; 9; 8; 4]
> interleave [ [1;2;3]; [4;5]; [6;7;8;9] ];; // also works for any number of lists
val it : int list = [1; 4; 6; 2; 5; 7; 3; 8; 9]
(请注意,List.transpose
会抛出不同长度的序列,而Seq.transpose
不会,因此您需要使用后者。)
答案 5 :(得分:0)
将在混合中添加另一个变体。只需在结尾处附加较长列表的其余部分,即可处理不同长度的列表。
let rec interleave lst1 lst2 = [
match lst1 with
| lst1H :: lst1T ->
yield lst1H
yield! interleave lst2 lst1T
| [] -> yield! lst2
]
工作方式:
假设我们有let a = [1;2;3]; let b = [4;5;6]
并打电话给interleave a b
。
1
)的 head 。在此中间点,我们有两个列表:第一个[2;3]
的其余部分和第二个[4;5;6]
。由于我们交换了参数的顺序,因此我们现在专注于第二个列表。
4
的 head 。此时的列表为[2;3]
和[5;6]
,而输出包含[1;4]
。
重复此过程,直到操作列表为空为止,该列表与第二个分支匹配,从而产生另一个列表的其余部分。