[英]Pattern-matching with (higher-order) functions in Haskell
我正在尝试使用在线书“学习你的哈斯克尔”学习一些Haskell,我对高阶函数有疑问。
我看了一些例子 ,我想做一些更高级的功能,但我不知道为什么我总是阅读以下异常:
***例外: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有很大不同(例如)
谢谢!
删除x
周围的[]
。 否则,第二个模式只能匹配仅包含1个元素的列表。
apply f n x
| n <= 1 = map f x
| otherwise = apply f (n-1) (map f x)
这与其他人所说的没有什么不同,但也许这一点应该付出代价? 列表有两个基本的“构造函数”,因此在从列表定义函数时需要考虑两个基本情况:形式[]
和(:)
。 后者, (:)
可以加入任何具有该类事物列表的东西,因此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':[]
更好'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
,我必须考虑我的List类型的两种类型的构造函数, Nil
和Cons first rest
(或者经常编写的Cons x xs
)。
或许你还没有完成对LYAH中Functor
类型类的一般性讨论 - 在这种情况下,只要考虑你可以将自己的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 : []
或[first]
- 即你写的时候是[x]
。 但这意味着你没有涵盖所有案例,你的定义是“非详尽的”。 如果它有多个成员,你还没有说过如何将f
和n
应用于列表。 如果列表的形式为Cons x (Cons y Nil)
或Cons x (Cons y (Cons z Nil))
而不是Nil
(您的第一行)或Cons x Nil
(您的第二行),该怎么办?
解决方案正如其他人所说,或使用我们的desugared列表类型:
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))
这里的“变量” 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))
您可以定义apply
于两种情况:n和一个空表n和一个元素的列表。 当列表包含多个元素时会发生什么? 这是缺失的模式。
与其他人相比,这不是一个新的答案,但希望是有见地的。
您已经在函数定义中展示了对模式匹配的一些理解; 当第一个模式无法匹配时,评估将继续进行到下一个模式。 能够无法匹配的模式被视为“可反复”。
通常,最后一个函数定义是“无可辩驳的”,这意味着它始终匹配是个好主意。 来自Haskell 2010报告 :
无可辩驳的模式如下:变量,通配符,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"
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.