4

我想学习一下Haskell与在线书籍“Learn you a 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]) 

我想一个pply'n'将一个名为'f'的具体函数添加到列表'[x]'中。我试图让这个函数具有多态性,所以参数的类型是'a'。但我想使用数字和列表也是如此,所以我直接使用列表(如果我想使用函数只是为了一个数字,它显然是[数字])

请问有人可以帮我吗?我喜欢这种语言,但是在学习时有点困难,因为它与Java或c有很大不同(例如)

谢谢!

+0

我建议重命名问题的标题:这是模式匹配的问题,不一定与高阶函数。 – 2011-02-01 19:27:59

+0

确定完成了。现在呢?谢谢! – albertoblaz 2011-02-01 21:41:19

回答

13

删除围绕x[]。否则第二个模式只能匹配具有1个元素的列表。

apply f n x 
    | n <= 1 = map f x 
    | otherwise = apply f (n-1) (map f x) 
+8

虽然我们在这里:将它重命名为“xs”,这是“任何事物列表”的更常见的名称。 – delnan 2011-02-01 16:21:17

+0

是的,这是真的。问题是我在尝试做很多事情做更改和更改,没有任何东西。但正确的是使用'xs' – albertoblaz 2011-02-01 21:42:50

5

您针对两种情况定义apply:n和一个空表n和一个元素的列表。当列表包含多个元素时会发生什么?这是缺失的模式。

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糖让我们写​​而不是['a','b','c'],这比'a':'b':'c':[]更好。 (根据上面的定义,我们必须编写Cons 'a' (Cons 'b' (Cons 'c' Nil)))这对于一个简短的字符串来说有点多! - 尽管它也提出了为什么人们应该更喜欢ByteStringText表示字符串用于很多目的。)有了这样一个更详细的列表定义,我们需要增加我们自己的map(或相当fmap),所以我们可以说

instance Functor List where 
    fmap f Nil = Nil 
    fmap f (Cons first rest) = Cons (f first) (fmap f rest) 

请注意,在定义fmap这种情况下,我不得不考虑这两种类型的构造函数对于我的列表类型,NilCons 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 Nilfirst : [][first] - 即[x]相同。但这意味着你没有覆盖每一个案例,你的定义是“非穷尽的”。您还没有说过如果将fn应用于列表中,如果它有多个成员。如果列表的形式是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)) 

这里的“变量” 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)) 
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"