Reputation: 347
In OCaml, a typical fold function looks like this:
let rec fold (combine: 'a -> 'b -> 'b) (base: 'b) (l: 'a list) : 'b =
begin match l with
| [] -> base
| x :: xs -> combine x (fold combine base xs)
end
For those familiar with OCaml (unlike me), it should be pretty straightforward what it's doing.
I'm writing a function that returns true when all items in the list satisfy the condition: if condition x is true for all x in some list l. However I'm implementing the function using a fold function and I'm stuck. Specifically I don't know what the list should return. I know that ideally the condition should be applied to every item in the list but I have no idea how the syntax should look. x && acc
works but it fails a very simply test (shown below)
let test () : bool =
not (for_all (fun x -> x > 0) [1; 2; -5; -33; 2])
;; run_test "for_all: multiple elements; returns false" test
Here is my preliminary attempt. Any help is appreciated:
let for_all (pred: 'a -> bool) (l: 'a list) : bool =
fold (fun(x:'a)(acc: bool)-> _?_&&_?_ )false l
Upvotes: 3
Views: 3059
Reputation: 36571
Your fold
works fine.
But you are operating under a faulty premise about how for_all
should work. You start with your initial value on false
, but for_all
is checking that a condition holds true for every element. It only needs to find one element for which that doesn't hold true to know that the entire thing is false.
On each iterative call, if the accumulator is already false
we know we can just propagate that to the end of the fold. If it's true, we update the accumulator to the result of calling the predicate on the current value.
let for_all predicate lt =
fold (fun x acc -> if not acc then acc else predicate x) true lst
This is an inherently inefficient way to implement for_all
because even if the very first iteration returns false
we still have to traverse the entire list.
By writing this without fold
we can exit the recursion as soon as the false
condition is found.
let rec for_all predicate = function
| [] -> true
| x::_ when not (predicate x) -> false
| _::xs -> for_all predicate xs
Upvotes: 0
Reputation: 435
let rec fold (combine: 'a -> 'b -> 'b) (base: 'b) (l: 'a list) : 'b =
match l with
| [] -> base
| x::xs -> combine x (fold combine base xs)
let for_all (pred: 'a -> bool) (lst: 'a list) =
let combine x accum =
(pred x) && accum
in
fold combine true lst
Your combine function should not do x && base
because elements of the list are not usually bool
. You want your predicate function first evaluate the element to bool, then you "and" it with the accumulator.
There is no need for begin
and end
in fold
. You can just pattern match with match <identifier> with
.
There are two widespread types of fold
: fold_left
and fold_right
. You're are using fold_right
, which, basically, goes through the whole list and begins "combining" from the end of the list to the front. This is not tail-recursive.
fold_left
, on the other hand goes from the front of the list and combines every element with the accumulator right away. This does not "eat up" your stack by a number of recursive function calls.
Upvotes: 2