我已经认识到,当我有嵌套数据结构时,我一直在手动编写代码来深入研究它们。像这样:
--one level
Prelude> map (*2) [1,2,3]
[2,4,6]
--nested two levels
Prelude> let t2 = map $ map (*2)
Prelude> t2 [[1,2,3],[4,5,6]]
[[2,4,6],[8,10,12]]
--nested three levels
Prelude> let t3 = map $ map $ map (*2)
Prelude> t3 [[ [1,2,3],[4,5,6] ],[ [1,2,3],[4,5,6] ]]
[[[2,4,6],[8,10,12]],[[2,4,6],[8,10,12]]]
所以我发现我应该能够自动构建一个函数,使用更高阶函数来深入研究我的嵌套数据结构:
Prelude> let t f n = (iterate map f) !! n
<interactive>:35:22:
Occurs check: cannot construct the infinite type: b0 = [b0]
Expected type: (a0 -> b0) -> a0 -> b0
Actual type: (a0 -> b0) -> [a0] -> [b0]
In the first argument of `iterate', namely `map'
In the first argument of `(!!)', namely `(iterate map f)'
让我感到震惊
答案 0 :(得分:9)
问题是这些“迭代”有不同的类型。对于每次迭代,您都会获得额外的嵌套级别,因此您需要
t f 0 :: a -> b
t f 1 :: [a] -> [b]
t f 2 :: [[a]] -> [[b]]
但是iterate :: (a -> a) -> a -> [a]
要求迭代都具有相同的类型。实际上,直接实现上述内容需要某种形式的依赖类型,因为返回类型取决于n
的值。
除非你有充分的理由不这样做,否则我建议保持简单,只需写出所需数量的map
来电。可以使用Template Haskell来生成它们,但这可能比它的价值更麻烦。
但是,如果您有复杂的嵌套数据结构,您可能需要查看SYB,它可以自动处理在嵌套结构中应用此类转换的样板。
这是一个简单的例子:
> import Data.Generics
> let t x = everywhere (mkT (*2)) x
> :t t
t :: Data a => a -> a
> t [2,4,6]
[4,8,12]
> t [[2,4,6],[8,10,12]]
[[4,8,12],[16,20,24]]
> t (Just [(1, 2, 3), (4, 5, 6)])
Just [(2,4,6),(8,10,12)]
答案 1 :(得分:3)
考虑(iterate map f) !! n
的类型。您希望a -> a
为n = 0
,[a] -> [a]
为n = 1
,[[a]] -> [[a]]
为n = 2
- 通常,您需要此类型表达式取决于n
的值。但是在Haskell中这是不可能的,因为它不是一种依赖类型的语言。