简体   繁体   English

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

[英]Pattern-matching with (higher-order) functions in Haskell

I'm trying to learn a bit of Haskell with the online book "Learn you a Haskell" and I have a question about higher-order functions. 我正在尝试使用在线书“学习你的哈斯克尔”学习一些Haskell,我对高阶函数有疑问。

I saw some examples and I want to do a few more advanced functions but I don't know why I always read the following exception: 我看了一些例子 ,我想做一些更高级的功能,但我不知道为什么我总是阅读以下异常:

*** Exception: euler13.hs:(11,0)-(15,39): Non-exhaustive patterns in function apply ***例外:euler13.hs:(11,0) - (15,39):函数中的非详尽模式适用

And the function I defined is this one: 我定义的功能就是这个:

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])

I want to apply 'n' times a concrete function called 'f' to a list '[x]'. 我想将一个名为'f'的具体函数'n'应用于列表'[x]'。 I tried to make this function polymorphic so the type of the param is 'a'. 我试图使这个函数具有多态性,因此param的类型是'a'。 But I want to use numbers and lists also, so directly I'm using a list (if I want to use the function just for a number, it would be [number] obviously) 但我也想使用数字和列表,所以我直接使用一个列表(如果我只想使用该函数的数字,那么显然会是[数字])

Could anybody help me please? 请问有人帮帮我吗? I love this language but it's a bit difficult when you are learning because it's so different of Java or c (for example) 我喜欢这种语言,但是当你学习它时有点困难,因为它与Java或c有很大不同(例如)

Thanks! 谢谢!

Remove the [] around the x . 删除x周围的[] Otherwise the 2nd pattern can only match lists with 1 element only. 否则,第二个模式只能匹配仅包含1个元素的列表。

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

This is no different from what others have said, but maybe the point should be labored? 这与其他人所说的没有什么不同,但也许这一点应该付出代价? There are two basic 'constructors' for lists, and thus two basic cases you need to consider in defining functions from lists: arguments of the form [] and (:) . 列表有两个基本的“构造函数”,因此在从列表定义函数时需要考虑两个基本情况:形式[](:) the latter, (:) can join anything with a list of that kind of thing, thus 1 with [] -- 1:[] or [1] . 后者, (:)可以加入任何具有该类事物列表的东西,因此1[] - 1:[][1] Or it can join 1 with something of just that kind: 1:(1:[]) ie 1:[1] , ie [1,1] as the special syntax lets us write. 或者它可以加入1与这种类型的东西: 1:(1:[])1:[1] ,即[1,1]作为特殊语法让我们写。

It would be more obvious what would had gone wrong if you had defined lists yourself, writing: 如果您自己定义了列表,那么更明显的是会出现什么问题,写道:

data List a = Nil | Cons a (List a) deriving (Show, Eq, Ord)

The use of [] and x:xs is just swank sugar for something like this. 使用[]x:xs就像这样的东西。 Similarly, the special String sugar lets us write "abc" rather than ['a','b','c'] , which is way better than 'a':'b':'c':[] . 类似地,特殊的String糖让我们写"abc"而不是['a','b','c'] ,这比'a':'b':'c':[]更好'a':'b':'c':[] (With the above definition we would have to write Cons 'a' (Cons 'b' (Cons 'c' Nil))) which is a bit much for a short string! (根据上面的定义,我们必须编写Cons 'a' (Cons 'b' (Cons 'c' Nil)))这对于短字符串来说有点多了! -- though it also brings out why one should prefer ByteString and Text representations of strings for many purposes.) With a more verbose list definition like this, we need to add our own map (or rather fmap ), so we can say - 虽然它也说明为什么人们应该更喜欢ByteString和字符串的Text表示用于许多目的。)有了这样一个更详细的列表定义,我们需要添加我们自己的map (或者更确切地说是fmap ),所以我们可以说

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

Notice that in defining fmap for this case I had to consider both types of constructor for my List type, Nil and Cons first rest (or Cons x xs as it is often written). 请注意,在为这种情况定义fmap ,我必须考虑我的List类型的两种类型的构造函数, NilCons first rest (或者经常编写的Cons x xs )。

Or maybe you haven't got up to the general discussion of the Functor type class in LYAH -- in that case, just consider that you can define your own map as 或许你还没有完成对LYAH中Functor类型类的一般性讨论 - 在这种情况下,只要考虑你可以将自己的map定义为

listMap f Nil = Nil
listMap f (Cons first rest) = Cons (f first) (listMap f rest)

In any case, given this desugared rewrite of the list type, your actual function definition would be: 在任何情况下,鉴于这种对列表类型的重写,您的实际函数定义将是:

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))

The cases you have covered are: 您涉及的案例包括:

apply f n Nil 
apply f n (Cons first Nil)

Cons first Nil is the same as first : [] or [first] -- ie [x] as you write it. Cons first Nilfirst : []相同first : [][first] - 即你写的时候是[x] But this means you haven't covered every case, your definition is 'non-exhaustive'. 但这意味着你没有涵盖所有案例,你的定义是“非详尽的”。 You haven't said how to apply f and n to a list if it has more than one member. 如果它有多个成员,你还没有说过如何将fn应用于列表。 What if a list has the form Cons x (Cons y Nil) or Cons x (Cons y (Cons z Nil)) rather than Nil (your first line) or Cons x Nil (your second line)? 如果列表的形式为Cons x (Cons y Nil)Cons x (Cons y (Cons z Nil))而不是Nil (您的第一行)或Cons x Nil (您的第二行),该怎么办?

The solution is as others said, or using our desugared list-type: 解决方案正如其他人所说,或使用我们的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))

Here the 'variable' rest covers all lists, Nil or not. 这里的“变量” rest涵盖了所有的名单, Nil与否。 Thus we get: 因此我们得到:

*Main> apply (+1) 3 Nil
Nil
*Main> apply (+1) 3 (Cons 3 Nil)
Cons 6 Nil

as you would, but also: 像你一样,但也:

*Main> apply (+1) 3 (Cons 0 (Cons 1 (Cons 2 Nil)))
Cons 3 (Cons 4 (Cons 5 Nil))

You define apply for two cases: n and an empty list and n and a list of one element. 您可以定义apply于两种情况:n和一个空表n和一个元素的列表。 What happens when the list contains more than one element? 当列表包含多个元素时会发生什么? That's the missing pattern. 这是缺失的模式。

This is not a new answer, compared to the others given, but hopefully is insightful nonetheless. 与其他人相比,这不是一个新的答案,但希望是有见地的。

You have already demonstrated some understanding of pattern matching in function definitions; 您已经在函数定义中展示了对模式匹配的一些理解; when the first pattern fails to match, evaluation moves on to the next. 当第一个模式无法匹配时,评估将继续进行到下一个模式。 Patterns that are able to fail to match are considered "refutable". 能够无法匹配的模式被视为“可反复”。

Generally it is a good idea to have your last function definition be "irrefutable", meaning it always matches. 通常,最后一个函数定义是“无可辩驳的”,这意味着它始终匹配是个好主意。 From the Haskell 2010 report : 来自Haskell 2010报告

The irrefutable patterns are as follows: a variable, a wildcard, N apat where N is a constructor defined by newtype and apat is irrefutable, var@apat where apat is irrefutable, or of the form ~apat. 无可辩驳的模式如下:变量,通配符,N apat,其中N是由newtype定义的构造函数,apat是无可辩驳的,var @ apat,其中apat是无可辩驳的,或者是~apat形式。 All other patterns are refutable. 所有其他模式都是可以反驳的。

Your misunderstanding was that you thought [x] is a variable (irrefutable pattern), when it is in fact a refutable pattern (the pattern for a single-element list, which binds x to that single element). 你的误解是你认为[x]是一个变量(无可辩驳的模式),当它实际上是一个可反射的模式(单个元素列表的模式,它将x绑定到那个单个元素)。

Suppose you wrote a function that works only on lists of length 3. If you want a more descriptive error message than "non-exhaustive patterns", then you can use the wildcard (underscore) irrefutable pattern. 假设您编写的函数仅适用于长度为3的列表。如果您需要比“非详尽模式”更具描述性的错误消息,则可以使用通配符(下划线)无可辩驳的模式。 A trivial example: 一个简单的例子:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM