我正在尝试使用在线书“学习你的Haskell”学习一些Haskell,我对高阶函数有疑问。
我看到some examples并且我想做一些更高级的功能,但我不知道为什么我总是阅读以下异常:
***例外:euler13.hs:(11,0) - (15,39): 函数中的非详尽模式适用
我定义的功能就是这个:
apply :: (Num b, Ord b) => (a -> a) -> b -> [a] -> [a]
apply f n [] = []
apply f n [x]
| n <= 1 = map f [x]
| otherwise = apply f (n-1) (map f [x])
我想将名为'f'的具体函数'n'应用于列表'[x]'。我试图使这个函数具有多态性,因此param的类型是'a'。但是我也想使用数字和列表,所以我直接使用一个列表(如果我想将这个函数用于一个数字,那么显然会是[数字])
是的,有人可以帮帮我吗?我喜欢这种语言,但是当你学习它时有点困难,因为它与Java或c有很大不同(例如)谢谢!
答案 0 :(得分:13)
移除[]
周围的x
。否则,第二个模式只能匹配仅包含1个元素的列表。
apply f n x
| n <= 1 = map f x
| otherwise = apply f (n-1) (map f x)
答案 1 :(得分:11)
这与其他人所说的没有什么不同,但也许这一点应该有用?列表有两个基本的“构造函数”,因此在从列表定义函数时需要考虑两个基本情况:[]
和(:)
形式的参数。后者(:)
可以加入任何包含此类事物列表的内容,因此1
与[]
- 1:[]
或[1]
相关联。或者它可以与1
加入1:(1:[])
之类的内容:1:[1]
,即[1,1]
,即data List a = Nil | Cons a (List a) deriving (Show, Eq, Ord)
,因为特殊语法允许我们编写。
如果您自己定义了列表,那么更明显的是会出错:写作:
[]
x:xs
和String
的使用对于这样的事情来说只是吃糖。同样,特殊的"abc"
糖让我们写['a','b','c']
而不是'a':'b':'c':[]
,这比Cons 'a' (Cons 'b' (Cons 'c' Nil)))
更好。 (根据上面的定义,我们必须编写ByteString
这对于短字符串来说有点多了! - 尽管它也说明了为什么人们应该更喜欢Text
和map
字符串表示出于多种目的。)使用更详细的列表定义,我们需要添加自己的fmap
(或更确切地说instance Functor List where
fmap f Nil = Nil
fmap f (Cons first rest) = Cons (f first) (fmap f rest)
),所以我们可以说
fmap
请注意,在为此案例定义Nil
时,我必须考虑我的列表类型的两种类型的构造函数,Cons first rest
和Cons x xs
(或Functor
,因为它经常书面)。
或者你可能没有达到LYAH中map
类型类的一般性讨论 - 在这种情况下,只要考虑你可以将自己的listMap f Nil = Nil
listMap f (Cons first rest) = Cons (f first) (listMap f rest)
定义为
apply :: (Num b, Ord b) => (a -> a) -> b -> List a -> List a
apply f n Nil = Nil
apply f n (Cons first Nil)
| n <= 1 = fmap f (Cons first Nil) -- or listMap f (Cons first Nil)
| otherwise = apply f (n-1) (fmap f (Cons first Nil))
在任何情况下,鉴于列表类型的这种令人遗憾的重写,您的实际函数定义将是:
apply f n Nil
apply f n (Cons first Nil)
您所涵盖的案例包括:
Cons first Nil
first : []
与[first]
或[x]
相同 - 即f
。但这意味着你没有涵盖所有案例,你的定义是“非详尽的”。您尚未说明如果n
和Cons x (Cons y Nil)
具有多个成员,则如何将其应用于列表。如果列表的格式为Cons x (Cons y (Cons z Nil))
或Nil
而不是Cons x Nil
(您的第一行)或apply :: (Num b, Ord b) => (a -> a) -> b -> List a -> List a
apply f n Nil = Nil
apply f n (Cons first rest)
| n <= 1 = fmap f (Cons first rest)
| otherwise = apply f (n-1) (fmap f (Cons first rest))
(您的第二行),该怎么办?
解决方案正如其他人所说,或使用我们的desugared list-type:
rest
此处'变量'Nil
涵盖所有列表,*Main> apply (+1) 3 Nil
Nil
*Main> apply (+1) 3 (Cons 3 Nil)
Cons 6 Nil
或不是*Main> apply (+1) 3 (Cons 0 (Cons 1 (Cons 2 Nil)))
Cons 3 (Cons 4 (Cons 5 Nil))
。因此我们得到:
{{1}}
就像你一样,但也:
{{1}}
答案 2 :(得分:5)
您可以为两种情况定义apply
:n和空列表以及n和一个元素的列表。当列表包含多个元素时会发生什么?这是缺失的模式。
答案 3 :(得分:2)
与其他人相比,这不是一个新的答案,但希望是有见地的。
您已经在函数定义中展示了对模式匹配的一些理解;当第一个模式无法匹配时,评估将继续进行到下一个模式。能够无法匹配的模式被视为“可反复”。
通常,最后一个函数定义是“无可辩驳的”,这意味着它始终匹配是个好主意。来自Haskell 2010 report:
无可辩驳的模式如下:变量,通配符,N apat,其中N是由newtype定义的构造函数,apat是无可辩驳的,var @ apat,其中apat是无可辩驳的,或者是~apat形式。所有其他模式都是可以反驳的。
您的误解是您认为[x]
是一个变量(无可辩驳的模式),当它实际上是一个可反射的模式(单个元素列表的模式,它将x
绑定到该单个元素元素)。
假设您编写的函数仅适用于长度为3的列表。如果您需要比“非详尽模式”更具描述性的错误消息,则可以使用通配符(下划线)无可辩驳的模式。一个简单的例子:
sum3 [x,y,z] = x+y+z
sum3 _ = error "sum3 only works on lists of length 3"