Привязка скаляра к переменной без сигилла (Perl 6) - PullRequest
0 голосов
/ 24 июня 2018

Позвольте мне начать с того, что я понимаю, что то, о чем я спрашиваю в названии, является сомнительной практикой (как объяснено здесь ), но мое непонимание касается синтаксиса.

Когда я впервые попытался связать скаляр с символом без знака, я сделал это:

my \a = $(3);

полагая, что $(...) упакует Int 3 в скаляр (как это предлагается в документации ), который затем будет связан с символом a. Это, похоже, не работает: скаляр нигде не найти (a.VAR.WHAT возвращает (Int), а не (скаляр)).

В вышеупомянутой публикации Раиф упоминает, что желаемое связывание может быть выполнено с использованием другого синтаксиса:

my \a = $ = 3;

который работает. Учитывая результат, я подозреваю, что утверждение может быть сформулировано эквивалентно, хотя и менее кратко, как: my \a = (my $ = 3), что я мог бы тогда понять.

Это оставляет вопрос: почему попытка с $(...) не работает, и что она делает вместо этого?

Ответы [ 2 ]

0 голосов
/ 24 июня 2018

Что делает $(…), это превращает значение в item.
(Значение в скалярной переменной ($a) также помечается как элемент)

say flat (1,2,      (3,4)  );
# (1 2 3 4)

say flat (1,2,    $((3,4)) );
# (1 2 (3 4))

say flat (1,2, item((3,4)) );
# (1 2 (3 4))

По сути, это предотвращает сглаживание значения. Причина его существования в том, что Perl 6 не сглаживает списки так сильно, как большинство других языков, и иногда вам нужно немного больше контролировать выравнивание.


Следующая только сортировка делает то, что вы хотите

my \a = $ = 3;

Обнаженная $ является анонимной state переменной.

my \a = (state $) = 3;

Проблема появляется, когда вы выполняете один и тот же бит кода более одного раза.

sub foo ( $init ) {
  my \a = $ = $init; # my \a = (state $) = $init;

  (^10).map: {
    sleep 0.1;
    ++a
  }
}

.say for await (start foo(0)), (start foo(42));
# (43 44 45 46 47 48 49 50 51 52)
# (53 54 55 56 57 58 59 60 61 62)

# If foo(42) beat out foo(0) instead it would result in:
# (1 2 3 4 5 6 7 8 9 10)
# (11 12 13 14 15 16 17 18 19 20)

Обратите внимание, что переменная является общей для вызовов.
Первый Promise останавливается при вызове sleep, а затем второй устанавливает переменную состояния перед первым запуском ++a.

Если вместо этого вы используете my $, теперь он работает правильно.

sub foo ( $init ) {
  my \a = my $ = $init;

  (^10).map: {
    sleep 0.1;
    ++a
  }
}

.say for await (start foo(0)), (start foo(42));
# (1 2 3 4 5 6 7 8 9 10)
# (43 44 45 46 47 48 49 50 51 52)

Дело в том, что сигиловые « переменные » на самом деле не являются переменными (они не меняются), они больше похожи на лексически ограниченные (не) константы.

constant \foo = (1..10).pick; # only pick one value and never change it

say foo;
for ^5 {
  my \foo = (1..10).pick;     # pick a new one each time through

  say foo;
}

По сути, весь их смысл в том, чтобы быть как можно ближе к значению, которое вы ему присваиваете. (Static Single Assignment)

# these work basically the same
-> \a        {…}
-> \a is raw {…}
-> $a is raw {…}

# as do these
my \a  = $i;
my \a := $i;
my $a := $i;

Обратите внимание, что выше я написал следующее:

my \a = (state $) = 3;

Обычно в объявлении состояния var присваивание происходит только при первом запуске кода. Голый $ не имеет декларации как таковой, поэтому я должен был предотвратить такое поведение, поместив декларацию в скобках.

# bare $
for (5 ... 1) {
  my \a =        $  = $_; # set each time through the loop

  say a *= 2; # 15 12 9 6 3
}

# state in parens
for (5 ... 1) {
  my \a = (state $) = $_; # set each time through the loop

  say a *= 2; # 15 12 9 6 3
}

# normal state declaration
for (5 ... 1) {
  my \a =  state $  = $_; # set it only on the first time through the loop

  say a *= 2; # 15 45 135 405 1215
}
0 голосов
/ 24 июня 2018

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

my \a = $(3); 
say a.WHAT; # OUTPUT: «(Int)␤»
say a.VAR.WHAT; # OUTPUT: «(Int)␤»

Здесь, выполнив $ (3), вы фактически помещаете в скалярный контекст то, что уже находится в скалярном контексте:

my \a = 3; say a.WHAT; say a.VAR.WHAT; # OUTPUT: «(Int)␤(Int)␤»

Однако вторая форма в вашем вопросе делает что-то другое,Вы привязываетесь к анонимной переменной, которая является контейнером:

my \a = $ = 3; 
say a.WHAT;    # OUTPUT: «(Int)␤»
say a.VAR.WHAT;# OUTPUT: «(Scalar)␤»

В первом случае a был псевдонимом для 3 (или $(3), что совпадает);во втором a - это псевдоним для $, который является контейнером, значение которого равно 3.Этот последний случай эквивалентен:

 my $anon = 3; say $anon.WHAT; say $anon.VAR.WHAT; # OUTPUT: «(Int)␤(Scalar)␤»

(Если у вас есть какие-либо предложения по улучшению документации, я был бы рад ее рассмотреть)

...