Do you have a question? Post it now! No Registration Necessary. Now with pictures!
- Subject
- Posted on
- Equality of hashref objects
- 02-04-2005
posted on
February 4, 2005, 11:32 pm
February 4, 2005, 11:32 pm
I have a class that is implemented as a hashref, and furthermore,
the values for the corresponding hash are always scalars. I want
a sub to test for equality between any two instances of this class,
defined roughly as "two instances $x and $y are equal if $x->{
$field } and $y->{ $field } are equal for all instance fields
$field". I took a crack at this function, but it is so *ugly* that
I'm sure there must be a better way. I give my version below for
your mocking pleasure. Any pointers to a better implementation
would be much appreciated.
bill
# ...
BEGIN { # private subs
$_equals = sub {
my @args = @_[0, 1];
for my $field (@_fields) { # @_fields is defined earlier
my @vals = map $_->$field, @args;
my $n_defined = grep defined $_, @vals;
next if $n_defined == 0;
return 0 if $n_defined == 1;
if (2 == grep $_is_numeric($_), @vals) {
return 0 unless $vals[0] == $vals[1];
}
else {
return 0 unless $vals[0] eq $vals[1];
}
}
return 1;
};
$_is_numeric = sub {
# cribbed from a clpm post by Anno Siegel
use warnings 'all', FATAL => 'numeric';
return defined eval { $_[0] == 0 };
};
}
use overload '==' => $_equals, fallback => 1;
# ...
Re: Equality of hashref objects
> I have a class that is implemented as a hashref, and furthermore,
> the values for the corresponding hash are always scalars. I want
> a sub to test for equality between any two instances of this class,
> defined roughly as "two instances $x and $y are equal if $x->{
> $field } and $y->{ $field } are equal for all instance fields
> $field".
If you have two hash references (say, $hashRef1 and $hashRef2), you
might be able to pull it off this way:
use Data::Dumper; # standard module; you should have it
print "Equal\n" if Dumper($hashRef1) eq Dumper($hashRef2);
I'm not positive that Dumper will return all the entries in the same
order for two equivalent hashes, so this MIGHT not work.
However, it still might be worth a shot. Try it out and see what you
get.
I hope this helps.
-- Jean-Luc
Re: Equality of hashref objects
> I have a class that is implemented as a hashref, and furthermore,
> the values for the corresponding hash are always scalars. I want
> a sub to test for equality between any two instances of this class,
> defined roughly as "two instances $x and $y are equal if $x->{
> $field } and $y->{ $field } are equal for all instance fields
> $field". I took a crack at this function, but it is so *ugly* that
> I'm sure there must be a better way. I give my version below for
> your mocking pleasure. Any pointers to a better implementation
> would be much appreciated.
>
> bill
[ snipped ]
Look at the is_deeply function in Test::More,
or the functions from Test::Deep on CPAN. See
if they offer what you need.
--
Hope this helps,
Steven
Re: Equality of hashref objects
> I have a class that is implemented as a hashref, and furthermore,
> the values for the corresponding hash are always scalars. I want
> a sub to test for equality between any two instances of this class,
> defined roughly as "two instances $x and $y are equal if $x->{
> $field } and $y->{ $field } are equal for all instance fields
> $field". I took a crack at this function, but it is so *ugly* that
> I'm sure there must be a better way. I give my version below for
> your mocking pleasure. Any pointers to a better implementation
> would be much appreciated.
Data::Compare
> if (2 == grep $_is_numeric($_), @vals) {
> return 0 unless $vals[0] == $vals[1];
> }
> else {
> return 0 unless $vals[0] eq $vals[1];
> }
> $_is_numeric = sub {
> use warnings 'all', FATAL => 'numeric';
> return defined eval { $_[0] == 0 };
> };
This has the effect of treating the strings '0' and '0000000000' as the
same. Is that what you wanted? What you you percieve as the problem
if you just use eq all the time?
Re: Equality of hashref objects
>bill wrote:
>> I have a class that is implemented as a hashref, and furthermore,
>> the values for the corresponding hash are always scalars. I want
>> a sub to test for equality between any two instances of this class,
>> defined roughly as "two instances $x and $y are equal if $x->{
>> $field } and $y->{ $field } are equal for all instance fields
>> $field". I took a crack at this function, but it is so *ugly* that
>> I'm sure there must be a better way. I give my version below for
>> your mocking pleasure. Any pointers to a better implementation
>> would be much appreciated.
>Data::Compare
>> if (2 == grep $_is_numeric($_), @vals) {
>> return 0 unless $vals[0] == $vals[1];
>> }
>> else {
>> return 0 unless $vals[0] eq $vals[1];
>> }
>> $_is_numeric = sub {
>> use warnings 'all', FATAL => 'numeric';
>> return defined eval { $_[0] == 0 };
>> };
>This has the effect of treating the strings '0' and '0000000000' as the
>same. Is that what you wanted?
Yes, that was the intent.
>What you you percieve as the problem
>if you just use eq all the time?
In this application it is far more likely for a "numeric" field to
hold, say, '1.0' in one instance and '1.00' in another, than it is
for a "string" field to hold a value for which $_is_numeric would
return true. In fact, I am debating whether to change
return 0 unless $vals[0] == $vals[1];
to
return 0 unless abs($vals[0], $vals[1]) < $EPSILON;
for some tiny $EPSILON.
bill
Re: Equality of hashref objects
>
> >bill wrote:
>
> >> I have a class that is implemented as a hashref, and furthermore,
> >> the values for the corresponding hash are always scalars. I want
> >> a sub to test for equality between any two instances of this class,
> >> defined roughly as "two instances $x and $y are equal if $x->{
> >> $field } and $y->{ $field } are equal for all instance fields
> >> $field". I took a crack at this function, but it is so *ugly* that
> >> I'm sure there must be a better way. I give my version below for
> >> your mocking pleasure. Any pointers to a better implementation
> >> would be much appreciated.
>
> >Data::Compare
>
> >> if (2 == grep $_is_numeric($_), @vals) {
> >> return 0 unless $vals[0] == $vals[1];
> >> }
> >> else {
> >> return 0 unless $vals[0] eq $vals[1];
> >> }
>
> >> $_is_numeric = sub {
> >> use warnings 'all', FATAL => 'numeric';
> >> return defined eval { $_[0] == 0 };
> >> };
>
> >This has the effect of treating the strings '0' and '0000000000' as the
> >same. Is that what you wanted?
>
> Yes, that was the intent.
>
> >What you you percieve as the problem
> >if you just use eq all the time?
>
> In this application it is far more likely for a "numeric" field to
> hold, say, '1.0' in one instance and '1.00' in another, than it is
> for a "string" field to hold a value for which $_is_numeric would
> return true. In fact, I am debating whether to change
Then ->new (or whatever) should take care that numeric fields are numeric
(by adding 0). Strings should also be normalized, if applicable. Usually.
> return 0 unless $vals[0] == $vals[1];
>
> to
>
> return 0 unless abs($vals[0], $vals[1]) < $EPSILON;
>
> for some tiny $EPSILON.
This sounds more and more like the wholesale approach "compare two hashes"
isn't the right one here. For general OO reasons, comparison should be
based on accessors, not on "wild" access to the underlying hash structure.
That some fields appear to need particular comparison routines is another
pointer in that direction. I'd say, the somewhat tedious
sub is_equal {
my ( $x, $y) = @_;
$x->some_field eq $y->some_field and
$x->other_field == $y->other_field and
abs( $x->third_field - $y->third_field) < EPS and
# ...
}
is the way to go. If there are very many fields some automation is
still possible.
Anno
> >bill wrote:
>
> >> I have a class that is implemented as a hashref, and furthermore,
> >> the values for the corresponding hash are always scalars. I want
> >> a sub to test for equality between any two instances of this class,
> >> defined roughly as "two instances $x and $y are equal if $x->{
> >> $field } and $y->{ $field } are equal for all instance fields
> >> $field". I took a crack at this function, but it is so *ugly* that
> >> I'm sure there must be a better way. I give my version below for
> >> your mocking pleasure. Any pointers to a better implementation
> >> would be much appreciated.
>
> >Data::Compare
>
> >> if (2 == grep $_is_numeric($_), @vals) {
> >> return 0 unless $vals[0] == $vals[1];
> >> }
> >> else {
> >> return 0 unless $vals[0] eq $vals[1];
> >> }
>
> >> $_is_numeric = sub {
> >> use warnings 'all', FATAL => 'numeric';
> >> return defined eval { $_[0] == 0 };
> >> };
>
> >This has the effect of treating the strings '0' and '0000000000' as the
> >same. Is that what you wanted?
>
> Yes, that was the intent.
>
> >What you you percieve as the problem
> >if you just use eq all the time?
>
> In this application it is far more likely for a "numeric" field to
> hold, say, '1.0' in one instance and '1.00' in another, than it is
> for a "string" field to hold a value for which $_is_numeric would
> return true. In fact, I am debating whether to change
Then ->new (or whatever) should take care that numeric fields are numeric
(by adding 0). Strings should also be normalized, if applicable. Usually.
> return 0 unless $vals[0] == $vals[1];
>
> to
>
> return 0 unless abs($vals[0], $vals[1]) < $EPSILON;
>
> for some tiny $EPSILON.
This sounds more and more like the wholesale approach "compare two hashes"
isn't the right one here. For general OO reasons, comparison should be
based on accessors, not on "wild" access to the underlying hash structure.
That some fields appear to need particular comparison routines is another
pointer in that direction. I'd say, the somewhat tedious
sub is_equal {
my ( $x, $y) = @_;
$x->some_field eq $y->some_field and
$x->other_field == $y->other_field and
abs( $x->third_field - $y->third_field) < EPS and
# ...
}
is the way to go. If there are very many fields some automation is
still possible.
Anno
Site Timeline
- » FAQ 1.3 Which version of Perl should I use?
- — Next thread in » PERL Discussions
- » FAQ 4.62 Why don't my tied hashes make the defined/exists distinction?
- — Previous thread in » PERL Discussions
- » s suffix question
- — Newest thread in » PERL Discussions
- » Dell Battery Slice LED codes
- — The site's Newest Thread. Posted in » Laptop Computers Forum