Re: programming languages (was: kylix2) IMHO: OFFTOPIC
Dmitry Astapov wrote:
Какой тип у foreach и function_obj? Подсказка: "void *" - это
несерьезно.
AS> В С++ эти элементы имеют определенные типы! Они, правда, подводятся
AS> под шаблоны, но проверка типов там строгая, и трюки с void * там не
AS> пройдут - придется применять специальные операторы-функции:
AS> static_cast или dynamic_cast! Вот точное, концептуальное определение
AS> данной функции (из STL):
AS> template<class In, class Op> Op for_each( In first, In last, Op f){
AS> while (first != last) f(*first++);
AS> return f;
AS> }
Вопрос: где зафиксирована информация о том, что f должна принимать
аргумент того же типа, который имеет (*first++)?
В определении Op (класса объекта f).
first имеет класс "итератора", вроде bidirectional_iterator в случае с
list. Внутри него перегружен его оператор operator*(), который, при
обычном систаксисе С, возвращает значение переменной, указываемой first.
Итератор в C++ - это усложненный и более "умный" указатель (pointer),
который может даже проверять свое значение (т.н. "итераторы с
проверкой"), если это необходимо - после дополнительного программирования.
*first таким образом возвращает (да, именно возвращает, так как в данном
случае мы имеем дело с оператором-функцией) объект, на который указывает
данный итератор. Чаще всего (и в данном мной примере из книжки в
частности) это делается для удобной работы с контейнерами (list, queue,
map, vactor, ...).
*first++ заставлет итератор указать на следующий элемент в
последовательности (контейнере) и возвратить значение элемента. Это
обыденный трюк с приоритетами операторов. Оператор * (получение
значения) имеет самый низкий приоритет в С/С++.
Значение элемента всегда имеет строго определенный тип. В приведенном
ниже примере *(lc.begin()) будет иметь тип Club, так как lc.begin()
указывает на первый элемент списка lc, состоящего из объектов типа Club.
Например (из "Языка программирования С++"):
void extract(const list<Club>& lc, list<Person*>& off){
//...размещаем персонал из 'lс' в 'off'
for_each(lc.begin(), lc.end(), Extract_officers(off));
}
class Extract_officers {
list<Person *> &lst;
public:
explicit Extract_officers(list<Person *>& arg): lst(x){}
void operator()(const Club& c){
copy(c.officers.begin(),
c.officers.end(), back_inserter(lst));
}
};
Попробуем сделать так:
void extract(const list<Punks>& lp, list<Person*>& off){
//...размещаем персонал из 'lс' в 'off'
for_each(lp.begin(), lp.end(), Extract_officers(off));
}
- будет выдана ошибка (типа: *first не принадлежит к классу Club) и
программа компилироваться не будет.
AS> Проверка типов является одним из приоритетов С++. Это, так сказать,
AS> "строго типизированный язык".
Однако наличие неявных приведений типов и динамической типизации все
портит. Для понимания того, как можно жить иначе, можно почитать о том, что
есть Hindley-Milner type system и как она используется в Clean, Haskell или
OCaml.
Хм... Дело в том, что при использовании static_cast и dynamic_cast
всегда происходит проверка соотвествия типов. На обе функции наложены
строгие ограничения. В частности, с помощью static_cast нельзя
преобразовывать виртуальные базовые классы в их производные классы. В
случае с dynamic_cast та же проверка просто откладывается на этап
выполнения. Конечно, это "hack", то есть возможность на грани излишней,
хотя она все-таки, очевидно необходима в некоторых случаях. Тут нельзя
как в С сделать преобразования вида:
char c = 'a';
void *f = (void*)&c;
// CoolWidget - виртуальный базовый класс для CoolButton.
CoolButton r = new CoolButton();
CoolWidget* ps = &r;
// Ошибка: нельзя выполнить приведение из виртуального базового класса
pr = static_cast<CoolButton*>(ps);
Каким образом происходит проверка того, что function_obj принимает аргумент
именно того типа, который содержится в списке?
Можно ли сделать:
foreach(list_iterator.first, list_iterator.last,
foreach(other_list_iterator.first, other_list_iterator.last, function_obj));
(это синтаксически неверно, но идея должна быть понятна. Если для этого
нужно более 3 строк кода, то хорошо ли это?)
AS> Почему нельзя? Можно! И синтаксически все правильно - мы говорим о С++
AS> в данном случае (см. выше: "> AS> В С++ это делается так:")
AS> (извините, незаметно переменил подтему). Именно для этого и писалась
AS> эта стандартная алгоритмическая функция, чтобы можно было
AS> объектно-ориентированными средствами языка имитировать кострукции вида:
AS> for(Iterator counter; counter != container.end(); counter++){
AS> for(Iterator counter2; counter2 != container[1].end(); counter2++){
AS> // do smth...
AS> }
AS> }
И где же ответ на мои вопросы? Я повторю:
1)Каким образом происходит проверка того, что function_obj принимает аргумент
именно того типа, который содержится в списке?
Это уже компетенция компилятора, я думаю... ;-) То есть компилятор
ДОЛЖЕН проверять это, и он это делает. Это провеяет модуль STL (Standard
Templates Library), который "инстанцирует" из шаблона list классы вида
list<int>, list<MyClass>, ... Если ты берешь объект my_list класса
list<MyClass> и пытаешься сделать с ним вот это:
for_each(my_list.begin(), my_list.end(), addvaluesbyone());
И при этом класс addvaluesbyone не перегружает оператор () или этот
перегруженный оператор не принимает объекты типа MyClass, а только int
или float, или user_defined, то будет выдана ошибка компиляции.
Каким конкретно образом это происходит, я, пардон, не знаю и пока что не
хочу знать. Главное, что это происходит, и, в частности, я уверен,
производится g++ из GCC.
2)Этот вопрос я сформулировал неверно. Постараюсь изменить. Есть ли способ
сделать type-safe композицию двух произвольных функций?
Т.е. написать функцию compose типа
forall c a b. (b -> c) -> (a -> b) -> a -> c
которая реализует композицию фунций (в том смысле, в котором это определено
в математике, (compose f g)(x) = f(g(x)))
Сколько строк кода на это потребуется? Что будет с проверкой типов?
Ах вот оно что! Ты хочешь реализацию сложной функции f(g(x))?
Вот реализация на С:
float f(float (*g_func)(float x)){
float a = (*g_func)(x);
return sin(sqrt(a));
}
float g(float x){
return do_something(x);
}
Совершенно type-safe. Другое дело, что с такой специлизацией это в
универсальную библиотеку С не в ставишь, но для конкретных мелких задач
вполне подойдет.
На С++ это можно записать совершенно также, но можно применить и ООП-метод.
Например:
template <class T>
class composite_func {
T rep;
composite_func g;
public:
explicit composite_func(composite_func& gf, T arg = 0.0):
g(gf), rep(arg){}
explicit composite_func(T arg = 0.0):
rep(arg){}
T operator()(T arg){
//do something with arg using g
return arg;
}
T operator()(void){
//do something with rep using g
return rep;
}
};
Теперь composite_func можно использовать как обычную функцию. Только
инстанциация должна проходить так, например: composite_func<float>();
Такую функцию можно использовать во всех стандартных алгоритмах STL C++.
Например:
x = 3.1415;
vector<float> vc(100);
generate(vc.begin(), vc.end(), composite_func(composite_func(x));
Наконец, замечу, что С++ все-таки язык системного программирования, и он
оптимизирован именно под системные нужды - скорость, низкоуровневость
(привязанность к системным особенностям), ... Я не могу сказать, что он
лучше всех, сверх-универсален, "и ваще". :-)
Еще я советую где-нибудь достать христоматийную книгу от Бьерна
Страуструпа "Язык программирования С++". У меня дома лежит русский
перевод от издательства "БИНОМ" 1999 года. Оригинал был издан
Addison-Wesley Longman. Очень полезная книга для тех кто сомневается в
достоинствах С++ или плохо с ним знаком.
С другой стороны, при всех достоинствах С++, его сложность,
обусловленная большой формализованностью и низкоуровневостью
(унаследованных от С) не позволяют использовать его во всех задачах
одинаково эффективно... Для простых задач (например, CGI-скрипты) я,
например, предпочитаю Python, Perl (в меньшей степени).
Вообще, у меня создалось впечатление, что мы с тобой говорим на разных
языках. Это даже не уверенность, а очевидность. Я тебе рассказываю про
С++, С, а ты мне про Haskell, Clean... Я очень плохо понимаю Haskell и
вижу, что ты не особо знаком с C++... Получается прикольно. :-) Впрочем,
я уверен, что это пойдет нам обоим на пользу. И искренне на это надеюсь. :-)
--
Andrei Sosnin
http://zzx.ath.cx
<!-- : it all depends on your vision : -->
Reply to: