Integer variables pass by value, string variables by reference? - ruby

Modifications made to b below are also made to a:
a = 'taco'
b = a
b << 's'
a #=> tacos
When I assign a variable x by pointing it at another variable y, x remains a shortcut to y; x and y will be identical. This seems confirmed by the dup method; by saying b = a.dup, b gets a's value at the moment of assignment rather than a shortcut.
But this doesn't happen with integers. When I assign b, it seems to be getting the value of a:
a = 4815
b = a
b /= 2
a #=> 4815
It seems like string variables pass by reference while integer variables pass by value. That doesn't seem right; I know something is wrong in my understanding, but I'm not sure what.

You need to understand the difference between variables and values. A variable points to a value. Multiple variables can point to the same value.
In Ruby, the only way to modify a variable is via assignment, either simple
foo = :something
or compound assignment
foo ω= :something # for some operator ω
# e.g.
foo += :something
foo <<= :something
foo ||= :something
If you modify a value, that modification will be visible no matter what variable you use to access that value.
Think about it this way: my mum calls me "son", my friends call me "Jörg", my close friends call me "jwm", my band colleagues call me "Jörgislaw", my girlfriend calls me "baby", but no matter what they call me, if I cut my hair, my hair will be gone, regardless of what name they use to refer to me. If, however, my girlfriend assigns a new value to the label "baby", then that does not affect me. (Well … bear with me, it's an analogy :-D )
In Ruby, << typically modifies the receiver, whereas / doesn't.
However, Ruby is always pass-by-value. But the value being passed is a pointer to a value, so that multiple variables can contain multiple copies of the same pointer to the same value.

Irrespective of what you are arguing, b /= 2 is a syntax sugar for b = b / 2. Thus, the new b is b / 2 (or a / 2), which has nothing to do with a.

That's correct, see Fixnum:
Fixnum objects have immediate value. This means that when they are assigned or passed as parameters, the actual object is passed, rather than a reference to that object.
Assignment does not alias Fixnum objects. There is effectively only one Fixnum object instance for any given integer value, so, for example, you cannot add a singleton method to a Fixnum. Any attempt to add a singleton method to a Fixnum object will raise a TypeError.

Related

What is the difference between a constant and a variable in Ruby?

So, I'm doing a Ruby course on CodeAcademy and I'm stuck in differentiating the difference between a variable and a class. Can someone please explain the difference to me? I'll give you cookies! ^^. No matter where I look online I can't find any information on this.
The idea of constants in Ruby is that they can get a value assigned only once while you can assign a new value to a variable as many times as you want. Now technically, you can assign a new value even to a constant. Ruby will however issue a warning in this case and you should try to avoid this case.
I guess the main point leading to confusion of people new to Ruby is that even values assigned to constants can be modified without a warning (e.g. by adding new elements to an array). References by a constant are no different to variables here in that the reference does not restrict what can be done with the value. The object referenced by either a variable or constant is always independent from that.
In this example, I assign a new array to the ARRAY constant. Later, I can happily change the array by adding a new member to it. The constant is not concerned by this.
ARRAY = []
# => []
ARRAY << :foo
ARRAY
# => [:foo]
The only thing forbidden (or, well, allowed with a warning) is if you try to assign a completely new value to a constant:
ARRAY2 = []
# => []
ARRAY2 = [:bar]
# warning: already initialized constant ARRAY2
ARRAY2
=> [:bar]
As such, it is common practice to immediately freeze values assigned to constants to fully deny any further changes and ensure that the original value is preserved (unless someone assigns a new value):
ARRAY3 = [:foo, :bar].freeze
ARRAY3 << :baz
# RuntimeError: can't modify frozen Array
A variable can change its value, it can vary.
A constant cannot change its value, it is constant.
In Ruby things are a bit more complex though. You can reassign the value of constants, but it will print a warning. This is meant to be used for debugging only and the general principle still applies that constants are meant to be used for values that never change.
In Ruby, a constant is an identifier that starts with a capital letter; it is intended to be assigned only once. You can reassign a constant, but you should not. Doing so will generate a warning:
NAME = "Fred"
NAME = "Barney" # => Warning: Already initialized constant NAME
A variable is an identifier that does not start with a capital letter; it may be assigned to more than once:
name = "Fred"
name = "Barney" # => No warning
When you create a class, a constant is created with the same name as the class; that constant is bound to the class:
class Foo
end
This is equivalent to this code which creates a new anonymous class and assigns it to the constant Foo:
Foo = Class.new do
end
You can reassign the constant identifier Foo, as you can with any other constant, but of course you shouldn't, and you will still get the warning:
Foo = 123 # => Already initialized constant Foo

Ruby "CONSTANTS" seem to be INVISIBLY ALTERABLE?

I understand that "constants" in Ruby are by convention called constants but are in fact mutable. However I was under the impression that when they were "mutated" that there was a warning:
class Z2
M = [0,1]
end
Z2::M # => [0, 1]
Z2::M = [0,3]
(irb):warning: already initialized constant Z2::M
(irb):warning: previous definition of M was here
However I found this is not the case all the time:
a = Z2::M
a[1] = 2
Z2::M # => [0,2] and no warning
Is this a gap in the "warning" system? I am inferring that assignment of a constant would duplicate it, but I guess that is not true either as it appears that constants and variables point to the same object? Does this mean that all so-called "constants" need to be frozen in order to prevent them from being changed without warning?
TL;DR
Short of monkey-patching Kernel#warn (see https://stackoverflow.com/a/662436/1301972) to raise an exception, you won't be able to prevent reassignment to the constant itself. This is generally not a pragmatic concern in idiomatic Ruby code where one expects to be able to do things like reopen classes, even though class names are also constants.
A Ruby constant isn't actually immutable, and you can't freeze a variable. However, you can get an exception to be raised when something attempts to modify the contents of a frozen object referenced by the constant.
Freezing Objects Deeply with Plain Ruby
Freezing an Array is easy:
CONSTANT_ONE = %w[one two three].freeze
but the strings stored in this Array are really references to String objects. So, while you can't modify this Array, you can still (for example) modify the String object referenced by index 0. To solve this problem, you need to freeze not just the Array, but the objects it holds, too. For example:
CONSTANT = %w[one two three].map(&:freeze).freeze
CONSTANT[2] << 'four'
# RuntimeError: can't modify frozen String
CONSTANT << 'five'
# RuntimeError: can't modify frozen Array
Freezing Objects Recursively with a Gem
Since freezing recursive references can be a bit unwieldy, it's good to know there's a gem for that. You can use ice_nine to deep-freeze most objects:
require 'ice_nine'
require 'ice_nine/core_ext/object'
OTHER_CONST = %w[a b c]
OTHER_CONST.deep_freeze
OTHER_CONST << 'd'
# RuntimeError: can't modify frozen Array
OTHER_CONST[2] = 'z'
# RuntimeError: can't modify frozen Array
A Better Way to Use Ruby Constants
Another option to consider is calling Object#dup when assigning the value of a constant to another variable, such as instance variables in your class initializers, in order to ensure you don't mutate your constant's references by accident. For example:
class Foo
CONSTANT = 'foo'
attr_accessor :variable
def initialize
#variable = CONSTANT.dup
end
end
foo = Foo.new
foo.variable << 'bar'
#=> "foobar"
Foo::CONSTANT
#=> "foo"
There is no gap, as you are not altering a constant. And the fact is that Ruby constants are just variables with extra warnings.
Constant, just as every variable, is merely a pointer to the object in memory. When you doM = [0,3] you are creating a new array and re-pointing constant to this new object, which triggers a warning.
However, when you run M[0] = 1 you are just modifying referenced object, but you do not change the constant, as it still points to the same object.
Important thing to realize here is that all classes in Ruby are just objects in memory, referenced with constants, so when you do:
class Z2
end
it is equivalent to (if Z2 is not defined or is not pointing onto a class object already):
Z2 = Class.new
Naturally class is a very dynamic object, as we keep adding methods to it and so on - we definitively don't want this to trigger any warnings.
If you do Z2::M[1] = 2 you won´t get the message either. I believe the lack of warning occours because you are changing the Array itself and not the reference Z2::M.
If M was an integer, for exemple:
class Z2
M = 1
end
a = Z2::M
a = 2
a # => 2
Z2::M # => 1
To modify an Array from a constant without modify the original you can do:
class Z2
M = [0,1]
end
a = Z2::M.dup
a[0] = 1
a # => [1,1]
Z2::M # => [0,1]

pass reference of primitive data types to functions in ruby

By default ruby passes copy of primitive values and references for object types. How to pass references of primitive type variables (ex: integers, floating points) into a function?
Ruby doesn't pass arguments by reference:
def change(x)
x = 2 # this assigns to a local variable 'x'
end
a = 1
change(a)
a #=> 1
You could pass a mutable object instead, e.g. a hash "containing" an integer:
def change(h)
h[:x] = 2
end
h = {x: 1}
change(h)
h[:x] #=> 2
Ruby does not work that way. There are no pointers, if that's what you mean and it . Arguments are passed by value, but these values are themselves references to objects in memory.
What you call "primitives" (eg. the value 1) are in fact immutable objects in Ruby so it would not make sense to have pointers to them. Passing a variable containing that object is the way to go.
I'm curious about what you want to achieve though.

ruby, define []= operator, why can't control return value?

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.

Ruby hash value converts to string, don't know why

I have a 'strange' problem, the following code converts the location lat value into a string (With a + sign for each iteration) leading to an eventual exception when comparing values. I've tried the code with values for another location and it works fine. The only difference is that the other numbers were negatives.
location= {:lng => 2.0781,:lat => 41.2899}
while location[:lat] < top
sleep(1)
checkTweets(location)
bottom+=0.075
location[:lat] = bottom
end
The issue occurs before entering the check tweets location. The values for the hash are as follows
To to conclude, my question is can anyone explain to me why location[:lat] ends up being a string in this circumstance?
Bottom is initialized as 30.0400 which is assigned to the :lat value. The checkTweets method simply writes a file based on a mongodb query.
Right I found the solution to this. It was the twitter library which was turning the Hash float values into strings.
Am I wrong in assuming that the scope of the variable in the checkTweets method should not impact the location variable here, they are both declared in seperate methods, they are not class level.
I wrong in assuming that the scope of the variable in the checkTweets method should not impact the location variable here, they are both declared in seperate methods, they are not class level.
No, but variable scope is not the issue here. The location variable is local to your method and as such cannot be changed by the checkTweets method. That is correct.
However the object that is referenced by the location variable can be changed from the checkTweets method and that is exactly what happens (though I have to say that mutating arguments is very bad style).
A little example to illustrate reference semantics and mutation in ruby:
def f1(arr)
arr = [1,2,3] # Changes the variable arr, which is local to f1
# This change is not visible on the outside
end
def f2(arr)
arr.concat [1,2,3] # Changes the object that arr refers to
# This change will be visible any place where the same
# array is referenced
end
foo = [42,23]
f1(foo)
# foo is still [42, 23]
f2(foo)
# foo is now [42, 23, 1, 2, 3]
Here the variable foo hasn't been changed to refer to another object (that would not be possible from inside a method), but the object that foo refers to has been changed. The same happens in your checkTweets method.

Resources