perldl
shell
PDL::SFilter - towards a less insane slice syntax
use PDL::SFilter;
$a(1:4) .= 2; print $b((0),1:$end); $a->xchg(0,1)->(($pos-1)) .= 0;
PDL
's slice
syntax sucks. This module attempts to reduce the
suction. How does it work? We can't change Perl's syntax limitations.
However, Damian Conway has shown how to move forward within the
framework of Perl 5 syntax restrictions. The answer lies in the murky
area of source filters. Source filters can be thought of as a very
powerful macro facility. Finally Perl will rival Lisp in
obscurity. The possibility for confusion is virtually infinite and
only matched by the power of the approach ;).
NOTE: PDL::SFilter
relies on modules from CPAN that make source filtering
and parsing easier. In particular it requires Filter::Simple (which
in turn requires a module from the Filter distribution) and
Text::Balanced.
An arglist in parentheses following directly after a scalar variable
name that is not preceded by &
will be translated into an
approriate call to mslice
, e.g.
$a(1:4) .= 2;
is equivalent to
$a->mslice([1,4]) .= 2
However,
&$a(4,5);
will be left untouched to not interfere with the current subref syntax.
The second syntax that will be recognized is what I called the
default method syntax. It is the method arrow ->
directly
followed by an open parenthesis (
, e.g.
$a->xchg(0,1)->(($pos)) .= 0;
corresponds to
$a->xchg(0,1)->mslice([$pos,$pos,0]) .= 0; # really the ($pos) # format of slice
Note that this conflicts with the use of code refs, since you can write in plain Perl
$sub = sub { print join ',', @_ }; $sub->(1,'a');
NOTE: Once use PDL::SFilter
is in effect the preprocessor will incorrectly
replace the above call to $sub with an invocation of the slicing method.
I am not sure if this bothers anybody; a simple workaround is to use the
&
-way of calling subrefs, e.g.:
$sub = sub { print join ',', @_ }; &$sub(1,'a');
Why two different ways to invoke slicing? The first syntax $a(args)
doesn't work with chained method calls. E.g.
$a->xchg(0,1)(0);
won't work. Instead, use the default method syntax:
$a->xchg(0,1)->(0);
Similarly, if you have a list of piddles @pdls
:
$b = $pdls[5]->(0:-1);
Note that perl variables and function invocations can be used in the argument list:
$a($pos-1:$end,myfunc(1,3)) .= 5;
There can even be other slicing commands in the arglist:
$a(0:-1:$pdl($step)->at(0)) *= 2;
You can access ranges using the usual :
format:
$a($start:$stop:$step) *= 4;
Note that you can omit the trailing step. ::
is not allowed to
avoid clashes with Perl's namespace syntax. A position in parentheses
($pos)
selects that position and removes the dim from the result piddle
(just like slice
does):
print $a((0));
An empty argument selects the whole dimension:
print $a(,(0));
Trailing dims can be omitted as usual and will be selected as a whole.
$a = sequence 5, 5, 5; $b = $a(0); # slice of shape [1,5,5]
Anyway, that's the theory. This whole thing is probably not yet foolproof but a start.
perldl
shellIn a script or module write something like
use PDL::SFilter;
# this code will be translated
no PDL::SFilter;
# this code won't
See also the Filter::Simple manpage and test.pl in this distribution for an example.
To use the filter in the perldl
shell
add the following two lines to your .perldlrc file:
use PDL::SFilter; $PERLDL::PREPROCESS = \&PDL::SFilter::perldlpp;
A more complete tool box of commands for experimentation is
in the file local.perldlrc in the PDL::SFilter
source
directory.
Error checking is not yet foolproof. Please send comments and bug reports to the pdl-porters list <pdl-porters@jach.hawaii.edu>.
This module relies on the latest version of mslice to work properly. These are included below for the moment. If your installed PDL has a different mslice version just include the following in your .perldlrc (or even better include the contents of the file local.perldlrc that is part of this dist):
# called for colon-less args # preserves parens if present sub intpars { $_[0] =~ /\(.*\)/ ? '('.int($_[0]).')' : int $_[0] } sub PDL::mslice { my($pdl) = shift; return $pdl->slice(join ',',(map { $_ eq "X" ? ":" : ref $_ eq "ARRAY" ? $#$_ > 1 && @$_[2] == 0 ? "(".int(@$_[0]).")" : join ':', map {int $_} @$_ : !ref $_ ? intpars $_ : die "INVALID SLICE DEF $_" } @_)); }
Undoubtedly ;)
. The module is still highly experimental.
Feedback and bug reports are welcome.
Copyright (c) 2001, Christian Soeller. All Rights Reserved. This module is free software. It may be used, redistributed and/or modified under the same terms as PDL itself (see http://pdl.perl.org).