Let's start by making four arrays:
>> x = [10, 20] >> y = [30] >> z = [40,50] >> all = [x,y,z]
Let's make a hash to hold labels, and assign labels to x and y based on their object_ids:
>> labels = {} >> labels[x.object_id] = "a1" >> labels[y.object_id] = "a2" >> labels => {7971260=>"a1", 8011640=>"a2"}
Now let's work through the elements of all and see what's been labeled:
>> labels[all[0].object_id] => "a1" >> labels[all[1].object_id] => "a2" >> labels[all[2].object_id] => nil
In the experimentation above we made a hash named labels but how could we handle that in our implementation of label? One possibility is a global named $labels but for a better way consider this skeleton for label:
def label(x, labels = {}) ... elem = ...some element of x... label(elem, labels) ... end
When label is called by the user, with one argument, such as label([10,20,[30]]), a brand new hash is created and assigned to labels. When label is called recursively, we pass the labels hash as a second argument, giving that call access to the accumulated collection of object_id/string pairs.
An invariant with this approach is that labels has a collection of labels for every array and hash that's been encountered for the top-level call of label(x).
I'll leave to you the details of producing sequence numbers. Remember that arrays and hashes are independently numbered: the first array is "a1" and the first hash is "h1".