Official Expansion of ||= Conditional Assignment Operator

Ruby Conditional-Assignment and Private Methods

That looks like a bug.

UPDATE: The bug was fixed in trunk, and is slated for back porting to 2.1 and 2.0.

Note that the problem is more general than that, it is broken for all abbreviated assignments, not just conditional abbreviated assignments:

private def foo=(*) end
public def foo; 0 end

self.foo = 42

self.foo += 42
# private method `foo=' called for main:Object (NoMethodError)

private :foo

self.foo += 42
# private method `foo' called for main:Object (NoMethodError)

||=' operator in Ruby

It's an assignment operator for 'Conditional Assignment'

See here -> http://en.wikibooks.org/wiki/Ruby_Programming/Syntax/Operators

Conditional assignment:

 x = find_something() #=>nil
x ||= "default" #=>"default" : value of x will be replaced with "default", but only if x is nil or false
x ||= "other" #=>"default" : value of x is not replaced if it already is other than nil or false

Operator ||= is a shorthand form of the expression:

x = x || "default" 

EDIT:

After seeing OP's edit, the example is just an extension of this, meaning:

car = method_1 || method_2 || method_3 || method_4

Will assign the first non-nil or non-false return value of method_1, method_2, method_3, method_4 (in that order) to car or it'll retain its old value.

What does ||= (or-equals) mean in Ruby?

This question has been discussed so often on the Ruby mailing-lists and Ruby blogs that there are now even threads on the Ruby mailing-list whose only purpose is to collect links to all the other threads on the Ruby mailing-list that discuss this issue.

Here's one: The definitive list of ||= (OR Equal) threads and pages

If you really want to know what is going on, take a look at Section 11.4.2.3 "Abbreviated assignments" of the Ruby Language Draft Specification.

As a first approximation,

a ||= b

is equivalent to

a || a = b

and not equivalent to

a = a || b

However, that is only a first approximation, especially if a is undefined. The semantics also differ depending on whether it is a simple variable assignment, a method assignment or an indexing assignment:

a    ||= b
a.c ||= b
a[c] ||= b

are all treated differently.

Is the ruby operator ||= intelligent?

This is extremely easy to test:

class MyCache
def initialize
@hash = {}
end

def []=(key, value)
puts "Cache key '#{key}' written"
@hash[key] = value
end

def [](key)
puts "Cache key '#{key}' read"
@hash[key]
end
end

Now simply try the ||= syntax:

cache = MyCache.new
cache["my key"] ||= "my value" # cache value was nil (unset)
# Cache key 'my key' read
# Cache key 'my key' written

cache["my key"] ||= "my value" # cache value is already set
# Cache key 'my key' read

So we can conclude that no assignment takes place if the cache key already exists.

The following extract from the Rubyspec shows that this is by design and should not be dependent on the Ruby implementation:

describe "Conditional operator assignment 'obj.meth op= expr'" do
# ...
it "may not assign at all, depending on the truthiness of lhs" do
m = mock("object")
m.should_receive(:foo).and_return(:truthy)
m.should_not_receive(:foo=)
m.foo ||= 42

m.should_receive(:bar).and_return(false)
m.should_not_receive(:bar=)
m.bar &&= 42
end
# ...
end

In the same file, there is a similar spec for [] and []= that mandates identical behaviour.

Although the Rubyspec is still a work in progress, it has become clear that the major Ruby implementation projects intend to comply with it.

Expansion with conditional ternary operator

Unfortunately this isn’t currently possible. The spread syntax, like its name tells us, is a part of the syntax of the language and not a ‘normal’ operator that deals with expressions (à la + or typeof). The ternary operator needs expressions after the ? and :, so you can’t use the syntax in those spots.

You’ll have to do e.g.

condition
? console.log(…)
: console.log(…)

What does the |= operator do in Java?

|= is a bitwise-OR-assignment operator. It takes the current value of the LHS, bitwise-ors the RHS, and assigns the value back to the LHS (in a similar fashion to += does with addition).

For example:

foo = 32;   // 32 =      0b00100000
bar = 9; // 9 = 0b00001001
baz = 10; // 10 = 0b00001010
foo |= bar; // 32 | 9 = 0b00101001 = 41
// now foo = 41
foo |= baz; // 41 | 10 = 0b00101011 = 43
// now foo = 43

Getting confused with == and = in if statement

Because the "result" of an assignment is the value assigned... so it's still a boolean expression in the second case. if expressions require the condition to be a boolean expression, which is satisfied by the second but not the first. Effectively, your two snippets are:

int a;

a = 1;
if (a) { }

and

boolean b;

b = true;
if (b) { }

Is it clear from that expansion that the second version will compile but not the first?

This is one reason not to do comparisons with true and false directly. So I would always just write if (b) instead of if (b == true) and if (!b) instead of if (b == false). You still get into problems with if (b == c) when b and c are boolean variables, admittedly - a typo there can cause an issue. I can't say it's ever happened to me though.

EDIT: Responding to your edit - assignments of all kinds can be used in if statements - and while loops etc, so long as the overall condition expression is boolean. For example, you might have:

String line;
while ((line = reader.readLine()) != null)
{
// Do something with a line
}

While I usually avoid side-effects in conditions, this particular idiom is often useful for the example shown above, or using InputStream.read. Basically it's "while the value I read is useful, use it."

How to build a conditional assignment in bash?

As per Jonathan's comment:

variable=$(( 1 == 1 ? 1 : 0 ))  

EDIT:

I revised the original answer which just echo'd the value of the condition operator, it didn't actually show any assignment.



Related Topics



Leave a reply



Submit