Operators
Complete List and Precedence
| Operator | Name/meaning | Arguments | Precedence[1] | Association |
|---|---|---|---|---|
| ! | Boolean NOT | Unary | 1 | Right |
| ~ | Bitwise complement | Unary | 1 | Right |
| + | Unary plus (no effect) | Unary | 1 | Right |
| ** | Exponentiation | Binary | 1 | Right |
| - | Unary minus (reverse sign) | Unary | 2 | Right |
| * | Multiplication | Binary | 3 | Left |
| / | Division | Binary | 3 | Left |
| % | Modulo (remainder) | Binary | 3 | Left |
| + | Addition or concatenation | Binary | 4 | Left |
| - | Subtraction | Binary | 4 | Left |
| << | Bitwise shift left or append (<< and <<- are also used in "here doc" notation) | Binary | 5 | Left |
| >> | Bitwise shift right | Binary | 5 | Left |
| & | Bitwise AND | Binary | 6 | Left |
| | | Bitwise OR | Binary | 7 | Left |
| ^ | Bitwise XOR | Binary | 7 | Left |
| < | Less-than | Binary | 8 | Left |
| <= | Less-than or equal-to | Binary | 8 | Left |
| > | Greater-than | Binary | 8 | Left |
| >= | Greater-than or equal to | Binary | 8 | Left |
| == | Equal (evaluate to the same value) | Binary | 9 | N/A |
| === | "Case equality", "case subsumption" or "three equals" operator. A === B if B is a member of the set of A. Operation varies considerably depending on the data types of A and B. | Binary | 9 | N/A |
| != | Not equal | Binary | 9 | N/A |
| =~ | Pattern match | Binary | 9 | N/A |
| !~ | Negative pattern match | Binary | 9 | N/A |
| <=> | A <=> B evaluates to -1, 0, or 1; if A is less-than, equal-to, or greater-than B, respectively | Binary | 9 | N/A |
| && | Boolean AND | Binary | 10 | Left |
| || | Boolean OR | Binary | 11 | Left |
| .. | Range creation, boolean flip-flop | Binary | 12 | N/A |
| ... | Open-ended range creation, boolean flip-flop | Binary | 12 | N/A |
| ?: | A?B:C evaluates to B if A is true, or C if A is false | Trinary | 13 | Right |
| rescue | Modifier for catching exceptions e.g. array[3] rescue nil | Binary | 14 | Left |
| = | Assignment | Binary | 15 | Right |
| **= | A **=B does A = A ** B | Binary | 15 | Right |
| *= | A *=B does A = A * B | Binary | 15 | Right |
| /= | A /=B does A = A / B | Binary | 15 | Right |
| %= | A %=B does A = A % B | Binary | 15 | Right |
| += | A +=B does A = A + B | Binary | 15 | Right |
| -= | A -=B does A = A - B | Binary | 15 | Right |
| <<= | A <<=B does A = A << B | Binary | 15 | Right |
| >>= | A >>=B does A = A >> B | Binary | 15 | Right |
| &&= | A &&=B assigns B to A if A is true or not nil | Binary | 15 | Right |
| &= | A &=B does A = A & B | Binary | 15 | Right |
| ||= | A ||=B assigns B to A iff A is nil or false | Binary | 15 | Right |
| |= | A |= B does A = A | B | Binary | 15 | Right |
| ^= | A ^=B does A = A ^ B | Binary | 15 | Right |
| defined? | nil if the expression cannot be evaluated (e.g. unset variable) | Unary | 16 | N/A |
| not | Boolean NOT | Unary | 17 | Right |
| and | Boolean AND | Binary | 18 | Left |
| or | Boolean OR | Binary | 18 | Left |
| if | Conditional, e.g. print x if x | Binary | 19 | N/A |
| unless | Negative conditional, e.g. x = 0 unless x | Binary | 19 | N/A |
| while | Loop conditional, e.g. print x += 1 while (x < 10) | Binary | 19 | N/A |
| until | Loop conditional, e.g. print x += 1 until (x == 10) | Binary | 19 | N/A |
Higher precedence (lower number in the above table) operators have their immediate arguments evaluated first. Precedence order can be altered with () blocks. For example, because * has higher precedence than +, then:
1 + 2 * 3 == 7
(1 + 2) * 3 == 9
Association direction controls which operators have their arguments evaluated first when multiple operators with the same precedence appear in a row. For example, because - has left association:
1 - 2 - 3 == (1 - 2) - 3 == -1 - 3 == -4
instead of:
1 - 2 - 3 == 1 - (2 - 3) == 1 - -1 == 0
Because ** has right association:
2 ** 3 ** 2 == 2 ** (3 ** 2) == 2 ** 9 == 512
instead of:
2 ** 3 ** 2 == (2 ** 3) ** 2 == 8 ** 2 == 64
{} blocks have lower precedence than the above operators, followed by do/end blocks. Array accesses with [] can be thought of as having a higher precedence than any above operator.
The operators ** through !~ can be overridden (defined for new classes, or redefined for existing operations).
Note that rescue, if, unless, while, and until are operators when used as modifiers in one-liners (as in the above examples) but can also be used as keywords.
Other operators
The dot operator . is used for calling methods on objects, also known as passing a message to the object.
Ruby 2.3.0 introduced the safe navigation operator &., also known as the "lonely operator".[2] This allows replacing
x = foo && foo.bar && foo.bar.baz
with
x = foo&.bar&.baz
An equivalent .dig() method was introduced for hashes and arrays:
hash_variable.dig(:foo, :bar, :baz)
array_variable.dig(1, 0, 2)
are safer versions of:
hash_variable[:foo][:bar][:baz]
array_variable[1][0][2]
The safe navigation operator will raise an error if a requested method, key, or index is not available; unlike the technique of using try() for this purpose, which will return nil.[3]
Yukihiro Matsumoto considered ! ?. and .? before settling on &. because:[4]
- ?. conflicts with *?
- ?. is used by other languages, thus .? is confusingly similar but different
- ! conflicts with "not" logic
- ? is already used by convention for functions that return booleans
- &. is reminiscent of the && syntax the operator is replacing
!! is sometimes seen, but this is simply the ! operator twice. It is used to force the following expression to evaluate to a boolean. This technique is considered non-idiomatic and poor programming practice, because there are more explicit ways to force such a conversion (which is rarely needed to begin with).
Assignment
Assignment in Ruby is done using the equal operator "=". This is both for variables and objects, but since strings, floats, and integers are actually objects in Ruby, you're always assigning objects.
Examples:
myvar = 'myvar is now this string'
var = 321
dbconn = Mysql::new('localhost','root','password')
Self assignment
x = 1 #=>1
x += x #=>2
x -= x #=>0
x += 4 #=>x was 0 so x= + 4 # x is positive 4
x *= x #=>16
x **= x #=>18446744073709551616 # Raise to the power
x /= x #=>1
A frequent question from C and C++ types is "How do you increment a variable? Where are ++ and -- operators?" In Ruby, one should use x+=1 and x-=1 to increment or decrement a variable.
x = 'a'
x.succ! #=>"b" : succ! method is defined for String, but not for Integer types
Multiple assignments
Examples:
var1, var2, var3 = 10, 20, 30
var1 #=> 10
var2 #=> 20
var3 #=> 30
myArray=%w(John Michel Fran Doug) # %w() can be used as syntactic sugar to simplify array creation
var1,var2,var3,var4=*myArray
puts var1 #=>John
puts var4 #=>Doug
names,school=myArray,'St. Whatever'
names #=>["John", "Michel", "Fran", "Doug"]
school #=>"St. Whatever"
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 that closely resembles the expression:[5]
x = x || "default"
Operator ||= can be shorthand for code like:
x = "(some fallback value)" unless respond_to? :x or x
In same way &&= operator works:
x = get_node() #=>nil
x &&= x.next_node #=> nil : x will be set to x.next_node, but only if x is NOT nil or false
x = get_node() #=>Some Node
x &&= x.next_node #=>Next Node
Operator &&= is a shorthand form of the expression:
x && x = x.get_node()
Scope
In Ruby there's a local scope, a global scope, an instance scope, and a class scope.
Local Scope
Example:
var = 2
4.times do |x|
puts x = x*var
end
#=>
0
2
4
6
puts x
#=> undefined local variable or method `x' for main:Object (NameError)
This error appears because this x(toplevel) is not the x(local) inside the do..end block the x(local) is a local variable to the block, whereas when trying the puts x(toplevel) we're calling a x variable that is in the top level scope, and since there's not one, Ruby protests.
Global scope
$global = 0
4.times do |var|
$global = $global + var
puts "var #{var} global #{$global}"
end
#=>
var 0 global 0
var 1 global 1
var 2 global 3
var 3 global 6
puts $global
#=> 6
This output is given because prefixing a variable with a dollar sign makes the variable a global.
Instance scope
Within methods of a class, you can share variables by prefixing them with an @.
class A
def setup
@instvar = 1
end
def go
@instvar = @instvar*2
puts @instvar
end
end
instance = A.new
instance.setup
instance.go
#=> 2
instance.go
#=> 4
Class scope
A class variable is one that is like a "static" variable in Java. It is shared by all instances of a class.
class A
@@classvar = 1
def go
@@classvar = @@classvar*2
puts @@classvar
end
end
instance = A.new
instance.go
#=> 2
instance = A.new
instance.go
#=> 4 -- variable is shared across instances
Here's a demo showing the various types:
$variable
class Test
def initialize(arg1='kiwi')
@instvar=arg1
@@classvar=@instvar+' told you so!!'
localvar=@instvar
end
def print_instvar
puts @instvar
end
def print_localvar
puts @@classvar
puts localvar
end
end
var=Test.new
var.print_instvar #=>"kiwi", it works because a @instance_var can be accesed inside the class
var.print_localvar #=>undefined local variable or method 'localvar' for #<Test:0x2b36208 @instvar="kiwi"> (NameError).
This will print the two lines "kiwi" and "kiwi told you so!!", then fail with a undefined local variable or method 'localvar' for #<Test:0x2b36208 @instvar="kiwi"> (NameError). Why? Well, in the scope of the method print_localvar there doesn't exists localvar, it exists in method initialize(until GC kicks it out). On the other hand, class variables '@@classvar' and '@instvar' are in scope across the entire class and, in the case of @@class variables, across the children classes.
class SubTest < Test
def print_classvar
puts @@classvar
end
end
newvar=SubTest.new #newvar is created and it has @@classvar with the same value as the var instance of Test!!
newvar.print_classvar #=>kiwi told you so!!
Class variables have the scope of parent class AND children, these variables can live across classes, and can be affected by the children actions ;-)
class SubSubTest < Test
def print_classvar
puts @@classvar
end
def modify_classvar
@@classvar='kiwi kiwi waaai!!'
end
end
subtest=SubSubTest.new
subtest.modify_classvar #lets add a method that modifies the contents of @@classvar in SubSubTest
subtest.print_classvar
This new child of Test also has @@classvar with the original value newvar.print_classvar. The value of @@classvar has been changed to 'kiwi kiwi waaai!!' This shows that @@classvar is "shared" across parent and child classes.
Default scope
When you don't enclose your code in any scope specifier, ex:
@a = 33
it affects the default scope, which is an object called "main".
For example, if you had one script that says
@a = 33 require 'other_script.rb'
and other_script.rb says
puts @a #=> 33
They could share variables.
Note however, that the two scripts don't share local variables.
Local scope gotchas
Typically when you are within a class, you can do as you'd like for definitions, like.
class A
a = 3
if a == 3
def go
3
end
else
def go
4
end
end
end
And also, procs "bind" to their surrounding scope, like
a = 3
b = proc { a }
b.call # 3 -- it remembered what a was
However, the "class" and "def" keywords cause an *entirely new* scope.
class A
a = 3
def go
return a # this won't work!
end
end
You can get around this limitation by using define_method, which takes a block and thus keeps the outer scope (note that you can use any block you want, to, too, but here's an example).
class A
a = 3
define_method(:go) {
a
}
end
Here's using an arbitrary block
a = 3
PROC = proc { a } # gotta use something besides a local
# variable because that "class" makes us lose scope.
class A
define_method(:go, &PROC)
end
or here
class A
end
a = 3
A.class_eval do
define_method(:go) do
puts a
end
end
Logical And
The binary "and" operator will return the logical conjunction of its two operands. It is the same as "&&" but with a lower precedence. Example:
a = 1 b = 2 c = nil puts "yay all my arguments are true" if a and b puts "oh no, one of my argument is false" if a and c
Logical Or
The binary "or" operator will return the logical disjunction of its two operands. It is the same as "||" but with a lower precedence. Example:
a = nil b = "foo" c = a || b # c is set to "foo" it's the same as saying c = (a || b) c = a or b # c is set to nil it's the same as saying (c = a) || b which is not what you want.
References
- ↑ http://ruby-doc.org/core-2.4.0/doc/syntax/precedence_rdoc.html
- ↑ https://www.ruby-lang.org/en/news/2015/12/25/ruby-2-3-0-released/
- ↑ http://blog.rubyeffect.com/ruby-2-3s-lonely-operator/
- ↑ https://bugs.ruby-lang.org/issues/11537
- ↑ http://www.rubyinside.com/what-rubys-double-pipe-or-equals-really-does-5488.html