mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
483 lines
12 KiB
Text
483 lines
12 KiB
Text
= Assignment
|
|
|
|
In Ruby, assignment uses the <code>=</code> (equals sign) character. This
|
|
example assigns the number five to the local variable +v+:
|
|
|
|
v = 5
|
|
|
|
Assignment creates a local variable if the variable was not previously
|
|
referenced.
|
|
|
|
An assignment expression result is always the assigned value, including
|
|
{assignment methods}[rdoc-ref:syntax/assignment.rdoc@Assignment+Methods].
|
|
|
|
== Local Variable Names
|
|
|
|
A local variable name must start with a lowercase US-ASCII letter or a
|
|
character with the eight bit set. Typically local variables are US-ASCII
|
|
compatible since the keys to type them exist on all keyboards.
|
|
|
|
(Ruby programs must be written in a US-ASCII-compatible character set. In
|
|
such character sets if the eight bit is set it indicates an extended
|
|
character. Ruby allows local variables to contain such characters.)
|
|
|
|
A local variable name may contain letters, numbers, an <code>_</code>
|
|
(underscore or low line) or a character with the eighth bit set.
|
|
|
|
== Local Variable Scope
|
|
|
|
Once a local variable name has been assigned-to all uses of the name for the
|
|
rest of the scope are considered local variables.
|
|
|
|
Here is an example:
|
|
|
|
1.times do
|
|
a = 1
|
|
puts "local variables in the block: #{local_variables.join ", "}"
|
|
end
|
|
|
|
puts "no local variables outside the block" if local_variables.empty?
|
|
|
|
This prints:
|
|
|
|
local variables in the block: a
|
|
no local variables outside the block
|
|
|
|
Since the block creates a new scope, any local variables created inside it do
|
|
not leak to the surrounding scope.
|
|
|
|
Variables defined in an outer scope appear inner scope:
|
|
|
|
a = 0
|
|
|
|
1.times do
|
|
puts "local variables: #{local_variables.join ", "}"
|
|
end
|
|
|
|
This prints:
|
|
|
|
local variables: a
|
|
|
|
You may isolate variables in a block from the outer scope by listing them
|
|
following a <code>;</code> in the block's arguments. See the documentation
|
|
for block local variables in the {calling
|
|
methods}[rdoc-ref:syntax/calling_methods.rdoc] documentation for an example.
|
|
|
|
See also Kernel#local_variables, but note that a +for+ loop does not create a
|
|
new scope like a block does.
|
|
|
|
== Local Variables and Methods
|
|
|
|
In Ruby local variable names and method names are nearly identical. If you
|
|
have not assigned to one of these ambiguous names ruby will assume you wish to
|
|
call a method. Once you have assigned to the name ruby will assume you wish
|
|
to reference a local variable.
|
|
|
|
The local variable is created when the parser encounters the assignment, not
|
|
when the assignment occurs:
|
|
|
|
a = 0 if false # does not assign to a
|
|
|
|
p local_variables # prints [:a]
|
|
|
|
p a # prints nil
|
|
|
|
The similarity between method and local variable names can lead to confusing
|
|
code, for example:
|
|
|
|
def big_calculation
|
|
42 # pretend this takes a long time
|
|
end
|
|
|
|
big_calculation = big_calculation()
|
|
|
|
Now any reference to +big_calculation+ is considered a local variable and will
|
|
be cached. To call the method, use <code>self.big_calculation</code>.
|
|
|
|
You can force a method call by using empty argument parentheses as shown above
|
|
or by using an explicit receiver like <code>self</code>. Using an explicit
|
|
receiver may raise a NameError if the method's visibility is not public or the
|
|
receiver is the literal <code>self</code>.
|
|
|
|
Another commonly confusing case is when using a modifier +if+:
|
|
|
|
p a if a = 0.zero?
|
|
|
|
Rather than printing "true" you receive a NameError, "undefined local variable
|
|
or method `a'". Since ruby parses the bare +a+ left of the +if+ first and has
|
|
not yet seen an assignment to +a+ it assumes you wish to call a method. Ruby
|
|
then sees the assignment to +a+ and will assume you are referencing a local
|
|
method.
|
|
|
|
The confusion comes from the out-of-order execution of the expression. First
|
|
the local variable is assigned-to then you attempt to call a nonexistent
|
|
method.
|
|
|
|
== Local Variables and eval
|
|
|
|
Using +eval+ to evaluate Ruby code will allow access to local variables defined
|
|
in the same scope, even if the local variables are not defined until after the
|
|
call to +eval+. However, local variables defined inside the call to +eval+
|
|
will not be reflected in the surrounding scope. Inside the call to +eval+,
|
|
local variables defined in the surrounding scope and local variables defined
|
|
inside the call to +eval+ will be accessible. However, you will not be able
|
|
to access local variables defined in previous or subsequent calls to +eval+ in
|
|
the same scope. Consider each +eval+ call a separate nested scope. Example:
|
|
|
|
def m
|
|
eval "bar = 1"
|
|
lvs = eval "baz = 2; ary = [local_variables, foo, baz]; x = 2; ary"
|
|
eval "quux = 3"
|
|
foo = 1
|
|
lvs << local_variables
|
|
end
|
|
|
|
m
|
|
# => [[:baz, :ary, :x, :lvs, :foo], nil, 2, [:lvs, :foo]]
|
|
|
|
== Instance Variables
|
|
|
|
Instance variables are shared across all methods for the same object.
|
|
|
|
An instance variable must start with a <code>@</code> ("at" sign or
|
|
commercial at). Otherwise instance variable names follow the rules as local
|
|
variable names. Since the instance variable starts with an <code>@</code> the
|
|
second character may be an upper-case letter.
|
|
|
|
Here is an example of instance variable usage:
|
|
|
|
class C
|
|
def initialize(value)
|
|
@instance_variable = value
|
|
end
|
|
|
|
def value
|
|
@instance_variable
|
|
end
|
|
end
|
|
|
|
object1 = C.new "some value"
|
|
object2 = C.new "other value"
|
|
|
|
p object1.value # prints "some value"
|
|
p object2.value # prints "other value"
|
|
|
|
An uninitialized instance variable has a value of +nil+. If you run Ruby with
|
|
warnings enabled, you will get a warning when accessing an uninitialized
|
|
instance variable.
|
|
|
|
The +value+ method has access to the value set by the +initialize+ method, but
|
|
only for the same object.
|
|
|
|
== Class Variables
|
|
|
|
Class variables are shared between a class, its subclasses and its instances.
|
|
|
|
A class variable must start with a <code>@@</code> (two "at" signs). The rest
|
|
of the name follows the same rules as instance variables.
|
|
|
|
Here is an example:
|
|
|
|
class A
|
|
@@class_variable = 0
|
|
|
|
def value
|
|
@@class_variable
|
|
end
|
|
|
|
def update
|
|
@@class_variable = @@class_variable + 1
|
|
end
|
|
end
|
|
|
|
class B < A
|
|
def update
|
|
@@class_variable = @@class_variable + 2
|
|
end
|
|
end
|
|
|
|
a = A.new
|
|
b = B.new
|
|
|
|
puts "A value: #{a.value}"
|
|
puts "B value: #{b.value}"
|
|
|
|
This prints:
|
|
|
|
A value: 0
|
|
B value: 0
|
|
|
|
Continuing with the same example, we can update using objects from either
|
|
class and the value is shared:
|
|
|
|
puts "update A"
|
|
a.update
|
|
|
|
puts "A value: #{a.value}"
|
|
puts "B value: #{b.value}"
|
|
|
|
puts "update B"
|
|
b.update
|
|
|
|
puts "A value: #{a.value}"
|
|
puts "B value: #{b.value}"
|
|
|
|
puts "update A"
|
|
a.update
|
|
|
|
puts "A value: #{a.value}"
|
|
puts "B value: #{b.value}"
|
|
|
|
This prints:
|
|
|
|
update A
|
|
A value: 1
|
|
B value: 1
|
|
update B
|
|
A value: 3
|
|
B value: 3
|
|
update A
|
|
A value: 4
|
|
B value: 4
|
|
|
|
Accessing an uninitialized class variable will raise a NameError exception.
|
|
|
|
Note that classes have instance variables because classes are objects, so
|
|
try not to confuse class and instance variables.
|
|
|
|
== Global Variables
|
|
|
|
Global variables are accessible everywhere.
|
|
|
|
Global variables start with a <code>$</code> (dollar sign). The rest of the
|
|
name follows the same rules as instance variables.
|
|
|
|
Here is an example:
|
|
|
|
$global = 0
|
|
|
|
class C
|
|
puts "in a class: #{$global}"
|
|
|
|
def my_method
|
|
puts "in a method: #{$global}"
|
|
|
|
$global = $global + 1
|
|
$other_global = 3
|
|
end
|
|
end
|
|
|
|
C.new.my_method
|
|
|
|
puts "at top-level, $global: #{$global}, $other_global: #{$other_global}"
|
|
|
|
This prints:
|
|
|
|
in a class: 0
|
|
in a method: 0
|
|
at top-level, $global: 1, $other_global: 3
|
|
|
|
An uninitialized global variable has a value of +nil+.
|
|
|
|
Ruby has some special globals that behave differently depending on context
|
|
such as the regular expression match variables or that have a side-effect when
|
|
assigned to. See the {global variables documentation}[rdoc-ref:globals.rdoc]
|
|
for details.
|
|
|
|
== Assignment Methods
|
|
|
|
You can define methods that will behave like assignment, for example:
|
|
|
|
class C
|
|
def value=(value)
|
|
@value = value
|
|
end
|
|
end
|
|
|
|
c = C.new
|
|
c.value = 42
|
|
|
|
Using assignment methods allows your programs to look nicer. When assigning
|
|
to an instance variable most people use Module#attr_accessor:
|
|
|
|
class C
|
|
attr_accessor :value
|
|
end
|
|
|
|
When using method assignment you must always have a receiver. If you do not
|
|
have a receiver, Ruby assumes you are assigning to a local variable:
|
|
|
|
class C
|
|
attr_accessor :value
|
|
|
|
def my_method
|
|
value = 42
|
|
|
|
puts "local_variables: #{local_variables.join ", "}"
|
|
puts "@value: #{@value.inspect}"
|
|
end
|
|
end
|
|
|
|
C.new.my_method
|
|
|
|
This prints:
|
|
|
|
local_variables: value
|
|
@value: nil
|
|
|
|
To use the assignment method you must set the receiver:
|
|
|
|
class C
|
|
attr_accessor :value
|
|
|
|
def my_method
|
|
self.value = 42
|
|
|
|
puts "local_variables: #{local_variables.join ", "}"
|
|
puts "@value: #{@value.inspect}"
|
|
end
|
|
end
|
|
|
|
C.new.my_method
|
|
|
|
This prints:
|
|
|
|
local_variables:
|
|
@value: 42
|
|
|
|
Note that the value returned by an assignment method is ignored whatever,
|
|
since an assignment expression result is always the assignment value.
|
|
|
|
== Abbreviated Assignment
|
|
|
|
You can mix several of the operators and assignment. To add 1 to an object
|
|
you can write:
|
|
|
|
a = 1
|
|
|
|
a += 2
|
|
|
|
p a # prints 3
|
|
|
|
This is equivalent to:
|
|
|
|
a = 1
|
|
|
|
a = a + 2
|
|
|
|
p a # prints 3
|
|
|
|
You can use the following operators this way: <code>+</code>, <code>-</code>,
|
|
<code>*</code>, <code>/</code>, <code>%</code>, <code>**</code>,
|
|
<code>&</code>, <code>|</code>, <code>^</code>, <code><<</code>,
|
|
<code>>></code>
|
|
|
|
There are also <code>||=</code> and <code>&&=</code>. The former makes an
|
|
assignment if the value was +nil+ or +false+ while the latter makes an
|
|
assignment if the value was not +nil+ or +false+.
|
|
|
|
Here is an example:
|
|
|
|
a ||= 0
|
|
a &&= 1
|
|
|
|
p a # prints 1
|
|
|
|
Note that these two operators behave more like <code>a || a = 0</code> than
|
|
<code>a = a || 0</code>.
|
|
|
|
== Implicit Array Assignment
|
|
|
|
You can implicitly create an array by listing multiple values when assigning:
|
|
|
|
a = 1, 2, 3
|
|
|
|
p a # prints [1, 2, 3]
|
|
|
|
This implicitly creates an Array.
|
|
|
|
You can use <code>*</code> or the "splat" operator or unpack an Array when
|
|
assigning. This is similar to multiple assignment:
|
|
|
|
a = *[1, 2, 3]
|
|
|
|
p a # prints [1, 2, 3]
|
|
|
|
You can splat anywhere in the right-hand side of the assignment:
|
|
|
|
a = 1, *[2, 3]
|
|
|
|
p a # prints [1, 2, 3]
|
|
|
|
== Multiple Assignment
|
|
|
|
You can assign multiple values on the right-hand side to multiple variables:
|
|
|
|
a, b = 1, 2
|
|
|
|
p a: a, b: b # prints {:a=>1, :b=>2}
|
|
|
|
In the following sections any place "variable" is used an assignment method,
|
|
instance, class or global will also work:
|
|
|
|
def value=(value)
|
|
p assigned: value
|
|
end
|
|
|
|
self.value, $global = 1, 2 # prints {:assigned=>1}
|
|
|
|
p $global # prints 2
|
|
|
|
You can use multiple assignment to swap two values in-place:
|
|
|
|
old_value = 1
|
|
|
|
new_value, old_value = old_value, 2
|
|
|
|
p new_value: new_value, old_value: old_value
|
|
# prints {:new_value=>1, :old_value=>2}
|
|
|
|
If you have more values on the right hand side of the assignment than variables
|
|
on the left hand side, the extra values are ignored:
|
|
|
|
a, b = 1, 2, 3
|
|
|
|
p a: a, b: b # prints {:a=>1, :b=>2}
|
|
|
|
You can use <code>*</code> to gather extra values on the right-hand side of
|
|
the assignment.
|
|
|
|
a, *b = 1, 2, 3
|
|
|
|
p a: a, b: b # prints {:a=>1, :b=>[2, 3]}
|
|
|
|
The <code>*</code> can appear anywhere on the left-hand side:
|
|
|
|
*a, b = 1, 2, 3
|
|
|
|
p a: a, b: b # prints {:a=>[1, 2], :b=>3}
|
|
|
|
But you may only use one <code>*</code> in an assignment.
|
|
|
|
== Array Decomposition
|
|
|
|
Like Array decomposition in {method arguments}[rdoc-ref:syntax/methods.rdoc]
|
|
you can decompose an Array during assignment using parenthesis:
|
|
|
|
(a, b) = [1, 2]
|
|
|
|
p a: a, b: b # prints {:a=>1, :b=>2}
|
|
|
|
You can decompose an Array as part of a larger multiple assignment:
|
|
|
|
a, (b, c) = 1, [2, 3]
|
|
|
|
p a: a, b: b, c: c # prints {:a=>1, :b=>2, :c=>3}
|
|
|
|
Since each decomposition is considered its own multiple assignment you can use
|
|
<code>*</code> to gather arguments in the decomposition:
|
|
|
|
a, (b, *c), *d = 1, [2, 3, 4], 5, 6
|
|
|
|
p a: a, b: b, c: c, d: d
|
|
# prints {:a=>1, :b=>2, :c=>[3, 4], :d=>[5, 6]}
|