检查列表中的所有布尔元素在Haskell中是否相同

时间:2018-11-26 16:08:42

标签: haskell

我想检查列表中的布尔元素是否全部为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语句呢?

6 个答案:

答案 0 :(得分:10)

首先想到函数allany

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

值得注意的是,作为练习,您发现以递归方式编写该函数很有价值,而无需使用诸如allany之类的高阶函数,即使它们处于序幕中也是如此。 。这是另一种尾部递归实现:

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

它检查所有元素的谓词,对于布尔来说,它们可能只是一对idnot函数,但是您也可以显式地检查是否等于某个值,从而使代码可读性强:

checkBoolean xs = 
    if all (== True) xs then Just_True
    else if all (== False) xs then Just_False
    else Both

对空列表([])情况进行更明确的描述可能也是一个好主意。应该是Just_TrueJust_FalseBoth还是其他值?

答案 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),其中tsfs分别是包含所有True和所有False元素的列表原始清单。然后,您只需要检查两个都不为空,或者仅检查其中一个为空。您也可以决定在两者均为空的情况下返回什么。

请注意,使用此实现,一旦找到TrueFalse元素,列表的遍历就会停止。因为这足以知道分区对中的两个列表都不为空。这是解决此问题的理想属性,即,一旦您收集了足够的信息来知道答案,就应该停止并返回结果。


尽管如此,考虑到您自己解决问题的尝试,我建议您也许应该专注于构建自己的递归解决方案。

答案 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)