Почему печатает ($ a = a..c): 1E0

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

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

Ответ 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:

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

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

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

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

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

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

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

будет печатать строки с 10 по 20 файла. Он делает это, используя особый случай поведения:

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

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

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

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

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

Если вы еще не прочитали строки из файла, $. будет undef, который 0 в числовом контексте. 0 == 0 истинно, поэтому .. возвращает истинное значение. Это первое истинное значение, поэтому оно 1. Поскольку и левая, и правая стороны истинны, первое истинное значение также является последним истинным значением, а суффикс 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.

Ответ 2

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

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

[надрез]

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

EDIT в ответ на комментарий пользователя DanD:

Мне тоже сложно переварить; честно говоря, я редко использую оператор .. и еще реже в скалярном контексте. Но, например, выражение 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'.

Ответ 3

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

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

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

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

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

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