Здравствуйте, VladD2, Вы писали:
VD>А что же с C# то? Давай как с Nemerle-ом сравним.
Да вот фиг. Я пока могу сравнивать с тем, на чем рискну делать реальные проекты.
VD>Хотя и тут в общем то видно, что из С++ пытаются выжать то на что он явно не рассчитан. Что за "var(x)" иди "_1"? Что это за не определенная грязь?
_1, _2 и т.д. — это плейсхолдеры, их семантика примерно такова (по аналогии с C#):
delegate(Type1 _1, Type2 _2, Type3 _3) { ... }
VD>Ты лучше вспомни, о том, что boost::lambda вынуждает:
VD>1. Тащить за собой огромную библиотеку.
За собой как раз тащить не надо (т.е. за готовым продуктом
), а во время разработки я и на C# кучу либ использую и меня не напрягает это, скорее наоборот, радует, что кто-то за меня сделал приличную часть работы
VD>2. Резко замедляет компиляцию.
если включить интересующее в stdafx.h, то не так уж и резко.
VD>3. Применима только в примитивнихшых случаях.
именно! и это важное замечание... если на какой-то алгоритм потрачено прилично времени и сил, то не стоит, ИМХО, хоронить его в локальном замыкании, добавь пару строчек оформления для public или там protected-использования.
Т.е. на мой взгляд, все эти "местные" мелкие ф-ии как раз нужны или для примитивных алгоритмых местного масштаба, или для "адаптации" уже имеющихся алгоритмов к местным данным/условиям (curring), вот как раз и с первым и со вторым лямбда прекрасно справляется (связка lambda + bind).
И еще насчет примитивности. Лямбды изначально расчитаны на функциональный стиль и польностью покрывают ВСЕ перегружаемые операторы С++. Хотя, в принципе, можно писать выражения через запятую, это означает их последовательное исполнение. Так же существуют switch, while, if, и loop. Не так уж и примитивно.
VD>4. Неполноценная реализация.
Ты имеешь ввиду — не встроена в язык?
Разумется, сама эта либа потому и существует, что это не встроено в язык. Хотя, мне до сих пор не понятно, почему этого не было сделано. Вон даже в Паскале это есть.
Мне вполне понятно, почему этого не было в С (ведь замыкание требует передачу "окружения", т.е. дополнительные расходы и лишний уровень коссвенной адресации), но в С++, с его оптимизациями и инлайнингом во многих случаях эти расходы банально нивелировались бы...
VD>Что же ты, например, не смог воспроизвести даже примитивного кода? Почему у тебя в последней функции:
ф-ии высшего порядка создаются через bind, вот аналог твоего варианта:
Predicate GreaterOrEquals = bind(Greater, _1) || bind(Equals, _1);
Забиндить можно было что угодно. Хоть ф-ию, хоть константу. Мы биндим (связываем) с аргументом ф-ии некое значение, либо ф-ию.
VD>В общем, очередная демонстрация так называемой аргументации. Какая на фиг краткость в плюсах? Акстись! Погляди на реальный код на С++. Это же ужасно!
VD>Тут рядомАвтор: VladD2
Дата: 06.03.06
орел даже не поверил, что понравившеся ему извращение на плюсах можно во много раз более кратко записать на шарпе.
Ну ладно... по этой теме мы когда-то ломали копья более обстоятельно и со знанием дела
Помниться, я даже вывел идею TransformIterator, на основе которых делал все остальные эмуляции yield. Хотя, действительно очень удобно. Особенно, когда надо перебрать несколько разнородных коллекций. Например, часть закешированных объектов, а остальные достать из базы (если очередь в переборе дойдет).
Извраты типа лямбды наверняка кто-то придумает в С++, чтобы закрыть наиболее частовстречающиеся сценарии с yield.
V>>Замыкание красиво и элегантно создается конструкцией var(x)
VD>Красота не описуемая.
VD>Да и что же ты этой красотой так же Greater и Equals не замкнул?
Потому как я их забиндил, уже показал выше как.
И кстати, у меня там небольшой "синтаксический оверхед". Когда в выражении участвует placeholder, то писать var(x) вовсе не обязательно! Для плейсхолдеров операторы уже перегружены таким образом, что нужное приведение выполняется само, вот исправленные варианты:
Predicate Greater = x > _1;
Predicate Equals = x == _1;
Если бы у нас замыкания были без параметров, то мне потребовалось бы хотя бы раз написать var(x) в выражении, чтобы дать возможность компилятору "поймать" нужную перезагрузку операторов для функциональных объектов библиотеки lambda, остальные замыкания выполнялись бы уже "сами" в процессе приведения типов.
V>> (есть еще константная версия, для избежания копирования по значению больших объектов, если мы не собираемся их модифицировать). Созданное замыкание — суть функциональный объект, который затем используется в функциях высшего порядка. Примеры эти тривиальны, иначе можно было бы показать такое же элегантное порождение curring-функций через bind.
VD> Я плякаль. Тут уже до тебя приводили примеры этой "элегантности". Поверь от нее тошнит.
Без ума креститься — только нагрешить. Локальные замыкания — это способ повышения выразительности и читабельности и ничего более. Если в результате у кого выразительность и читабельность понижается — то в пень такие лямбды, с глаз долой и не позоримся. У С++ есть нормальная возможность создать функциональный объект. Да, пусть мы потрам ровно 3 лишние строчки "синтаксического оверхеда" на вот это, например:
struct MyLambda {
VeryComplexClass& param;
MyLambda(VeryComplexClass& p) : param(p) {}
...
}
Зато напишем вместо "..." вполне читабельный некий код:
bool operator()(T1 p1, T2 p2, T3 p3) {
/* тут нетривиальный и длинный код, работающий с очень сложным классом */
}
Т.е. категорически не приветствую, когда из кода месят неперевариваемую лапшу... И это независимо от языка, разумеется. Я и на C# перлов нечитаемых уже видел достаточно и даже в форумах приводил. Дело, как всегда, скорее в людях, догмах/модах и ожидающих своей очереди несъеденных собаках.
V>>Но это все синтаксис. С точки зрения run-time, в C# динамически создаются экземпляры объектов/замыканий, так что подобный код я бы не рекомендовал
VD>Твои рекомендации к щастью ничего не стоят.
То-то вы там полные счастья, в своем Early парсере символы и состояния по номерам расписывали... Каменный век, однака...
Это все от того, что ты-то в отличие от многих из твоей аудитории, прекрасно разбираешься в границах этого "счастья", которое отнюдь не безгранично.
(кстати, у меня есть кое-какие работающие наметки для декларативного строго-типизированного описания лексеров и парсеров, захотелось мне взять ваш "волшебный" парсер, сижу вот кумекаю, чтобы он AST строил и моими символами-объектами оперировал, а не номерами. Вот пример описания грамматики лексического анализатора:
static SelectRule DecDigit = "0123456789";
[Token(typeof(int))]
static Rule DecNumber = DecDigit + ~(DecDigit);
[Token(typeof(double))]
static Rule FloatNumber = ~DecDigit + '.' + DecNumber | DecNumber + '.' + ~DecDigit;
пока не придумал ничего лучше, как взять символ '~' в качестве оператора "{ expr }", т.е. повтор expr от 0 и более раз, еще вот думаю, чтобы такое задействовать в качестве оператора "[ expr ]", т.е. повтор expr 0 или 1 раз.
Символ '!' уже задействован, и означает примерно свой похожий смысл — любой терминал, кроме заданного/заданных.
)
VD>Ты хоть знаешь сколько стоит создать один мелкий объект?
Прекрасно знаю. Особенно хорошо знаю, что значит создавать их постоянно тоннами в цикле некоего частоиспользуемого алгоритма. Я даже знаю, сколько стоит СОЗДАНИЕ одного делегата (не путать со стоимостью его вызова). Потому и предостерегаю от подобных ситуаций. Разумеется, на одиночных вызовах никого не интересуют рекомендации, применимые к циклам.
VD>Что касается невозможности оптимизаций, то это не правда. Подобные вещи потенциально полностью устраняются совершенно универсальными алгоритмами.
На данном этапе развития дотнета — никак. Посмотри код MSIL, который генерится в случае этих замыканий. Там создаются вполне независимые классы, и с какой это стати JIT будет нивелировать динамическое создание объекта и делегата на метод? До такого он пока не докатился. Хотя вполне признаю, что может быть к 4-й или 5-й версии докатится. Но это будет еще очень не скоро.
V>>Напротив, в С++ не создаеются динамические объекты, не используется ни полиморфизм или коссвенная адресация, а значит все инлайнится в момент компиляции таким образом,
VD>Хм. Нэемерле тоже не пользуется для таких случаев полиморфизмом. А инлайнится или нет — это уже качество оптимизатора кокретного компилятора. К языку это отношение не имеет.
Стоп, тут ты не прав. Пока в дотнете мы жестко привязаны к делегатам, как к функциональным объектам, ты не можешь это ограничение "перешагнуть". Я уже демонстрировал технику, по которой использовал общий интерфейс, типа такого:
public interface IFunction<RetT> {
RetT Invoke();
}
public interface IFunction<RetT, ArgT> {
RetT Invoke(ArgT arg);
}
// и т.д.
Так вот, у нас есть возможность не только наследовать свои классы и структуры от этого интерфейса, но так же я сгенерировал делегаты до 16-ти параметров. И в чем самый непонятный мне прикол, так это в том, что я не могу такой делегат описать на C#, зато могу спокойно создать его через Emit. Да, именно, вот такой делегат:
public sealed class Function<RetT> : MulticastDelegate, IFunction<RetT> {
public RetT Invoke();
}
И все это означает, что я теперь смогу создавать generic-методы и типы, в которые могу подавать как делегаты, так и функциональные объекты (!!!), кои могут быть структурами в т.ч., а значит полностью инлайниться JIT-ом.
Как говорится WOW!
Только тем более не понятны 2 вещи:
— почему этого нельзя добиться штатными ср-вами, а только через злостный Emit?
— почему я пишу Invoke, вместо op_invoke???
VD>Зато С++-ный код точно не полноценный. Такое замыкание ни передать никуда нельзя, ни продлить время жизни за пределы функции.
Как раз лямбду в С++ используют в основном не так, а вот так:
for_each(vec.begin(), vec.end(), _1+=10);
Т.е. передают как аргумент циклов... Или других лямбд...
Передавать можно что угодно и куда угодно. Посмотри в моем примере класс typedef Predicate. Фокус в том, что наши лямбды — они совсем не такого типа. Там происходит хитрое приведение результата к моему типу. И вот этот результат ты можешь подать хоть куда угодно. Хотя, так никто не делает, это же не C#, у нас нет никаких ограничений. Функциональный объект обычно является аргументом шаблона и принимает любой параметр, т.е. смысла в таком приведении нет.
VD>В общем, оправдания убогости стремлением к скорости явно не прокатывает. Скорости ведь хватает.
Можно без комментариев? Я вот занят экспериментами с вещами, для которых мне скорости моих 3.2GHz начинает нехватать. И я уже перешел с C# обратно на С++. (Начинал макетировать на C#).
VD>Куда больше можно "выжать" используя более подходящие алгоритмы.
Бывают такие области применений, где данных много, а алгоритмы несложные и уже "выжаты" до предела. Только данных от этого меньше не становится...
VD>А вот выразительности и простоты явно не хватает. Их никогда хватать не будет.
Это да, это есть. И всегда будет, я надеюсь... Как говориться — нет предела совершенству.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Здравствуйте, Cyberax, Вы писали:
C>Тогда это уже не обратная, а прямая ситуация.
хоть горшоком назови, только в печь не ставь
Если отказаться от использования некоторых частей .NET, которые
пока не в полном объеме реализованы в Моно — никаких проблем с переносимостью не должно быть. Даже если эти части используются, локализовать их и заменить — вполне тривиальная задача.
>> C>С++ поможет новый Стандарт и новые компиляторы.
>> новый стандарт только всё ухудшит. Но это уже тема для отдельного флейма
C>Конечно ухудшит Тот же C# будет по сравнению с ним выглядеть
C>маломощным карликом.
Скорее, новый C++ будет выглядеть многоножкой, которая запуталась в своих многочисленных ногах. Растущих не только с нижней стороны тела, но также с боков и даже сверху — из соображений совместимости с предыдущими версиями
Не говоря уже о том, что к тому моменту, когда опубликуют наконец новый стандарт С++ (и, что более важно, соответствующие ему компиляторы
) — M$ уже выпустит C# за версией 4.0. А независимые разработчики языков для .NET тоже не дремлют.
Вот ты сам как думаешь, когда появятся полноценные рабочие компиляторы для нового стандарта?
... << RSDN@Home 1.1.4 stable rev. 510>>