高阶函数哈斯克尔



我是 Haskell 的新手,我必须做一个函数,使用高阶函数折叠器计算字符串中的元音数

我尝试创建此函数

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

但是它不起作用,我无法使用foldr来做到这一点,有什么建议吗?

那么foldr :: (a -> b -> b) -> b -> [a] -> b是一个函数,其中第一个参数是一个函数f :: a -> b -> b。您可以在此处看到a参数作为列表的">",第二个参数bfoldr递归的结果,因此您希望根据这两个结果为整个函数生成结果。此逻辑基本上封装在函数的第二个子句中。

事实上:

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

可以改写为:

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

因此,rec是递归调用的结果,递归调用是"折叠"函数的第二个参数。 另一方面,x是"折叠"函数的第一个参数。因此,我们只需要根据xrec来编写这个函数,这很简单:

x rec ->if elem x "aeiou" then 1 +recelserec

此外,我们需要处理空列表的情况,这是函数的第一个子句。在这种情况下,结果是0,这是foldr的第二个参数,所以我们得到:

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

或者更干净的语法:

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

我们可以通过抽象rec来进一步清理它:

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

你需要看看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,而不是递归添加一个。

if elem x "aeiou"这部分很好。then 1 + acc<-看看我在这里做什么?我正在向累加器添加一个,而不是手动递归,这是由foldr完成的,至于else情况:acc.就是这样。你甚至不需要触摸x.

综合起来:vowels = foldr (x acc -> if elem x "aeiou" then 1 + acc else acc) 00acc将开始的样子。

如果你想了解更多关于折叠的信息,我建议你自己重新实现它们。

编写类似内容的最简单方法是让编译器指导您。

首先,只看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

粗体部分本质上是单个字符的函数,它产生一个数字修饰符。因此,我们可以将其放入foldr实现中,使用 lambda 语法:

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

您的定义可以调整为

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 = vowelsr = g,还有z = 0.

该"模式"实际上是foldr函数的有效定义。

因此,我们确实有

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

最新更新