Почему File::Slurp возвращает скаляр, когда он должен возвращать список?
Я новичок в модуле File::Slurp, и в моем первом тесте он не дал ожидаемых результатов. Мне потребовалось некоторое время, чтобы понять это, так что теперь меня интересует, почему я вижу это определенное поведение.
Мой вызов File::Slurp выглядел так:
my @array = read_file( $file ) || die "Cannot read $file\n";
Я включил часть "die", потому что я привык делать это при открытии файлов. Мой @array всегда будет содержать все содержимое файла в первом элементе массива. Наконец я вынул раздел "|| die", и он начал работать, как я и ожидал.
Вот пример для иллюстрации:
perl -de0
Loading DB routines from perl5db.pl version 1.22
Editor support available.
Enter h or `h h' for help, or `man perldebug' for more help.
main::(-e:1): 0
DB<1> use File::Slurp
DB<2> $file = '/usr/java6_64/copyright'
DB<3> x @array1 = read_file( $file )
0 'Licensed material - Property of IBM.'
1 'IBM(R) SDK, Java(TM) Technology Edition, Version 6'
2 'IBM(R) Runtime Environment, Java(TM) Technology Edition, Version 6'
3 ''
4 'Copyright Sun Microsystems Inc, 1992, 2008. All rights reserved.'
5 'Copyright IBM Corporation, 1998, 2009. All rights reserved.'
6 ''
7 'The Apache Software License, Version 1.1 and Version 2.0'
8 'Copyright 1999-2007 The Apache Software Foundation. All rights reserved.'
9 ''
10 'Other copyright acknowledgements can be found in the Notices file.'
11 ''
12 'The Java technology is owned and exclusively licensed by Sun Microsystems Inc.'
13 'Java and all Java-based trademarks and logos are trademarks or registered'
14 'trademarks of Sun Microsystems Inc. in the United States and other countries.'
15 ''
16 'US Govt Users Restricted Rights - Use duplication or disclosure'
17 'restricted by GSA ADP Schedule Contract with IBM Corp.'
DB<4> x @array2 = read_file( $file ) || die "Cannot read $file\n";
0 'Licensed material - Property of IBM.
IBM(R) SDK, Java(TM) Technology Edition, Version 6
IBM(R) Runtime Environment, Java(TM) Technology Edition, Version 6
Copyright Sun Microsystems Inc, 1992, 2008. All rights reserved.
Copyright IBM Corporation, 1998, 2009. All rights reserved.
The Apache Software License, Version 1.1 and Version 2.0
Copyright 1999-2007 The Apache Software Foundation. All rights reserved.
Other copyright acknowledgements can be found in the Notices file.
The Java technology is owned and exclusively licensed by Sun Microsystems Inc.
Java and all Java-based trademarks and logos are trademarks or registered
trademarks of Sun Microsystems Inc. in the United States and other countries.
US Govt Users Restricted Rights - Use duplication or disclosure
restricted by GSA ADP Schedule Contract with IBM Corp.
'
Почему || умереть изменить ситуацию? У меня есть ощущение, что это может быть больше вопрос о приоритете Perl вместо вопроса File::Slurp. Я посмотрел в модуле File::Slurp, и похоже, что он настроен на квакинг, если есть проблема, поэтому я думаю, что правильный способ сделать это - позволить File::Slurp квакать за вас. Теперь мне просто любопытно, почему я видел эти различия.
3 ответа
||
оператор помещает свой левый операнд в логический (скалярный) контекст:
C-style Logical Or
двоичный
||
выполняет операцию логического ИЛИ короткого замыкания. То есть, если левый операнд имеет значение true, правый операнд даже не оценивается. Скалярный или списочный контекст распространяется вниз до правого операнда, если он вычисляется.
Вы также можете обнаружить это опытным путем:
#! /usr/bin/perl
sub lhs {
my $ctx = wantarray;
if (defined $ctx) {
if ($ctx) { "list" }
else { "scalar" }
}
else { "void" }
}
print lhs || 0, "\n";
Выход:
$./or-ctx.pl скаляр
Чтобы исправить вашу программу, вы можете немного ее изменить:
my @array = eval { read_file("/etc/issue") };
die unless @array;
Это необходимо, только если вы хотите добавить сообщение об ошибке (хранится в специальной переменной $@
после eval
) так как read_file
будут die
если что-то пойдет не так.
Логическое значение или оператор ||
связывает жестче, чем оператор присваивания, и помещает вызов функции в скалярный контекст.
my @array = read_file( $file ) || die "Cannot read $file\n";
То есть: попробуйте прочитать файл и вернуть либо конкатенацию файла, либо "return" из die
как первый элемент в @array
, Вы не умрете, потому что он прочитал файл и вернул истинное значение, хотя и скалярное.
Стандартное использование - оператор или оператор (or
), вот так:
my @array = read_file( $file ) or die "Cannot read $file\n";
Это пытается назначить @array
в контексте списка, а затем оценивает назначенный список в скалярном контексте, получая количество элементов в массиве. Таким образом, вы сначала назначаете массив, а затем не умираете, потому что в массиве есть записи.
Вторая форма не пытается назначить "возврат" из die
в @array
потому что назначение выполняется первым, так @array
либо содержит строки файла, либо является пустым массивом.
Обратите внимание, что документация для File::Slurp
read_file
говорит:
В контексте списка он вернет список строк (используя текущее значение $/ в качестве разделителя, включая поддержку режима абзаца, когда он установлен на ''). В скалярном контексте он возвращает весь файл как один скаляр. [ курсив мой ]
И приводит следующие примеры:
my $text = read_file( 'filename' ) ;
my @lines = read_file( 'filename' ) ;
Но это простейшие случаи и самые основные выражения контекста. Присвоение переменной определенного типа не гарантирует, что @lines
будет назначен в контексте списка независимо от окружающего кода.
На ваш вопрос уже дан ответ. Как примечание стороны, во-первых, имейте в виду, что or
а также and
лучше подходят для операторов управления потоком. Однако, что более важно, проверка ниже
my @array = read_file( $file ) || die "Cannot read $file\n";
совершенно ненужно. По умолчанию File:: Slurp:: read_file croak
s по ошибке:
err_mode
Вы можете использовать эту опцию для управления поведением read_file при возникновении ошибки. Эта опция по умолчанию имеет значение "квакает". Вы можете установить его на "carp" или "quiet", чтобы не обрабатывать ошибки.