pio*_*ojo 5 overloading hashset perl6
我的问题与集合操作中的用户定义函数有关,但我认为我可以切入问题的核心:
如何选择特定的散列函数?例如,如果我想进行基于值的匹配而不是引用匹配,并且我想查看某个元组是否存在(或者只是删除它):
my %data := SetHash.new: (1, 2), (3, 4);
%data{$(1, 2)}:delete; # False
Run Code Online (Sandbox Code Playgroud)
在C++或C#中,我可以为构造函数提供自定义哈希/比较函数.在C#中,如果我的数据类型是a struct
(值类型而不是引用类型),则会自动按值进行散列.Perl 6在某种程度上进行了值类型哈希Pair
(如果Pair不包含任何容器),但我不知道如何使它适用于任何其他复杂类型.
一方面,我明白为什么这不是最安全的操作 - 很容易定义哈希代码插入后可以更改的对象.但这并没有阻止.NET和C++ STL允许自定义散列.
一种可能的API使用(与由激发了链式散列逻辑此,最初来自升压)将是:
class MyHasher does Hasher of Array[Int] {
method get-hash-value(Int @array) {
reduce
-> $a, $b {$a +^ ($b + 0x9e3779b97f4a7c16 + ($a +< 6) + ($a +> 2))},
0,
|@array;
}
method equals(Int @a, Int @b) { @a eqv @b; }
}
my %data := SetHash.new(
my Int @=[1, 2], my Int @=[3, 4],
:hasher(MyHasher.new)
);
say %data{$[1, 2]}; # should be True
Run Code Online (Sandbox Code Playgroud)
这将是另一个角色,它应该由Perl 6的核心库提供,如果它还不存在的话:
role Hasher[::T=Any] { method equals(T $a, T $b --> Bool) { ... }; method get-hash-value(T $obj) { ... } }
Run Code Online (Sandbox Code Playgroud)
解决方案:目前,最合理的解决方案是覆盖类的.WHICH
方法,该方法用作哈希值并用于相等性测试.我举了一个哈希键类的例子,它在这里模拟了一个值类型.它几乎与每个哈希对象的自定义哈希函数一样通用,因为在创建哈希时可以声明键类型.(Set
由于Set
未参数化,因此无法完成此操作.)
\n\n
哈希的工作方式是使用键来存储值,并使用完全相同的键来检索该值。
\n\n对于 Str 和 Int 等值类型,您可以拥有多个实例,这些实例的作用就好像它们是完全相同的值一样。因此,即使它们不是同一实例,42
也要40\xc2\xa0+\xc2\xa02
表现得好像它们是完全相同的实例。
所以这有效:
\n\nmy %h{Any}; # defaults to Str keys\n\n%h{\'abc\'} = 42;\n\nmy ($a,$b,$c) = \'a\', \'b\', \'c\';\n\nsay %h{"$a $b $c"}; # 42\n\n%h{42} = \'The answer\';\n\nsay %h{"42"}; # (Any)\nsay %h{42}; # The answer\n
Run Code Online (Sandbox Code Playgroud)\n\n确实没有一种工具可以使多个不同的值假装为哈希值的相同值。
\n\n\'abc\' === \'cba\'; # False\n\n\'abc\'.WHICH eq \'cba\'.WHICH; # basically how the above works\n
Run Code Online (Sandbox Code Playgroud)\n\n我认为你所要求的是一个不应该添加的功能。
\n\n有一种WHICH
方法,只能用于使整个语言中各处的两个值相同。
say 42.WHICH.perl; # ValueObjAt.new("Int|42")\nsay (40 + 2).WHICH.perl; # ValueObjAt.new("Int|42")\n42 === (40 + 2); # True\n\nsay Hash.new.WHICH.perl; # ObjAt.new("Hash|94014087733456")\nsay Hash.new.WHICH.perl; # ObjAt.new("Hash|94014087735232")\n
Run Code Online (Sandbox Code Playgroud)\n\n请注意,Hash.new
它们不匹配,因为它们是不同的实例,可能会随着时间的推移而改变。
举个例子,这是一件好事。假设您有两名名为“Bob”的员工。
\n\nmy $a = Employee.new( name => \'Bob\' );\nmy $b = Employee.new( name => \'Bob\' );\n\nmy %salary{Employee};\n\n%salary{$a} = 1200; # arbitrary number for an example\n%salary{$b} = 2000;\n
Run Code Online (Sandbox Code Playgroud)\n\n请注意,通过覆盖该WHICH
方法,您最终可能会意外地加薪Bob\xc2\xa0$a
。
.WHICH
基本上,除非你确切地知道自己在做什么,并且有充分的理由,否则乱搞可能不是一个好主意。
所以你不能/不应该这样做。至少不是您尝试的方式。
\n\n相反,创建一个新的关联类,它按照您想要的方式工作。
\n\nrole Custom-Str-Hasher {\n method hashed ( --> Str:D ){\xe2\x80\xa6}\n}\n\nclass Custom-SetHash is SetHash {\n multi method AT-KEY ( Custom-Str-Hasher:D $key ) is rw {\n self.AT-KEY( $key.hashed() ); # call base class\'s method\n }\n}\n\n\nclass Foo does Custom-Str-Hasher {\n has Str:D $.Str is required;\n\n # required by the Custom-Str-Hasher role\n method hashed ( --> Str:D ){\n $!Str.comb(/\\w/).unique.sort.join;\n # \'b cb a\' \xe2\x86\x92 \'abc\' and \'aaababcccba\' \xe2\x86\x92 \'abc\'\n }\n}\n\nmy $a = Foo.new(:Str(\'b cb a\'));\nmy $b = Foo.new(:Str(\'aaababcccba\'));\n\nmy %h is Custom-SetHash; # use a different class than the default\n\n%h{$a} = True;\nsay %h{$b}; # True;\n\nput $a; # b cb a\nput $b; # aaababcccba\n
Run Code Online (Sandbox Code Playgroud)\n\n请注意,上面只是一个简单的示例,对于真实的示例,我会更改很多内容。其一,由于我实现该方法的方式,%h{\'abc\'}
也会返回。它还缺少一些像和 之类的方法。True
AT-KEY
ASSIGN-KEY
DELETE-KEY