简体   繁体   English

高阶函数haskell

[英]higher order function haskell

I'm new to Haskell, I've to do a function that counts the number of vowels in a string using the higher order function foldr 我是Haskell的新手,我要做一个函数,使用高阶函数foldr计算字符串中元音的数量

I've tried to create this function 我试过创建这个功能

vowels [] = 0
vowels (x:xs)= if elem x "aeiou" then 1 + vowels xs else vowels xs

But it doesn't work and I'm not able to do it using foldr , any suggestion? 但它不起作用,我无法使用foldr ,任何建议吗?

Well a foldr :: (a -> b -> b) -> b -> [a] -> b is a function where the first parameter is a function f :: a -> b -> b . 那么一个foldr :: (a -> b -> b) -> b -> [a] -> b是一个函数,其中第一个参数是函数f :: a -> b -> b You can here see the a parameter as the " head " of the list, the second parameter b as the result of the recursion with foldr , and you thus want to produce a result in terms of these two for the entire function. 您可以在此处将a参数视为列表的“ head ”,将第二个参数b视为使用foldr递归的结果 ,因此您希望根据这两个参数为整个函数生成结果。 This logic is basically encapsulated in the second clause of your function. 该逻辑基本上封装在函数的第二个子句中。

Indeed: 确实:

vowels (x:xs) = if elem x "aeiou" then 1 + vowels xs else vowels xs

can be rewritten as: 可以改写为:

vowels (x:xs) = if elem x "aeiou" then 1 + rec else rec
    where rec = vowels xs

and rec is thus the outcome of the recursive call, the second parameter of the "fold"-function. 因此, rec是递归调用的结果,是“fold”函数的第二个参数。 x on the other hand is the first parameter of the "fold"-function. 另一方面, x是“fold”函数的第一个参数。 We thus need to write this function, only in terms of x and rec , and this is simply: 因此,我们只需要在xrec方面编写这个函数,这很简单:

\x rec -> if elem x "aeiou" then 1 + rec else rec

Furthermore we need to handle the case of an empty list, this is the first clause of your function. 此外,我们需要处理空列表的情况,这是您的函数的第一个子句。 In that case the result is 0 , this is the second paramter of the foldr , so we got: 在这种情况下,结果是0 ,这是foldr的第二个参数,所以我们得到:

vowels = foldr (\x rec -> if elem x "aeiou" then 1 + rec else rec) 0

Or a more clean syntax: 或者更简洁的语法:

vowels = foldr f 0
    where f x rec | elem x "aeiou" = 1 + rec
                  | otherwise = rec

We can further clean it up, by abstracting away rec : 我们可以通过抽象rec来进一步清理它:

vowels = foldr f 0
    where f x | elem x "aeiou" = (1+)
              | otherwise = id

You need to take a look at foldr 's signature. 你需要看看foldr的签名。

foldr :: Foldable t => (a -> b -> b) -> b -> ta -> b

Never mind the Foldable part and focus on the first function it takes. 别介意Foldable部件,并专注于它所需的第一个功能。 (a -> b -> b) b is the same type that you are supposed to return, so directly translating the signature into a lambda gives you \\x acc -> acc , but you want to do more than just ignore every element. (a -> b -> b) b与你应该返回的类型相同,所以直接将签名转换为lambda会给你\\x acc -> acc ,但你想做的不仅仅是忽略每个元素。

Take a look at your function if elem x "aeiou" then 1 + vowels xs else vowels xs . 看看你的功能, if elem x "aeiou" then 1 + vowels xs else vowels xs You need to return b , not recurse adding one to it. 您需要返回b ,而不是递归添加一个。

if elem x "aeiou" this part is fine. if elem x "aeiou"这部分很好。 then 1 + acc <- see what I'm doing here? then 1 + acc < - 看看我在这做什么? I'm adding one to the accumulator, not recursing manually, that is done by foldr , as for the else case: acc . 我正在向累加器添加一个,而不是手动递归,这是由foldr完成的,就像else情况一样: acc That's it. 而已。 You don't need to even touch x . 你甚至不需要触摸x

Putting it all together: vowels = foldr (\\x acc -> if elem x "aeiou" then 1 + acc else acc) 0 The 0 is what the acc will start as. 把它们放在一起: vowels = foldr (\\x acc -> if elem x "aeiou" then 1 + acc else acc) 0 0acc将开始的。

If you want to know more about folds, I suggest you reimplement them yourself. 如果你想了解更多关于折叠的信息,我建议你自己重新实现它们。

The easiest way to write something like that is to let the compiler guide you. 编写类似内容的最简单方法是让编译器指导您。

First, look only at the obvious parts of the foldr signature. 首先,只看一下foldr签名的明显部分。 This is the traditional signature, specialised to lists. 这是传统的签名,专门用于列表。 Nowedays, foldr can actually work on any other suitable container as well, but this isn't important here. 现在, foldr实际上可以在任何其他合适的容器上工作,但这在这里并不重要。

foldr :: (a -> b -> b)  -- ^ Not obvious
      -> b              -- ^ Not obvious
      -> [a]            -- ^ A list... that'll be the input string
      -> b              -- ^ Final result, so nothing to be done here.

So, your implementation will be of the form 因此,您的实现将是形式

vowels :: String -> Int
vowels s = foldr _ _ s

where we yet need to find out what to put in the _ gaps. 我们还需要找出_空档中的内容。 The compiler will give you useful hints as to this: 编译器将为您提供有用的提示:

$ ghc wtmpf-file6869.hs 
[1 of 1] Compiling Main             ( wtmpf-file6869.hs, wtmpf-file6869.o )

/tmp/wtmpf-file6869.hs:2:18: error:
    • Found hole: _ :: Char -> Int -> Int
    • In the first argument of ‘foldr’, namely ‘_’
      In the expression: foldr _ _ s
      In an equation for ‘Main.vowels’: Main.vowels s = foldr _ _ s
    • Relevant bindings include
        s :: String (bound at /tmp/wtmpf-file6869.hs:2:8)
        vowels :: String -> Int (bound at /tmp/wtmpf-file6869.hs:2:1)
  |
2 | vowels s = foldr _ _ s
  |                  ^

So, a function that merely takes a single character, and then modifies an integer. 所以,一个只占用一个字符的函数,然后修改一个整数。 That was actually already part of your original implementation: 这实际上已经是原始实现的一部分:

vowels (x:xs) = if elem x "aeiou" then 1 + vowels xs else vowels xs

The bold part is essentially a function of a single character, that yields a number-modifier. 粗体部分基本上是单个字符的函数,它产生数字修饰符。 So we can put that in the foldr implementation, using lambda syntax: 所以我们可以使用lambda语法将它放在foldr实现中:

vowels s = foldr (\x -> if x`elem`"aeiou" then (1+) else _) _ s

I had to put the 1+ in parenthesis so it works without an explicit argument, as an operator section . 我必须将1+放在括号中,以便它在没有显式参数的情况下工作,作为运算符部分

Ok, more gaps: 好的,差距更大:

    • Found hole: _ :: Int -> Int
    • In the expression: _
      In the expression: if x `elem` "aeiou" then (1 +) else _
      In the first argument of ‘foldr’, namely
        ‘(\ x -> if x `elem` "aeiou" then (1 +) else _)’
    • Relevant bindings include
        x :: Char (bound at wtmpf-file6869.hs:2:20)
        s :: String (bound at wtmpf-file6869.hs:2:8)
        vowels :: String -> Int (bound at wtmpf-file6869.hs:2:1)
  |
2 | vowels s = foldr (\x -> if x`elem`"aeiou" then (1+) else _) _ s
  |                                                          ^

So that's the modifier that should take action when you've found a non-vowel. 这就是你找到非元音时应采取行动的修饰语。 What do you want to modify in this case? 在这种情况下你想要修改什么? Well, nothing actually: the count should stay as-is. 嗯,实际上没有:计数应保持原样。 That's accomplished by the id function. 这是由id函数完成的。

vowels s = foldr (\x -> if x`elem`"aeiou" then (1+) else id) _ s
    • Found hole: _ :: Int
    • In the second argument of ‘foldr’, namely ‘_’
      In the expression:
        foldr (\ x -> if x `elem` "aeiou" then (1 +) else id) _ s
      In an equation for ‘vowels’:
          vowels s
            = foldr (\ x -> if x `elem` "aeiou" then (1 +) else id) _ s
    • Relevant bindings include
        s :: String (bound at wtmpf-file6869.hs:2:8)
        vowels :: String -> Int (bound at wtmpf-file6869.hs:2:1)
  |
2 | vowels s = foldr (\x -> if x`elem`"aeiou" then (1+) else id) _ s
  |                                                              ^

So that's an integer that's completely outside of the foldr . 所以这是一个完全在foldr之外的整数。 Ie it can't depend on the string. 即它不能依赖于字符串。 In particular, it will also be used if the string is empty . 特别是,如果字符串为 ,也将使用它。 Can only be 0 ! 只能是0

vowels s = foldr (\x -> if x`elem`"aeiou" then (1+) else id) 0 s

No more gaps, so the compiler will just accept this. 没有更多的差距,所以编译器会接受这个。 Test it: 测试一下:

$ ghci wtmpf-file6869
GHCi, version 8.2.1: http://www.haskell.org/ghc/  :? for help
Loaded GHCi configuration from /home/sagemuej/.ghc/ghci.conf
Loaded GHCi configuration from /home/sagemuej/.ghci
[1 of 1] Compiling Main             ( wtmpf-file6869.hs, interpreted )
Ok, 1 module loaded.
*Main> vowels "uwkaefdohinurheoi"
9

Your definition can be tweaked into 您的定义可以调整为

vowels []     = 0
vowels (x:xs) = g x (vowels xs)
  where
  g x rec = if elem x "aeiou" then 1 + rec else rec

which matches the pattern 哪个匹配模式

foldr r z [] = z
foldr r z (x:xs) = r x (foldr r z xs)

if we have foldr rz = vowels and r = g , and also z = 0 . 如果我们有foldr rz = vowelsr = g ,并且z = 0

That "pattern" is in fact a valid definition of the foldr function. 该“模式”实际上是foldr函数的有效定义。

Thus we indeed have 因此我们确实有

vowels xs = foldr g 0 xs
  where
  g x rec = if elem x "aeiou" then 1 + rec else rec

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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