OCaml

时间:2015-05-11 09:20:27

标签: list polymorphism tuples ocaml

我想迭代列表列表中所有元素的组合,这些列表具有相同的长度但不一定是相同的类型。这就像两个列表的笛卡尔积(在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

2 个答案:

答案 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的版本中。我在这里重现它以供参考。在一个简单的情况下,只有floatint类型可以出现在输入列表中,首先设置

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),而不是像这里那样编译时间。