Re: Python 'is' command
by AnomalousMonk (Archbishop) on Aug 14, 2019 at 16:04 UTC
|
c:\@Work\Perl\monks>perl -wMstrict -le
"my $ar_a = [ qw(a b c d) ];
my $ar_b = $ar_a;
;;
print 0+$ar_a;
print 0+$ar_b;
;;
print 'is equal' if $ar_a == $ar_b;
"
22632908
22632908
is equal
This works due to numeric coercion of a reference.
Give a man a fish: <%-{-{-{-<
| [reply] [d/l] [select] |
Re: Python 'is' command
by haukex (Archbishop) on Aug 14, 2019 at 20:14 UTC
|
I am assuming that you can do exactly the same in Perl using a ref command in conjunction with '=='.
Not quite, since ref returns the type of the reference, not its address. You'll need Scalar::Util's refaddr for the latter. By default, Perl references used in numeric context will return the memory address (perlref), however, if the Perl objects use operator overloading, this is no longer true - see my example here.
If you look at the Python docs, you'll see that if the class defines a __eq__ method, it is used for == comparisons, and the fallback is to check if it's the same object, i.e. it's basically the same behavior as Perl's operator overloading. Python's is is basically what BillKSmith implemented here (and it's the only "entirely correct" answer in this thread).
Note that the general concept of "does one object equal another" is actually a lot more difficult than it sounds. When are two database handles equal - when they connect to the same database, or they have the same state, and so on? When are two objects loaded through an ORM like DBIx::Class equal - when their primary key is equal, or when all of their fields are equal, and so on? In such cases it's easier to code a custom comparator instead of the class attempting to provide a sensible default.
| [reply] [d/l] [select] |
Re: Python 'is' command
by BillKSmith (Monsignor) on Aug 14, 2019 at 17:00 UTC
|
My 'is' function is overkill for my test case, but it demonstrates the functionality you describe.
use strict;
use warnings;
use Test::Simple tests => 2;
sub is {
use Scalar::Util qw(refaddr);
return (refaddr $_[0] == refaddr $_[1]);
}
my $x = 3;
my $y = 3;
my $x_ref = \$x;
my $test_ref_1 = \$x;
my $test_ref_2 = \$y;
ok( is($test_ref_1, $x_ref), 'Same' );
ok( !is($test_ref_2, $x_ref), 'Different' );
| [reply] [d/l] |
|
c:\@Work\Perl\monks>perl
use strict;
use warnings;
use Test::Simple tests => 4;
1..4
sub is { $_[0] == $_[1] }
my $x = 3;
my $y = 3;
my $x_ref = \$x;
my $test_ref_1 = \$x;
my $test_ref_2 = \$y;
ok( is($test_ref_1, $x_ref), 'is Same' );
ok( !is($test_ref_2, $x_ref), 'is Different' );
ok( $test_ref_1 == $x_ref, '==' );
ok( $test_ref_2 != $x_ref, '!=' );
__END__
ok 1 - is Same
ok 2 - is Different
ok 3 - ==
ok 4 - !=
Give a man a fish: <%-{-{-{-<
| [reply] [d/l] [select] |
|
use warnings;
use strict;
package Foo {
my $x;
use overload '<=>'=>sub{1}, 'cmp'=>sub{1}, '0+'=>sub{++$x};
}
my $x = bless {}, 'Foo';
my $y = $x;
print $x==$y ? "True\n" : "False\n"; # False
use Scalar::Util qw/refaddr/;
sub is { return refaddr $_[0] == refaddr $_[1] }
print is($x,$y) ? "True\n" : "False\n"; # True
| [reply] [d/l] [select] |
|
|
|
Your comment clarifies exactly what I meant. However, the documentation of the refaddr function in Scalar::Util assures us that our operatores are comparing addresses. The user documentation for references (perlref) makes no such claim.
| [reply] |
|
| [reply] [d/l] [select] |
Re: Python 'is' command
by Fletch (Bishop) on Aug 14, 2019 at 13:25 UTC
|
Actually for references '==' is comparing for the same instance (referential equality). For recent enough perls the smartmatch ~~ operator can do at least a first level check for structural or object equality. If you want to compare if they more deeply nested structures contain the same contents you need do more work; see the FAQ "How do I test whether two arrays or hashes are equal?".
$ perl -MTest::More -dE 0
[...]
DB<1> $a = { qw/a 1 b 2/ }
DB<2> $b = { qw/a 1 b 2/ }
DB<3> x $a
0 HASH(0x7f9140905ad0)
'a' => 1
'b' => 2
DB<4> x $b
0 HASH(0x7f914090b698)
'a' => 1
'b' => 2
DB<5> x $a == $b
0 ''
DB<6> x is_deeply( $a, $b )
ok 1
0 1
DB<7> x is_deeply( $a, {} )
not ok 2
# Failed test at /Users/nbkawb9/perl5/lib/perl5/Test/Builder.pm line
+ 152.
# Structures begin differing at:
# $got->{a} = '1'
# $expected->{a} = Does not exist
0 0
DB<8> q
Additionally: For real fun check out Lisp which has several predicates for different amounts of equality.
Edit: Tweaked slightly ambiguous "they" to more explicit wording.
The cake is a lie.
The cake is a lie.
The cake is a lie.
| [reply] [d/l] [select] |
|
For recent enough perls the smartmatch ~~ operator
Smart matching was retroactively documented as experimental since v5.16 (2012), and once in a while there are discussions on P5P on completely overhauling its behavior. I wouldn't recommend it, as code relying on it may break on newer versions of Perl. As of v5.18 using it will generate warnings about its experimental status.
I also mentioned that the behavior of == can be changed with operator overloading in my other replies in this thread.
| [reply] [d/l] [select] |
Re: Python 'is' command
by Jenda (Abbot) on Aug 15, 2019 at 17:58 UTC
|
It helps to use correct terminology. It's not a command, it's an operator. Also the programming is object oriented.
I can't decipher the "It is a classic case of where the theory overrides the non requirement at the code level." which is maybe why I don't see what it is Python is supposed to have right.
Jenda
1984 was supposed to be a warning,
not a manual!
| [reply] |
Re: Python 'is' command
by syphilis (Archbishop) on Aug 16, 2019 at 07:42 UTC
|
Hi betmatt,
Not sure how well this would fit the bill:
use strict;
use warnings;
use Math::BigInt; # for demo
use Inline C => Config =>
BUILD_NOISY => 1,
;
use Inline C => <<'EOC';
int is(SV * a, SV * b) {
if(a == b) return 1;
if(SvROK(a) && SvROK(b)) {
if(SvRV(a) == SvRV(b)) return 2;
}
return 0;
}
EOC
my $x = 15;
my $y = 14;
my $r1 = \$x;
my $r2 = \$x;
my $r3 = \$y;
my $m1 = Math::BigInt->new(7);
my $m2 = $m1;
my $m3 = Math::BigInt->new(7);
print is($x, $y), "\n";
print is($r1, $r2), "\n";
print is($r1, $r1), "\n";
print is($r1, $r3), "\n";
print is($m1, $m2), "\n";
print is($m3, $m2), "\n";
Cheers, Rob | [reply] [d/l] |
Re: Python 'is' command
by kcott (Archbishop) on Aug 15, 2019 at 08:57 UTC
|
G'day betmatt,
"... do exactly the same in Perl using a ref command in conjunction with '=='."
Depending on context, it may be worthwhile using ref
to ensure you're actually comparing references; however, it's not needed for the comparison.
Instead of ==, I'd probably choose eq and do the comparison test like this:
$ref1 eq $ref2
Here's a short example:
$ perl -E '
my @x = ({}, []);
my $y = $x[0];
say for @x, $y;
say $_->[0] eq $_->[1] ? "Y" : "N" for [@x], [$x[0], $y];
'
HASH(0x600003a70)
ARRAY(0x600076050)
HASH(0x600003a70)
N
Y
| [reply] [d/l] [select] |
|
| [reply] [d/l] [select] |
|
| [reply] |
Re: Python 'is' command
by daxim (Curate) on Aug 16, 2019 at 10:28 UTC
|
Are there any other examples like this that I can follow?
Yes! See the comprehensive is_equal method in perl5i::Meta (source). That's what people usually have in mind when they talk about equality.
All the solutions shown in this thread involving references are not general enough due to wrong assumptions, see FAQ in Object::ID. (I think the original Python behaviour also suffers from the problem, but I haven't checked.) Again, the correct solution is also part of perl5i (source).
perl5i is full of "done right" pieces of code. Monks, study it, even if you never intend to run it.
| [reply] [d/l] [select] |
|
All the solutions shown in this thread involving references are not general enough due to wrong assumptions, see FAQ in Object::ID.
Really ? ... *all* of them ?
I think the OP is concerned about determining whether 2 existent objects are the same, not whether a current object has the same "address" as an obsolete one.
So long as 2 reference addresses of currently existent objects are being compared, then I don't really see the relevance of the FAQ in Object::ID.
Cheers, Rob
| [reply] |
|
> see FAQ in Object::ID
Interesting but not relevant here.
The OP wants to compare two existing objects with an is operator.
References can't be reused if the objects haven't been garbage collected yet.
On a side note: why should one want to keep track of numeric or string representations of ref-adresses?
Keeping the original ref in a hash value would keep it protected from destruction.
| [reply] [d/l] |