Каков синтаксис удаления элемента массива, если вы не знаете его индекс?

Вы можете использовать наречие :delete в Perl 6 чтобы удалить элемент массива, если вы знаете его индекс:

my @letters = <a b c>; @letters[0]:delete; say @letters
# OUTPUT: «[(Any) b c]␤»

Однако вы не можете этого сделать, если не знаете индекс:

my @letters = <a b c>; $_:delete if $elem eq 'a' for @letters
#ERROR! → Variable '$_:delete' is not declared

Если вы объявите переменную цикла is rw, проблема останется прежней:

my @letters = <a b c>; for @letters -> $l is rw { $l:delete if $l eq 'a' }; say @letters
#ERROR! → Variable '$l:delete' is not declared

Другого способа удаления элементов массива, похоже, не существует. эквивалент Perl 5 delete фактически указывает на это наречие. Вы можете использовать splice, но, опять же, вам нужно знать индекс. Реализация выглядит как этой функцией, _8 _, которому фактически необходимо знать индекс массива.

Итак, вопрос, как и в заголовке, есть ли способ удалить элемент массива (или, если на то пошло, ассоциативного массива), если у вас есть дескриптор только самого элемента, а не его индекса?


person jjmerelo    schedule 02.06.2018    source источник
comment
Как следует из сообщения об ошибке, синтаксис $foo:bar - это единственный идентификатор, а не идентификатор в сочетании с наречием :delete. Синтаксически :delete должен следовать за явным оператором нижнего индекса (например, [...]), чтобы его можно было интерпретировать как наречие. Я предполагаю, что вы знали это, но подумали, что я прокомментирую это для тех, кто прочитает позже.   -  person raiph    schedule 02.06.2018


Ответы (2)


Зависит от того, что вы называете delete. Внутри массивов выполнение DELETE-POS привязывает элемент массива к nqp::null, что отбрасывает любой контейнер, находящийся в этой позиции. На уровне HLL это представлено типом массива:

my Str @letters = <a b c>;
@letters[1]:delete;
say @letters;   # [a (Str) c]

Однако вы можете добиться того же эффекта, назначив Nil:

my Str @letters = <a b c>;
@letters[1] = Nil;
say @letters;   # [a (Str) c]

В этом случае контейнер в этом элементе остается прежним, вы просто позволяете ему вернуться в свое «естественное» состояние. И если вас устраивает такой тип удаления, вы также можете использовать его в своем цикле:

my Str @letters = <a b c>;
$_ = Nil if $_ eq "b" for @letters;
say @letters;   # [a (Str) c]

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

my Str @letters is default<foo> = <a b c>;
$_ = Nil if $_ eq "b" for @letters;
say @letters;   # [a foo c]

Если вы хотите действительно удалить элементы из списка без сохранения существующих элементов на их позициях, вы, конечно, можете использовать grep:

my @letters = <a b c>;
@letters .= grep: * ne "b";
say @letters;  # [a c]

Но это не похоже на то, что вы намеревались.

person Elizabeth Mattijsen    schedule 02.06.2018

Пожалуйста, прочтите ответ Лизмат, чтобы получить вдумчивый и полезный анализ вашего титульного вопроса, в котором она обращает должное внимание на двусмысленность английского слова «удаление».

Вот один из способов вернуть одно из предложений в ее ответе к использованию :delete наречия, чтобы последний @letters оставался тем же массивом с первым элементом :delete'd, поэтому результат будет таким же, как ваш исходный ([(Any) b c]):

my @letters = <a b c>;
.[.grep: 'a', :k]:delete given @letters;
say @letters;

Наречие :k на grep говорит, что вы хотите, чтобы результат был индексом.

person raiph    schedule 02.06.2018