Метаоператоры X и Z в Perl 6

    Одна из новых идей Perl 6 – метаоператор. Это оператор, который можно скомбинировать с обычным оператором, изменив его поведение. Таких метаоператоров есть несколько штук, но в этой статье мы рассмотрим только X и Z.

    Оператор X вы могли видеть в роли инфиксного комбинирования. Он комбинирует списки, по элементу из каждого, во всех возможных комбинациях:

    > say ((1, 2) X ('a', 'b')).perl
    ((1, "a"), (1, "b"), (2, "a"), (2, "b"))
    


    Однако, запись infix: - это короткая запись метаоператора X, примененного к оператору конкатенации infix:<,> . И действительно, можно написать:

    > say ((1, 2) X, (10, 11)).perl
    ((1, 10), (1, 11), (2, 10), (2, 11))
    


    Что произойдёт, если мы применим Х к другому инфиксному оператору? Например, к infix:<+>

    > say ((1, 2) X+ (10, 11)).perl
    (11, 12, 12, 13)
    


    Вместо создания списка из всех возможных комбинаций, оператор применяет инфиксное сложение к спискам. Получается не список, а простой номер – сумма всех элементов этой комбинации.

    Это работает со всеми инфиксными операторами. Возьмём объединение строк infix:<~>

    > say ((1, 2) X~ (10, 11)).perl
    ("110", "111", "210", "211")
    


    Или же числовое сравнение infix:<==>

    > say ((1, 2) X== (1, 1)).perl
    (Bool::True, Bool::True, Bool::False, Bool::False)
    


    Теперь перейдём к метаоператору Z. Если вы уже встречали инфиксный оператор
    infix:, который выступает короткой записью оператора "Z,", вы можете догадаться, для чего он нужен. Если программист на Haskell воспринимает infix: как zip-функцию, то метаоператор Z – это как функция zipWith.

    > say ((1, 2) Z, (3, 4)).perl ((1, 3), (2, 4)) > say ((1, 2) Z+ (3, 4)).perl (4, 6) > say ((1, 2) Z== (1, 1)).perl (Bool::True, Bool::False)


    Z, берёт по очереди каждый элемент каждого списка, затем работает сначала на первых элементах, потом на вторых, потом на третьих, и т.д. Останавливается он, когда доходит до конца списка, неважно, на какой стороне.

    Также Z – ленивый, поэтому его можно применить к двум бесконечным спискам, и он выдаст столько результатов, сколько вы запросите. X может работать только когда бесконечный список будет слева.

    Эти метаоператоры становятся мощными инструментами для совершения операций над индивидуальными элементами многих списков, связаны ли эти элементы между собой через индексы, как в случае Z, или же вам нужно проверить все возможные комбинации, как в случае с X.

    Сделать ассоциативный массив из списков ключей и значений? Легко.

    my %hash = @keys Z=> @values;
    


    Пройти по двум спискам одновременно:

    for @a Z @b -> $a, $b { ... }
    


    Или по трём?

    for @a Z @b Z @c -> $a, $b, $c { ... }
    


    Или нужно найти все возможные суммы, которые получатся от бросков трёх десятигранных кубиков:

    my @d10 = 1 ... 10;
    my @scores = (@d10 X+ @d10) X+ @d10;
    


    Чтобы изучить, как метаоператоры используются в реальных проектах, обратитесь к модулю для решения судоку.
    • +18
    • 5,3k
    • 3
    Поделиться публикацией
    Похожие публикации
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 3
    • +1
      J!
      • +1
        А почему не оформлено как перевод? И ссылка на судоку неактивна.
        • 0
          Кстати, если в Haskell Z — это zipWith, то X — это liftM2, специализированный для списков:
          ghci> (liftM2 (,)) [1, 2] [1, 2]
          [(1,1),(1,2),(2,1),(2,2)]
          ghci> (liftM2 (+)) [1, 2] [1, 2]
          [2,3,3,4]
          ghci> (liftM2 (==)) [1, 2] [1, 2]
          [True,False,False,True]
          

          Жаль, нельзя частично-применённую функцию в инфиксной форме написать.

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.