我是Haskell的新手,我做了一个函数,使用高阶函数计算字符串中元音的数量 foldr
我试图创建此功能
vowels [] = 0
vowels (x:xs)= if elem x "aeiou" then 1 + vowels xs else vowels xs
但它不起作用,我无法使用foldr
,任何建议吗?
答案 0 :(得分:7)
好foldr :: (a -> b -> b) -> b -> [a] -> b
是第一个参数是函数f :: a -> b -> b
的函数。您可以在此处将a
参数视为列表的“ head ”,第二个参数b
作为递归的结果 { {1}},因此您希望根据这两个函数为整个函数生成结果。这个逻辑基本上封装在你函数的第二个子句中。
事实上:
foldr
可以改写为:
vowels (x:xs) = if elem x "aeiou" then 1 + vowels xs else vowels xs
因此,和vowels (x:xs) = if elem x "aeiou" then 1 + rec else rec
where rec = vowels xs
是递归调用的结果,是“fold”函数的第二个参数。另一方面,rec
是“fold”函数的第一个参数。因此,我们只需要在x
和x
方面编写此函数,这只是:
rec
此外,我们需要处理空列表的情况,这是函数的第一个子句。在这种情况下,结果是\x rec -> if elem x "aeiou" then 1 + rec else rec
,这是0
的第二个参数,所以我们得到了:
foldr
或者语法更清晰:
vowels = foldr (\x rec -> if elem x "aeiou" then 1 + rec else rec) 0
我们可以通过抽象vowels = foldr f 0
where f x rec | elem x "aeiou" = 1 + rec
| otherwise = rec
:
rec
答案 1 :(得分:4)
您需要查看foldr
的签名。
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
不要介意Foldable
部分,并专注于它所需的第一个功能。
(a -> b -> b)
b
与您应该返回的类型相同,因此直接将签名转换为lambda会为您提供\x acc -> acc
,但您要做的不仅仅是忽略每个元素。
查看您的函数if elem x "aeiou" then 1 + vowels xs else vowels xs
。您需要返回b
,而不是递归添加一个。{1}}。
if elem x "aeiou"
这部分没问题。 then 1 + acc
< - 看看我在这做什么?我正在向累加器添加一个,而不是手动递归,由foldr
完成,与else
情况一样:acc
。就是这样。您甚至不需要触摸x
。
全部放在一起:vowels = foldr (\x acc -> if elem x "aeiou" then 1 + acc else acc) 0
0
是acc
的起始位置。
如果你想了解更多关于折叠的信息,我建议你自己重新实现它们。
答案 2 :(得分:4)
编写类似内容的最简单方法是让编译器指导您。
首先,只查看foldr
签名的明显部分。 这是传统的签名,专门用于列表。现在,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.
因此,您的实现将采用
的形式vowels :: String -> Int
vowels s = foldr _ _ s
我们还需要找出_
间隙中的内容。编译器将为您提供有用的提示:
$ 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
| ^
所以,一个只占用一个字符的函数,然后修改一个整数。这实际上已经是原始实现的一部分:
vowels (x:xs) = if elem x "aeiou" then 1 + vowels xs else vowels xs
粗体部分基本上是单个字符的函数,它产生一个数字修饰符。所以我们可以使用lambda语法将它放在foldr
实现中:
vowels s = foldr (\x -> if x`elem`"aeiou" then (1+) else _) _ s
我必须将1+
放在括号中,使其在没有显式参数的情况下工作,作为运算符部分。
好的,差距更大:
• 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
| ^
这样,当您找到非元音时,应该采取行动的修饰符。在这种情况下你想要修改什么?嗯,实际上没有:计数应保持原样。这是由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
| ^
这是一个完全在foldr
之外的整数。即它不能依赖于字符串。特别是,如果字符串为空,也会使用它。只能是0
!
vowels s = foldr (\x -> if x`elem`"aeiou" then (1+) else id) 0 s
没有更多的差距,所以编译器会接受这个。测试一下:
$ 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
答案 3 :(得分:1)
您的定义可以调整为
vowels [] = 0
vowels (x:xs) = g x (vowels xs)
where
g x rec = if elem x "aeiou" then 1 + rec else rec
匹配模式
foldr r z [] = z
foldr r z (x:xs) = r x (foldr r z xs)
如果我们有foldr r z = vowels
和r = g
,还有z = 0
。
那"模式"实际上是foldr
函数的有效定义。
因此我们确实有
vowels xs = foldr g 0 xs
where
g x rec = if elem x "aeiou" then 1 + rec else rec