使用模式匹配区分列表和列表

时间:2018-04-30 08:47:41

标签: haskell

基本上我想做这样的事情:(哪些不起作用)

g2 (x:xs) = x
g2 ((x:xs):ys) = x

因此,如果g2获得一个列表,它应该提取列表的第一个元素,如果它是一个列表列表,它应该提取第一个列表中的第一个元素。

1 个答案:

答案 0 :(得分:2)

详细说明Willem Van Onsem所说的内容,如果你注释你的功能,你会得到:

-- aka `head`
g2 :: [a] -> a
g2 (x:xs) = x

-- aka `head . head`
g2' :: [[a]] -> a
g2' ((x:xs):ys) = x

这些功能不能相同,因为如果g2有时作为输入[a]而有时作为输入[[a]],那么GHC会尝试统一a = [a], GHC无限调用无限类型(a = [a]a = [[a]]a = [[[...a...]]]

当你想要一个有时候是列表而有时候是列表列表的函数时,Haskell会使用求和类型处理它,例如

g2 :: Either [a] [[a]] -> a
g2 (Left (x:xs)) = x
g2 (Right ((x:xs):ys)) = x

并且在任何输入上使用此功能之前,您必须知道它是哪一个(例如Left "Hello"Right ["Hello", "World"])。但要注意到目前为止所有的g2都是partial,这很糟糕。例如。调用g2 (Left [])g2 (Right [])g2 [Right [[]]会崩溃。

如果您没有列表,而是有一个n-ary树(Data.Tree),

import Data.Tree

然后这可以让你任意嵌套列表:

g2 :: Tree a -> a
g2 (Tree x []) = x
g2 (Tree _ (Tree x : _)) = x

或者您甚至可以获得第一棵树的第一个元素,没有子森林,或者最多n深:

g :: Int -> Tree a -> a
g 0 (Tree x _) = x
g _ (Tree x []) = x
g n (Tree _x t) = g (n-1) t

g2 :: Tree a -> a
g2 = g 1

由于Tree a的定义方式,这种树不能为空,因此g2不是部分的。与[[a]]不同,Tree a在树的顶层(例如上面的x)和_x中的另一个级别都有x。< / p>

如果你(a)打算让你的输入只嵌套两个深,(b)“父值”的概念没有意义你的情况和(c)你希望在类型级别保证你所寻找的x存在,也许最好是撰写Data.List.NonEmpty:< / p>

import Data.List.NonEmpty

type NestedNonEmpty a = Either (NonEmpty a) (NonEmpty (NonEmpty a))

g2 :: NestedNonEmpty a -> a
g2 (Left (x :| _)) = x
g2 (Right ((x :| _) :| _)) = x

然后您就可以使用它:

onetwothree :: NonEmpty Int
onetwothree = 1 :| 2 : 3 : []

t1, t2 :: NestedNonEmpty Int
t1 = Left onetwothree
t2 = Right $ onetwothree :| fmap (+3) onetwothree : []

demo1, demo2 :: Int
demo1 = g2 t1 -- 1
demo2 = g2 t2 -- 1

但是由于这种数据类型过于具体,或许最好修改输入实际看起来的内容,然后修改它的类型。既然你评论说你必须找到另一种方法来解决你的问题,或许可以问一个关注那个问题的问题,不要把它变成X-Y problem。 : - )