如何评估连接点?

Whi*_*ist 9 raku

连接点的想法最初是由达米安·康威(Damian Conway)提出的,目的是模仿量子叠加并表达量子计算算法。

尽管这可能很可爱,但叠加和折叠的概念在编程环境中没有意义,并且评估规则也远不明确。

hob*_*bbs 15

结点只是包装器内部的一组值,其中any、、 或取决于用于创建结点的运算符all。稍后我们会讨论这四种类型之间的区别;在大多数情况下,它们的行为完全相同。onenone

当您将结点传递给除显式声明为采用 a 的函数、方法或运算符之外的Junction任何函数、方法或运算符时,调用将是“自动线程”,并且结果将是一个结点。这实际上非常简单,而且一点也不量子: (1 & 2 & 3) * 2等于1 * 2 & 2 * 2 & 3 * 2equals
2 & 4 & 6f(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


Whi*_*ist 4

作为霍布斯回答和评论的后续:

事实证明,连接点的分布行为并不是故事的全部。如果是这种情况,这就是以下表达式的求值方式。

(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)