如何在Perl中不强制类型转换比较对象?需多版本解决方案
嘿,刚好能帮你解决这个Perl里严格类型相等的问题!你想要的是那种既检查值、又检查类型的严格比较,对吧?默认的==(数值比较)、eq(字符串比较)、~~(智能匹配)都会做自动类型转换,肯定满足不了你的需求,下面分不同Perl版本给你解决方案:
核心思路
你需要的是双重校验:先确认两个值的类型完全一致,再比较它们的值是否相等。对于对象来说,还要额外检查实例类型或内存地址。
Perl 5.8及以上(含5.10+、现代Perl)
普通标量的严格比较
最方便的方式是借助核心模块Scalar::Util里的looks_like_number函数,先判断两个值是否同属数字/非数字类型,再用对应方式比较值:
use Scalar::Util 'looks_like_number'; sub strict_equals { my ($val1, $val2) = @_; # 先判断类型是否一致:都是数字 或 都不是数字 return 0 unless looks_like_number($val1) == looks_like_number($val2); # 根据类型选择比较逻辑 if (looks_like_number($val1)) { return $val1 == $val2; # 数字用数值比较 } else { return $val1 eq $val2; # 字符串用字符串比较 } }
测试一下你要的场景:
strict_equals(1, 1)→ 返回1(相等)strict_equals("1", "1")→ 返回1(相等)strict_equals(1, "1")→ 返回0(类型不同)strict_equals("1", "01")→ 返回0(字符串值不同)
如果需要更精准的类型判断(比如区分整数、浮点数、纯字符串),可以用B模块直接读取Perl标量的内部类型标记:
use B; sub get_scalar_type { my $sv = B::svref_2object(\$_[0]); if ($sv->FLAGS & (B::SVp_IOK | B::SVp_NOK)) { return 'number'; } elsif ($sv->FLAGS & B::SVp_POK) { return 'string'; } else { return 'undef'; } } sub strict_equals { my ($val1, $val2) = @_; my $type1 = get_scalar_type($val1); my $type2 = get_scalar_type($val2); return 0 unless $type1 eq $type2; if ($type1 eq 'number') { return $val1 == $val2; } elsif ($type1 eq 'string') { return $val1 eq $val2; } else { return !defined $val1 && !defined $val2; } }
对象的严格比较
对象本质是引用,所以分两种场景:
- 判断是否是同一个实例:直接用
==比较,因为引用的==是检查内存地址是否相同:if ($obj1 == $obj2) { print "是同一个对象实例\n"; } - 判断同类型且内容相等:先检查两个对象的类是否一致,再自定义
equals方法做属性的严格校验:package MyClass; use Scalar::Util 'looks_like_number'; sub new { my ($class, $data) = @_; bless { data => $data }, $class; } sub equals { my ($self, $other) = @_; # 先检查是否是同一类的实例 return 0 unless ref($self) eq ref($other); # 对属性做严格比较 return strict_equals($self->{data}, $other->{data}); } # 复用之前的strict_equals函数 sub strict_equals { my ($val1, $val2) = @_; return 0 unless looks_like_number($val1) == looks_like_number($val2); return looks_like_number($val1) ? ($val1 == $val2) : ($val1 eq $val2); }
Perl 5.6及更早(老旧版本)
如果你的Perl版本非常老,Scalar::Util可能不是默认安装的,那可以用正则表达式来判断数字(虽然不如looks_like_number准确,但能覆盖大部分场景):
sub is_number { my $val = shift; return $val =~ /^-?\d+(?:\.\d+)?$/; # 简单匹配整数和浮点数 } sub strict_equals { my ($val1, $val2) = @_; my $is_num1 = is_number($val1); my $is_num2 = is_number($val2); return 0 unless $is_num1 == $is_num2; return $is_num1 ? ($val1 == $val2) : ($val1 eq $val2); }
对象比较的逻辑和上面一致,因为引用的==行为在Perl 5里是一直稳定的。
内容的提问来源于stack exchange,提问作者Pavel
相关产品推荐
相关产品推荐

