While working on the "Intensity Landscape" code, I happened to use unpdl on a (3,72000) ndarray. It took about 45 seconds to run. That's because it uses the dog method, which is fabulously slow for ndarrays with a large top dimension (a known problem: see https://github.com/PDLPorters/pdl/issues/421). I decided I would write an XS version, based on the already-existing listref_c code:
static inline SV *pdl2avref(pdl *x, char flatten) { int stop = 0, badflag = (x->state & PDL_BADVAL) > 0; volatile PDL_Anyval pdl_val = { PDL_INVALID, {0} }; /* same reason a +s below */ volatile PDL_Anyval pdl_badval = { PDL_INVALID, {0} }; if (badflag) { if (!(x->has_badvalue && x->badvalue.type != x->datatype)) { if (x->has_badvalue) pdl_badval = x->badvalue; else { #define X(datatype, ctype, ppsym, ...) \ pdl_badval.type = datatype; pdl_badval.value.ppsym = PDL.bval +s.ppsym; PDL_GENERICSWITCH(PDL_TYPELIST_ALL, x->datatype, X, ) #undef X } } if (pdl_badval.type < 0) barf("Error getting badvalue, type=%d", p +dl_badval.type); } pdl_barf_if_error(pdl_make_physvaffine( x )); if (!x->nvals) return newRV_noinc((SV *)newAV()); void *data = PDL_REPRP(x); PDL_Indx ind, inds[!x->ndims ? 1 : x->ndims]; AV *avs[(flatten || !x->ndims) ? 1 : x->ndims]; if (flatten || !x->ndims) { inds[0] = 0; avs[0] = newAV(); av_extend(avs[0], flatten ? x->nvals : 1); if (flatten) for (ind=1; ind < x->ndims; ind++) inds[ind] = 0; } else for (ind=x->ndims-1; ind >= 0; ind--) { inds[ind] = 0; avs[ind] = newAV(); av_extend(avs[ind], x->dims[ind]); if (ind < x->ndims-1) av_store(avs[ind+1], 0, newRV_noinc((SV *) +avs[ind])); } PDL_Indx *incs = PDL_REPRINCS(x), offs = PDL_REPROFFS(x), lind = 0; while (!stop) { pdl_val.type = PDL_INVALID; PDL_Indx ioff = pdl_get_offset(inds, x->dims, incs, offs, x->ndims +); if (ioff >= 0) ANYVAL_FROM_CTYPE_OFFSET(pdl_val, x->datatype, data, ioff); if (pdl_val.type < 0) croak("Position out of range"); SV *sv; if (badflag) { /* volatile because gcc optimiser otherwise won't recalc for com +plex double when long-double code added */ volatile int isbad = ANYVAL_ISBAD(pdl_val, pdl_badval); if (isbad == -1) croak("ANYVAL_ISBAD error on types %d, %d", pdl +_val.type, pdl_badval.type); if (isbad) sv = newSVpvn( "BAD", 3 ); else { sv = newSV(0); ANYVAL_TO_SV(sv, pdl_val); } } else { sv = newSV(0); ANYVAL_TO_SV(sv, pdl_val); } av_store( avs[0], flatten ? lind++ : inds[0], sv ); stop = 1; char didwrap[x->ndims]; for (ind = 0; ind < x->ndims; ind++) didwrap[ind] = 0; for (ind = 0; ind < x->ndims; ind++) { if (++(inds[ind]) < x->dims[ind]) { stop = 0; break; } inds[ind] = 0; didwrap[ind] = 1; } if (stop) break; if (flatten) continue; for (ind=x->ndims-2; ind >= 0; ind--) { /* never redo outer so -2 +*/ if (!didwrap[ind]) continue; avs[ind] = newAV(); av_extend(avs[ind], x->dims[ind]); av_store(avs[ind+1], inds[ind+1], newRV_noinc((SV *)avs[ind])); } } return newRV_noinc((SV *)avs[(flatten || !x->ndims) ? 0 : x->ndims-1 +]); } # ... MODULE = PDL::Core PACKAGE = PDL SV * unpdl(x) pdl *x CODE: RETVAL = pdl2avref(x, 0); OUTPUT: RETVAL
The bit I thought was quite neat is the logic to keep making new AVs when it wraps dimensions (the n-dimension walking code was already there, but this was new). While this is quite PDL-specific, the concepts should be applicable for any n-dimensional mapping.

In reply to XS to make n-dimensional data into Perl array-refs etc by etj

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.