If you have a problem or need to report a bug please email : support@dsprobotics.com
There are 3 sections to this support area:
DOWNLOADS: access to product manuals, support files and drivers
HELP & INFORMATION: tutorials and example files for learning or finding pre-made modules for your projects
USER FORUMS: meet with other users and exchange ideas, you can also get help and assistance here
NEW REGISTRATIONS - please contact us if you wish to register on the forum
Users are reminded of the forum rules they sign up to which prohibits any activity that violates any laws including posting material covered by copyright
2D Array in Ruby
2D Array in Ruby
Trying to build a 2 dimensional array in Ruby and send it to another Ruby module, via the input and output methodology of flowstone.
It seems that sending a 2D array is not possible.
Is this correct?
Is there a way to overcome it?
First Ruby module
Code: Select all
a = [1,2,3]
b = Array.new(3,a)
b[1][1] = 7
# b is now a sD array of shape
# [1,2,3], [1,7,3], [1,2,3]
output b
#For example, print out a specific value:
b[1][1]
Within second Ruby module:
Code: Select all
#but here, @b[1] is nil:
@b[1]
Please see attached schematic for details.
- Attachments
-
- 2D_Array.fsm
- (447 Bytes) Downloaded 983 times
Re: 2D Array in Ruby
but you can send to another ruby module without linkwire if you use ruby class.
see my uploaded attachment.
- Attachments
-
- 2D_Array.fsm
- (573 Bytes) Downloaded 990 times
Re: 2D Array in Ruby
Also you probably want to create your 2d array like this:
Code: Select all
a = [2,2,0]
b = Array.new(3) { |i| Array.new(a) }
otherwise the elements of the outer array will all reference a and changing one of them will change all of the others aswell.
Re: 2D Array in Ruby
Generally, and in OOP especially, values can be passed in two ways: By reference ("byRef") or by value ("byVal"). The difference is significant.
byRef means that only an address to a spot in memory is passed, and the method then looks at this spot to use the data that's stored there.
byVal means that still an address to a spot in memory is passed, but the method doesn't use the data directly, but first copies it to another spot in memory and works with this copy.
In Ruby everything is passed byRef, until you explicitly tell it to not do so. The advantage is clear. There's no copying going on, so it is faster. The disadvantage is also clear. Every method that's supposed to work with the data will have the same access. If method A changes the content, method B will see the changed content and not the original data. Often times this is wanted, so you don't stumble upon it. For arrays however, this can produce unwanted results.
arr = [1, 2, 3]
arr address will point to this object. The data stored there are the addresses to the number objects 1, 2 and 3
my_2d_arr = [arr, arr]
my_2d_arr address will point to this object. The data stored there is two times the address to arr.
arr[1] = 4
we changed the data stored at arr address. The data stored there are now the addresses to the number objects 1, 4 and 3
my_2d_arr
this call will show the stored data. Since it points two times to arr, we will see
[[1, 4, 3],[1, 4, 3]]
Simplified one could get the impression that we changed the content of my_2d_arr without ever touching it. But that's not true. It still has the same content, which is two times arr. But we changed arr, and so we see the changes in my_2d_arr as well.
A byVal passing of data is possible, but complex.
arr2 = arr.clone
This method creates a shallow copy of arr. What does that mean? It means that arr2 is now an array of its own. arr and arr2 point to different spots in memory. BUT, the contents of those two arrays still point to the same addresses to the number objects.
A true copy, where every element is in its own spot in memory, can be achieved in various ways, for example via marshalling or by using blocks. Those are the {} elements you often see in Ruby code. A block basically is a short method attached to a loop. It will runs the code inside {} for each element of the loop.
Code: Select all
a = ["a", "b", "c"]
b = a.map { |e| e.dup}
a[1] = "d"Now you have a deep copy, where every element is in its own spot in memory. That means, after the last line of above code, the contents of the arrays will be
a = ["a", "d", "c"]
b = ["a", "b", "c"]
-
KG_is_back
- Posts: 1196
- Joined: Tue Oct 22, 2013 5:43 pm
- Location: Slovakia
Re: 2D Array in Ruby
Most convenient way to create deep copy is to add a method that recursively makes the deep copy for you. For example:
Code: Select all
def deep_copy(ins)
ins.map{|x|
if x.class==Array
deep_copy(x)
else
x
end
}
end
You add this to the ruby component that needs to make deep copies and whenever you need a deep copy you write
newArray=deep_copy(oldArray);
The example Tulamide provided only works for 2D arrays. This one will work for any crazy array arrangement possible with a single exception - the array (or any of the subarrays) must not contain itself, otherwise you get infinite regress (ruby will freeze and either time out or it'll run out of RAM).
Alternatively, if you know you will be needing to make deep copies all over your schematic, it may be convenient to add this method to the Array class directly, so its available everywhere in the schematic. NOTE: this is a bit more advanced, but it's mentioned in the Flowstone manual. The way you do this is you add a new rubyedit component (preferably somewhere in the top most module of your schematic) and copy this code to it:
Code: Select all
class Array
def deep_copy
self.map{|x|
if x.class==Array
x.deep_copy
else
x
end
}
end
end
This time you make copies simply by writing:
newArray=oldArray.deep_copy
You also will have to make sure that when schematic loads this component is loaded as one of the first (so that the method is added when loading before FS attempts to use it). The way you do this is:
1. select everything (ctrl+a)
2. deselect this single ruby component (ctrl+shift+leftclick),
3. then cut and paste what is selected (ctrl+x and ctrl+v)
Now this ruby component is guaranteed to be loaded first and no errors should occur when you load the schematic next time.
NOTE: There is a 0.02% chance this process will spawn satan, since as we all know, copy-pasting programming code to make it work is a form of black magic. Lit candles, pentagrams and fresh animal remains are known to increase the probability of "success" up to 0.13%
Re: 2D Array in Ruby
Thanks for the educational material. Always welcomed.
Re: 2D Array in Ruby
yes, I was only interested in explaining the 'by reference' issue, rather than giving a fits-all-solution. Regarding your proposal I think I miss something important. Just from the code it doesn't seem to be a deep copy.
Code: Select all
if x.class==Array
deep_copy(x)
else
x
endWhile you go through all layers you still just return the element ref and not a copy (for example x.dup or x.clone). This works for some elements like numbers, because numbers are singletons, but it will still point to the original object. So I'm missing something, but I can't get it. Care to help me?
-
KG_is_back
- Posts: 1196
- Joined: Tue Oct 22, 2013 5:43 pm
- Location: Slovakia
Re: 2D Array in Ruby
tulamide wrote:While you go through all layers you still just return the element ref and not a copy (for example x.dup or x.clone). This works for some elements like numbers, because numbers are singletons, but it will still point to the original object. So I'm missing something, but I can't get it. Care to help me?
Yes, you are correct, it will only work for singleton objects like numbers. It also mostly works for strings, but there the behaviour is rather inconsistent (though the inconsistencies are well documented and intentional). The deep-copied array will still contain reference to original string. If you try to modify the string, some methods will create new string in its place, while others will modify the original.
Code: Select all
a=["abc",5]
b=a.deep_copy
b[0]+="d"
[a,b] #returns [["abc",5],["abcd",5]] which means new copy is created
a=["abc",5]
b=a.deep_copy
b[0][3]="d"
[a,b] #returns [["abcd",5],["abcd",5]] which means original string is modified
As far as I'm aware, there is no simple solution to this problem. Ruby does not have any default way to duplicate arbitrary Object. Even if it did, each object has instance variables, which themselves are references to other objects and ruby has no way of predicting whether you expect given variable to be a reference or not. For example, let's say you make a strategy game and you want to duplicate a unit. The unit probably contains reference to the player who owns it as an instance variable, so you expect the duplicate to have reference to the same player, not a copy of the player. On the other hand, the unit may have picked up items (for example weapons or tools), and you expect the copy to have duplicates of those original items. Otherwise you may end up in situation where both units share the same weapon and magazines and drain ammo from them (the unit you left at your base runs out of ammo despite not shooting once).
To Ruby interpreter these two scenarios look identical - it has no idea which behaviour you expect, so it defaults to the first one. In ruby arrays, hashes and strings have .dup and .clone methods to explicitly create copies, but most other classes do not have this feature - you will get "no method error" in those cases.
Re: 2D Array in Ruby
I had to deal with this issue for my spline class. It gave me a headache. If you have a look at the code you will see that I implemented a whole method tree dealing with creating deep copies. I did it by manually creating each object new and then filling it with needed data from the original class. A pita, and relatively slow in execution, but the only way to ensure two totally independent objects.