> В итоге получится, что требование «только стандартная библиотека, ну максимум буст» является неудачной попыткой получить фору
Да нет, просто опасался получить решение в одну строку вида superlib::do_some_magic(), где функция берётся из какой-то хитрой никому не известной специализированной библиотеки. А Qt ок, почему нет.
> Что вам не понятно в этом решении? (кстати, извиняюсь за опечатку: там не хватает открывающей скобки "(" после «fromLocal8Bit»)
Мне всё понятно, даже синтаксические ошибки могу в уме исправлять :) Но время на восприятие плюсового намного выше, чем у аналогичного C#-кода. И дело не только в приведённых кратких фрагментах, я говорю на основании опыта промышленного использования C++ (тогда ещё без 0x, но уже с блэкджеком) и C#. В последнем благодаря Linq обработка коллекций любых типов выглядит одинаково и идиоматично, легко узнаваема.
А ваш код вполне нормален, где-то недалеко от максимума выразительности, которую можно выжать из языка. Правда, я хотел бы увидеть хоть какие-то ФВП общего назначения, получающие лямбды (предикаты фильтрации, сравнения, селектор, etc), но сходу предложенный пример, как оказалось, решился частными библиотечными функциями для работы со строками. Ну и бог с ним.
> При наличии единожды установленного Qt оверхед 0 мегабайт. Я вам даже больше скажу: при наличии единожды установленного $anything оверхед будет 0 мегабайт :)
Так а в чём тогда цимес уделять столько внимания размерам дистрибутивов рантаймов?
> Просто предкомпилированные пользовательские библиотеки весят 55 мегабайт в распакованном виде (и 18 мегабайт в виде тупого ZIP-а, сколько будет весить инсталлятор прикиньте сами, я не в курсе, какой там алгоритм сжатия используется)
Ещё можно приплюсовать размер плюсового рантайма (vcredist*.exe, Visual C++ 2010 Runtime Redistributable Package), его ж у пользователя тоже может не быть. И в итоге что получится? А хрен его знает, мне абсолютно лень считать, но да — мегабайтов 30 выиграет у .NET'а, HUGE SUCCESS!
> В итоге первый пример легко и непринуждённо решается с помощью Qt ничуть не сложнее, чем на шарпе
Легко и непринуждённо, ничуть не сложнее? :) Хоть в одном из предложенных C++-вариантов навскидку (человеком, впервые читающим код) вычисление декомпозируется на стандартные ФВП: фильтрация—сортировка—проекция—итерация… (аггрегация, свёртка, zip, unfold, etc)? Подобные Linq-запросы на C# пишутся секунд за 30 без циклов, условных операторов и прочей императивщины с изменением состояний. C++ по скорости написания кода и скорости его восприятия даже близко не стоит.
> Собственно, если вы переформулируете своё заявление как «есть вещи, над которыми мне приходится корпеть в плюсах», то все вопросы моментально отпадут.
Это не ко мне, изначально не я формулировал это заявление. Я просто привёл пару простых примеров.
> Это как это мы так предполагаем, если мы только что сами ясно написали, что данная лямбда осуществляет захват по значению и оригинал менять не может?
> Вот только если вы захотите распространять свою программу на Qt, можете вместе с ней кинуть пару нужных dll и всё.
Тут тройку dll, там пару тех же dll…
> А на сишлаке хочешь — не хочешь, а весь .NET Framework, будь добр, поставь!
В современных операционках от Microsoft фреймворк уже предустановлен, т.е. идёт как часть системы. В операционках 10-летней давности фреймворка может и не быть, но, как правило, у клиентов он всё равно уже установлен с другим софтом типа GTA IV или AutoCAD.
> Тогда зачем это спрашивать у меня, нужно самому писать и говорить так и так, а не троллить
Я никого не тянул за язык, просто deMonteCristo спросил «пример… вещей, над которыми надо корпеть в плюсах и совсем не надо в шарпе». Я просто предоставил два примера, с меня и взятки гладки. Какой троллинг, прости господи?
> В C# нет operator() и ваше решение обходит это
Это как раз наоборот, в C++ нет первоклассных функций и не решена upwards funarg problem, так что обходят кривым способом с помощью operator ().
> Конечно не знали до текущего момента.
Это грязные инсинуации, благородному дону не пристало. Не только знал, но ещё до публикации задачи написал proof-of-concept реализацию на C# и вариант на C++0x, воспроизводящий UB.
Исходный псевдокод:
Foo : () → (int → int) =
() ↦ {
valueToBeClosed : int = 3;
assignNewValue : int → int =
value ↦ {
result : int = valueToBeClosed;
valueToBeClosed = value;
return result;
}
return assignNewValue;
}
Подстрочный перевод на C#:
static Func<int, int> Foo()
{
int valueToBeClosed = 3;
Func<int, int> assignNewValue = value =>
{
var result = valueToBeClosed;
valueToBeClosed = value;
return result;
};
return assignNewValue;
}
Аналогичный вариант на C++, содержащий скрытую ошибку:
std::function<int (int)> Foo()
{
int valueToBeClosed = 3;
auto const assignNewValue = [&] (int value) -> int
{
auto const result = valueToBeClosed;
valueToBeClosed = value;
return result;
};
return assignNewValue;
}
>>> Такие замыкания в паре с мутабельной лямбдой используют в Scheme для имитации объектов.
>> А схемщики считают, что это объекты в ООП служат для имитации замыканий.
> Схемщиков тута нету.
Какая разница, есть ли тута схемщики. Я имел в виду слова (емнип) Нормана Адамса «Objects are a poor man's closures», которые считал достаточно известными.
> Что ожидалось, понятно: «Так и быть, можно Boost, Qt и Loki.», а было представлено простое решение, минимально отличающееся от псевдокода, но сохранить лицо надо было.
Какое «сохранить лицо», «ожидалось» и «программист на C#, который С++ не видел»? Я прекрасно знаю, как подобный код пишется на C++. Упомянув библиотеки, я всего лишь имел в виду, что, раз уж в C++ нет первоклассных функций, допускается использовать библиотечные функторы из boost, Loki, std::tr1 или вот из нового std.
> В момент, когда такая задача возникла
Вы не улавливаете контекста. Я имел в виду, что если у вас уже есть функция с int valueToBeClosed = 3;, то вы не можете просто добавить замыкание, не меняя его контекст. Вы должны не забыть превратить окружение (valueToBeClosed) в ссылку (в общем смысле, не C++) на нестековое значение и передать её по значению.
> Это решение какой-то задачи, не самое лучше.
Вполне идиоматичное решение. Подобным состоянием может быть датчик случайных чисел, или какой другой Enumerator. От пользователя внутренний итератор скрыт, он получает просто генератор (возможно, бесконечной) последовательности в виде функции.
> В C# зло так делать, если нужно вернуть объект с состоянием — возвращай объект с состоянием, а не делай грязные лямбды на пустом месте.
Ерунда. Зачем мне передавать объект с состоянием, если я хочу это состояние скрыть от пользователя? Получится объект с одной функцией и недоступным пользователю состоянием. Так почему бы сразу не вернуть одну лишь функцию, зачем плодить сущности?
> Такие замыкания в паре с мутабельной лямбдой используют в Scheme для имитации объектов.
А схемщики считают, что это объекты в ООП служат для имитации замыканий.
> ideone.com/qnbrm
Вот это и называется «вещами, над которыми надо корпеть в плюсах и совсем не надо в шарпе». Ты не можешь просто замкнуть переменную по ссылке. В момент, когда такая задача возникла, тебе надо изменить окружающий код, заморачиваться с передачей по значению ([=]) ссылки (указателя shared_ptr). А без этого программа скомпилируется без всяких предупреждений, и, самое страшное, не исключено что при определённых условиях UB будет проявляться так, что создаст видимость правильной работы.
> И да, использованная здесь QtCore вполне укладывается в «без фанатизма» и «по минимуму» — она весит чуть более 2.5 мегабайт.
А сколько будет весить рантайм для вашего решения? ;)
Какой-то странный подход, сравнивать среду выполнения и библиотеку.
1) Дополнительных библиотек в моём коде не требуется, так что при наличии единожды установленного .NET 4.0 оверхед 0 мегабайт.
2) Если учитывать вес рантайма, то полный дистрибутив dotNetFx40_Full_x86_x64.exe весит 48.1 Мб, достаточный дистрибутив dotNetFx40_Client_x86_x64.exe — 41.0 Мб. Если верить офсайту Qt, то даже без SDK (который за гигабайт), просто предкомпилированные пользовательские библиотеки весят от 200 Мб.
Это неправильный ответ. Для usage-ошибокне надо использовать try...catch.
> более половины пишут что всегда анализируют делитель
И правильно делают. Это не от незнания про исключения, а от умения ими не пользоваться в неподходящих местах.
> Я проводя собеседования на вакансию программера С++ сильно удивился, что про исключения знает менее чем каждый второй соискатель.
Вы и сами бы не прошли собеседования на джуниора.
> предполагается ответ с try...catch
Правильный ответ (с учётом рассуждений про баррикады) может быть примерно таким (на C#).
Вызываемый код: private double DivUnsafe(double a, double b)
{
Debug.Assert(!/*корректное сравнение b с 0.0*/);
return a / b;
}
public double Div(double a, double b)
{
// Не допускается выбрасывание из библиотеки ZeroDivisionException,
// NullReferenceException и тому подобных исключений.
if (/*корректное сравнение b с 0.0*/)
{
throw new ArgumentException(...);
}
return DivUnsafe(a, b);
}
В библиотечном коде (по «нашу сторону» баррикад) всегда вызываем DivUnsafe, контекст вызова всегда можем контролировать. Пользовательскому коду (на корректность которого мы не можем прямо влиять) предоставляется Div.
Неправильный вызывающий код: try
{
c = Div(a, b);
}
cactch (ArgumentException ex)
{
// Обработка ошибки, например:
throw new MyBusinessLogicException(..., a, b, ex);
}
Правильный вызывающий код: if (/*корректное сравнение b с 0.0*/)
{
// Обработка ошибки, например:
throw new MyBusinessLogicException(..., a, b);
}
с = Div(a, b);
Да нет, просто опасался получить решение в одну строку вида
superlib::do_some_magic(), где функция берётся из какой-то хитрой никому не известной специализированной библиотеки. А Qt ок, почему нет.Мне всё понятно, даже синтаксические ошибки могу в уме исправлять :) Но время на восприятие плюсового намного выше, чем у аналогичного C#-кода. И дело не только в приведённых кратких фрагментах, я говорю на основании опыта промышленного использования C++ (тогда ещё без 0x, но уже с блэкджеком) и C#. В последнем благодаря Linq обработка коллекций любых типов выглядит одинаково и идиоматично, легко узнаваема.
А ваш код вполне нормален, где-то недалеко от максимума выразительности, которую можно выжать из языка. Правда, я хотел бы увидеть хоть какие-то ФВП общего назначения, получающие лямбды (предикаты фильтрации, сравнения, селектор, etc), но сходу предложенный пример, как оказалось, решился частными библиотечными функциями для работы со строками. Ну и бог с ним.
Так а в чём тогда цимес уделять столько внимания размерам дистрибутивов рантаймов?
> Просто предкомпилированные пользовательские библиотеки весят 55 мегабайт в распакованном виде (и 18 мегабайт в виде тупого ZIP-а, сколько будет весить инсталлятор прикиньте сами, я не в курсе, какой там алгоритм сжатия используется)
Ещё можно приплюсовать размер плюсового рантайма (vcredist*.exe, Visual C++ 2010 Runtime Redistributable Package), его ж у пользователя тоже может не быть. И в итоге что получится? А хрен его знает, мне абсолютно лень считать, но да — мегабайтов 30 выиграет у .NET'а, HUGE SUCCESS!
Легко и непринуждённо, ничуть не сложнее? :) Хоть в одном из предложенных C++-вариантов навскидку (человеком, впервые читающим код) вычисление декомпозируется на стандартные ФВП: фильтрация—сортировка—проекция—итерация… (аггрегация, свёртка, zip, unfold, etc)? Подобные Linq-запросы на C# пишутся секунд за 30 без циклов, условных операторов и прочей императивщины с изменением состояний. C++ по скорости написания кода и скорости его восприятия даже близко не стоит.
> второй тоже недалеко от этого ушёл.
Путём решения фунарг-проблемы на ручной тяге?
> Собственно, если вы переформулируете своё заявление как «есть вещи, над которыми мне приходится корпеть в плюсах», то все вопросы моментально отпадут.
Это не ко мне, изначально не я формулировал это заявление. Я просто привёл пару простых примеров.
Ну, а надо по ссылке!
А если мы вызываем assignNewValue(31) перед выходом из Foo() и предполагаем, что вызов таки изменит нашу локальную переменную valueToBeClosed?
Тут тройку dll, там пару тех же dll…
> А на сишлаке хочешь — не хочешь, а весь .NET Framework, будь добр, поставь!
В современных операционках от Microsoft фреймворк уже предустановлен, т.е. идёт как часть системы. В операционках 10-летней давности фреймворка может и не быть, но, как правило, у клиентов он всё равно уже установлен с другим софтом типа GTA IV или AutoCAD.
Я никого не тянул за язык, просто deMonteCristo спросил «пример… вещей, над которыми надо корпеть в плюсах и совсем не надо в шарпе». Я просто предоставил два примера, с меня и взятки гладки. Какой троллинг, прости господи?
> В C# нет operator() и ваше решение обходит это
Это как раз наоборот, в C++ нет первоклассных функций и не решена upwards funarg problem, так что обходят кривым способом с помощью
operator ().> Конечно не знали до текущего момента.
Это грязные инсинуации, благородному дону не пристало. Не только знал, но ещё до публикации задачи написал proof-of-concept реализацию на C# и вариант на C++0x, воспроизводящий UB.
Исходный псевдокод:
Foo : () → (int → int) = () ↦ { valueToBeClosed : int = 3; assignNewValue : int → int = value ↦ { result : int = valueToBeClosed; valueToBeClosed = value; return result; } return assignNewValue; }Подстрочный перевод на C#:
static Func<int, int> Foo() { int valueToBeClosed = 3; Func<int, int> assignNewValue = value => { var result = valueToBeClosed; valueToBeClosed = value; return result; }; return assignNewValue; }Аналогичный вариант на C++, содержащий скрытую ошибку:
std::function<int (int)> Foo() { int valueToBeClosed = 3; auto const assignNewValue = [&] (int value) -> int { auto const result = valueToBeClosed; valueToBeClosed = value; return result; }; return assignNewValue; }>> А схемщики считают, что это объекты в ООП служат для имитации замыканий.
> Схемщиков тута нету.
Какая разница, есть ли тута схемщики. Я имел в виду слова (емнип) Нормана Адамса «Objects are a poor man's closures», которые считал достаточно известными.
Какое «сохранить лицо», «ожидалось» и «программист на C#, который С++ не видел»? Я прекрасно знаю, как подобный код пишется на C++. Упомянув библиотеки, я всего лишь имел в виду, что, раз уж в C++ нет первоклассных функций, допускается использовать библиотечные функторы из boost, Loki, std::tr1 или вот из нового std.
> В момент, когда такая задача возникла
Вы не улавливаете контекста. Я имел в виду, что если у вас уже есть функция с
int valueToBeClosed = 3;, то вы не можете просто добавить замыкание, не меняя его контекст. Вы должны не забыть превратить окружение (valueToBeClosed) в ссылку (в общем смысле, не C++) на нестековое значение и передать её по значению.> Это решение какой-то задачи, не самое лучше.
Вполне идиоматичное решение. Подобным состоянием может быть датчик случайных чисел, или какой другой Enumerator. От пользователя внутренний итератор скрыт, он получает просто генератор (возможно, бесконечной) последовательности в виде функции.
> В C# зло так делать, если нужно вернуть объект с состоянием — возвращай объект с состоянием, а не делай грязные лямбды на пустом месте.
Ерунда. Зачем мне передавать объект с состоянием, если я хочу это состояние скрыть от пользователя? Получится объект с одной функцией и недоступным пользователю состоянием. Так почему бы сразу не вернуть одну лишь функцию, зачем плодить сущности?
А схемщики считают, что это объекты в ООП служат для имитации замыканий.
> ideone.com/qnbrm
Вот это и называется «вещами, над которыми надо корпеть в плюсах и совсем не надо в шарпе». Ты не можешь просто замкнуть переменную по ссылке. В момент, когда такая задача возникла, тебе надо изменить окружающий код, заморачиваться с передачей по значению (
[=]) ссылки (указателяshared_ptr). А без этого программа скомпилируется без всяких предупреждений, и, самое страшное, не исключено что при определённых условиях UB будет проявляться так, что создаст видимость правильной работы.А сколько будет весить рантайм для вашего решения? ;)
Какой-то странный подход, сравнивать среду выполнения и библиотеку.
1) Дополнительных библиотек в моём коде не требуется, так что при наличии единожды установленного .NET 4.0 оверхед 0 мегабайт.
2) Если учитывать вес рантайма, то полный дистрибутив dotNetFx40_Full_x86_x64.exe весит 48.1 Мб, достаточный дистрибутив dotNetFx40_Client_x86_x64.exe — 41.0 Мб. Если верить офсайту Qt, то даже без SDK (который за гигабайт), просто предкомпилированные пользовательские библиотеки весят от 200 Мб.
Требуется что-то вроде такого решения на псевдокоде:
Foo : () → (int → int) = () ↦ { valueToBeClosed : int = 3; assignNewValue : int → int = value ↦ { result : int = valueToBeClosed; valueToBeClosed = value; return result; } return assignNewValue; }Использование:
> Дык о чём и речь :)
Можно задачу без библиотек. Например: вернуть из первой функции другую функцию, замыкающую по ссылке локальную переменную первой функции.
File.ReadAllLines("test.txt") .Where(line => line.Split(' ').Length > 3) .OrderBy(s => s) .Run(Console.WriteLine);Давайте.
> Я уже указывал в комментариях, что не нужно отказываться от мощи чудесный фреймворков и библиотек.
Ок, только без фанатизма; скажем, можно Boost. Но желательно также приложить вариант, ограничивающийся только стандартной библиотекой.
> Подобного говна можно найти про любой язык. :)
Но только про C++ его так много.
«Вывести на экран все строки текстового файла, в которых более трех слов, отсортированные по алфавиту.»
> вещей, над которыми надо корпеть в плюсах...
The Dark Side of C++
C++ FQA Lite
И вообще: «If you’re familiar with C++, you’ll notice that the special syntax C# requires for defining a Finalize method looks just like the syntax you’d use to define a C++ destructor. In fact, the C# Programming Language Specification calls this method a destructor. However, a Finalize method doesn’t work like an unmanaged C++ destructor at all, and this has caused a great deal of confusion for developers migrating from one language to another.
The problem is that developers mistakenly believe that using the C# destructor syntax means that the type’s objects will be deterministically destructed, just as they would be in C++. However, the CLR doesn’t support deterministic destruction, preventing C# from providing this mechanism.» © Richter
Это неправильный ответ. Для usage-ошибок не надо использовать try...catch.
> более половины пишут что всегда анализируют делитель
И правильно делают. Это не от незнания про исключения, а от умения ими не пользоваться в неподходящих местах.
> Я проводя собеседования на вакансию программера С++ сильно удивился, что про исключения знает менее чем каждый второй соискатель.
Вы и сами бы не прошли собеседования на джуниора.
> предполагается ответ с try...catch
Правильный ответ (с учётом рассуждений про баррикады) может быть примерно таким (на C#).
Вызываемый код:
private double DivUnsafe(double a, double b)
{
Debug.Assert(!/*корректное сравнение b с 0.0*/);
return a / b;
}
public double Div(double a, double b)
{
// Не допускается выбрасывание из библиотеки ZeroDivisionException,
// NullReferenceException и тому подобных исключений.
if (/*корректное сравнение b с 0.0*/)
{
throw new ArgumentException(...);
}
return DivUnsafe(a, b);
}
В библиотечном коде (по «нашу сторону» баррикад) всегда вызываем DivUnsafe, контекст вызова всегда можем контролировать. Пользовательскому коду (на корректность которого мы не можем прямо влиять) предоставляется Div.
Неправильный вызывающий код:
try{
c = Div(a, b);
}
cactch (ArgumentException ex)
{
// Обработка ошибки, например:
throw new MyBusinessLogicException(..., a, b, ex);
}
Правильный вызывающий код:
if (/*корректное сравнение b с 0.0*/){
// Обработка ошибки, например:
throw new MyBusinessLogicException(..., a, b);
}
с = Div(a, b);