Пара тонкостей Perl - PullRequest
       47

Пара тонкостей Perl

2 голосов
/ 25 августа 2009

Я некоторое время программировал на Perl, но я никогда не понимал пару тонкостей Perl:

Использование и установка / удаление переменной $ _ смущают меня. Например, почему

# ...
shift @queue;
($item1, @rest) = split /,/;

работа, но (по крайней мере для меня)

# ...
shift @queue;
/some_pattern.*/ or die();

не похоже на работу?

Кроме того, я не понимаю разницы между итерацией по файлу с использованием foreach против while. Например, я, кажется, получаю разные результаты для

while(<SOME_FILE>){  
    # Do something involving $_        
}

и

foreach (<SOME_FILE>){
    # Do something involving $_
}

Может кто-нибудь объяснить эти тонкие различия?

Ответы [ 8 ]

13 голосов
/ 25 августа 2009
shift @queue;
($item1, @rest) = split /,/;

Если я вас правильно понимаю, вам кажется, что это сдвигает элемент с @queue на $_. Это не правда.

Значение, которое смещено на @queue, просто исчезает. Следующее split действует на все, что содержится в $_ (которое не зависит от вызова shift).

while(<SOME_FILE>){  
    # Do something involving $_        
}

Чтение из файлового дескриптора в выражении while является специальным: оно эквивалентно

while ( defined( $_ = readline *SOME_FILE ) ) {

Таким образом, вы можете построчно обрабатывать даже колоссальные файлы.

С другой стороны,

for(<SOME_FILE>){  
    # Do something involving $_        
}

сначала загрузит весь файл в виде списка строк в память. Попробуйте файл объемом 1 ГБ и увидите разницу.

5 голосов
/ 26 августа 2009

Другое, хотя и неуловимое, различие между:

while (<FILE>) {
}

и

foreach (<FILE>) {
}

заключается в том, что while () изменит значение $ _ вне своей области, тогда как foreach () делает $ _ локальным Например, умрет следующее:

$_ = "test";
while (<FILE1>) {
    print "$_";
}
die if $_ ne "test";

тогда как, это не будет:

$_ = "test";
foreach (<FILE1>) {
    print "$_";
}
die if $_ ne "test";

Это становится более важным с более сложными сценариями. Представьте себе что-то вроде:

sub func1() {
    while (<$fh2>) {  # clobbers $_ set from <$fh1> below
        <...>
    }
}

while (<$fh1>) {
    func1();
    <...>
}

Лично я не использую $ _ по этой причине, в дополнение к тому, что он менее читабелен и т.д.

3 голосов
/ 26 августа 2009

По второму вопросу:

while (<FILE>) {
}

и

foreach (<FILE>) {
}

Имеют такое же функциональное поведение, включая настройку $_. Разница в том, что while() оценивает <FILE> в скалярном контексте, а foreach() оценивает <FILE> в контексте списка. Рассмотрим разницу между:

$x = <FILE>;

и

@x = <FILE>;

В первом случае $x получает первую строку FILE, а во втором случае @x получает весь файл. Каждая запись в @x - это отдельная строка в FILE.

Итак, если FILE очень велико, вы тратите впустую память, теряя все сразу, используя foreach (<FILE>) по сравнению с while (<FILE>). Это может или не может быть проблемой для вас.

Место, где это действительно важно, это если FILE - дескриптор канала, как в:

open FILE, "some_shell_program|";

Теперь foreach(<FILE>) должен ждать завершения some_shell_program, прежде чем он сможет войти в цикл, в то время как while(<FILE>) может считывать вывод some_shell_program по одной строке за раз и выполнять параллельно some_shell_program.

Тем не менее, поведение в отношении $_ остается неизменным между двумя формами.

2 голосов
/ 25 августа 2009

foreach оценивает весь список заранее. while оценивает условие, чтобы увидеть, истинно ли оно при каждом проходе. Хотя следует учитывать для дополнительных операций, foreach только для источников списка.

Например:

my $t= time() + 10 ;
while ( $t > time() ) { # do something }
1 голос
/ 26 августа 2009
0 голосов
/ 26 августа 2009

Пожалуйста, прочитайте perldoc perlvar, чтобы вы имели представление о различных переменных в Perl.

perldoc perlvar .

0 голосов
/ 25 августа 2009

Во избежание путаницы такого рода лучше не использовать неявные конструкции $ _.

my $element = shift @queue;
($item,@rest) = split /,/ , $element;

или

($item,@rest) = split /,/, shift @queue;

также

while(my $foo = <SOMEFILE>){

do something 

}

или

foreach my $thing(<FILEHANDLE>){

  do something

}
0 голосов
/ 25 августа 2009

while только проверяет, является ли значение истинным, for также помещает значение в $_, за исключением некоторых случаев. Например, <> установит $_, если используется в цикле while.

чтобы получить похожее поведение:

foreach(qw'a b c'){
    # Do something involving $_
}

Вы должны установить $_ явно.

while( $_ = shift @{[ qw'a b c' ]} ){  
    # Do something involving $_        
}

Лучше явно установить переменные

for my $line(<SOME_FILE>){
}

или еще лучше

while( my $line = <SOME_FILE> ){
}

, который будет читать в файле только одну строку за раз.


Также shift не устанавливает $_, если вы не зададите этого слишком

$_ = shift @_;

И split работает на $_ по умолчанию. При использовании в скалярном или пустом контексте заполняется @_.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...