TIE-20200 Ohjelmistojen suunnittelu Luento 12 : Lambdat sun muut TIE-20200 Samuel Lahtinen Matti Rintala 1
Ohjelmassa tänään Lambdat, templatet, genericsit Erilaisia tapoja nähdä ohjelman rakenne: Single page application Entity component system Funktionaaliset jutut
Entity Component -juttu Ohjelman rakenne System-osassa ohjelman toiminnallisuusosat Esim. pelissä pelilogiikka, grafiikan piirto, fysiikkamoottori, törmäystarkastukset jne. Entity: yleiskäyttöinen objekti/olio/käsite, tunniste pelissä oleville asioille (sisältää tunnisteen, esim. int) Component: Tiedot jollekin tietylle käsitteelle yhdestä systemnäkökulmasta, data (joissain tapauksissa tarvittava toiminnallisuus) System-osien välillä tiedotusta esim. tapahtumien avulla Yleisin käyttökohde pelit (myös valmiit pelimoottorit/peli/ohjelmistokehykset (frameworkit)) No miksi tätä opetetettu heti kurssin alussa?
Entity Component juttuun liittyen Esimerkkejä ECS-lähestymistapaan liittyen: http://www.gamedev.net/page/resources/_/technical/game-programming/understandingcomponent-entity-systems-r3013 http://gameprogrammingpatterns.com/component.html http://www.richardlord.net/blog/what-is-an-entity-framework
Erittäin kevyesti funktionaalisista kielistä ~Kaikki esitetään funktioina, niiden parametreina & paluuarvoina Jotain funktionaalisuuteen liittyviä piirteitä: Sivuvaikutuksettomuus: funktion tulos aina sama samoilla parametreilla kutsuttuna Funktiot parametreina, paluuarvoina, korkeamman luokan funktiot Rekursion käyttö (ei silmukkamuuttujaa) Immutable data yleistä: tieto luonnin jälkeen muuttumatonta Epäpuhtauksia: tilamuuttujia, sivuvaikutusten salliminen (käyttöliittymään liittyvät asiat, tietojen syöttö/tulostus) Funktionaalisuuden tunkeutuminen normikielien (imperatiivisten) puolelle, esim. funktiot parametreina, lambdat, yms. rakenteet
Erittäin kevyesti funktionaalisista kielistä Johdantoa funktionaalisuuteen, Lambdat jne. Kannattaa tutustua johonkin funktionaaliseen kieleen: Yleissivistyksen kannalta hyvä Hyvä ajatustapaharjoitus Trendikästä teollisuudessa Pääsee eksklusiivisen kerhon jäseneksi ja voi päteä sanoilla kuten monadi, s-expression, atoms, catamorphism, puhdas funktio, higher order function, currying Helpompi hämätä asiakkaita jne., osa ymmärtää jo tavallisen koodin päälle Työkalut ja menetelmät osin epäkypsiä
Funktionaalisuudesta Kieliä: Haskell, Clojure, Lisp(Scheme) olio-funktionaalinen hybridit: Scala, F#, kummalliset: Erlang Lisäluettavaa kiinnostuneille Kurssimateriaalit: http://www.cs.tut.fi/~bitti/functional-seminar/ http://www.cs.tut.fi/~okp/2011/luentomatsku-pdf/funktio.pdf http://www.cs.helsinki.fi/u/mnykanen/fop/ http://www.mit.jyu.fi/opiskelu/seminaarit/bak/funktion/ http://www.cs.hut.fi/~cessu/fp-sem/ Online tutoriaaleja: http://tryhaskell.org/ http://www.tryclj.com/ http://java.ociweb.com/mark/clojure/article.html
Lambdat, C++11: miksi? Tarve välittää kirjastolle/funktiolle toiminnallisuutta callback-funktiot Geneeristen kirjastojen räätälöinti STL:n algoritmit (esim. find, sort) Koodin rinnakkainen suoritus Aiemmin sama mahdollista funktio-osoittimilla ja funktio-olioilla Kömpelömpiä, määrittely käyttökohteesta irrallaan
Funktio-osoittimista Funktio-osoittimet funktionaalisten kielten tyylisiä Parametrit ainoa data sisään, viiteparametrit & paluuarvo datana ulos bool alle5( int a ) return a < 5; std::find_if( v.begin(), v.end(), &alle5 ); Muun datan välitys vaikeaa (lähinnä globaalit muuttujat) int raja; // Oltava globaali muuttuja! bool allerajan( int a ) return a < raja; std::cin >> raja; std::find_if( v.begin(), v.end(), &allerajan );
Lambdat Lambdat ikivanha keksintö (Lisp) C++11:n lambdoissa kuitenkin joitain eroja Idea: Lambdat ovat funktion kaltaisia tuotteita, ottavat parametreja, palauttavat paluuarvon nimettömiä, määrittelemättömän tyyppisiä (melkein) pystyvät viittaamaan luontiympäristönsä muuttujiin, samoin muuttamaan pystyvät kopioimaan itseensä osia luontiympäristöstään
Lambdojen syntaksi []() C++11:n syntaksi lambdalle [ympäristö](parametrit)->paluutyyppirunko; Ympäristö tyhjä, jos ei viittaa ympäristöönsä parametrit tyhjä, jos ei parametreja (myös () sulut voivat puuttua) jos ->paluutyyppi puuttuu, se on void, paitsi jos runko pelkkä return-lause, jolloin päätellään
Lambda-esimerkkejä Lambda (std::sort-funktion parametriksi) std::sort( stuff.begin(), stuff.end(), // Lambda alkaa [](float a, float b) return (std::abs(a) < std::abs(b)); // lambda loppuu ); std::find_if( v.begin(), v.end(),[]( int a ) ->bool return a<5; ); std::find_if( v.begin(), v.end(),[]( int a )return a<5; ); int raja; // Paikallinen muuttuja std::cin >> raja; std:find_if( v.begin(), v.end(), [raja]( int a )return a<raja; );
Ympäristöön viittaaminen Lambda voi viitata paikallisiin muuttujiin: lambdan elinaika vs muuttujan elinaika! C++:n ongelma, muuttujilla määrätty elinaika (C++:n muistimalli) Toinen tapa ajatella: lambda saa parametreja ympäristöstään luontihetkellä, normaalit parametrit kutsuhetkellä Kaksi tapaa: arvon ja viitteen välitys
Ympäristön kopioiminen Tapa 1: ympäristön kopioiminen = (oletus) Käytetyt muuttujat kopioidaan lambdan sisään (vrt. arvoparametri), elinkaaret eivät ongelma Kopioita voi muuttaa vain, jos lambda on mutable int raja; std::cin >> raja; std::find_if( v.begin(), v.end(), [=raja](int a)return a<raja; ); std::for_each(v.begin(), v.end(), [raja](int a) mutable std::cout << ++raja; );
Ympäristöön viiteviittaaminen Tapa 2: Ympäristöön viittaaminen & Lambda käyttää suoraan ympäristön muuttujia Ohjelmoija vastaa, että muuttujat pysyvät elossa int summa = 0; std::for_each(v.begin(), v.end(),[&summa](int a)summa += a;);
Ympäristöön viittaaminen Ympäristöön viittauksia voi yhdistellä int raja = 5; int vertailuja = 0; std::find_if( v.begin(), v.end(), [=raja,&vertailuja](int a)->bool ++vertailuja; return a<raja; ); Implisiittinen ympäristöön viittaus (valittava, onko viite vai kopio) int raja = 5; int summa = 0; std::find_if( v.begin(), v.end(), [=](int a)return a<raja; ); std::for_each( v.begin(), v.end(), [&](int a)summa+=a; );
Ympäristöön viittaaminen, luokissa [this]() jäsenfunktiossa määritelty lambda voi viitata jäsenmuuttujiin ja -funktioihin class X int i_; void g( int x ); void f( std::vector<int> const& v ) std::for_each( v.begin(), v.end(), [this](int a) i_+= a; g(a); ); ;
Funktioon viittaus Fibonaccin sarjaa, esimerkkinä funktion antaminen ympäristönä std::function<int(int)> recursivefibonacci = [&recursivefibonacci]( int n ) return n < 2? 1 : recursivefibonacci( n-1 ) + recursivefibonacci( n-2 ); ;
Auton käyttöä, paikallinen funktio lambdalla Labdan kutsu määrittelyn jälkeen ei onnistu, ei nimeä, ei mitä kutsua C++11 sallii lambdojen tallentamisen myös nimettyihin muuttujiin voidaan kutsua samaa lambdaa useampaan kertaan int kerronta( std::vector<int>& v, std::list<int>& l ) int x=1; auto kerro = [&](int i)x *= i;; // lambda talteen for( auto i: v ) kerro( i ); // käyttö vektorilla for( auto i: l ) kerro( i ); // käyttö listalla return x;
Lambdan toteutus? Yksi mahdollisuus: lambdat funktio-olioita Nimettömiä luokkia, joissa kutsuoperaattori operator() Jäsenmuuttuja jokaista ympäristöviittausta kohti (viite, jos viittaus ympäristöön) Kääntäjä saa myös toteuttaa miten haluaa Esim. funktio-olio, jossa suoraan aktivaatiotietueen osoite yms.
C# lambdat sun muut Tukee lambdoja, myös LINQ (Language- Integrated Query) Käyttö kuten C++:ssa, public partial class Form1 : Form customers.where(c => c.city == "London"); public Form1() InitializeComponent(); button1.click += async (sender, e) => // ExampleMethodAsync returns a Task. await ExampleMethodAsync(); textbox1.text += "\r\ncontrol returned to Click event handler.\r\n"; ; async Task ExampleMethodAsync() // The following line simulates a task-returning asynchronous process. await Task.Delay(1000); TIE-20200 Samuel Lahtinen http://msdn.microsoft.com/en-us/library/bb397687.aspx
C# LINQ-esimerkki class LINQQueryExpressions static void Main() // Specify the data source. int[] scores = new int[] 97, 92, 81, 60 ; // Define the query expression. IEnumerable<int> scorequery = from score in scores where score > 80 select score; // Execute the query. foreach (int i in scorequery) Console.Write(i + " "); // Output: 97 92 81 http://msdn.microsoft.com/en-us/library/bb397933.aspx
Java & Lambdat (Java 8) Java 8:ssa tukea lambdoille (hieman rajatumpi kuin c++/c#) Arrays.sort(words, (first, second) -> Integer.compare(first.length(), second.length())); public static void repeatmessage(string text, int count) Runnable r = () -> for (int i = 0; i < count; i++) System.out.println(text); Thread.yield(); ; new Thread(r).start();
Geneerisyys ja mallit (mallineet, sapluunat ) (generics & templates) Perintä ja polymorfismi: kantaluokkaosoittimen/viittee Esim. funktiolle kelpaa mikä tahansa kantaluokan tyy Periytymissuhdevaatimus ei aina hyvä asia Mallin idea: Koodissa yksi tai useampi auki jätetty tyyppiparametri auki jätetty tyyppi kiinnittämällä saadaan todellista koodia Malli on mahdollista instantioida useita kertoja useita sam TIE-20200 Samuel Lahtinen 24
Aihiot, mallineet Mallit mahdollistavat geneerisen ohjelmoinnin (gener Auki jätetyn tyypin käyttö koodissa määrää sen omina Sijoitus sijoitusoperaattori oltava Arvoparametrina kopiorakentaja oltava Jäsenfunktiokutsu ko. jäsenfunktio löydyttävä Mallia käytettäessä ominaisuusvaatimukset eivät tote Mallia suunniteltaessa hyvä minimoida ominaisuusva TIE-20200 Samuel Lahtinen 25
Funktiomallit (function template) Malli funktioille, joissa tyyppejä (normaalisti parametreja) jätetty auki Listaus 9.5 s. 233 (9.5 s. 259): 1 template<typename T> // Tai template <class T> (identtinen) 2 T min(t p1, T p2) 3 4 T tulos; 5 if(p1 < p2) 6 7 tulos = p1; 8 else 9 tulos = p2; 10 11 return tulos; 12 Funktiomalli instantioidaan automaattisesti kutsuttaessa (mahdollista myös kertoa tyyppi ekp min(1,2) int min(int p1, int p2) min( a, e ) char min(char p1, char p2) TIE-20200 Samuel Lahtinen 26
Luokkamallit (class template) Malli luokille, joissa tyyppejä jätetty auki Itse malli ei ole luokka, vaan siitä tehdyt instanssit Luokkamallit instantioitava aina ekplisiittisesti: Jokainen instanssi oma tyyppinsä, jotka eivät ole keskenää TIE-20200 Samuel Lahtinen 27
Luokkamalli-esimerkki // hieno luokkamalli, joka laskee minkä tahansa olioiden/muuttujien // instanssien määrän template< typename T > class Laskuri public: Laskuri(): laskuri_() ~Laskuri() void lisaa( T stat ); void tulosta( std::ostream& out ) const; T annayleisin() const; private: typedef map< T, long > IRegister; typedef typename IRegister::const_iterator citerator; IRegister laskuri_; ; TIE-20200 Samuel Lahtinen 28
Luokkamallin toteutukset template< typename T > void Laskuri< T >::lisaa( T stat ) laskuri_[ stat ]++; template< typename T > void Laskuri< T >::tulosta( std::ostream& out ) const out << "kutakin tyyppiä oli (tyyppi - määrä): " << endl; for( citerator it = laskuri_.begin(); it!= laskuri_.end(); ++ it ) out << "'" << it->first << "' - " << it->second << endl; template< typename T > T Laskuri< T >::annayleisin() const if( laskuri_.empty() ) throw std::out_of_range( "Laskuri on tyhjä" ); citerator yleisin = laskuri_.begin(); for( citerator it = laskuri_.begin(); it!= laskuri_.end(); ++ it ) if( yleisin->second < it->second ) yleisin = it; return yleisin->first; TIE-20200 Samuel Lahtinen 29
Luokkamallin käyttö int main() Laskuri< char > chars; cout << "kirjoittele jotain ja paina enter..." << endl; string temp; std::getline( std::cin, temp ); if( std::cin.eof() )std::cout<< "EOF!!!!" << endl; for( unsigned i( 0 ); i < temp.size(); ++i ) chars.lisaa( temp.at( i ) ); chars.tulosta( cout ); cout << "yleisin merkki oli: " << chars.annayleisin() << endl; cout << "Sama murtoluvuilla" << endl; Murtoluku m1( 5,6); Murtoluku m2( 5,6); Murtoluku m3( 5,9); Murtoluku m4( 1,6); Murtoluku m5( 5,9); Murtoluku m6( 5,12); Murtoluku m7( 5,6); Murtoluku m8( 2,3); Laskuri< Murtoluku > murtoluvut; murtoluvut.lisaa( m1 ); murtoluvut.lisaa( m2 ); murtoluvut.lisaa( m4 ); murtoluvut.lisaa( m5 ); murtoluvut.lisaa( m6 ); murtoluvut.lisaa( m7 ); murtoluvut.lisaa( m8 ); murtoluvut.tulosta( cout ); TIE-20200 Samuel Lahtinen 30
Tyyppiparametrien vaatimukset Mitä ominaisuuksia mallin tyyppiparametrilla pitää olla Malli ei kerro sitä eksplisiittisesti Ainoa vaatimus: mallin koodin on käännyttävä Ongelma: koodin vaatimukset vaikeasti näkyvissä Ohjeita geneeriseen ohjelmointiin: Tyyppiparametrien vaatimukset dokumentoitava selkeästi Vaatimusten määrä minimoitava yleiskäyttöisyyden lisäämis Tiedettävä kääntäjän kulissien takana tekemän koodin vaa TIE-20200 Samuel Lahtinen 31
Yhteenveto Opittiin, että isot perintähierarkiat ja sun muut eivät aina ole oikea tapa lähestyä asioita Saatiin pakollinen mainosspämmi funktionaalisista kielistä Lambdat, funktionaalista ohjelmointia ei-funktionaalisella kielellä? (Mitä eroja puhtaaseen funktionaalisuuteen?) Opittiin lambdojen perusidea ja käyttö Kertauksena templatet, muistuksena yleiskäyttöisyydestä ja sen hyödyistä/vaaroista