Почему print ($ a = a..c) выдает: 1E0 - PullRequest
10 голосов
/ 04 ноября 2011
print (a..c) # this prints: abc  
print ($a = "abc") # this prints: abc

print ($a = a..c); # this prints: 1E0

Я бы подумал, что это напечатает: abc

use strict;
print ($a = "a".."c"); # this prints 1E0

Почему? Это только мой компьютер? редактировать: у меня есть частичный ответ (оператор диапазона .. возвращает логическое значение в скалярном контексте - спасибо), но я не понимаю: почему: print ($ a = "a" ... "c") выдает 1 вместо 0 почему: print ($ a = "a" .. "c") выдает 1E0 вместо 1 или 0

Ответы [ 3 ]

17 голосов
/ 04 ноября 2011

Здесь происходит множество тонких вещей. Во-первых, .. - это два совершенно разных оператора в зависимости от контекста, в котором он вызывается. В контексте списка он создает список значений (с шагом в 1) между заданной начальной и конечной точками.

@numbers =  1  ..  3;  # 1, 2, 3
@letters = 'a' .. 'c'; # a, b, c (Yes, Perl can increment strings)

Поскольку print интерпретирует свои аргументы в контексте списка

print 'a' .. 'c';    # <-- this
print 'a', 'b', 'c'; # <-- is equivalent to this

В скалярном контексте .. является оператором триггера. С Операторы диапазона в perlop:

Это ложно, если его левый операнд ложен. Однажды левый операнд равен true, оператор диапазона остается истинным до тех пор, пока правильный операнд истина, ПОСЛЕ того, как оператор диапазона снова становится ложным.

Присваивание скалярному значению, как в $a = ..., создает скалярный контекст. Это означает, что .. в print ($a = 'a' .. 'c') является экземпляром оператора триггера, а не оператором создания списка.

Оператор триггера предназначен для использования при фильтрации строк в файле. например,

while (<$fh>) {
    print if /first/ .. /last/;
}

будет печатать все строки в файле, начиная с той, которая содержит first и заканчивая той, которая содержит last.

У оператора триггера есть дополнительная магия, разработанная для упрощения фильтрации по номеру строки.

while (<$fh>) {
    print if 10 .. 20;
}

напечатает строки с 10 по 20 файла. Это достигается с помощью особого поведения:

Если любой операнд скаляра .. является постоянным выражением, то операнд считается истинным, если он равен (==) текущему входу номер строки (переменная $.).

Строки a и c являются константными выражениями, поэтому они вызывают этот особый случай. Они не числа, но они используются как числа (== - числовое сравнение). Perl будет преобразовывать скалярные значения между строками и числами по мере необходимости. В этом случае оба значения нумеруются до 0. Поэтому

print ($a = 'a' .. 'c');             # <-- this
print ($a = 0 .. 0);                 # <-- is effectively this
print ($a = ($. == 0) .. ($. == 0)); # <-- which is really this

Мы приближаемся ко дну тайны. К следующему биту. Больше от perlop:

Возвращаемым значением является либо пустая строка для false, либо последовательность число (начиная с 1) для истины. Порядковый номер сбрасывается для каждый встреченный диапазон. Конечный порядковый номер в диапазоне имеет строка "E0" добавлена ​​к нему

Если вы еще не прочитали ни одной строки из файла, $. будет undef, что равно 0 в числовом контексте. 0 == 0 имеет значение true, поэтому .. возвращает истинное значение. Это первое истинное значение, поэтому оно 1. Поскольку и левая и правая стороны имеют значение true, первое истинное значение также является последним истинным значением, а к возвращаемому значению добавляется суффикс E0 «это последнее значение». Это , поэтому print ($a = 'a' .. 'c') печатает 1E0. Если вы установите $. в ненулевое значение, .. будет ложным и вернет пустую строку.

print ($a = 'a' .. 'c'); # prints "1E0"
$. = 1;
print ($a = 'a' .. 'c'); # prints nothing

Самая последняя часть головоломки (и я мог бы зайти слишком далеко) состоит в том, что оператор присваивания возвращает значение. В этом случае это значение присваивается $a 1 - 1E0. Это значение является то, что в конечном итоге выплевывает print.

1: Технически, назначение создает lvalue для назначенного элемента. т.е. он возвращает lvalue для переменной $a, которая затем оценивается в 1E0.

12 голосов
/ 04 ноября 2011

Это вопрос контекста списка и скалярного контекста, как объяснено в perldoc perlop:

В скалярном контексте ".." возвращает логическое значение.Оператор является бистабильным, как триггер, и эмулирует оператор диапазона строк (запятая) sed, awk и различных редакторов.Каждый оператор «..» поддерживает свое собственное логическое состояние, даже при вызовах подпрограммы, которая его содержит.Это ложно, пока его левый операнд ложен.Если левый операнд равен true, оператор диапазона остается истинным, пока правый операнд не станет true, ПОСЛЕ того, как оператор диапазона снова становится ложным.Это не становится ложным, пока в следующий раз оператор диапазона не будет оценен.Он может проверить правильный операнд и стать ложным при той же оценке, в которой он стал истинным (как в awk), но он все равно возвращает истину один раз.Если вы не хотите, чтобы он проверял правильный операнд до следующей оценки, как в sed, просто используйте три точки ("...") вместо двух.Во всех других отношениях, "..." ведет себя так же, как и "..".

[snip]

Последний порядковый номер в диапазоне содержит строкуК нему добавлено «E0», которое не влияет на его числовое значение, но дает вам возможность искать, если вы хотите исключить конечную точку.

EDIT в ответ наКомментарий Дэнда:

Мне тоже трудно переваривать;честно говоря, я редко использую оператор .., а еще реже - в скалярном контексте.Но, например, выражение 5..10 в цикле ввода неявно сравнивается с текущим значением $. (это часть описания, которое я не цитировал; см. Руководство).В строках с 5 по 9 он возвращает истинное значение (эксперимент показывает, что это число, но в документации этого не сказано).В строке 10 он возвращает число с добавленным к нему "E0", т. Е. Оно имеет экспоненциальную запись, но с тем же значением, которое было бы без "E0".

Точка "E0" твик позволяет вам определить, находитесь ли вы в указанном диапазоне и , чтобы отметить последнюю строку в диапазоне для специальной обработки.Без "E0" вы не смогли бы обработать финальный матч специально.

Пример:

#!/usr/bin/perl

use strict;
use warnings;

while (<>) {
    my $dotdot = 2..4;
    print "On line $., 2..4 yields \"$dotdot\"\n";
}

При 5 вводимых строках это печатает:

On line 1, 2..4 yields ""
On line 2, 2..4 yields "1"
On line 3, 2..4 yields "2"
On line 4, 2..4 yields "3E0"
On line 5, 2..4 yields ""

Это позволяет определить, находится ли линия внутри или вне диапазона и , когда это последняя строка в диапазоне.

Но скаляр .., вероятно, используется чащетолько для его логического результата, часто в однострочниках;например, perl -ne 'print if 2..4' напечатает строки 2, 3 и 4 любого ввода, который вы дадите.Это намеренно похоже на sed -n '2,4p'.

10 голосов
/ 04 ноября 2011

Ответ можно найти, обратившись к странице perlop страницы perldoc:

Двоичный ".." - оператор диапазона, который на самом деле представляет собой два разных оператора в зависимости от контекста.В контексте списка он возвращает список значений, подсчитывающих (с увеличением на единицу) от левого значения к правому значению ...

Это знакомое использование, которое вызывается print "a" .. "c";, потому чтоаргументы функций оцениваются в контексте списка.(Если бы они оценивались в скалярном контексте, то print @list напечатал бы размер @list, что почти точно не то, что обычно хотят люди.)

В скалярном контексте ".. "возвращает логическое значение. Оператор является бистабильным, как триггер, и эмулирует оператор строкового диапазона (запятая) для sed, awk и различных редакторов.Каждый оператор «..» поддерживает свое собственное логическое состояние, даже при вызовах подпрограммы, которая его содержит. Это ложно, если его левый операнд ложен.Если левый операнд имеет значение «истина», оператор диапазона остается истинным, пока правый операнд не становится «истиной», ПОСЛЕ того, как оператор диапазона снова становится ложным. Он не становится ложным до следующей оценки оператора диапазона.Он может проверить правильный операнд и стать ложным при той же оценке, в которой он стал истинным (как в awk), но он все равно возвращает истину один раз.Если вы не хотите, чтобы он проверял правильный операнд до следующей оценки, как в sed, просто используйте три точки ("...") вместо двух.Во всех других отношениях «...» ведет себя так же, как и «..».

Это более подробно, но выделенные жирным шрифтом разделы являются важными частями для понимания работы оператора.Скалярный контекст форсируется $a =, то есть присваиванием скалярному lvalue.Если вы сделали @a =, он напечатал бы то, что вы ожидаете.

Обратите внимание, что "a" .. "b" не производит строку "abc", он создает список ("a", "b", "c").Вы получите аналогичные результаты, если использовали список (хотя значение, выводимое при принудительном переводе списка в скалярный контекст).

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