Perl FAQ 5.2: How can I make an array of arrays or other recursive data types?

Perl FAQ 5.2

How can I make an array of arrays or other recursive data types?

In Perl5, it's quite easy to declare these things. For example

    @A = (
        [ 'ww' .. 'xx'  ], 
        [ 'xx' .. 'yy'  ], 
        [ 'yy' .. 'zz'  ], 
        [ 'zz' .. 'zzz' ], 
    );

And now reference $A[2]->[0] to pull out ``yy''. These may also nest and mix with tables:

    %T = (
        key0, { k0, v0, k1, v1 },   
        key1, { k2, v2, k3, v3 },   
        key2, { k2, v2, k3, [ 'a' .. 'z' ] },    
    );
    
    Allowing you to reference $T{key2}->{k3}->[3] to pull out 'd'.

Perl4 is infinitely more difficult. Remember that Perl[0..4] isn't about nested data structures. It's about flat ones, so if you're trying to do this, you may be going about it the wrong way or using the wrong tools. You might try parallel arrays with common subscripts.

But if you're bound and determined, you can use the multi-dimensional array emulation of $a{'x','y','z'}, or you can make an array of names of arrays and eval it.

For example, if @name contains a list of names of arrays, you can get at a the j-th element of the i-th array like so:

    $ary = $name[$i];
    $val = eval "\$$ary[$j]";

or in one line

    $val = eval "\$$name[$i][\$j]";

You could also use the type-globbing syntax to make an array of *name values, which will be more efficient than eval. Here @name hold a list of pointers, which we'll have to dereference through a temporary variable.

For example:

    { local(*ary) = $name[$i]; $val = $ary[$j]; }

In fact, you can use this method to make arbitrarily nested data structures. You really have to want to do this kind of thing badly to go this far, however, as it is notationally cumbersome.

Let's assume you just simply have to have an array of arrays of arrays. What you do is make an array of pointers to arrays of pointers, where pointers are *name values described above. You initialize the outermost array normally, and then you build up your pointers from there. For example:

    @w = ( 'ww' .. 'xx' );
    @x = ( 'xx' .. 'yy' );
    @y = ( 'yy' .. 'zz' );
    @z = ( 'zz' .. 'zzz' );

    @ww = reverse @w;
    @xx = reverse @x;
    @yy = reverse @y;
    @zz = reverse @z;

Now make a couple of arrays of pointers to these:

    @A = ( *w, *x, *y, *z );
    @B = ( *ww, *xx, *yy, *zz );

And finally make an array of pointers to these arrays:

    @AAA = ( *A, *B );

To access an element, such as AAA[i][j][k], you must do this:

    local(*foo) = $AAA[$i];
    local(*bar) = $foo[$j];
    $answer = $bar[$k];

Similar manipulations on associative arrays are also feasible.

You could take a look at recurse.pl package posted by Felix Lee*, which lets you simulate vectors and tables (lists and associative arrays) by using type glob references and some pretty serious wizardry.

In C, you're used to creating recursive datatypes for operations like recursive decent parsing or tree traversal. In Perl, these algorithms are best implemented using associative arrays. Take an array called %parent, and build up pointers such that $parent{$person} is the name of that person's parent. Make sure you remember that $parent{'adam'} is 'adam'. :-) With a little care, this approach can be used to implement general graph traversal algorithms as well.


Other resources at this site: