Ruby map! does not change contained variable - ruby

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.

Related

Why isn't the value picked up in this Ruby inject call?

In the following code, if I don't add parentheses around the key, value parameters in "original.inject" (i.e., I do original.inject({}){ |result, key, value| ), I get a nil error as in the code comments below, as if the value is not being passed correctly. Why is this? What exactly is going on here? (I'm running ruby-2.1.1)
def hash_test(original,options={},&block)
original.inject({}){ |result, (key, value)|
value = value + 2
block.call(result, key, value)
result
}
end
h={:a=>3, :b=>4}
r = hash_test(h) { |result, key, value| result[key]=value }
puts r #=> {:a=>5, :b=>6}
#if no parentheses around (key, value) in original.inject, you get a:
# hash_transformer.rb:5:in `block in hash_test': undefined method `+' for nil:NilClass (NoMethodError)
# from hash_transformer.rb:4:in `each'
# from hash_transformer.rb:4:in `inject'
# from hash_transformer.rb:4:in `hash_test'
# from hash_transformer.rb:15:in `<main>'
When inject is called on a Hash inject yields the key value pairs as an array to the supplied block, example as below:
{:a=>3, :b=>4}.inject({}) do |x,y|
p y
x[y[0]] = y[1]
x
end
#>>[:a, 3]
#>>[:b, 4]
#=> {:a=>3, :b=>4}
y yielded the first time is [:a, 3], and second time is [:b, 4], so you need to destructure the array by supplying the parenthesis around the arguments:
(a,b) = [:a, 3]
a #=> :a
b #=> 3
This shows what's going on.
With deconstruction of hash element
{:a=>3, :b=>4}.inject({}) do |x,(y,z)|
puts "x = #{x}"
puts "y = #{y}"
puts "z = #{z}"
puts
x
end
x = {}
y = a
z = 3
x = {}
y = b
z = 4
Without deconstruction of hash element
{:a=>3, :b=>4}.inject({}) do |x,y,z|
puts "x = #{x}"
puts "y = #{y}"
puts "z.nil? = #{z.nil?}"
puts
x
end
x = {}
y = [:a, 3]
z.nil? = true
x = {}
y = [:b, 4]
z.nil? = true
Take a look at this post :
http://apidock.com/ruby/Enumerable/inject
It said inject is defined to take pairs parameter.
If you want to use three parameters, like (inject do |c,key,val|), then you should use parentheses to combine the (key,val)

Combine multiple strings in array 1 to form array 2

I have an array of strings "A" and a target string "B". The B string
contains only characters from a to z and does not contain spaces. I need a ruby function
that returns the array of strings which represents all possible ways to form B from elements of Array A. The order in which combinations are returned is irrelevant. Words from A can be used multiple times.
Example:
A = [‘x’, ‘y’, ‘z’, 'yz',‘xy’, ‘xyz’]
B = ‘xyz’
method(A, B) => [x y z, x yz, xyz, xy z]
I looked into permutation method but can't get it to work.
Here's a recursive solution that doesn't iterate through the permutations:
def find_possible_combinations( list, str )
list.select { |a| str.start_with? a }.map do |a|
if a == str
[a]
else
find_possible_combinations( list-[a], str.slice(a.length..-1) )
.map { |c| "#{a} #{c}" }
end
end.flatten
end
puts "#{find_possible_combinations( %w( x y z yz xy xyz ), 'xyz' )}"
Output:
["x y z", "x yz", "xy z", "xyz"]
A = %w[x y z yz xy xyz]
B = "xyz"
SIZE = B.size
def combos
(1..SIZE).inject([]) {|c, n| c.concat(combos_by_size(n))}.reject(&:empty?)
end
# Select all combinations of size n having SIZE characters
def combos_by_size(n)
A.combination(n).to_a.inject([]) {|c, a| c.concat(matching_combos(a)) if a.join.size == SIZE; c}.reject(&:empty?)
end
# Select all matching combinations using all elements of array a
def matching_combos(a)
a.permutation.to_a.select {|p| p.join == B}
end
combos # => [["xyz"], ["x", "yz"], ["xy", "z"], ["x", "y", "z"]]

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.

Ruby multidimensional array

Maybe it's just my lack of abilities to find stuff here that is the problem, but I can't find anything about how to create multidimensional arrays in Ruby.
Could someone please give me an example on how to do it?
Strictly speaking it is not possible to create multi dimensional arrays in Ruby. But it is possible to put an array in another array, which is almost the same as a multi dimensional array.
This is how you could create a 2D array in Ruby:
a = [[1,2,3], [4,5,6], [7,8,9]]
As stated in the comments, you could also use NArray which is a Ruby numerical array library:
require 'narray'
b = NArray[ [1,2,3], [4,5,6], [7,8,9] ]
Use a[i][j] to access the elements of the array. Basically a[i] returns the 'sub array' stored on position i of a and thus a[i][j] returns element number j from the array that is stored on position i.
you can pass a block to Array.new
Array.new(n) {Array.new(n,default_value)}
the value that returns the block will be the value of each index of the first array,
so..
Array.new(2) {Array.new(2,5)} #=> [[5,5],[5,5]]
and you can access this array using array[x][y]
also for second Array instantiation, you can pass a block as default value too. so
Array.new(2) { Array.new(3) { |index| index ** 2} } #=> [[0, 1, 4], [0, 1, 4]]
Just a clarification:
arr = Array.new(2) {Array.new(2,5)} #=> [[5,5],[5,5]]
is not at all the same as:
arr = Array.new(2, Array.new(2, 5))
in the later case, try:
arr[0][0] = 99
and this is what you got:
[[99,5], [99,5]]
There are two ways to initialize multi array (size of 2).
All the another answers show examples with a default value.
Declare each of sub-array (you can do it in a runtime):
multi = []
multi[0] = []
multi[1] = []
or declare size of a parent array when initializing:
multi = Array.new(2) { Array.new }
Usage example:
multi[0][0] = 'a'
multi[0][1] = 'b'
multi[1][0] = 'c'
multi[1][1] = 'd'
p multi # [["a", "b"], ["c", "d"]]
p multi[1][0] # "c"
So you can wrap the first way and use it like this:
#multi = []
def multi(x, y, value)
#multi[x] ||= []
#multi[x][y] = value
end
multi(0, 0, 'a')
multi(0, 1, 'b')
multi(1, 0, 'c')
multi(1, 1, 'd')
p #multi # [["a", "b"], ["c", "d"]]
p #multi[1][0] # "c"
The method given above don't works.
n = 10
arr = Array.new(n, Array.new(n, Array.new(n,0.0)))
arr[0][1][2] += 1
puts arr[0][2][2]
is equivalent to
n = 10
a = Array.new(n,0.0)
b = Array.new(n,a)
arr = Array.new(n, b)
arr[0][1][2] += 1
puts arr[0][2][2]
and will print 1.0, not 0.0, because we are modifiyng array a and printing the element of array a.
Actually this is much quicker than the block method given above:
arr = Array.new(n, Array.new(n, Array.new(n,0.0)))
arr[0][1][2] += 1
I had to reproduce PHP-style multidimensional array in Ruby recently. Here is what I did:
# Produce PHP-style multidimensional array.
#
# Example
#
# arr = Marray.new
#
# arr[1][2][3] = "foo"
# => "foo"
#
# arr[1][2][3]
# => "foo"
class Marray < Array
def [](i)
super.nil? ? self[i] = Marray.new : super
end
end
Perhaps you can simulate your multidimensional Array with a Hash. The Hash-key can by any Ruby object, so you could also take an array.
Example:
marray = {}
p marray[[1,2]] #-> nil
marray[[1,2]] = :a
p marray[[1,2]] #-> :a
Based on this idea you could define a new class.
Just a quick scenario:
=begin rdoc
Define a multidimensional array.
The keys must be Fixnum.
The following features from Array are not supported:
* negative keys (Like Array[-1])
* No methods <<, each, ...
=end
class MArray
INFINITY = Float::INFINITY
=begin rdoc
=end
def initialize(dimensions=2, *limits)
#dimensions = dimensions
raise ArgumentError if limits.size > dimensions
#limits = []
0.upto(#dimensions-1){|i|
#limits << (limits[i] || INFINITY)
}
#content = {}
end
attr_reader :dimensions
attr_reader :limits
=begin rdoc
=end
def checkkeys(keys)
raise ArgumentError, "Additional key values for %i-dimensional Array" % #dimensions if keys.size > #dimensions
raise ArgumentError, "Missing key values for %i-dimensional Array" % #dimensions if keys.size != #dimensions
raise ArgumentError, "No keys given" if keys.size == 0
keys.each_with_index{|key,i|
raise ArgumentError, "Exceeded limit for %i dimension" % (i+1) if key > #limits[i]
raise ArgumentError, "Only positive numbers allowed" if key < 1
}
end
def[]=(*keys)
data = keys.pop
checkkeys(keys)
#content[keys] = data
end
def[](*keys)
checkkeys(keys)
#content[keys]
end
end
This can be used as:
arr = MArray.new()
arr[1,1] = 3
arr[2,2] = 3
If you need a predefined matrix 2x2 you can use it as:
arr = MArray.new(2,2,2)
arr[1,1] = 3
arr[2,2] = 3
#~ arr[3,2] = 3 #Exceeded limit for 1 dimension (ArgumentError)
I could imagine how to handle commands like << or each in a two-dimensional array, but not in multidimensional ones.
It might help to remember that the array is an object in ruby, and objects are not (by default) created simply by naming them or naming a the object reference. Here is a routine for creating a 3 dimension array and dumping it to the screen for verification:
def Create3DimensionArray(x, y, z, default)
n = 0 # verification code only
ar = Array.new(x)
for i in 0...x
ar[i] = Array.new(y)
for j in 0...y
ar[i][j] = Array.new(z, default)
for k in 0...z # verification code only
ar[i][j][k] = n # verification code only
n += 1 # verification code only
end # verification code only
end
end
return ar
end
# Create sample and verify
ar = Create3DimensionArray(3, 7, 10, 0)
for x in ar
puts "||"
for y in x
puts "|"
for z in y
printf "%d ", z
end
end
end
Here is an implementation of a 3D array class in ruby, in this case the default value is 0
class Array3
def initialize
#store = [[[]]]
end
def [](a,b,c)
if #store[a]==nil ||
#store[a][b]==nil ||
#store[a][b][c]==nil
return 0
else
return #store[a][b][c]
end
end
def []=(a,b,c,x)
#store[a] = [[]] if #store[a]==nil
#store[a][b] = [] if #store[a][b]==nil
#store[a][b][c] = x
end
end
array = Array3.new
array[1,2,3] = 4
puts array[1,2,3] # => 4
puts array[1,1,1] # => 0

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