Сегодня наткнулся на непонятное (с точки зрения Паскаля) поведение FPC. Вот такой простейший код, совершенно корректно отрабатывающий в Турбо Паскале:
type tset = set of 'a' .. 'z'; const myset: tset = ['a' .. 'z']; s: string = 'the test'; count: integer = 0; var i: integer;
begin for i := 1 to length(s) do begin if s[ i ] in myset then inc(count); end; writeln(count); end.
Попробуйте без его компиляции и запуска определить, что будет выведено на печать. А потом запустите на выполнение...
Так что осторожнее с множествами...
TarasBer
5.11.2010 23:02
Должно быть 7 же, а в ФПЦ чё за прикол происходит?
Archon
5.11.2010 23:17
FreePascal 2.4.0. Предсказуемо, 7.
volvo
5.11.2010 23:20
Ну вот, и я думал, что 7...
Однако, на самом деле происходит очень неприятная штука (FPC 2.4.0, Debug/Normal mode, любой из режимов совместимости TP/ObjFPC/Delphi/FPC):
Running "f:\programs\pascal\tst.exe " No heap dump by heaptrc unit Exitcode = 201 Runtime error 201 at $00401481 $00401481 main, line 15 of F:/Programs/Pascal/tst.pp $004083C1
15 строка - это
if s[i] in myset then inc(count);
Как только очередной символ строки не входит во множество допустимых значений для myset - программа завершается аварийно при попытке проверить In. В багтрекере есть одна ошибка, связанная с множествами (она уже исправлена, в 2.5.1). Но этой нет. Вот я и думаю, это ж баг? Так быть не должно - не присутствует значение во множестве, значит надо вернуть False и все.
Lapp
6.11.2010 2:45
Цитата(volvo @ 5.11.2010 23:20)
Вот я и думаю, это ж баг? Так быть не должно - не присутствует значение во множестве, значит надо вернуть False и все.
Другими словами, ты полагаешь, что тут должно работать автоматическое приведение типов? Но как?
Мне такое поведение представяется логичным.. То есть сравнение (выяснение принадлежности) происходит только для элементов множества. Не элемент - выяснение невозможно.
Сейчас пытался сделать нечто подобное для чисел и без множеств.. Не вышло )). Числовые типы приводятся, похоже, всегда. Например:
{$R+} type tN = 2..8;
var n: tN;
begin if n<5 then WriteLn('good'); if n<10 then WriteLn('bad'); ReadLn end.
- это отрабатывает на ура без всяких ошибок..
volvo
6.11.2010 2:59
Цитата
Не элемент - выяснение невозможно.
, и надо выбрасывать критическую ошибку, да? В топку такое поведение. У меня не строка может состоять из символов 'a'..'z', а посторонняя переменная. И если символ из строки в ней не присутствует или не может присутствовать - то In должен вернуть False.
Заметь, подобное поведение было как минимум до версии 2.0.0 (на двойке не помню, на 1.0.10 проверил только что - работает), обратная совместимость где?
Да. И еще одно - что мне совершенно непонятно. Откомпилируем программу в Options -> Mode = Release, все прекрасно работает и в 2.4.0. То есть, все-таки баг в отладочных режимах.
Lapp
6.11.2010 3:04
Цитата(volvo @ 6.11.2010 2:59)
И еще одно - что мне совершенно непонятно. Откомпилируем программу в Options -> Mode = Release, все прекрасно работает и в 2.4.0. То есть, все-таки баг в отладочных режимах.
Гм. А разве в моде Release включен Range Checking?..
Володь, я в целом согласен, что фича ненужная и бесполезная. Я не могу привести примера для себя, где оно бы помогло. Но все же логику тут отрицать пока не могу. Я еще подумаю над этим..
Добавлено через 4 мин.
Цитата(volvo @ 6.11.2010 2:59)
, и надо выбрасывать критическую ошибку, да?
Это обычный RangeCheck.. Выключи/включи его для этого куска кода - и проблема решена..
Lapp
6.11.2010 8:26
Цитата(Lapp @ 6.11.2010 3:04)
Я еще подумаю над этим..
Я подумал, как и обещал. И по-прежнему считаю, что это совершенно правильное поведение компилятора строго типизированного языка (каковым является объектный Паскаль в поздних реализациях). Справедливости ради, скажу, что я так и думал, что именно это произойдет еще при первом просмотре кода. Я понимаю, что это не довод, конечно )).
В твоем примере происходит обработка ВСЕХ входящих символов. Значит, конструкция для обработки должна принимать их ВСЕ. То, что в число "хороших" символов могут входить только буквы, еще не значит, что ты можешь ими ограничиться при обработке. Поэтому введение типа tset в этом коде не есть правильное действие. Если в твоем потоке символов предполагаются буквы и пробелы, а также цифры, но (допустим) никак не управляющие символы, то организуй соответствующее множество, и RCE (в процессе отладки, разумеется) будет означать, что пролез какой-нить ctrl-c, что есть признак ошики в коде и требует реакции программера.
Так что, тут нет никакого бага, мне кажется. IMHO, ессно. Но довольно твердое .
Archon
6.11.2010 10:53
Delphi не ругается ни при выключенном RangeCheck, ни при включенном. При операциях с числами аргументы приводятся к большему диапазону, так-что я не вижу смысла в случае с множествами поступать иначе.
Client
6.11.2010 11:25
ошибка всегда, если Range Checking включен, во всех 3 режимах. и я считаю это злостным багом
Lapp
7.11.2010 5:31
Цитата(Archon @ 6.11.2010 10:53)
При операциях с числами аргументы приводятся к большему диапазону, так-что я не вижу смысла в случае с множествами поступать иначе.
То, что ты его не видишь, не значит, что его нет )). Я уже привел ситуацию, когда такое поведение может быть полезным. Что касается больший/меньший диапазон - это неправильное проектирование числовых свойств на множества. Мне это тоже сначала пришло в голову, потом я покрутил в мозгах и понял, что это совсем разные вещи. В числах диапазон - это, как правило, ограничение, наложенное вычислительными возможностями. А тут ты можешь задать
tak = set of 'a'..'k'; tlz = set of 'l'..'z';
- и причиной для этого может быть именно отслеживание диапазона средствами компилятора на этапе отладки. Всякое "приведение, исходя из здравого смысла" тут неуместно. Арчон, ты в ладах с граматиками и т.п. stuff'ом, ты должен это понять (или строго доказать мне обратное)).
Молчание volvo я склонен воспринимать как согласие со мной (если, конечно, он не супер занят на другом фронте)). Зная его нелюбовь признавать свои ляпы (очень редкие, правда), я полагаю, что он просто отмалчивается..
volvo
7.11.2010 14:20
Я не отмалчиваюсь, я просто сейчас сидел и разбирался с ассемблерными листингами разных тестов работы со множествами. Смотри, что получается...
Берем кусок кода №1:
// переменные описаны так: type byteset = set of 128 .. 255; const bset: byteset = [128 .. 255]; arr: array[1 .. 5] of byte = (1, 128, 129, 130, 131);
// for i := 1 to 5 do if arr[ i ] in bset then inc(count); writeln(count);
. Обратил внимание? Сразу запихиваем содержимое очередного элемента массива в EAX, и смотрим, есть ли оно в множестве. А теперь вышеприведенный мной код:
type tset = set of 'a' .. 'z'; const myset: tset = ['a' .. 'z']; s: string = 'the test';
// for i := 1 to length(s) do if s[ i ] in myset then inc(count); writeln(count);
То есть, сначала из выдирается очередной символ, от него отнимается 97 (ну, это понятно - приводим к началу отсчета, 'a' считается нулевым элементом множества - значит ото всего будем отнимать 97, начинался бы интервал при описании tset с 'b' - отнимали бы 98), потом проверяем результат на попадание в допустимый интервал (26 символов, начиная с 0 - если значение больше 25, то RCE). А потом опять выдираем значение из строки и его уже проверяем на наличие во множестве...
Если так все-таки было задумано, то чего бы не сделать то же самое действие для целочисленных базовых типов - непонятно. Для перечислимых тоже есть такая проверка:
type t = (_1, _2, _3, _4); st = set of _2 .. _4;
const ssv: st = [_2 .. _4]; one: t = _1; begin if one in ssv then writeln('wrong'); end.
точно так же, как и при работе с Char-ами вылетает при отладке.
Неоднозначность какая-то получается. Учитывая то, что есть Generic-и - это ОЧЕНЬ нехорошая неоднозначность.
Lapp
8.11.2010 4:12
Цитата(volvo @ 7.11.2010 14:20)
Если так все-таки было задумано, то чего бы не сделать то же самое действие для целочисленных базовых типов - непонятно. Для перечислимых тоже есть такая проверка: <...> Неоднозначность какая-то получается. Учитывая то, что есть Generic-и - это ОЧЕНЬ нехорошая неоднозначность.
Да, с этим я не могу не согласиться. Явная неоднозначность. И неоднозначность плохая, да. В такой ситуации ответ может дать только тот, кто делал.. Володь, ты еще не запостил этот вопрос на форум FP?
volvo
8.11.2010 13:28
Нет еще... Сегодня вечером запощу.
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.