Как указать тип возвращаемого массива?

avatar
sid_com
8 августа 2021 в 17:50
192
2
8
sub a { my @c = 1, 2, 3, 4;     return @c };
sub b { my $d = [ 1, 2, 3, 4 ]; return $d };

say a().WHAT; # (Array)
say b().WHAT; # (Array)

my @e = a();
my @f = b();

say @e;     # [1 2 3 4]
say @e[1];  # 2

say @f;     # [[1 2 3 4]]
say @f[1];  # (Any)

# valid raku code, no errors messages

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

sub b ( --> Array ) { my $d = [ 1, 2, 3, 4 ]; return $d };
say b().WHAT;       # (Array)
sub b ( --> Scalar ) { my $d = [ 1, 2, 3, 4 ]; return $d };
say b().WHAT;

# Type check failed for return value; expected Scalar but got Array ([1, 2, 3, 4])
#   in sub b at ...
#   in block <unit> at ...
Источник
Holli
8 августа 2021 в 18:32
3

Они ведут себя по-разному, потому что вы возвращаете разные вещи. Один представляет собой массив — список вещей, а второй возвращает скаляр — одну вещь, которая содержит массив. См. docs.raku.org/language/…

sid_com
9 августа 2021 в 03:02
1

@Holli: я знаю, что возвращаю разные вещи, которые ведут себя по-разному.

Ответы (2)

avatar
raiph
9 августа 2021 в 01:31
9

Все дело в Scalar, который не был деконтейнеризирован.

Вы можете изменить return $d на return @$d

Одним из способов добиться такого же поведения является изменение процедуры b.

Вы написали: "Обе эти подпрограммы возвращают массив, но возвращаемые массивы ведут себя по-разному". Но, как отмечает Холли, b вместо этого возвращает Scalar, привязанный к $d (который, в свою очередь, содержит массив):

sub b { my $d = [ 1, 2, 3, 4 ]; return $d };
say b().VAR.WHAT; # (Scalar)

Вы можете изменить это, "деконтейнеризируя" $d, например, добавив перед @:

sub b { my $d = [ 1, 2, 3, 4 ]; return @$d };
say b().VAR.WHAT; # (Array)

my @f = b();

say @f;     # [1 2 3 4]
say @f[1];  # 2

Или измените @f = b() на @f = b[]

Если вы не деконтейнеризировали значение, возвращаемое b, тогда возникает вторая проблема/возможность.

Независимо от того, что возвращает b, присваивание @f будет оценивать список присваиваемых ему значений. В контексте списка Scalar остаются как есть (так же, как они были с простым return $d). Поэтому, если вы не измените b на деконтейнеризацию, тогда вам нужно будет сделать это в назначении на @f вместо этого, если вы хотите, чтобы @e и @f в конечном итоге были одинаковыми. .

На этот раз вы не можете просто добавить @ для этого. Потому что это будет означать @b, что Раку интерпретирует как @b переменную.

Одним из вариантов является запись @f = @(b()), но это будет некрасиво/неидиоматично. Другой вариант — написать @f = b[]. Это использует тот факт, что круглые скобки в вызовах b были избыточными. Добавление [] ("срез дзен") имеет тот же эффект, что и запись @(b), но на один символ меньше.

Итак, для деконтейнеризации в назначении списка можно написать:

sub b { my $d = [ 1, 2, 3, 4 ]; return $d };
say b().VAR.WHAT; # (Scalar)

my @f = b[];

say @f;     # [1 2 3 4]
say @f[1];  # 2

"разъяснить в документации"

Каким выражением правильно указать в документации какой тип массива что возвращает подпрограмма?

Я не уверен, что вы имеете в виду под этим вопросом, даже с переключением на просто "что возвращается".

Я также не уверен, на что указать вам в документе, и даже если есть что-то хорошее, на что можно указать, относительно сценария в вашем SO.

Я знаю, что на моем месте следующие разделы документа смущают относительно вашего сценария:

  • Ссылка на раздел Скалярные контейнеры и списки вещей Holli. Этот раздел, как мне кажется, в настоящее время посвящен использованию контейнеров Scalar внутри списков/массивов, что имеет отношение к второй проблеме , о которой я писал выше ($d находится в списке справа от присвоение @f). Но это не относится к первой проблеме, о которой я писал (return $d из подпрограммы b). Там все наоборот, а именно внутри Scalar находится массив.

  • Раздел Скалярные контейнеры выше на той же странице. Начальное предложение — «Хотя объекты типа Scalar повсюду в Раку, вы редко видите их непосредственно как объекты, потому что большинство операций по деконтейнеризации ...» работает для меня. Но «подпрограмма может вернуть контейнер, если он помечен как is rw» более проблематичен. Это является истинным:

my $x = 23;
sub f() is rw { $x };
f() = 42;
say $x;                 # OUTPUT: «42␤»

Но не нужно пометить подпрограмму is rw для возврата контейнера. Можно использовать подпрограмму return, как вы сделали:

my $x = 23;
sub f() { return $x };
say f().VAR.WHAT;       # OUTPUT: «Scalar␤»
sid_com
9 августа 2021 в 04:53
1

Я деконтейнеризирую переменную в соответствии с вашим предложением ( $d[] ). В документации я просто пишу, что подпрограмма возвращает массив. Пользователю не так сложно узнать, как возвращается массив.

raiph
9 августа 2021 в 10:49
3

@sid_com Проснувшись, особенно если вы пишете документ, который выравнивает return @d с return $d, я рекомендую, чтобы для списка контекстуализации последний был написан return @$d, а не $d[]. Если пользователь использует поле поиска документа на веб-сайте для поиска в документе @, то второй записью прямо сейчас является "@ list contextualizer", что является правильным битом информации, тогда как при поиске документа для [], единственная запись, которая даже отдаленно близка, - это последняя, ​​и это действительно не помогает имхо. На самом деле нужно было бы поискать «кусочки дзен», но откуда это знать новичку?

Mustafa Aydın
12 августа 2021 в 08:52
1

привет, у меня сложилось впечатление, что последние два фрагмента достигают тех же целей, но f() = 84 для последнего ошибочны. Но я думаю, дело в том, что оба возвращают контейнер, но один из них доступен для чтения и записи; это так? Также return-rw's doc читает "Подпрограмма return будет возвращать значения, а не контейнеры."; Я полагаю, что это неправильно, как вы показали, но я могу что-то упустить в терминологии.

raiph
12 августа 2021 в 13:01
1

@MustafaAydın «[возможно] оба [return и return-rw] возвращают контейнер, но один из них доступен для чтения и записи?» ➊ Я надеялся, что кто-нибудь прокомментирует, как вы. Наши комментарии теперь лаконично знакомят с вопросами, связанными с этим, что меня очень радует. Итак, во-первых, спасибо! ➋ В обоих случаях это определенно Scalar. ➌ Предположительно, авторы документа предпочитают педагогическое упрощение ссылки на доступный только для чтения Scalar как на значение, потому что они кажутся с точки зрения прозрачности ссылок эквивалентными. ➍ Я предсказываю, что однажды кто-то опубликует вопрос о таких вещах... ;)

avatar
p6steve
14 мая 2022 в 21:03
1

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

sub b ( --> Scalar ) { my $d = [ 1, 2, 3, 4 ]; return $d };
say b().WHAT;

# Type check failed for return value; expected Scalar but got Array ([1, 2, 3, 4])
#   in sub b at ...
#   in block <unit> at ...

Этот ответ предназначен для уточнения ограничений возвращаемого типа, как описано здесь

Если вы сделаете это, теперь вы возвращаете скаляр:

sub b( --> Scalar ) { my $x=42; my $d = $x; return $d };
say b().WHAT;

Вы получаете другую ошибку

Type check failed for return value; expected Scalar but got Int (42)
  in sub b at scum.raku line 2
  in block <unit> at scum.raku line 7

Что здесь происходит?

Ну, raku автоматически деконтейнеризирует Scalar $x перед проверкой типа — так работают Scalar-контейнеры. Таким образом, при запуске стандартного кода вы должны использовать конкретные ограничения типа возвращаемого значения, такие как Int, Real, Num, IntStr, Allomorph, Cool и т. д. Встроенные типы Raku прекрасно продуманы, чтобы дать программисту точный контроль над уровнем этого теста.

НО - если вы хотите выполнять силовые трюки, вы можете применить ограничение --> Scalar следующим образом (как указано @raiph):

sub b( --> Scalar ) { my $x=42; my $d = $x.VAR; return $d };
say b().WHAT;   #(Scalar)

Другими словами, метаметод .VAR обходит обычное прозрачное разделение контейнеров Scalar, чтобы вы могли проверить, является ли ваш возвращаемый тип контейнером верхнего уровня.

Итак (как я уверен, вы поняли из лучшего ответа raifh), задокументируйте <41650273332498> как редкую форму, в отличие от обычной проверки --> Array.