Ruby map gives weird results - ruby

Consider the following code
a="123456789"
t=[[1,4],[3,4],[4,5],[1,2]]
p t.map{|x,y|
a[x],a[y]=a[y],a[x]
#p a
a
}
I know ruby map method collects the last expression of the given block but when using the above code to swap the chars in a using the indexes in t won't succeeds.My intention was to collect the state of a after each swap in the index of t.But map always gives the array of a which is in the last state ie)["135264789", "135264789", "135264789", "135264789"].
The results shows that the map method have collected the final result of a after completing each indexes in t.But when printing the a after each swap prints correct value of a at each state.
Is this the correct behavior or am i missing something?

This is because the String#[]= method mutates the string.
Quick fix would be something like this:
a="123456789"
t=[[1,4],[3,4],[4,5],[1,2]]
p t.map{|x,y]
b = "#{a}" # IMPORTANT - this builds a new string
b[x],b[y]=b[y],b[x] # this mutates the new string
#p b
b
}
An alternative to "#{a}" would be to say a.clone, it does the same thing in this case.
The reason this works, is because instead of directly modifying a with a[x],a[y]=a[y],a[x], you're making a temporary copy of a and modifying that instead
edit - I misread the question - if you want to show the result of chaining each operation on the previous result, use dup/clone after the modification as Stefan said in his answer

Is my understanding correct?
Yes, I believe it is. I second what Max says, and I'll also elaborate a bit in case it helps.
Each b is a newly created object because it gets created inside the block, so it gets recreated with every new iteration. The a is created outside the block, so the same object (a) keeps getting referenced inside the block for each iteration.
You can better understand how this works by experimenting with #object_id. Try running this code:
a="123456789"
t=[[1,4],[3,4],[4,5],[1,2]]
p t.map { |x,y|
b = "#{a}" # IMPORTANT - this builds a new string
b[x],b[y]=b[y],b[x]
p "a.object_id = #{a.object_id}"
p "b.object_id = #{b.object_id}"
b
}
You will notice that a is the same object for each iteration of the #map method, while b is a new one.
This is an example of the concept of a closure. A closure is some sort of enclosed code structure that retains access to whatever state is available in the context in which it was created, while that context doesn't have access to its, the enclosed code's, state. Sort of like a "one way mirror": the enclosed code can see outside, but the outside can't see into the enclosed code.
In Ruby, closures are implemented as blocks: blocks are closures. So, everything that is visible to whatever context a block is created in (in this case, main) is also visible to that block, although the reverse is not true — for example, you can't reference b from outside the block. (Methods are not closures: if your block were a method, it wouldn't be able to see a unless you passed it in as an argument to your method.)
So, as Max says, when you make changes to a inside your block, you are actually changing (mutating) the same a that you defined up top each time.
Side topic
Now, if you are referencing individual characters in strings it's important to understand that the underlying structure of strings differs from that of arrays. Also, arrays behave differently when you mutate their elements from strings when you mutate their characters.
I'm mentioning this because I have this vague feeling that you are thinking of string character references as pretty much analogous to array element references. This is pretty much only true with respect to syntax.
You may find the results of running this code interesting:
a = '123456789'
p a.object_id
p a[0].object_id
p a[1].object_id
a[0] = '7'
p a.object_id
p a[0].object_id
p a[1].object_id
puts
a = '123456789'.chars
p a.object_id
p a[0].object_id
p a[1].object_id
a[0] = '7'
p a.object_id
p a[0].object_id
p a[1].object_id
In particular, a comparison of the four outputs of a[1].object_id should be instructive, because it shows where strings and arrays differ. If you reassign an element in an array, that element and only that element gets a new object id. If you reassign a character in a string, the string object itself remains the same, but every character in the string gets recreated.

Since you are returning a from map, the result will contain a four times. Those a's all refer to the same object.
You probably want to return a copy of a to preserve its current state:
a = '123456789'
t = [[1, 4], [3, 4], [4, 5], [1, 2]]
r = t.map { |x, y|
a[x], a[y] = a[y], a[x]
a.dup
}
r #=> ["153426789", "153246789", "153264789", "135264789"]

Related

Why can't I reassign a variable in a Ruby code block?

Why doesn't calling these two .map methods bring about equivalent results? The first one works as expected, whereas the second has no effect.
array = ["batman","boobytrap"]
puts array.map { |x| x.reverse! }
=> namtab
=> partyboob
puts array.map { |x| x = x.reverse }
=> batman
=> boobytrap
Print array after puts array.map { |x| x.reverse! }. You will see - array has changed.
Read documentation for reverse! method.
The problem is that in your first map, the ! has modified the values in the original array, so it now contains reversed strings.
irb:001:0> array = ["batman","boobytrap"]
=> ["batman", "boobytrap"]
irb:002:0> puts array.map { |x| x.reverse! }
namtab
partyboob
=> nil
irb:003:0> array
=> ["namtab", "partyboob"]
So the second time is doing what you expect it to, but the entry data is not what you think it is.
If you try the second case standalone without doing the first one you will see that it works as you expect it to.
You have to change your view of what a variable is here. The variable is not the actual value, but only a reference to that value.
array = ["batman"]
# We are freezing this string to prevent ruby from creating a
# new object for a string with the same value.
# This isnt really necessary, just to "make sure" the memory
# address stays the same.
string = "boobytrap".freeze
array.each do |val|
puts "before: ",
"val.object_id: #{val.object_id}",
"string.object_id: #{string.object_id}",
"array[0].object_id: #{array[0].object_id}",
""
val = string
puts "after: ",
"val.object_id: #{val.object_id}",
"string.object_id: #{string.object_id}",
"array[0].object_id: #{array[0].object_id}"
end
# before:
# val.object_id: 70244773377800,
# string.object_id: 70244761504360,
# array[0].object_id: 70244773377800
#
# after:
# val.object_id: 70244761504360,
# string.object_id: 70244761504360,
# array[0].object_id: 70244773377800
Obviously the values will differ if you run this code on your machine, but the point is, the memory address for val changes, while array[0] (which is where val comes from) stays the same after we assigned string to val. So basically what we do with the reassignment is, we tell ruby the value for val is no longer found in 70244773377800, but in 70244761504360. The array still refers to its first value with 70244773377800 though!
The #reverse! method call you use in your example on x on the other hand changes the value of whatever is found at 70244773377800 in memory, which is why it works as you expected.
TLDR;
Your first example changes the value in memory, while the second example assigns a new memory address to a local variable.
When you do array.map { |x| x.reverse! }, it changed the array values.
array = ["batman","boobytrap"]
puts array.map { |x| x.reverse! }
=> namtab
=> partyboob
array
=> ["namtab", "partyboob"]
If you perform second operation on the same array, it will produce the same results as you have stated in the question. However, it will not change the value of original array.
array = ["batman","boobytrap"]
puts array.map { |x| x.reverse! }
=> namtab
=> partyboob
array
=> ["namtab", "partyboob"]
puts array.map { |x| x = x.reverse }
=> batman
=> boobytrap
array
=> ["namtab", "partyboob"]
To change the value of original array, use map! in second operation.
array = ["batman","boobytrap"]
puts array.map { |x| x.reverse! }
=> namtab
=> partyboob
array
=> ["namtab", "partyboob"]
puts array.map! { |x| x.reverse }
=> batman
=> boobytrap
array
=> ["batman", "boobytrap"]
There are several problems with your approach.
The first problem is that you don't properly isolate your test cases: in your first test case you reverse the strings in the array. In your second test case you reverse them again. What happens if you reverse something twice? That's right: nothing! So, the reason why you think it isn't working is actually precisely the fact that it is working! And if it weren't working (i.e. not reversing the strings), then it would print the previously-reversed strings, and you would think it does work.
So, lesson #1: Always isolate your test cases!
Problem #2 is that the second piece of code doesn't do what you (probably) think it does. x is a local variable (because it starts with a lowercase letter). Local variables are local to the scope they are defined in, in this case the block. So, x only exists inside the block. You assign to it, but you never do anything with it after assigning to it. So, the assignment is actually irrelevant.
What is relevant, is the return value of the block. Now, in Ruby, assignments evaluate to the value that is being assigned, so considering that the assignment to x is superfluous, we can just get rid of it, and your code is actually exactly equivalent to this:
array.map { |x| x.reverse }
Your third problem is that the first piece also doesn't do what you (probably) think it does. Array#map returns a new array and leaves the original array untouched, but String#reverse! mutates the strings! In other words: its primary mode of operation is a side-effect. In addition to the side-effect, it also returns the reversed string, which is another thing that confused you. It could just as well return nil instead to indicate that it performs a side-effect, in which case you would instead see the following:
array = %w[batman boobytrap]
array.map(&:reverse!)
#=> [nil, nil]
array
#=> ['namtab', 'partyboob']
As you can see, if String#reverse! did return nil, what you would observe is the following:
array.map returns a new array whose elements are the return values of the block, which is just nil
array now still contains the same String objects as before, but they have been mutated
Now, since String#reverse! actually does return the reversed String what you actually observe is this:
array = %w[batman boobytrap]
array.map(&:reverse!)
#=> ['namtab', 'partyboob']
array
#=> ['namtab', 'partyboob']
array.map(&:reverse)
#=> ['batman', 'boobytrap']
array
#=> ['namtab', 'partyboob']
Which brings me to lesson #2: side-effects and shared mutable state are evil!
You should avoid both side-effects and shared mutable state (ideally, mutable state in general, but mutable state that is shared between different pieces is especially evil) as far as possible.
Why are they evil? Well, just look at how much they confused even in this extremely simple tiny example? Can you imagine the same thing happening in a much, much larger application?
The problem with side-effects is that they "happen on the side". They are not arguments, they are not return values. You cannot print them out, inspect them, store them in a variable, place assertions on them in a unit test, and so on.
The problem with shared mutable state (mutation is just a form of side-effect, by the way) is that enables "spooky action at a distance": you have a piece of data in one place of your code, but this piece of data is shared with a different place of your code. Now, if one place mutates the data, the other place will seemingly magically have their data change out under them. In your example here, the shared state were the strings inside the array, and mutating them in one line of code made them also change in another line of code.

Ruby array changes by changing a 'copy' of one of its elements

I'm trying to confirm whether my understanding is correct of these six lines of code:
string="this is a sentence"
words=string.split
first_word=words[0]
first_word[0]=first_word[0].upcase
out=words.join(" ")
puts(out)
which prints "This is a sentence" (with the first letter capitalized).
It would appear that changing the "first_word" string, which is defined as the first element of the "words" array, also changes the original "words" array. Is this indeed Ruby's default behavior? Does it not make it more difficult to track where in the code changes to the array take place?
You just need need to distinguish between a variable and an object. Your string is an object. first_word is a variable.
Look for example
a = "hello"
b = a
c = b
now all variables contain the same object, a string with the value "hello". We say they reference the object. No copy is made.
a[0] = 'H'
This changes the first character of the object, a string which now has the value "Hello". Both b and c contain the same, now changed object.
a = "different"
This assigns a new object to the variable a. b and c still hold the original object.
Is this Rubys default behaviour? yes. And it also works like this in many other programming languages.
Does it make it difficult to track changes? Sometimes.
If you takes an element from an array (like your first_word), you need to know:
If you change the object itself, no matter how you access it,
all variables will still hold your object, which just happened to be changed.
But if you replace the object in the array, like words[0] = "That", then all your other variables will still hold the original object.
This behavior is caused by how ruby does pass-by-value and pass-by-reference.
This is probably one of the more confusing parts of Ruby. It is well accepted that Ruby is a pass-by-value, high level programming language. Unfortunately, this is slightly incorrect, and you have found yourself a perfect example. Ruby does pass-by-value, however, most values in ruby are references. When Ruby does an assignment of a simple datatypes, integers, floats, strings, it will create a new object. However, when assigning objects such as arrays and hashes, you are creating references.
original_hash = {name: "schylar"}
reference_hash = original_hash
reference_hash[:name] = "!schylar"
original_hash #=> "!schylar"
original_array = [1,2]
reference_array = original_array
reference_array[0] = 3
reference_array #=> [3,2]
original_fixnum = 1
new_object_fixnum = original_fixnum
new_object_fixnum = 2
original_fixnum #=> 1
original_string = "Schylar"
new_object_string = original_string
new_object_string = "!Schylar"
original_string #=> "Schylar'
If you find yourself needing to copy by value, you may re-think the design. A common way to pass-by-value complex datatypes is using the Marshal methods.
a = {name: "Schylar"}
b = Marshal.load(Marshal.dump(a))
b[:name] = "!!!Schylar"
a #=> {:name => "Schylar"}

Ruby variables managment issue

I'm trying to figure out why the following codes doesn't return the same result:
CODE 1
p0 = "hello"
a = []
b = p0
1.upto(5) do |i|
b.insert(2,"B")
a.push b
end
a => ["heBBBBBllo", "heBBBBBllo", "heBBBBBllo", "heBBBBBllo", "heBBBBBllo"]
CODE 2
p0 = "hello"
a = []
b = p0
1.upto(5) do |i|
b.insert(2,"B")
a.push b.inspect
end
a => ["\"heBllo\"", "\"heBBllo\"", "\"heBBBllo\"", "\"heBBBBllo\"", "\"heBBBBBllo\""]
What I need is the Code 2's result, but I don't need the escaped char like the inspect method does.
Honestly, I really don't understand why with the inspect method works, and why in the code 1 doesn't.
It seems like that in code 1, "b" is used as a pointer, and every time it's updated, all the "linked"-b are updated.
Any clue??
thank you in advance.
In code 1 you're pushing references to the same object. The array will contain multiple references to the same thing.
In code 2 you're pushing the inspect output at a distinct moment in time. The array will contain a history of inspect's returned strings.
p0 = "hello"
a = []
b = p0
1.upto(5) do |i|
b.insert(2,"B")
a.push b.clone
end
It seems like that in code 1, "b" is used as a pointer, and every time
it's updated, all the "linked"-b are updated.
Correct! In the first case, you're pushing b into the array five times, so the result is that a contains five pointers to b. In the second case you're pushing b.inspect - which is a different object from b.
The easiest way to fix your first example would be to call a.push b.dup instead, which creates a duplicate of b which won't be affected by future changes to b.
'b' is a string, which is an object. When you insert a letter, it modifies the object.
In CODE 1, that same object b is being pushed into the array multiple times, and modified multiple times.
However, b.inspect returns a new string. So in CODE 2, you push a new string into the array each iteration, and that new string is a snapshot of how 'b' looked at the time.
You could use a.push b.dup instead, which creates a copy of b without changing the format.

Learning Ruby: Making infinite dimensional array via block abuse

Somebody tell me what's going on here:
a = [0,1,2]
a.each {|x| a[x] = a}
The result is [[...], [...], [...]]. And if I evaluate a[0] I get [[...], [...], [...]]. And if I evaluate a[0][0] I get [[...], [...], [...]] ad infinitum.
Have I created an array of infinite dimensionality? How/Why should this possibly work?
Basically you've modified every element in a to reference the list itself. The list is recursively referencing itself:
a[0] # => a
a[0][0] # => a[0], which is a
a[0][0][0] # => a[0][0], which is a[0], which is a
...
(# => is a Rubyism for "this line evaluates to")
Depending on how you look at it it is not infinite. It's more or less just like a piece of paper with the words "please turn over" written on both sides.
The reason that Ruby prints [...] is that it is clever enough to discover that the list is recursive, and avoids going into an infinite loop.
By the way, your usage of each is a bit non-idiomatic. each returns the list, and you usually don't assign this return value to a variable (since you already have a variable referencing it, a in this case). In other words, your code assigns [0,1,2] to a, then loops over a (setting each element to a), then assigns a to a.
I think it's a self-referential data structure. The a[x]=a puts a's pointer in a[x].

Strange Feature? of Ruby Arrays

I came around this strange feature(?) of arrays in Ruby and it would be very helpful if someone could explain to me why they work the way they do.
First lets give an example of how things usually work.
a = "Hello" #=> "Hello"
b = a #=> "Hello"
b += " Goodbye" #=> "Hello Goodbye"
b #=> "Hello Goodbye"
a #=> "Hello"
Ok cool, when you use = it creats a copy of the object (this time a string).
But when you use arrays this happens:
a = [1,2,3] #=> [1,2,3]
b = a #=> [1,2,3]
b[1] = 5 #=> [1,5,3]
b #=> [1,5,3]
a #=> [1,5,3]
Now thats just strange. Its the only object I've found that doesn't get copied when using = but instead just creates a refrance to the original object.
Can someone also explain (there must be a method) for copying an array without having it point back to the original object?
Actually, you should re-examine your premise.
The string assignment is really b = b + " Goodbye". The b + " Goodbye" operation returns an entirely new string, so the variable b is pointing to a new object after the assignment.
But when you assign to an individual array element, you are not creating an entirely new array, so a and b continue to point to the same object, which you just changed.
If you are looking for a rationale for the mutating vs functional behavior of arrays, it's simple. There is nothing to be gained by modifying the string. It is most likely necessary to allocate new memory anyway, so an entirely new string is created.
But an array can be arbitrarily large. Creating a new array in order to change just one element could be hugely expensive. And in any case, an array is like any other composite object. Changing an individual attribute does not necessarily affect any other attributes.
And to answer your question, you can always do:
b = a.dup
What happends there is that ruby is treating the Array object by reference and not by value.
So you can see it as this:
b= [1,2,3]
a= b
--'b' Points to---> [1,2,3] <--'a' points t---
So as you can see both point to the same reference, that means that if you change anything in a it will be reflected on b.
As for your question on the copying the object you could use the Object#clone method to do so.
Try your Array case with a String:
a = "Hello" #=> "Hello"
b = a #=> "Hello"
b[1] = "x" #=> "x"
b #=> "Hxllo"
a #=> "Hxllo"
Strings and Arrays work the same way in this regard.
The key difference in the two cases, as you wrote them, is this:
b += " Goodbye"
This is syntactic shorthand for
b = b + " Goodbye"
which is creating a new string from b + " Goodbye" and then assigning that to b. The way to modify an existing string, rather than creating a new one, is
b << " Goodbye"
And if you plug that into your sequence, you'll see that it modifies both a and b, since both variables refer to the same string object.
As for deep copying, there's a decent piece about it here:
http://ruby.about.com/od/advancedruby/a/deepcopy.htm

Resources