要求:单独编写函数 - 测试列表中是否只有一个元素满足给定条件。
single :: (a -> Bool) -> [a] -> Bool
我写了这个函数:
single p xs = count p xs == 1
where count pred = length . filter pred
问题:在不使用“高阶函数”的情况下,将上述函数转换为一个递归函数的简单(正确)方法是什么?
答案 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)
xor
和foldl
我最直观的事情就是通过列表折叠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,使用single
将foldMap
借给更多代数实现。
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的新手,但也许这些想法对你很有意思。或者也许他们很糟糕!如果我能做些什么来改善答案,请给我留言^ _ ^