Haskell - 将高阶函数转换为递归函数

时间:2017-05-31 10:36:06

标签: haskell recursion

要求:单独编写函数 - 测试列表中是否只有一个元素满足给定条件。

single :: (a -> Bool) -> [a] -> Bool

我写了这个函数:

single p xs = count p xs == 1
    where count pred  = length . filter pred 

问题:在不使用“高阶函数”的情况下,将上述函数转换为一个递归函数的简单(正确)方法是什么?

3 个答案:

答案 0 :(得分:2)

你可以这样做:

public String getGoogleToken() {
        String idToken = null;
        String SERVER_CLIENT_ID = "xxxxxxxxxx-xxxxxxxxxxxxxxxxx.apps.googleusercontent.com";
        String magicString = "audience:server:client_id:" + SERVER_CLIENT_ID;
        try {
            idToken = GoogleAuthUtil.getToken(this, Util.getUserEmail(this), magicString);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (GoogleAuthException e) {
            e.printStackTrace();
        }
        return idToken;
    }

答案 1 :(得分:0)

我们可以使用none谓词来检查列表的其余部分是否不满足条件:

single :: (a -> Bool) -> [a] -> Bool
single p []     = False
single p (x:xs) | p x       = none p xs
                | otherwise = single p xs

none :: (a -> Bool) -> [a] -> Bool
none _ [] = True
none p (x:xs) = not (p x) && none p xs

因此,我们还定义了一个none函数,用于检查给定列表中的任何元素是否满足给定的谓词。

或者没有通过递归传递谓词:

single :: (a -> Bool) -> [a] -> Bool
single p = helper
    where helper []     = False
          helper (x:xs) | p x = none xs
                        | otherwise = helper xs
          none []     = True
          none (x:xs) = not (p x) && none xs

上述编写的函数也会在找到满足谓词的第二项时立即停止。如果我们使用无限列表工作,这将非常有用。如果有第二个项满足约束,那么函数将停止,如果没有生成这样的元素或只生成一个这样的元素,我们将陷入无限循环(我们无能为力) )。例如:

*Main> single even [1..] -- two or more elements satisfy the constraint
False
*Main> single even [1,3..] -- no element satisfies the constraint
^CInterrupted.
*Main> single even ([1,2]++[3,5..]) -- one element satisfies the constraint
^CInterrupted.

答案 2 :(得分:0)

xorfoldl

我最直观的事情就是通过列表折叠xor(独占或)和你的函数f。如果您不熟悉二进制函数xor,则仅当(最多)1个参数为True时才返回True;否则False

xor :: Bool -> Bool -> Bool
xor True  b = not b
xor False b = b

single :: (a -> Bool) -> [a] -> Bool
single f [] = False
single f xs = foldl (\acc -> xor acc . f) False xs

main = do
  putStrLn $ show (single even [])        -- False
  putStrLn $ show (single even [1])       -- False
  putStrLn $ show (single even [2])       -- True
  putStrLn $ show (single even [1,2])     -- True
  putStrLn $ show (single even [1,2,3])   -- True
  putStrLn $ show (single even [1,2,3,4]) -- False

Xor monoid

xor可以很好地编码为Monoid tho,使用singlefoldMap借给更多代数实现。

data Xor = Xor Bool

instance Monoid Xor where
  mempty                      = Xor False
  mappend (Xor True)  (Xor b) = Xor (not b)
  mappend (Xor False) (Xor b) = Xor b

single f xs = aux $ foldMap (Xor . f) xs
  where
    aux (Xor b) = b

main = do
  putStrLn $ show (single even [])        -- False
  putStrLn $ show (single even [1])       -- False
  putStrLn $ show (single even [2])       -- True
  putStrLn $ show (single even [1,2])     -- True
  putStrLn $ show (single even [1,2,3])   -- True
  putStrLn $ show (single even [1,2,3,4]) -- False

辅助助手

这是使用auxiliary帮助程序执行此操作的另一种方法。这个有一个额外的好处,因为它在确定答案后立即退出(通过列表停止迭代)

single :: (a -> Bool) -> [a] -> Bool
single f xs = aux False f xs
  where
    aux b     f []     = b
    aux True  f (x:xs) = if (f x) then False else aux True f (xs)
    aux False f (x:xs) = aux (f x) f xs  

main = do
  putStrLn $ show (single even [])        -- False
  putStrLn $ show (single even [1])       -- False
  putStrLn $ show (single even [2])       -- True
  putStrLn $ show (single even [1,2])     -- True
  putStrLn $ show (single even [1,2,3])   -- True
  putStrLn $ show (single even [1,2,3,4]) -- False

欢迎反馈!

我是Haskell的新手,但也许这些想法对你很有意思。或者也许他们很糟糕!如果我能做些什么来改善答案,请给我留言^ _ ^