Здравствуйте, WolfHound, Вы писали:
WH>goto может быть практически незаменим при генерации кода. WH>Да и в редких случаях его можно довольно красиво использовать значительно упрощая код. WH>Например
Попробовал, кажется получилось красивее чем в Вашем примере с goto:
PROCEDURE Merge(a, b: Node; less: Less): Node;
VAR head, tail: Node;
BEGIN
IF a = NIL THEN RETURN b END;
IF b = NIL THEN RETURN a END;
IF less(a, b) THEN head := a; a := a.next ELSE head := b; b := b.next END;
tail := head;
LOOP
IF a = NIL THEN tail.next := b; EXIT END;
IF b = NIL THEN tail.next := a; EXIT END;
IF less(a, b) THEN
tail.next := a; tail := a; a := a.next
ELSE
tail.next := b; tail := b; b := b.next
END
END;
RETURN head;
END Merge;
Если вместо less(a, b): BOOLEAN использовать min(a, b): Node, то будет еще красивее.
WH>...и с одной точкой выхода из функции.
Здравствуйте, Сергей Губанов, Вы писали:
СГ>Попробовал, кажется получилось красивее чем в Вашем примере с goto:
Что за дурацкая привычка лепить все в одну строчку?
PROCEDURE Merge(a, b: Node; less: Less): Node;
VAR head, tail: Node;
BEGIN(* Это лишнее ибо эта функция не расчитана на прямое использование
IF a = NIL THEN
RETURN b
END;
IF b = NIL THEN
RETURN a
END;
*)
(*Дублирование кода*)IF less(a, b) THEN
head := a;
a := a.next
ELSE
head := b;
b := b.next
END;
tail := head;
LOOP
(*лишние проверки в цикле*)
(*если на предыдущей итерации не выполнилось less(a, b)
то это условие никогда не выполнится*)IF a = NIL THEN
tail.next := b;
EXIT
END;
(*если на предыдущей итерации выполнилось less(a, b)
то это условие никогда не выполнится*)IF b = NIL THEN
tail.next := a;
EXIT
END;
IF less(a, b) THEN
tail.next := a;
tail := a;
a := a.next
ELSE
tail.next := b;
tail := b;
b := b.next
END
END;
RETURN head;
END Merge;
Итого: лишнии проверки в цикле и два неосуществимых пути. Это не способствует ни пониманию ни производительности.
СГ>Зачем с одной точкой выхода?
Чтобы все совсем структурно было...
... << RSDN@Home 1.1.4 beta 6a rev. 436>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, IT, Вы писали:
IT>Здравствуйте, Сергей Губанов, Вы писали:
VD>>>Не. Делать тесты ради одного человека — это уже пребор.
СГ>>Дык, а если тесты для оберонов попросить написать того человека, разьве-ж он откажется...
IT>Если попросить его, то оберон несомненно победит.
Дать исходники и попросить перевести на оОберон — вполне нормальное дело. Надо дать человеку "высказаться по существу".
Здравствуйте, Зверёк Харьковский, Вы писали:
ЗХ>К слову, эта идея (избавления от "ненужных операторов") не распространена на операторы вида op= (+=, -= и т.д.). Почему? То есть, конечно, логику в этом, видимо, можно найти... Но лень.
Логика такая, что не все домены имеют обратные операции.
Если мы реализуем группу — тогда да, на каждый плюс найдётся свой минус. Но какой декремент может быть, например, для строк? str+=tail, но не str-=tail.
Кстати, хохму придумал. Почему бы не трактовать для строк a-b как b+a, и соответственно a-=b как a=a-b=b+a, то есть прибавление к началу строки?
Здравствуйте, Зверёк Харьковский, Вы писали:
E>>Так вот в C++ мне достаточно определить только оператор "строго меньше"...
ЗХ>...что, по сути, является определением совсем другого оператора — оператора порядка, а не оператора сравнения. Ты не застрахован от удивления в клиентском коде, когда ЗХ>
ЗХ>Compound_Key a, b;
ЗХ>if(a < b) //так можно
ЗХ>if(a > b) //а так почему-то нельзя :(
ЗХ>
ЗХ>Я, когда мне нужно использовать в качестве ключей мапы класс, сравнение объектов которого бессмысленно, предпочитаю определять для этого свой функтор-компаратор, который не выглядит как operator<
Тут история вот какая:
1) разные отношения
— эквивалентность, которому соответствует семейство сравнений ==, !=
— порядок, которому соответствуют < > <= >= == != (т.е. эквивалентность вытекает из порядка)
Причём для введения каждого из них необходимо и достаточно определить, соответственно, равенство ( == ) и одно из строгих неравенств ( < > )
2) отношение порядка можно задавать как предикатом неравенства (X,X)->bool, так и троичной функцией-компаратором (X,X)->{-,0,+} — эти способы взаимозаменяемы
3) троичный компаратор в роли базиса — практически удобнее, чем неравенство:
— он антисимметричен (а значит, его проще и реализовывать, и использовать)
— все операторы (включая ==) определяются через одно обращение к компаратору:
— — x>y = cmp(x,y)>0; x<=y = cmp(x,y)<=0; x==y = cmp(x,y)==0
— — x<y = less(y,x); x<=y = !less(y,x); x==y = !less(x,y) && !less(y,x)
для дорогостоящих сравнений (например, строк) это может здорово аукнуться.
То, что в STL в роли базиса был взят предикат — это, возможно, упущение авторов. Хотели добиться минимализма (типа — встроенный оператор уже есть, а компаратор потребует нового имени, да ещё и заморочек с ADL). И устроили головную боль пользователям. Хотя в этом есть некое изящество: если операторы < и > определены и согласованы, то смена направления сортировки достигается заменой less<T> на greater<T> (определённые через операторы).
Здравствуйте, Кодт, Вы писали:
ЗХ>>Я, когда мне нужно использовать в качестве ключей мапы класс, сравнение объектов которого бессмысленно, предпочитаю определять для этого свой функтор-компаратор, который не выглядит как operator<
К>Тут история вот какая: К>1) разные отношения К>- эквивалентность, которому соответствует семейство сравнений ==, != К>- порядок, которому соответствуют < > <= >= == != (т.е. эквивалентность вытекает из порядка) К>Причём для введения каждого из них необходимо и достаточно определить, соответственно, равенство ( == ) и одно из строгих неравенств ( < > ) К>2) отношение порядка можно задавать как предикатом неравенства (X,X)->bool, так и троичной функцией-компаратором (X,X)->{-,0,+} — эти способы взаимозаменяемы К>3) троичный компаратор в роли базиса — практически удобнее, чем неравенство: К>- он антисимметричен (а значит, его проще и реализовывать, и использовать) К>- все операторы (включая ==) определяются через одно обращение к компаратору: К>- — x>y = cmp(x,y)>0; x<=y = cmp(x,y)<=0; x==y = cmp(x,y)==0 К>- — x<y = less(y,x); x<=y = !less(y,x); x==y = !less(x,y) && !less(y,x) К>для дорогостоящих сравнений (например, строк) это может здорово аукнуться.
К>То, что в STL в роли базиса был взят предикат — это, возможно, упущение авторов. Хотели добиться минимализма (типа — встроенный оператор уже есть, а компаратор потребует нового имени, да ещё и заморочек с ADL). И устроили головную боль пользователям. Хотя в этом есть некое изящество: если операторы < и > определены и согласованы, то смена направления сортировки достигается заменой less<T> на greater<T> (определённые через операторы).
Наблюдение верное. И даже в некоторой мере очевидное. Но тут есть еще 2 фактора:
1) как-никак, компаратор редко удается изящно написать. В этой ветке приводилось достаточно примеров того, насколько неизящен унутре этот самый компаратор.
2) иногда (часто?) между объектами типа определено только отношение равенства, но не порядка. (иногда и наоборот, но это уже клинический случай). Соответственно, получится либо "частично определенный" компаратор (который возвращает либо 0 либо не 0), что опять же может запутать юзера; либо два оператора — компаратор и равенство (как собственно и сделано в D, имеющем opEqual и opCmp), что уже несколько дискредитирует всю задумку.
Здравствуйте, Кодт, Вы писали:
ЗХ>>К слову, эта идея (избавления от "ненужных операторов") не распространена на операторы вида op= (+=, -= и т.д.). Почему? То есть, конечно, логику в этом, видимо, можно найти... Но лень.
К>Логика такая, что не все домены имеют обратные операции. К>Если мы реализуем группу — тогда да, на каждый плюс найдётся свой минус. Но какой декремент может быть, например, для строк? str+=tail, но не str-=tail.
К>Кстати, хохму придумал. Почему бы не трактовать для строк a-b как b+a, и соответственно a-=b как a=a-b=b+a, то есть прибавление к началу строки?
Я не то имел в виду, когда говорил об избавлении от ненужных операторов. Слияние не operator+ и operator-, а operator+ и operator+=
... << RSDN@Home 1.1.4 beta 6a rev. 436>>
FAQ — це мiй ай-кью!
Re[16]: ЧАСТЬ 3: Конструкторы, деструкторы, и RAII
Здравствуйте, VladD2, Вы писали:
VD>Про управление памятью я уже говорил. Отсуствие ЖЦ — это конечно недостаток, но управление памятью в Дельфи отнимало не бьльше чем при пронраммировании на С++. Умные указатели и т.п. — это все костыли.
Умный указатель, это "заместитель" по GoF. Все паттерны по сути — костыли, они же трюки и приемчики.
Управление памятью в Дельфи отнимает не больше чем в С++ только в GUI. И там и там оно вообще ничего не отнимает ибо много чего берет на себя библиотека и явно их вызывать почти никогда не нужно. Если же речь шла об активном динамическом создании/удалении объектов, то в дельфи — это приличный объем кода на ровном месте (писал когда-то на Дельфи иерарахические структуры описаний графических примитивов — то еще удовольствие ).
Re[17]: ЧАСТЬ 3: Конструкторы, деструкторы, и RAII
Здравствуйте, Cyberax, Вы писали:
C>VB6 использовался (да и сейчас используется) для простых C>GUI-интерфейсов. В бизнес-логике VB6 я чего-то не встречал.
Да не, "там" очччень дохрена на VB/VBA всякого понаписано. Ситуация с бизнес-приложениями в тех же штатах вообще комическая с нашей т.з. Там или откровенные примитивы типа QuickBook, или сразу монстры типа SAP. Весь промежуточный провал — кто во что горазд. У нас в этой нише прочно сидит 1С, это на порядок более качественное решение чем весь тот сброд, что существует у них (по большей части на VB писанный, и сервера приложений в т.ч !!!). Дотнет, кстати, весьма неплохо подходит как раз для заполнения этой ниши, предоставляя единообразный масштабируемый фреймворк для всех уровней.
C>Мне не очень понятна, например, в отношении C++Builder'а.
Это вообще старый и больной вопрос. До сих пор считаю, что ставка на ObjectPascal была ошибочной. Им стоило двигать в визуальном-виндовом направлении свой С++, который был одним из лучших на то время. Расстановка сил на данный момент могла бы быть несколько другой.
Re[12]: ЧАСТЬ 3: Конструкторы, деструкторы, и RAII
Здравствуйте, Сергей Губанов, Вы писали:
СГ>2) Не замучаетесь на каждый чих писать все новые и новые аналоги классов ResGuard (лишние сущности)? Использование структурной конструкции finally избавляет от этой лишней писанины и структурирует программу.
Это зависит от того, на какую глубину ты способен структурироваться.
Обычное дело:
создать ресурс №1.
использовать ресурс №1.
проверить условие.
создать ресурс №2.
использовать ресурс №2.
проверить условие.
создать ресурс №3.
использовать ресурс №3.
проверить условие.
создать ресурс №4.
использовать ресурс №4.
проверить условие.
и т.д., порой получается большой итоговый №.
Со всякими scope-контоллерами подобные алгоритмы не представляют трудностей, записываются "как под диктовку". В случае try-finally мы должны либо городить новый уровень вложенности на каждый №, либо усложнять блок finally, проверяя, что мы успели создать, а что нет. Получаем скорее деструктуризацию, чем структуризацию.
Здравствуйте, vdimas, Вы писали:
V>Да не, "там" очччень дохрена на VB/VBA всякого понаписано. Ситуация с бизнес-приложениями в тех же штатах вообще комическая с нашей т.з. Там или откровенные примитивы типа QuickBook, или сразу монстры типа SAP. Весь промежуточный провал — кто во что горазд. У нас в этой нише прочно сидит 1С, это на порядок более качественное решение чем весь тот сброд, что существует у них (по большей части на VB писанный, и сервера приложений в т.ч !!!).
Джавы у них много на серверах, гораздо больше чем VB.
... << RSDN@Home 1.1.4 beta 7 rev. 447>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, WolfHound, Вы писали:
WH>Что за дурацкая привычка лепить все в одну строчку?
Это не дурацкая привычка.
Вы
же
не
пишите
по
одному
слову
на
одной
строчке,
а пишите так как удобнее читать.
WH>Итого: лишнии проверки в цикле и два неосуществимых пути. Это не способствует ни пониманию ни производительности.
Не могу с Вами согласиться.
Эффективная реализации процедуры less, может быть, например, такой:
PROCEDURE NodeLess(a, b: Node): BOOLEAN;
BEGIN
RETURN a.key < b.key
END NodeLess;
т.е. внутри нее a и b не проверяются на неравенство NIL.
Отсюда следует, что прежде чем вызвать less(a, b) надо быть точно уверенным, что ни a ни b не равны NIL.
LOOP
IF a = NIL THEN tail.next := b; EXIT END;
IF b = NIL THEN tail.next := a; EXIT END;
IF less(a, b) THEN
tail.next := a; tail := a; a := a.next
ELSE
tail.next := b; tail := b; b := b.next
END
END;
написан правильно, нет ни одной лишней проверки, и нет неосуществляемых путей.
1) less(a, b) вызывается только тогда когда (a # NIL) & (b # NIL) = TRUE
2) Оба пути в IF равновероятны на случайных данных.
Re[13]: ЧАСТЬ 3: Конструкторы, деструкторы, и RAII
Здравствуйте, vdimas, Вы писали:
V>...либо усложнять блок finally, проверяя, что мы успели создать, а что нет...
Вы правы, но наполовину. Дело в том, что проверить перед уничтожением объекта был ли он создан, вообще-то, еще ни кому ни когда не вредило в любом случае, не зависимо от того есть finally или нет его.
Здравствуйте, Зверёк Харьковский, Вы писали:
ЗХ>Наблюдение верное. И даже в некоторой мере очевидное. Но тут есть еще 2 фактора: ЗХ>1) как-никак, компаратор редко удается изящно написать. В этой ветке приводилось достаточно примеров того, насколько неизящен унутре этот самый компаратор.
Предикат — ещё менее изящен
Например, поэлементное (в т.ч. лексикографическое) сравнение выглядит так
int cmp(A a, A b)
{
int c;
c = cmp(a.x, b.x); if(c) return c;
c = cmp(a.y, b.y); if(c) return c;
...
return 0;
}
bool less(A a, A b)
{
if(less(a.x, b.x)) return true; if(less(b.x, a.x)) return false;
if(less(a.y, b.y)) return true; if(less(b.y, a.y)) return false;
...
return false;
}
ЗХ>2) иногда (часто?) между объектами типа определено только отношение равенства, но не порядка. (иногда и наоборот, но это уже клинический случай).
Как это?! Если есть порядок, то автоматически есть и равенство.
Другое дело, что можно ввести несколько разных равенств/порядков (пример для строк: порядки с учётом регистра, без учёта регистра, по созвучию, по контрольной сумме).
В этом случае необходимо и достаточно определить внешние функции/функторы.
ЗХ>Соответственно, получится либо "частично определенный" компаратор (который возвращает либо 0 либо не 0), что опять же может запутать юзера; либо два оператора — компаратор и равенство (как собственно и сделано в D, имеющем opEqual и opCmp), что уже несколько дискредитирует всю задумку.
Задумку дискредитирует то, что
— в С++ операторы между собой не связаны
— а в D, где связь есть, не пошли до конца (скажем, могли потребовать: если определён opCmp, то opEqual определяется автоматически и не подлежит перекрытию)
Здравствуйте, Сергей Губанов, Вы писали:
СГ>Это не дурацкая привычка. СГ>Вы же не пишите по одному слову на одной строчке, а пишите так как удобнее читать.
Не надо путать естественные языки и языки программирования.
СГ>Не могу с Вами согласиться.
А придется. СГ>Эффективная реализации процедуры less, может быть, например, такой:
Это не имеет значения. СГ>Отсюда следует, что прежде чем вызвать less(a, b) надо быть точно уверенным, что ни a ни b не равны NIL.
Условия вызова исходной функции таковы что на вход всегда подаются коректные, отсортированые списки состоящие хотябы из одного элемента.
СГ>Стало быть цикл: СГ>
СГ> LOOP
СГ> IF a = NIL THEN tail.next := b; EXIT END;
СГ> IF b = NIL THEN tail.next := a; EXIT END;
СГ> IF less(a, b) THEN
СГ> tail.next := a; tail := a; a := a.next
СГ> ELSE
СГ> tail.next := b; tail := b; b := b.next
СГ> END
СГ> END;
СГ>
СГ>написан правильно,
Правильно но не эффективно.
Разберем его.
Допустим в начале цикла списки a и b содержат элементы.
Те ни одно из условий
IF a = NIL THEN tail.next := b; EXIT END;
IF b = NIL THEN tail.next := a; EXIT END;
не выполнится.
Далие происходит проверка
IF less(a, b) THEN
Теперь если это условие выполнилось то мы из списка a забираем один элемент при этом мы не модифицируем список b.
Далие переходим на начало списка. Так как мы не меняли список b то условие
IF b = NIL THEN tail.next := a; EXIT END;
гарантировано не выполнится.
Вот вам один неосуществимый путь и одна лишняя проверка.
Тоже самое касается другого списка. СГ>нет ни одной лишней проверки, и нет неосуществляемых путей.
Почемуто в моей реализацие на одну проверку в цикле меньше.
СГ>1) less(a, b) вызывается только тогда когда (a # NIL) & (b # NIL) = TRUE СГ>2) Оба пути в IF равновероятны на случайных данных.
В этом цикле не два, а 6 путей из которых 2 невозможны.
... << RSDN@Home 1.1.4 beta 6a rev. 436>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[14]: ЧАСТЬ 3: Конструкторы, деструкторы, и RAII
Здравствуйте, Сергей Губанов, Вы писали:
СГ>Вы правы, но наполовину. Дело в том, что проверить перед уничтожением объекта был ли он создан, вообще-то, еще ни кому ни когда не вредило в любом случае, не зависимо от того есть finally или нет его.
Эта проврека пишется один раз в деструкторе.
... << RSDN@Home 1.1.4 beta 6a rev. 436>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, Зверёк Харьковский, Вы писали:
ЗХ>Я не то имел в виду, когда говорил об избавлении от ненужных операторов. Слияние не operator+ и operator-, а operator+ и operator+=
Это опять накладывает ненужные ограничения. А именно, требует, чтобы тип результата был равен типу левого операнда.
L& operator += ( L& lhs, const R& rhs) { lhs = lhs + rhs; return lhs; }
L operator + (const L& lhs, const R& rhs) { L res = lhs; res += rhs; return res; }
Самый сильный пример того, где это мешает — boost::spirit, где можно складывать унарные функции и скаляры, порождая новые унарные функции.