avva: (Default)
[personal profile] avva
Сегодня один из классических багов Перла привёл к тому, что в только что созданные журналы/коммьюнити невозможно было отправить записи, в течение 5 часов примерно. То есть баг не самого перла как языка, а типичный баг в программах на нём: в данном случае, путаница между 0 и undef.

Однако, несмотря на такие баги, отдельное значение undef в Перле себя оправдало, по-моему.

Другой типичный баг, который время от времени всплывает в исходниках ЖЖ, связан с тем фундаментальным фактом, что в Перле нет отдельных типов строк и чисел; скаляр в Перле может содержать целое число, или действительное число, или строку, и переводить одно в другое в зависимости от контекста. Но это значит, в частности, что проверка if("0") не проходит, т.к. "0" это то же самое, что 0, т.е. false. Для строк, возможно, удобнее было бы иметь другой критерий истинности, при котором любая непустая строка истинна, а пустая - ложна, но не получается. Из-за забывания этого иногда получаются баги.

Третий баг возникает как раз вследствие единственного нарушения вышеописанного принципа. Оператор & (двоичный and) действует по-разному в зависимости от того, что расположено внутри скаляров, на которые он действует: числа или строки.

$ perl -e 'print 12 & 5;'
4
$ perl -e 'print "12" & "5";'
1


Почему результаты разные? Потому что & действует на двоичное представление скаляра, не пытаясь привести его к канонической форме (которая в данном случае должна быть численной, конечно). Когда выполняется 12 & 5, выходит двоичный and между 1100 и 0101; результат - 0100, 4. Когда же выполняется "12" & "5", происходит двоичный and между ASCII-представлением строки "12", т.е.
00110001 00110010 , и ASCII-представлением строки "5", т.е. 00110101 . В результате выходит то же, самое, что "1" & "5", т.к. второму байту строки "12" ничего не соответствует в строке "5"; а
"1" & "5" выходит 00110001, т.е. "1", что корректно преобразуется при надобности в 1.

Таким образом, "x" & "y" и x & y дают одинаковый результат, если x и y - цифры от 0 до 9 (это выходит благодаря тому, что ASCII-представления цифр 0..9 начинаются с 48, т.е. совпадают с самими цифрами в четырёх последних двоичных регистрах), но если числа выходят за предел 0..9, результаты выходят разными.

Особенно больно это бьёт по разработчику, если он использует & для вычисления двоичной маски секьюрити, скажем, в целях обеспечения привилегированного доступа к какому-то объекту. Если при этом его числа, с которыми он делает &, на самом деле не числа, а в данный момент строки (например, потому, что их вернул модуль доступа к БД, всегда возвращающий строки, или Перл их внутри перевёл в строки для какой-то операции, того же print), то результат операции будет неверен, и легко может случиться так, что непривилегированный юзер получит доступ к объекту (всё это касается и двоичного or, кстати, т.е. оператора |, но он используется намного реже на практике, так что редко случаются баги с ним).


Четвёртый... пусть будет на баг, а kludge довольно забавный внутри Перла.
Цитируя документацию функции ioctl:
              The return value of "ioctl" (and "fcntl") is as follows:
 
                       if OS returns:          then Perl returns:
                           -1                    undefined value
                            0                  string "0 but true"
                       anything else               that number
 
               Thus Perl returns true on success and false on failure, yet you
               can still easily determine the actual value returned by the
               operating system:
 
                   $retval = ioctl(...) || -1;
                   printf "System returned %d\n", $retval;
 
               The special string "0 but true" is exempt from -w complaints
               about improper numeric conversions.

То же верно ещё для нескольких функций, являющихся перловскими обложками для системных функций: они возвращают обычно результат системной функции или undef в случае ошибки, но, если системная функция возвращает 0 и это для неё не ошибка, они возвращают "0 but true", таким образом пытаясь предотвратить второй баг, описанный выше: если неосторожный разработчик напишет
if(ioctl(...)), то это сработает, т.к. "0 but true" истинно, в отличие от "0"; а если нужно перевести в числовой контекст, то "0 but true" переведётся в 0:
$ perl -e 'print "0 but true" + 5;'
5


Но если мы запускаем perl с опцией -w (warnings), то обычно перевод строки с не-числовым "мусором" в число выдаёт предупреждение:
$ perl -w -e 'print "12garbage" + 5;' 
Argument "12garbage" isn't numeric in addition (+) at -e line 1.
17


А со строкой "0 but true" это не происходит, как и обещано в документации выше:

$ perl -w -e 'print "0 but true" + 5;' 
5


И это верно только для этой строки. На уровне исходников Перла это происходит так. В файле sv.c, имплементирующем операции со скалярами (sv=scalar value), есть функция looks_like_number(). Её вызывают, когда нужно определить, можно ли перевести данную строку в число; она смотрит на строку, и возвращает ноль, если эта строка не представляет из себя число, и ненулевое значение в обратном случае, причём тогда она возвращает значение, указывающее на то, какую функцию надо использовать для перевода данной строки в число (atol() для целых чисел, atof() для действительных). И вот, в этой функции, когда она уже проверила, выглядит ли строка как целое или действительное число, и вернула соответствующие значения в таких случаях, и уже совсем готова возвращать 0, указывая на неудачу, там стоит:

    if (len == 10 && memEQ(sbegin, "0 but true", 10))
        return IS_NUMBER_TO_INT_BY_ATOL;
    return 0;


Классический пример того, что по-английски называют kludge, по-моему.

Date: 2003-10-18 10:00 am (UTC)
From: [identity profile] igorlord.livejournal.com
Когда же выполняется "12" & "5", происходит двоичный and между ASCII-представлением строки "12", т.е.
00110001 00110010 , и ASCII-представлением строки "5", т.е. 00110101 . В результате выходит то же, самое, что "1" & "5", т.к. второму байту строки "12" ничего не соответствует в строке "5"; а
"1" & "5" выходит 00110001, т.е. "1", что корректно преобразуется при надобности в 1.


Это не является ли одним из примеров того что Perl -- просто плохо продуманный язык? Я помню здесь раньше был пример того что случайный break внутри под-процедуты может вдруг "найти" какой-то цикл в вызываящих процедурах и сработать как non-local goto из того цикла. Это-же бред! Не понимаю как кто-то может изпользовать этот hack (Perl) для мало-мальски серьёзных систем. (Про необязательность декларации переменных я не говорю, хотя это похоже скорее на BASIC чем на серьёзный язык).

Date: 2003-10-18 10:03 am (UTC)
lxe: (Default)
From: [personal profile] lxe
use strict - и никакой необязательности.

Я вообще не понимаю

Date: 2003-10-18 10:04 am (UTC)
From: [identity profile] payalnik.livejournal.com
не понимаю зачем сегодня нужен перл

Date: 2003-10-18 10:14 am (UTC)
From: [identity profile] meshko.livejournal.com
А что сегодня случилось, что он вдруг перестал быть нужен? Или это Вы про субботу?
Есть куча людей, которые его хорошо знают и могут очень эффективно пистаь на нём скрипты для обработки текстов, например.
Конечно сейчас есть альтернативы, и я, когда мне нужно сделать что-нибудь перловское, делаю это на питоне, но перл от этого не перестал быть нужен тем, кто его по-настоящему хорошо знает и любит.

Date: 2003-10-18 10:14 am (UTC)
From: [identity profile] ex-ilyavinar899.livejournal.com
В Амазоне сейчас всё больше будет использоваться Пёрл; меня тошнит.

Date: 2003-10-18 10:16 am (UTC)
stas: (Default)
From: [personal profile] stas
А в каких областях?
Это, впрочем, довольно логично - знающих Perl стало довольно много, а писать на нём мелкие вещи значительно проще, чем на C[++].

Date: 2003-10-18 02:36 pm (UTC)
From: [identity profile] igorlord.livejournal.com
Согласен. Пока вещи очень маленькие, то можено на чём угодно писать. Но когда она "подростают", то это тихий ужас. Не только в индустрии куча "хэкеров" (терпеть не могу!), но если и язык этому способстует, то через год активного "development", код станет не поддерживаемый никем кроме самого автора (и то большой вопрос).

(no subject)

From: [identity profile] avva.livejournal.com - Date: 2003-10-18 02:40 pm (UTC) - Expand

(no subject)

From: [identity profile] igorlord.livejournal.com - Date: 2003-10-18 03:06 pm (UTC) - Expand

(no subject)

From: [personal profile] stas - Date: 2003-10-19 12:45 am (UTC) - Expand

(no subject)

From: [identity profile] igorlord.livejournal.com - Date: 2003-10-19 07:42 am (UTC) - Expand

(no subject)

From: [personal profile] nine_k - Date: 2003-10-20 02:52 am (UTC) - Expand

(no subject)

From: [identity profile] igorlord.livejournal.com - Date: 2003-10-20 06:43 am (UTC) - Expand

(no subject)

From: [personal profile] nine_k - Date: 2003-10-20 06:57 am (UTC) - Expand

(no subject)

From: [identity profile] igorlord.livejournal.com - Date: 2003-10-20 07:28 am (UTC) - Expand

(no subject)

From: [personal profile] nine_k - Date: 2003-10-20 07:35 am (UTC) - Expand

(no subject)

From: [identity profile] avva.livejournal.com - Date: 2003-10-20 07:37 am (UTC) - Expand

(no subject)

From: [personal profile] nine_k - Date: 2003-10-20 08:16 am (UTC) - Expand

(no subject)

From: [identity profile] onodera.livejournal.com - Date: 2003-10-21 10:06 am (UTC) - Expand

(no subject)

From: [identity profile] igorlord.livejournal.com - Date: 2003-10-20 10:16 am (UTC) - Expand

(no subject)

From: [personal profile] nine_k - Date: 2003-10-21 06:01 am (UTC) - Expand

(no subject)

From: [identity profile] igorlord.livejournal.com - Date: 2003-10-21 06:40 am (UTC) - Expand

Date: 2003-10-18 10:30 am (UTC)
From: [identity profile] avva.livejournal.com
То у тебя "Perl rules", то тошнит. Хороший язык, только привыкнуть надо.

Date: 2003-10-18 10:56 am (UTC)
From: [identity profile] meshko.livejournal.com
Так все правильно. "There is more than one way to look at it" ;)

Date: 2003-10-18 05:27 pm (UTC)
From: [identity profile] cema.livejournal.com
Так же, говорят, с некоторыми наркотиками: тошнит, а всё равно хочется.

Date: 2003-10-18 11:03 am (UTC)
From: [identity profile] syarzhuk.livejournal.com
Переменные, как тут уже объясняли, можно требовать объявлять через use strict.
Ошибка была не в случайном break'е, а в случайном next'е, которого в тех процедурах (после того, как из них убрали цикл) не должно было там быть. Другое дело, что такие вещи надо было отлавливать на этапе компиляции/интерпретации.
На самом деле Перл - офигительно мощный язык, но с очень нетрадиционной логикой. Каждый раз, когда мне надо написать что-то на Перле, у меня уходит 30-60 минут на переключение мозгов, а потом 5 минут на собственно написание.

Date: 2003-10-18 11:38 am (UTC)
From: [identity profile] avva.livejournal.com
Это не является ли одним из примеров того что Perl -- просто плохо продуманный язык?

Нет. Перл - хорошо продуманный язык, который сознательно идёт на множество "некрасивых" и неэффективных трюков внутри имплементации runtime-среды ради удобства программиста, которое ставится на первое место. Mutability чисел в строки и обратно - исключительно удобное для программиста свойство языка, и хорошо продуманное к тому же; отдельные недочёты, типа этой проблемы с &, этого факта не меняют. В любом языке есть свои недочёты. А проблему с & обещают исправить в следующей версии, кстати.

Date: 2003-10-18 02:49 pm (UTC)
From: [identity profile] igorlord.livejournal.com
удобства программиста, которое ставится на первое место

Это же анафема! Вот я и говорю что это очень большая ошибка допускать в индустрию язык, который ставит на первое место "удобства" программиста. Нужны системы которые на 1ое место ставят читабельность и поддерживаемость кода. Пусть програмист потратит в 2 раза больше времени пиша нечто чтобы другой человек смог на много быстрее разобраться что к чему и, очень важно, был уверен что меняя нечто он не ломает какой-то "хитрый трюк". К сожалению, в индустрии слишком много "хэкеров", готовых изощряться до умопомрачения "для совего удобства". Если ещё и язык этому способствует, то получается тихий ужас.

(no subject)

From: [identity profile] msh.livejournal.com - Date: 2003-10-18 03:37 pm (UTC) - Expand

(no subject)

From: [identity profile] igorlord.livejournal.com - Date: 2003-10-18 04:36 pm (UTC) - Expand

(no subject)

From: [identity profile] msh.livejournal.com - Date: 2003-10-18 05:13 pm (UTC) - Expand

(no subject)

From: [identity profile] igorlord.livejournal.com - Date: 2003-10-18 06:27 pm (UTC) - Expand

(no subject)

From: [identity profile] msh.livejournal.com - Date: 2003-10-19 01:03 am (UTC) - Expand

(no subject)

From: [identity profile] igorlord.livejournal.com - Date: 2003-10-19 08:09 am (UTC) - Expand

(no subject)

From: [identity profile] msh.livejournal.com - Date: 2003-10-19 10:53 am (UTC) - Expand

(no subject)

From: [identity profile] igorlord.livejournal.com - Date: 2003-10-19 01:16 pm (UTC) - Expand

(no subject)

From: [identity profile] msh.livejournal.com - Date: 2003-10-19 01:36 am (UTC) - Expand

(no subject)

From: [identity profile] igorlord.livejournal.com - Date: 2003-10-19 08:24 am (UTC) - Expand

(no subject)

From: [identity profile] msh.livejournal.com - Date: 2003-10-19 10:44 am (UTC) - Expand

(no subject)

From: [identity profile] igorlord.livejournal.com - Date: 2003-10-19 12:37 pm (UTC) - Expand

(no subject)

From: [identity profile] 109.livejournal.com - Date: 2003-10-19 08:39 am (UTC) - Expand

(no subject)

From: [identity profile] msh.livejournal.com - Date: 2003-10-19 10:30 am (UTC) - Expand

(no subject)

From: [identity profile] xfyre.livejournal.com - Date: 2003-11-15 02:12 pm (UTC) - Expand

(no subject)

From: [identity profile] snaky.livejournal.com - Date: 2003-10-20 09:08 pm (UTC) - Expand

(no subject)

From: [identity profile] 109.livejournal.com - Date: 2003-10-21 05:38 am (UTC) - Expand

(no subject)

From: [identity profile] snaky.livejournal.com - Date: 2003-10-21 08:21 am (UTC) - Expand

(no subject)

From: [identity profile] 109.livejournal.com - Date: 2003-10-21 08:43 am (UTC) - Expand

(no subject)

From: [identity profile] snaky.livejournal.com - Date: 2003-10-21 09:46 am (UTC) - Expand

(no subject)

From: [identity profile] 109.livejournal.com - Date: 2003-10-21 09:59 am (UTC) - Expand

(no subject)

From: [identity profile] snaky.livejournal.com - Date: 2003-10-21 02:10 pm (UTC) - Expand

(no subject)

From: [personal profile] stas - Date: 2003-10-19 12:44 am (UTC) - Expand

(no subject)

From: [identity profile] igorlord.livejournal.com - Date: 2003-10-19 07:34 am (UTC) - Expand

(no subject)

From: [personal profile] stas - Date: 2003-10-19 08:25 am (UTC) - Expand

Date: 2003-10-18 06:03 pm (UTC)
From: (Anonymous)
и хорошо, что про "необязательность декларации переменных" не говорите, - не стоит так-то в полной вакуоли на месте языков с выводимыми типами данных расписываться. вполне серьезных и редкостно удобных и мощных языков. надомник, видать, самопальный.

а Перл, что Перл, в каждом поколении подтекающая кухонная раковина положена. хоть не PL/I или алгол-68

Date: 2003-10-18 10:06 am (UTC)
From: [identity profile] meshko.livejournal.com
По-моему необходимость делать вещи вроде "0 but true" указывает на то, что язык изначально построен неправильно. Во всяком случае есть же другие untyped языки, которые обошлись без этого.

Date: 2003-10-18 10:23 am (UTC)
From: [identity profile] avva.livejournal.com
Скорее это указывает на то, что встроенная библиотека была построена неправильно; в погоне за большей и большей лаконичностью вызовов функций стандартной библиотеки пришлось пойти на kludge в самом языке. Надо было остановиться до этого.

В конце концов, в C ведь тоже if(ioctl(...)) было бы багом, т.к. ioctl() может вернуть 0 в случае успеха. В C делать нечего, приходится писать что-то вроде if(ioctl(...)!=-1) , а в Перле разработчики встроенной библиотеки хотели упростить это для программиста на Перле, но зашли слишком далеко в этом своём стремлении.

Date: 2003-10-18 01:54 pm (UTC)
From: [identity profile] sartoris.livejournal.com
В конце концов, в C ведь тоже if(ioctl(...)) было бы багом, т.к. ioctl() может вернуть 0 в случае успеха. В C делать нечего, приходится писать что-то вроде if(ioctl(...)!=-1) , а в Перле разработчики встроенной библиотеки хотели упростить это для программиста на Перле, но зашли слишком далеко в этом своём стремлении.

В C всё-таки надо писать if( ioctl(...) == 0 ) - потому как при позитивной валидации, нужно искать позитивный результат, а не отстутсвие отрицательного. Впрочем POSIX ввел стандарт 0=success по той простой причине, что логичней было бы:

if( ioctrl(...) ) {
error handling
}

и так получается короче, чем if(ioctl(...) != 0)...

А perl, при всём моём к нему уважении, один из тех языков, которые соблазняют на спагетти. Впрочем плохой код, при желании, можно написать на любом языке:)

(no subject)

From: [identity profile] msh.livejournal.com - Date: 2003-10-18 02:04 pm (UTC) - Expand

(no subject)

From: [identity profile] avva.livejournal.com - Date: 2003-10-18 02:05 pm (UTC) - Expand

(no subject)

From: [identity profile] sartoris.livejournal.com - Date: 2003-10-18 02:09 pm (UTC) - Expand

Date: 2003-10-18 02:54 pm (UTC)
From: [identity profile] igorlord.livejournal.com
То что в C нет boolean типа и язык позволяет использовать любые численные выражения как conditionals -- большая недороботка языка. Максимум на что бы я согласился для языка - -использовать ссылки как conditionals (NULL или не NULL).

(no subject)

From: [personal profile] stas - Date: 2003-10-19 09:10 am (UTC) - Expand

(no subject)

From: [identity profile] igorlord.livejournal.com - Date: 2003-10-19 09:46 am (UTC) - Expand

(no subject)

From: [personal profile] stas - Date: 2003-10-19 09:57 am (UTC) - Expand

my 2 cents

From: [identity profile] alehhandro.livejournal.com - Date: 2003-10-19 01:02 pm (UTC) - Expand

Date: 2003-10-18 10:14 am (UTC)
stas: (Default)
From: [personal profile] stas
Интересно, что в PHP, который в каком-то смысле родственен Perlу, есть похожие проблемы с числами и строками, изображающими числа. Правда, у PHP есть булвеский тип и, соответственно, родные true и false. Я думаю, все эти штуки растут из того факта, что оба языка часто использовались для обработки текстовой информации и данный, полученых, скажем, от http-сервера. Понятно, что эти данные должны быть строками, т.к. сервер не станет разбирать, число там или не число. С другой стортоны, хочется, чтобы что-то вроде:

$count_plus_two = $input{'field1'}+2;

было именно так просто как написано, а не требовало чего-то вроде atoi со всеми сопутствующими проблемами. Соответственно, числа и строки делаются автоматически конвертируемыми друг в друга, а дальше уже вырастает весь букет.

Ещё интересно: "0 but true" на самом деле не столь уж уникален. Например, выражение "1 but true" + 2 равно 3, а "13 and a candle" + "42 is the answer" - естественно, 55. Т.е. "0 but true" - в цифровом контексте просто 0.

Date: 2003-10-18 10:19 am (UTC)
From: [identity profile] avva.livejournal.com
Ещё интересно: "0 but true" на самом деле не столь уж уникален. Например, выражение "1 but true" + 2 равно 3, а "13 and a candle" + "42 is the answer" - естественно, 55. Т.е. "0 but true" - в цифровом контексте просто 0.

Ну да, это просто следствие того, что всё равно вызывается atol() для перевода в числовой контекст, а atol() переводит столько цифр, сколько сможет, пока не встречает незаконный для числа символ. Уникальность "0 but true" не в этом, а в том, что только эта строка не выдаёт warning в режиме -w, при таком переводе.

Date: 2003-10-18 10:14 am (UTC)
From: [identity profile] oblomov-jerusal.livejournal.com
Ужасы какие рассказываете.

Date: 2003-10-18 12:15 pm (UTC)
From: [identity profile] msh.livejournal.com

Для строк, возможно, удобнее было бы иметь другой критерий истинности, при котором любая непустая строка истинна, а пустая - ложна


if (strlen($s))

А все остальное - мне просто непонятно, как можно путать undef/0, ==/eq и т.п. Там же под этим лежит ровно одно правило приведения строки к числу, которое надо просто один раз понять и запомнить






Date: 2003-10-18 04:31 pm (UTC)
From: [identity profile] dmarck.livejournal.com
По-моему, именно ради таких distinctions в DBI возвращают в соответствующих случаях строку "0E0" ;-)

For a non-SELECT statement, execute returns the number of rows
affected, if known. If no rows were affected, then execute returns
"0E0", which Perl will treat as 0 but will regard as true. Note
that it is not an error for no rows to be affected by a statement.
If the number of rows affected is not known, then execute returns
-1.

Date: 2003-10-18 05:17 pm (UTC)
From: [identity profile] msh.livejournal.com
Да, кстати. Вот ведь Perl-ский undef очень похож на SQL-ый NULL и они даже друг в друга конвертируются. Так что это концепция совсем не perl-specific

Date: 2003-10-18 06:08 pm (UTC)
From: (Anonymous)
не так давно один из классических багов исторического материализма привел к тому, что Советский Союз накрылся. а ведь какая мощная была система.

December 2025

S M T W T F S
  123 4 56
78 9 10 11 1213
1415 1617181920
21 22 23 24 2526 27
2829 30 31   

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jan. 1st, 2026 06:34 pm
Powered by Dreamwidth Studios