我正在尝试编写一个将给定值附加到嵌套列表结构的最内层列表的函数,但是当我甚至不确定这样的类型签名是什么时,我遇到了类型错误功能将是。
digpend a xs = case xs of [_:_] -> map (digpend a) xs
[[]] -> [[a]]
xs -> a:xs
例如,
digpend 555 [ [ [ 5,1,-12,33 ] , [ 6,22 ] ] , [ [ -9,0,9,12,83 ] ] ]
应该返回
[ [ [ 555,5,1,-12,33 ] , [ 555,6,22 ] ] , [ [ 555,-9,0,9,12,83 ] ] ]
理想情况下,它可以通过递归在任何级别的嵌套上工作。这是允许的吗?
答案 0 :(得分:6)
这是一个不完全令人满意的实现,使用类型类:
{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}
class DigPend a b where
digpend :: a -> [b] -> [b]
instance DigPend a a where
digpend x xs = (x:xs)
instance (DigPend a b) => (DigPend a [b]) where
digpend x xs = map (digpend x) xs
只要完全指定了参数的类型,它就能正常工作:
*Main> digpend (5 :: Int) ([6,7,8] :: [Int])
[5,6,7,8]
*Main> digpend (555 :: Int) ([[[5,1,-12,33],[6,22]],[[-9,0,9,12,83]]] :: [[[Int]]])
[[[555,5,1,-12,33],[555,6,22]],[[555,-9,0,9,12,83]]]
*Main> digpend (5 :: Int) ([] :: [Int])
[5]
*Main> digpend (5 :: Int) ([] :: [[Int]])
[]
但是,像digpend 5 [6,7,8]
这样的调用会触发大量“模糊类型变量”错误 - 像5
这样的数字文字是多态的(它可以存在于Num
的任何实例中),而ghci
通常很高兴默认为Integer
,它首先尝试解决DigPend
的类型类约束,并且在那个阶段,没有足够的类型信息来知道它的哪个实例digpend
申请。
答案 1 :(得分:4)
解决这个问题需要一些类型级编程技巧和一些GHC扩展。
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverlappingInstances #-}
class Digpend a d where
digpend :: a -> d -> d
instance (Digpend a d) => Digpend a [d] where
digpend a list = map (digpend a) list
instance Digpend a [a] where
digpend a list = a : list
main = do
-- We have to help the compiler disambiguate the numbers by putting explicit
-- type signatures:
print $ digpend
(555 :: Int)
([ [ [ 5,1,-12,33 ] , [ 6,22 ] ] , [ [ -9,0,9,12,83 ] ] ] :: [[[Int]]])
-- In case of specific literals, such as `Char`, it's not a problem though.
print $ digpend '!' [['a', 'b', 'c'], "def"]
结果:
[[[555,5,1,-12,33],[555,6,22]],[[555,-9,0,9,12,83]]]
["!abc","!def"]
答案 2 :(得分:2)
如果您可以/允许定义自己的数据类型,您还可以使用以下内容:
data Tree a = Leaves [a] | InnerNodes [Tree a] deriving (Show)
digpend :: a -> Tree a -> Tree a
digpend x (Leaves xs) = Leaves $ x:xs
digpend x (InnerNodes []) = InnerNodes [Leaves [x]]
digpend x (InnerNodes xs) = InnerNodes . map (digpend x) $ xs
一些输出示例:
*Main> digpend 10 $ InnerNodes [ Leaves [], Leaves [], InnerNodes []]
InnerNodes [Leaves [10],Leaves [10],InnerNodes [Leaves [10]]]
*Main> digpend 555 $ InnerNodes [InnerNodes [Leaves [5, 1, -12, 33], Leaves [6, 22]], InnerNodes [Leaves [-9, 0, 9, 12, 83]]]
InnerNodes [InnerNodes [Leaves [555,5,1,-12,33],Leaves [555,6,22]],InnerNodes [Leaves [555,-9,0,9,12,83]]]