【问题标题】:How can I write this function only by using recursion in F#?如何仅通过在 F# 中使用递归来编写此函数?
【发布时间】:2015-11-22 23:53:29
【问题描述】:
let rec n_cartesian_product = function
    | [] -> [[]]
    | x :: xs ->
      let rest = n_cartesian_product xs 
      List.concat (List.map (fun i -> List.map (fun rs -> i :: rs) rest) x)

你好!我写了这个函数,但我需要在不使用任何List.* 内置函数的情况下编写它。由于有一个调用外部函数的内部函数,我假设我必须定义两个相互递归的函数。

定义一个 concat 函数似乎很容易:

let rec list_concat ( lst : 'a list list ) : 'a list =
match lst with 
[]    -> []
|x::xs  -> x @ (list_concat xs)

问题是,我被困在产生 concat 参数的函数的定义上:

let rec fun_i rest =
    match rest with
    [] -> []
    |x::xs -> fun_rs

and fun_rs =
fun_i :: fun_rs

我似乎无法设计出合适的解决方案。你能帮帮我吗?

编辑:例如,给定这个输入

[["A";"a"];["B";"b"];["C";"c"]]

我想要这个输出:

[["A"; "B"; "C"]; ["A"; "B"; "c"]; ["A"; "b"; "C"]; ["A"; "b"; "c"];
 ["a"; "B"; "C"]; ["a"; "B"; "c"]; ["a"; "b"; "C"]; ["a"; "b"; "c"]]

【问题讨论】:

标签: function recursion f# mutual-recursion


【解决方案1】:

N-笛卡尔积

要递归定义第 n 个笛卡尔积,最简单的方法就是对原始(非递归)示例中使用的函数进行递归定义:

let rec list_concat lst =
    match lst with 
    |[]    -> []
    |x::xs  -> x @ (list_concat xs)

let rec list_map f lst =
    match lst with
    |[] -> []
    |x::xs -> (f x) :: list_map f xs

let rec n_cartesian_product = 
    function
    | [] -> [[]]
    | x :: xs ->
      let rest = n_cartesian_product xs 
      list_concat (list_map (fun head -> list_map (fun tail -> head :: tail) rest) x)

就 F# 的惯用写法而言,最好使用更通用的函数(如 fold)来编写,而不是使用显式递归来编写大量自定义函数。所以,你可以定义一些额外的函数:

let list_collect f = list_concat << list_map f

let rec list_fold f acc lst =
    match lst with
    |[] -> acc
    |hd::tl -> list_fold f (f acc hd) tl

let n_cartesian_product_folder rest first = 
    list_collect (fun head -> list_map (fun tail -> head :: tail) rest) first

那么我们可以将n_cartesian_product简单地重新定义为:

let n_cartesian_product2 lst = list_fold (n_cartesian_product_folder) [[]] lst

如果我们使用 F# 核心库函数(而不是自定义递归实现),这种方法将涉及更多标准代码,并且出错率更低。

笛卡尔积 (我将把这部分留在这里,因为它显然很有用)

定义一个函数,它接受'a 的列表并创建'b * 'a 的列表,其中'b 类型的所有事物都是一些提供的元素y

/// take a list of 'a and make a list of (y, 'a)
let rec tuplify y lst =
    match lst with
    |[] -> []
    |x::xs -> (y, x) :: (tuplify y xs)

然后定义一个在我的两个列表中递归的函数,在第一个列表和整个第二个列表的当前元素上调用tuplify,并将其与对笛卡尔积的递归调用连接起来。

/// cartesian product of two lists
let rec cartesianProduct lst1 lst2 =
    match lst1 with
    |[] -> []
    |x::xs -> tuplify x lst2 @ (cartesianProduct xs lst2)

【讨论】:

  • 感谢 TheInnerLight,您的解决方案让我走上了正轨。但是,我需要输出 n 元素列表而不是元组列表。我通过简单地将 tuplify 的最后一行修改为 |x::xs -&gt; y :: x :: (tuplify y xs) 并定义一个驱动 cartesianProduct 函数的函数来实现它。 let rec cp (lst : 'a list list) : ( 'a list ) = match lst with [x] -&gt; x |x::xs -&gt; cartesianProduct x ( cp xs )
  • 现在的问题是cp [["A";"a"];["B";"b"];["C";"c"]];; 输出["A"; "B"; "A"; "C"; "A"; "B"; "A"; "c"; "A"; "b"; "A"; "C"; "A"; "b"; "A"; "c"; "a"; "B"; "a"; "C"; "a"; "B"; "a"; "c"; "a"; "b"; "a"; "C"; "a"; "b"; "a"; "c"] 而不是[["A"; "B"; "C"]; ["A"; "B"; "c"]; ["A"; "b"; "C"]; ["A"; "b"; "c"]; ["a"; "B"; "C"]; ["a"; "B"; "c"]; ["a"; "b"; "C"]; ["a"; "b"; "c"]]
  • @AntonMariaPrati 我已经用一个笛卡尔积解决方案更新了我的答案。
  • @AntonMariaPrati 将concatmap 结合起来可能值得创建一个函数,这就是collect 函数。它将简化您的函数嵌套。
  • @AntonMariaPrati 我已经更新了答案,详细说明了您可能如何在更典型的用法中解决此问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多