Что считается внешним списком для слипа?

В документах для Slip упоминается, что Slip — это список, который автоматически сводится во внешний список (или другой список-подобный контейнер или итерируемый). Исходя из этого определения, это имеет смысл:

dd my @a = 1, |(2, 3); # OUTPUT: «Array @a = [1, 2, 3]»

Однако меня удивляет следующее:

dd my @b = do {@a[2] := |(3, 4); @a} # OUTPUT: «Array @b = [1, 2, slip(3, 4)]»

Я ожидал, что slip(3, 4) превратится в @b, а не останется slip. (То есть был удивлен, что @a[2] := |(3, 4) не имеет той же семантики, что и @a.splice(2, 1, [3, 4]).)

Рассматривается ли здесь присвоение списка как особый случай с другой семантикой, чем у обычных бланков? Или есть что-то в семантике бланков/списков/массивов, что делает все это согласованным без присваивания специального регистра?


person codesections    schedule 06.06.2021    source источник
comment
В данном случае он детализирован. Если он не будет де-детаминирован, он не сгладится.   -  person jjmerelo    schedule 06.06.2021


Ответы (2)


Slip — это список значений, который может совмещаться во внешнюю последовательность.

Таким образом, следующее создает сглаженный список.

1, |(2, 3)

Это происходит из-за запятой ,.

Если вы вставляете этот список в массив в заданной позиции, вы вставляете этот список в массив в одну заданную позицию.

@a[0] = 1, |(2, 3); # [(1,2,3),]

То же самое произойдет, если вы вставите Slip, поскольку Slip — это просто подкласс List.

@a[0] = |(2, 3); # [slip(2,3),]

На самом деле Slip — это почти исключительно список. Вот код от Rakudo для Slip.

# A Slip is a kind of List that is immediately incorporated into an iteration
# or another List. Other than that, it's a totally normal List.
my class Slip { # is List

    # XXX this makes an empty Slip undefined?
    multi method defined (Slip:D: --> Bool:D) { self.Bool }

    multi method Slip(Slip:D:) { self }
    method CALL-ME (+args)     { args.Slip }
    multi method raku(Slip:D: --> Str:D) {
        nqp::if(
          nqp::eqaddr(self,Empty),
          'Empty',
          nqp::stmts(
            (my str $guts = callsame),
            nqp::if(
              nqp::eqat($guts,'$',0), # we're itemized
              nqp::concat('$(slip',nqp::concat(nqp::substr($guts,1),')')),
              nqp::concat('slip',$guts)
            )
          )
        )
    }
    multi method List(Slip:D: --> List:D) {
        my $list := nqp::create(List);
        nqp::bindattr($list,List,'$!todo',nqp::getattr(self,List,'$!todo'))
          if nqp::isconcrete(nqp::getattr(self,List,'$!todo'));
        nqp::bindattr($list,List,'$!reified',nqp::getattr(self,List,'$!reified'))
          if nqp::isconcrete(nqp::getattr(self,List,'$!reified'));
        $list
    }
}

Это заставляет работать только 4 функции.

  1. |().defined Это определено?
    Если он содержит элементы, да, иначе нет.
    (Empty определенно не определено, но |(,) или slip(), возможно, должны быть определены. Этот метод просто говорит, что оба не определены.)
  2. Slip((1,2)) Превратите существующий список в бланк.
  3. Empty.raku / |(,).raku Напечатайте значение таким образом, чтобы его потенциально можно было оценить.
    Empty – это конкретный экземпляр пустой квитанции, который имеет специальную обработку во время выполнения.
  4. |().List Получите список вместо слипа.
    Это должно быть здесь, потому что слип является подклассом списка, поэтому обычно он просто возвращает сам себя.

Ничто из этого не имеет ничего общего с сведением Slip в список.

Обратите внимание, что даже в комментарии вверху указано, что это обычный список.


Вы можете сгладить его, если используете список (в данном случае List или Range) в качестве индекса.

@a[2,  ] = |(3,4); # [Any, Any, 3]
@a[2, 3] = |(3,4); # [Any, Any, 3, 4]
@a[2..3] = |(3,4); # [Any, Any, 3, 4]

Используя индекс списка, вы сообщаете Раку, что результатом операции @a[…] является список, а не одно значение.

Это не имеет ничего общего с тем, что rvalue является Slip. Это связано с тем, что rvalue является подклассом List.


Чуть более явно.

my $l-value := @a[2]; # contains the actual Scalar object in the array
my $r-value := |(3,4);
$l-value = $r-value;

Это в основном то же самое, что и ваш код

@a[2] = |(3,4);

@a[2] = — это две отдельные операции. Индексация, а затем присвоение

@a[2] возвращает скалярный контейнер, затем = присваивает значение справа этому единственному контейнеру.

Чтобы сгладить Slip, заданию потребуется доступ к самому массиву. Чего-то в нем нет. Он имеет доступ только к единственному скалярному контейнеру. Таким образом, он вставляет Slip в Scalar, который у него есть. (На самом деле назначение на самом деле не знает, что скаляр даже является членом массива.)

Когда вы связываетесь, вы делаете что-то немного другое. Дело в том, что привязка должна быть более низкого уровня, чем обычное присваивание. Таким образом, было бы даже более вероятно просто вставить в одно место вместо выравнивания.

Чтобы сделать что-то другое, @a[2] должен вернуть прокси, который знает, как сгладить Slip в массив.


Если вы действительно хотите это сделать, используйте splice, так как он имеет ссылку на массив.

my @a = 1, |(2, 3);
@a.splice: 2, 1, |(3,4);

Опять же, это не особенное из-за скольжения.

person Brad Gilbert    schedule 06.06.2021
comment
Спасибо, большая часть из этого имеет смысл. Я не совсем понимаю одну часть: почему @a[2, ] = |(3,4); не добавляет к @a и 3, и 4? Или, в более общем смысле, когда rvalue сводится к срезу @a, почему имеет значение длина slice /lvalue, а не только длина rvalue? - person codesections; 07.06.2021
comment
@codesections Дает только один из индексов. Если бы вы использовали @a[2,3,4], к массиву добавились бы 3, 4 и Any. - person Brad Gilbert; 08.06.2021

append и push оба хорошо играют со скольжением:

my @a = 1, |(2, 3);     #[1,2,3]
@a.append: |(3,4);      #[1 2 3 3 4]
@a.push: |(3,4);        #[1 2 3 3 4]

напротив, без проскальзывания:

@a.append: (3,4);       #[1 2 3 3 4]
@a.push: (3,4);         #[1 2 3 (3 4)]
person p6steve    schedule 06.06.2021