Bool-like
The boolean operators &&
and ||
take boolean values as their operands. Sometimes you want to apply a boolean operator to a list of boolean values. Writing something like a && b && c && d && e
can quickly become unwieldy and tedious. What you want are versions of &&
and ||
that can operate on lists, not necessarily of boolean values. Well, this is your lucky day. For a limited time only starting from now to eternity, you can use various functional versions of your favourite boolean operators. Too good to be True
? Au contraire, mon ami(e).
and
The function and
outputs True
if all elements of a list are True
, and False
otherwise. It is the function counterpart of the operator &&
. The function and
is useful when we want to know whether all elements of a list satisfy a given property. For example, below we check whether all elements of a list are even.
1
2
3
4
5
6
ghci> and [even x | x <- [1 .. 10]]
False
ghci> and [even x | x <- [2, 4 .. 10]]
True
ghci> and [even x | x <- [1, 3 .. 10]]
False
Pietro manages a pet shop. As part of his monthly routine, Pietro checks to see whether all pets in his shop have had a health check within the last 12 months. A number of new pets were delivered to the shop recently. Pietro does not recall bringing the new pets to a veterinary clinic within the last two weeks. The following shows the pets database and the health check status of all animals in Pietro’s shop.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import Data.Foldable
import Text.Printf
-- | Database of pets. Each element in the database follows this format:
--
-- (pet-name, animal-type, health-check)
--
-- pet-name := The name of the pet.
-- animal-type := The type of animal, e.g. cat, dog, fish, etc.
-- health-check := Whether the pet has had a health check within the last
-- 12 months.
pet =
[ ("Anonymouse", "mouse", False)
, ("Cat Scan", "cat", True)
, ("Catherine the Grater", "cat", True)
, ("Charlie Chihuahua", "dog", True)
, ("Chirpie O'Tweet", "bird", False)
, ("Dogalogue", "dog", True)
, ("Fido Dogstoevsky", "dog", True)
, ("Frankie Frankfurt", "dog", True)
, ("Garry Longtongue", "lizard", True)
, ("Goldie Horn", "fish", False)
, ("Hamsuke Hamton", "hamster", True)
, ("Harry Speedbump", "hedgehog", True)
, ("Hissy Fit", "cat", True)
, ("Meowzart", "cat", True)
, ("Mrs. Clucky", "bird", True)
, ("Papa's New Guinea", "guinea pig", True)
, ("Roam Ferret Roam", "ferret", True)
, ("Robbie Hopster", "rabbit", True)
, ("Scratchy Meowser", "cat", True)
, ("Tabby Whiskers", "cat", True)
, ("Terry Terrier", "dog", True)
, ("Woofy McBark", "dog", True)
]
-- | Extract a particular type of animal from the database.
extract :: String -> [(String, String, Bool)]
extract "" = error "Cannot be empty string"
extract t = filter (\(_, animal, _) -> animal == t) pet
-- | Whether a list of pets have had a health check within the last
-- 12 months.
hasHealthCheck :: [(String, String, Bool)] -> Bool
hasHealthCheck [] = False
hasHealthCheck xs = and [check | (_, _, check) <- xs]
-- | Managing a pets database.
main = do
let animal =
[ "bird"
, "cat"
, "dog"
, "ferret"
, "fish"
, "guinea pig"
, "hamster"
, "hedgehog"
, "lizard"
, "mouse"
, "rabbit"
]
putStrLn "Health check status"
printf "all pets -> %s\n" $ show . hasHealthCheck $ pet
for_ animal $ \anim -> do
let pt = extract anim
putStr anim
printf " -> %s\n" $ show . hasHealthCheck $ pt
or
The function or
outputs True
if one element of a list is True
. Like its operator counterpart ||
, or
outputs False
if all elements are False
. The function or
is useful in situations where we only care that one element is True
. It might be the case that multiple elements are True
. However, we are only interested in whether at least one element is True
.
For example, the list of integers from 1 up to and including 10 has at least one odd number. Similarly, the list [1 .. 10]
has at least one even number. The following GHCi session should confirm the above observations:
1
2
3
4
ghci> or [even x | x <- [1 .. 10]]
True
ghci> or [odd x | x <- [1 .. 10]]
True
The vowels of the English alphabet are “A”, “E”, “I”, “O”, and “U”. Some words in English do not contain a vowel at all. Here are some common, and not so common, words that consist of only consonants:
C | G | P | S | T | W |
---|---|---|---|---|---|
cry | glyph | ply | sty | thy | why |
crypt | gym | pwn | sync | try | wry |
cyst | gypsy | pygmy | syzygy | tryst | wyrm |
If you were to test each of the above words for vowels, the result would be False
. Observe:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import Data.Foldable
import Text.Printf
-- | Some words in the English language that contain only consonants.
word =
[ "cry"
, "crypt"
, "cyst"
, "glyph"
, "gym"
, "gypsy"
, "ply"
, "pwn"
, "pygmy"
, "sty"
, "sync"
, "syzygy"
, "thy"
, "try"
, "tryst"
, "why"
, "wry"
, "wyrm"
]
-- | Whether a word has one or more vowels.
hasVowel :: String -> Bool
hasVowel str = or [s `elem` "aeiou" | s <- str]
-- | Test words to see whether they have vowels.
main = do
let result = or $ map (\w -> hasVowel w) word
printf "Any words have a vowel? %s\n" $ show result
putStrLn "Test individual word for vowels"
for_ word $ \w -> do
printf "%s -> %s\n" w $ show . hasVowel $ w
all
The function all
is a predicate counterpart of and
. Whereas and
expects all elements of a list to be boolean values, all
accepts a predicate that is used to determine whether all elements of a list satisfy the predicate. For example, suppose we have a list of integers. We do not know whether all the integers are positive. The function all
can help us in this scenario. Consider the GHCi session below:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ghci> import Control.Monad
ghci> import System.Random.Stateful
ghci> ella <- replicateM 20 (uniformRM (-3, 20) globalStdGen :: IO Integer)
ghci> ellb <- replicateM 20 (uniformRM (-3, 20) globalStdGen :: IO Integer)
ghci> ellc <- replicateM 20 (uniformRM (1, 20) globalStdGen :: IO Integer)
ghci> ella
[1,2,15,12,1,10,17,-3,7,20,-3,3,8,8,14,19,11,-2,1,18]
ghci> all (\x -> x > 0) ella
False
ghci> ellb
[1,10,4,1,3,2,10,-1,6,4,20,0,4,-2,14,19,-3,18,16,13]
ghci> all (\x -> x > 0) ellb
False
ghci> ellc
[6,9,9,13,16,2,17,11,15,7,2,7,9,16,20,8,4,7,18,5]
ghci> all (\x -> x > 0) ellc
True
As another example, consider the table below of the goals scored by players of Arsenal during the 2022—2023 season of the Premier League. We included only male players who scored at least one goal. The header “Goals-PK” means non-penalty goals.
Player | Goals | Goals-PK |
---|---|---|
Ben White | 2 | 2 |
Bukayo Saka | 14 | 12 |
Eddie Nketiah | 4 | 4 |
Fabio Vieira | 1 | 1 |
Gabriel Dos Santos | 3 | 3 |
Gabriel Jesus | 11 | 10 |
Gabriel Martinelli | 15 | 15 |
Granit Xhaka | 7 | 7 |
Jakub Kiwior | 1 | 1 |
Leandro Trossard | 1 | 1 |
Martin Ødegaard | 15 | 15 |
Oleksandr Zinchenko | 1 | 1 |
Rob Holding | 1 | 1 |
Reiss Nelson | 3 | 3 |
Thomas Partey | 3 | 3 |
William Saliba | 2 | 2 |
Our question is: Were all the goals scored non-penalty? If a player scored $g$ goals and statistics says the player scored $h$ non-penalty goals, then all goals scored by the player is non-penalty provided that $g = h$. I’m too lazy to check by hand. Let’s delegate the manual work to Haskell:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import Text.Printf
-- | Goals scored by male players of Arsenal during the 2022--2023
-- season of the Premier League. Each tuple follows the format
-- (name, goals, goals-pk), where
--
-- name := The name of the player.
-- goals := The number of goals the player scored.
-- goals-pk := The number of non-penalty goals scored.
goal =
[ ("Ben White", 2, 2)
, ("Bukayo Saka", 14, 12)
, ("Eddie Nketiah", 4, 4)
, ("Fabio Vieira", 1, 1)
, ("Gabriel Dos Santos", 3, 3)
, ("Gabriel Jesus", 11, 10)
, ("Gabriel Martinelli", 15, 15)
, ("Granit Xhaka", 7, 7)
, ("Jakub Kiwior", 1, 1)
, ("Leandro Trossard", 1, 1)
, ("Martin Ødegaard", 15, 15)
, ("Oleksandr Zinchenko", 1, 1)
, ("Rob Holding", 1, 1)
, ("Reiss Nelson", 3, 3)
, ("Thomas Partey", 3, 3)
, ("William Saliba", 2, 2)
]
-- | Were all the goals scored non-penalty?
main = do
let testAll = all (\(_, g, p) -> g == p) goal
let testAnd = and [g == p | (_, g, p) <- goal]
let testOr = or [g == p | (_, g, p) <- goal]
printf "(all) all non-penalty goals? %s\n" $ show testAll
printf "(and) all non-penalty goals? %s\n" $ show testAnd
printf "(or) some non-penalty goals? %s\n" $ show testOr
any
The function any
is the predicate counterpart of or
. The function or
requires all elements of a list to be boolean values. On the other hand, any
requires a predicate based upon which the function decides whether some elements of a list satisfy the predicate.
Consider a bunch of random integers. We know that there are negative integers and zero. Is there at least one positive integer? Let’s find out.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ghci> import Control.Monad
ghci> import System.Random.Stateful
ghci> a <- replicateM 20 (uniformRM (-20, 3) globalStdGen :: IO Integer)
ghci> b <- replicateM 20 (uniformRM (-20, 2) globalStdGen :: IO Integer)
ghci> c <- replicateM 20 (uniformRM (-20, 0) globalStdGen :: IO Integer)
ghci> a
[-16,-14,-1,-16,-19,-4,-8,-19,-1,-5,-6,1,-7,-3,-8,-15,-4,-11,-17,-10]
ghci> any (\x -> x > 0) a
True
ghci> b
[2,-4,-5,-5,2,0,2,-1,-18,-14,-2,-15,-11,-20,-15,-11,0,-4,-14,-4]
ghci> any (\x -> x > 0) b
True
ghci> c
[-20,-17,-10,-1,-6,-6,-3,-4,-3,-3,-1,-8,-4,-7,-19,-14,0,-15,-1,-7]
ghci> any (\x -> x > 0) c
False
Violet wants to increase her vocabulary. The words in her list vary in length. She wants to know whether the list has a short word, i.e. a word of at most 5 letters. Learning short words all the time is no fun. Violet also wants to determine whether the list has a long word, i.e. a word of at least 8 letters. Let’s use Haskell to find out:
1
2
3
4
5
ghci> a = ["benign", "elixir", "tumid", "intemperance", "narcissist"]
ghci> any (\str -> length str <= 5) a
True
ghci> any (\str -> length str >= 8) a
True
Exercises
Exercise 1. Consider the script arsenal.hs
from the section all. Modify the script to use any
to determine whether any of the goals scored were a result of a penalty kick.
Exercise 2. Recall the script pet.hs
from the section and. Modify the script to determine whether all birds have had a health check within the last 12 months. Furthermore, determine whether some of the birds have had a health check within the same time period.
Exercise 3. Many words in English that do not have a vowel use the letter “Y” to simulate a vowel sound. Refer to the script word.hs
from the section or. Modify the script to determine whether some of the words in the word list do not use “Y”. Print such words to standard output.
Exercise 4. Recall the exercise on Gilbreath’s conjecture from the section Free range numbers. Let $\ell$ be a list of prime numbers not exceeding an integer $n > 1$. Successive application of the difference operation would eventually result in the list [1]
. Determine how many times you must perform the difference operation in order to obtain a list that satisfies one of the properties below:
- All elements of the list are 1.
- The list has a zero.
- Each element of the list is either 0 or 1.
- Each element of the list is either 0, 1, or 2.
The values of $n$ are drawn from the list [10, 100, 1000, 10000, 100000]
.
Exercise 5. The Britannica Dictionary has a list of 100 core words for the TOEFL. Let’s refer to these words as the Britannica core. Use the Britannica core to answer the following questions.
- For each letter of the alphabet, is there a word that starts with the given letter? Output each letter for which the Britannica core does not have a word beginning with the letter.
- Do all words have at least two unique vowels? Output each word in the Britannica core that uses exactly one unique vowel.
- Are all words of at least length 5? Output each word that has length 4 or less.
Exercise 6. Consider the list below of female players of Sydney FC during the 2022—2023 soccer season in Australia.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
-- | Data on female players of Sydney FC during the 2022--2023 season.
-- Each tuple follows the format:
--
-- (name, nationality, goals, non-penalty-goals, penalty-kicks)
--
-- where
--
-- name := The name of the player.
-- nationality := The nationality of the player.
-- goals := The number of goals scored.
-- non-penalty-goals := The number of goals that do not result from a
-- penalty kick.
-- penalty-kicks := The number of penalty kicks.
player =
[ ("Princess Ibini-Isei", "AUS", 7, 6, 1)
, ("Charlize Rule", "AUS", 0, 0, 0)
, ("Sarah Hunter", "AUS", 4, 4, 0)
, ("Cortnee Vine", "AUS", 7, 7, 0)
, ("Mackenzie Hawkesby", "AUS", 6, 5, 1)
, ("Natalie Tobin", "AUS", 2, 2, 0)
, ("Charlotte Mclean", "AUS", 0, 0, 0)
, ("Jada Mathyssen-Whyman", "AUS", 0, 0, 0)
, ("Shay Hollman", "AUS", 0, 0, 0)
, ("Deborah De La Harpe", "IRL", 0, 0, 0)
, ("Kirsty Fenton", "AUS", 0, 0, 0)
, ("Madison Haley", "USA", 8, 8, 0)
, ("Rachel Lowe", "AUS", 4, 4, 0)
, ("Anna Green", "NZL", 0, 0, 0)
, ("Katie Offer", "AUS", 0, 0, 0)
, ("Indiana Dos Santos", "AUS", 1, 1, 0)
, ("Tahlia Franco", "AUS", 0, 0, 0)
, ("Rola Badawiya", "AUS", 1, 1, 0)
, ("Mary Stanic-Floody", "AUS", 0, 0, 0)
, ("Remy Siemsen", "AUS", 2, 2, 0)
, ("Shadeene Evans", "AUS", 0, 0, 0)
, ("Abbey Lemon", "AUS", 0, 0, 0)
, ("Jynaya Dos Santos", "AUS", 0, 0, 0)
, ("Teigan Collister", "AUS", 0, 0, 0)
, ("Anika Stajcic", "AUS", 0, 0, 0)
, ("Jasmine Black", "AUS", 0, 0, 0)
, ("Margaux Chauvet", "AUS", 0, 0, 0)
]
Write a program to help you answer the following questions:
- Are all players of Australian nationality? Output those players who are not Australian nationals.
- Of those players who scored at least one goal, were all the goals the results of non-penalty kicks? Output those players who scored penalty goals.
- Of those players who had at least 1 penalty kick, did the kicks all result in goals?
- Of those players who are not Australian nationals, did any of them score goals? Output non-Australian nationals and the number of goals each of those players scored.
Exercise 7. Implement the function and
without using the operator &&
.
Exercise 8. Implement the function or
without using the operator ||
.
Exercise 9. Implement the function all
without using the operator &&
.
Exercise 10. Implement the function any
without using the operator ||
.