2010-06-10 3 views
20

Я пытаюсь выяснить, есть ли полезное значение для использования лямбда-выражений в C++, а именно «этот код компилируется/работает быстрее/медленнее, потому что мы используем лямбда-выражения» или это просто аккуратный приступ развития открыть для злоупотребления плохие кодеры, пытаясь выглядеть круто?Какова мотивация выражения лямбда-выражения C++ 11?

Я понимаю, что этот вопрос может показаться субъективным, но я был бы очень признателен за мнение сообщества по этому вопросу.

+17

Я возьму "исключенного третьего" за $ 500 Алекс. –

+3

Итак, единственная причина, по которой вы можете думать о языковой функции, - «она идет быстрее» или «это как быстрая полоса на рисовой ракете»?Каков новый латинский термин для «доказательства из-за отсутствия воображения»? –

+0

Это было поучительно. Спасибо всем за ответы. – LoudNPossiblyWrong

ответ

41

Я не думаю, что это почти так же, как вычислительная производительность, как увеличение выразительности власти.

+9

Язык программирования без лямбда - это как день без солнечного света. – Brian

+11

@Brian, вы живете в Сиэтле, не должно быть большого изменения;) – JaredPar

+4

@Brian: тогда C++ с лямбдой похожа на жизнь в подвале, но с телевизором, который показывает солнце, сияющее над сельским пейзажем - вы видите свет, но он просто не чувствует то же самое. –

8

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

Можно утверждать, что это помогает в удобочитаемости кода (если ваш функтор объявлен встроенным, где он используется).

9

IMO, самая важная вещь в лямбда-это держит связанный код близко друг к другу. Если у вас есть этот код:

std::for_each(begin, end, unknown_function); 

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

+1

В идеале «unknown_function» будет назван достаточно хорошо, чтобы дать вам хорошее представление о том, что он делает. –

+5

@Fred: Если у вас есть определение лямбда прямо там, в месте использования, читатель избавляется от желания задаться вопросом, хорошо ли названа функция или нет, и писателю избавляется от необходимости выбирать доброе имя для тривиальной операции, которая будет использоваться только в одном месте. –

+0

@ Jim Lewis: Согласен. Я не думаю, что лямбды - это плохо. Но в то же время я не думаю, что всегда сложно переместить тело цикла в функтор. Как всегда, используйте то, что лучше для вашей ситуации. –

43

Выгода, что самое главное в написании компьютерных программ: легче понять код. Я не знаю никаких соображений производительности.

C++ позволяет, в определенной степени, делать Функциональное программирование. Рассмотрим это:

std::for_each(begin, end, doer); 

Проблема с этим состоит в том, что функция (объект) doer

  1. указывает , что сделано в петле
  2. еще несколько скрывает, что на самом деле сделали (у вас есть для поиска функции объекта operator())
  3. должно быть , определенное в Различные масштабы чем std::for_each вызов
  4. содержит определенное количество шаблонного кода
  5. часто кодвыбрасывать, которая не используется ни для чего, кроме этого одного цикла построить

Лямбда значительно улучшить все это (и, возможно, еще немного забыл).

2

Ну, сравните:

int main() { 
    std::vector<int> x = {2, 3, 5, 7, 11, 13, 17, 19}; 

    int center = 10; 
    std::sort(x.begin(), x.end(), [=](int x, int y) { 
     return abs(x - center) < abs(y - center); 
    }); 

    std::for_each(x.begin(), x.end(), [](int v) { 
     printf("%d\n", v); 
    }); 

    return 0; 
} 

с этим:

// why enforce this to be defined nonlocally? 
void printer(int v) { 
    printf("%d\n", v); 
} 

int main() { 
    std::vector<int> x = {2, 3, 5, 7, 11, 13, 17, 19}; 

    // why enforce we to define a whole struct just need to maintain a state? 
    struct { 
     int center; 
     bool operator()(int x, int y) const { 
      return abs(x - center) < abs(y - center); 
     } 
    } comp = {10}; 

    std::sort(x.begin(), x.end(), comp); 

    std::for_each(x.begin(), x.end(), printer); 

    return 0; 
} 
+4

Я не думаю, что второй даже законный. Функциональные локальные типы AFAIK не должны использоваться в качестве аргументов шаблона. Или C++ 2003 исправить эту неловкую проблему? – sbi

+1

@sbi: Шаблоны? Какие шаблоны? Во всяком случае, вторая часть все еще скомпилирована в режиме C++ 0x (обратите внимание на использование списка инициализаторов), поскольку OP просто запрашивает мотивацию лямбда-выражений. – kennytm

+4

'std :: sort()' и 'std :: for_each()' - это упомянутые шаблоны @sbi. Однако C++ 0x лишает это ограничение. –

13

Там нет выигрыш в производительности по себе, но потребность в лямбда пришел как следствие широкого принятия СТЛ и его дизайнерские идеи.

В частности, алгоритмы STL часто используют функторы. Без лямбда эти функторы должны быть предварительно объявлены для использования. Lambdas позволяет иметь «анонимных», работающих на месте функторов.

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

Я, например, использую STL много, но без C++ 0x Я использую гораздо больше для циклов(), чем для алгоритма for_each() и его кузенов. Это потому, что если бы я использовал for_each() вместо этого, мне нужно было бы получить код внутри цикла и объявить для него функтором. Кроме того, все локальные переменные перед циклом не будут доступны, поэтому мне нужно будет написать дополнительный код, чтобы передать их в качестве параметров конструктору-функтора или эквивалент другой вещи. Как следствие, я склонен не использовать for_each(), если нет сильной мотивации, иначе код будет длиннее и труднее читать.

Плохо, потому что хорошо известно, что использование for_each() и подобных алгоритмов дает гораздо больше возможностей компилятору & для оптимизации, включая автоматический параллелизм. Таким образом, косвенно, лямбда будет способствовать более эффективному коду.

+2

Лучшее объяснение «C++ лямбда в двух словах», которое я нашел до сих пор! – hubeir

5

Хотя я думаю, что другие части C++ 0x являются more important, лямбды больше, чем просто «синтаксический сахар» для C++ 98 функциональных объектов стиля, потому что они могут захватить контексты, и они делают это по имени и то они могут взять эти контексты в другом месте и выполнить. Это нечто новое, а не то, что «компилируется быстрее/медленнее».

#include <iostream> 
#include <vector> 
#include <functional> 
void something_else(std::function<void()> f) 
{ 
     f(); // A closure! I wonder if we can write in CPS now... 
} 
int main() 
{ 
     std::vector<int> v(10,0); 
     std::function<void()> f = [&](){ std::cout << v.size() << std::endl; }; 

     something_else(f); 
} 
+0

Нет, это не ново. Lambdas захватывает по названию в точке определения. Точно как функции-объекты с конструктором. Поскольку лямбда * является синтаксическим сахаром для функционального объекта и может быть реализована как пересылка источника в источник (см. Http://blogs.msdn.com/b/vcblog/archive/2008/10/28/lambdas- auto-and-static-assert-c-0x-features-in-vc10-part-1.aspx для примеров такой перезаписи). Что ж, в недавнем проекте это невозможно, по другой причине: безгражданная лямбда должна теперь распадаться на функцию указателя, так что теперь она немного отличается от обычного объекта функции, но не для части захвата. –

+0

@Thomas Petit: Объект функции с конструктором не имеет понятия области видимости в точке определения. Чтобы автоматически переписать [&] лямбда в качестве функтора, нужно было бы изучить его полный источник и изучить полный источник программы до определения лямбда для разрешения типов, связанных с именами, используемыми в лямбда. Не синтаксическое преобразование. – Cubbi

+0

Не синтаксическое преобразование, а более чистый синтаксис для чего-то, что можно было сделать ранее. Отсюда синтаксический сахар. – jalf

2

«аккуратным развития задирать открытой для злоупотреблений со стороны бедных кодеров пытаются выглядеть круто?» ... все, что вы называете это, это делает код много более читаемым и ремонтопригодны. Это не увеличивает производительность.

Чаще всего программист выполняет итерацию по целому ряду элементов (поиск элемента, аккумулирующих элементов, элементов сортировки и т. Д.). Используя функциональный стиль, вы сразу же видите, что намеревается сделать программист, в отличие от использования для циклов, где все «выглядит» одинаково.

Сравнение алгоритмов + лямбда:

iterator longest_tree = std::max_element(forest.begin(), forest.end(), [height]{arg0.height>arg1.height}); 
iterator first_leaf_tree = std::find_if(forest.begin(), forest.end(), []{is_leaf(arg0)}); 
std::transform(forest.begin(), forest.end(), firewood.begin(), []{arg0.trans(...)); 
std::for_each(forest.begin(), forest.end(), {arg0.make_plywood()}); 

с OLDSCHOOL для петель;

Forest::iterator longest_tree = it.begin(); 
for (Forest::const_iterator it = forest.begin(); it != forest.end(); ++it{ 
    if (*it.height() > *longest_tree.height()) { 
    longest_tree = it; 
    } 
} 

Forest::iterator leaf_tree = it.begin(); 
for (Forest::const_iterator it = forest.begin(); it != forest.end(); ++it{ 
    if (it->type() == LEAF_TREE) { 
    leaf_tree = it; 
    break; 
    } 
} 

for (Forest::const_iterator it = forest.begin(), jt = firewood.begin(); 
    it != forest.end(); 
    it++, jt++) { 
      *jt = boost::transformtowood(*it); 
    } 

for (Forest::const_iterator it = forest.begin(); it != forest.end(); ++it{ 
    std::makeplywood(*it); 
} 

(я знаю, что это pieace кода содержит синтаксические ошибки.)

Смежные вопросы