為自定義的類添加下標(biāo)(subscripts)
假如你定義了一個(gè)類,你想把類的實(shí)例用作散列那樣,可以索引其中的元素, 那么你需要讓你得自定義遵守 Associative 接口,并重寫
AT-KEY
、EXISTS-KEY
、DELETE-KEY
、push
等跟散列有關(guān)的方法:
use v6;
class HTTPHeader { ... }
class HTTPHeader does Associative {
has %!fields handles <self.AT-KEY self.EXISTS-KEY self.DELETE-KEY self.push list kv keys values>;
method Str { say self.hash.fmt; }
multi method EXISTS-KEY ($key) { %!fields{normalize-key $key}:exists }
multi method DELETE-KEY ($key) { %!fields{normalize-key $key}:delete }
multi method push (*@_) { %!fields.push: @_ }
sub normalize-key ($key) { $key.subst(/\w+/, *.tc, :g) } # titileCase 駝峰式的鍵
method AT-KEY (::?CLASS:D: $key) is rw {
my $element := %!fields{normalize-key $key};
Proxy.new(
FETCH => method () { $element },
STORE => method ($value) {
$element = do given $value?.split(/',' \s+/).flat {
when 1 { .[0] } # a single value is stored as a string
default { .Array } # multiple values are stored as an array
}
}
);
}
}
my $header = HTTPHeader.new;
say $header.WHAT; #-> (HTTPHeader)
"".say;
$header<Accept> = "text/plain";
$header{'Accept-' X~ <Charset Encoding Language>} = <utf-8 gzip en>;
$header.push('Accept-Language' => "fr"); # like .push on a Hash
say $header.hash.fmt;
"".say;
# say $header.Str; # 同上
say $header<Accept-Language>.values;
say $header<Accept-Charset>;
輸出:
(HTTPHeader)
Accept text/plain
Accept-Charset utf-8
Accept-Encoding gzip
Accept-Language en fr
(en fr)
utf-8
同樣,你也可以使用數(shù)組下標(biāo),只要你重寫相應(yīng)地方法。
關(guān)于為自定義的類添加下標(biāo)這個(gè)問題, stackoverflow 上的回答是不需要在 handles 后面所跟的方法中添加 self
。 他的解釋如下:
為自定義的類添加下標(biāo)(subscripts)
在自定義類上實(shí)現(xiàn)關(guān)聯(lián)式下標(biāo)(associative subscripting)。
通過代理實(shí)現(xiàn)
Perl 6 通過在實(shí)現(xiàn)了集合類型的對象身上調(diào)用良定義的方法來實(shí)現(xiàn)關(guān)聯(lián)式下標(biāo)和位置下標(biāo)(對于內(nèi)置類型)。通過在 %!fields
屬性后面添加 handles
特性(trait), 你就把這些方法調(diào)用傳遞給了 %!fields
-- 它作為一個(gè)散列,會知道怎么來處理那些方法。
靈活的鍵
However, HTTP header field names are supposed to be case-insensitive (and preferred in camel-case). We can accommodate this by taking the *-KEY and push methods out of the handles list, and implementing them separately...
把所有的鍵處理方法代理給內(nèi)部的散列意味著你的鍵得到了散列那樣的插值 -- 意味著它們將是大小寫無關(guān)的因?yàn)樯⒘械逆I是大小寫無關(guān)的。為了避免那,你把所有跟鍵有關(guān)的方法從 handles 子句中拿出并自己實(shí)現(xiàn)那些方法。在例子中,鍵在被索引到 %!fields
讓鍵變成大小寫無關(guān)之前先進(jìn)行了鍵的「標(biāo)準(zhǔn)化」。
靈活的值
例子中的最后一部分展示了當(dāng)值存入到散列那樣的容器中時(shí)你如何控制值的插值。到目前為止,通過賦值給這個(gè)自定義容器的實(shí)例提供的值要么是一個(gè)字符串,要么是一個(gè)字符串的數(shù)組。額外的控制是通過移除定義在靈活的鍵中的 AT-KEY 方法來達(dá)成的并提供一個(gè) Proxy 對象來代替它。如果你給容器賦值,那么代理人對象的 STORE 方法會被調(diào)用并且那個(gè)方法會掃描所提供的字符串值中的 ", "
(注意空格是必要的)。如果找到會接收那個(gè)字符串值作為幾個(gè)字符串值的說明書。
use v6;
class HTTPHeader { ... }
class HTTPHeader does Associative {
has %!fields handles <list kv keys values>;
method Str { say self.hash.fmt; }
multi method EXISTS-KEY ($key) { %!fields{normalize-key $key}:exists }
multi method DELETE-KEY ($key) { %!fields{normalize-key $key}:delete }
multi method push (*@_) { %!fields.push: @_ }
sub normalize-key ($key) { $key.subst(/\w+/, *.tc, :g) }
method AT-KEY (::?CLASS:D: $key) is rw {
my $element := %!fields{normalize-key $key};
Proxy.new(
FETCH => method () { $element },
STORE => method ($value) {
$element = do given $value?.split(/',' \s+/).flat {
when 1 { .[0] } # a single value is stored as a string
default { .Array } # multiple values are stored as an array
}
}
);
}
}
my $header = HTTPHeader.new;
say $header.WHAT; #-> (HTTPHeader)
"".say;
$header<Accept> = "text/plain";
$header{'Accept-' X~ <Charset Encoding Language>} = <utf-8 gzip en>;
$header.push('Accept-Language' => "fr"); # like .push on a Hash
say $header.hash.fmt;
"".say;
# say $header.Str; # 同上
say $header<Accept-Language>.values;
say $header<Accept-Charset>;
輸出:
(HTTPHeader)
Accept text/plain
Accept-Charset utf-8
Accept-Encoding gzip
Accept-Language en fr
(en fr)
utf-8
同樣,你也可以使用數(shù)組下標(biāo),只要你重寫相應(yīng)地方法。