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, по-моему.
Page 1 of 3 << [1] [2] [3] >>

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

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:14 am (UTC)
From: [identity profile] ex-ilyavinar899.livejournal.com
В Амазоне сейчас всё больше будет использоваться Пёрл; меня тошнит.

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

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

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

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

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

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 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 12:15 pm (UTC)
From: [identity profile] msh.livejournal.com

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


if (strlen($s))

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






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, при всём моём к нему уважении, один из тех языков, которые соблазняют на спагетти. Впрочем плохой код, при желании, можно написать на любом языке:)

Date: 2003-10-18 02:04 pm (UTC)
From: [identity profile] msh.livejournal.com
Принято возвращать из ioctl -1 в случае ошибки, все остальные значения - не ошибка. Может быть 0 - success, могут быть осмысленные значения

Date: 2003-10-18 02:05 pm (UTC)
From: [identity profile] avva.livejournal.com
ioctl() возвращает 0 или >0 в случае удачного завершения, -1 в случае ошибки; так, например, в Линуксе и BSDях. Поэтому сравнение с нулём не проходит. Обычно она возвращает 0, но некоторые вызовы используют результат для передачи какой-то информации и возвращают положительное значение при успехе. Насколько мне известно, POSIX тоже не предписывает success=0 в случае ioctl() (более того, позволяет любое значение кроме -1 в качестве success, хотя на практике используется только >=0).

Date: 2003-10-18 02:09 pm (UTC)
From: [identity profile] sartoris.livejournal.com
RETURN VALUE
On success, zero is returned. On error, -1 is returned,
and errno is set appropriately.

Впрочем действительно - с некоторыми устройствами, возвращаться будет совсем другое значение. Я как бы имел ввиду основную конвенцию 0=success и её исторические предпосылки. Не ругайтесь:)

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

Date: 2003-10-18 02:40 pm (UTC)
From: [identity profile] avva.livejournal.com
то через год активного "development", код станет не поддерживаемый никем кроме самого автора (и то большой вопрос).

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

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

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

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

Date: 2003-10-18 03:06 pm (UTC)
From: [identity profile] igorlord.livejournal.com
Понимаю. Я прекрасно понимая что есть множество мест и мегабайтами кода на Перле. Может группа людей поддерживающих LJ особенно строго относится к програмной дисциплине. Может эта группа особенно талантлива. А может если бы это был не Перл а, скажем, Java или ещё что, то было бы ещё проще поддерживать и модернизировать код. Или, может быть, надо подождать ещё 7 лет.

Из того что я понимаю, ошибки приведённые выше (и другие, с тем break или next) -- результат плохой семантики языка, позволяющей делать такие "глупости".
Page 1 of 3 << [1] [2] [3] >>

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 09:55 pm
Powered by Dreamwidth Studios