也许这很有帮助:
my \buffer = Buf.new: 'mystring'.encode: 'utf-8';
my \offset = 3;
my \mask = 0x03;
sub mask-buffer-from-to (\mask,
\buffer,
\from = 0,
\to = buffer.end) {
buffer[from..to]».&{ $_ +&= mask }
}
mask-buffer-from-to mask, buffer, offset;
say buffer; # Buf:0x<6d 79 73 00 02 01 02 03>
我删去了符号(在变量声明中使用
\
而不是
$
等)。 希望这样可以减少混乱(删去符号意味着它们在声明后被省略),我的目的是使其更易读。 如果这对您有用或实际上使阅读更加困难,请告诉我。
子例程签名中的= 0
和= buffer.end
位是默认值。
如果您对P6比较熟悉,那么».&{ $_ +&= mask }
表达式的含义显然。但由于您并不熟悉,所以我会解释一下。
用英语阅读,它的意思是,给定左侧缓冲区元素的范围,在每个元素上迭代(如果编译器愿意并行执行),将每个元素与mask
进行逐位逻辑与运算。
计算机术语的阅读方式如下:
»
这是SPMD的P6精简版,用于超级运算符。
»
迭代其左侧操作数(左操作数对应SPMD的MD位),并在每次迭代时对其右侧操作数(对应SPMD的SP位)应用操作。
仅当SP和MD操作数是SPMD安全的时才使用»
,即单个操作不会相互干扰。(如果您不确定,请使用像for
循环或map
之类的内容。)在这种情况下,您可以明显看出该操作(使用掩码进行逐位与操作)是SPMD安全的。
为了记住这种使用
»
的方式,只需记住它是一个SPMD操作或者注意到它显示了两个并行的
>
(提醒您它假定并行语义,即编译器可以并行化它),左侧大于右侧(提醒您左操作数是复数,即它可以并且通常有多个元素,而右操作数始终只是单个操作)。
.
这是方法调用运算符。它在左侧的调用者的基础上,在右侧给定的方法上进行调用。
&
在一个
.
后面紧跟着一个
&
,会强制右侧表达式被解释为一个例程。
{ ... }
在
.&
之后的大括号代码被解释为闭包(例程)。在闭包内,
$_
(可以读作“这个”)被赋予左侧
».&
操作数迭代产生的每个元素的别名。
$_ +&= mask
这行代码的形式为LHS op= RHS
。这种形式是LHS = LHS op RHS
的简写。因此,闭包中的代码简写为:
$_ = $_ +& mask
即“这个通过位运算与掩码相与后变成了这个”。对于觉得这个答案有些困惑的读者,我感到抱歉;请在下方留言,我会做出相应的反应,或许会写一个完全不同、更简单、新的答案。我希望@madcrazydrumma觉得这个答案有趣且有帮助。
sub foo ($data) { $data[1] = 0x99 }; my $bar = Buf.new: 'mystring'.encode: 'utf-8'; say $bar; #\
[Buf:0x<6d 79 73 74 72 69 6e 67>]; foo $bar; say $bar; #`[Buf:0x<6d 99 73 74 72 69 6e 67>]`? - raiph$data[offset + x] & 0xff
这样的操作。 - madcrazydrumma+&
,所以$data[offset + x] +& 0xff
,没错,这样就可以了。如果你想要加强类型检查,可以在签名中添加Buf
类型:sub foo (Buf $data) ...
。也许你可以尝试一下这些方法,然后自己回答你的问题(这在 SO 上是合法的)。我要去睡觉了。 :) - raiph