【问题标题】:Filtering OCaml list to one variant将 OCaml 列表过滤为一个变体
【发布时间】:2014-11-27 06:28:37
【问题描述】:

所以我有一个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

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

【问题讨论】:

    标签: ocaml


    【解决方案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】:

      您可以使用 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
      

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

      【讨论】:

        【解决方案3】:

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

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

        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)]
        

        【讨论】:

        • 抱歉,我应该更具体一些。参数列表是一组变体,我正在尝试将其减少为一个变体。
        • 如果只有一个变体,类型仍然是stmt list。它不会改变类型,这听起来像是您所要求的。
        猜你喜欢
        • 1970-01-01
        • 2017-03-05
        • 2016-09-27
        • 1970-01-01
        • 2013-12-16
        • 1970-01-01
        • 2017-04-18
        • 2011-06-14
        • 1970-01-01
        相关资源
        最近更新 更多