{"id":497,"date":"2018-12-15T11:21:54","date_gmt":"2018-12-15T10:21:54","guid":{"rendered":"https:\/\/www.kompikownia.pl\/?p=497"},"modified":"2018-12-15T18:27:07","modified_gmt":"2018-12-15T17:27:07","slug":"wyrazenia-lambda-uzyteczna-nowosc-c11","status":"publish","type":"post","link":"https:\/\/www.kompikownia.pl\/index.php\/2018\/12\/15\/wyrazenia-lambda-uzyteczna-nowosc-c11\/","title":{"rendered":"Wyra\u017cenia lambda &#8211; u\u017cyteczna nowo\u015b\u0107 C++11."},"content":{"rendered":"<span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">Czas czytania:<\/span> <span class=\"rt-time\">&lt; 1<\/span> <span class=\"rt-label rt-postfix\">minut\u0119<\/span><\/span><p>Nowsze standardy C++ wprowadzaj\u0105 wiele udogodnie\u0144, kt\u00f3re sprawiaj\u0105 \u017ce nam \u2013 programistom \u2013 \u017cyje si\u0119 wygodniej. Musimy pisa\u0107 coraz mniej kodu, otrzymuj\u0105c t\u0119 sam\u0105 funkcjonalno\u015b\u0107. Sprawia to, \u017ce uzyskujemy wi\u0119ksz\u0105 wydajno\u015b\u0107. Poza tym, mniejsza ilo\u015b\u0107 lepiej i zwi\u0119\u017alej napisanego kodu zwi\u0119ksza jego czytelno\u015b\u0107. Dzi\u0119ki temu ludzie, kt\u00f3rzy obejm\u0105 projekt po nas nie b\u0119d\u0105 wyrywali sobie w\u0142os\u00f3w z g\u0142owy zastanawiaj\u0105c si\u0119, jak to w og\u00f3le dzia\u0142a. Dzisiaj poznamy co\u015b, co istotnie mo\u017ce upro\u015bci\u0107 kod. To u\u0142atwienie zwie si\u0119 wyra\u017ceniami lambda.<\/p>\n<p>Lambda to nowa rzecz, kt\u00f3ra zosta\u0142a po raz pierwszy wprowadzona wraz ze standardem C++11. Wyra\u017cenia te zosta\u0142y potem zaadaptowane przez inne j\u0119zyki, takie jak Javascript. Je\u015bli kiedykolwiek programowa\u0142e\u015b w ES6, z pewno\u015bci\u0105 spotka\u0142e\u015b si\u0119 z funkcjami strza\u0142kowymi, kt\u00f3re s\u0105 dalekim kuzynem lambdy z C++. Zanim przejdziemy dalej, sp\u00f3jrz na przyk\u0142adowy kod, na kt\u00f3rym b\u0119dziemy pracowali.<\/p>\n<h2 class=\"western\">Niezoptymalizowany kod<\/h2>\n<pre line=\"1\" lang=\"cpp\">\r\n#include <iostream>\r\n#include <string>\r\n#include <algorithm>\r\n#include <vector>\r\nusing namespace std;\r\nstruct ScRegister {\r\n    int id;\r\n    string name;\r\n    string surname;\r\n    int grade;\r\n};\r\nstruct {\r\n    bool operator()(ScRegister a,ScRegister b) const {\r\n        return a.id<b.id;\r\n    }\r\n} IDSort;\r\nstruct {\r\n    bool operator() (ScRegister a,ScRegister b) const {\r\n        return a.surname<b.surname;\r\n    }\r\n} SurnameSort;\r\nvoid printList(vector<ScRegister> people_list) {\r\n       for(ScRegister person: people_list) {\r\n            cout<<\"Nr: \"<<person.id<<endl;\r\n            cout<<\"imie: \"<<person.name<<endl;\r\n            cout<<\"nazwisko: \"<<person.surname<<endl;\r\n            cout<<\"ocena: \"<<person.grade<<endl;\r\n            cout<<endl;\r\n        }\r\n\r\n}\r\nint main()\r\n{\r\n    vector<ScRegister> people_list = {{1,\"Jan\",\"Kowalski\",5},{4,\"Marcin\",\"Pomagier\",4},{2,\"Patryk\",\"Programista\",3},{7,\"Teodor\",\"Mufflon\",2},{3,\"Adam\",\"Nowak\",4},{5,\"Karol\",\"Z\u0119bkiewicz\",2}};\r\n    cout<<\"Przed sortowaniem\"<<endl;\r\n    printList(people_list);\r\n    sort(people_list.begin(),people_list.end(),IDSort);\r\n    cout<<\"Posortowano wg indeksow\"<<endl;\r\n    printList(people_list);\r\n    sort(people_list.begin(),people_list.end(),SurnameSort);\r\n    cout<<\"Posortowano wg nazwisk\"<<endl;\r\n    printList(people_list);\r\n    return 0;\r\n}\r\n<\/pre>\n<p>Program na powy\u017cszym listingu jest dosy\u0107 d\u0142ugi. Na pocz\u0105tku zadeklarowali\u015bmy struktur\u0119 \u201eScRegister\u201d kt\u00f3ra przechowuje ID, imi\u0119, nazwisko i ocene. Nast\u0119pnie zauwa\u017camy dwie \u201eanonimowe\u201d struktury. Zawieraj\u0105 one jeden przeci\u0105\u017cony operator (). Zajmuje si\u0119 on relacj\u0105 element\u00f3w struktury \u201eScRegister\u201d. Struktury te m\u00f3wi\u0105 funkcji sort z biblioteki algorithm w jaki spos\u00f3b rozr\u00f3\u017cni\u0107 kt\u00f3ry element po sortowaniu powinien znale\u017a\u0107 si\u0119 wcze\u015bniej, a kt\u00f3ry p\u00f3\u017aniej. Funkcja printList() s\u0142u\u017cy do wypisania element\u00f3w vectora, kt\u00f3ry przechowuje osoby zapisane w dzienniku.<\/p>\n<p>Przechodzimy do funkcji main. W linijce 34 powiniene\u015b zauwa\u017cy\u0107 u\u017cycie <a href=\"https:\/\/www.kompikownia.pl\/index.php\/2018\/11\/09\/uniform-initialization-prostota-wypelniania-struktur-danych\/\">\u201euniform initialization\u201d<\/a>. W ten spos\u00f3b wype\u0142niamy vector przyk\u0142adowymi danymi. Nast\u0119pnie u\u017cywamy wcze\u015bniej zdefiniowanych struktur i funkcji do sortowania danych na r\u00f3\u017cne sposoby.<\/p>\n<p>Kod nie jest skomplikowany, ale jak na prac\u0119 kt\u00f3r\u0105 wykonuje jest troch\u0119 d\u0142ugi. Czy mo\u017cemy go jako\u015b zoptymalizowa\u0107, skr\u00f3ci\u0107?<\/p>\n<p>Oczywi\u015bcie! W\u0142a\u015bnie w tym celu u\u017cyjemy wyra\u017ce\u0144 lambda \ud83d\ude42<\/p>\n<h2 class=\"western\">Lambda \u2013 co to jest?<\/h2>\n<p>Wyra\u017cenia lambda w j\u0119zyku C++ s\u0105 takimi \u201eanonimowymi funkcjami\u201d. S\u0105 podobne zar\u00f3wno do zwyk\u0142ych zmiennych, jak i do funkcji. Wyra\u017cenia lambda posiadaj\u0105 swoje cia\u0142o, w kt\u00f3rym mog\u0105 wykonywa\u0107 jakie\u015b dzia\u0142anie. Mog\u0105 przyjmowa\u0107 parametry oraz zwraca\u0107 warto\u015bci. Lecz, w przeciwie\u0144stwie do funkcji nie posiadaj\u0105 nazwy. Wyra\u017cenia lambda powinny by\u0107 przypisane do zmiennej. Oczywi\u015bcie, wcale nie musz\u0105. Wszystko zale\u017cy od tego, co chcemy osi\u0105gn\u0105\u0107.<\/p>\n<p>Jak wygl\u0105da podstawowe wyra\u017cenie? Sk\u0142ada si\u0119 ono z trzech cz\u0119\u015bci:<\/p>\n<ul>\n<li><strong>\"capture list\"[]<\/strong> - wskazujemy, kt\u00f3re zmienne maj\u0105 by\u0107 dost\u0119pne wewn\u0105trz lambdy<\/li>\n<li><strong>\"parameter list\"()<\/strong> - cz\u0119\u015b\u0107 opcjonalna. W tym miejscu mo\u017cemy przekaza\u0107 jakie\u015b zmienne do lambdy<\/li>\n<li><strong>\"body\"{}<\/strong> - czyli esencja lambdy. Wewn\u0105trz \u201ecia\u0142a\u201d lambda wykonuje zaplanowane przez programist\u0119 operacje.<\/li>\n<\/ul>\n<p>Po\u0142\u0105czmy ca\u0142\u0105 powy\u017csz\u0105 teori\u0119 w jeden zapis. Wygl\u0105da\u0142by on tak: [](){}. Prawda,\u00a0\u017ce kr\u00f3tko i\u00a0zwi\u0119\u017ale?<\/p>\n<p>No dobra. Spr\u00f3bujmy zastosowa\u0107 lambd\u0119 w praktyce. Pozb\u0105d\u017amy si\u0119 funkcji printList. Chcemy aby to zrobi\u0142o wyra\u017cenie lambda. Skopiuj zawarto\u015b\u0107 funkcji printList do schowka, a nast\u0119pnie wykasuj j\u0105.<\/p>\n<pre line=\"1\" lang=\"cpp\">\r\n#include <iostream>\r\n#include <string>\r\n#include <algorithm>\r\n#include <vector>\r\nusing namespace std;\r\nstruct ScRegister {\r\n    int id;\r\n    string name;\r\n    string surname;\r\n    int grade;\r\n};\r\nstruct {\r\n    bool operator()(ScRegister a,ScRegister b) const {\r\n        return a.id<b.id;\r\n    }\r\n} IDSort;\r\nstruct {\r\n    bool operator() (ScRegister a,ScRegister b) const {\r\n        return a.surname<b.surname;\r\n    }\r\n} SurnameSort;\r\nint main()\r\n{\r\n    vector<ScRegister> people_list = {{1,\"Jan\",\"Kowalski\",5},{4,\"Marcin\",\"Pomagier\",4},{2,\"Patryk\",\"Programista\",3},{7,\"Teodor\",\"Mufflon\",2},{3,\"Adam\",\"Nowak\",4},{5,\"Karol\",\"Z\u0119bkiewicz\",2}};\r\n    auto printList = [people_list](){\r\n        for(ScRegister person: people_list) {\r\n            cout<<\"Nr: \"<<person.id<<endl;\r\n            cout<<\"imie: \"<<person.name<<endl;\r\n            cout<<\"nazwisko: \"<<person.surname<<endl;\r\n            cout<<\"ocena: \"<<person.grade<<endl;\r\n            cout<<endl;\r\n        }\r\n    };\r\n    cout<<\"Przed sortowaniem\"<<endl;\r\n    printList();\r\n    sort(people_list.begin(),people_list.end(),IDSort);\r\n    cout<<\"Posortowano wg indeksow\"<<endl;\r\n    printList();\r\n    sort(people_list.begin(),people_list.end(),SurnameSort);\r\n    cout<<\"Posortowano wg nazwisk\"<<endl;\r\n    printList();\r\n    return 0;\r\n}\r\n<\/pre>\n<p>W linijce 25 znajduje si\u0119 clue naszej pierwszej lambdy. Zapisujemy j\u0105 do zmiennej typu auto. W\u00a0cz\u0119\u015bci \u201ecapture list\u201d przechwytujemy kopi\u0119 zmiennej lista_osob. \u201eParameter list\u201d zostawiamy puste. W ciele znajduje si\u0119 dok\u0142adnie to samo, co wcze\u015bniej znajdowa\u0142o si\u0119 w funkcji \u201eprintList\u201d<\/p>\n<p>Jak wygl\u0105da wywo\u0142anie takiej lambdy? Po prostu:<\/p>\n<pre lang=\"cpp\">printList();<\/pre>\n<p>Co zyskali\u015bmy? W tej chwili niewiele. Ten przyk\u0142ad by\u0142 stworzony nieco na si\u0142\u0119 aby pokaza\u0107 ci najprostsz\u0105 mo\u017cliw\u0105 lambd\u0119. Prawdziw\u0105 si\u0142\u0119 lambda pokazuje przy callbackach.<\/p>\n<p>Czym jest callback? Jest to taki \u201ewska\u017anik na funkcj\u0119\u201d, kt\u00f3ry przekazujemy innej funkcji. Idealnym przyk\u0142adem jest sort w linijkach 36 i 39. Tam trzecim argumentem jest w\u0142a\u015bnie taki callback. Sort wykorzystuje \u201eanonimow\u0105 struktur\u0119\u201d w kt\u00f3rej stworzyli\u015bmy przeci\u0105\u017cony operator() aby posortowa\u0107 elementy wg naszego \u017cyczenia. Rozwi\u0105zanie to dzia\u0142a, ale po co komplikowa\u0107 sobie \u017cycie, skoro mo\u017cna to zrobi\u0107 pro\u015bciej za pomoc\u0105 wyra\u017ce\u0144 lambda?<\/p>\n<h2 class=\"western\">Lambda \u2013 sort \u2013 praktyczne zastosowanie<\/h2>\n<p>Lamba najcz\u0119\u015bciej jest wykorzystywana w\u0142a\u015bnie w celu inline\u2019owania kodu. Poprawny nasze funkcje sortuj\u0105ce tak, aby zajmowa\u0142y mniej miejsca. W sumie, zmienimy tylko dwie linijki. (jedn\u0105 wsp\u00f3lnie, drug\u0105 analogicznie poprawisz samodzielnie).<br \/>\nLinijka 36 przed zmianami wygl\u0105da tak:<\/p>\n<pre lang=\"cpp\">sort(people_list.begin(),people_list.end(),IDSort)<\/pre>\n<p>W IDSort znajduje si\u0119 przeci\u0105\u017cony operator(), kt\u00f3ry m\u00f3wi o tym jak nale\u017cy posortowa\u0107 elementy. Jak za pomoc\u0105 lambdy mo\u017cemy utworzy\u0107 anonimow\u0105 funkcj\u0119, kt\u00f3ra pozwoli nam skasowa\u0107 zajmuj\u0105c\u0105 kilka linijek struktur\u0119?<\/p>\n<pre lang=\"cpp\">sort(people_list.begin(),people_list.end(),[](ScRegister a,ScRegister b){return a.id>b.id;});<\/pre>\n<p>Tak wygl\u0105da wywo\u0142anie funkcji sort() po dokonaniu zmian. Trzecim argumentem, zamiast IDSort jest lambda. Tym razem nie przechwytuje ona \u017cadnych parametr\u00f3w (\u201ecapture list\u201d jest puste). Przyjmuje za to dwa argumenty, podobnie jak operator() w strukturze IDSort. Cia\u0142o r\u00f3wnie\u017c jest podobne. Je\u015bli nie wierzysz \u017ce ten kod dzia\u0142a, wprowad\u017a zmiany i skompiluj.<br \/>\nW tym momencie mo\u017cesz wywali\u0107 anonimowe strukturki IDSort i SurnameSort, kt\u00f3re zdefiniowali\u015bmy wcze\u015bniej \ud83d\ude42 One nie s\u0105 ju\u017c do niczego potrzebne.<\/p>\n<p>W ramach \u0107wicze\u0144 zalecam analogiczne poprawienie sortowania wg nazwisk. Poprawiony kod\u00a0\u017ar\u00f3d\u0142owy jest dost\u0119pny <a href=\"https:\/\/github.com\/karol221-10\/blog_apps\/blob\/master\/lambda\/p3.cpp\">tutaj<\/a>.<\/p>\n<h2 class=\"western\">Lambda \u2013 budowa wyra\u017cenia<\/h2>\n<p>Wiesz ju\u017c, jak dzia\u0142a lambda i kiedy j\u0105 stosujemy. Teraz zajmijmy si\u0119 dok\u0142adniejszym przeanalizowaniem dw\u00f3ch z trzech cz\u0119\u015bci lambdy: \u201eCapture list\u201d i \u201eParameter list\u201d<\/p>\n<h3 class=\"western\">\u201eCapture list\u201d []<\/h3>\n<p>\u201eCapture list\u201d jest pierwsz\u0105 cz\u0119\u015bci\u0105 lambdy, zamkni\u0119t\u0105 w nawiasach kwadratowych. U\u017cyli\u015bmy tego wtedy, kiedy tworzyli\u015bmy wersj\u0119 lambda funkcji wypiszListe. Ale co w\u0142a\u015bciwie robi captureList?<\/p>\n<p>Domy\u015blnie, anonimowa funkcja zwana lambd\u0105 nie ma dost\u0119pu do zmiennych lokalnych zadeklarowanych wewn\u0105trz otaczaj\u0105cej lambd\u0119 funkcji. Aby zapewni\u0107 taki dost\u0119p, musimy wskaza\u0107 kt\u00f3re zmienne maj\u0105 by\u0107 dost\u0119pne. Robimy to w\u0142a\u015bnie wewn\u0105trz \u201ecapture list\u201d. Tylko do zmiennych wypisanych w \u201ecapture list\u201d lambda b\u0119dzie mia\u0142a dost\u0119p. Sp\u00f3jrz na poni\u017cszy kod:<\/p>\n<pre line=\"1\" lang=\"cpp\">\r\n#include <iostream>\r\n\r\nusing namespace std;\r\n\r\nint main()\r\n{\r\n    int a = 4, b = 6, c = 9;\r\n    auto lam1 = [](){a+=6;};\r\n    auto lam2 = [b](){cout<<b<<endl;};\r\n    auto lam3 = [b](){b+=9;};\r\n    auto lam4 = [&#038;c](){c+=12;};\r\n    auto lam5 = [&#038;](){a+=3; b+=6;};\r\n    return 0;\r\n}\r\n\r\n<\/pre>\n<p>Widzimy tu 4 lambdy. Wewn\u0105trz pierwszej z nich, lam1, chcemy zwi\u0119kszy\u0107 warto\u015b\u0107 zmiennej a o 6. Zmienna a jest zmienn\u0105 lokaln\u0105 funkcji a. Kompilator nie pozwoli na takie co\u015b. Wyst\u0105pi b\u0142\u0105d kompilacji m\u00f3wi\u0105cy o tym, \u017ce zmienna a nie zosta\u0142a przechwycona przez lambd\u0119.<\/p>\n<p>W drugiej lambdzie, lam2, przechwytujemy zmienn\u0105 b. Wewn\u0105trz cia\u0142a lambdy u\u017cywamy funkcji cout aby wypisa\u0107 warto\u015b\u0107 przechowywan\u0105 w b na ekran. Kompilator nie b\u0119dzie protestowa\u0142, gdy\u017c jedynie wykorzystujemy zawarto\u015b\u0107 zmiennej b, nie wprowadzaj\u0105c w niej zmian.<\/p>\n<p>Trzecia lambda wygl\u0105da poprawnie. Przechwytujemy zmienn\u0105 b i pr\u00f3bujemy zwi\u0119kszy\u0107 j\u0105 o 9. Mimo, \u017ce lambda wygl\u0105da poprawnie to kompilator nie pozwoli nam skompilowa\u0107 tej linijki kodu. Dlaczego? Poniewa\u017c zmiennych przechwyconych \u201eprzez warto\u015b\u0107\u201d domy\u015blnie nie mo\u017cemy modyfikowa\u0107 w\u00a0lambdzie. Mo\u017cemy je tylko odczytywa\u0107. To kolejna w\u0142a\u015bciwo\u015b\u0107 tej mechaniki, o kt\u00f3rej warto pami\u0119ta\u0107.<\/p>\n<p>W czwartej lambdzie u\u017cywamy referencji do przechwycenia zmiennej c. Wewn\u0105trz cia\u0142a zwi\u0119kszamy j\u0105 o 12. Tym razem wszystko si\u0119 powiedzie, gdy\u017c wzi\u0119li\u015bmy pod uwag\u0119 szczeg\u00f3\u0142 o\u00a0kt\u00f3rym m\u00f3wili\u015bmy w poprzednim akapicie.<\/p>\n<p>Ostatnia lambda wygl\u0105da ciekawie. Przechwytujemy tylko referencj\u0119? Czy takie co\u015b w\u00a0og\u00f3le si\u0119 skompiluje? Okazuje si\u0119, \u017ce tak. A co oznacza taki zapis? Je\u015bli w \u201ecapture list\u201d wstawimy jedynie symbol referencji, to wewn\u0105trz cia\u0142a lambdy b\u0119dziemy mieli dost\u0119p do referencji wszystkich zmiennych lokalnych utworzonych w otaczaj\u0105cej lambd\u0119 funkcji.<\/p>\n<p>Je\u015bli zamiast znaku &amp; wstawimy znak =, uzyskamy prawie ten sam efekt. Wewn\u0105trz lambdy b\u0119dziemy mieli wtedy dost\u0119p do kopii ka\u017cdej zmiennej lokalnej.<\/p>\n<h3 class=\"western\">\u201eParameter list\u201d ()<\/h3>\n<p>W \u201eParameter list\u201d mo\u017cemy przekaza\u0107 list\u0119 argument\u00f3w, kt\u00f3re b\u0119dzie pobiera\u0142a lambda. Argumenty te b\u0119dziemy musieli jawnie przekaza\u0107 lambdzie podczas jej wywo\u0142ywania.<\/p>\n<pre line=\"1\" lang=\"cpp\">\r\n#include <iostream>\r\n\r\nusing namespace std;\r\n\r\nint main()\r\n{\r\n    auto lam1 = [](int arg1,int arg2){return arg1+arg2;};\r\n    int wynik = lam1(5,3);\r\n    cout<<\"wynik: \"<<wynik<<endl;\r\n    return 0;\r\n}\r\n<\/pre>\n<p>W powy\u017cszym przyk\u0142adzie znajduje si\u0119 prosty przyk\u0142ad lambdy przyjmuj\u0105cej argumenty. Lam1 pobiera dwie liczby typu int. Wewn\u0105trz cia\u0142a lambdy sumujemy te liczby je zwracamy.<\/p>\n<pre line=\"1\" lang=\"cpp\">\r\n#include <iostream>\r\n\r\nusing namespace std;\r\n\r\nint main()\r\n{\r\n    auto lam2 = []{cout<<\"Lambda bez parameterlist\";};\r\n    lam2()\r\n    return 0;\r\n}\r\n<\/pre>\n<p>Ten przyk\u0142ad wygl\u0105da ciekawie. Czy programista o czym\u015b nie zapomnia\u0142? W definicji lambdy brakuje \u201eparameter list\u201d. Czy takie co\u015b si\u0119 skompiluje? Owszem. Je\u015bli lambda nie pobiera \u017cadnych argument\u00f3w, mo\u017cemy bez problemu pomin\u0105\u0107 nawiasy okr\u0105g\u0142e \u201eparameter list\u201d i skr\u00f3ci\u0107 zapis o te kilka znak\u00f3w.<\/p>\n<pre line=\"1\" lang=\"cpp\">\r\n#include <iostream>\r\n\r\nusing namespace std;\r\n\r\nint main()\r\n{\r\n    auto lam1 = [](int a,int b){return a<b;};\r\n    auto lam2 = [](auto a,auto b){return a<b;};\r\n    cout<<lam1(4,3)<<endl;\r\n    cout<<lam2(4,3)<<endl;\r\n    cout<<lam1(\"Bolek\",\"Lolek\")<<endl;\r\n    cout<<lam2(\"Bolek\",\"Lolek\")<<endl;\r\n    return 0;\r\n}\r\n<\/pre>\n<p>Podobnie jak w funkcjach, tak\u017ce i w li\u015bcie argument\u00f3w mo\u017cemy u\u017cywa\u0107 \u201eauto\u201d jako typu zmiennej. Dzi\u0119ki temu mo\u017cemy stworzy\u0107 bardziej \u201euog\u00f3lnion\u0105\u201d lambd\u0119. Pokazuje to powy\u017cszy przyk\u0142ad. Lam1 potrafi por\u00f3wnywa\u0107 tylko liczby ca\u0142kowite. Linijka 11 nie skompiluje si\u0119, gdy\u017c lam1 nie potrafi por\u00f3wnywa\u0107 string\u00f3w. Takiego ograniczenia nie ma lam2. Kompilator automatycznie dopasuje sobie typ zmiennej i wykona prawid\u0142owe por\u00f3wnanie.<\/p>\n<h2 class=\"western\">Lambda mutable<\/h2>\n<p>Lambda ma jedn\u0105 bardzo ciekaw\u0105 w\u0142asno\u015b\u0107. Je\u015bli przechwycimy zmienn\u0105 lokaln\u0105 przez \u201ewarto\u015b\u0107\u201d, nie b\u0119dziemy mogli jej zmodyfikowa\u0107 w lambdzie. Jednym z mo\u017cliwych rozwi\u0105za\u0144 tego problemu jest \u201eprzechwycenie\u201d zmiennej jako referencji. Czy istnieje inny spos\u00f3b? Tak. Jest nim s\u0142\u00f3wko kluczowe \u201emutable\u201d.<\/p>\n<p>W definicji lambdy, po \u201eparameter list\u201d mo\u017cemy umie\u015bci\u0107 s\u0142owo kluczowe mutable.<\/p>\n<pre lang=\"cpp\">auto lam3 = [b]() mutable {b+=9;}<\/pre>\n<p>Powy\u017csza linijka tworzy lambd\u0119, kt\u00f3ra zwi\u0119ksza zawarto\u015b\u0107 zmiennej lokalnej b o 9. Normalnie nie mogliby\u015bmy tego zrobi\u0107. Ale dzi\u0119ki umieszczeniu s\u0142\u00f3wka kluczowego <b>mutable <\/b>kompilator nie b\u0119dzie protestowa\u0142 i pos\u0142usznie wykona \u017c\u0105dan\u0105 przez programist\u0119 czynno\u015b\u0107.<\/p>\n<h2 class=\"western\">Lambda w klasach<\/h2>\n<p>C++ jest j\u0119zykiem obiektowym. A skoro obiektowym, to pewnie zastanawiasz si\u0119 jak zachowa si\u0119 lambda zdefiniowana wewn\u0105trz cia\u0142a klasy? Czy taka lambda ma dost\u0119p do w\u0142a\u015bciwo\u015bci prywatnych klasy czy mo\u017ce korzysta\u0107 jedynie z jej publicznych element\u00f3w? A mo\u017ce wcale nie mo\u017ce u\u017cywa\u0107 w\u0142a\u015bciwo\u015bci klasy? Zaraz si\u0119 przekonamy.<\/p>\n<pre line=\"1\" lang=\"cpp\">\r\n#include <iostream>\r\n\r\nusing namespace std;\r\nclass Foo {\r\n    public:\r\n    void bar(float arg) {\r\n        auto lamb = [](float c,Foo& f){f.a+=c;};\r\n        lamb(arg,*this);\r\n    }\r\n    void print() {\r\n        cout<<\"a: \"<<a<<endl<<\"b: \"<<b<<endl;\r\n    }\r\n    private:\r\n    float a = 4;\r\n    float b = 6;\r\n};\r\nint main()\r\n{\r\n    Foo cl;\r\n    cl.print();\r\n    cout<<endl;\r\n    cl.bar(4);\r\n    cl.print();\r\n    return 0;\r\n}\r\n<\/pre>\n<p>W powy\u017cszym listingu mamy przyk\u0142ad bardzo prostej klasy. Foo posiada dwie zmienne prywatne. Klasa posiada dwie publiczne metody: print, kt\u00f3ra wypisuje zawarto\u015b\u0107 zmiennych oraz bar, kt\u00f3ra b\u0119dzie nas interesowa\u0107 najbardziej.<\/p>\n<p>Wewn\u0105trz metody bar definiujemy lambd\u0119. Lambda ta przyjmuje dwa argumenty: zmienn\u0105 typu float oraz referencj\u0119 na klas\u0119 Foo. Wewn\u0105trz lambdy modyfikujemy w\u0142a\u015bciwo\u015b\u0107 prywatn\u0105 klasy Foo. Czy tak mo\u017cna? Okazuje si\u0119, \u017ce tak. Ka\u017cda lambda zdefiniowana wewn\u0105trz klasy jest \u201ez\u00a0automatu\u201d zaprzyja\u017aniona z t\u0105 klas\u0105.<\/p>\n<p>W powy\u017cszym przyk\u0142adzie przekazali\u015bmy lambdzie referencj\u0119 do klasy Foo. A czy lambda mo\u017ce przechwyci\u0107 wska\u017anik this? Oczywi\u015bcie \ud83d\ude42 Zmodyfikujmy nieco metod\u0119 bar:<\/p>\n<pre line=\"1\" lang=\"cpp\">\r\n#include <iostream>\r\n\r\nusing namespace std;\r\nclass Foo {\r\n    public:\r\n    void bar(float arg) {\r\n        auto lamb = [this](float c){this->a+=c;};\r\n        lamb(arg);\r\n    }\r\n    void print() {\r\n        cout<<\"a: \"<<a<<endl<<\"b: \"<<b<<endl;\r\n    }\r\n    private:\r\n    float a = 4;\r\n    float b = 6;\r\n};\r\nint main()\r\n{\r\n    Foo cl;\r\n    cl.print();\r\n    cout<<endl;\r\n    cl.bar(4);\r\n    cl.print();\r\n    return 0;\r\n}\r\n<\/pre>\n<p>Tym razem na li\u015bcie argument\u00f3w lambdy nie widzimy referencji do klasy Foo. Zauwa\u017camy za to wska\u017anik \u201ethis\u201d w \u201ecapture list\u201d. Wska\u017anik przechwytujemy przez warto\u015b\u0107. Nasuwa to pytanie, dlaczego mo\u017cemy zmieni\u0107 warto\u015b\u0107 zmiennej prywatnej klasy? Zgodnie z tym, co wcze\u015bniej ustalili\u015bmy, lambda nie mo\u017ce modyfikowa\u0107 zmiennych przechwyconych przez warto\u015b\u0107. Rozwi\u0105zanie jest proste. Istnieje tutaj tylko pozorna sprzeczno\u015b\u0107. Nie mo\u017cemy modyfikowa\u0107 wska\u017anika \u201ethis\u201d. Ale nie blokuje to mo\u017cliwo\u015bci modyfikowania zmiennych wskazywanych przez ten wska\u017anik. Dok\u0142adnie tak samo zadzia\u0142a to w przypadku zwyk\u0142ych wska\u017anik\u00f3w zadeklarowanych przez ciebie. Je\u015bli nie wierzysz, spr\u00f3buj!<\/p>\n<h2 class=\"western\">Lambda \u2013 kilka zagadek na zako\u0144czenie<\/h2>\n<p>Na koniec dam ci dwa ciekawe zadania. Postaraj si\u0119 odpowiedzie\u0107 nad zamieszczone pod listingami pytania bez uruchamiania program\u00f3w. Skompiluj je dopiero wtedy, gdy b\u0119dziesz chcia\u0142 sobie sprawdzi\u0107 odpowiedzi. W zadaniach dla utrudnienia u\u017cy\u0142em pewnej konstrukcji, kt\u00f3rej nie wyja\u015bnia\u0142em w tym artykule. Po analizie powiniene\u015b si\u0119 dowiedzie\u0107, o co chodzi \ud83d\ude42<\/p>\n<pre line=\"1\" lang=\"cpp\">\r\n#include <iostream>\r\n\r\nusing namespace std;\r\n\r\nint main()\r\n{\r\n    int a = 15;\r\n    [a]mutable{a+=4;};\r\n    cout<<\"a: \"<<a<<endl;\r\n}\r\n<\/pre>\n<p>1. Czy program si\u0119 skompiluje?<br \/>\n2. Po poprawieniu b\u0142\u0119d\u00f3w, jak\u0105 warto\u015b\u0107 b\u0119dzie mia\u0142a zmienna a?<\/p>\n<pre line=\"1\" lang=\"cpp\">\r\n#include <iostream>\r\n\r\nusing namespace std;\r\n\r\nint main()\r\n{\r\n    int a = 0,b=4,c=0;\r\n    a=[&a]{return a+5;}();\r\n    b=[=]{return a+b;}();\r\n    [=,&c]{c=a+b;};\r\n    cout<<\"a: \"<<a<<endl;\r\n    cout<<\"b: \"<<b<<endl;\r\n    cout<<\"c: \"<<c<<endl;\r\n    return 0;\r\n}\r\n<\/pre>\n<p>1. Czy program si\u0119 skompiluje?<br \/>\n2. Po poprawieniu b\u0142\u0119d\u00f3w, jak\u0105 warto\u015b\u0107 b\u0119dzie mia\u0142a zmienna a,b i c?<\/p>\n<h2 class=\"western\">Podsumowuj\u0105c<\/h2>\n<p>Lambda jest bardzo przydatn\u0105 funkcjonalno\u015bci\u0105 j\u0119zyka. Kiedy tylko wyczujesz, kiedy nale\u017cy jej u\u017cywa\u0107, znacznie upro\u015bcisz sobie swoje programistyczne \u017cycie. Oczywi\u015bcie, wymaga to treningu. Ale kto powiedzia\u0142, \u017ce \u017cycie jest proste?<\/p>\n<p>Dzi\u0119ki za przeczytanie artyku\u0142u i zainteresowanie tematem \ud83d\ude42 Je\u015bli masz jakie\u015b pytania, w\u0105tpliwo\u015bci - napisz o nich w komentarzu lub na <a href=\"https:\/\/www.facebook.com\/kompikownia\/\">fanpage<\/a>, do kt\u00f3rego polubienia zach\u0119cam.<br \/>\nStandardowo, wszystkie kody \u017ar\u00f3d\u0142owe zamieszczone w tym artykule s\u0105 dost\u0119pne tak\u017ce w moim repozytorium na platformie <a href=\"https:\/\/github.com\/karol221-10\/blog_apps\/tree\/master\/lambda\">GitHub<\/a>. <\/p>\n","protected":false},"excerpt":{"rendered":"<p><span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">Czas czytania:<\/span> <span class=\"rt-time\">&lt; 1<\/span> <span class=\"rt-label rt-postfix\">minut\u0119<\/span><\/span> Nowsze standardy C++ wprowadzaj\u0105 wiele udogodnie\u0144, kt\u00f3re sprawiaj\u0105 \u017ce nam \u2013 programistom \u2013 \u017cyje si\u0119 wygodniej. Musimy pisa\u0107 coraz mniej kodu, otrzymuj\u0105c t\u0119 sam\u0105 funkcjonalno\u015b\u0107. Sprawia to, \u017ce uzyskujemy wi\u0119ksz\u0105 &#8230;<\/p>\n","protected":false},"author":1,"featured_media":518,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[31],"tags":[],"_links":{"self":[{"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/posts\/497"}],"collection":[{"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/comments?post=497"}],"version-history":[{"count":26,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/posts\/497\/revisions"}],"predecessor-version":[{"id":524,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/posts\/497\/revisions\/524"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/media\/518"}],"wp:attachment":[{"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/media?parent=497"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/categories?post=497"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/tags?post=497"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}