简体   繁体   中英

Writing a recursive function on lists in Haskell

I have the following question:

Define the functions

 and, or :: [Bool] -> Bool 

which give the conjunction and disjunction of a list of Booleans. For instance,

 and [False, True] = False or [False, True] = True 

On an empty list and gives True and or gives False ; explain the reason for these choices.

I know I can answer it but am not sure of the most efficient way to lay it out. Any help would be much appreciated.

My solution was this (but I think it could be more efficient):

and, or :: [Bool] -> Bool

and []             = True
and [True, True]   = True
and [False, False] = True
and [True, False]  = False

or []             = False
or [True, True]   = False
or [False, False] = False
or [True, False]  = True

The key step you're going to have to make is to think inductively . Your current solution:

and :: [Bool] -> Bool

and []             = True
and [True, True]   = True
and [False, False] = True
and [True, False]  = False

enumerates some of the possibilities, but it obviously doesn't work for all lists. So how to write one that will work for lists of any length?

In Haskell, you can usually write your functions by taking apart a data type. In this case, lists. Lists are defined as:

data [a] = []
         | a : [a]

So, lists have two cases: either the empty list, or a one element with a tail. Let's start writing your and function then, so that it matches those two cases for lists:

and []     = True
and (a:as) = ...

So, the "base case", the empty list, is True . But what should we do for the case of a list with one element, and some tail?

Well, we already have the && function in Haskell:

> True && True
True
> True && False
False
> False && True
False
> False && False
False

Interesting! So, && takes two arguments, and correctly determines if both arguments are True. And we are currently have a list with one element, and a list for a tail. And at the same time, we're defining the and function, that results in a single boolean value when applied to a list.

So we can make the inductive step and use the function we're defining, and , together with the && binary operator, to finish our definition:

and []     = True
and (a:as) = a && (and as)

So we evaluate the tail of the list to some value (recursively), and use the && operator to combined that value with the current value, and that's our function written.

Recursion and induction, driven by the recursive structure of the lists, are the key technique to learn in programming. If you can write this, you're making a big step forward.

I would suggest to use pattern matching to dissect the list. Start with these condidtions:

and [] = True
and (True : xs) = ...
and (False : xs) = ...

OK, for an empty list and is True . If a list starts with a " True " head, how do you determinde the "truth" of the complete list? Do you need a recursive call or not? What if you have a " False " head?

The or case is similar, just start with or [] = False

Note that when you start with these definitions, which already pattern match on the truth values, you don't even need to use && or || .

Here are few bits from me:

and [] = True
and (True : xs) = and xs
and (False : xs) = False

or [] = False
or (True : xs) = True
or (False : xs) = or xs

Sorry, if I have spoiled the "homework" mode of this question.

i tried to solve this with folds

and = foldl (&&) True
or = foldl (||) False

this would be a simple solution to write - but i think a more abstract one and takes a [Bool] and &&s the first element of this list with the accumulator True this result will be the next accumulator to foldl with.

yours ε/2

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM