与Haskell中的(高阶)函数进行模式匹配

时间:2011-02-01 16:04:04

标签: function haskell functional-programming pattern-matching

我正在尝试使用在线书“学习你的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有很大不同(例如)

谢谢!

4 个答案:

答案 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:xsString的使用对于这样的事情来说只是吃糖。同样,特殊的"abc"糖让我们写['a','b','c']而不是'a':'b':'c':[],这比Cons 'a' (Cons 'b' (Cons 'c' Nil)))更好。 (根据上面的定义,我们必须编写ByteString这对于短字符串来说有点多了! - 尽管它也说明了为什么人们应该更喜欢Textmap字符串表示出于多种目的。)使用更详细的列表定义,我们需要添加自己的fmap(或更确切地说instance Functor List where fmap f Nil = Nil fmap f (Cons first rest) = Cons (f first) (fmap f rest) ),所以我们可以说

fmap

请注意,在为此案例定义Nil时,我必须考虑我的列表类型的两种类型的构造函数,Cons first restCons 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。但这意味着你没有涵盖所有案例,你的定义是“非详尽的”。您尚未说明如果nCons 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"