加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

重载 – 在Perl 6中使用自定义散列函数设置/散列

发布时间:2020-12-15 23:33:17 所属栏目:大数据 来源:网络整理
导读:我的问题与 user defined function in set operations有关,但我认为我可以切入问题的核心: 如何选择特定的散列函数?例如,如果我想进行基于值的匹配而不是引用匹配,并且我想查看某个元组是否存在(或者只是删除它): my %data := SetHash.new: (1,2),(3,4);%
我的问题与 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这样的方法.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读