重载 – 在Perl 6中使用自定义散列函数设置/散列
我的问题与
user defined function in set operations有关,但我认为我可以切入问题的核心:
如何选择特定的散列函数?例如,如果我想进行基于值的匹配而不是引用匹配,并且我想查看某个元组是否存在(或者只是删除它): my %data := SetHash.new: (1,2),(3,4); %data{$(1,2)}:delete; # False 在C或C#中,我可以为构造函数提供自定义哈希/比较函数.在C#中,如果我的数据类型是结构(值类型而不是引用类型),则会自动发生按值散列. 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))},|@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执行此操作.) 解决方法
Hash的工作方式是使用键来存储值,并使用完全相同的键来检索值.
对于像Str和Int这样的值类型,您可以拥有多个实例,这些实例就像它们是完全相同的值一样.因此42和40 2就好像它们是完全相同的实例,即使它们不是. 这样可行: my %h{Any}; # defaults to Str keys %h{'abc'} = 42; my ($a,$b,$c) = 'a','b','c'; say %h{"$a $b $c"}; # 42 %h{42} = 'The answer'; say %h{"42"}; # (Any) say %h{42}; # The answer 实际上并没有一种设施可以使几个不同的值假装为Hash的相同值. 'abc' === 'cba'; # False 'abc'.WHICH eq 'cba'.WHICH; # basically how the above works 我认为你要求的是一个不应该添加的功能. 有一种WHICH方法,它只能用于使整个语言中的两个值相同. say 42.WHICH.perl; # ValueObjAt.new("Int|42") say (40 + 2).WHICH.perl; # ValueObjAt.new("Int|42") 42 === (40 + 2); # True say Hash.new.WHICH.perl; # ObjAt.new("Hash|94014087733456") say Hash.new.WHICH.perl; # ObjAt.new("Hash|94014087735232") 请注意,对于Hash.new,它们不匹配,因为它们是可以随时间变化的不同实例. 作为一个例子,这是一件好事.假设你有两个名叫’Bob’的员工. my $a = Employee.new( name => 'Bob' ); my $b = Employee.new( name => 'Bob' ); my %salary{Employee}; %salary{$a} = 1200; # arbitrary number for an example %salary{$b} = 2000; 请注意,通过覆盖WHICH方法,您最终可能会意外地给Bob $加注. 基本上,除非你确切地知道自己在做什么,否则你可能不是一个好主意.WHICH你有一个非常好的理由. 所以你不能/不应该这样做.至少不是你想要的方式. 而是创建一个新的Associative类,它以您想要的方式工作. role Custom-Str-Hasher { method hashed ( --> Str:D ){…} } class Custom-SetHash is SetHash { multi method AT-KEY ( Custom-Str-Hasher:D $key ) is rw { self.AT-KEY( $key.hashed() ); # call base class's method } } class Foo does Custom-Str-Hasher { has Str:D $.Str is required; # required by the Custom-Str-Hasher role method hashed ( --> Str:D ){ $!Str.comb(/w/).unique.sort.join; # 'b cb a' → 'abc' and 'aaababcccba' → 'abc' } } my $a = Foo.new(:Str('b cb a')); my $b = Foo.new(:Str('aaababcccba')); my %h is Custom-SetHash; # use a different class than the default %h{$a} = True; say %h{$b}; # True; put $a; # b cb a put $b; # aaababcccba 请注意,上面只是一个简单的例子,我会为一个真实的例子改变一些东西.首先,%h {‘abc’}也会返回True,因为我实现了AT-KEY方法.它也缺少像ASSIGN-KEY和DELETE-KEY这样的方法. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |