Найти размер массива в Perl

243

Кажется, я натолкнулся на несколько разных способов найти размер массива. В чем разница между этими тремя методами?

my @arr = (2);
print scalar @arr; # First way to print array size

print $#arr; # Second way to print array size

my $arrSize = @arr;
print $arrSize; # Third way to print array size
Дэвид
источник
13
другие способы: print 0+@arr, print "".@arr,print ~~@arr
моб
3
@mob, гул, можно хотеть избежать, "".@arrпоскольку "@arr"делает что-то совсем другое.
Икегами
39
«Второй способ» - это НЕ способ напечатать размер массива ...
tadmc
в скалярном контексте; @arr возвращает размер таблицы. $ x = @ arr - скалярный контекст. $ # arr возвращает последний индекс массива. индексация начинается с 0, а затем выполняется уравнение $ # arr + 1 == @arr. Если вы записываете некоторый элемент не по порядку, например, $ arr [100] = 'any', то таблица автоматически увеличивается до максимального индекса 100 и (включая индекс 0) до 101 элемента.
Зник

Ответы:

234

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

Второй способ фактически возвращает последний индекс массива, который (обычно) не совпадает с размером массива.

Крис Шут-Янг
источник
29
Размер (1,2,3) равен 3, а индексы (по умолчанию) 0, 1 и 2. Таким образом, $ # arr будет 2 в этом случае, а не 3.
Nate CK
5
Предопределенная переменная $[указывает «Индекс первого элемента в массиве и первого символа в подстроке» ( perldoc perlvar). По умолчанию он установлен на 0, и установка его на любое значение, отличное от 0, крайне не рекомендуется.
Кит Томпсон,
5
@ Кит Томпсон, $[обескуражен (и был в течение десятилетия). $[устарела. Использование $[выдает предупреждение об устаревании, даже если вы не включаете предупреждения. Присвоение чего-либо, кроме нуля, $[будет ошибкой в ​​5.16. Можем ли мы перестать упоминать $[уже?
Икегами
2
@ Кит Томпсон, на самом деле старше 5.14. Но, как я уже сказал, это было обескуражено и осуждается гораздо дольше, и кто-то, использующий, $[будет знать о его последствиях.
Икегами
7
@ikegami: Да, но кто - то пытается понять разницу между scalar @arrи $#arrдолжны еще понять возможные последствия $[, редко , хотя они есть.
Кейт Томпсон
41

Во-первых, второе не эквивалентно двум другим. $#arrayвозвращает последний индекс массива, который на единицу меньше размера массива.

Два других практически одинаковы. Вы просто используете два разных средства для создания скалярного контекста. Это сводится к вопросу читабельности.

Я лично предпочитаю следующее:

say 0+@array;          # Represent @array as a number

Я нахожу это яснее, чем

say scalar(@array);    # Represent @array as a scalar

и

my $size = @array;
say $size;

Последнее выглядит довольно ясно, как это, но я считаю, что лишняя строка лишает ясности, когда часть другого кода. Это полезно для обучения тому, что @arrayделает в скалярном контексте, и, возможно, если вы хотите использовать $sizeболее одного раза.

Ikegami
источник
15
Лично я предпочитаю версию, которая использует ключевое слово «scalar», потому что совершенно очевидно, что оно форсирует скалярный контекст. my $size=@arrayПохоже, это может быть ошибкой, когда использовался неправильный символ.
Nate CK
5
Это действительно плохая идея. Люди, которые используют scalarбез причины, получают неправильный урок. Они начинают думать, что операторы возвращают списки, которые можно привести в скаляры. Видел это десятки раз.
икегами
2
Почему это «без причины»? Вы используете, scalarпотому что вы приводите список в скалярный контекст. Это правильная причина использовать его. Ваш пример делает то же самое, но полагается на то, что делает Perl, когда вы оцениваете переменную списка в неявно скалярном контексте. Таким образом, ваш пример требует, чтобы читатель знал о неявном поведении Perl в этом контексте. Вы просто добавляете еще один слой неявного поведения к выражению, и Perl уже имеет слишком много неявного поведения, которое вы должны объяснить, чтобы расшифровать программу.
Нейт СК
2
@Nate CK, Re "Почему это" без причины "? Вы используете, scalarпотому что приводите список в скалярный контекст", Вы доказали мою точку зрения о том, что выучили неправильный урок. Это совершенно неверно. Никакой список никогда не принуждается scalar. (Если бы он сделал scalar(@array)и scalar(@array[0..$#array])вернул бы то же самое.) scalar(@array)Велит @arrayвернуть скаляр, с которым вы уже сказали, что делать my $size=.
икегами
2
Верьте или нет, разработчики должны отлаживать код, написанный другими разработчиками. И разработчики должны отлаживать код, который они написали три года назад.
Нейт СК
27

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

print scalar @arr;

Это еще один способ приведения массива в скалярный контекст, поскольку он присваивается скалярной переменной:

my $arrSize = @arr;

Это получает индекс последнего элемента в массиве, так что это на самом деле размер минус 1 (при условии, что индексы начинаются с 0, что настраивается в Perl, хотя обычно это плохая идея):

print $#arr;

Этот последний не очень хорош для получения размера массива. Было бы полезно, если вы просто хотите получить последний элемент массива:

my $lastElement = $arr[$#arr];

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

Нейт СК
источник
2
Sidenote: просто используйте, $arr[-1]чтобы получить последний элемент. И $arr[-2]чтобы получить предпоследний, и так далее.
Туомассало
1
@tuomassalo: Я согласен, что ваше предложение - лучший подход. Оглядываясь назад, $#arrэто не очень полезная функция, и не случайно, что в других языках ее нет.
Nate CK
6

Чтобы использовать второй способ, добавьте 1:

print $#arr + 1; # Second way to print array size
jhoanna
источник
for [0..$#array] { print $array[$_ ] } работает очень хорошо, хотя, если целью получения количества элементов является перебор массива. Преимущество в том, что вы получаете элемент, а также счетчик, которые выровнены.
Уэстрок
5

Все три дают одинаковый результат, если мы немного изменим второй:

my @arr = (2, 4, 8, 10);

print "First result:\n";
print scalar @arr; 

print "\n\nSecond result:\n";
print $#arr + 1; # Shift numeration with +1 as it shows last index that starts with 0.

print "\n\nThird result:\n";
my $arrSize = @arr;
print $arrSize;
Зон
источник
5
Это чем-то отличается от того, что уже упоминалось в этом и этом ответе ?
Devnull
5

Пример:

my @a = (undef, undef);
my $size = @a;

warn "Size: " . $#a;   # Size: 1. It's not the size
warn "Size: " . $size; # Size: 2
Dimas
источник
2

Разделе «Perl типы переменных» в документации perlintro содержит

Специальная переменная $#arrayсообщает вам индекс последнего элемента массива:

print $mixed[$#mixed];       # last element, prints 1.23

Возможно, $#array + 1вы захотите сказать, сколько элементов в массиве. Не беспокойся Как это происходит, использование @arrayтам , где Perl ожидает найти скалярное значение («в скалярном контексте»), даст вам количество элементов в массиве:

if (@animals < 5) { ... }

Документация perldata также описывает это в разделе «Скалярные значения» .

Если вы оцениваете массив в скалярном контексте, он возвращает длину массива. (Обратите внимание, что это не так для списков, которые возвращают последнее значение, такое как оператор запятой C, или для встроенных функций, которые возвращают то, что они хотят вернуть.) Следующее всегда верно:

scalar(@whatever) == $#whatever + 1;

Некоторые программисты предпочитают использовать явное преобразование, чтобы не оставлять никаких сомнений:

$element_count = scalar(@whatever);

Ранее в этом же разделе описано, как получить индекс последнего элемента массива.

Длина массива является скалярным значением. Вы можете найти длину массива @days, оценив $#days, как в csh. Однако это не длина массива; это индекс последнего элемента, который имеет другое значение, поскольку обычно существует 0-й элемент.

Грег Бэкон
источник
2

Существуют различные способы печати размера массива. Вот значения всех: допустим, наш массивmy @arr = (3,4);

Метод 1: скаляр

Это правильный способ получить размер массивов.

print scalar @arr;  # prints size, here 2

Способ 2: индексный номер

$#arrдает последний индекс массива. поэтому, если массив имеет размер 10, то его последний индекс будет 9.

print $#arr;     # prints 1, as last index is 1
print $#arr + 1; # Add 1 to last index to get array size

Мы добавляем 1 здесь, считая массив индексированным 0 . Но если его не на основе нуля, эта логика потерпит неудачу .

perl -le 'local $[ = 4; my @arr=(3,4); print $#arr + 1;'   # prints 6

Приведенный выше пример печатает 6, потому что мы установили его начальный индекс на 4. Теперь индекс будет 5 и 6, с элементами 3 и 4 соответственно.

Способ 3:

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

my $size = @arr;
print $size;   # prints size, here 2

На самом деле метод 3 и метод 1 одинаковы.

Камаль Наян
источник
2

Из perldoc perldata , которую следует с уверенностью процитировать:

Следующее всегда верно:

scalar(@whatever) == $#whatever + 1;

До тех пор, пока вы не $ # независимо от ++ и загадочно увеличиваете размер или ваш массив.

Индексы массива начинаются с 0.

и

Вы можете обрезать массив до нуля, назначив ему пустой список (). Следующее эквивалентно:

    @whatever = ();
    $#whatever = -1;

Что подводит меня к тому, что я искал, как определить, что массив пуст. Я нашел это, если $ # empty == -1;

jwal
источник
1

А int(@array)как насчет того, чтобы он стал аргументом скалярным?

отражающий
источник
0

Чтобы найти размер массива, используйте scalarключевое слово:

print scalar @array;

Чтобы узнать последний индекс массива $#(переменная Perl по умолчанию). Это дает последний индекс массива. Поскольку массив начинается с 0, мы получаем размер массива, добавляя его к $#:

print "$#array+1";

Пример:

my @a = qw(1 3 5);
print scalar @a, "\n";
print $#a+1, "\n";

Вывод:

3

3
Sandeep_black
источник