将OCaml列表过滤为一个变体

时间:2014-11-27 06:28:37

标签: ocaml

所以我有一个stmt(代数类型)列表,列表中包含许多VarDecl。 我想将列表从stmt list缩减为VarDecl list。 当我使用List.filter时,我可以删除所有其他类型,但我仍然留下stmt list

我发现我能够通过折叠进行过滤以及类型更改,但我无法弄清楚如何概括它(我需要在项目中的许多地方使用此模式)。

let decls = List.fold_left
    (fun lst st -> match st with
        | VarDecl(vd) -> vd :: lst
        | _ -> lst
    ) [] stmts in

是否有更好的方法来执行过滤器并转换为列表类型的变体?

3 个答案:

答案 0 :(得分:1)

您可以使用GADT或多态变体,但两者都会增加复杂性。

这里有一个粗略的草图,说明如何使用多态变体解决这个问题:

type constant = [ `Int of int | `String of string ]
type var = [ `Var of string ]
type term = [ constant | var | `Add of term * term ]

let rec select_vars (list : term list) : var list =
  match list with
  | [] -> []
  | (#var as v)::list -> v::select_vars list
  | _::list -> select_vars list

let rec select_constants (list : term list) : constant list =
  match list with
  | [] -> []
  | (#constant as k)::list -> k::select_constants list
  | _::list -> select_constants list

另一种可能性是将var的位拉出为可以列出的显式类型:

type var = {
  ...
}

type term =
  | Int of int
  | Var of var

这有一些开销,因为这些位只是构造函数args,而var不是term,所以你可能需要做一些包装和解包。

答案 1 :(得分:1)

假设您有类似

的类型
type stmt = VarDecl of int | Foo of int | Bar | Fie of string

和stmt列表,Batteries让你做

let vardecl_ints l =
    List.filter_map (function Vardecl i -> Some i | _ -> None) l

let foo_ints l =
    List.filter_map (function Foo i -> Some i | _ -> None) l

我认为这是简明扼要的。我不 认为你可以为ADT制作一般的“列表获取者”,例如。

let bars l =
    List.filter_map (function Bar -> Some Bar | _ -> None) l

https://github.com/ocaml-batteries-team/batteries-included/blob/master/src/batList.mlv#L456 如果你不想要的话,有电池实现的filter_map 依赖。使用[]而不是Acc的功能版本将非常相似,只是这样做 (x :: dst)和最后的|>List.rev

答案 2 :(得分:0)

如果没有看到您的类型定义(或简化版本),很难回答。

但请注意,如果您有这个定义:

type xyz = X | Y | Z

值X,Y和Z不是类型。他们的价值观。可能Vardecl也是一个值。因此,您无法获得该类型的列表(在OCaml中)。

<强>更新

我为这种情况做的一件事是使用从你想要的一个变体投射的类型:

type xyz = X | Y of int * int | Z

let extract_proj v l =
    match v with
    | X | Z -> l
    | Y (a, b) -> (a, b) :: l

let filter_to_y l =
    List.fold_right extract_proj l []

这是一个顶级会议:

type xyz = X | Y of int * int | Z
val extract_proj : xyz -> (int * int) list -> (int * int) list = <fun>
val filter_to_y : xyz list -> (int * int) list = <fun>
# filter_to_y [X; Z; Y(3,4); Z; Y(4,5)];;
- : (int * int) list = [(3, 4); (4, 5)]