В чем разница между этими двумя соглашениями о вызовах функций? - PullRequest
0 голосов
/ 08 мая 2018

Функции можно вызывать несколькими способами:

say(1, 2, 3) # 123
say: 1, 2, 3 # (1, 2, 3)

Последний, кажется, проходит Positional, но кроме этого я не знаю, как еще они отличаются. Есть ли различия, которые важно знать? Какие типы ситуаций вы бы использовали один над другим?

Ответы [ 2 ]

0 голосов
/ 11 мая 2018

@ Ответ jjmerelo охватывает основы. Этот дополнительный ответ, цель которого быть несколько исчерпывающим, но, надеюсь, не исчерпывающим, охватывает ловушки, редкие случаи и советы.

foo: valuea, valueb, ...

Удивительно, но это не вызов подпрограммы или метода с именем foo.

Вместо этого это утверждение начинается с метки , foo:.

Строка say: в вашем вопросе не будет работать в обычной программе:

say: <a b c>; # Useless use of constant value a b c ...

Предупреждение «Бесполезное использование» означает, что <a b c> не используется полезным способом. say: ничего не делает со списком значений. Это просто лейбл, который ничего не делает.

Предположительно, вы используете что-то вроде Perl 6 REPL. REPL автоматически say устанавливает последнее значение в строке, если оно не используется иным образом, и, таким образом, создается впечатление, что строка работает без предупреждения.

.a-method:

Если вызов метода постфикса с использованием формы .a-method не имеет других аргументов, кроме invocant (аргумент слева от . или , то текущая тема , если явного инвоканта нет), вы можете просто написать ее в виде:

42.say ;

При желании можно добавить двоеточие:

42.say: ;

Нет веской причины, но это соответствует:

.a-method: arg2, arg3, ...

Если вы хотите предоставить один или несколько аргументов (кроме инвоканта) для постфикса .a-method, то вам нужно выбрать один из двух способов их введения.

Один из способов - написать двоеточие сразу после имени метода перед аргументом (ами). Между именем метода и открывающей скобкой не должно быть пробела. 1

Например, следующий код использует двоеточие перед аргументом Numeric в следующем вызове метода:

say <abc 2 def ghi> .first: Numeric ; # 2

В приведенной выше строке выражение вызова метода (.first: Numeric) оканчивается на терминаторе оператора (;). Если имеется вложенное подвыражение, такое как индекс массива, то выражение вызова метода заканчивается в конце этого подвыражения:

say .[1 + .first: Numeric] given <abc 2 def ghi> ; # ghi

Список аргументов вызова метода формы в двоеточии также закрывается с помощью действительного модификатора оператора подобно given:

say .first: Numeric given <abc 2 def ghi> ; # 2

a-sub arg1, arg2, ...

Это соответствующая форма для вызовов подпрограмм. Единственные различия в форматах заключаются в том, что в подпункте нет имени или . перед именем подпункта, и вы должны пропустить двоеточие после имени подпункта.

.a-method( arg2, arg3, ... )

a-sub( arg1, arg2, ... )

Другая распространенная форма, используемая как для вызова метода, так и для дополнительного вызова, состоит в том, чтобы сразу же следовать за именем метода или дополнительного имени с помощью паренов, чтобы разделить аргументы. Открывающая скобка должна следовать сразу за , без пробела между именем процедуры и (.

Вот парены, используемые методом .first:

say 1 + .first(Numeric) given <abc 2 def ghi> ; # 3

Это имеет то преимущество, что это, возможно, красивее, чем альтернатива использования внешних паренов:

say 1 + (.first: Numeric) given <abc 2 def ghi> ; # 3

Если вы хотите разместить дополнительный вызов непосредственно внутри строки в двойных кавычках, вам необходимо добавить префикс имени к символу & и использовать форму Parens постфикса:

my @array = <abc 2 def ghi> ;
say "first number is &first(Numeric,@array)" ; # first number is 2

Чтобы выполнить вызов метода, вам снова нужно использовать форму parens postfix, а также вы должны предоставить явный инвокант (вы не можете просто написать "Some text .a-method()"):

my @array = <abc 2 def ghi> ;
say "first number is @array.first(Numeric)" ; # first number is 2

Если нет аргументов (кроме инвоканта для вызова метода), вам все равно нужно использовать эту форму с пустыми паренами, если вы хотите интерполировать вызов sub или метода в строке:

my @array = <abc 2 def ghi> ;
say "no method call @array[3].uc" ;     # no method call ghi.uc
say "with method call @array[3].uc()" ; # with method call GHI
say "&rand";                            # &rand
say "&rand()";                          # 0.929123203371282

.a-method ( arrgh, arrgh, ... ) ;

Это не сработает.

Поскольку за .a-method не стоит двоеточие, вызов метода считается завершенным.

Это означает, что следующая вещь должна быть либо выражением / оператором, например ;, либо постфиксным оператором, который будет работать с результатом вызова метода, либо инфиксным оператором, который будет работать с результатом и некоторым последующим аргументом. .

Но ( arrgh, arrgh, ... ) не является ни одним из них. Таким образом, вы получите ошибку компиляции «Два члена подряд».

.a-method:( arrgh, arrgh, ... ) ;

.a-method: ( arrgh, arrgh, ... ) ;

В общем случае, НЕ СМЕШИВАТЬ использование : с использованием скобок вокруг аргументов как части вызова метода. Нет веских причин для этого, потому что это не сработает, или сработает только случайно, или сработает, но, скорее всего, запутает читателей.

Выполнение этого без пробела между двоеточием и открывающей пареней приводит к загадочной ошибке компиляции:

This type (QAST::WVal) does not support positional operations

Похоже, что работать с пробелом - но, как правило, только по счастливой случайности:

say .first: (Numeric) given <abc 2 def ghi> ; # 2

(Numeric) - это одиночное значение в скобках, которое дает Numeric, поэтому эта строка такая же как:

say .first: Numeric given <abc 2 def ghi> ; # 2

Но если в паренсе есть два или более аргументов, все пойдет не так. Используйте одну из следующих форм:

say .first: Numeric, :k given <abc 2 def ghi> ; # 1
say .first(Numeric, :k) given <abc 2 def ghi> ; # 1

, который правильно возвращает индекс массива ("ключ") элемента 2, а не:

say .first: (Numeric, :k) given <abc 2 def ghi> ; # Nil

, что приводит к Nil, потому что метод .first ничего не делает с аргументом single , представляющим собой список вида (Numeric, :k).

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

invocant.a-method(( valuea, valueb, ... ));

a-sub ( arrgh1, arrgh2, ... ) ;

Как только что объяснено для вызовов методов, он передает ОДИН аргумент a-sub, а именно один список ( arrgh1, arrgh2, ... ), который редко будет тем, что имеет в виду писатель.

Опять же, мой совет, вместо этого напишите это как:

`a-sub( valuea, valueb, ... ) ;`

или

`a-sub  valuea, valueb, ...   ;`

если вы хотите передать несколько аргументов или если вы хотите передать список как один аргумент, то:

`a-sub(( valuea, valueb, ... )) ;`

.a-method : arrgha, arrghb, ...

a-sub : arrgha, arrghb, ...

Для формы метода это приведет к ошибке "Confused" компиляции.

То же самое верно для подформы, если a-sub не принимает аргументов. Если a-sub принимает аргументы, вы получите ошибку компиляции «Предшествующий контекст ожидает термин, но обнаружил инфикс: вместо».

.&a-sub

Существует форма вызова, которая позволяет вам вызывать подпрограмму, объявленную как подчиненную, но с использованием синтаксиса вызова .method. Следующее передает "invocant" qux слева от точки в качестве первого аргумента для подпрограммы с именем a-sub:

qux.&a-sub

Используйте : или круглые скобки как обычно, чтобы передать дополнительные аргументы a-sub:

sub a-sub ($a, $b) { $a == $b }
say 42.&a-sub(42), 42.&a-sub(43); # TrueFalse
say 42.&a-sub: 42;                # True

(В моей первоначальной версии этого раздела я писал, что нельзя передавать дополнительные аргументы. Я проверял это и думал, что не смогу. Но я, должно быть, только что-то запутался. Комментарий Enheh привел меня к повторному тестированию и обнаружению что один может передавать дополнительные аргументы, как при обычных вызовах методов. Спасибо @Enheh.:))

a-method( invocant: arg2, arg3, ... )

a-method invocant: arg2, arg3, ...

Эти форматы, называемые косвенной нотацией объекта в документах проектирования, представляют собой недокументированную и очень редко встречаемую форму вызова метода, в которой вызов имитирует объявление метода - сначала идет имя метода, а затем - инвокант, за которым следует двоеточие. :

say first <abc 2 def ghi>: Numeric ; # 2

Обратите внимание, что say - это вызов sub , потому что за следующим токеном, first, не стоит двоеточие. В отличие от этого first является вызовом метода , поскольку токен после него является , за которым следует двоеточие.

Сноска

1 Все комментарии о пробелах в этом ответе игнорируются unspace .

0 голосов
/ 08 мая 2018

Как сказал выше Райф, say: - это ярлык. Таким образом, вы ничего не сделали say (даже если думали, что сделали) и - за пределами использования REPL - компилятор будет жаловаться, что использование <a b c> бесполезно:

say: <a b c>; # OUTPUT: «WARNINGS for <tmp>:␤Useless use of constant value a b c in sink context (lines 1, 1, 1, 1, 1, 1)␤»

Однако вы часто можете использовать нотацию : вместо скобок в вызовах методов. Рассмотрим четыре стандартных вызова ниже (два вызова подпрограммы, а затем два вызова метода):

my @numbers = (33, 77, 49, 11, 34);
say map  *.is-prime, @numbers  ;  # simplest subroutine call syntax
say map( *.is-prime, @numbers );  # same meaning, but delimiting args
say @numbers.map( *.is-prime ) ;  # similar, but using .map *method*
say @numbers.map: *.is-prime   ;  # same, but using : instead of parens

Все эти предложения будут возвращать одно и то же (False False False True False).

В общем, как вы видели выше с map, вы можете использовать () в вызовах методов везде, где вы используете :, но обратное неверно; : может использоваться только в вызовах методов.

Используйте (), если необходимо точно разграничить аргументы, как Райф комментирует ниже.

Этот ответ фокусируется на основах. См. Ответ Райфа для более полного охвата точных деталей синтаксиса обычного вызова. (В качестве важного примера значение этих вызовов обычно изменяется, если между именем процедуры и двоеточием (:) или открывающей скобкой (()) есть пробелы.

...