高阶函数haskell

时间:2018-06-07 08:28:39

标签: string haskell higher-order-functions fold

我是Haskell的新手,我做了一个函数,使用高阶函数计算字符串中元音的数量 foldr

我试图创建此功能

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

但它不起作用,我无法使用foldr,任何建议吗?

4 个答案:

答案 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”函数的第一个参数。因此,我们只需要在xx方面编写此函数,这只是:

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 0acc的起始位置。

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

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

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

因此我们确实有

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