This question already has answers here:
What does ||= (or-equals) mean in Ruby?
(23 answers)
Closed 8 years ago.
I have some doubts regarding OR EQUALS (||=) operator in ruby. How does ruby interpreter implement it? Here is a sample of code:
class C
def arr
#num ||= []
end
end
When we use OR EQUALS operator in this circumstances, the first call to this method initializes the variable and adds an element, that's fine. When a second call is made to arr, how does it know that array has one element in it..
In Ruby, there are two values that are considered logical false. The first is the boolean value false, the other is nil. Anything which is non-nil and not explicitly false is true. The first time though the method, #num is nil, which is treated as false and the logical or portion of ||= needs to be evaluated and ends up assigning the empty array to #num. Since that's now non-nil, it equates to true. Since true || x is true no matter what x is, in future invocations Ruby short circuits the evaluation and doesn't do the assignment.
In general terms x ||= y is equivalent to x = x || y, it's just shorthand. It's implemented as the expanded form, same as &&=, += or -=.
Most programming languages, Ruby included, will stop executing a logical comparison statement like || on the first true value it encounters and return that. Likewise, it will halt on the first false value when using &&.
In general terms:
false || foo()
This will return false and not evaluate foo().
The pattern is best described as a "lazy initializer", that is the variable is defined only once, but only when it's actually used. This is in contrast to an "eager initializer" that will do it as early as possible, like inside the initialize method.
You'll see other versions of this pattern, like:
def arr
#num ||= begin
stuff = [ ]
# ...
stuff
end
end
This handles cases where the initial value is not as trivial and may need some work to produce. Once again, it's only actually generated when the method is called for the first time.
How does Ruby know on the second pass to not initialize it again? Simple, by that point #num is already defined as something.
As a note, if you're using values like false that would evaluate as non-true, then ||= will trigger every time. The only two logically false values in Ruby are nil and false, so most of the time this isn't an issue.
You'll have to do this the long-form way if you need to handle false as well:
def arr
return #num unless num.nil?
#num = false
end
There was talk of adding an ||=-like operator that would only trigger on nil but I don't think that has been added to Ruby yet.
Related
I'm having a little difficulty in understanding the below under initialize, and color methods. This is from the solution I was given:
The one under initialize:
I think it means #given equals value, and if value is equal to 0, then its false, otherwise its true
Under color not sure what the 2nd question mark means.
def initialize(value)
#value = value
#given = value == 0 ? false : true
end
def color
given? ? :blue : :red
end
def given?
#given
end
Under color not sure what the 2nd question mark means.
This is called the conditional operator. (See, for example, section 11.5.2.2.5 Conditional operator expression of the ISO Ruby Language Specification.)
condition ? consequence_truthy : consequence_falsy
will evaluate condition and depending on whether the result is truthy or falsy, it will evaluate either the first or the second consequence.
Personally, I find the conditional operator useless in Ruby. In C and similar languages, the conditional operator is required because it is an expression, whereas the conditional statement is, well, a statement. So, if you need to use a conditional in an expression, you cannot use a conditional statement, you must use the conditional operator.
However, this does not apply to Ruby: in Ruby, everything is already an expression, there are no statements. The conditional expressions (see, for example, section 11.5.2.2.2 The if expression, section 11.5.2.2.3 The unless expression, and section 11.5.2.2.4 The case expression) can already be used as an expression, so there is no need to use the conditional operator.
Personally, I find the conditional expression more readable than the conditional operator:
if condition then consequence_truthy else consequence_falsy end
This is semantically equivalent to the conditional operator, the only difference is syntactical: it has different precedence, which however, actually works more natural than the precedence of the conditional operator.
So, your examples could, in my opinion, be clearer written as
def initialize(value)
#value = value
#given = if value == 0 then false else true end
end
def color
if given? then :blue else :red end
end
Actually, the initialize contains a useless-use-of-conditional because Integer#== already returns a boolean. So, the following would be even clearer:
def initialize(value)
#value = value
#given = value != 0
end
Mostly we use ? mark as a part of the method's name which returns boolean value. this is just a convention we ruby coders use to follow.
In your example given? is the method name as it return boolean value so here we have follow the convention.
and in ternary conditional operator we use
condition ? do something when condition is true : do something when condition is false
In your case
given? it's returning true or false and which is also method name
So the first question mark is a part of the method name and 2nd question mark is the part of ternary condition
This question already has answers here:
Do all? and any? guarantee short-circuit evaluation?
(3 answers)
Closed 4 years ago.
Does any? break from the loop when a match is found?
The following is the any? source code, but I don't understand it.
static VALUE
enum_any(VALUE obj)
{
VALUE result = Qfalse;
rb_block_call(obj, id_each, 0, 0, ENUMFUNC(any), (VALUE)&result);
return result;
}
Yes, it does break the loop. One does not need to dig into c code to check that:
[1,2,3].any? { |e| puts "Checking #{e}"; e == 2 }
# Checking 1
# Checking 2
#⇒ true
The term is "short-circuiting" and yes, any? does that. After it finds a match, it doesn't look any further.
Does any? break from the loop when a match is found?
The documentation is unclear about that:
The method returns true if the block ever returns a value other than false or nil.
Note: it does not say "when the block ever returns a value other than false or nil" or "as soon as the block ever returns a value other than false or nil".
This can be interpreted either way, or it can be interpreted as making no guarantees at all. If you go by this documentation, then you can neither guarantee that it will short-ciruit, nor can you guarantee that it won't short-circuit.
Generally speaking, this is typical for API specifications: make the minimum amount of guarantees, giving the API implementor maximum freedom in how to implement the API.
There is somewhere else we can look: the ISO Ruby Programming Language Specification (bold emphasis mine):
15.3.2.2.2 Enumerable#any?
any?(&block)
Visibility: public
Behavior:
a) Invoke the method each on the receiver
b) For each element X which each yields
If block is given, call block with X as the argument.
If this call results in a trueish object, return true
As you can see, again it only says "if", but not "when" or "as soon as". This sentence can be interpreted in two ways: "Return true as the result of the method" (no indication of how often the block gets called, only that the method will return true at the end) or "return true when you encounter an invocation of the block that evaluates to a trueish value".
Try #3: The Ruby Spec:
it "stops iterating once tähe return value is determined" do
So, yes, we can indeed rely on the fact that the block is only evaluated until the first truthy value is encountered.
The following is the any? source code, but I don't understand it.
Note: by looking at the source code, you can not determine how something behaves in Ruby. You can only determine how something behaves in that specific version of that specific implementation of Ruby. Different implementations may behave differently (for example, in YARV, Ruby threads cannot run at the same time, in JRuby, they can). Even different versions of the same implementation can behave differently.
It is usually not a good idea to make assumptions about the behavior of a programming language by just looking at a single version of a single implementation.
However, if you really want to look at some implementation, and are fully aware about the limitations of this approach, then I would suggest to look at Rubinius, Topaz, Opal, IronRuby, or JRuby. They are (in my opinion) better organized and easier to read than YARV.
For example, this is the code for Enumerable#any? in Rubinius:
def any?
if block_given?
each { |*element| return true if yield(*element) }
else
each { return true if Rubinius.single_block_arg }
end
false
end
This looks rather clear and readable, doesn't it?
This is the definition in Topaz:
def any?(&block)
if block
self.each { |*e| return true if yield(*e) }
else
self.each_entry { |e| return true if e }
end
false
end
This also looks fairly readable.
The soure in Opal is a little bit more complex, but only marginally so:
def any?(pattern = undefined, &block)
if `pattern !== undefined`
each do |*value|
comparable = `comparableForPattern(value)`
return true if pattern.public_send(:===, *comparable)
end
elsif block_given?
each do |*value|
if yield(*value)
return true
end
end
else
each do |*value|
if Opal.destructure(value)
return true
end
end
end
false
end
[Note the interesting use of overriding the ` method for injecting literal ECMAScript into the compiled code.]
Most of the added complexity compared to the Rubinius and Topaz versions stems from the fact that Opal already supports the third overload of any? taking a pattern which was introduced in Ruby 2.5, whereas Rubinius and Topaz only support the two overloads with a block and without any arguments at all.
IronRuby's implementation implements the short-circuiting like this:
if (predicate.Yield(item, out blockResult)) {
result = blockResult;
return selfBlock.PropagateFlow(predicate, blockResult);
}
JRuby's implementation is a little bit more involved still, but you can see that as soon as it encounters a truthy block value, it breaks out of the loop by throwing a SPECIAL_JUMP exception and catching it to return true.
Yes and it's easy to prove:
irb(main):009:0> %w{ant bear cat}.any? {|word| puts "hello"; word.length >= 4}
hello
hello
=> true
It has printed only twice. If it did not break it would print 3 times.
This question already has an answer here:
Why isn't `method=` treated the same as any other method?
(1 answer)
Closed 6 years ago.
Would someone care to explain why in older versions of Ruby, the result of the assignment was the value returned by the attribute-setting method, but after Ruby 1.8, the value of the assignment is always the value of the parameter; the return value of the method is discarded. In the code that follows, older versions of Ruby would set result to 99. Now result will be set to 2.
class Test
def val=(val)
#val = val
return 99
end
end
t = Test.new
result = (t.val = 2)
result # => 2
What was the reasoning behind this change?
It's not uncommon to chain assignments together when you want to assign the same value to multiple variables. This is even more common in other languages.
#user_id = user.id = next_user_id
But what happens when you aren't thinking about that, and so the return value isn't the same as the input value?
class User
def id=(name)
#id = name
#modified = true
end
def modified?
#modified
end
end
This code will work totally fine until one day when you go drop it in an assignment chain like the above, when all of a sudden you'll get unexpected results.
So, the interpreter does some sort of voodoo and ensures that the RHS of the assignment is the return value, discarding the actual return value.
Assignments always evaluate to the assigned value. That's a simple and consistent rule, both consistent within Ruby itself, as well as consistent with most other expression-based programming languages.
Everything else would be an inconsistent special case, and those are bad.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What does ||= mean in Ruby?
I'm new to ruby and I saw this being used in one of the answers here:
RACK_ENV = ENV['ENVIRONMENT'] ||= 'test'
I couldn't find any reference to the ||= operator...
here is the article which explains it really good.
« Ruby, concurrency,... | Main | How does one update... »
The curious case of the Ruby T-Square operator.
By prashant on Dec 14, 2008
The "||=" operator is interesting, both in what it does as much as in how it's widely used in Ruby land. The operator does not do what you would usually expect. i.e.,
a ||= expr
is not the same as
a = a || expr
The evaluation happens to be
a or a = expr
and the difference is important in at least one use case [0]
As a little DTrace script will verify, this operator is not implemented is a method(or anywhere in Ruby land) and is intrinsic to the VM. The reason is performance, and the fact that the entire expression does not have to be evaluated to yield a result when you're 'OR'ing:
"Ruby's Boolean operators are built into the language and are not based on methods: classes, for example, cannot define their own && method. Ruby defines special true and false values but does not have a Boolean type. method. The reason for this is that Boolean operators can be applied to any value and must behave consistently for any kind of operand."
. . .
"Another reason that Ruby's Boolean operators are a core part of the language rather than redefinable methods is that the binary operators are "short-circuiting." If the value of the operation is completely determined by the lefthand operand, then the righthand operand is ignored and is never even evaluated."
https://blogs.oracle.com/prashant/entry/the_ruby_t_square_operator
what does || do? If you have a and b then a || b is true if and only if either a or b is true. It is the same with ||= this operator combines two operations '=' and '||'. So a ||= b is equivelent to c || c = b
EDIT: so in your context ENV['ENVIRONMENT'] ||= 'test' means that if ENV['ENVIRONMENT'] is not nil and not false it will preserve its value, otherwise it will become 'test' and after that the new value of ENV['ENVIRONMENT'] is assigned to RACK_ENV
Trying to do something weird that might turn into something more useful, I tried to define my own []= operator on a custom class, which you can do, and have it return something different than the value argument, which apparently you can't do. []= operator's return value is always value; even when you override this operator, you don't get to control the return value.
class Weird
def []=(key, value)
puts "#{key}:#{value}"
return 42
end
end
x = Weird.new
x[:a] = "a"
output "a:a"
return value => "a" # why not 42?
Does anyone have an explanation for this? Any way around it?
ruby MRI 1.8.7. Is this the same in all rubys; Is it part of the language?
Note that this behavior also applies to all assignment expressions (i.e. also attribute assignment methods: def a=(value); 42; end).
My guess is that it is designed this way to make it easy to accurately understand assignment expressions used as parts of other expressions.
For example, it is reasonable to expect x = y.a = z[4] = 2 to:
call z.[]=(4,2), then
call y.a=(2), then
assign 2 to the local variable x, then finally
yield the value 2 to any “surrounding” (or lower precedence) expression.
This follows the principle of least surprise; it would be rather surprising if, instead, it ended up being equivalent to x = y.a=(z.[]=(4,2)) (with the final value being influenced by both method calls).
While not exactly authoritative, here is what Programming Ruby has to say:
Programming Ruby (1.8), in the Expressions section:
An assignment statement sets the variable or attribute on its left side (the lvalue) to refer to the value on the right (the rvalue). It then returns that value as the result of the assignment expression.
Programming Ruby 1.9 (3rd ed) in section 22.6 Expressions, Conditionals, and Loops:
(right after describing []= method calls)
The value of an assignment expression is its rvalue. This is true even if the assignment is to an attribute method that returns something different.
It’s an assignment statement, and those always evaluate to the assigned value. Making this different would be weird.
I suppose you could use x.[]= :a, "a" to capture the return value.