我想迭代列表列表中所有元素的组合,这些列表具有相同的长度但不一定是相同的类型。这就像两个列表的笛卡尔积(在OCaml中很容易做到),但对于任意数量的列表。
首先,我尝试编写一个通用的笛卡尔(外部)产品函数,该函数获取列表列表并返回元组列表,但由于列表的输入列表不具有相同的元素,因此无法工作类型。
现在我要使用类型
的功能'a list * 'b list * 'c list -> ('a * 'b * 'c) list
,遗憾的是将输入数量固定为三个(例如)。它' S
let outer3 (l1, l2, l3) =
let open List in
l1 |> map (fun e1 ->
l2 |> map (fun e2 ->
l3 |> map (fun e3 ->
(e1,e2,e3))))
|> concat |> concat
这有效,但它很麻烦,因为必须为每个输入数量重做一次。有更好的方法吗?
背景:我想将生成的平面列表提供给Parmap.pariter
。
答案 0 :(得分:3)
要解决任意ntuple的任务,我们需要使用存在类型。我们可以使用GADT,但它们默认是接近的。当然我们可以使用开放式变体,但我更喜欢在语法上更重,但更容易使用一流模块的解决方案(并且它可以工作,因为GADT可以通过一流模块expressed)。但是足够的理论,首先我们需要一个能够为我们生成n_cartesian_product
的函数,类型为'a list list -> 'a list list
let rec n_cartesian_product = function
| [] -> [[]]
| x :: xs ->
let rest = n_cartesian_product xs in
List.concat (List.map (fun i -> List.map (fun rs -> i :: rs) rest) x)
现在我们需要将不同的类型放入一个类型'a
中,这里有存在类型,让我们定义一个签名:
module type T = sig
type t
val x : t
end
现在让我们尝试为这个存在主义写一个提升者:
let int x = (module struct type t = int let x = x end : T)
它有类型:
int -> (module T)
让我们用更多的案例扩展这个例子:
let string x = (module struct type t = string let x = x end : T)
let char x = (module struct type t = char let x = x end : T)
let xxs = [
List.map int [1;2;3;4];
List.map string ["1"; "2"; "3"; "4"];
List.map char ['1'; '2'; '3'; '4']
]
# n_cartesian_product xxs;;
- : (module T) list list =
[[<module>; <module>; <module>]; [<module>; <module>; <module>];
[<module>; <module>; <module>]; [<module>; <module>; <module>];
...
如果您的类型要求允许,则可以使用其他抽象(如对象或函数)而不是第一类模块(例如,如果您不需要公开类型t
)。当然,我们的存在主义非常简洁,也许你需要扩展签名。
答案 1 :(得分:3)
我使用@ivg的答案,但是在带有GADT的版本中。我在这里重现它以供参考。在一个简单的情况下,只有float
和int
类型可以出现在输入列表中,首先设置
type wrapped = Int : int -> wrapped | Float : float -> wrapped
这是一个没有类型参数的GADT。然后
let wrap_f f = Float f
let wrap_i i = Int f
将类型换行为sum类型。在wrapped
值列表中,我们可以从@ivg的回答中调用n_cartesian_product
。结果是列表combinations: wrapped list list
,它是平的(出于本目的)。
现在使用Parmap
,我有例如工人职能work : float * int * float * float -> float
。为了从包装器中获取参数,我模式匹配:
combinations |> List.map (function
| [Float f1; Int i; Float f2; Float f3] -> (f1, i, f2, f3)
| _ -> raise Invalid_argument "wrong parameter number or types")
构建元组的平面列表。最终可以使用worker函数Parmap.pariter
将其提供给work
。
此设置与使用常规和类型type wrapsum = F of float | I of int
而非wrapped
几乎相同。模式匹配是相同的;唯一的区别似乎是获得错误的输入,例如只会在运行时检测(F 1, I 1, F 2.0, F, 3.0)
,而不是像这里那样编译时间。