in reply to Re: Selecting Ranges of 2-Dimensional Data (array of aliases)
in thread Selecting Ranges of 2-Dimensional Data

D'oh! Of course, @_! Thank you very much, LanX :-)

An updated getsubset (for the code here):

sub getsubset { my ($data,$range) = @_; my $cols = @{$$data[0]}; @$_==$cols or croak "data not rectangular" for @$data; $range = rangeparse($range) unless ref $range eq 'ARRAY'; @$range==4 or croak "bad size of range"; my @max = (0+@$data,$cols)x2; for my $i (0..3) { $$range[$i]=$max[$i] if $$range[$i]<0; croak "index $i out of range" if $$range[$i]<1 || $$range[$i]>$max[$i]; } croak "bad rows $$range[0]-$$range[2]" if $$range[0]>$$range[2]; croak "bad cols $$range[1]-$$range[3]" if $$range[1]>$$range[3]; my @cis = $$range[1]-1 .. $$range[3]-1; return [ map { sub{\@_}->(@{$$data[$_]}[@cis]) } $$range[0]-1 .. $$range[2]-1 ] }

Replies are listed 'Best First'.
Re^3: Selecting Ranges of 2-Dimensional Data
by LanX (Saint) on Oct 27, 2018 at 15:15 UTC
    Honestly I wouldn't have implemented it your way in order to have a higher degree of reusability and readability.

    For instance

    • using a callback instead of arr_alias would allow a version to return copies which pass your first test criteria.
    • the nested maps could be abstracted to slice arbitrary nested structures, not only matrices.
    • allowing different projections, not only ranges
    OTOH I tend to get lost in abstraction. .. ;)

    > Of course, @_!

    It's a hack I once learned from Ikegami++, but it seems to be reliable.

    Though not many people know if it depends on an implementation detail.

    And unfortunately I don't think it can be used to alias hashes (i.e. values) too.

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

      Honestly I wouldn't have implemented it your way in order to have a higher degree of reusability and readability.

      Yeah, I wrote it to be kind of compact - I want to hide it away in a pm and not worry about it anymore (hence all the the tests). My application has limited scope, definitely only 2D data (fiddling with CSV), and I'm probably going to hide it away in a class so that the implementation can be upgraded later if necessary, or maybe replaced with an existing module (hence this thread).

      Though not many people know if it depends on an implementation detail.

      Hm, well I found this thread by tye where it sounds like it's probably reliable, and a whole bunch of other threads where it's a suggested technique. I just tested on Perl 5.6 up to 5.28, it works fine on all of them.

      And unfortunately I don't think it can be used to alias hashes (i.e. values) too.

      It can :-) Also:

      $ perl -wMstrict -MData::Dump -le 'my %h=("a".."j"); my $x=sub{\@_}->(@h{"c","e","g"}); $_=uc for @$x; dd \%h' { a => "b", c => "D", e => "F", g => "H", i => "j" }
        > It can :-)

        Unfortunately not, $x is an array ref not a hash ref.

        ( I got that far already :)

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

Re^3: Selecting Ranges of 2-Dimensional Data
by Aldebaran (Curate) on Oct 30, 2018 at 20:51 UTC

    This is all very interesting to read. I've put some say statements in the new getsubset to figure out aspects of the parameter array and matrix manipulations. I'll put abridged output and (unabridged) source between readmore tags and pull out the bits I want to ask about after. All of the useful source in this has been listed upthread, so I'd probably skip to the code niblets...

    I have not seen this syntax before, and just to be sure, I thumbed through _Learning Perl_, not seeing it in chapter 4, Lists and Arrays. Asking google what "perl arrays x 2" means does not ask an effective question.

      my @max = ( 0 + @$data, $cols ) x 2;

    I'm just looking for a reference to read up on that. The second thing I wanted to bring up was about the parameter array. Is it the case that @_ does not change over the life of the function? Does it have intrinsic aliasing?

    Finally, after days of tinkering with it, I'm still baffled by the return from getsubset. We know what it's to be because we print it out when it gets returned. Lo and behold, it is a reference to an array. I can't see how the sausage gets made here:

    return [ map { sub { \@_ } ->( @{ $$data[$_] }[@cis] ) } $$range[0] - 1 .. $$range[2] - 1 ];

    You use the range operator once. LanX (upthread for the curious) used it twice:

    return [ map { arr_alias @$_[ $cols->[0] .. $cols->[1] ] # x-slice } @$data[ $rows->[0] .. $rows->[1] ] # y-slice ];

    Are they logically equivalent? Thanks for your comment and raising a topic I haven't looked at in perl very far. (Scientific computing in my day was fortran.) Wouldn't it be relatively easy to display these values using Tk::TableMatrix?

      my @max = ( 0 + @$data, $cols ) x 2; I'm just looking for a reference to read up on that.

      See the x operator under Multiplicative Operators. I'm just using it as a shorthand for my @max = ( 0 + @$data, $cols, 0 + @$data, $cols );.

      The second thing I wanted to bring up was about the parameter array. Is it the case that @_ does not change over the life of the function? Does it have intrinsic aliasing?

      @_ is described in perlsub: "The array @_ is a local array, but its elements are aliases for the actual scalar parameters. In particular, if an element $_[0] is updated, the corresponding argument is updated ... Assigning to the whole array @_ removes that aliasing, and does not update any arguments." As to whether it doesn't change, that depends on what the sub does - it is not read-only. For example, shift and pop can modify @_, and in Perl versions 5.10 and older, split could clobber @_. And there are some other potentially tricky issues with @_, for example, if a sub is called with an & and no argument list, "no @_ array is set up for the subroutine: the @_ array at the time of the call is visible to subroutine instead" (also perlsub).

      You use the range operator once. LanX (upthread for the curious) used it twice:

      Well, actually I used it twice, note how I set up the @cis array. And yes, those two snippets of code from LanX and myself are basically equivalent. One difference is that I use 1-based indexing in the indices stored in the @$range array.

      I can't see how the sausage gets made here

      Ok, so here's my original code:

      my @cis = $$range[1]-1 .. $$range[3]-1; return [ map { sub{\@_}->(@{$$data[$_]}[@cis]) } $$range[0]-1 .. $$range[2]-1 ]

      First, let's reformat that, and instead of @$range, let me use four lexical variables corresponding to the elements of the array ($row1, $col1, $rowN, $colN), and make them 0-based instead of 1-based.

      my @column_indices = $col1 .. $colN; return [ map { sub{ \@_ }->( @{ $$data[$_] }[@column_indices] ) } $row1 .. $rowN ]

      Now, map can be translated into a for with push (I hope that transformation is clear?). I've also pulled out various bits of expressions into lexical variables.

      my @row_indices = $row1 .. $rowN; my @column_indices = $col1 .. $colN; my @row_subset; for my $row_idx (@row_indices) { my $row = $$data[$row_idx]; # deref $data and get row my $column_subset_aliases = sub{ \@_ }->( @$row[@column_indices] # deref $row and get array slice ); push @row_subset, $column_subset_aliases; } return \@row_subset;

      Now the last bit of trickery here is sub{\@_}->(...). sub {...} constructs an anonymous subroutine, which is then immediately called (via ->(...)) and with the arguments (...). The body of the sub is just \@_, which means "return a reference to @_". Because the elements of @_ are aliases to the original arguments, what we get back from the whole expression is an arrayref whose elements are aliases to the arguments. In the above code, those arguments are the elements of the array referred to by $row, which were selected by the array slice.

      I hope it's at least a little bit more clear now?

        I hope it's at least a little bit more clear now?

        It is, thank you. I struggle with the map {sub{\@_}->(...)} syntax, but can imitate it and know where to find it now. Indeed, I wasn't really grasping it until happening on the thread where davido, you, and others comment on the parameter array referring to other source: Re: passing a hash ref in a sub

        I like how the monastery can solve one's questions with deeper reading and cross-referencing nodes that carry a topic forward instead of beating it to death in one thread with one example....