in reply to Type checker not checking out

I would attack this problem differently, by use of higher-level functions (predicates).

Like this:

#!/usr/bin/perl -w use strict; sub map_recursive { my ($structure, $predicate) = @_; my @result; if (UNIVERSAL::isa($structure, 'ARRAY')) { for (@{$structure}) { push @result, map_recursive($_, $predicate); } } elsif (UNIVERSAL::isa($structure, 'HASH')) { for (keys %{$structure}) { push @result, map_recursive($structure->{$_}, $predicate); } } else { push @result, $predicate->($structure); } return @result; } my $scalar = 2; my @list = (1, 3, 8, 10); my %hash = (a => 1, b => 2, c => 3); my $oddp = sub { (((shift) % 2) == 1) ? 1 : 0 }; print "#1: ", join(",", map_recursive($scalar, $oddp)), "\n"; print "#1: ", join(",", map_recursive(\@list, $oddp)), "\n"; print "#1: ", join(",", map_recursive(\%hash, $oddp)), "\n";

Then you can hide the complexity of actually checking the type in the predicate.

Christian Lemburg
Brainbench MVP for Perl
http://www.brainbench.com

Replies are listed 'Best First'.
Re: Re: Type checker not checking out
by premchai21 (Curate) on Aug 05, 2001 at 07:31 UTC
    Hmm... but what if I need to check for, say, an array of objects of a certain type? Suppose I do this:
    package Foo; sub new { bless {}, shift } sub get { $_[0]->{$_[1]} } sub set { $_[0]->{$_[1]} = $_[2] } package main; my @list = (Foo->new, Foo->new, Foo->new); $list[0]->set('a', 'b'); $list[1]->set('b', 'a'); $list[2]->set('x', 'y'); my $isa_foo = sub { UNIVERSAL::isa(shift, 'Foo') }; print join ',', map_recursive(\@list, $isa_foo);
    This doesn't work, because map_recursive will descend an extra level into the Foo structure itself, and give me intermingled 1s and 0s which have no meaning to me or my program whatsoever.

    It descends into the Foo, finding it to be a hash, and declares 0 on 'a', 'b', and 'y', since they aren't Foos. Or:

    my @list2 = ( {a => 'm'}, { 1 => 55}, { Foo => 'Bar' }); my $h = sub { UNIVERSAL::isa(shift, 'HASH') }; print join ',', map_recursive (\@list2, $h);
    And here I'm looking to check for an array of hashes. But oh my, 55 isn't a hash, so this gets disqualified. You see now why map_recursive won't do; the format I'm using allows one to specify the whole structure.

    However, it doesn't quite work yet...

    Update: clemburg's solution below works better than the one above. I think I'll probably use that. Thanks you, clemburg.

      Easy. Change map_recursive() to not descend into recognized structures. That is probably the more sensible default behavior, agreed.

      #!/usr/bin/perl -w use strict; sub map_recursive { my ($structure, $predicate) = @_; # don't descend into recognized structures if ($predicate->($structure)) { return $predicate->($structure); } # not recognized - pick apart my @result; if (UNIVERSAL::isa($structure, 'ARRAY')) { for (@{$structure}) { push @result, map_recursive($_, $predicate); } } elsif (UNIVERSAL::isa($structure, 'HASH')) { for (keys %{$structure}) { push @result, map_recursive($structure->{$_}, $predicate); } } else { push @result, $predicate->($structure); } return @result; } package Foo; sub new { bless {}, shift } sub get { $_[0]->{$_[1]} } sub set { $_[0]->{$_[1]} = $_[2] } package main; my @list = (Foo->new, Foo->new, Foo->new); $list[0]->set('a', 'b'); $list[1]->set('b', 'a'); $list[2]->set('x', 'y'); my $isa_foo = sub { UNIVERSAL::isa(shift, 'Foo') }; print "#1: ", join(',', map_recursive(\@list, $isa_foo)), "\n"; my @list2 = ( {a => 'm'}, { 1 => 55}, { Foo => 'Bar' }); my $h = sub { UNIVERSAL::isa(shift, 'HASH') }; print "#2: ", join(',', map_recursive (\@list2, $h)), "\n";

      Christian Lemburg
      Brainbench MVP for Perl
      http://www.brainbench.com