equal? and eql? operator on Fixnum - ruby

As per definition, equal? checks if the two objects are same, where as eql? checks if the class are same and values are same.
x = 'hi'
y = 'hi'
x.equal? y # => false
x.eql? y # => true
x = 1
y = 1
x.equal? y # => true
x.eql? y # => true
Why is the second x.equal? y true? Aren't x and y two instances of Fixnum? Why doesn't it apply to Fixnum/Float as shown in the examples above?

Because x and y do actually refer to the exact same object. Unlike strings, each integer value has only one instance at any given time.
Reference: http://ruby-doc.org/core-2.2.1/Fixnum.html
There is effectively only one Fixnum object instance for any given integer value [...]
Edit:
To make it a bit more clear, you might want to look at the object_id for these objects:
irb(main):001:0> x = 1
=> 1
irb(main):002:0> y = 1
=> 1
irb(main):003:0> x.object_id
=> 3
irb(main):004:0> y.object_id
=> 3 # Same ID as above
irb(main):005:0> x = 'hi'
=> "hi"
irb(main):006:0> y = 'hi'
=> "hi"
irb(main):007:0> x.object_id
=> 70287051883000
irb(main):008:0> y.object_id
=> 70287051869720 # Different ID than X

I know this question already has been answered but I'll add this about object_id's
my_string << 'something' and my_string.replace 'something' and bang methods like my_string.strip! don't change the object_id that's why you can change a constant's content like MyString = 'test'
But MyString.freeze will prevent you from the constant beeing mutable.
It's good to try things like this to learn the language :
irb(main):024:0> x = '1'
=> "1"
irb(main):025:0> y = x
=> "1"
irb(main):026:0> x.equal? y
=> true
irb(main):027:0> x << 'test'
=> "1test"
irb(main):028:0> y
=> "1test"
irb(main):029:0> nope = nope
=> nil

Related

Ruby map! does not change contained variable

Suppose I have an object x, and an array y=[x] which contains x. If I manipulate x, then y does not change:
x = 1 # => 1
y = [x] # => [1]
x = x+1 # => 2
x # => 2
y # => [1]
and if I change y, then x does not change.
x = 1 # => 1
y = [x] # => [1]
y.map!{|a| a+1} # => [2]
y # => [2]
x # => 1
Is there a way to have them change in parallel? It feels like when I map! over an array the underlying values should change.
first of all
x = x + 1
will create a new variable with an old name x
x = 1
y = [x]
x.object_id
# 3
y[0].object_id
# 3
x = x + 1
x.object_id
# 5
y[0].object_id
# 3
Second, numbers are immutable objects in Ruby (and Strings, for example, are mutable). So you just can't do what you want using number objects in Ruby. What you can do is slightly more obscure. You can create your own mutable object (container for a number) and use a reference to this object in your array.
class MutableNumber
attr_accessor :n
def initialize(n)
#n = n
end
end
x = MutableNumber.new(1)
y = [x]
y[0].n
#=> 1
x.n += 1
y[0].n
#=> 2
You can go little bit further and to add more magic here to mimic numbers
class MutableNumber
attr_accessor :n
def initialize(n)
#n = n
end
def method_missing(m, *args, &blk)
#n = #n.send(m, *args, &blk)
self
end
end
x = MutableNumber.new(1)
y = [x]
y[0].n
#=> 1
x += 1
y[0].n
#=> 2
But I would not encourage you to do any of this.
Just stick to the idea that numbers are immutable. And overall you should be careful with mutability.
map! is mutable for the array, not for its elements. It means that the array gets new values in place (i.e. it's assigned to original array). Elements inside the array are not mutated, but replaced by new elements.
If you want to change old values, you can iterate using each and call mutating methods on each element. You can see this with array of strings:
a = "a"; b = "b"; aa = [a, b]
#=> ["a", "b"]
aa.map!{|e| e.capitalize }
#=> ["A", "B"]
[a, b]
#=> ["a", "b"]
a = "a"; b = "b"; aa = [a, b]
#=> ["a", "b"]
aa.each{|e| e.capitalize! }
#=> ["A", "B"]
[a, b]
#=> ["A", "B"]
Note that it won't work for immutable objects and numbers are immutable, as #fl00r explained in his answer.

Variable changing value, ruby

I am not sure how this variable called origString is changing value in my loop
def scramble_string(string, positions)
i = 0
origString = string
puts origString
newString = string
while i < string.length
newString[i] = origString[positions[i]]
i = i + 1
end
puts origString
return newString
end
for example if I run scramble_string("abcd", [3, 1, 2, 0])
origString changes from "abcd" in the first "puts" to "dbcd" in the second one.
How am I changing the value of origString if I am only declaring it once?
When you say x = y in Ruby that creates a variable with a reference to exactly the same object. Any modifications to x will apply to y and vice-versa:
y = "test"
x = y
x[0] = "b"
x
# => "best"
y
# => "best"
You can tell because of this:
x.object_id == y.object_id
# => true
They're identical objects. What you want is to make a copy first:
x = y.dup
x[0] = "b"
x
# => "best"
y
# => "test"
This results in two independent objects:
x.object_id == y.object_id
# => false
So in your case what you need is to change it like:
orig_string = string.dup
Now that being said, often the best way to process things in Ruby is by using functions that return copies, not manipulating things in place. A better solution is this:
def scramble_string(string, positions)
(0...string.length).map do |p|
string[positions[p]]
end.join
end
scramble_string("abcd", [3, 1, 2, 0])
"dbca"
Note that's a lot more succinct than the version with string manipulation.

Do all? and any? guarantee short-circuit evaluation?

Testing out some code in both pry and irb, I get the following results:
[1] pry(main)> a = [1, 3, 5, 7, 0]
=> [1, 3, 5, 7, 0]
[2] pry(main)> a.any? {|obj| p obj; 3 / obj > 1}
1
=> true
[3] pry(main)> a.all? {|obj| p obj; 3 / obj > 1}
1
3
=> false
In [2] and [3] I see that there appears to be short-circuit evaluation that aborts the iteration as soon as possible, but is this guaranteed behaviour? Reading the documentation there is no mention of this behaviour. I realise that I can use inject instead as that will iterate over everything, but I'm interested in finding out what the official Ruby view is.
Yes.
In the final draft of the Ruby standard, all? is defined as such:
Invoke the method each on the receiver
For each element X which the method each yeilds:
If block is given, call block with X as argument. If this call returns a falseish object, return false.
If block is not given, and X is a falseish object, return false.
Return true.
Note the word return in step 2. This guarantees short circuit evaluation. any? is defined similarly. However the standard is still a draft and I don't know which Ruby implementations (if any) aim to be standards-compliant.
The any? method just realizes the 'or' logic function over the Enumerable. It could be interpreted as statement:
y = x1 v x2 v x3 v ... v xn
And the all? method realizes 'and' logic function over the Enumerable. It also could be interpreted as statement:
y = x1 * x2 * x3 * ... * xn
Since the Array is an Enumerable, it also includes those methods. So, for the method any? the first occurience of true (exactly neither nil nor false) result breaks enumeration with true result. In example the yielded becomes true on number 4, so the methods breaks the execution the returns true:
[1,2,3,4,5].any? {| x | puts x ; x > 3 }
# 1
# 2
# 3
# 4
# => true
Also you can apply DeMorgan's rule to the function any?, and to use all? method:
![1,2,3,4,5].all? {| x | puts x ; x <= 3 }
# 1
# 2
# 3
# 4
# => true
For the method all? the first occurience of either false or nil result do something similar, i.e. returns false. In example the yielded becomes false on number 3, so the methods breaks the execution the returns false:
[1,2,3,4,5].all? {| x | puts x ; x < 3 }
# 1
# 2
# 3
# => false
And with DeMorgan's transformation to use any? method:
![1,2,3,4,5].any? {| x | puts x ; x >= 3 }
# 1
# 2
# 3
# => false
I think there is some ambiguity here.
Consider the following:
RUBY_VERSION
=> "2.3.7"
When yielding to a block:
[1,2,3,4,5].all? {| x | puts x ; x < 3 }
# 1
# 2
# 3
# => false
When not yielding to a block:
def a
puts "in a"
true
end
def b
puts "in b"
false
end
def c
puts "in c"
true
end
[a,b,c].all?
# in a
# in b
# in c
# => false
Seems like condition #2:
If block is not given, and X is a falseish object, return false.
Is not valid.

Ruby References

When I have the following method:
def n_times(thing)
lambda { |n| thing * n }
end
and I call it like that:
x = [:a]
p1 = n_times(x)
x = [:b]
p p1.call(3) # => [:a, :a, :a]
x will not be changed, the output will be [:a]. Why?
When doing something like .pop instead, x will be changed:
x = [:a]
p1 = n_times(x)
x.pop
p p1.call(3) # => []
Is it because [:b] is a new object?
This is because x was assigned a new object in first case. You can check by inspecting the object_id
x = [:a]
p x.object_id # => //some number
p1 = n_times(x)
x = [:b]
p x.object_id # => //diff number
p p1.call(3) # => [:a, :a, :a]
x = [:a]
p x.object_id # => //some number
p1 = n_times(x)
x.pop
p x.object_id # => //same number
p p1.call(3) # => []
# Another example
x = [:a]
p x.object_id # => //some number
p1 = n_times(x)
x[0] = :b
p x.object_id # => //same number
p p1.call(3) # => [:b, :b, :b]
Is it because [:b] is a new object?
Yes. When you do x.pop, you're modifying the object that both x and thing refer to. When you do x = [:b], you make x refer to a new object. Making a variable refer to a new object doesn't affect other variables or objects.
Note that if the lambda closed over the variable x, it would be a different matter. In that case changing x would affect the lambda, but that's not the case. The lambda closes over the variable thing, which is a different variable that only happened to refer to the same object until x was reassigned.

make an object behave like an Array for parallel assignment in ruby

Suppose you do this in Ruby:
ar = [1, 2]
x, y = ar
Then, x == 1 and y == 2. Is there a method I can define in my own classes that will produce the same effect? e.g.
rb = AllYourCode.new
x, y = rb
So far, all I've been able to do with an assignment like this is to make x == rb and y = nil. Python has a feature like this:
>>> class Foo:
... def __iter__(self):
... return iter([1,2])
...
>>> x, y = Foo()
>>> x
1
>>> y
2
Yep. Define #to_ary. This will let your object be treated as an array for assignment.
irb> o = Object.new
=> #<Object:0x3556ec>
irb> def o.to_ary
[1, 2]
end
=> nil
irb> x, y = o
=> [1,2]
irb> x
#=> 1
irb> y
#=> 2
The difference between #to_a and #to_ary is that #to_a is used to try to convert
a given object to an array, while #to_ary is available if we can treat the given object as an array. It's a subtle difference.
Almost:
class AllYourCode
def to_a
[1,2]
end
end
rb = AllYourCode.new
x, y = *rb
p x
p y
Splat will try to invoke to_ary, and then try to invoke to_a. I'm not sure why you want to do this though, this is really a syntactical feature that happens to use Array in its implementation, rather than a feature of Array.
In other words the use cases for multiple assignment are things like:
# swap
x, y = y, x
# multiple return values
quot, rem = a.divmod(b)
# etc.
name, age = "Person", 100
In other words, most of the time the object being assigned from (the Array) isn't even apparent.
You can't redefine assignment, because it's an operator instead of a method. But if your AllYourCode class were to inherit from Array, your example would work.
When Ruby encounters an assignment, it looks at the right hand side and if there is more than one rvalue, it collects them into an array. Then it looks at the left hand side. If there is one lvalue there, it is assigned the array.
def foo
return "a", "b", "c" # three rvalues
end
x = foo # => x == ["a", "b", "c"]
If there is more than one lvalue (more specifically, if it sees a comma), it assigns rvalues successively and discards the extra ones.
x, y, z = foo # => x == "a", y == "b", z == "c"
x, y = foo # => x == "a", y == "b"
x, = foo # => x == "a"
You can do parallel assignment if an array is returned, too, as you have discovered.
def bar
["a", "b", "c"]
end
x = bar # => x == ["a", "b", "c"]
x, y, z = bar # => x == "a", y == "b", z == "c"
x, y = bar # => x == "a", y == "b"
x, = bar # => x == "a"
So in your example, if rb is an Array or inherits from Array, x and y will be assigned its first 2 values.

Resources