Perl 6元素成员判断机制解析及列表匹配问题
解决Raku中列表集合的元素匹配问题
好问题!这确实是Raku(原Perl 6)里容易踩的一个「对象标识 vs 值相等」的坑,咱们一步步来拆解你的疑问:
为什么相同字面量生成不同对象?
Raku里的List是不可变的容器对象——每次你写出(1,1)这样的字面量,解释器都会在内存里创建一个全新的List实例。哪怕两个列表的内容完全一样,它们的对象标识(可以理解为内存地址)是不同的。
===运算符是用来判断对象标识是否完全一致的,也就是检查两个变量是不是指向同一个内存对象,所以(1,1).List === (1,1).List返回False。==运算符对于列表来说,会逐元素进行数值相等判断,所以(1,1) == (1,1)返回True。
默认的∈运算符正是基于===的对象标识逻辑来判断「元素是否存在」的,这就是为什么你直接用独立生成的(1,1)判断会返回False,而引用@fib[0](同一个对象)会返回True。
怎么用类似∈的逻辑判断独立生成的列表是否属于集合?
核心是把判断逻辑从对象标识换成值/结构相等,Raku里用eqv运算符来判断两个结构(比如列表)的内容和嵌套结构是否完全一致((1,1) eqv (1,1)返回True)。你可以用以下几种方式实现:
1. 用any结合eqv模拟∈
my @fib = (1,1, * + * … * > 200).rotor(2 => -1); say any(@fib) eqv (1,1); # 输出 True
any(@fib)会生成一个 Junction 对象,和目标列表用eqv比较时,只要有一个元素匹配就返回True,效果和∈类似。
2. 自定义一个基于值相等的包含运算符
如果想更贴近∈的语法,可以自己实现一个运算符:
sub infix:<∈=> ($elem, $coll) { any($coll) eqv $elem } my @fib = (1,1, * + * … * > 200).rotor(2 => -1); say (1,1) ∈=> @fib; # 输出 True
3. 用grep配合eqv检查
my @fib = (1,1, * + * … * > 200).rotor(2 => -1); say @fib.grep(* eqv (1,1)).Bool; # 输出 True
grep会过滤出所有和目标列表值相等的元素,再用.Bool判断是否存在匹配项。
有没有其他策略?
如果需要频繁进行这类值匹配检查,可以提前把集合转换成支持值相等判断的结构:
使用自定义哈希逻辑的SetHash
SetHash允许你自定义哈希和相等判断逻辑,我们可以指定用eqv来处理:
use SetHash; my @fib = (1,1, * + * … * > 200).rotor(2 => -1); my $fib-set = SetHash.new(:hash(&[eqv]), :identity(&[eqv])); $fib-set.add($_) for @fib; say $fib-set ∋ (1,1); # 输出 True
这样后续的包含检查就可以直接用∋(反向包含运算符),和原生∈的体验一致,而且是基于值相等的。
内容的提问来源于stack exchange,提问作者jjmerelo
相关产品推荐
相关产品推荐

