Идиома Perl для получения максимального количества элементов в массиве

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

@foo = @foo[ 0 .. 4 ];

и от души хвалил мою сообразительность. Но это сломалось, как только @foo оказалось только с тремя элементами, потому что тогда я получил два undef на конце вместо трехэлементного массива. Поэтому я изменил его на:

@foo = @foo > 5 ? @foo[ 0 .. 4 ] : @foo;

Это работает, но как-то некрасиво. Есть ли лучшая идиома для фразы «дайте мне все до первых пяти элементов массива?»


person friedo    schedule 27.07.2010    source источник
comment
Только потому, что я не являюсь носителем английского языка, я нахожу название вопроса совершенно неудовлетворительным? Я ожидаю, что быстрые читатели (включая Google) поймут это как идиому Perl для получения максимального числа из массива.   -  person Wolf    schedule 12.04.2017


Ответы (4)


Вы можете установить последний индекс массива, чтобы сократить или удлинить его. Как и ваш код, вам нужно будет проверить, не создаете ли вы элементы undef.

$#foo = 4 if $#foo > 4;
person Ven'Tatsu    schedule 27.07.2010
comment
Интересный взгляд на это!! Я не уверен, что мне это нравится больше, чем splice(), но мне нравится, что оно уникально. - person Evan Carroll; 28.07.2010
comment
Я забыл про это! :) +1 - person Axeman; 28.07.2010
comment
Потрясающий. Мне очень нравится этот. - person friedo; 28.07.2010
comment
Н.Б. Без if это страдает от того же, тогда я получил два undef на конце вместо проблемы с трехэлементным массивом. - person ysth; 28.07.2010
comment
@ysth, верно, я не понимаю, как это отвечает на вопрос. Вы по-прежнему используете функцию, требующую явной проверки. Даже если rw-ness $#foo отличный. Он мог бы изменить свой вопрос на @foo = @foo[0..4] if $#foo > 4; и получить почти то же самое (если мы просто пытаемся достичь не-немного уродливого) - person Evan Carroll; 28.07.2010

Если вас не интересуют мутации (подразумеваемые самореферентным lhs @foo = нечто, ссылающееся на @foo), используйте splice() с двумя аргументами, см. perldoc -f splice для получения дополнительной информации.

СОЕДИНЕНИЕ МАССИВ,СМЕЩЕНИЕ

Удаляет элементы, указанные OFFSET и LENGTH из массива, и заменяет их элементами LIST, если они есть. В контексте списка возвращает элементы, удаленные из массива. В скалярном контексте возвращает последний удаленный элемент или undef, если элементы не удалены. Массив увеличивается или уменьшается по мере необходимости. Если OFFSET отрицательное, то оно начинается так далеко от конца массива. Если LENGTH не указан, удаляется все, начиная со OFFSET. Если LENGTH имеет отрицательное значение, удаляются элементы, начиная со OFFSET, за исключением элементов -LENGTH в конце массива. Если и OFFSET, и LENGTH опущены, удаляется все. Если OFFSET превышает конец массива, Perl выдает предупреждение и выполняет сращивание в конце массива.

Затем наблюдайте за эффектом:

@_ = 1..10;
splice @_, 5;
say for @_;


@_ = 1..3;
splice @_, 5;
say for @_;

Если вы используете warnings, и я надеюсь, вам придется проверить длину (как в предложении Axeman) или отключить шумное предупреждение (splice() смещение за конец массива):

{
  no warnings 'misc';
  splice @_, 5;
}
person Evan Carroll    schedule 27.07.2010
comment
Я всегда забываю о splice - person friedo; 27.07.2010
comment
Да, кажется, что @foo = @foo[ stuff; ] делать по кругу, сращивание будет намного быстрее. - person Evan Carroll; 27.07.2010
comment
К сожалению, это приводит к смещению предупреждения splice() за конец массива, поэтому вы можете добавить local $SIG{__WARN__}, когда делаете это, или выполнить проверку длины, как сказал Аксман. - person Ether; 27.07.2010
comment
@Ether, это не печально, наверное, это хорошая идея. Я обновил вопрос, указав правильный способ отключить предупреждение. - person Evan Carroll; 27.07.2010

Еще один способ:

@foo = splice(@foo, 0, 5);

В отличие от другого предложения по сращиванию, это не вызывает предупреждения; 5 явно означает «до 5».

person ysth    schedule 28.07.2010
comment
наиболее явное выражение того, что требуется splice - perldoc.perl.org это также самое короткое решение от психологического POV ;) - person Wolf; 12.04.2017

Это не так изящно, но вы можете выразить это так:

@foo[ 0..( $#foo > 4 ? 4 : $#foo ) ];

Обобщенная функция min может выглядеть лучше.

use List::Util qw<min>;    
@foo[ 0..min( $#foo, 4 ) ];

Но если вы просто хотите избавиться от всего остального, вам просто нужно splice избавиться от всего остального:

splice( @foo, 5 ) if 5 < @foo;
person Axeman    schedule 27.07.2010