Hi, Todd & Jeff, :)

From: "Jeff 'japhy' Pinyan" <[EMAIL PROTECTED]>
> On Nov 13, todd shifflett said:

>> the above works.  Now I would like to be able to pass in a
>> subroutine from a declared object.

> You mean a "method" (that is, a function that is "bound" to an object).

>> my $fft = new Math::FFT(\@nums);

>> print foo(\&{'fft->stdev'},\@nums);

> You want to use the UNIVERSAL::can method:

>   my $fft = Math::FFT->new(\@nums);
>   print foo($fft->can('stdev'), \@nums);

> perldoc UNIVERSAL

_The Perl Cookbook_, in the recipe from which I quoted earlier (11.8),
specifically considers this solution and has this to say:

"The 'can' method from the UNIVERSAL base class, while appealing, is
also unlikely to produce what you want.

   $cref = $obj->can( "meth" );

This produces a code ref to the appropriate method (should one be
found), one that carries *no* *object* *information*.  Think of it as
a raw function pointer.  The information about the object is lost.
That's why you need a closure to capture both the object state as well
as the method call."

(Emphasis is mine.)  I rewrote Todd's test FFT script to be:

#!/usr/bin/perl

use strict;
use warnings;

use Math::FFT;

my @nums = qw(3 4 5 7 10 8 7 9);
my $fft = new Math::FFT( \@nums );

# my $stdev = sub { $fft->stdev( @_ ) };
# print foo( $stdev, \@nums );

my $cref = $fft->can( "stdev" );
print foo( $cref, \@nums );

sub foo {
   my($f, @args) = @_;
   return $f->( @args );
}

When I ran this, it failed.  Here is the result of my running it under
Cygwin Perl:

$ ./ref.pl
Can't coerce array into hash at 
/usr/lib/perl5/site_perl/5.6.1/cygwin-multi/Math/FFT.pm line 500.

I don't entirely understand what this error means.  Here is the source
of the Math::FFT->stdev() method:

sub stdev {
  my $self = shift;
  my ($n, $data, $mean);
  if ($data = shift) {
    die 'Must call with an array reference'
      unless ref($data) eq 'ARRAY';
    $n = @$data;
    $mean = $self->mean($data);
  }
  else {
->  $data = $self->{data};
    $n = $self->{n};
    $mean = $self->{mean} || $self->mean;
  }
  die 'Cannot find the standard deviation with n = 1'
    if $n == 1;
  my $sum = 0;
  $sum += ($_ - $mean)*($_ - $mean) for @$data;
  return sqrt($sum / ($n-1));
}

In my version of the module (0.25), line 500 is the first line in the
'else' block (pointed to by the arrow).

What appears to be happening is this:

Since the function pointer returned by '->can()' does not relate to a
specific object, the implicit '$self' parameter is not pushed on to
the head of the argument list.  This means that the only argument to
'stdev()' is actually the array reference '\@nums'.  The first line of
the method thus sets '$self' to the array reference.  Since there
aren't any more args, the routine drops into the 'else' block.  The
first line fails because it's trying to use the array reference
('\@nums') stored in '$self' as a hash reference.

OK, now I *really* believe that TPC recipe. ;>

Todd: I hope that this isn't completely opaque and actually has some
meaning.  I believe that you really do need to use the closure
strategy (commented in my little example above) if you want to capture
the object's state as well as the method pointer.

Good luck!

---Jason
Sonos Handbell Ensemble
http://www.sonos.org/

P.S. - I get the impression that the '->can()' strategy would work if
you were trying to call a "static" method; i.e. one that didn't depend
on operating on a specific object instance and thus getting the
implicit '$self' first argument.


-- 
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to