[英]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: 因此,我们只需要在
x
和rec
方面编写这个函数,这很简单:
\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
0
是acc
将开始的。
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 = vowels
和r = 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.