我是OCaml的完全初学者,但我在介绍性示例中看到的代码类似于以下内容
let sum = List.fold ~f:(+.) ~init:0.0
在这段代码中让我烦恼的是明确使用List.fold
。在我所知道的大多数语言中,容器的显式类型将通过使用接口,特征或类型类来抽象,以便可以重用此代码来对数组或任何其他顺序容器求和。
什么是OCaml方式使其更通用?
答案 0 :(得分:7)
更通用的解决方案是使用仿函数:
module type Foldable = sig
type 'a t
val fold : 'a t -> init:'b -> f:('b -> 'a -> 'b) -> 'b
end
module MakeSum(Container : Foldable) = struct
let sum xs = Container.fold ~f:(+.) ~init:0.0
end
正如您可能已经注意到,这种额外的参数化带来了显着的语法开销。实际上,我们可以重用Core
中的接口来减少它:
module MakeSum(Container : Container.T) = struct
let sum xs = Container.fold ~f:(+.) ~init:0.0
end
或
module MakeSum(Container : Container.S1) = struct
let sum xs = Container.fold ~f:(+.) ~init:0.0
end
另外,作为一个例子,您可能最感兴趣的不是对容器类型进行参数化,而是对求和运算和零值进行参数化。这个特定的例子是使用OCaml Core库中的第一类函子(另一个选项)实现的,每个容器都实现了这个函数:
(** Returns the sum of [f i] for i in the container *)
val sum
: (module Commutative_group.S with type t = 'sum)
-> t -> f:(elt -> 'sum) -> 'sum
实际上,在您的特定情况下,即使没有仿函数,我们也可以概括,因为您不需要知道类型以实现您的代码。因此,我们可以将通用sum
定义为:
let sum fold xs = fold ~f:(+.) ~init:0.0