基于其现有值将值添加到ocaml列表

时间:2018-02-20 04:52:57

标签: list pattern-matching ocaml

我正在学习地图和折叠功能。我正在尝试编写一个带有列表的函数,并返回一个包含原始值中所有值的列表,每个值后跟该值的双倍。

Example: add_dbls [2;5;8] = [2;4;5;10;8;16]

我尝试的所有内容都会产生列表列表,而不是列表。我正在努力想出一个更好的方法,使用地图或折叠(或两者)。

这是我最初提出的。我理解为什么这会返回一个列表列表,但无法弄清楚如何修复它。任何想法将不胜感激!

let add_dbls list = 
    match list with
    | h::t -> map (fun a-> [a;(a*2)]) list
    | [] -> []

另外,我的地图功能:

let rec map f list =
    match list with
    | h::t -> (f h)::(map f t)
    | [] -> []

2 个答案:

答案 0 :(得分:1)

应该很容易看到List.map总是返回与输入列表长度相同的列表。但是你想要一个两倍长的列表。所以List.map不适合你。

您可以使用List.fold_leftList.fold_right解决此问题。如果在切换到使用折叠后仍然遇到问题,可以使用新信息更新问题。

<强>更新

折叠功能的类型(左侧折叠)是:

('a -> 'b -> 'a) -> 'a -> 'b list -> 'a

因此,折叠函数采用累积答案和列表元素,并返回新的累积答案。

你的折叠功能是这样的:

fun a b -> a::b::(b*2)

它尝试使用::运算符将新元素添加到累积列表的末尾。但这不是::运算符的作用。它为列表的开头添加了一个元素。

没有特别好的方法将元素添加到列表的末尾。这是故意的,因为这是一个缓慢的操作。

使用左侧折叠时,您需要协调自己以相反的顺序构建结果,并可能在结束时将其反转。或者你可以使用右折(通常不是尾递归)。

答案 1 :(得分:1)

你快到了。正如您所观察到的,由于我们获得了列表列表,我们需要将其展平以获得最终列表。 List.concat函数就是这样:

let add_dbls list =
  let l =
    match list with
    | h::t -> List.map (fun a -> [a;(a*2)]) list
    | [] -> []
  in
  List.concat l

这是更新的函数,用于计算所需的输出。 现在是add_dbls [2;5;8] = [2;4;5;10;8;16]的输出。 虽然这有效,但它可能效率不高,因为它在原始列表中为每个项目分配一个新列表。以下是具有不同特征的相同功能的变体,这取决于l的大小。

(* Safe version - no stack overflow exception. Less efficient(time and size) than add_dbls3 below. *)
let add_dbls2 l =
  List.fold_left
    (fun acc a -> (a*2)::a::acc)
    []
    l
  |> List.rev

(* Fastest but unsafe - stack overflow exception possible if 'l' is large - fold_right is not tail-recursive. *)
let add_dbls3 l =
  List.fold_right
    (fun a acc -> a::(a*2)::acc)
    l
    []