Performant alternative to Ruby none? - ruby

Does Ruby's none? method still loop through all the elements of an array even if the condition evaluates to true before the last element? The docs do not specify, and on some further inspection, I can get some interesting results...
arr = [3, 4, 5, 6]
arr.none? {|e| p e > 4 }
false
false
true
=> false
arr.none? {|e| puts e > 4 }
false
false
true
true
=> true
Puts doesn't return the object though, so none? returns true in that example because print e > 4 neither evaluates to true nor false; therefore, the block never returns true for all the elements?
Since p does return the object, I am led to conclude that none? does in fact stop when the block first evaluates to true, but I'm not 100% sure.
If it does loop through all the elements no matter what, why? What's the most performant alternative? The method returns a boolean, so it should just stop the first time it evaluates to true and return false.

I think there is just a fundamental misunderstanding between what is true and false in ruby.
In ruby every object is either "truthy" or "falsey". (and everything is an object)
Falsey Objects: NilClass (nil) and FalseClass (false)
Truthy Objects: Everything else
For Example:
[1,"a",Object,nil,false].map do |o|
[o,o ? true : false]
end
#=> [[1, true],
# ["a",true]
# [Object,true]
# [nil, false]
# [false,false]
So now to answer your question:
Yes none? short circuits. (Stops at the first "truthy" value). As your first example shows.
Your second example uses puts. puts always returns nil and since nil is "falsey" and none? is looking for "truthy" values the loop continues until the end.

Related

Why isn't 'false' equal to 'nil'?

false is not equal to nil, for example:
false == nil # => false
The same goes for false and 0:
false == 0 # => false
You get the same result for nil and 0:
nil == 0 # => false
Why does Ruby act like this?
In Ruby, the only time nil will return true on a == comparison is when you do: nil == nil
You can read more about nil here:
https://ruby-doc.org/core-2.2.3/NilClass.html
nil is the equivalent of undefined in JavaScript or None in Python
Consider in JavaScript:
>> (undefined === false)
=> false
Or Python:
>> (None == False)
=> False
Nil and False (Equality)
Despite what many people new to the language may expect, nil and false are objects rather than language keywords. Consider FalseClass which says:
The global value false is the only instance of class FalseClass and represents a logically false value in boolean expressions.
Likewise, NilClass is:
[t]he class of the singleton object nil.
When you use a method like Comparable#== to compare them, Ruby invokes the spaceship operator <=> on the instances since neither class defines the #== method. Because nil and false are not the same objects, and because neither overrides the basic equality operator, the expression false == nil will always be false.
Likewise, an Integer like 0 is neither an instance of FalseClass nor NilClass. Both false == 0 and nil == 0 are therefore also false because neither instance evaluates to zero.
Truthy and Falsey (Conditionals)
For branching purposes, both false and nil evaluate as false in a conditional expression. Everything else evaluates as true.
Consider the following non-idiomatic, contrived, but hopefully illustrative example of Ruby's basic truth tables:
def truth_table value
if value
true
else
false
end
end
truth_table nil
#=> false
truth_table false
#=> false
truth_table true
#=> true
truth_table 'a'
#=> true
truth_table 0
#=> true
truth_table 1
#=> true
Boolean Evaluation
Nil and false are semantically different in Ruby, and descend from different base classes. They are also not equal to each other. However, they can both be treated as "not true" for branching purposes or within a Boolean context. For example, consider the following idioms for casting a result as a Boolean true or false value:
!(nil)
#=> true
!!(nil)
#=> false
You can use this idiom to shorten the previous truth table example to:
# Convert any value to a Boolean.
def truth_table value
!!value
end
# Test our truth table again in a more compact but less readable way.
table = {}
[nil, false, true, 'a', 0, 1].map { |v| table[v] = truth_table(v) }; table
#=> {nil=>false, false=>false, true=>true, "a"=>true, 0=>true, 1=>true}

Rubymonk Ascent lesson: "Ripping the Guts"

From the Rubymonk ascent tutorial "Ripping the Guts", what's the difference between this code: (mine)
# compute([[4, 8], [15, 16]])
def compute(ary)
return nil unless ary
ary.map do |a, b|
a + b unless b.nil?
a if b.nil?
end
end
and the solution given:
def compute(ary)
return nil unless ary
ary.map { |(a, b)| !b.nil? ? a + b : a }
end
My code doesn't pass the tests, but the solution does. They look like they do the same thing though. What am I doing wrong?
If b.nil? is false, then the following line:
a if b.nil?
Evaluates to nil. Since this is the last statement in the block, this is the value that is returned from your block. It doesn't see the nil and return the previous line's value instead, or completely "skip over" the line since the if wasn't true, it just returns nil.
If you were interested in doing something instead of returning a value, then I think your code would do the same thing as the given solution. The statements a and a + b aren't evaluated unless they're the result you're interested in (which would be important if they were method calls doing something external).

Need clarification on Ruby logical operators

Recently I started learning Ruby. I am practicing logical operators in irb and I got these results, which I don't understand. Can you please clarify these examples for me?
1 and 0
#=> 0
0 and 1
#=> 1
0 && 1
#=> 1
As opposed to other languages like C, in Ruby all values except for nil and false are considered “truthy”. This means, that all these values behave like true in the context of a boolean expression.
Ruby's boolean operators will not return true or false. Instead, they return the first operand that causes the evaluation of the condition to be complete (also known as short-circuit evaluation). For boolean and that means, it will either return the first “falsy” operand or the last one:
false && 1 # => false (falsy)
nil && 1 # => nil (falsy)
false && nil # => false (falsy)
1 && 2 # => 2 (truthy)
For boolean or that means, it will either return the first “truthy” operand or the last one:
false || 1 # => 1 (truthy)
nil || 1 # => 1 (truthy)
false || nil # => nil (falsy)
1 || 2 # => 1 (truthy)
This allows for some interesting constructs. It is a very common pattern to use || to set default values, for example:
def hello(name)
name = name || 'generic humanoid'
puts "Hello, #{name}!"
end
hello(nil) # Hello, generic humanoid!
hello('Bob') # Hello, Bob!
Another similar way to acheive the same thing is
name || (name = 'generic humanoid')
With the added benefit that if name is truthy, no assignment is performed at all. There is even a shortcut for this assignment of default values:
name ||= 'generic humanoid'
If you paid careful attention you will have noticed that this may cause some trouble, if one valid value is false:
destroy_humans = nil
destroy_humans ||= true
destroy_humans
#=> true
destroy_humans = false
destroy_humans ||= true
destroy_humans
#=> true, OMG run!
This is rarely the desired effect. So if you know that the values can only be a String or nil, using || and ||= is fine. If the variable can be false, you have to be more verbose:
destroy_humans = nil
destroy_humans = true if destroy_humans.nil?
destroy_humans
#=> true
destroy_humans = false
destroy_humans = true if destroy_humans.nil?
destroy_humans
#=> false, extinction of humanity digressed!
That was close! But wait, there is another caveat – specifically with the usage of and and or. These should never be used for boolean expressions, because they have very low operator precedence. That means they will be evaluated last. Consider the following examples:
is_human = true
is_zombie = false
destroy_human = is_human && is_zombie
destroy_human
#=> false
is_human = true
is_zombie = false
destroy_human = is_human and is_zombie
destroy_human
#=> true, Waaaah but I'm not a zombie!
Let me add some parentheses to clarify what's happening here:
destroy_human = is_human && is_zombie
# equivalent to
destroy_human = (is_human && is_zombie)
destroy_human = is_human and is_zombie
# equivalent to
(destroy_human = is_human) and is_zombie
So and and or are really just useful as “control-flow operators”, for example:
join_roboparty or fail 'forever alone :('
# this will raise a RuntimeError when join_roboparty returns a falsy value
join_roboparty and puts 'robotz party hard :)'
# this will only output the message if join_roboparty returns a truthy value
I hope that clarifies everything you need to know about these operators. It takes a bit of getting used to, because it differs from the way other languages handle it. But once you know how to use the different options, you've got some powerful tools at hand.
Both values are 'truthy' (in Ruby everything that isn't nil or false is truthy), so in all cases the second value is returned. On the contrary, if you use 'or', first value will be returned:
1 || 0 #=> 1
0 || 1 #=> 0
In Ruby both 0 and 1 is truth value. (Only nil and false are false value)
If both operands are truth value, and, && returns the last value.

Use case for `&&=`

I have seen and used by myself lots of ||= in Ruby code, but I have almost never seen or used &&= in practical application. Is there a use case for &&=?
Not to be glib, but the obvious use case is any time you would say x && foo and desire the result stored back into x. Here's one:
list = [[:foo,1],[:bar,2]]
result = list.find{ |e| e.first == term }
result &&= result.last # nil or the value part of the found tuple
any sort of iteration where you want to ensure that the evaluation of a boolean condition on all elements returns true for all the elements.
e.g.
result = true
array.each do |elem|
# ...
result &&= condition(elem) # boolean condition based on element value
end
# result is true only if all elements return true for the given condition
Validation of the document such as,
a = {'a' => ['string',123]}
where the element in a['a'] should be a String. To validate them, I think you can use,
def validate(doc, type)
valid = true
doc.each{|x|
valid &&= x.is_a? type
}
valid
end
1.9.3p392 :010 > validate(a['a'],String) => false

How do I test if all items in an array are identical?

I can generate a few lines of code that will do this but I'm wondering if there's a nice clean Rubyesque way of doing this. In case I haven't been clear, what I'm looking for is an array method that will return true if given (say) [3,3,3,3,3] or ["rabbits","rabbits","rabbits"] but will return false with [1,2,3,4,5] or ["rabbits","rabbits","hares"].
Thanks
You can use Enumerable#all? which returns true if the given block returns true for all the elements in the collection.
array.all? {|x| x == array[0]}
(If the array is empty, the block is never called, so doing array[0] is safe.)
class Array
def same_values?
self.uniq.length == 1
end
end
[1, 1, 1, 1].same_values?
[1, 2, 3, 4].same_values?
What about this one? It returns false for an empty array though, you can change it to <= 1 and it will return true in that case. Depending on what you need.
I too like preferred answer best, short and sweet. If all elements were from the same Enumerable class, such as Numeric or String, one could use
def all_equal?(array) array.max == array.min end
I would use:
array = ["rabbits","rabbits","hares", nil, nil]
array.uniq.compact.length == 1
I used to use:
array.reduce { |x,y| x == y ? x : nil }
It may fail when array contains nil.

Resources