Re[14]: Следующий язык программирования
От: Dyoma Россия http://www.livejournal.com/users/dyomap/
Дата: 08.10.05 15:13
Оценка:
Здравствуйте, Зверёк Харьковский, Вы писали:

ЗХ>BTW, идеальной "записью намерений" для этого случая мне кажется что-то в этом роде:

ЗХ>
ЗХ>int sum = 0;
ЗХ>sum += each(array);    //ну или each(array.begin, array.end);
ЗХ>

ЗХ>Читается "прибавить к сумме каждый элемент последовательности". Поддается легкой модификации:
ЗХ>а) что сделать с элементами (например, += заменим на *=)
ЗХ>б) с какими элементами (заменяем each на select(последовательность, условие) )

Очень интересная идея. Ты ее сам придумал или взял откуда? Если взял, то поделись где, хочу по-больше узнать.
А пока по-больше не узнал, то есть у меня такой вопрос о расширяемости.
+= и *= — это понятно. Более того понятно даже как можно расширить, если я, к примеру, захочу прямоугольники пересекать:
Rectangle intersection = Rectangle.PLANE; // Забудем о пересечении пустого набора, не о ней речь...
intersection = each(array).intersect(intersection);   // Для каждого элемента вызвать intersect с параметром, текущее значение intersection


Все хорошо, до тех пор, пока у элементов коллекции есть нужный мне метод. Но что делать если я хочу применить очень специфическую аккумуляцию? Например, я хочу сложить только четные элементы массива. Можно поступить так:
int evenSum = 0;
evenSum += eventOrZero(each(array));

int evenOrZero(int x) {
    return x % 2 == 0 ? x : 0;
}

Но тут мы потеряли исходную красоту, и получили почти C++ (забыл как этот алггоритм в stl называется, пусть for_each будет):
int addEven(int sum, int x) {
    return sum + (x % 2 == 0 ? x : 0);
}

int evenSum = for_each(array.begin, array.end, addEven);

Ну или можно типа C# лямбду применить
int evenSum = ((sum, x) => sum + (x % 2 == 0 ? x : 0)) (0, each(array));

Но уж больно исходный inject:into: напоминает...
А если пойдем еще дальше и потребуется вызывать размые методы (применять разные операции) в зависимости от значения очередного элемента? Например, четные прибывляем, на нечетные умножаем. Тут вроде как надо явно указать промежуточный результат.

Dyoma
ALMWorks
http://deskzilla.com/
Что такое полиморфизм
От: Dyoma Россия http://www.livejournal.com/users/dyomap/
Дата: 08.10.05 16:49
Оценка: :)
Здравствуйте, VladD2, Вы писали:

VD>А на месте ПК я бы постыдился бы ставить плюсы таким заблуждениям.

VD>Почему только ПК? Ну, eao197 только что сам "плавл" в этом вопросе. Я вообще даже представить не мог, что на нашем сайте в таких вопросах народ "плавает".

Опустил! Опустил ниже плинтуса!
Ладно, предлагаю конструктивно поговорить на тему, что мы привыкли называть полиморфизмом. Вот, например такой код:
if (area.containsPoint(mouseLocation) {
 ... // Что-нить сделаем
}

Это есть пример использования полиморфизма, а именно использование метода containsPoint(Point). Но, в языке со статической типизацией, что бы данный алгоритм был полиморфным, нужно еще определить интерфейс, например так:
class Area {
    virtual bool containsPoint(Point p) = 0;
}

Получается, что здесь мы тоже используем полиморфизм.

А еще надо сделать реализацию:
class Rectangle : public Area {
  virtual bool containsPoint(Point& p) {
      return (x >= p.x) && ...;
    }
}

Написано, что Rectangle наследуется от Area для того, что бы его можно было использовать как Area, в частности в исходном if. А значит и здесь мы используем полиморфизм.

Это был полиморфизм динамический переходим к статическому. Перегрузку пропустим — imho не интересно. Возьмемся за шаблоны:
template<class T>
T sum(iterator<T> begin, iterator<T> end) {
    T result = T();
  while (begin != end) {
        result = result + *begin;
        begin++;
    }
    return result;
}

class Vector2D {
    Vector2D operator+(Vector2D& v) { return Vector2D(x+v.x, y+v.y); }
}

Опять видим два использования полиморфизма. Один — алгоритм вычисления суммы, элементов, умеющих складываться. Второй — определение сложения для векторов, для общего удобства и применения алгоритма sum к векторам. В данном случае интерфейс из оператора + за нас определен в стандарте C++.

Теперь вернемся к факториалу:
D>>
D>>template<int N> int factorial() {
D>>    return N*factorial<N-1>();
D>>}
D>>int factorial<0>() {
D>>    return 1;
D>>}
D>>

Я предлагаю взгянуть на этот так: Здесь описан интерфейс
int factorial<int>();

И две его имплементации. Для 0 и для всех остальных целых.

А можно было определить и так:
int factorial<0>() { return 1; }
int factorial<1>() { return 1; }
int factorial<2>() { return 2; }
....
int factorial<12>() { return 479001600; } // Вроде это последний какой в 32 бита влезет

template<int N>
int factorial() {
    error "int overflow or negative";
}

А теперь сам полиморфный алгоритм:
int f = factorial<10>();

А теперь вопрос, что тебя особо смущает в примере с factorial?

Dyoma
ALMWorks
http://deskzilla.com/
Re[23]: Следующий язык программирования
От: Sinclair Россия https://github.com/evilguest/
Дата: 08.10.05 17:29
Оценка:
Здравствуйте, eao197, Вы писали:

E>Продолжим разговор про синхронность и асинхронность.


E>В том то и дело, что традиция эта в таких языках, как C++, Java, C# и пр. зашита намертво. Если нам в 5% случаев потребуется именно fire and forget, то сделать это в том же самом синтаксисе, что и обычный call, мы уже не сможем. А вот если бы смогли, это бы я и считал прорывом.
Ну, давай изобретем синтаксис. Например...
О!
p.Call(); // синхронный вызов
p->Call(); // fire and forget;

E>А теперь расскажи, если не сложно, как ты видишь здесь детерминизм в виде синхронных вызовов, если объекты B, C и D должны работать на разных нитях. Во что все это выльется?

В работу в одной нити, конечно же. Потому что нет возможности добиться детерминированной работы в честно одновременной реализации. Все равно все, получается, будут вынуждены ждать друг друга.
Надо либо изобретать другую схему, либо возвращаться к однопоточной работе.
Либо требовать от классов готовности к неупорядоченному приходу сообщений.
1.1.4 stable rev. 510
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[14]: Следующий язык программирования
От: Andrei N.Sobchuck Украина www.smalltalk.ru
Дата: 08.10.05 17:31
Оценка:
Здравствуйте, Cyberax, Вы писали:

>> C>А вообще, идея с бинарным репозиторием исходных файлов, с которым

>> IDE работает напрямую, была опробована в VisualAge for Java. Успешно
>> провалилась
>> Так дело не бинарности/текстовости. А в том, что единицей
>> версионирования является файл, а не функция (или еще какая часть AST).

C>Так VAJе ведь именно так и было — пользователь работал на уровне

C>элементов AST.

Так какие были проблемы, кроме нестабильности самого VAJ?
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Я ненавижу Hibernate
Автор: Andrei N.Sobchuck
Дата: 08.01.08
!
Re[23]: Следующий язык программирования
От: Sinclair Россия https://github.com/evilguest/
Дата: 08.10.05 17:46
Оценка:
Здравствуйте, eao197, Вы писали:

E>Здравствуйте, Sinclair, Вы писали:


E>Совпадения не случайные (по-моему про случайность я и не говорил). Есть два типа PDU -- deliver_sm и data_sm. Они могут применятся для одной и той же задачи: доставки входящего SMS. Т.к. задача одинаковая, то и многие параметры у этих PDU одинаковые. Но deliver_sm не есть data_sm, т.к., в отличии от data_sm, deliver_sm применяется только для входящих SMS. А data_sm может применятся и для исходящих. В свою очередь, data_sm, не есть deliver_sm, т.к. у deliver_sm больше параметров.

Это все прекрасно и понятно.
E>Вот упрощенные спецификации:
class deliver_sm_t
    {
    public :
        const std::string & service_type() const;

        const oess_1::uchar_t & source_addr_ton() const;
        const oess_1::uchar_t & source_addr_npi() const;
        const std::string & source_addr() const;

        const oess_1::uchar_t    & dest_addr_ton() const;
        const oess_1::uchar_t    & dest_addr_npi() const;
        const std::string & dest_addr() const;

        const oess_1::uchar_t & esm_class() const;

        const oess_1::uchar_t & protocol_id() const;

        const oess_1::uchar_t & priority_flag() const;

        const std::string & schedule_delivery_time() const;

        const std::string & validity_period() const;

        const oess_1::uchar_t & registered_delivery() const;

        const oess_1::uchar_t & replace_if_present_flag() const;

        const oess_1::uchar_t & data_coding() const;

        const oess_1::uchar_t & sm_default_msg_id() const;

        const std::string &    short_message() const;
    };

class data_sm_t
    {
    public :
        const std::string & service_type() const;

        const oess_1::uchar_t & source_addr_ton() const;
        const oess_1::uchar_t & source_addr_npi() const;
        const std::string & source_addr() const;

        const oess_1::uchar_t    &    dest_addr_ton() const;
        const oess_1::uchar_t    & dest_addr_npi() const;
        const std::string & dest_addr() const;

        const oess_1::uchar_t & esm_class() const;

        const oess_1::uchar_t & registered_delivery() const;

        const oess_1::uchar_t & data_coding() const;
    };


E>Пересечения методов не случайно. Однако, я не думаю, что имеет смысл объединять общие методы классов deliver_sm_t и data_sm_t в какой-то интерфейс.

E>Хотя бы потому, что часть этих методов присутствуют и в других типах PDU.
Великолепно! Я бы наборот, увидел в этом перст судьбы: двукратное повторение — еще не повод. Но если оказывается, что некоторый набор методов встречаетя многократно —
это верный знак наличия стабильного интерфейса. Который лучше всего сразу же и задекларировать. Благо стоит это недорого, даже без средств автоматизации рефакторинга.
E> Например, broadcast_sm (source_addr_ton, source_addr_npi, source_addr, sm_default_msg_id), cancel_sm (source_addr*, dest_addr*).
E>Если идти по такому пути, то придется плодить массу мелких интерфейсвов (pdu_with_source_addr_t, pdu_with_dest_addr_t, pdu_with_data_coding_t, ...).
Именно. Имена только неудачные.
E> Но зачем?
Как зачем? В первую очередь для документирования этой общности. Вот, например, посмотрел ты на массив, и увидел у него метод GetEnumerator. Посмотрел на список, и увидел у него метод GetEnumerator. Подумал — и зафиксировал интерфейс IEnumerable. Который специфицирует некоторое общее поведение.
E>Способы обработки PDU могут быть разными. Где-то, например, только поле service_type контролируется, где-то только data_coding.
Можно плясать от обратного — не от сравнения интерфейсов классов, а от использования. В каком-то контексте есть согласованное использование подмножества методов — бах, выделил их в интерфейс.

E>Мы наверное говорим о разных use-case. Я имел ввиду, что в одном модуле приложения потребовалось как-то обрабатывать и deliver_sm, и data_sm. А во втором модуле -- как deliver_sm, data_sm, так и submit_multi_sm. Две разных, независимых задачи. В разных модулях.

Ну, всякое конечно бывает. Не повезло значит — та задача требовала одновременно несколько интерфейсов, а мы по ошибке запихали все в один интерфейс. Вот, например. в дотнете у IEnumerable свойства Count нету. А у IList — есть.
Ничего страшного — это же наш интерфейс. Отрефакторим его пополам. Все равно рано или поздно набор примитивов стабилизируется. А после небольшого опыта такого подхода (пять-шесть лет) ты будешь видеть эти интерфейсы сразу же, еще до того, как начнешь писать классы.
1.1.4 stable rev. 510
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[15]: Следующий язык программирования
От: Cyberax Марс  
Дата: 08.10.05 17:49
Оценка:
Andrei N.Sobchuck wrote:

> C>Так VAJе ведь именно так и было — пользователь работал на уровне

> C>элементов AST.
> Так какие были проблемы, кроме нестабильности самого VAJ?

Ну просто неудобно это было. Может для какого-нибудь Smalltalk'а среды
типа VAJ — это и нормально, но для Java они не очень-то и подходят.
Проще работать с программой как с _текстом_, а не с синтаксическим
деревом представленым визуально.

--
С уважением,
Alex Besogonov (alexy@izh.com)
Posted via RSDN NNTP Server 1.9
Sapienti sat!
Re[15]: Следующий язык программирования
От: Зверёк Харьковский  
Дата: 08.10.05 18:09
Оценка: +1
Здравствуйте, Dyoma, Вы писали:

ЗХ>>BTW, идеальной "записью намерений" для этого случая мне кажется что-то в этом роде:

ЗХ>>
ЗХ>>int sum = 0;
ЗХ>>sum += each(array);    //ну или each(array.begin, array.end);
ЗХ>>

ЗХ>>Читается "прибавить к сумме каждый элемент последовательности". Поддается легкой модификации:
ЗХ>>а) что сделать с элементами (например, += заменим на *=)
ЗХ>>б) с какими элементами (заменяем each на select(последовательность, условие) )

D>Очень интересная идея. Ты ее сам придумал или взял откуда?


С одной стороны, вроде бы сам — пока сообщение писал.
С другой стороны, сейчас я вспомнил — было обсуждение, что такое вроде есть в последнем Perl'e, и как бы это ввести в С++. Где-то здесь, в форуме С++ было.

D>Если взял, то поделись где, хочу по-больше узнать.

D>А пока по-больше не узнал, то есть у меня такой вопрос о расширяемости.
D>+= и *= — это понятно. Более того понятно даже как можно расширить, если я, к примеру, захочу прямоугольники пересекать:
D>
D>Rectangle intersection = Rectangle.PLANE; // Забудем о пересечении пустого набора, не о ней речь...
D>intersection = each(array).intersect(intersection);   // Для каждого элемента вызвать intersect с параметром, текущее значение intersection
D>


К слову сказать, бльшАя часть этих форичей, имхо, обрабатывает вот такие простые случаи — которые можно было бы сильно упростить таким each-ем.

D>Все хорошо, до тех пор, пока у элементов коллекции есть нужный мне метод. Но что делать если я хочу применить очень специфическую аккумуляцию? Например, я хочу сложить только четные элементы массива. Можно поступить так:

D>
D>int evenSum = 0;
D>evenSum += eventOrZero(each(array));

D>int evenOrZero(int x) {
D>    return x % 2 == 0 ? x : 0;
D>}
D>

D>Но тут мы потеряли исходную красоту, и получили почти C++ (забыл как этот алггоритм в stl называется, пусть for_each будет):

Факт.

D>Ну или можно типа C# лямбду применить

D>
D>int evenSum = ((sum, x) => sum + (x % 2 == 0 ? x : 0)) (0, each(array));
D>


Ну, я бы себе это себе представил как буст::лямбду.
int evenSum = 0;
evenSum += select(array, _1 % 2 == 0);

Вроде лакониччненько

D>Но уж больно исходный inject:into: напоминает...

D>А если пойдем еще дальше и потребуется вызывать размые методы (применять разные операции) в зависимости от значения очередного элемента? Например, четные прибывляем, на нечетные умножаем. Тут вроде как надо явно указать промежуточный результат.

Тады ой. Тады все. Хотя
int complexSum = 0;
complexSum += select(array, _1 % 2 == 0);
complexSum *= select(array, _1 % 2 != 0);


Но простые случаи, имхо, вполне неплохо бы смотрелись с таким штуками
FAQ — це мiй ай-кью!
Re[21]: Следующий язык программирования
От: VladD2 Российская Империя www.nemerle.org
Дата: 08.10.05 18:45
Оценка:
Здравствуйте, eao197, Вы писали:

E>Просто, исходя из смысла слов "отсылать" (send) и "вызывать" (call) не следует считать, что "отсылка" == "вызову". А в большинстве языков так и есть, причем изменить ничего нельзя.


Перечисли, пожалуйста, языки используещие ассинхронные механимы передачи сообщений.

Даю подсказку, Смолток, откуда ты выдрал эту терминалогию посылает сообщения синхронно.
... << RSDN@Home 1.2.0 alpha rev. 618>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[18]: Следующий язык программирования
От: Павел Кузнецов  
Дата: 08.10.05 19:37
Оценка: 1 (1)
VladD2,

> Шарп избежал большинство проблем С++.


Многие существенные стороны С++ в C# также остались за бортом (value-семантика, детерминированное разрушение, латентная типизация и т.п.), так что, действительно, можно о происхождении C# от C++ речи не вести

> Есть дженерики, которые и по идеологии, и по реализации сильно отличаются от плюсов. А почему их добавили ясно. Поняли, что языкам нехватает статического полимпорфизма.


Проще говоря, устали в коллекциях приводить типы из Object обратно в тип, который в колеекцию положили. Собственно, возможности generics в C# и Java примерно этим (пока?) по большей части и ограничиваются, если не считать оптимизаций, доступных в generics C# для value-типов.

> А это тут не причем. Хотя тех кто их использут достаточно. Важен дух программированя. В С++ очень часто из-за боязни тормозов вызываемых проверками типов и динамическим полипорфизмом отказываются от чистого ОО-дизайна и испльзуют разные не-ОО-приемы.


В C++ "отказываются от чистого ОО-дизайна" не из-за "боязни тормозов", а из-за понимания, что "чистое ООП" имеет достаточно ограниченную применимость, и для ряда моментов разные другие приемы приводят к более удачному (гибкому, понятному и т.п.) дизайну.

> На дизайн приложения при этом уже начхать. Мы же 4 байта сэкономили!


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

> функция без параметров:

>
> void f();
>

> рассматривается как:
>
> void f(...);
>

> в С <...>

Уточнение: она не так в C рассматривается. Она рассматривается как функция, информация о параметрах которой отсутствует. А объявление:
void f(...);

в C вообще допустимым не является.

> Разница между дженериками и шаблонами заключается в том, что в дженериках те самые контракты можно задать явно при создании дженериков. А в С++ они определяются неявно в момент вопложения шаблона. При этом копилятор С++ просто пораждает код по шаблону, а потом пытается проверить рпзультат на корректность.


Разница не столько в явном или неявном задании контрактов, сколько в том, какого рода контракты предполагаются или явно задаются там и там. В C# это (по большей части) требование реализовать тот или иной интерфейс. В C++ -- требование к типу обладать некоторой структурой. Concepts это только позволят задавать явно. Наследования от каких-либо интерфейсов для этого нужно не будет. Кроме явного задания concepts ничего от текущей ситуации в C++ не изменится.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re: Что такое полиморфизм
От: VladD2 Российская Империя www.nemerle.org
Дата: 08.10.05 19:51
Оценка: 18 (1)
Здравствуйте, Dyoma, Вы писали:

Нда... тяжело было почитать приведенные ссылки?

D>Ладно, предлагаю конструктивно поговорить на тему, что мы привыкли называть полиморфизмом. Вот, например такой код:

D>
D>if (area.containsPoint(mouseLocation) {
D> ... // Что-нить сделаем
D>}
D>

D>Это есть пример использования полиморфизма, а именно использование метода containsPoint(Point).

Откуда видно, что он полиморфен?

D>
D>class Area {
D>    virtual bool containsPoint(Point p) = 0;
D>class Rectangle : public Area {
D>  virtual bool containsPoint(Point& p) {
D>


Вот теперь это можно утверждать. У нас есть два типа к которым может применяться один метод.

Формализуем сигнатуру этого метода, чтобы далее было проще рассуждать.
Area      * Point -> bool
Rectangle * Point -> bool

Если не ясна запись, поясню... Мы имеем метод который можно представить как пару методов:
1. Принимает в качестве параметра аргументы типа Area, Point и возвращающий значение типа bool.
2. Принимает метод принимает аргументы типа Rectangle, Point и возвращает bool.
Собственно данное описание подразумевает, чтом может существовать неограниченное множетсво методов с сигнатурой:
T * Point -> bool

Где T тип являющийся наследником типа Area.
Второй метод как раз в него входит.
D>Написано, что Rectangle наследуется от Area для того, что бы его можно было использовать как Area, в частности в исходном if. А значит и здесь мы используем полиморфизм.

D>Это был полиморфизм динамический переходим к статическому. Перегрузку пропустим — imho не интересно.


Обрати внимание на выделенный фрагмент. Ниже мы к нему вернемся.

D> Возьмемся за шаблоны:

D>
D>template<class T>
D>T sum(iterator<T> begin, iterator<T> end) {
D>    T result = T();
D>  while (begin != end) {
D>        result = result + *begin;
D>        begin++;
D>    }
D>    return result;
D>}
D>

Да, это параметрический полиморфизм. В данном случае имеем метод с сигнатурой:
iterator<T> * iterator<T> -> T

Где T — это множество типов для которых применима операция сложения.

D>class Vector2D {
D>    Vector2D operator+(Vector2D& v) { return Vector2D(x+v.x, y+v.y); }
D>}
D>

D>Опять видим два использования полиморфизма. Один — алгоритм вычисления суммы, элементов, умеющих складываться. Второй — определение сложения для векторов, для общего удобства и применения алгоритма sum к векторам. В данном случае интерфейс из оператора + за нас определен в стандарте C++.

Ага. Это пример перегрузки методов про которое ты сказал, что она не интересна.
Полморфен в данном случае сам опрератор "+". Но он и до этого был в С++ плиморфен, так как его можно было применять к типам int, char, double... Ты всего лишь добавил поддержку для еще одного типа, т.е. сделал еще дну перегрузку.

D>Теперь вернемся к факториалу:

D>>>
D>>>template<int N> int factorial() {
D>>>    return N*factorial<N-1>();
D>>>}
D>>>int factorial<0>() {
D>>>    return 1;
D>>>}
D>>>

D>Я предлагаю взгянуть на этот так: Здесь описан интерфейс
D>
D>int factorial<int>();
D>

D>И две его имплементации. Для 0 и для всех остальных целых.

D>А можно было определить и так:

D>
D>int factorial<0>() { return 1; }
D>int factorial<1>() { return 1; }
D>int factorial<2>() { return 2; }
D>....
D>int factorial<12>() { return 479001600; } // Вроде это последний какой в 32 бита влезет

D>template<int N>
D>int factorial() {
D>    error "int overflow or negative";
D>}
D>


Замечательно. Теперь опишим сигнатуру этой функции:
void -> int

Все! Больше сигнатур породить нельзя! Параметр шаблона в данном случае это константа! Значение этой константы у всех реализаций метода имеет динаковый тип. Причем этот тип вообще нельзя ипользовать для задания типов внутри метода!

Таким образом мы получаем набор неполиморфных функций. В заблуждение же вводит схожесть синтаксисам с полиморфными описаниями. Но если присмотреться, то можно заметить, что в отличии от этой функции у действительно полиморфных сигнатура будет отличаться. Так для метода фанк:
template<class T> T fsnc();

можно породить несколько специализаций:
template <> int    fsnc<int>()    { return 1; }
template <> double fsnc<double>() { return 1.0; }
template <> char*  fsnc<char*>()  { return "1"; }
...

Описание приведенных ниже сигнатур:
void -> int
void -> double
void -> char*


Множество допустимых типов будет определяться исключительно содержимым полиморфного метода (так как код накладывает неявные ограничения на это множество). Однако то что С++ допускает явную специализацию позволяет утверждать, что этот список потенциально бесконечен.

D>А теперь сам полиморфный алгоритм:

D>
D>int f = factorial<10>();
D>

Продолжим список:
int f1 = factorial<1>();
int f2 = factorial<2>();
int f3 = factorial<3>();
int f4 = factorial<4>();

Не находишь, что разница тут только в названии метода? Если придерживаться твоей логики, то полиморфными будут и такие вызовы:
int f1 = factorial1();
int f2 = factorial2();
int f3 = factorial3();
int f4 = factorial4();

А собственно в чем между ними разница? Неужели то что первые сгенерированы компилятором в следствии выполнения метапрограммы, а вторные созданы вручную может влиять на свойства метода вроде полиморфизма? А если я, как ты предлагал, специализирую все варианты явно? В чем разница между:
template<int i> int func();

template<> int func<1>() { return 1; }
template<> int func<2>() { return 2; }
template<> int func<3>() { return 3; }
template<> int func<4>() { return 4; }
template<> int func<5>() { return 5; }

и:
int func1() { return 1; }
int func2() { return 2; }
int func3() { return 3; }
int func4() { return 4; }
int func5() { return 5; }



D>А теперь вопрос, что тебя особо смущает в примере с factorial?


Меня смущает, то что ты неверно трактуешь понятие полиморфизма. Причем делаешь это даже после того как я дал ссылку на довольно внятных источник описывающий значение этого термина. Ну, и еще смущает, неадекватность тех кто поставил тебе оценку, так как один из них является лучшим С++-ника рунета (думаю, это небудет приувеличением).
... << RSDN@Home 1.2.0 alpha rev. 618>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[19]: Следующий язык программирования
От: VladD2 Российская Империя www.nemerle.org
Дата: 08.10.05 20:28
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>Многие существенные стороны С++ в C# также остались за бортом (value-семантика, детерминированное разрушение, латентная типизация и т.п.), так что, действительно, можно о происхождении C# от C++ речи не вести


+1

ПК>Проще говоря, устали в коллекциях приводить типы из Object обратно в тип, который в колеекцию положили. Собственно, возможности generics в C# и Java примерно этим (пока?) по большей части и ограничиваются, если не считать оптимизаций, доступных в generics C# для value-типов.


Коллекции — это то где отсуствие средств обобщенного программирования проявляется особенно ярко. Вспомним, ради чегов С++ были введены шаблоны. Первым посылом был именно создать библиотеку для работы с коллекциями (контейнерами в С++-терминологии).

Однако дженерики пригодны везде где требуется статический полиморфизм. А то что ты все время пыташся принизить их значимость и выдать их за эдакую несостоятельную реализацию — это эже твои личные психологические проблемы.

ПК>В C++ "отказываются от чистого ОО-дизайна" не из-за "боязни тормозов", а из-за понимания, что "чистое ООП" имеет достаточно ограниченную применимость, и для ряда моментов разные другие приемы приводят к более удачному (гибкому, понятному и т.п.) дизайну.


Да, да, да. Как я незаметил? Конечно! Делается это именно из благих побудждений сделать куда более лучший дизайн. Что там еще С++ прилично поддерживает? Процедурное программирование? Ну, конечно! Ведь с его помощью почти все задачи решаются куда лучше чем с помощью ООП. А вот это — этак недоразумение.

Я бы поверил в эту сказку, если бы не видел примеры такого "лучшего" дизайна. И если бы сам не искушался бы соблазном нахимичить ради скорости.

>> На дизайн приложения при этом уже начхать. Мы же 4 байта сэкономили!


ПК>Дело не столько в экономии, сколько в существенном изменении семантики класса при добавлении к нему виртуальной функции. Например, его уже нельзя побайтно записать/прочитать из файла.


Ага. Согласен. Я от желания побайтно скопировать объекты отвыкал где-то год. Страшно приставучее заболевание. И конечно же такое копирование делается не из-за экономии времени, а ради лучшего дизайна. Ну, для улучшения качества и количества GPF-ов.

ПК>Уточнение: она не так в C рассматривается. Она рассматривается как функция, информация о параметрах которой отсутствует.


Это и есть с переменным количеством параметров. В С из-за этого параметры раком в стэк запихивают.

ПК> А объявление:

ПК>
ПК>void f(...);
ПК>

ПК>в C вообще допустимым не является.

Ну, да. Главное что интерпретация "void f();" разная. Если ты еще не забыл, я как бы говорил, о том что С и С++ совместимы не на 100 процентов. Корректный С-файл не обязан так же быть корректным С++-фйлом. А стало быть и проблем 100-ой совместимости нет.

>> Разница между дженериками и шаблонами заключается в том, что в дженериках те самые контракты можно задать явно при создании дженериков. А в С++ они определяются неявно в момент вопложения шаблона. При этом копилятор С++ просто пораждает код по шаблону, а потом пытается проверить рпзультат на корректность.


ПК>Разница не столько в явном или неявном задании контрактов, сколько в том, какого рода контракты предполагаются или явно задаются там и там.


Нет, уж, позволь... Как раз это основное отличие. Оно и приводит к неприминимости шаблонов в дотете. Ведь шаблоны можно будет использовать только внутри одного модуля. Библиотеки же останутся кривыми или через некоторое время весь их код окажется в шаблонах. Собственно это мы и наблюдаем в С++ сегодня.

ПК> В C# это (по большей части) требование реализовать тот или иной интерфейс.


И это тоже. Но это уже отличие не системное.

ПК> В C++ -- требование к типу обладать некоторой структурой. Concepts это только позволят задавать явно. Наследования от каких-либо интерфейсов для этого нужно не будет.


Ну, да. Те самые "утиные интерфейсы". В принципе удачное решение некоторых пробем С++.

ПК>Кроме явного задания concepts ничего от текущей ситуации в C++ не изменится.


Хм. Надеюсь изменится. Все же та задница которая существует с шаблонами сейчас, когда одна опечатка может привести к натуральному словестному поносу компилятора из которого ровным счетом ничего нельзя понять, надеюсь исчезнет. Хотя concept будет ведь необязательным... Ну, да хоть разные АТЛ-и с СТЛ-ями перепишут.
... << RSDN@Home 1.2.0 alpha rev. 618>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[24]: Следующий язык программирования
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 08.10.05 21:11
Оценка:
Здравствуйте, Sinclair, Вы писали:

E>>В том то и дело, что традиция эта в таких языках, как C++, Java, C# и пр. зашита намертво. Если нам в 5% случаев потребуется именно fire and forget, то сделать это в том же самом синтаксисе, что и обычный call, мы уже не сможем. А вот если бы смогли, это бы я и считал прорывом.

S>Ну, давай изобретем синтаксис. Например...
S>О!
S>p.Call(); // синхронный вызов
p->>Call(); // fire and forget;

Нет, мне больше нравится другой подход: не каждый метод приспособлен для того, чтобы исполнятся асинхронно. Хотя бы из-за необходимости синхронизации доступа к атрибутам объекта. Поэтому лучше объявлять метод асинхронным в описании класса, а для вызова использовать ту же самую нотацию:
class Big_Calculator
    {
    public :
        async some_calculation();
    };
    
Big_Calculator c;
c.some_calculation(); // fire and forget.


E>>А теперь расскажи, если не сложно, как ты видишь здесь детерминизм в виде синхронных вызовов, если объекты B, C и D должны работать на разных нитях. Во что все это выльется?

S>В работу в одной нити, конечно же. Потому что нет возможности добиться детерминированной работы в честно одновременной реализации. Все равно все, получается, будут вынуждены ждать друг друга.

Вовсе не обязательно.

S>Надо либо изобретать другую схему, либо возвращаться к однопоточной работе.


Вот, например, такая схема. У нас есть объекты A, B, C и D. Каждый работает на свой нити (активные объекты). Объект A отсылает сообщение a объектам B и C. После завершения обработки этого сообщения объект B отсылает сообщение b, а объект C -- сообщение c. Эти сообщения должны поступить объекту D, причем сначала b, затем c. Сложность в том, что B может заканчивать свою обработку после объекта C.

Для начала будем считать, что цепочка операций A (a) -> B, C; B(b) -> D, C(c) -> D является транзакцией. Если затем объект A породит следуюшее сообщение a (a2), то эта же цепочка повторится в виде новой транзакции: A(a2) -> B, C; B(b2) -> D, C(c2) -> D.

Для поддержки такой синхронизации сообщений bi, ci нам нужен некий монитор транзакции Mi (где i -- номер транзакции). Пусть этот монитор будет представлять из себя следующий конечный автомат:
начальное состояние "wait_b_c";
в состоянии "wait_b_c":
    если поступает сообщение b:
        сохраняем сообщение b;
        переходим в состояние "wait_c";
    если поступает сообщение c:
        сохраняем сообщение c;
        переходим в состояние "wait_b";
в состоянии "wait_c":
    если поступает сообщение с:
        отсылаем объекту D сообщения b, c;
        завершаем работу;
в состоянии "wait_b":
    если поступает сообщение b:
        отсылаем объекту D сообщения b, c;
        завершаем работу;


При наличии такого конечного автомата работа объектов A, B, C выглядит следующим образом:
Объект A
создаем монитор Mi;
отсылаем сообщение ai объектам B, C;

Объект B
ожидаем сообщения ai;
обрабатываем сообщение ai;
отсылаем сообщение bi (но так, чтобы его получил монитор Mi);

Объект C
ожидаем сообщения ai;
обрабатываем сообщение ai;
отсылаем сообщение ci (но так, чтобы его получил монитор Mi);


Собственно все. Объекты A, B, C, D работают на своих контекстах. Мониторам Mi собственный контекст не нужен, т.к. они вступают в дело только в редкие моменты времени (для их активации можно либо использовать контекст либого из активных объектов, либо отдельный контекст для не активных /пассивных/ объектов).
... << RSDN@Home 1.1.4 stable rev. 510>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[24]: Следующий язык программирования
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 08.10.05 21:11
Оценка:
Здравствуйте, Sinclair, Вы писали:

E>>Если идти по такому пути, то придется плодить массу мелких интерфейсвов (pdu_with_source_addr_t, pdu_with_dest_addr_t, pdu_with_data_coding_t, ...).

S>Именно. Имена только неудачные.

Имхо, это не самая удачная идея.

E>> Но зачем?

S>Как зачем? В первую очередь для документирования этой общности. Вот, например, посмотрел ты на массив, и увидел у него метод GetEnumerator. Посмотрел на список, и увидел у него метод GetEnumerator. Подумал — и зафиксировал интерфейс IEnumerable. Который специфицирует некоторое общее поведение.

E>>Способы обработки PDU могут быть разными. Где-то, например, только поле service_type контролируется, где-то только data_coding.

S>Можно плясать от обратного — не от сравнения интерфейсов классов, а от использования. В каком-то контексте есть согласованное использование подмножества методов — бах, выделил их в интерфейс.

В том-то все и дело. Интерфейсы нам нужны для определенного поведения в той или иной задаче. Чем больше разнородных задач, тем больше разнородных интерфейсов нам потребуется. Когда речь идет о PDU какого-либо протокола не нужно забывать, что PDU -- это данные. Т.е. у нас есть одни и те же данные, которые в разных задачах будут требовать для себя разных интерфейсов. Так вот вопрос в том, нужно ли в классы, которые инкапсулируют данные PDU, внедрять разнообразные интерфейсы? И проводить затем периодические рефакторинги этих классов при необходимости добавления новых интерфейсов для новой задачи.

Может быть проще оставить классы PDU в виде простейшего инкапсулятора данных PDU с методами getter-ами/setter-ами и сериализацией/десериализацией? А нужные для конкретной задачи интерфейсы представлять в виде внешних классов с врапперами-реализациями для конкретного типа PDU. Имхо, это более перспективный и гибкий путь.

Но если мы идем по этому пути в C++, то у нас есть выбор из двух альтернатив. Во-первых, обычный полиморфизм:
class pdu_with_source_addr_t
    {
    public:
        virtual std::string source_addr() const = 0;
    };
class deliver_sm_source_addr_t
    : public pdu_with_source_addr_t
    {
    public:
        virtual std::string source_addr() const { return pdu_.source_addr(); }
        ...
    };
class data_sm_source_addr_t
    : public pdu_with_source_addr_t
    {
    public:
        virtual std::string source_addr() const { return pdu_.source_addr(); }
        ...
    };
    
void handle_source_addr_from_pdu( const pdu_with_source_addr_t & pdu )
    { ... pdu.source_addr(); ... }


Во-вторых, статический полиморфизм на основе шаблонов:
template< class Pdu >
void handle_source_addr_from_pdu( const Pdu & pdu )
    { ... pdu.source_addr(); ... }


В C++ оба эти решения, имхо, эквиваленты, если у классов pdu есть нужные методы с одинаковыми сигнатурами. Но второй вариант, с одной стороны, короче и проще, а с другой стороны -- эффективней, т.к. нет вызова виртуальных методов и создания промежуточных объектов-врапперов.

Ну и я считаю, что шаблонная функция handle_source_addr_from_pdu и есть разновидность специфического интерфейса для специфической задачи над простейшей инкапсуляцией PDU.

В C#, насколько я понимаю, у меня есть только один путь -- через обычный полиморфизм, там даже generic-и не потребуются.
... << RSDN@Home 1.1.4 stable rev. 510>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[22]: Следующий язык программирования
От: _vovin http://www.pragmatic-architect.com
Дата: 08.10.05 21:12
Оценка: 1 (1)
Здравствуйте, VladD2, Вы писали:

VD>Здравствуйте, eao197, Вы писали:


E>>Просто, исходя из смысла слов "отсылать" (send) и "вызывать" (call) не следует считать, что "отсылка" == "вызову". А в большинстве языков так и есть, причем изменить ничего нельзя.


VD>Перечисли, пожалуйста, языки используещие ассинхронные механимы передачи сообщений.


VD>Даю подсказку, Смолток, откуда ты выдрал эту терминалогию посылает сообщения синхронно.


Надо уточнять — Smalltalk-72 посылал сообщения асинхронно.
Re[25]: Следующий язык программирования
От: Sinclair Россия https://github.com/evilguest/
Дата: 08.10.05 21:56
Оценка:
Здравствуйте, eao197, Вы писали:
E>Нет, мне больше нравится другой подход: не каждый метод приспособлен для того, чтобы исполнятся асинхронно.
E>Хотя бы из-за необходимости синхронизации доступа к атрибутам объекта.
Хм. Я неявно предполагал, что каждый объект исполняется в собственном потоке, и синхронизовывать ему нечего. Закончил обрабатывать одно сообщение — получил другое.
E>Вовсе не обязательно.
E>Вот, например, такая схема. У нас есть объекты A, B, C и D. Каждый работает на свой нити (активные объекты). Объект A отсылает сообщение a объектам B и C. После завершения обработки этого сообщения объект B отсылает сообщение b, а объект C -- сообщение c. Эти сообщения должны поступить объекту D, причем сначала b, затем c. Сложность в том, что B может заканчивать свою обработку после объекта C.

E>Для начала будем считать, что цепочка операций A (a) -> B, C; B(b) -> D, C(c) -> D является транзакцией. Если затем объект A породит следуюшее сообщение a (a2), то эта же цепочка повторится в виде новой транзакции: A(a2) -> B, C; B(b2) -> D, C(c2) -> D.


E>Для поддержки такой синхронизации сообщений bi, ci нам нужен некий монитор транзакции Mi (где i -- номер транзакции). Пусть этот монитор будет представлять из себя следующий конечный автомат:

E>Собственно все. Объекты A, B, C, D работают на своих контекстах. Мониторам Mi собственный контекст не нужен, т.к. они вступают в дело только в редкие моменты времени (для их активации можно либо использовать контекст либого из активных объектов, либо отдельный контекст для не активных /пассивных/ объектов).
Ага. А монитор, значить, вызывается синхронно. При этом в нем есть еще и встроенные средства защиты его ресурсов от конкурентного доступа.
В сиомеге такую штуку реализовали. Там все эти конечные автоматы генерируются компилятором. И именно так все и работает — ты вызвал метод, и он сразу же к тебе вернул управление, хотя никаких действий не было выполнено. А будет выполнено действие только тогда, когда кто-то вызовет соответствующий ему метод рандеву. В общем, довольно грамотно все завернуто. По крайней мере, потокобезопасная очередь получается вообще без объявления полей только пара методов.
1.1.4 stable rev. 510
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[22]: Следующий язык программирования
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 08.10.05 22:32
Оценка: 5 (1)
Здравствуйте, VladD2, Вы писали:

E>>Просто, исходя из смысла слов "отсылать" (send) и "вызывать" (call) не следует считать, что "отсылка" == "вызову". А в большинстве языков так и есть, причем изменить ничего нельзя.


VD>Перечисли, пожалуйста, языки используещие ассинхронные механимы передачи сообщений.


Я не знаю таких языков.
Но в языках, где вызов метода рассматривается как отсылка сообщения (Ruby, может быть, Smalltalk) существует возможность перехвата вызова любого метода. А это делает возможным при перехвате сохранить параметры и передать их на выполнение в другой поток. Но сам, исходный вызов в коде не будет отличаться от вызова обычного метода.

Вот например:
class Test < AsyncBase

    # Для объявления асинхронного метода используем async вместо def.
    async :hello do
        puts "hello"
    end
end

t = Test.new
t.hello

печатает:
hello intercepted
hello


Происходит это благодоря тому, что в базовом классе AsyncBase было немного шаманства с метапрограммированием и перехватом методов:
class AsyncBase
    def self.async( method, &blk )
        define_method method, &blk
        class_eval <<-EOS
            alias :pre_async_#{method} :#{method}
            def #{method}
                puts "#{method} intercepted"
                pre_async_#{method}
            end
        EOS
    end
end


Эта реализация просто печатает сообщение о перехвате метода и тут же запускает метод на выполнение. А могла бы инициировать вызов метода на контексте другой нити. Но вызов hello для объекта t ничем не отличается от вызова любого другого метода.
... << RSDN@Home 1.1.4 stable rev. 510>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[26]: Следующий язык программирования
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 08.10.05 22:38
Оценка:
Здравствуйте, Sinclair, Вы писали:

E>>Нет, мне больше нравится другой подход: не каждый метод приспособлен для того, чтобы исполнятся асинхронно.

E>>Хотя бы из-за необходимости синхронизации доступа к атрибутам объекта.
S>Хм. Я неявно предполагал, что каждый объект исполняется в собственном потоке, и синхронизовывать ему нечего. Закончил обрабатывать одно сообщение — получил другое.

Это самый простой и не интересный вариант

E>>Вовсе не обязательно.

E>>Вот, например, такая схема. У нас есть объекты A, B, C и D. Каждый работает на свой нити (активные объекты). Объект A отсылает сообщение a объектам B и C. После завершения обработки этого сообщения объект B отсылает сообщение b, а объект C -- сообщение c. Эти сообщения должны поступить объекту D, причем сначала b, затем c. Сложность в том, что B может заканчивать свою обработку после объекта C.

E>>Для начала будем считать, что цепочка операций A (a) -> B, C; B(b) -> D, C(c) -> D является транзакцией. Если затем объект A породит следуюшее сообщение a (a2), то эта же цепочка повторится в виде новой транзакции: A(a2) -> B, C; B(b2) -> D, C(c2) -> D.


E>>Для поддержки такой синхронизации сообщений bi, ci нам нужен некий монитор транзакции Mi (где i -- номер транзакции). Пусть этот монитор будет представлять из себя следующий конечный автомат:

E>>Собственно все. Объекты A, B, C, D работают на своих контекстах. Мониторам Mi собственный контекст не нужен, т.к. они вступают в дело только в редкие моменты времени (для их активации можно либо использовать контекст либого из активных объектов, либо отдельный контекст для не активных /пассивных/ объектов).
S>Ага. А монитор, значить, вызывается синхронно. При этом в нем есть еще и встроенные средства защиты его ресурсов от конкурентного доступа.

Не обязательно. Он так же может быть объектом, реагирующим на сообщения.

S>В сиомеге такую штуку реализовали. Там все эти конечные автоматы генерируются компилятором. И именно так все и работает — ты вызвал метод, и он сразу же к тебе вернул управление, хотя никаких действий не было выполнено. А будет выполнено действие только тогда, когда кто-то вызовет соответствующий ему метод рандеву. В общем, довольно грамотно все завернуто. По крайней мере, потокобезопасная очередь получается вообще без объявления полей только пара методов.


А у нас в SObjectizer-е (это та штука, ссылку на которую я тебе дал) именно обмен сообщениями используется. Т.к. все сделано на C++ без кодогенерации, то отсылка сообщения сильно отличается от простого вызова метода. А хотелось бы как-то так: Re[22]: Следующий язык программирования
Автор: eao197
Дата: 09.10.05
... << RSDN@Home 1.1.4 stable rev. 510>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[19]: Следующий язык программирования
От: alexeiz  
Дата: 08.10.05 23:43
Оценка: +1
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>VladD2,


>> Разница между дженериками и шаблонами заключается в том, что в дженериках те самые контракты можно задать явно при создании дженериков. А в С++ они определяются неявно в момент вопложения шаблона. При этом копилятор С++ просто пораждает код по шаблону, а потом пытается проверить рпзультат на корректность.


ПК>Разница не столько в явном или неявном задании контрактов, сколько в том, какого рода контракты предполагаются или явно задаются там и там. В C# это (по большей части) требование реализовать тот или иной интерфейс. В C++ -- требование к типу обладать некоторой структурой. Concepts это только позволят задавать явно. Наследования от каких-либо интерфейсов для этого нужно не будет. Кроме явного задания concepts ничего от текущей ситуации в C++ не изменится.


Кроме того, что по concept'ам можно будет перегружать.
Re[22]: Следующий язык программирования
От: Cyberax Марс  
Дата: 09.10.05 11:08
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Здравствуйте, eao197, Вы писали:

E>>Просто, исходя из смысла слов "отсылать" (send) и "вызывать" (call) не следует считать, что "отсылка" == "вызову". А в большинстве языков так и есть, причем изменить ничего нельзя.
VD>Перечисли, пожалуйста, языки используещие ассинхронные механимы передачи сообщений.
Эрланг Общение между потоками там происходит с помощью посылки асинхронных сообщений.
Sapienti sat!
Re[23]: Следующий язык программирования
От: Andrei N.Sobchuck Украина www.smalltalk.ru
Дата: 09.10.05 11:39
Оценка: 18 (3)
Здравствуйте, eao197, Вы писали:

E>Здравствуйте, VladD2, Вы писали:


E>>>Просто, исходя из смысла слов "отсылать" (send) и "вызывать" (call) не следует считать, что "отсылка" == "вызову". А в большинстве языков так и есть, причем изменить ничего нельзя.


VD>>Перечисли, пожалуйста, языки используещие ассинхронные механимы передачи сообщений.


E>Я не знаю таких языков.

E>Но в языках, где вызов метода рассматривается как отсылка сообщения (Ruby, может быть, Smalltalk) существует возможность перехвата вызова любого метода. А это делает возможным при перехвате сохранить параметры и передать их на выполнение в другой поток. Но сам, исходный вызов в коде не будет отличаться от вызова обычного метода.

Похожая схема /пока что/ и используется в Croquet, который базируется на Squeak. Там, правда, реч идёт не об синхронности/асинхронности, а об локальном/реплицируемом-удалённо вызове. Если отправка обычного сообщения выглядит как
    aReceiver someMessage

то отправка реплицируемого на удалённые машины сообщения производится так:
  aReceiver meta someMessage


meta возвращает прокси, который и производит переадресацию сообщения. Такая техника позволяет выбирать режим отправки. Естественно, это применимо и к асинхронным сообщениям. Например в И-нете я надыбал сообщение за 1999г. о применении этой техники для отправки асинхронных сообщений в Squeak.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Я ненавижу Hibernate
Автор: Andrei N.Sobchuck
Дата: 08.01.08
!
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.