hide random home http://www.fmi.uni-passau.de/archive/doc/unix/perl/faq/2.10.html (Einblicke ins Internet, 10/1995)

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

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


    Remember that Perl isn't about nested data structures (actually,
    perl0 ..  perl4 weren't, but maybe perl5 will be, at least
    somewhat).  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 array 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
    <flee@cs.psu.edu>, 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.

    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, [ 0, 'a' .. 'z' ] },    
        );
    
    Allosing you to reference $T{key2}->{k3}->[3] to pull out 'c'.