【发布时间】:2017-12-10 04:56:56
【问题描述】:
我的问题与user defined function in set operations有关,但我想我可以切入问题的核心:
如何选择特定的哈希函数?例如,如果我想做基于值的匹配而不是引用匹配,并且我想查看某个元组是否存在(或者干脆删除它):
my %data := SetHash.new: (1, 2), (3, 4);
%data{$(1, 2)}:delete; # False
在 C++ 或 C# 中,我可以为构造函数提供自定义散列/比较函数。在 C# 中,如果我的数据类型是 struct(值类型而不是引用类型),则按值散列将自动发生。 Perl 6 在一定程度上为 Pair 进行值类型散列(如果 Pair 不包含任何容器),但我不知道如何使其适用于任何其他复杂类型。
一方面,我明白为什么这不是最安全的操作 - 很容易定义在插入后哈希码可以更改的对象。但这并没有阻止 .NET 和 C++ STL 允许自定义散列。
一种可能的 API 用法(由 this 启发的链式哈希逻辑,最初来自 Boost)是:
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
这将是散列器角色,它应该由 Perl 6 的核心库提供,如果它不存在的话:
role Hasher[::T=Any] { method equals(T $a, T $b --> Bool) { ... }; method get-hash-value(T $obj) { ... } }
解决方案:目前最合理的解决方案是重写一个类的.WHICH方法,该方法作为哈希值,用于相等性测试。我给出了一个模拟值类型here 的哈希键类的示例。它几乎与每个散列对象的自定义散列函数一样通用,因为可以在创建散列时声明键类型。 (对于 Set 不能这样做,因为 Set 没有参数化。)
【问题讨论】:
-
请注意,构造函数不能简单地重载,因为当前的设计是将所有参数都转换为集合元素,所以
:hasher(hash-logic)会被吞入数据结构中。 -
FWIW,
Set.new只接受位置,目前忽略命名参数:dd Set.new(:foo) # Set.new() -
就我目前所见,允许自定义哈希函数将是一项相当多的工作,并且在一般情况下会对性能产生负面影响。此外,我不完全确定
.classify(docs.perl6.org/routine/classify) 是否具有上述映射器,这不是您真正想要的。可以吗? -
感谢@ElizabethMattijsen 的更正。我有时会忘记传递一对和命名参数 [这是一对] 之间的区别。
-
请注意
.classify也有一个:into' 命名参数(显然似乎没有记录),它允许my %h = 97 => ["a"]; <b c d>.classify( *.ord, :into(%h) ); dd %h # Hash %h = {"100" => $["d"], "97" => $["a"], "98" => $["b"], "99" => $["c"]}。这应该使使用.classify作为Set的一种类型更容易。
标签: overloading hashset raku