use*_*601 8 hash immutability perl6
我正在创建一个具有大量嵌套的哈希值的模块。哈希值需要由模块进行半规则修改,不幸的是,该规则排除了使用Map。
通常,嵌套哈希的分支将返回给模块[1]的用户,最简单的方法是仅返回该嵌套哈希,例如:
return %data{$branch}{$subbranch}
# ?? %(subsubbranch1 => ... , subsubbranch2 => ... )
Run Code Online (Sandbox Code Playgroud)
但是,容器(例如数组或哈希)的性质是,尽管您可以将它们设置为只读,但仍可以修改键/值。但是,出于多种原因,模块用户实际上不应修改这些值。强制Map将无济于事,因为如果任何值也是容器,它们也将是可修改的。
我的第一个想法是继承Hash(或以其他方式进行自定义Associative),但是默认情况下,自动生存仍然归哈希所有。然而,这可以很容易地通过重写都解决AT-KEY,并ASSIGN-KEY使得AT-KEY收益子类的实例,如果该键不存在:
class ProtectedHash is Hash {
has %!hash = ();
method EXISTS-KEY ($key) { %!hash{$key}:exists }
method ASSIGN-KEY ($key, \value) { %!hash{$key} = value }
method AT-KEY ($key) {
%!hash{$key} := ProtectedHash.new unless %!hash{$key}:exists;
%!hash{$key};
}
}
Run Code Online (Sandbox Code Playgroud)
我想做的是如果从我的模块外部调用ASSIGN-KEY(或的自动生存部分AT-KEY)失败。我曾考虑过使用$?MODULE之类的东西,但这将在编译时设置,并且始终是真实的。看起来我可以稍微回移Backtrace并检查被调用文件的名称,但是我可以假设对这两个函数的调用跟踪的一致性如何?
例如,因为ASSIGN-KEY我有:
method ASSIGN-KEY ($key, \value) {
my @trace = Backtrace.new.list[3..*];
# The first three can be ignored:
# 0: code at ...Backtrace.pm6
# 1: method new at ...Backtrace.pm6
# 2: method AT-KEY at ...ThisFile.pm6
if/unless ??? {
%!hash{$key} = value
}
}
Run Code Online (Sandbox Code Playgroud)
AT-KEY通常由sub调用postcircumfix<{ }>(在这种情况下@trace[0]可以忽略,并且trace[1]将是感兴趣的那个),但是也可以直接调用(尽管很少见),在这种情况下trace[0],我想验证文件名。
是否有任何其他常见的方式在其中AT-KEY或ASSIGN-KEY可能被称为?还是应该检查这两个步骤占这些方法调用的99.9%?[2]
[1]用户可能只想操纵几个subx4分支,因此我认为最好.Hash是在他们真正需要它时为他们提供必要的慢速方法,而不是假设他们总是需要一个可操作的容器。有时这些调用可能足够多(尤其是通过某种get-branch($foo){$subbranch}{$subsubbranch}模式),以至于在创建Hash的深克隆时所产生的额外开销变得相当重要。
[2]我不太担心阻止任何访问(尽管我很好奇这是否完全可以通过子类化实现),因为我确信一个相当勤奋的编码器总能找出一些问题,但是我想抓住最常见的说法,说“不能碰这个!” (提示90'
通过返回包裹原始Array或的内容Hash或使用but进行浅拷贝并混入其中(这意味着您保留原始类型),可能更容易实现此目的。
我们可以声明一个这样的角色:
role Can'tTouchThis {
method AT-KEY(|) {
untouchable callsame
}
method ASSIGN-KEY(|) {
die "Cannot assign to this";
}
method AT-POS(|) {
untouchable callsame
}
method ASSIGN-POS(|) {
die "Cannot assign to this";
}
}
Run Code Online (Sandbox Code Playgroud)
其中sub untouchable定义为:
multi untouchable(Positional \p) {
p but Can'tTouchThis
}
multi untouchable(Associative \a) {
a but Can'tTouchThis
}
multi untouchable(\o) {
o
}
Run Code Online (Sandbox Code Playgroud)
因此,通过(在访问时)也为嵌套数据结构创建一个只读外观,可以处理嵌套数据结构。
这是一个示例和一些测试案例来说明效果:
class Example {
has %!foo = a => [ 1, 2, [ 3, 4] ], b => { c => { d => 42, e => 19 }, f => 100 };
method get($sym) {
untouchable %!foo{$sym}
}
}
given Example.new {
use Test;
# Positional cases
is .get('a')[0], 1;
is .get('a')[2][1], 4;
dies-ok { .get('a')[1] = 42 };
is .get('a')[1], 2;
# Associative cases
is .get('b')<c><d>, 42;
dies-ok { .get('b')<f> = 99 };
dies-ok { .get('b')<c><d> = 99 };
is .get('b')<f>, 100;
is .get('b')<c><d>, 42;
# Auto-viv also doesn't work
dies-ok { .get('a')[4]<a> = 99 };
dies-ok { .get('a')[4][0] = 99 };
}
Run Code Online (Sandbox Code Playgroud)
删除方法中的untouchable调用get以查看此处的大多数测试由于缺乏保护而失败。