Injecting truth

I had a scenario arise today where I needed an iterator with similar
semantics as Enumerable#all?, but non-short-circuiting. We came up with
a fairly elegant solution (imho), so I thought I’d be a good rubyzen
and share :slight_smile:

  1. First… the standard #all? (short-circuits):

irb> (1…5).all? { |i| puts i; i < 3 }
1
2
3
=> false

  1. Non-short-circuiting version (injecting truth):

irb> (1…5).inject(true) { |retval, i| puts i; retval &&= (i < 3) }
1
2
3
4
5
=> false

  1. One more way (though I like #2 better)

irb(main):016:0> (1…5).partition { |i| puts i; i < 3 }.last.empty?
1
2
3
4
5
=> false

-Ken

Hmm, why Enumerable#all? without short circuit? If you get to a point
where
the block doesnt apply, then you might aswell break.

j`ey
http://www.eachmapinject.com

Ken Kunz wrote:

3
=> false

-Ken

How about (1…5).map { |x| x < 3 }.all?

Ken Kunz wrote:

3
=> false

-Ken

Also, with #2, obviously your way works, but the &&= isn’t neccesary, a
&& has the intended effect, since the result of the block is put in the
accumulator. Again, it doesn’t matter at all, I’m mostly just writing
to avoid doing work.

Hi –

On Thu, 28 Sep 2006, Ken Kunz wrote:

3
=> false

  1. One more way (though I like #2 better)

irb(main):016:0> (1…5).partition { |i| puts i; i < 3 }.last.empty?
1
2
3
4
5
=> false

Cool. I want to play too :slight_smile:

[*1…5].map {|i| puts i; i < 3 }.all?
1
2
3
4
5
=> false

David

Hi –

On Thu, 28 Sep 2006, Joey wrote:

Hmm, why Enumerable#all? without short circuit? If you get to a point where
the block doesnt apply, then you might aswell break.

The idea is that it’s a case where you want the block to be executed,
unconditionally, once for every element in the array, and also want to
know whether at least one iteration returned false.

David

How about (1…5).map { |x| x < 3 }.all?

This approach is nice and concise… but I still prefer “truth
injection” for my actual code scenario. In my real code, the block is
multiple lines, and for clarity I don’t love the idea of tacking a
method call on the end of a multi-line block.

Plus I just like saying “injecting truth” :slight_smile:

… obviously your way works, but the &&= isn’t neccesary …

Thanks for the tip.

-Ken

Hi –

On Thu, 28 Sep 2006, Mike H. wrote:

How about (1…5).map { |x| x < 3 }.all?

Whoops, I didn’t see that before I (re)posted it :slight_smile:

David

Mike H. [email protected] wrote:

Also, with #2, obviously your way works, but the &&= isn’t neccesary,
a && has the intended effect, since the result of the block is put in
the accumulator. Again, it doesn’t matter at all, I’m mostly just
writing to avoid doing work.

In that case order should be reversed to avoid short circuiting
altogether
(in this case it does not matter as the test has no side effects, but if
it
had…)

(1…5).inject(true) { |retval, i| puts i; i < 3 && retval}
1
2
3
4
5
=> false

Kind regards

robert

Robert K. wrote:

effects, but if it had…)
Kind regards

robert

Very good point, neglected to think about that, glossing over the fact
that the puts executing doesn’t mean the relevant expression evaluated.
With the fixed example:

irb(main):001:0> (1…5).inject(true) { |s,i| s &&= (puts i; i < 3) }
1
2
3
=> false
irb(main):002:0> (1…5).inject(true) { |s,i| s && (puts i; i < 3) }
1
2
3
=> false
irb(main):003:0> (1…5).inject(true) { |s,i| (puts i; i < 3) && s }
1
2
3
4
5
=> false

With the &&= vs && thing, I made a salient point about a bug in the
example and I was too dumb to realize it.