使用递归混合来自两个List的值

时间:2014-11-25 19:43:18

标签: list f#

我想使用两个列表混合值:

List1 : [3; 2; 8; 1; 9; 3; 6]
List2: [5; 7; 0]
Output : [3; 5; 2; 7; 8; 0; 1; 9; 3; 6]

我只发现了有关列表的信息,当我有不同的时候,这个例子的长度相同吗?

长度相同:

let rec mix =function
|(x::xs,y::ys) -> x::y::(mix (xs,ys))
|([],[]) -> []
| _ -> failwith "mix:param";;

mix ([3;2;8;1;9;3;6], [5; 7; 0]) ;;

3 个答案:

答案 0 :(得分:3)

第1步:了解示例

如果您非常了解示例函数的作用,这并不难。确保你知道以下含义;如果你不确定,请查看它们(搜索web / Stackoverflow):

  • 递归

  • 基本F#模式匹配

  • 缺点(::),作为创建列表和模式的运算符

  • F#list expressions [...],再次创建列表和模式

如果你完全理解原始功能,你只需要从模式匹配中删除不再需要的案例,并添加新案例:如何在到达一个列表的末尾时完成混合。

因此,我建议您在阅读本答复的第二部分之前自行解决。 F#的MSDN参考是一个很好的起点。例如,请参阅this page for rules and examples on pattern matching

第2步:解决方案变体(首先阅读第1步!)

在这种情况下,您无法修改原始功能。这是一项有一些改进的提案:

let rec private mixAux acc = function
    | h1 :: t1, h2 :: t2 -> mixAux (h2 :: h1 :: acc) (t1, t2)
    | [], t | t, [] -> List.rev acc @ t

let mix l1 l2 = mixAux [] (l1, l2)

mix [3; 2; 8; 1; 9; 3; 6] [5; 7; 0] // yields [3; 5; 2; 7; 8; 0; 1; 9; 3; 6]

这里有多种技术可供您查找:

  • | [], t | t, []开头的行是一个或两个案例只有一个主体的模式。 t以某种方式匹配,但在两种情况下都使用相同的表达式。

  • 使用累加器acc有助于使函数尾递归。如果函数需要处理长输入或需要快速,请查找尾递归。

  • 使用private隐藏原始的“辅助”函数并公开一个以 curried形式接受参数的函数,并且在调用时不需要accumulator参数。

  • 问题中的代码不遵循常见的格式约定。例如,函数体通常是缩进的,逗号后面通常是空格。例如,请参阅this page on formatting conventions,了解有关F#格式的人数的起点。

答案 1 :(得分:3)

解决这个问题的最佳方法是考虑特定步骤的每种可能情况。你现在有:

  1. |(x::xs,y::ys)这是每个列表至少还有一个项目
  2. 的情况
  3. |([],[])这两个列表都是空的
  4. _此案例处理其他所有事情。
  5. 因此,您缺少的案例是一个列表为空而另一个列表至少剩下一个项目的情况。这些案例是|(x::xs,[])|([],y::ys)。所以将这两个选项添加到匹配语句中,如下所示:

    let rec mix =function
    |(x::xs,[]) |([],y::ys) -> failwith "do something here"
    |(x::xs,y::ys) -> x::y::(mix (xs,ys))
    |([],[]) -> []
    | _ -> failwith "mix:param"
    

    您现在会注意到您现在收到一条警告,指出最后一个案例永远不会匹配,因此可以像这样删除:

    let rec mix =function    
    |([],[]) -> []
    |(x,[]) |([],x) -> failwith "test"
    |(x::xs,y::ys) -> x::y::(mix (xs,ys))
    

    注意我是如何将基本案例移到顶部,以便在(x,[])([], x)之前匹配。现在所需要的只是处理最后两种情况的代码。它看起来像这样:

    |(x,[]) |([],x) -> x即返回列表中的其余值。

    所以最终的解决方案如下:

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

    您可以进行的进一步偷偷摸摸的优化是完全删除基本情况,因为它将被第二种情况覆盖。我(x,[])也匹配([],[]),并会根据需要返回[]。这让我们只有:

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

答案 2 :(得分:2)

嗯,实际上只有四种情况,所以这是一种天真的方式:

let rec mx xs ys =
    match (xs, ys) with
    | (x::xs, y::ys) -> x:: y :: (mx xs ys)
    | ([], y::ys) -> y :: (mx [] ys)
    | (x::xs, []) -> x :: (mx xs [])
    | ([], []) -> []