tags:

views:

82

answers:

3

Suppose I have an array of values [a,b,c,d,...] and a function f(x,...) which returns true or false.

[1,2,3,4].map {|x| f(x)}  => [true true false true]

First, what is the best way to collapse the resultant list into a true or false value (via AND)? Is there a function that would allow me to map:

[true true false true]

to:

((true && true) && false) && true

using a cumulative pair-wise application of the binary && operator?

In this case the cost of evaluating the function is substancial, so would like to use a lisp-style "and" to evaluate the arguments (function applications) sequentially until one of them is false. Determined that could do:

!![1,2,3,4].each {|x| break false if !f(x) }

Which is ugly. Hoping that there is a more elegant way to do this. I am aware that I could add a new comprehension to Array, but hoping that there is something built-in that does this better. Thanks

+10  A: 

You're looking for Enumerable#all?, which returns true if all of the invocations of its block are truthy. There is also Enumerable#any?, which returns true if any of the invocations of its block are truthy:

#!/usr/bin/ruby1.8

def even(n)
  n % 2 == 0
end

p [1, 2, 3, 4].all? { |i| even(i) }    # => false
p [2, 4, 6, 8].all? { |i| even(i) }    # => true
p [1, 2, 3, 4].any? { |i| even(i) }    # => true
p [1, 3, 5, 7].any? { |i| even(i) }    # => false

any? short-curcuits: the first truthy value causes it to return true. So does all? short circuit: the first falsy value causes it to return false.

Wayne Conrad
Sorry for nitpicking, but `Fixnum` already has methods `even?` and `odd?`... ;)
Mladen Jablanović
@Mladen Jablanovic, Good point. For this example I wanted a user-defined function.
Wayne Conrad
+1  A: 

How about: [true, true, false, true].all?

This will return true if the array does not contain anything that evaluates to false or nil.

Stephen Doyle
+2  A: 

You could try folding your array:

[true,true,false,true].inject(:&) #=> false (AND-ed)
[true,true,false,true].inject(:|) #=> true  (OR-ed)
[1,2,3,4].inject(:+)              #=> 10    (summed)
[1,2,3,4].inject(:*)              #=> 24    (multiplied)

You get the clue. Back to your example:

[1,2,3,4].map {|x| f(x) }.inject(:&)

However it won't shortcircuit - you probably better look into "any?" or "all?" except your "f()" has side-effects (like modifying a persistent storage).

hurikhan77
Thanks, this answers the first question! Wayne's answers the second.
Jonathan Shore