Jump to content

Ruby Programming/Syntax/Operators

From Wikibooks, open books for an open world

Operators

[edit | edit source]

Complete List and Precedence

[edit | edit source]
OperatorName/meaningArgumentsPrecedence[1]Association
!Boolean NOTUnary1Right
~Bitwise complementUnary1Right
+Unary plus (no effect)Unary1Right
**ExponentiationBinary1Right
-Unary minus (reverse sign)Unary2Right
*MultiplicationBinary3Left
/DivisionBinary3Left
%Modulo (remainder)Binary3Left
+Addition or concatenationBinary4Left
-SubtractionBinary4Left
<<Bitwise shift left or append (<< and <<- are also used in "here doc" notation)Binary5Left
>>Bitwise shift rightBinary5Left
&Bitwise ANDBinary6Left
|Bitwise ORBinary7Left
^Bitwise XORBinary7Left
<Less-thanBinary8Left
<=Less-than or equal-toBinary8Left
>Greater-thanBinary8Left
>=Greater-than or equal toBinary8Left
==Equal (evaluate to the same value)Binary9N/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.Binary9N/A
!=Not equalBinary9N/A
=~Pattern matchBinary9N/A
!~Negative pattern matchBinary9N/A
<=>A <=> B evaluates to -1, 0, or 1; if A is less-than, equal-to, or greater-than B, respectivelyBinary9N/A
&&Boolean ANDBinary10Left
||Boolean ORBinary11Left
..Range creation, boolean flip-flopBinary12N/A
...Open-ended range creation, boolean flip-flopBinary12N/A
?:A?B:C evaluates to B if A is true, or C if A is falseTrinary13Right
rescueModifier for catching exceptions e.g. array[3] rescue nilBinary14Left
=AssignmentBinary15Right
**=A **=B does A = A ** BBinary15Right
*=A *=B does A = A * BBinary15Right
/=A /=B does A = A / BBinary15Right
%=A %=B does A = A % BBinary15Right
+=A +=B does A = A + BBinary15Right
-=A -=B does A = A – BBinary15Right
<<=A <<=B does A = A << BBinary15Right
>>=A >>=B does A = A >> BBinary15Right
&&=A &&=B assigns B to A if A is true or not nilBinary15Right
&=A &=B does A = A & BBinary15Right
||=A ||=B assigns B to A if A is nil or falseBinary15Right
|=A |= B does A = A | BBinary15Right
^=A ^=B does A = A ^ BBinary15Right
defined?nil if the expression cannot be evaluated (e.g. unset variable)Unary16N/A
notBoolean NOTUnary17Right
andBoolean ANDBinary18Left
orBoolean ORBinary18Left
ifConditional, e.g. print x if xBinary19N/A
unlessNegative conditional, e.g. x = 0 unless xBinary19N/A
whileLoop conditional, e.g. print x += 1 while (x < 10)Binary19N/A
untilLoop conditional, e.g. print x += 1 until (x == 10)Binary19N/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 == 2

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

[edit | edit source]

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

[edit | edit source]

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=321dbconn=Mysql::new('localhost','root','password')

Self assignment

x=1#=>1x+=x#=>2x-=x#=>0x+=4#=>x was 0 so x= + 4 # x is positive 4x*=x#=>16x**=x#=>18446744073709551616 # Raise to the powerx/=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,30var1#=> 10var2#=> 20var3#=> 30myArray=%w(John Michel Fran Doug)# %w() can be used as syntactic sugar to simplify array creationvar1,var2,var3,var4=*myArrayputsvar1#=>Johnputsvar4#=>Dougnames,school=myArray,'St. Whatever'names#=>["John", "Michel", "Fran", "Doug"]school#=>"St. Whatever"

Conditional assignment

x=find_something()#=>nilx||="default"#=>"default" : value of x will be replaced with "default", but only if x is nil or falsex||="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)"unlessrespond_to?:xorx

In same way &&= operator works:

x=get_node()#=>nilx&&=x.next_node#=> nil : x will be set to x.next_node, but only if x is NOT nil or falsex=get_node()#=>Some Nodex&&=x.next_node#=>Next Node

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

x&&x=x.get_node()

Scope

[edit | edit source]

In Ruby there's a local scope, a global scope, an instance scope, and a class scope.

Local Scope

[edit | edit source]

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

[edit | edit source]
 $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

[edit | edit source]

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

[edit | edit source]

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 accessed 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

[edit | edit source]

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

[edit | edit source]

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

[edit | edit source]

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

[edit | edit source]

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

[edit | edit source]
  1. http://ruby-doc.org/core-2.4.0/doc/syntax/precedence_rdoc.html
  2. https://www.ruby-lang.org/en/news/2015/12/25/ruby-2-3-0-released/
  3. http://blog.rubyeffect.com/ruby-2-3s-lonely-operator/
  4. https://bugs.ruby-lang.org/issues/11537
  5. http://www.rubyinside.com/what-rubys-double-pipe-or-equals-really-does-5488.html
close