Программист учел проблемы в функции read_file_with_metadata
(см. предыдущую историю), исправил их и заодно немного сократил код,
избавившись от переменной @stat
:
1 # Получает имя файла,
2 # читает файл и его метаданные,
3 # возвращает ссылку на хеш { filename => '...', content => '...', size => NNN }.
4 # Если файл не существует, content и size равны undef
5 sub read_file_with_metadata
6 {
7 my ($filename) = @_;
8
9 return {
10 filename => $filename,
11 size => (stat($filename))[7],
12 content => scalar eval {scalar read_file($filename)},
13 };
14 }
И снова не все ладно: на этот раз неправильные значения иногда оказываются в поле size
.
Пример кода можно скачать и поотлаживаться локально.
Снова проблемы с несуществующими файлами.
Пример неправильного результата:
{
'' => undef,
'filename' => '<nonexistent file>',
'size' => 'content'
};
В случае неуспеха (например, файл не найден), stat
возвращает пустой список.
Что будет, если взять 7-й элемент пустого списка? А 7-й элемент пустого массива?
Во-первых, если бы программист использовал прагму warnings
,
он по крайней мере заметил бы предупреждение
Odd number of elements in anonymous hash
.
Во-вторых, в строчке 11 выбирается 7-й элемент из списочного литерала, не из переменной-массива.
Хотя извлекается всего один элемент, на самом деле это срез (slice),
а срезы списковых литералов ведут себя следующим образом:
если все запрашиваемые индексы выходят за пределы списка, срез возвращает пустой список.
Иначе (если хотя бы один индекс попал в размер списка)
возвращаются элементы списка, дополненные undef
-ами для слишком больших индексов.
Примеры со списковыми литералами:
()[0] # ()
()[1,2] # ()
("a")[0] # ("a")
("a")[0,1] # ("a", undef)
("a")[1,2] # ()
Для сравнения – срезы массива:
@a = (); @a[0]; # (undef)
@a = (); @a[1,2]; # (undef, undef)
@a = ("a"); @a[0]; # ("a")
@a = ("a"); @a[0,1] # ("a", undef)
@a = ("a"); @a[1,2] # (undef, undef)
Подробнее про поведение срезов массивов и списковых литералов см. http://perldoc.perl.org/perldata.html#Slices.