有没有办法在OCaml中创建一个多态加法函数,对于整数和浮点数同样有效?例如,如果我有一个像:
这样的函数 partialsums [1; 2; 3; 4; 5]
我应该[1; 3; 6; 10; 15]
但是这个函数不能在[1.; 2.; 3.; 4.; 5.]
上工作,因为在OCaml中,并且浮点数绝对不能混合。但是如果我希望我的函数同样适用于int列表和浮点列表呢?是否存在int和float是子类型的一般类型?如果是这样,它是什么?我在这一点上有点失落。谢谢你的帮助?
答案 0 :(得分:13)
对于某些类型 t ,请定义一个模块
module type Semigroup = sig
type t
val add : t -> t -> t
end
和partialsums
之类的一些实用函数在仿函数中依赖于它,
module Utils (S : Semigroup) = struct
let partialsums xs =
match xs with
| [] -> []
| (x::xs) ->
List.rev (snd (List.fold_left
(fun (acc, ys) x -> let y = S.add acc x in (y, y::ys)) (x, [x]) xs))
end
您可以将partialsums
专门用于特定类型 t ,
module IntUtils = Utils(struct type t = int
let add = (+) end)
module FloatUtils = Utils(struct type t = float
let add = (+.) end)
let int_test = IntUtils.partialsums [1; 2; 3; 4] ;;
let float_test = FloatUtils.partialsums [1.0; 2.0; 3.0; 4.0]
这很酷,但也有点乏味;你仍然需要为你的函数添加特定类型的东西,但至少你只需编写一次函数。这只是模块系统很棒。
使用White,Bour和Yallop的Modular Implicits (2014),你可以写,
implicit module Semigroup_int =
type t = int
let add = (+)
end
implicit module Semigroup_float =
type t = float
let add = (+.)
end
implicit module Semigroup_string =
type t = string
let add = (^)
end
let add {S : Semigroup} x y = S.add x y
允许定义泛型和重载partialsums
,
let partialsums xs =
match xs with
| [] -> []
| (x::xs) ->
List.rev (snd (List.fold_left
(fun (acc, ys) x -> let y = add acc x in (y, y::ys)) (x, [x]) xs))
所以现在它对于整数和浮点数同样有效!
let int_test = partialsums [1; 2; 3; 4] ;;
let float_test = partialsums [1.0; 2.0; 3.0; 4.0]
let string_test = partialsums ["a"; "b"; "c"; "d"]
显然有几次尝试统一ML模块系统和Haskell的类型类概念。参见例如Dreyer,Harper和Chakravarty Modular Type Classes (2007)提供了一个很好的背景故事。
答案 1 :(得分:8)
int list
和float list
的唯一常见类型是'a list
,即任何类型的列表。由于元素类型可以是任何类型,因此没有可以应用于元素的特定操作。所以没有直接的方法来编写你想要的功能。
如果您愿意将列表与对其元素进行操作的+
函数捆绑在一起,则可以通过这种方式解决问题。
let partialsums plus list =
List.rev
(List.fold_left
(fun l n ->
if l = [] then [n] else (plus (List.hd l) n) :: l)
[] list)
# partialsums (+) [1;3;5;7];;
- : int list = [1; 4; 9; 16]
# partialsums (+.) [1.;3.;5.;7.];;
- : float list = [1.; 4.; 9.; 16.]
在这种情况下,列表元素不必是数字:
# partialsums (^) ["a"; "b"; "c"; "d"];;
- : string list = ["a"; "ab"; "abc"; "abcd"]
另一种常见的解决方案是使用变体类型:
let numlist = Flist of float list | Ilist of int list
liet partialsums (list: numlist) =
match list with
| Flist l -> ...
| Ilist l -> ...
答案 2 :(得分:0)
您也可以像Base所做的那样尝试一流的模块(https://github.com/janestreet/base/blob/57240d0d8403031f37e105351d7d928a6aea1524/src/container.ml#L17),例如:
let sum (type a) ~fold (module M : Commutative_group.S with type t = a) t ~f =
fold t ~init:M.zero ~f:(fun n a -> M.(+) n (f a))
;;
这给你一个非常轻量级的语法:
List.sum (module Int) [1; 2; 3; 4; 5] ~f:Fn.id