funkcje lambda

0

Cześć :)
Powiedźcie mi proszę jaki jest sens używania funkcji lambda? Coś musi to mieć na celu, skoro wprowadzili to w C++ i Javie. Dlaczego ja uważam, że to nie ma sensu?
1) takiej funkcji i tak nie można ponownie wywołać, a to jest właśnie najistotniejsze zastosowanie funkcji.
2) nie wiem dlaczego upierac przy utrudnianiu składni, skoro zamiast pisać funkcję lambda można po prostu ten kawałem kodu wpisać w "to miejsce".
Pozdrawiam :)

0

takiej funkcji i tak nie można ponownie wywołać, a to jest właśnie najistotniejsze zastosowanie funkcji.

Mozna jak sie przypisze do zmiennych.
Glowne zastosowanie lambd to tak zwane higher-order functions, czyli funkcje przyjmujace (badz zwracajace) inne funkcji. Na przyklad cale <algorithm>

nie wiem dlaczego upierac przy utrudnianiu składni, skoro zamiast pisać funkcję lambda można po prostu ten kawałem kodu wpisać w "to miejsce".

Nie mozna, pisanie lambdy jest duzo szybsze i bardziej przedstawia intencje autora.

W niektorych jezykach masz jeszcze dodatkowe smaczki zwiazane z lambdami, jak na przyklad rozwalanie ich na drzewa wyrazen w C#,

W niektorych przypadkach ciezko jest uzyc czegos innego niz lambda, bo potrzebujemy aktualnego 'kontekstu' (ewentualnie jego czesci).

1

Ad 1. Przeważnie takie funkcje są wywoływany tylko w jednym miejscu całego programu, albo na przykład przypisywane od razu do tablicy funkcji.
Ad 2. Pokaż jak wstawisz bezpośrednio tu:

sort(mMyClassVector.begin(), mMyClassVector.end(), 
    [](const MyClass & a, const MyClass & b) -> bool
{ 
    return a.mProperty > b.mProperty; 
});

lub tu:

struct { char opr; double (*fun)(double,double); } Tb[]=
  {
     {'+',[](double a,double b) -> double { return a+b; } },
     {'-',[](double a,double b) -> double { return a-b; } },
     {'*',[](double a,double b) -> double { return a*b; } },
     {'/',[](double a,double b) -> double { return a/b; } },
  };
0

@mielony źle na to patrzysz. Skokiem cywilizacyjnym nie jest sam fakt że mozesz napisać sobie funkcję anonimową - lambdę. Jest nim fakt że możesz używać funkcji jako obiektów 1 klasy. To znaczy możesz zrobić API które wymaga funkcji jako parametrów. To jest dopiero coś! Zauważ że jeśli nie masz takiego mechanizmu to musisz się bawić w głupie protezy -> np. tworzyć klasy/interfejsy z jedną metodą które potem przyjmujesz jako parametr. W efekcie użytkownik takiego API musi się bawić w tworzenie nowej klasy tylko po co żeby napisać w niej x < y.

2
mielony napisał(a):

nie wiem dlaczego upierac przy utrudnianiu składni, skoro zamiast pisać funkcję lambda można po prostu ten kawałem kodu wpisać w "to miejsce".
Nie można, gdyby było można to nie byłoby potrzeby pisać lambd. W zasadzie lambdy są właśnie odpowiedzią na problem pisania kodu w miejscu logicznym dla jego wywołania.

Ponadto, za pomocą lambdy jesteś w stanie zrobić (niewiele, ale jednak) więcej niż za pomocą funktorów/funkcji: lambda może przyjmować argumenty tak jak szablon, jednocześnie pomimo tego, że jest zdefiniowana w ciele funkcji, a do tego ma dostęp do składników prywatnych klasy, w której funkcji została zdefiniowana.

0

jak wygląda taka deklaracja funkcji, do której jako argument można podać jako funkcję? Oczekuje po prostu na wskaźnik do funkcji?

0

zrób sobie typedefa ze wskaźnika na funkcję, i dalej już jest oczywiste.

1

Uzywaj szablonow, na przyklad:

#include <iostream>
using namespace std;

template<typename UnaryF>
void callWith3(UnaryF f) {
    f(3);
}

int main() {
    callWith3([](int i){ cout << i << endl; });
    return 0;
}

Generalnie konkretny typ (czy dziwy C++) sa malo istotne, chcesz dostac jakis argument, ktory mozna z czyms tam wywolac i cos konkretnego zwraca, badz nie zwraca nic. Srednio kogokolwiek martwi czy to bedzie funkcja czy jakas klasa z operatorem(), no i nie trzeba tych typedefow pisac.

Mozesz jeszcze uzyc std::function<>, ale to sie wiaze z innymi problemami.

1

@mielony, w Javie to fajnie widać, bo masz klasy anonimowe i Lambdy świetnie sprawdzają się jako zamienniki:

executorService.submit(new Callable<Object>(){
     public Object call(){
         return new Object();
    }
});

można zamienić na

executorService.submit(()-> new Object());

Co wygląda nieźle i ma jedną ciekawą przewagę w stosunku do np. wzkaźnika na funkcję - można wykorzystać zmienne lokalne, pola oraz metody obiektu w ktorym utworzono lambdę. Podobnie w Cpp można się z lambdy odwołać do składowych obiektu gdzie zdefiniowaną mamy lambdę.

Z innych zalet - funkcje jako obiekty pierwszej kategorii - można je przyjmowac jako parametry i zwracać jako wyniki. Pozwala to na np. nieewaluowanie wyrażenie jeżeli nie ma takiej potrzeby:

format("Log z %s, %s", ()->"ewaluacja", ()->"ewaluacja", ()->"a mnie już nie wywołasz" );

Po prostu nie wywołujesz funkcji.
Kolejna sprawa to tworzenie kodu funkcyjnego. Ze wszystkimi jego wadami i zaletami. Do tego dochodzi tworzenie kodu deklaratywnego, a nie imperatywnego, co pozwala na osiągnięcie wysokiej separacji dla poszczególnych elementów.

Podsumowując. Lambdy wprowadzają do kodu elemnty pozwalające na tworzenie zwięzłych, przejrzystych i delkaratywnych konstrukcji. Tym samym kod jest prostszy w testowaniu, utrzymaniu i rozwoju.

0

@Azarien Nie wszystkie kompilatory (jeszcze) sobie radza z std::function bez straty wydajnosci.

#include <functional>
#include <iostream>

struct Virtual {
  virtual ~Virtual() {}
  virtual int operator() () const = 0;
};

template <typename T>
int test (T const& fun) {
  int ret = 0;
  for (int i=0; i<1024*1024*1024; ++i) {
    ret += fun();
  }
  return ret;
}

struct Impl : Virtual {
  virtual ~Impl() {}
  virtual int operator()() const { return 1; }
};

Virtual* create_virt() { return new Impl; }

std::function<int()> create_fun() {
  return []() { return 1; };
}

std::function<int()> create_fun_with_state() {
  int x,y,z;
  return [=]() { return 1; };
}

int main () {
  const clock_t start = clock();
  std::cout << test(*create_virt()) << '\n';
  const double secs = (clock()-start) / double(CLOCKS_PER_SEC);
  std::cout << "virtual: " << secs << " secs.\n";
  const clock_t start = clock();
  std::cout << test(create_fun()) << '\n';
  const double secs = (clock()-start) / double(CLOCKS_PER_SEC);
  std::cout << "std::function: " << secs << " secs.\n";
  const clock_t start = clock();
  std::cout << test(create_fun_with_state()) << '\n';
  const double secs = (clock()-start) / double(CLOCKS_PER_SEC);
  std::cout << "std::function with bindings: " << secs << " secs.\n";
}

Dla porownania, GCC i Clang.

─> g++ test.cpp -o test -std=c++11
-> ./test
1073741824
virtual: 2.88545 secs.
1073741824
std::function: 17.0908 secs.
1073741824
std::function with bindings: 16.9255 secs.

-> g++ test.cpp -o test -std=c++11 -O2
-> ./test
1073741824
virtual: 0.729707 secs.
1073741824
std::function: 2.35557 secs.
1073741824
std::function with bindings: 2.87575 secs.

-> g++ test.cpp -o test -std=c++11 -O3
-> ./test
1073741824
virtual: 0.702171 secs.
1073741824
std::function: 1.83678 secs.
1073741824
std::function with bindings: 1.83082 secs

-> clang++ test.cpp -o test -std=c++11
-> ./test
1073741824
virtual: 3.22913 secs.
1073741824
std::function: 15.7867 secs.
1073741824
std::function with bindings: 15.6888 secs.

-> clang++ test.cpp -o test -std=c++11 -O2
-> ./test
1073741824
virtual: 1.85572 secs.
1073741824
std::function: 1.83129 secs.
1073741824
std::function with bindings: 1.76058 secs.

-> clang++ test.cpp -o test -std=c++11 -O3
-> ./test
1073741824
virtual: 1.86154 secs.
1073741824
std::function: 1.83953 secs.
1073741824
std::function with bindings: 1.78675 secs.

gcc (GCC) 4.9.1
clang version 3.4.2 (tags/RELEASE_34/dot2-final)

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0