【问题标题】:How to iterate over all elements in a record type with the same type (purely functional iterators for record types)如何迭代具有相同类型的记录类型中的所有元素(记录类型的纯功能迭代器)
【发布时间】:2015-04-24 02:27:14
【问题描述】:

有没有一种好方法可以对记录中具有相同类型的所有元素进行迭代、折叠或循环?比如在下面的OCaml代码中

type foo = {
    a : int;
    b : float;
    c : int;
    d : float
}

let foo = {
    a = 1;
    b = 2.0;
    c = 3;
    d = 4.0
}

我想分别迭代所有整数或浮点数。我知道 Fieldslib,但它似乎没有做我想做的事。例如,使用 Fieldslib,我可以编写代码:

open Fieldslib
type bar = {
    w : int;
    x : float;
    y : int;
    z : float
} with fields

let bar = {
    w = 1;
    x = 2.0;
    y = 3;
    z = 4.0
}

let print_int bar x = Printf.printf "%d\n" (Fieldslib.Field.get x bar)

let print_float bar x = Printf.printf "%f\n" (Fieldslib.Field.get x bar);;

Fields_of_bar.iter ~w:(print_int bar) 
                   ~x:(print_float bar)
                   ~y:(print_int bar)
                   ~z:(print_float bar)

但这迫使我们遍历所有元素,而不仅仅是单独的整数或浮点数。它还要求单独指定每个元素上调用的函数。真的,我只想说迭代、映射或折叠一些函数到结构中。

如果这对于异构记录类型很困难,那么对于所有元素都具有单一类型的记录类型会更容易吗?在这种情况下,我们可以定义一个镜头来投射出所有的整数、浮点数等。

从某种意义上说,感觉就像我想要某种与记录类型一起工作的纯函数式迭代器,如果存在这样的技术,我不确定它叫什么。


编辑 1

实际上,这可能比我想象的要简单得多。 Fieldslib 已经提供了一流的元素。只要我们列出所有具有相同类型的列表,就可以了。换句话说:

open Fieldslib
type bar = {
    w : int;
    x : float;
    y : int;
    z : float
} with fields

let ints=[w;y]
let floats=[x;z]

let bar = {
    w = 1;
    x = 2.0;
    y = 3;
    z = 4.0
};;

let print_ints bar = List.iter (fun l -> Printf.printf "%d\n" (l bar)) ints
let print_floats bar = List.iter (fun l -> Printf.printf "%f\n" (l bar)) floats

更一般地说,具有相同类型的镜头列表应该使所有这一切成为可能,因为我们可以从列表中应用 map、fold 和 iter 函数。


编辑 2

如果有人想运行 @j-abrahamson 的代码,这里有一些轻微的修改,以便它在我的机器上运行

{-# LANGUAGE RankNTypes #-}

import Control.Applicative
import Control.Monad.Identity

data Foo = Foo {
    a :: Int,
    b :: Float,
    c :: Int,
    d :: Float
} deriving Show

type Traversal s a = forall f . Applicative f => (a -> f a) -> (s -> f s)

intsOfFoo :: Traversal Foo Int
intsOfFoo inj foo = build <$> inj (a foo) <*> inj (c foo) where
  build a c = foo { a = a, c = c }

mapOf :: Traversal s a -> (a -> a) -> (s -> s)
mapOf trav f = runIdentity . trav (Identity . f)

foo0 = Foo { a = 1, b = 1, c = 1, d = 1 }

foo1 = mapOf intsOfFoo (+1) foo0

我还添加了 Haskell 标志以防万一这对其他人有帮助。

【问题讨论】:

  • 干得好!您介意将“编辑”部分作为单独的答案吗?
  • 谢谢!根据您的要求,张贴。

标签: haskell data-structures functional-programming ocaml


【解决方案1】:

实际上,这可能比我想象的要简单得多。 Fieldslib 已经提供了一流的元素。只要我们列出所有具有相同类型的列表,就可以了。换句话说:

open Fieldslib
type bar = {
    w : int;
    x : float;
    y : int;
    z : float
} with fields

let ints=[w;y]
let floats=[x;z]

let bar = {
    w = 1;
    x = 2.0;
    y = 3;
    z = 4.0
};;

let print_ints bar = List.iter (fun l -> Printf.printf "%d\n" (l bar)) ints
let print_floats bar = List.iter (fun l -> Printf.printf "%f\n" (l bar)) floats

更一般地说,具有相同类型的镜头列表应该使所有这一切成为可能,因为我们可以从列表中应用 map、fold 和 iter 函数。

【讨论】:

    【解决方案2】:

    作为一个半无关的旁注,在 Haskell 中,这可以通过 van Laarhoven Traversal 来完成

    data Foo = Foo {
        a :: Int,
        b :: Float,
        c :: Int,
        d :: Float
    }
    
    type Traversal s a = forall f . Applicative f => (a -> f a) -> (s -> f s)
    
    intsOfFoo :: Traversal Foo Int
    intsOfFoo inj foo = build <$> inj (a foo) <*> inj (b foo) where
      build a b = foo { a = a, b = b }
    

    这个值intsOfFoo一般会遍历Fooinj函数触摸每个int,并将这些“触摸”的所有效果粉碎在一起重新build得到Foo

    mapOf :: Traversal s a -> (a -> a) -> (s -> s)
    mapOf trav f = getIdentity . trav (Identity . f)
    
    > let foo0 = Foo { a = 1, b = 1, c = 1, d = 1 }
    > mapOf intsOfFoo (+1) foo0
    Foo { a = 2, b = 1.0, c = 2, d = 1.0 }
    

    不确定如何在 Fieldslib 中完成此操作,但这是 OCaml 中的一个丑陋草稿。

    module type Applicative = sig
      type 'a t
      val map : ('a -> 'b) -> ('a t -> 'b t)
      val pure : 'a -> 'a t
      val ap : ('a -> 'b) t -> ('a t -> 'b t)
    end
    
    module type Traversal = functor (F : Applicative) ->
    sig
      type s
      type a
      val it : (a -> a F.t) -> (s -> s F.t)
    end
    
    module Identity = struct
      type 'a t = 'a
      let map f = f
      let pure x = x
      let ap f = f
    end
    
    module Traversals (T : Traversal) = struct
      module Ti = T(Identity)
      include Ti
      let mapOf : (a -> a) -> (s -> s) = fun f s -> it f s
    end
    
    type foo = {
      a : int;
      b : float;
      c : int;
      d : float;
    }
    
    module TraverseFooInts (F : Applicative) = struct
      let (<$>) = F.map
      let (<*>) = F.ap
    
      type s = foo
      type a = int
    
      let it inj foo =
        let build a c = {foo with a = a; c = c} in
        build <$> inj foo.a <*> inj foo.c
    end
    
    module Z = Traversals(TraverseFooInts)
    

    最后,

    # Z.mapOf;;
    - : (int -> int) -> foo -> foo = <fun>
    # Z.mapOf (fun x -> x + 1) { a = 1; b = 1.; c = 1; d = 1. };;
    - : foo = {a = 2; b = 1.; c = 2; d = 1.}
    

    【讨论】:

    • 我不知道你为什么会投票,但这很有帮助。谢谢!
    • 顺便说一句,您在 OCaml 示例中遗漏了 module Z = Traversals(TraverseFooInts)。有了它,一切都完美无缺。
    • 谢谢!我可能会因为提出如此丑陋的事情而遭到反对:)
    猜你喜欢
    • 1970-01-01
    • 2019-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多