IPB
ЛогинПароль:

> 

Наладить общение поможет, если вы подпишитесь по почте на новые темы в этом форуме.

 
 Ответить  Открыть новую тему 
> Работа со множествами, FPC, Будьте аккуратнее :)
сообщение
Сообщение #1


Гость






Привет всем.

Сегодня наткнулся на непонятное (с точки зрения Паскаля) поведение 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.

Попробуйте без его компиляции и запуска определить, что будет выведено на печать. А потом запустите на выполнение... smile.gif

Так что осторожнее с множествами...
 К началу страницы 
+ Ответить 
сообщение
Сообщение #2


Злостный любитель
*****

Группа: Пользователи
Сообщений: 1 755
Пол: Мужской

Репутация: -  62  +


Должно быть 7 же, а в ФПЦ чё за прикол происходит?


--------------------
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #3


Профи
****

Группа: Пользователи
Сообщений: 618
Пол: Мужской

Репутация: -  24  +


FreePascal 2.4.0. Предсказуемо, 7.


--------------------
Close the World...txeN eht nepO
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #4


Гость






Ну вот, и я думал, что 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 и все.
 К началу страницы 
+ Ответить 
сообщение
Сообщение #5


Уникум
*******

Группа: Пользователи
Сообщений: 6 823
Пол: Мужской
Реальное имя: Лопáрь (Андрей)

Репутация: -  159  +


Цитата(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.

- это отрабатывает на ура без всяких ошибок..


--------------------
я - ветер, я северный холодный ветер
я час расставанья, я год возвращенья домой
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #6


Гость






Цитата
Не элемент - выяснение невозможно.
, и надо выбрасывать критическую ошибку, да? В топку такое поведение. У меня не строка может состоять из символов 'a'..'z', а посторонняя переменная. И если символ из строки в ней не присутствует или не может присутствовать - то In должен вернуть False.

Заметь, подобное поведение было как минимум до версии 2.0.0 (на двойке не помню, на 1.0.10 проверил только что - работает), обратная совместимость где? smile.gif

Да. И еще одно - что мне совершенно непонятно. Откомпилируем программу в Options -> Mode = Release, все прекрасно работает и в 2.4.0. То есть, все-таки баг в отладочных режимах.
 К началу страницы 
+ Ответить 
сообщение
Сообщение #7


Уникум
*******

Группа: Пользователи
Сообщений: 6 823
Пол: Мужской
Реальное имя: Лопáрь (Андрей)

Репутация: -  159  +


Цитата(volvo @ 6.11.2010 2:59) *
И еще одно - что мне совершенно непонятно. Откомпилируем программу в Options -> Mode = Release, все прекрасно работает и в 2.4.0. То есть, все-таки баг в отладочных режимах.
Гм. А разве в моде Release включен Range Checking?..

Володь, я в целом согласен, что фича ненужная и бесполезная. Я не могу привести примера для себя, где оно бы помогло. Но все же логику тут отрицать пока не могу.
Я еще подумаю над этим..


Добавлено через 4 мин.
Цитата(volvo @ 6.11.2010 2:59) *
, и надо выбрасывать критическую ошибку, да?
Это обычный RangeCheck.. Выключи/включи его для этого куска кода - и проблема решена..


--------------------
я - ветер, я северный холодный ветер
я час расставанья, я год возвращенья домой
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #8


Уникум
*******

Группа: Пользователи
Сообщений: 6 823
Пол: Мужской
Реальное имя: Лопáрь (Андрей)

Репутация: -  159  +


Цитата(Lapp @ 6.11.2010 3:04) *
Я еще подумаю над этим..
Я подумал, как и обещал. И по-прежнему считаю, что это совершенно правильное поведение компилятора строго типизированного языка (каковым является объектный Паскаль в поздних реализациях). Справедливости ради, скажу, что я так и думал, что именно это произойдет еще при первом просмотре кода. Я понимаю, что это не довод, конечно )).

В твоем примере происходит обработка ВСЕХ входящих символов. Значит, конструкция для обработки должна принимать их ВСЕ. То, что в число "хороших" символов могут входить только буквы, еще не значит, что ты можешь ими ограничиться при обработке. Поэтому введение типа tset в этом коде не есть правильное действие. Если в твоем потоке символов предполагаются буквы и пробелы, а также цифры, но (допустим) никак не управляющие символы, то организуй соответствующее множество, и RCE (в процессе отладки, разумеется) будет означать, что пролез какой-нить ctrl-c, что есть признак ошики в коде и требует реакции программера.

Так что, тут нет никакого бага, мне кажется. IMHO, ессно. Но довольно твердое smile.gif.


--------------------
я - ветер, я северный холодный ветер
я час расставанья, я год возвращенья домой
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #9


Профи
****

Группа: Пользователи
Сообщений: 618
Пол: Мужской

Репутация: -  24  +


Delphi не ругается ни при выключенном RangeCheck, ни при включенном. При операциях с числами аргументы приводятся к большему диапазону, так-что я не вижу смысла в случае с множествами поступать иначе.


--------------------
Close the World...txeN eht nepO
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #10


Профи
****

Группа: Пользователи
Сообщений: 865
Пол: Мужской
Реальное имя: Вячеслав

Репутация: -  20  +


ошибка всегда, если Range Checking включен, во всех 3 режимах.
и я считаю это злостным багом smile.gif

Сообщение отредактировано: Client -


Эскизы прикрепленных изображений
Прикрепленное изображение
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #11


Уникум
*******

Группа: Пользователи
Сообщений: 6 823
Пол: Мужской
Реальное имя: Лопáрь (Андрей)

Репутация: -  159  +


Цитата(Archon @ 6.11.2010 10:53) *
При операциях с числами аргументы приводятся к большему диапазону, так-что я не вижу смысла в случае с множествами поступать иначе.
То, что ты его не видишь, не значит, что его нет )). Я уже привел ситуацию, когда такое поведение может быть полезным. Что касается больший/меньший диапазон - это неправильное проектирование числовых свойств на множества. Мне это тоже сначала пришло в голову, потом я покрутил в мозгах и понял, что это совсем разные вещи. В числах диапазон - это, как правило, ограничение, наложенное вычислительными возможностями. А тут ты можешь задать
tak = set of 'a'..'k';
tlz = set of 'l'..'z';

- и причиной для этого может быть именно отслеживание диапазона средствами компилятора на этапе отладки. Всякое "приведение, исходя из здравого смысла" тут неуместно. Арчон, ты в ладах с граматиками и т.п. stuff'ом, ты должен это понять (или строго доказать мне обратное)).

Молчание volvo я склонен воспринимать как согласие со мной (если, конечно, он не супер занят на другом фронте)). Зная его нелюбовь признавать свои ляпы (очень редкие, правда), я полагаю, что он просто отмалчивается.. smile.gif


--------------------
я - ветер, я северный холодный ветер
я час расставанья, я год возвращенья домой
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #12


Гость






Я не отмалчиваюсь, я просто сейчас сидел и разбирался с ассемблерными листингами разных тестов работы со множествами. smile.gif Смотри, что получается...

Берем кусок кода №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);

Что имеем на выходе? Вот что:
# [18] if arr[ i ] in bset then inc(count);
movl U_P$PROGRAM_I,%eax
subl $1,%eax
cmpl $4,%eax
jbe .Lj8
call FPC_RANGEERROR
.Lj8:
movl U_P$PROGRAM_I,%edx
movl %edx,%eax
subl $1,%eax
cmpl $4,%eax
jbe .Lj9
call FPC_RANGEERROR
.Lj9:
movzbl TC_P$PROGRAM_ARR-1(,%edx,1),%eax
btl %eax,TC_P$PROGRAM_BSET

jc .Lj6
jmp .Lj7
.Lj6:
movl TC_P$PROGRAM_COUNT,%eax
addl $1,%eax
jno .Lj12
call FPC_OVERFLOW
.Lj12:
movl %eax,TC_P$PROGRAM_COUNT
.Lj7:
cmpl $5,U_P$PROGRAM_I
jl .Lj5
.stabn 68,0,19,.Ll4 - _main
.Ll4:
# [19] 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);

На выходе:

# [24] if s[ i ] in myset then inc(count);
movl U_P$PROGRAM_I,%edx
cmpl $255,%edx
jbe .Lj30
call FPC_RANGEERROR
.Lj30:
movzbl U_P$PROGRAM_I,%ecx
movzbl TC_P$PROGRAM_S(,%ecx,1),%edx
subl $97,%edx
cmpl $25,%edx
jbe .Lj31
call FPC_RANGEERROR

.Lj31:
movzbl TC_P$PROGRAM_S(,%ecx,1),%edx
btl %edx,TC_P$PROGRAM_MYSET

jc .Lj28
jmp .Lj29
.Lj28:
movl TC_P$PROGRAM_COUNT,%edx
addl $1,%edx
jno .Lj34
call FPC_OVERFLOW
.Lj34:
movl %edx,TC_P$PROGRAM_COUNT
.Lj29:
cmpl U_P$PROGRAM_I,%eax
jg .Lj27
.Lj26:
.stabn 68,0,26,.Ll8 - _main
.Ll8:
# [26] writeln(count);
То есть, сначала из выдирается очередной символ, от него отнимается 97 (ну, это понятно - приводим к началу отсчета, 'a' считается нулевым элементом множества - значит ото всего будем отнимать 97, начинался бы интервал при описании tset с 'b' - отнимали бы 98), потом проверяем результат на попадание в допустимый интервал (26 символов, начиная с 0 - если значение больше 25, то RCE). А потом опять выдираем значение из строки и его уже проверяем на наличие во множестве...

blink.gif Если так все-таки было задумано, то чего бы не сделать то же самое действие для целочисленных базовых типов - непонятно. Для перечислимых тоже есть такая проверка:
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-и - это ОЧЕНЬ нехорошая неоднозначность.
 К началу страницы 
+ Ответить 
сообщение
Сообщение #13


Уникум
*******

Группа: Пользователи
Сообщений: 6 823
Пол: Мужской
Реальное имя: Лопáрь (Андрей)

Репутация: -  159  +


Цитата(volvo @ 7.11.2010 14:20) *
Если так все-таки было задумано, то чего бы не сделать то же самое действие для целочисленных базовых типов - непонятно. Для перечислимых тоже есть такая проверка:
<...>
Неоднозначность какая-то получается. Учитывая то, что есть Generic-и - это ОЧЕНЬ нехорошая неоднозначность.
Да, с этим я не могу не согласиться. Явная неоднозначность. И неоднозначность плохая, да. В такой ситуации ответ может дать только тот, кто делал.. Володь, ты еще не запостил этот вопрос на форум FP?


--------------------
я - ветер, я северный холодный ветер
я час расставанья, я год возвращенья домой
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #14


Гость






Нет еще... Сегодня вечером запощу.
 К началу страницы 
+ Ответить 

 Ответить  Открыть новую тему 
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 





- Текстовая версия 1.10.2020 21:24
500Gb HDD, 6Gb RAM, 2 Cores, 7 EUR в месяц — такие хостинги правда бывают
Связь с администрацией: bu_gen в домене octagram.name