2

I would like to do something like this to call myfunc() on each element in-place on an existing PDL:

$pdl = pdl [1,2,3];
$pdl = pdl [ map { myfunc($_) } @{ unpdl($pdl) } ];

I've searched through the documentation, but nothing yet:

Is there a way to visit each element of a PDL and run something on the element?

brian d foy
  • 129,424
  • 31
  • 207
  • 592
KJ7LNW
  • 1,437
  • 5
  • 11

1 Answers1

4

You can use broadcast_define (thread_define in older versions) for this:

use PDL;

broadcast_define('square(a();[o]b())', over { $_[1] .= $_[0] ** 2 });

my $pdl = pdl [[1,2,3], [4,5,6]];
square($pdl, (my $out = null));
print($out); # $out is pdl [[1,4,9], [16,25,36]]

this is slower than using native ops ($pdl**2 gets the same result in this case, and many more complicated things are possible by composing native ops), or PDL::PP, in which you write your function in C, but it is faster than unpdl/map/pdl.

You have to look at the PDL::PP docs for explanations of the signature like (a();[o]b()), but this is the simple case: the function has a scalar input and a scalar output, and can be broadcast to any number of dimensions.

I can't seem to get broadcast_defined functions to return a value when the output argument is omitted (e.g. my $out = square($pdl)), but you can do in-place modification with square($pdl, $pdl).

hobbs
  • 223,387
  • 19
  • 210
  • 288
  • 1
    +1 for a great answer. The return value thing is showing a shortcoming of `broadcast_define`; it doesn't handle varargs like the generated XS for "real" PP operations. If you can show some inputs and expected varargs-ish output, then that can form a test for such functionality (though the ideal is a pull-request with that and a fix ;-) – Ed. Jun 17 '22 at 16:02