Ото ж лямбда, блям...

Тема в разделе "Флудилка", создана пользователем ИгорьК, 14 мар 2024.

  1. ИгорьК

    ИгорьК Гуру

    Таки вот великий и могучий язык имени Ардиуно и по фамилии Сиплюсплюс живет, развивается (полнеет) и обзаводится внебрачными(?) детьми 2011, 2014, 2017, 2020 годов рождения.
    Все прогрессивное дивмейкерство, узнав о таком блуде, желает увидеть оныхъ перспективных деток, дабы поиграть с ними в песочнице.
    Вопрос. Где-то есть сконцентрированное и вменяемое описание новшеств годное к "взял и применил"?

    Я о чем. Вот текст. Годный текст, для любителей гонять сферических коней. Но мне, как простому скотоводу, история лямбда в С++, что уже много-много лет есть в другом, любимом мной языке, не нужна вовсе. Хочется понятное описание синтаксиса. Тож касается и корутины, что, по слухам, объявилась в С++.

    Короче. Кто знает место для почитать и не убиться?
     
  2. serg3295

    serg3295 Гуру

    ИгорьК нравится это.
  3. Asper Daffy

    Asper Daffy Иксперд

    На мой взгляд, лучший популяризатор С++ - вот эта девочка. Она, кстати постоянный участник CppCon - и активный участник группы разработки стандарта. Не знаю, есть ли у неё про лямбды - поищите.

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

    Не знаю, что Вы имели в виду под:

    но лямбды имеют смысл, там где функции являются объектами первого класса, а в С++ это не так. Например, у Вас сеть функция сложения чисел-аргументов, типа такой:

    Код (C++):
    sum(a, b) { return a+b; }
    Что будет, если вызвать её с одним аргументом?

    Код (C++):
    sum(5)
    В С++ будет синтаксическая ошибка. А вот в языках, где лямбды "родные", а не "с боку припёка", результатом такого вызова будет функция одного аргумента, примерно такая:

    Код (C++):
    sum(b) { return 5+b; }
    Именно такую функцию вернёт этот вызов, её можно присвоить чему-нибудь, а потом вызвать с одним аргументом (она к нему пятёрку прибавит)

    Понятно, о чём это я?

    Что касается статьи на ravesti, которую Вам посоветовал @serg3295 - не ведитесь. Она не про лямбды, а про "анонимные функции". Автор не стал лезть в лямбды (или сам не понимает, что это такое). Запомните, главная фишка лямбд - замыкание. Использовать лямбды без замыкания - это как пихать везде слово class, но не использовать наследование и утверждать, что практикуешь ООП - точно такой же идиотизм. Поэтому, любая статья про лямбды без замыканий - это из серии "посмотрите, какой я у мамки умный, во какие слова знаю!"

    Ну, как-то так.

    А в современных С++ мне понравился синтаксический сахар про апострофы для разделения разрядов числа и про самогонные литералы - красиво, удобно :)
     
    Последнее редактирование: 14 мар 2024
    ИгорьК нравится это.
  4. DayGaik

    DayGaik Гик

    Что-то не в одном языке я такого отношения к параметрам не встречал.

    Чтобы получить производную функцию нужно написать что-то вроде
    Код (C++):
    sum5(b) { return sum(5, b) }
     
    ИгорьК нравится это.
  5. Asper Daffy

    Asper Daffy Иксперд

    Как говаривал известный датский принц:
     
    ИгорьК нравится это.
  6. ИгорьК

    ИгорьК Гуру

    Спасибо, товарищи Сергеи! (Интересно, во множественном числе имена пишутся с большой буквы или нет?)

    Ну это не пугайте так, все проще.

    Как начинающий ардуинщик по теме своего мнения не имею, рад любым объяснениям.

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

    Lua от NodeMCU, который, фактически, JavaScript.

    По ряду причин занялся Ардуино языком. Одна, но не главная, - ваша таблетница. Переписывать библиотеку для E-paper под Lua было лень, а на Ардуино - тяп-ляп и код готов.

    Привык к передаче функций как аргументов, вот и пытаюсь разобраться
     
  7. Asper Daffy

    Asper Daffy Иксперд

    Ну, в С++ для этого вовсе не нужны лямбды.

    Если не устраивает передача указателя на функцию (кстати, чем?), то можно воспользоваться т.н. "функторами".

    Функтор - это класс для которого определена операция вызова функции (если надо, могу небольшой примерчик накропать, но после обеда). Если создать экземпляр такого класса, то его можно использовать как функцию (синтаксически), но, при этом, можно передавать параметром и возвращать из функций как значение.

    Единственное, зачем нужно следить, это если у класса есть нетривиальные конструктор/деструктор, то такой экземпляр лучше по ссылке передавать, а не по значению, чтобы конструктор с деструктором лишний раз не вызывались.
     
    ИгорьК и KindMan нравится это.
  8. parovoZZ

    parovoZZ Гуру

    как это не одно и тоже? В JS или в Gо не видел ни разу, чтобы между ними делали различия.
     
  9. parovoZZ

    parovoZZ Гуру

    вот здесь согласен на 100500 процентов.
     
  10. Asper Daffy

    Asper Daffy Иксперд

    Конечно, нет.
     
  11. ИгорьК

    ИгорьК Гуру

    Спасибо! Будет здорово.
     
  12. parovoZZ

    parovoZZ Гуру

    т.е. лямбда и стрелочные функции не анонимны?
     
  13. Asper Daffy

    Asper Daffy Иксперд

    Вы не заметили, как перевернули всё с ног на голову? Разумеется, любая лямбда-функция анонимна, но далеко не любая анонимная функция является лямбда-функцией.

    А то у Вас как у Шолома Алейхема:
     
    ИгорьК и DetSimen нравится это.
  14. ИгорьК

    ИгорьК Гуру

    Спасибо. Важно. Для меня не было разницы, но, вследствие того что тема мало где раскрыта.
    P.S. Не обращал внимание на то, что в С++ функции не объекты первого класса. И на все следствия этого.
     
    Последнее редактирование: 18 мар 2024
  15. Asper Daffy

    Asper Daffy Иксперд

    Ну, я сделал простенький функтор. Если его вызывать как функцию, то он просто установит заранее заданный (в конструкторе) уровень на переданный параметром пин. И подготовил три примера его использования.
    1. Первый - прямой вызов экземпляров - просто как функций;
    2. второй пример - передачу экземпляров функтора в левую функцию и вызов уже оттуда;
    3. и третий пример - наоборот, возврат экземпляров функтора из левой функции и вызов того, что вернулось
    Все три примера реализуют блинк с частотой 5Гц на UNO-подобных

    Код (C++):
    //
    // Класс "крутой функтор"
    // В конструкторе нужно задать "значение" HIGH или LOW
    // При использовании экземпляра в качестве функции с параметром pin,
    //    устанавливает на pin уровень "значение"
    //
    class CoolFunctor {
       const uint8_t m_val;
    public:
       CoolFunctor(const  uint8_t val) : m_val(val) {}

       void operator()(const uint8_t pin = LED_BUILTIN) {
          digitalWrite(pin, m_val);
       }
    };

    //
    // Создаём два функтора
    //
    CoolFunctor fHigh(HIGH);
    CoolFunctor fLow(LOW);

    void setup(void) {
       Serial.begin(9600);
       Serial.println("Let's go!");
       pinMode(LED_BUILTIN, OUTPUT);
    }

    //////////////////////////////////////
    //
    // Прямое использование экземпляров объекта, как функций
    //
    void loop(void) {
       fHigh();
       delay(100);
       fLow();
       delay(100);
    }
     

    Код (C++):
    //
    // Класс "крутой функтор"
    // В конструкторе нужно задать "значение" HIGH или LOW
    // При использовании экземпляра в качестве функции с параметром pin,
    //    устанавливает на pin уровень "значение"
    //
    class CoolFunctor {
       const uint8_t m_val;
    public:
       CoolFunctor(const  uint8_t val) : m_val(val) {}

       void operator()(const uint8_t pin = LED_BUILTIN) {
          digitalWrite(pin, m_val);
       }
    };

    //
    // Создаём два функтора
    //
    CoolFunctor fHigh(HIGH);
    CoolFunctor fLow(LOW);

    void setup(void) {
       Serial.begin(9600);
       Serial.println("Let's go!");
       pinMode(LED_BUILTIN, OUTPUT);
    }

    //////////////////////////////////////
    //
    // Передача экземпляра в качестве параметра фукнции
    // и уже оттуда использование как функции
    //
    void doDR(CoolFunctor & f) {
       f();
    }
    void loop(void) {
       static bool currentLedState = LOW;
       doDR((currentLedState = ! currentLedState) ? fHigh : fLow);
       delay(100);
    }
     

    Код (C++):

    //
    // Класс "крутой функтор"
    // В конструкторе нужно задать "значение" HIGH или LOW
    // При использовании экземпляра в качестве функции с параметром pin,
    //    устанавливает на pin уровень "значение"
    //
    class CoolFunctor {
       const uint8_t m_val;
    public:
       CoolFunctor(const  uint8_t val) : m_val(val) {}

       void operator()(const uint8_t pin = LED_BUILTIN) {
          digitalWrite(pin, m_val);
       }
    };

    //
    // Создаём два функтора
    //
    CoolFunctor fHigh(HIGH);
    CoolFunctor fLow(LOW);

    void setup(void) {
       Serial.begin(9600);
       Serial.println("Let's go!");
       pinMode(LED_BUILTIN, OUTPUT);
    }

    //////////////////////////////////////
    //
    // Возвращение экземпляра как результат работы функции
    // и вызов этого возвращённого значения как функции
    //
    CoolFunctor & chooseF(void) {
       static bool currentLedState = LOW;
       return (currentLedState = ! currentLedState) ? fHigh : fLow;
    }

    void loop(void) {
       chooseF()();
       delay(100);
    }
     

    Обратите внимание на строку
    Код (C++):
    chooseF()();
    Первая пара скобок - это вызов функции chooseF. Она (chooseF) возвращает функтор (т.е. по сути - функцию), поэтому вторая пара скобок - это уже вызов этого возвращённого функтора. Кстати, в ней (во второй паре скобок) можно поставить номер пина (см. определение функтора, там есть такой параметр). Я не ставлю, т.к. есть умолчание LED_BUILTIN, но поставить можно, почему нет?
     
    Последнее редактирование: 18 мар 2024
    ZAZ-965, DetSimen, KindMan и ещё 1-му нравится это.
  16. ИгорьК

    ИгорьК Гуру

    Спасибо огромное! Нужно время, чтобы это осмыслить!
     
  17. Asper Daffy

    Asper Daffy Иксперд

    Смотрите внимательно в JS.

    Переменным f1 и f2 (обеим) присваиваются анонимные функции. Функции по сути одинаковые - печатают a и b в хромовскую консоль.

    Код (Javascript):

    <html>
    <script language="JavaScript">
    var a = 3.62, b = 4.12;

    function testKaka() {
        var a = 123;
        var b = 321;
        var f1 = new Function("console.log('f1: a=' + a + ' b=' + b);");
        var f2 = () => { console.log('f2: a=' + a + ' b=' + b); };
        f1();
        f2();
    }
    testKaka();
    </script>
    </html>
     
    Теперь запустите это в хроме и убедитесь, что результат в консоли будет вот такой:
    Код (Text):

    f1: a=3.62 b=4.12
    f2: a=123 b=321
     
    Что получается? f1 не увидела переменных a и b в контексте вызывающей функции testKaka и взяла их из глобального контекста. А вот функция f2 увидела и взяла из локального.

    В чём же разница? Вроде, обе функции - анонимные! А разница в том, что стрелочная функция - это лямбда-функция и для неё сработало полное замыкание. А вот функция, созданная через new - ни разу не лямбда-функция, хотя и анонимная, и для неё замыкание не сработало и не могло сработать.
     
    Последнее редактирование: 18 мар 2024
    ИгорьК нравится это.
  18. ИгорьК

    ИгорьК Гуру

    А если var поменять на let (внутри testKaka)?
     
  19. Asper Daffy

    Asper Daffy Иксперд

    Попробуйте. Можно внутри, можно снаружи, можно и там, и там - ничего не изменится. Всё равно стрелочная функция останется лямбдой, а созданная через new таковой не станет. Это я просто показывал паровозу, что безымянная функция и лямбда-функция - это не одно и то же на примере языка, который ему знаком.
     
    ИгорьК нравится это.
  20. DetSimen

    DetSimen Гуру

    То, что пишет Петрович, нужно пидец какое время, чтобы осмыслить. :) Лично я - сдалса, у мня стока полушарий нету.