hob*_*bbs 15
结点只是包装器内部的一组值,其中any、、 或取决于用于创建结点的运算符all。稍后我们会讨论这四种类型之间的区别;在大多数情况下,它们的行为完全相同。onenone
当您将结点传递给除显式声明为采用 a 的函数、方法或运算符之外的Junction任何函数、方法或运算符时,调用将是“自动线程”,并且结果将是一个结点。这实际上非常简单,而且一点也不量子:
(1 & 2 & 3) * 2等于1 * 2 & 2 * 2 & 3 * 2equals
2 & 4 & 6和f(any(1, 2, 3))equals any(f(1), f(2), f(3))。f与集合一样,连接中项目的顺序并不重要,并且如果愿意的话,Raku 可以并行评估自动线程操作,因此可能会同时进行多个调用。
当结点被Bool上下文或显式运算符(如 )强制为 时so,它会变成单个值。如果它包含的任何值都为真,则结点any将为真;仅当其所有值都为真时,结点才为真(包括不包含任何值的情况);如果没有任何值为真,则结点为真;并且a如果里面恰好有一个值为 true。Trueallnoneone
所以假设我们有
my @words = <raku is fun>;
if all(@words).chars < 10 {
say "nice short words";
}
Run Code Online (Sandbox Code Playgroud)
这将打印出来,因为与、which is 、which is 、which is和isall(@words).chars < 10相同。
all('raku'.chars, 'is'.chars, 'fun'.chars) < 10
all(4, 2, 3) < 10
all(4 < 10, 2 < 10, 3 < 10)
all(True, True, True)so all(True, True, True)True
如果是 3 的倍数或 5 的倍数,但不是 15 的倍数,同样如此so $num %% one(3, 5)。$num
作为霍布斯回答和评论的后续:
事实证明,连接点的分布行为并不是故事的全部。如果是这种情况,这就是以下表达式的求值方式。
(1 | 2) + (3 & 4)
1 + (3 & 4) ==> all(4, 5)
2 + (3 & 4) ==> all(5, 6)
(4 & 5) | (5 & 6) ==> any(all(4, 5), all(5, 6))
Run Code Online (Sandbox Code Playgroud)
但实际评估结果是:
(1 | 2) + (3 & 4) ==> all(any(4, 5), any(5, 6))
Run Code Online (Sandbox Code Playgroud)
连接类型影响由多个连接组成的表达式的计算。在这里找到了解决方案: https: //design.raku.org/S09.html#Junctions,我只是引用它。
如果两个或多个参数是连接的,则选择为“自动线程”的参数是:
- 最左边的全或无结点(如果有),否则
- 最左边的一个或任何一个路口
按该顺序应用测试。
然后,每个结果调用集都会递归地自动线程化,直到不再有连接参数为止。那是:
substr("camel", 0|1, 2&3)
-> all( substr("camel", 0|1, 2), # autothread the conjunctive arg
substr("camel", 0|1, 3)
)
-> all( any( substr("camel", 0, 2), # autothread the disjunctive arg
substr("camel", 1, 2),
),
any( substr("camel", 0, 3), # autothread the disjunctive arg
substr("camel", 1, 3),
)
)
-> all( any( "ca", # evaluate
"am",
),
any( "cam",
"ame",
)
-> ("ca"|"am") & ("cam"|"ame") # recombine results in junctions
Run Code Online (Sandbox Code Playgroud)
遵循这些规则,(1 | 2) + (3 & 4)评估如下:
(1 | 2) + (3 & 4)
((1 | 2) + 3) & ((1 | 2) + 4)
(4 | 5) & (5 | 6)
Run Code Online (Sandbox Code Playgroud)
相同的规则也可以正确导出以下表达式的求值:
any(1, 2) + all(10, 20)
==>
all(any(11, 12), any(21, 22))
all(1, 2) + any(10, 20)
==>
all(any(11, 21), any(12, 22))
one(1, 2, 3) + any(4, 5)
==>
one(any(5, 6), any(6, 7), any(7, 8))
one(1, 2, 3) + one(4, 5)
==>
one(one(5, 6), one(6, 7), one(7, 8))
one(1, 2, 3) + all(4, 5)
==>
all(one(5, 6, 7), one(6, 7, 8))
one(1, 2, 3) + none(4, 5)
==>
none(one(5, 6, 7), one(6, 7, 8))
Run Code Online (Sandbox Code Playgroud)
https://docs.raku.org/type/Junction中完全缺少这些基本表达式评估规则,因此官方文档没有任何帮助。
PS: 感谢 raiph 也找到了答案。
编辑:
阅读完本节https://design.raku.org/S09.html#Junctions后,您可能会认为包含结点的表达式将在编译时使用宏进行脱糖处理,但事实并非如此。一切都发生在运行时,但AUTOTHREAD下面的方法中发生的事情看起来很像宏扩展过程。
常规值(Any 类型的值、任何不是结点的值)对其自身求值
连接点(有 4 种连接类型:任意、全部、一个、无)自行评估
any()、all()、one()、none()(或它们各自的运算符 infix:<|>、infix:<&>、infix:<^>。none() 没有关联的运算符)有它们的参数正常计算,并构造并返回一个连接值。参数的类型(Any 或 Junction)无关紧要。
(这里的类型实际上意味着类型/子类型)
所有参数均为 Any 类型或 Junction 类型但相应参数也为 Junction(或 Mu)类型的函数调用是常规函数调用
至少有一个 Junction 类型的参数和 Any 类型的相应参数的函数调用有多次调度失败并回退到method AUTOTHREAD(&call, |args)https://github.com/rakudo/rakudo/blob/main/src/core.c/Junction 。下午 6 .
下面是 Perl 中的简化(并且大部分是正确的)翻译AUTOTHREAD。
调用具有返回的infix:<+>(1 | 2, 3)效果,该过程与宏扩展非常相似,这是可能的,因为多重分派创建了间接并回退到. 这既令人着迷又令人恐惧。infix:<+>(1, 3) | infix:<+>(2, 3)AUTOTHREAD
sub AUTOTHREAD {
my ($call, $args) = @_;
my $positionals = $args->{list};
sub thread_junction {
my $pos = shift;
my $junction = $positionals->[$pos];
my @storage = $junction->{eigenstates}->@*;
my @result;
for (my $i=0; $i < @storage; $i++) {
$positionals->[$pos] = $storage[$i];
push @result, $call->($args); # really multiple_dispatch($call, $args)
}
Junction->new(type => $junction->{type}, eigenstates => \@result);
}
for (my $i=0; $i < $positionals->@*; $i++) {
my $arg = $positionals->[$i];
if ($arg isa Junction) {
if ($arg->{type} eq "all" || $arg->{type} eq "none") {
return thread_junction($i);
}
}
}
for (my $i=0; $i < $positionals->@*; $i++) {
my $arg = $positionals->[$i];
if ($arg isa Junction) {
if ($arg->{type} eq "any" || $arg->{type} eq "one") {
return thread_junction($i);
}
}
}
my $named = $args->{hash};
for my $key (keys $named->%*) {
my $arg = $named->{$key};
if ($arg isa Junction) {
my $junction = $arg;
my @storage = $junction->{eigenstates}->@*;
my @result;
for (my $i=0; $i < @storage; $i++) {
$named->{$key} = $storage[$i];
push @result, $call->($args); # really multiple_dispatch($call, $args)
}
return Junction->new(type => $junction->{type}, eigenstates => \@result);
}
}
$call->($args); # really multiple_dispatch($call, $args)
}
Run Code Online (Sandbox Code Playgroud)