Почему File :: Slurp возвращает скаляр, когда он должен вернуть список? - PullRequest
3 голосов
/ 02 июня 2010

Я новичок в модуле 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 ]

9 голосов
/ 02 июня 2010

Логический оператор или || связывает более тесно, чем оператор присваивания, и переводит вызов функции в скалярный контекст .

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 в контексте списка, а затем оценивает назначенный список в скалярном контексте, получая количество элементов в массиве. Таким образом, вы сначала назначаете массив, а затем не умираете, потому что в массиве есть записи.

Вторая форма не пытается присвоить "return" от die до @array, потому что присвоение выполняется первым, поэтому @array либо содержит строки файла, либо является пустым массивом.

Обратите внимание , что в документации для File::Slurp read_file написано:

В list context он вернет список строк (используя текущее значение $ / в качестве разделителя, включая поддержку режима абзаца, когда он установлен в ''). В скалярном контексте он возвращает весь файл как один скаляр. [ Курсив мой ]

И приводит следующие примеры:

    my $text = read_file( 'filename' ) ;
    my @lines = read_file( 'filename' ) ;

Но это самые простые случаи и самые основные выражения контекста. Присвоение переменной определенного типа не гарантирует, что @lines будет назначено в контексте списка независимо от окружающего кода.

6 голосов
/ 02 июня 2010

Оператор || помещает свой левый операнд в логический (скалярный) контекст:

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
scalar

Чтобы исправить вашу программу, вы можете немного ее изменить:

my @array = eval { read_file("/etc/issue") };
die unless @array;

Это необходимо, только если вы хотите добавить сообщение об ошибке (хранится в специальной переменной $@ после eval), поскольку read_file будет die, если что-то пойдет не так.

4 голосов
/ 02 июня 2010

На ваш вопрос уже дан ответ. В качестве примечания, во-первых, имейте в виду, что or и and лучше подходят для операторов управления потоком. Однако, что более важно, проверка ниже

 my @array = read_file( $file ) || die "Cannot read $file\n";

совершенно не нужно. По умолчанию File :: Slurp :: read_file croak s при ошибке:

err_mode

Вы можете использовать эту опцию для управления поведением read_file при возникновении ошибки. Эта опция по умолчанию имеет значение «квакает». Вы можете установить его на «carp» или «quiet», чтобы не обрабатывать ошибки.

...