【发布时间】: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