Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Здравствуйте, pagid, Вы писали:
EP>>>Конкретный пример — бинарный поиск: EP>>>
EP>>>template<typename I, typename P>
P>>....
P>>....
EP>>>
P>>Так это же совсем другое дело, здесь шаблоны как раз к месту применены.
EP>Здесь разговор не о том что шаблоны или макросы где-то применяют не к месту, а о том что EP>
EP>если нужно понять как что-то работает, то смотреть лучше в C реализации, нежели в C++
EP>Вот я и прошу показать на простом примере как на C всё становится понятнее.
Я уже приводил ссылку на операции с полгонами.
Вот другой пример попроще: наибольший общий делитель
unsigned gcd(unsigned a, unsigned b) {
unsigned c;
while (a) {
c = a;
a = b % a;
b = c;
}
return b;
}
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Здравствуйте, kov_serg, Вы писали:
EP>>>Отсутствие компилятора это один из немногих случаев где оправданно использование C, по очевидным причинам. _>>Есть еще причина. Т.к. C менее универсальный нежели С++ авторы вынужденые реализовывать конкретику, а не абстракции. _>>Такая деятельность дисциплинирует, требует большей ответственности и усердия от ремеслеников, занимающихся подобным трудом.
EP>То есть программисты тратят свои ресурсы на "усердие" и прочую борьбу с языком.
_>>И если нужно понять как что-то работает, то смотреть лучше в C реализации, нежели в C++. _>>Что бы далеко не ходить можно посмотреть реализацию базовых операций с полигонами GPC и boost
EP>Конкретный пример — бинарный поиск: EP>
EP>template<typename I, typename P>
EP>I partition_point_m(I first, I last, P p)
EP>{
EP> while(first != last)
EP> {
EP> I middle = first + (last - first)/2;
EP> if( p(*middle) )
EP> last = middle;
EP> else
EP> first = middle + 1;
EP> }
EP> return first;
EP>}
EP>
Конечно видна, версия C замусорена размером элемента, звёздами пустоты, при этом работает только для плоских массивов, да ещё и медленнее (runtime sizeof, косвенный вызов предиката). Версия же C++ работает для всего начиная с Forward Interator — например для односвязных списков (логарифмическое количество сравнений).
Здравствуйте, pagid, Вы писали:
P>Здравствуйте, so5team, Вы писали:
S>>Или даже вот в таком.
P>На С это будет так
Трудно себе представить, чтобы под современной ОС на современном компутере, не в ядре, strdup() вернул бы не NULL, и программа при этом бы сумела обработать ошибку и выжить. Гораздо вероятмее, что strdup() найдет место в адресном пространстве (а не реальную память), и вернет какой-то адрес, а вот при попытке обратиться по этому адресу выяснится, что памяти-то и нет, и программа получит SIGFAULT.
Поэтому я бы, для очистки совести, обернул бы strdup() и подобные функции в нечто, что вместо возврата NULL аварийно завершается с разумной диагностикой, и сильно упростил бы этот код.
не намного больше чем в с/c++
но выполняться будет намного быстрее
ни один компилятор не оптимизирует
чаще всего компиляторы с аргументами работают через стек
Здравствуйте, tranzit, Вы писали:
T>не намного больше чем в с/c++
В C++ работает для произвольных последовательностей, начиная от Forward Sequence, для любых типов элементов и т.п.
T>но выполняться будет намного быстрее
Это с call *%rdx внутри ?
T>ни один компилятор не оптимизирует
ШТА?
T>чаще всего компиляторы с аргументами работают через стек
Зависит от calling convention, на x64 часто через регистры (пока их хватает), НО — это не имеет никакого отношения к делу, ибо вызов partition_point на C++ будет заинлайнен, то есть вообще не будет никакого call.
template<typename I, typename P>
I partition_point_m(I first, I last, P p)
{
while(first != last)
{
I middle = first + (last - first)/2;
if( p(*middle) )
last = middle;
else
first = middle + 1;
}
return first;
}
int *concrete_test(int *first, int *last)
{
return partition_point_m(first, last, [](int x){ return x <= 42; });
}
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Здравствуйте, tranzit, Вы писали:
T>>не намного больше чем в с/c++
EP>В C++ работает для произвольных последовательностей, начиная от Forward Sequence, для любых типов элементов и т.п.
T>>но выполняться будет намного быстрее
EP>Это с call *%rdx внутри ?
T>>ни один компилятор не оптимизирует
EP>ШТА?
T>>чаще всего компиляторы с аргументами работают через стек
EP>Зависит от calling convention, на x64 часто через регистры (пока их хватает), НО — это не имеет никакого отношения к делу, ибо вызов partition_point на C++ будет заинлайнен, то есть вообще не будет никакого call. EP>
EP>template<typename I, typename P>
EP>I partition_point_m(I first, I last, P p)
EP>{
EP> while(first != last)
EP> {
EP> I middle = first + (last - first)/2;
EP> if( p(*middle) )
EP> last = middle;
EP> else
EP> first = middle + 1;
EP> }
EP> return first;
EP>}
EP>int *concrete_test(int *first, int *last)
EP>{
EP> return partition_point_m(first, last, [](int x){ return x <= 42; });
EP>}
EP>
Здравствуйте, tranzit, Вы писали:
T>в исходной задаче не было функции. Но с учетом тела функции сравни
Ты предлагаешь вручную выписывать бинарный поиск на ассемблере под каждую комбинацию предикатов, типов элементов, не говоря уже о типах последовательностей?
T>обрати внимание на цикл и количество обращений к памяти
По одному обращению на итерацию, и там и там в cmp. А ты сколько насчитал?
И кстати, у тебя нет проверки на входные first==last.
И судя по всему тебя не O3 интересует, а что-то типа Os:
#include <algorithm>
int *concrete_test(int *first, int *last)
{
return std::partition_point(first, last, [](int x){ return x > 42; });
}
Корабли лавировали, лавировали, да не вылавировали — вариант цикла нарушен — диапазон [first..last) должен уменьшаться как минимум на один элемент на каждой итерации, иначе зацикливание.
А теперь предположим, что вместо unsigned вдруг понадобился какой-нибудь bigint. И все, на Си приходится руками переписывать "a = b % a" в "bigint_mod(&a, &b, &a)". А с шаблонным решением достаточно определить свои traits и ВСЕ алгоритмы, их использующие автоматически начинают поддерживать ваш тип.
EP>Корабли лавировали, лавировали, да не вылавировали — вариант цикла нарушен — диапазон [first..last) должен уменьшаться как минимум на один элемент на каждой итерации, иначе зацикливание.
ну прям обижаешь
middle(%r10)=first[%rdi]+(last[%rsi]-fist[%rdi])/2[shr]
в квадртаных скобках соотвествие переменному
можно еще убрать 2 оператора из цикла
за счет команды lzcnt предварительный расчет количества итераций
но добавятся 3 до цикла
и еще в формуле(алгоритме) заложена ошибка
middle при некоторых обстоятельствах никогда не достигнет last
middle — last = 0 или 1
надо учитывать epsilon http://math.semestr.ru/optim/dichotomy.php
либо last делать заглушкой
тут я чуток ошибся
ранее епсилон учитывается в конструкции first = middle + 1;
я как то пропустил
B>А теперь предположим, что вместо unsigned вдруг понадобился какой-нибудь bigint. И все, на Си приходится руками переписывать "a = b % a" в "bigint_mod(&a, &b, &a)". А с шаблонным решением достаточно определить свои traits и ВСЕ алгоритмы, их использующие автоматически начинают поддерживать ваш тип.
B>А теперь предположим, что вместо unsigned вдруг понадобился какой-нибудь bigint. И все, на Си приходится руками переписывать "a = b % a" в "bigint_mod(&a, &b, &a)". А с шаблонным решением достаточно определить свои traits и ВСЕ алгоритмы, их использующие автоматически начинают поддерживать ваш тип.
Вот где будет копипаста — в траитсах. Вместо преписи 4х строк надо 100500 строк вспомогательных сущностей.
С++ способствует излишним обобщения. А если я туда комплексные числа суну, а если полиномы — не порядок надо предусмотреть.
Поэтому в C++ любой алгоритм пытаются сделать универсальным.
Хотя если взглянуть правде в глаза то бывает что для решения задачи даже точного алгоритма нет и используют приближенные решения и всякие эвристика. Тут лафа для обобщений.
Здравствуйте, kov_serg, Вы писали:
_>Вот где будет копипаста — в траитсах. Вместо преписи 4х строк надо 100500 строк вспомогательных сущностей. _>С++ способствует излишним обобщения. А если я туда комплексные числа суну, а если полиномы — не порядок надо предусмотреть. _>Поэтому в C++ любой алгоритм пытаются сделать универсальным. _>Хотя если взглянуть правде в глаза то бывает что для решения задачи даже точного алгоритма нет и используют приближенные решения и всякие эвристика. Тут лафа для обобщений.
все можно реализовать на любом языке особенно на низком уровне.
Вопрос удобства, времени на код, желания.
Но не стоит для микроконтроллера с памятью в 1к писать программы на c++/c
также системные вещи не стоить писать на c++, не контролируемого кода много будет.
Когда запускается прога, ее исполнение никогда не начинается с main.
Сначала разные иниты, потом статические классы(конструкторы)
а ф функции main в конечном счете можно написать только exit(0), а вся программа будет в конструкторах
Каждый язык имеет сильные и слабые стороны.
Дождемся искусственного интеллекта, может тогда будет интересная форма составления алгоритма.
Здравствуйте, tranzit, Вы писали:
EP>>Корабли лавировали, лавировали, да не вылавировали — вариант цикла нарушен — диапазон [first..last) должен уменьшаться как минимум на один элемент на каждой итерации, иначе зацикливание. T> T>ну прям обижаешь T>middle(%r10)=first[%rdi]+(last[%rsi]-fist[%rdi])/2[shr] T>в квадртаных скобках соотвествие переменному
Ошибка не вычислении middle, а в выборе следующего диапазона.
У тебя выбирается либо [first..middle), либо [middle..last) — во втором случае ошибка. middle может равняться first с предыдущей операции, например если всего один элемент в диапазоне, и если продолжать в [middle..last) — то получается зацикливание. Более того, значение *middle уже проверенно предикатом, и его проверять вообще не нужно.
T>и еще в формуле(алгоритме) заложена ошибка T>middle при некоторых обстоятельствах никогда не достигнет last T>middle — last = 0 или 1 T>надо учитывать epsilon T>http://math.semestr.ru/optim/dichotomy.php T>либо last делать заглушкой
Это у тебя в алгоритме ошибка — об этом я и сказал когда говорил про лавировку и вариант цикла. И никакой epsilon не нужен. Сравни с тем вариантом который я привёл ранее.
Здравствуйте, Ops, Вы писали:
Ops>Здравствуйте, tranzit, Вы писали:
T>>Но не стоит для микроконтроллера с памятью в 1к писать программы на c++/c
Ops>Я пишу периодически. Вот, например, неплохая либа https://github.com/KonstantinChizhov/Mcucpp
какое-нибудь устройство делал со своей прошивкой ?
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Здравствуйте, kov_serg, Вы писали:
EP>>>Конкретный пример — бинарный поиск: EP>>>
EP>>>template<typename I, typename P>
EP>>>I partition_point_m(I first, I last, P p)
EP>>>
EP>>>Покажи аналог на C — вот и сравним где понятнее как "что-то работает" _>>Разве это бинарный поиск. Это сферический конь в вакууме.
EP>Самый что ни на есть реальный.
_>>Вот реальный бинарный поиск
EP>Вообще-то вот
_>>на C++ http://llvm.org/svn/llvm-project/libcxx/trunk/include/algorithm (искать по binary_search и __lower_bound)
EP>Там же и partition_point
_>>на C http://lxr.free-electrons.com/source/lib/bsearch.c _>>Разница, видна?
EP>Конечно видна, версия C замусорена размером элемента, звёздами пустоты, при этом работает только для плоских массивов, да ещё и медленнее (runtime sizeof, косвенный вызов предиката). Версия же C++ работает для всего начиная с Forward Interator — например для односвязных списков (логарифмическое количество сравнений).
Вот я с вас поражаюсь. Это разговор слепого с глухонемым. Вы как сектант: "Верую и всё и никакие доводы не собъют меня с пути истенного! Только C++ и пророк его Бьярн Страустрап" Я разве против. Да гибко, Да универсально, Да во многих случаях 99.9% нафиг не надо, да компилятор мегабайтами треша лопатит, да копипаста всёравно никуда не девается, да сбоирает проект по 7 часов и хорошо...
Из за излишнего обобщения, которое возникает само собой в шаблонах, вам приходится рассматривать ситуации которые вам нафиг не нужны в реальном проекте.
Бинарный поиск и разбиение на две части это разные алгоритмы.