我想检查列表中的布尔元素是否全部为True,全部为False,或者均为True和False。现在是我的代码:
data Answer3 = Just_True | Just_False | Both deriving (Eq, Ord, Show)
type A3 = Answer3
checkBoolean :: [Bool] -> A3
checkBoolean (x:x1:xs) = if (length (x:x1:xs) `mod` 2) == 0
then if (x && x1) && checkBoolean'(x1:xs)== True
then Just_True
else if (x && x1) == True
then Both
else if checkBoolean'(xs)== True
then Both
else Just_False
else if x && checkBoolean'(x1:xs) == True
then Just_True
else if x == False
then Just_False
else Both
checkBoolean':: [Bool] -> Bool
checkBoolean' (x:xs) = x && (checkBoolean'(xs))
checkBoolean' [] = True
致电
*Main> checkBoolean [False, False, False]
Just_False
或
*Main> checkBoolean [True, True, True]
Just_True
或
*Main> checkBoolean [True, False, False]
Both
给出正确的结果。但是
*Main> checkBoolean [False, False, True, False, True]
Just_False
和
*Main> checkBoolean [False, False, True]
Just_False
不按我的预期工作。 [False, False, True, False, True]
都是Both,而[False, False, True]
也是Both
我知道我没有想到所有可能的情况,这就是为什么这不起作用的原因,但是有没有一种有效的方式来编写此代码,而无需编写太多if语句呢?
答案 0 :(得分:10)
首先想到函数all
或any
:
any :: Foldable t => (a -> Bool) -> t a -> Bool
all :: Foldable t => (a -> Bool) -> t a -> Bool
如果我们专门研究列表,则将t
替换为[]
,这看起来像:
any :: (a -> Bool) -> [a] -> Bool
all :: (a -> Bool) -> [a] -> Bool
我们可以将所有元素与第一个元素进行比较,以查看列表是否包含所有相同的值。如果任何元素不同,那么我们知道列表是混合的。
checkBoolean (x:xs)
-- Check if any elements are not equal to the first one.
| any (/= x) xs = Both
| x = Just_True
| otherwise = Just_False
我们可能还想处理退化的情况:
-- "Just_True" is arbitrary... you could say "Just_False", too.
checkBoolean [] = Just_True
值得注意的是,作为练习,您发现以递归方式编写该函数很有价值,而无需使用诸如all
或any
之类的高阶函数,即使它们处于序幕中也是如此。 。这是另一种尾部递归实现:
checkBoolean [] = Just_True
checkBoolean (x:xs) = check xs
where
check [] = if x
then Just_True
else Just_False
check (y:ys) = if x == y
then check ys
else Both
我不希望在实际的项目代码中看到更长的版本。
答案 1 :(得分:9)
哦,你有点复杂了。
有一个非常有用的函数,叫做all
:
all :: (a -> Bool) -> [a] -> Bool
它检查所有元素的谓词,对于布尔来说,它们可能只是一对id
和not
函数,但是您也可以显式地检查是否等于某个值,从而使代码可读性强:
checkBoolean xs =
if all (== True) xs then Just_True
else if all (== False) xs then Just_False
else Both
对空列表([]
)情况进行更明确的描述可能也是一个好主意。应该是Just_True
,Just_False
,Both
还是其他值?
答案 2 :(得分:6)
您缺少处理空列表的案例:
-- checkBoolean [] = Neither
data Answer4 = Just_True | Just_False | Both | Neither
Answer4
有一个简单的Semigroup
实例,如果需要考虑的话。
instance Semigroup Answer4 where
-- like combines with like
Just_True <> Just_True = Just_True
Just_False <> Just_False = Just_False
-- Neither is "like" anything
x <> Neither = x
Neither <> x = x
-- otherwise, you get Both
_ <> _ = Both
和一个简单的Monoid
实例:
instance Monoid Answer4 where
mempty = Neither
现在,如果您可以将Bool
转换为Answer4
lift :: Bool -> Answer4
lift True = Just_True
lift False = Just_False
您可以将Bool
的列表快速减少为单个Answer4
值:
-- foldMap :: (Foldable t, Monoid m) => (a -> m) -> t a -> m
-- Here, t ~ [], a ~= Bool, and m ~ Answer4, for a specialized type
-- foldMap :: (Bool -> Answer4) -> [Bool] -> Answer4
checkBoolean :: [Bool] -> Answer4
checkBoolean = foldMap lift
答案 3 :(得分:3)
如果要使用列表上的预可用功能,则应考虑
partition :: (a -> Bool) -> [a] -> ([a], [a])
在Data.List
中可用。使用此功能,您可以将列表划分为一对(ts,fs)
,其中ts
和fs
分别是包含所有True
和所有False
元素的列表原始清单。然后,您只需要检查两个都不为空,或者仅检查其中一个为空。您也可以决定在两者均为空的情况下返回什么。
请注意,使用此实现,一旦找到True
和False
元素,列表的遍历就会停止。因为这足以知道分区对中的两个列表都不为空。这是解决此问题的理想属性,即,一旦您收集了足够的信息来知道答案,就应该停止并返回结果。
尽管如此,考虑到您自己解决问题的尝试,我建议您也许应该专注于构建自己的递归解决方案。
答案 4 :(得分:3)
这种方式也许更容易
Private Sub GeckoWebBrowser1_CreateWindow(sender As Object, e As Gecko.GeckoCreateWindowEventArgs) Handles GeckoWebBrowser1.CreateWindow
e.Cancel = True
Process.Start(New ProcessStartInfo(e.Uri.ToString))
End Sub
答案 5 :(得分:2)
模式匹配可能可以集成到checkBoolean
中,但主要观察结果是我们不需要两次检查所有元素:无论如何我们都检查True和False的成员资格,因此从not (elem True xs)
我们可以结论是该列表为空或仅包含False。为此,我将在None
情况下扩展Answer3。
data Answer3 = Just_True | Just_False | Both | None deriving (Eq, Ord, Show)
cat True True = Both
cat True False = Just_True
cat False True = Just_False
cat False False = None
checkBoolean xs = cat (elem True xs) (elem False xs)