Support

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

2D Array in Ruby

For general discussion related FlowStone

2D Array in Ruby

Postby Rocko » Sun Jun 04, 2017 9:19 am

Hi,

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 898 times
Rocko
 
Posts: 186
Joined: Tue May 15, 2012 12:42 pm

Re: 2D Array in Ruby

Postby cjsdks128 » Sun Jun 04, 2017 11:47 am

Hi, probably flowstone not support in/ouput 2D array.
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 905 times
cjsdks128
 
Posts: 5
Joined: Sun Jan 24, 2016 12:40 pm

Re: 2D Array in Ruby

Postby TheOm » Sun Jun 04, 2017 11:54 am

You should use the ruby value connector type, obviously it can't work with the float array connector type.
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.
TheOm
 
Posts: 103
Joined: Tue Jan 28, 2014 7:35 pm
Location: Germany

Re: 2D Array in Ruby

Postby tulamide » Sun Jun 04, 2017 4:37 pm

In case, you're not so familiar with programming, here's what this referencing is about. I simplify the explanation, so that it is understandable. It might not be an 100% accurate image of the technical ongoings.

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"]
"There lies the dog buried" (German saying translated literally)
tulamide
 
Posts: 2687
Joined: Sat Jun 21, 2014 2:48 pm
Location: Germany

Re: 2D Array in Ruby

Postby KG_is_back » Sun Jun 04, 2017 7:13 pm

Simply put, Arrays in ruby do not contain actual data. They only contain references to data. When you have nested array (arrays within array (within array...)) and make copy of the original. The copy still points to the same exact sub arrays.

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%
KG_is_back
 
Posts: 1196
Joined: Tue Oct 22, 2013 5:43 pm
Location: Slovakia

Re: 2D Array in Ruby

Postby RJHollins » Mon Jun 05, 2017 12:48 am

Spoken like a true Magician.

Thanks for the educational material. Always welcomed.
8-)
RJHollins
 
Posts: 1568
Joined: Thu Mar 08, 2012 7:58 pm

Re: 2D Array in Ruby

Postby tulamide » Mon Jun 05, 2017 5:03 am

Hey KG,

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
      end

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?
"There lies the dog buried" (German saying translated literally)
tulamide
 
Posts: 2687
Joined: Sat Jun 21, 2014 2:48 pm
Location: Germany

Re: 2D Array in Ruby

Postby KG_is_back » Mon Jun 05, 2017 10:02 pm

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.
KG_is_back
 
Posts: 1196
Joined: Tue Oct 22, 2013 5:43 pm
Location: Slovakia

Re: 2D Array in Ruby

Postby tulamide » Mon Jun 05, 2017 10:57 pm

I'm glad to hear that I did not miss something :D

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.
"There lies the dog buried" (German saying translated literally)
tulamide
 
Posts: 2687
Joined: Sat Jun 21, 2014 2:48 pm
Location: Germany


Return to General

Who is online

Users browsing this forum: No registered users and 29 guests