Как оцениваются соединения?

Идея соединений была первоначально представлена ​​Дамианом Конвеем, чтобы имитировать квантовую суперпозицию и выражать алгоритмы квантовых вычислений.

Как бы мило это ни звучало, концепции суперпозиции и коллапса не имеют смысла в контексте программирования, а правила вычислений далеко не ясны.

Вы вообще читали docs.raku.org/type/Junction? Правила оценки довольно просты и достаточно совместимы с любым языком, в котором есть концепция вещания.

hobbs 11.06.2023 20:42

Обратите внимание, что R-язык программирования имеет схожие функции: any и all можно найти/вызвать как S4 Group Generic Functions

jubilatious1 12.06.2023 12:52

@raiph Я обнаружил их после использования одноименных функций Раку. Они являются дженериками в системе классов R OO S4. Да, они кажутся чисто логическими (большего я не могу сказать). Я упоминаю их, потому что люди, интересующиеся перекрестками Раку, могут ожидать, что они будут действовать аналогично функциям R.

jubilatious1 12.06.2023 19:12

@jubilatious1 В R: any или all читайте их аргументы слева направо, возвращая TRUE, если какой-либо или все их аргументы приводят к TRUE, и FALSE в противном случае. Вот и все. Например, all(3,4) + any(5,6) возвращает 2, работа выполнена. Этот код оценивается следующим образом. Во-первых, предупреждения о приведении значений из двойных в логические. (3 и 4 из all, а затем только 5 из any.) Затем all возвращает TRUE, затем any тоже. Наконец, TRUE + TRUE выдает предупреждения о приведении логики к удвоению (две TRUE к 1), а 2 возвращается. Дело сделано.

raiph 12.06.2023 23:00

В Раку: all(3,4) + any(5,6) строит соединение 3 и 4, дизъюнкт 5 и 6, а затем складывает их, получая новое соединение all(any(8, 9), any(9, 10)). Код оценивается следующим образом. all создает и возвращает соединение all, а any соединение any. Эти соединения добавляются, и в результате получается новое соединение, содержащее добавления. Обратите внимание, что ничего логического не задействовано, и соединение должно быть дополнительно обработано - работа не выполнена.

raiph 12.06.2023 23:11

@raiph, возможно, вы путаете полезность с ожиданиями. В Rany(c(0:1)) возвращает TRUE; аналогично any(c(FALSE, TRUE)) возвращает TRUE. OTOH, мы видим, что all(c(0:1)) возвращает FALSE и аналогично all(c(FALSE, TRUE)) возвращает FALSE. Имеет смысл, нет? И довольно легко догадаться о доходах, которые я не опубликовал...

jubilatious1 13.06.2023 01:28

Давайте продолжим обсуждение в чате.

raiph 13.06.2023 01:51
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
11
7
405
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Соединение — это просто набор значений внутри оболочки, которая представляет собой any, all, one или none в зависимости от оператора, используемого для создания соединения. Мы поговорим о различиях между четырьмя типами позже; по большей части они ведут себя точно так же.

Когда вы передаете соединение любой функции, методу или оператору, за исключением тех, которые явно объявлены для получения Junction, вызов «автопоточен», и результатом будет соединение. Это на самом деле очень просто и совсем не квантово: (1 & 2 & 3) * 2 равно 1 * 2 & 2 * 2 & 3 * 2 равно
2 & 4 & 6, а f(any(1, 2, 3)) равно any(f(1), f(2), f(3)). Порядок элементов в соединении, как и в наборе, не имеет значения, и Raku может, если захочет, параллельно оценивать операции с автопотоком, поэтому одновременно может выполняться более одного вызова f.

Когда соединение приводится к Bool либо контекстом, либо явным оператором, таким как so, оно становится единственным значением. any-соединение будет True, если любое из содержащихся в нем значений истинно, all-соединение истинно, только если все его значения истинны (включая случай, когда оно не содержит значений), none-соединение истинно, если ни одно из его значений истинны, и one, если истинно только одно значение внутри.

Итак, предположим, что у нас есть

my @words = <raku is fun>;
if all(@words).chars < 10 {
    say "nice short words";
}

это напечатает, потому что all(@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.

Точно так же so $num %% one(3, 5) будет истинным, если $num кратно 3 или кратно 5, но не кратно 15.

Спасибо, это, безусловно, немного проясняет Junctions. Я лучше понимаю, почему это конкретное выражение дает свой результат. raku -e 'say any(1, 2, 3) + any(10, 20);'any(any(11, 21), any(12, 22), any(13, 23)) Возможно, это должен быть правильный вопрос, но можем ли мы сказать, что соединения распределяют (в алгебраическом смысле) прикладной вызов оператора/функции и его аргументы по содержимому соединения?

WhiteMist 11.06.2023 21:31

@WhiteMist да. Я не нашел четкой документации по этому поводу, но да, он распространяется. Самое интересное — выяснить логику соответствующих значений any(1, 2) + all(10, 20) и all(1, 2) + any(10, 20).

hobbs 11.06.2023 21:47

@WhiteMist (и да, я бы поддержал вопрос об этом, потому что я не знаю ответа, и в любом случае это, вероятно, было бы слишком много для этого вопроса и ответа)

hobbs 11.06.2023 21:48

Я проверил ваши примеры any(1, 2) + all(10, 20) и all(1, 2) + any(10, 20), результаты неожиданны, и я понятия не имею, как они получены. Вот почему я не принял ваш ответ. В вашем посте говорится исключительно о распределительном свойстве соединений, когда у вас есть вызов оператора или функции с 1 соединением и 1 или более нормальными значениями. Это тривиальная часть работы соединений. Но, как мы видим из ваших примеров, дистрибутивной модели недостаточно, соединительный тип влияет на результат, поэтому они явно представляют собой другие неявные правила в отношении того, как оцениваются соединения.

WhiteMist 12.06.2023 08:32

Вот другие нетривиальные выражения соединений: one(1, 2, 3) + any(4, 5), one(1, 2, 3) + all(4, 5), one(1, 2, 3) + one(4, 5), one(1, 2, 3) + none(4, 5).

WhiteMist 12.06.2023 08:32

@Белый туман. Это "самое интересное". @hobbs упомянул. Прочитайте «Если два или более аргумента являются соединительными...» в разделе проектной документации Соединения. К вашему сведению, я опубликовал, а затем удалил ответ на ваш вопрос, но я рассматриваю возможность его восстановления с помощью вставки из этого раздела документации по дизайну.

raiph 12.06.2023 13:35

@raiph Да, это было так, я узнал немного раньше, чем ты написал, я собирался сдаться. Я действительно не понимаю, как это не является частью документов. Мне пришлось обновить страницу, когда вы опубликовали ее, поэтому я прочитал ваш комментарий и даже сохранил веб-страницу. Я не знал об этимологии слова "перекресток", это было интересно.

WhiteMist 12.06.2023 14:42

Хороший пример разбивки!

uzluisf 15.06.2023 18:21
Ответ принят как подходящий

В качестве дополнения к ответу и комментариям Хоббса:

Как оказалось, дистрибутивное поведение соединений — это еще не все. Вот как оценивалось бы следующее выражение, если бы это было так.

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

но фактическая оценка производит:

(1 | 2) + (3 & 4) ==> all(any(4, 5), any(5, 6))

Соединительные типы влияют на оценку выражения, состоящего из более чем одного соединения. Решение было найдено здесь: 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

Следуя этим правилам, (1 | 2) + (3 & 4) оценивается следующим образом:

(1 | 2) + (3 & 4)

((1 | 2) + 3) & ((1 | 2) + 4)

(4 | 5) & (5 | 6)

Эти же правила также правильно выводят оценку следующих выражений:

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

Эти базовые правила вычисления выражений полностью отсутствуют в https://docs.raku.org/type/Junction, поэтому официальная документация ничем не помогла.

PS: Спасибо Раифу за то, что он тоже нашел ответ.

Обновлено:

Прочитав этот раздел 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.pm6.

Ниже приведен упрощенный (и в основном правильный) перевод AUTOTHREAD на Perl.

Вызов infix:<+>(1 | 2, 3) приводит к тому, что возвращается infix:<+>(1, 3) | infix:<+>(2, 3), и этот процесс действительно похож на раскрытие макроса, что возможно из-за косвенности, создаваемой отправкой multipl и откатом к 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)
}

Хорошее продолжение! Теперь, если бы @raiph просто повторно опубликовал свой ответ. Мне любопытно!

jubilatious1 13.06.2023 01:33

Другие вопросы по теме