Как можно использовать subj в императивном языке (меня интересует С, С++)?
Если не сложно, приведите примеры небольших кусков кода, реализующих одно и тоже в виде:
(ни фига не функционально ) ====> (весьма функционально ).
Спасибо .
Здравствуйте, realloc, Вы писали:
R>Как можно использовать subj в императивном языке (меня интересует С, С++)? R>Если не сложно, приведите примеры небольших кусков кода, реализующих одно и тоже в виде: R>(ни фига не функционально ) ====> (весьма функционально ). R>Спасибо .
Были глобальные переменные — стало протягивание состояния.
Здравствуйте, thesz, Вы писали:
T>Здравствуйте, realloc, Вы писали:
R>>Как можно использовать subj в императивном языке (меня интересует С, С++)? R>>Если не сложно, приведите примеры небольших кусков кода, реализующих одно и тоже в виде: R>>(ни фига не функционально ) ====> (весьма функционально ). R>>Спасибо .
T>Были глобальные переменные — стало протягивание состояния.
Угу, хорошо. А ещё есть, посложнее?
T>Линейное протягивание состояния функционально.
Здравствуйте, DemAS, Вы писали:
DAS>"thesz" <49985@users.rsdn.ru> writes:
>> Были глобальные переменные — стало протягивание состояния.
DAS>Как то это больше на полу-объектно-ориентированное программирование стало DAS>похоже. Еще структуру заменить на класс, а функции сделать методами класса.
DAS>По теме — может статью Влада про LINQ посмотреть ?
>> Были глобальные переменные — стало протягивание состояния. DAS>Как то это больше на полу-объектно-ориентированное программирование стало DAS>похоже. Еще структуру заменить на класс, а функции сделать методами класса.
Тогда получится глобальное состояние в членах класса.
Нам надо, чтобы всё зависело только от входов, менять можно только линейно протягиваемое состояние.
Yours truly, Serguey Zefirov (thesz NA mail TOCHKA ru)
Здравствуйте, thesz, Вы писали:
T>>>Были глобальные переменные — стало протягивание состояния. R>>Угу, хорошо. А ещё есть, посложнее?
T>А что интересует?
Всё! Читая про ФП, часто встречал такую мысль: "даже если вы в дальнейшем не будете использовать никакой ФЯ, функциональный стиль окажет сильное влияние на ваш стиль написания программ". Хочется понять на конкретных примерах, как этот самый стиль выливается в конкретные конструкции императивного языка.
T>Было дело, делал оптимизацию выражений (распространение констант, уменьшение глубины и тп).
T>Так я сделал арены, в которых выделялась память и всегда возвращал новое вычисление.
T>Типа того: T>
T>expr* simplify(expr*a) {
T> switch (a->tag) {
T> case PLUS:
T> expr* l = simplify(a->bin->left);
T> expr* r = simplify(a->bin->right);
T> if (l->tag == CONST && r->tag == CONST)
T> return expr_const(l->constant+r->constant);
T> return expr_bin(PLUS,l,r);
T> case MINUS: ...
T> }
T>} /* simplify */
T>
T>В результате вероятность переписать поверх чего-то важного исчезла практически полностью.
T>По завершению некоторого цикла я копировал в новые арены мои "корни" (важные для меня выражения), а старые переиспользовал.
T>OCaml, вид в профиль.
Э-ээ...., поясни, плиз, вполне процедурный код, имхо. Что здесь функционального?
Здравствуйте, realloc, Вы писали (смайлики скиппед):
R>Как можно использовать subj в императивном языке (меня интересует С, С++)? R>Если не сложно, приведите примеры небольших кусков кода, реализующих одно и тоже в виде: R>(ни фига не функционально) ====> (весьма функционально). R>Спасибо.
Я делаю так.
1) Предпочитаю statement'ам expression'ы.
a) Стараюсь по возможности вместо if-statement'а использовать if-expression (т. е. тернарный оператор). Недостатком такого подхода является то, что некоторые недалёкие программисты в штыки встречают синтаксис (cond ? expr1 : expr2). Дескать он ухудшает читаемость, и вообще нефиг выпендриваться. Мне же семантика важнее синтаксиса, даже если последний не очень удачен.
Пример перехода от if-statement'а к if-expression'у в C++. Было:
int i = 42;
std::string s("Hello world");
bool condition = true;
if (condition) {
i = 4;
s = "Hello";
}
else {
i = 2;
s = "world";
}
std::cout << boost::format("(i, s) = (%1%, %2%)") % i % s << std::endl; // (i, s) = (4, Hello)
Возможно, такой код покажется слишком сложным, но это вызвано неуклюжестью языка C++, в ФЯ такой код намного проще.
b) Предпочитаю вызов функции, возвращающей результат, вместо «процедуры», меняющей аргумент. В случае C# делать рефакторинг «Повышение Декларативности» проще, там ссылочная модель. Так, например, в C++ я не могу просто взять и заменить функцию типа
void Foo(std::vector<int>& v, …);
на функцию
std::vector<int> Foo(…);
потому что return value optimization не гарантируется Стандартом. Приходится либо оборачивать вектор boost::shared_ptr'ом (а то и вовсе использовать boost::shared_array), либо смириться, и передавать его по ссылке. Либо перейти на вменяемый императивный язык ;)
c) В задаче замены statement'ов expression'ами иногда помогает редко используемый оператор запятая. Чаще всего он применяется в инструкции return для превращении цепочки инструкций в выражение. Например, было:
Ещё на тему оператора запятая поприкалывался Саттер в первом «задачнике», см. пункт 1.18. Mastermind, Решение №2.
2) Если есть возможность писать без внешних состояний (блюсти чистоту), я эту возможность использую. Меньше состояний, меньше глобальных данных, за счёт более частого протаскивания нужных данных в аргументы, и возвращения их из функций вместо модификации. В крайнем случае, можно «запомнить» ссылку на внешние данные в замыкании. В C# это делается с помощью лямбда-выражений, в C++ (ограниченно) с помощь boost::bind. Кроме того, замыкания часто позволяют уменьшить связность в коде (если класс A должен вызывать метод класса B, то не обязательно передавать классу A ссылку на B, можно просто передать классу A анонимную функцию, использующую B, а сам A ничего о B знать не будет).
3) Меньше функций, меняющих состояние каких-то входных данных. Если нужно что-то изменить, лучше вернуть его из функции (как говорилось выше, в C++ такой подход не очень просто сделать эффективным). Грубо говоря, надо стараться минимизировать функции, принимающие параметры по неконстантной ссылке. В C++/C# есть другая проблема — что, если результатом функции должно быть несколько значений? В хороших языках есть встроенные tupl'ы, в C++ есть корявые Boost.Tuples. В C# по-прежнему приходится менять параметры (out/ref), ну или возвращать их упакованными в структуру (самопальный недокортеж).
4) Где только можно использую immutable данные. Вместо модификации данных лучше создавать новые объекты. Т. е. что-то типа такого: вместо
// C#var c = new MyComplex(2.0, 3.0);
// …
c.Real = 4.0;
сделать MyComplex неизменяемым и при необходимости создавать новый экземпляр:
c = new MyComplex(4.0, c.Imag);
5) Функции высших порядков — очень упрощают рассуждения над программой и её кодирование. К сожалению, в C++98 сейчас не поддерживаются почти никак (boost::function, boost::bind), в C++09 будут поддерживаться кое-как.
Здравствуйте, realloc, Вы писали:
R>Как можно использовать subj в императивном языке (меня интересует С, С++)? R>Если не сложно, приведите примеры небольших кусков кода, реализующих одно и тоже в виде: R>(ни фига не функционально ) ====> (весьма функционально ). R>Спасибо .
Здравствуйте, DemAS, Вы писали:
DAS>Как то это больше на полу-объектно-ориентированное программирование стало DAS>похоже. Еще структуру заменить на класс, а функции сделать методами класса.
Нет это совсем не похоже на ООП. Там логика практически противоположная.
Я такое протягивание состояния достаточно часто использую, но как-то не думал что
оно функционально, и даже допускал его как легкое нарушение функциональности для
повышения эффективности.
Здравствуйте, Qbit86, Вы писали:
Q>Пример перехода от if-statement'а к if-expression'у в C++. Было:
Через мой code review этот кошмар не прошёл бы. В исходном примере кода следовало бы заменить boost::format на нормальную работу с std::cout чтобы исключить boost из проекта и этим ограничится, остальное там ясно и просто. Вы же вместо этого заменили простой и понятный каждому промышленный код на усложнённый, непонятный для новичка и даже для сениора неудобочитаемый. К тому же гораздо дольше компилируемый. И вероятно к тому же со штрафами на исполнение, хотя тут профайлером надо смотреть, но уж точно не ускорили. В топку такой рефакторинг.
Здравствуйте, Tilir, Вы писали:
T>Через мой code review этот кошмар не прошёл бы.
Год назад через мой код ревью тоже не прошёл бы.
T>В исходном примере кода следовало бы заменить boost::format на нормальную работу с std::cout
Ссспаде, дался вам здесь Буст. Он в этом примере только как «подстрочный перевод» человеческой записи
Console.WriteLine("(i, s) = ({0}, {1})", i, s);
Синтаксис boost::format'а страшен как смертный грех скорее не из-за разработчиков Буста, а из-за разработчиков C++. Тем не менее, если абстрагироваться от внешнего вида, и сосредоточится на семантике (как в C# выше), то я бы предпочёл вариант с Бустом. Так как он лучше отражает мои намерения, чем какие-то потоки вывода.
T>на нормальную работу с std::cout
Работа с std::cout не может быть нормальной. Уж очень часто приходится прибегать к boost::ios_state_saver для сохранения состояния потока in a RAII-manner.
T>чтобы исключить boost из проекта и этим ограничится
А зачем его исключать из проекта? Он сокращает время разработки и увеличивает свободное время разработчика. Я своё время ценю.
T>Вы же вместо этого заменили простой и понятный каждому промышленный код на усложнённый, непонятный для новичка и даже для сениора неудобочитаемый.
Во-первых, такое усложнение происходит редко, как правило, нужно менять по условию только одну переменную, так что обходится без кортежей в 99% случаев. Во-вторых, семантически этот код прост и ясно выражает мои намеренья, а его синтаксическая сложность вызвана только ущербностью C++. В том же Питоне работа с туплами не намного отличается от работы с одиночными переменными, код единообразен и прост. Т. е. в этом случае не «код усложнённый», а синтаксис усложнённый. Моей вины в том нет.
Про «непонятный для новичка». Моим коллегам такой код был бы понятен. Пойми, мне важно получать кайф от работы. Тратить треть суток на «перебирание гречки» для меня неприемлемо. Я скорее найду другую работу, чем продолжу тянуть лямку в том месте, где сеньоры из-за своей костности и инертности настаивают на снижении качества кода для понимания новичками, вместо подтягивания самих новичков.
T>К тому же гораздо дольше компилируемый.
C++ вообще долгокомпилируемый язык.
T>И вероятно к тому же со штрафами на исполнение, хотя тут профайлером надо смотреть, но уж точно не ускорили.
Об этом я подумаю в самую последнюю очередь. Прежде всего я буду оптимизировать свой труд, и если компилятор плохо справляется со своим (оптимизация), тогда уже буду помогать компилятору. Ну, или сменю язык (как и сделал год назад).
Здравствуйте, realloc, Вы писали:
R>Всё! Читая про ФП, часто встречал такую мысль: "даже если вы в дальнейшем не будете использовать никакой ФЯ, функциональный стиль окажет сильное влияние на ваш стиль написания программ". Хочется понять на конкретных примерах, как этот самый стиль выливается в конкретные конструкции императивного языка.
Тут какой вопрос. Взгляды на мир менять придется. Код-то по прежнему будет написан на императивном языке. И показав этот код, человеку, незнакомому с ФП, можно будет услышать что это красиво написанный код. А человек который знаком с ФП может сказать, ага, это же XXX из Haskell, вид сбоку. А смотреть будут на один и тот же код.
Здравствуйте, realloc, Вы писали:
R>Как можно использовать subj в императивном языке (меня интересует С, С++)? R>Если не сложно, приведите примеры небольших кусков кода, реализующих одно и тоже в виде: R>(ни фига не функционально ) ====> (весьма функционально ). R>Спасибо .
Я сделаю проще. Дам тебе два ключевых слова по которым можно будет найти примеры в Гугле:
STL, boost
Поддержка функционального стиля в С++ очень плохая, но хоть что-то...
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.