Στατική Συμβολική Παραγώγιση Λάμδα Εκφράσεων στην C++

26
Στατική Συμβολική Παραγώγιση Λάμδα Εκφράσεων στην C++ Η βιβλιοθήκη λ&d++.

description

Στατική Συμβολική Παραγώγιση Λάμδα Εκφράσεων στην C++. Η βιβλιοθήκη λ& d++. λάμδα εκφράσεις>τί είναι;>στον λ-λογισμό. Τί είναι λοιπόν οι λάμδα εκφράσεις; - PowerPoint PPT Presentation

Transcript of Στατική Συμβολική Παραγώγιση Λάμδα Εκφράσεων στην C++

Page 1: Στατική Συμβολική Παραγώγιση Λάμδα Εκφράσεων στην  C++

Στατική Συμβολική ΠαραγώγισηΛάμδα Εκφράσεων στην C++

Η βιβλιοθήκη λ&d++.

Page 2: Στατική Συμβολική Παραγώγιση Λάμδα Εκφράσεων στην  C++

λάμδα εκφράσεις>τί είναι;>στον λ-λογισμό

Τί είναι λοιπόν οι λάμδα εκφράσεις;

Ο όρος λάμδα έκφραση προέρχεται από τον λάμδα λογισμό του Alonzo Church ο οποίος είναι ένα υπολογιστικό μοντέλο αντίστοιχο των μηχανών Turing, δηλαδή μια μαθηματική θεωρεία που σκοπό έχει να ορίσει τί είναι υπολογισμός και να ερευνήσει το τί είναι υπολογίσιμο και τί όχι.

Ο λ-λογισμός ορίζει την γλώσσα των λ-όρων σαν την γλώσσα που παράγεται από τον κανόνα:<λ-όρος>::=<μεταβλητή>|(λ<μεταβλητή>.<λ-όρος>)|(<λ-όρος> <λ-όρος>)

Ή αλλιώς:

•Μια μεταβλητή είναι λ-όρος.•Αν x μεταβλητή και t λ-όρος τότε το (λx.t) είναι λ-όρος.•Αν t και u είναι λ-όροι τότε (t u) είναι λ-όρος.

Οπότε το (λx.x) το ((λx.(y x)) (λx.x)) και το (λf.(λx.(f (λy.((x x) y)))))(λx.(f ((λy.((x x) y)))))είναι όλα λ-όροι.

Page 3: Στατική Συμβολική Παραγώγιση Λάμδα Εκφράσεων στην  C++

λάμδα εκφράσεις>τί είναι;>στον λ-λογισμό

Οι λ-όροι της 2ης μορφής ονομάζονται λάμδα συναρτήσεις,ή λάμδα εκφράσεις, ή συναρτησιακές αφαιρέσεις, ή ανώνυμες συναρτήσεις.

Αλλά αυτή είναι η μορφή των λάμδα εκφράσεων στον λ-λογισμό.

Page 4: Στατική Συμβολική Παραγώγιση Λάμδα Εκφράσεων στην  C++

λάμδα εκφράσεις>τί είναι;>στις γλώσσες προγραμματισμού

Στις γλώσσες προγραμματισμού οι λ-εκφράσεις είναι απλές συναρτήσεις με 2 κυρίως διαφορές:1.Δεν έχουν όνομα!2.Ο ορισμός τους μπορεί να εμφανίζεται μέσα σε μια έκφραση.

Page 5: Στατική Συμβολική Παραγώγιση Λάμδα Εκφράσεων στην  C++

βιβλιοθήκη>χρήση>includes & δηλώσεις

Για να χρησιμοποιήσουμε την βιβλιοθήκη:1. Συμπεριλαμβάνουμε το κατάλληλο αρχείο επικεφαλίδας:

#include "λnd++.h"2. Δηλώνουμε μια μεταβλητή τύπου “Arg”:

Arg x;(ο τύπος Arg είναι ένας τύπος πολυμορφικής ταυτοτικής συνάρτησης Τ->Τ)

3. Γράφουμε την λάμδα συνάρτηση που θέλουμε. π.χ.: x+1

ή κάτι πιο περίπλοκο όπως:4*x*x+3*x+2.5

ή(2*x+2.2)*(cos(-x)+x*x*sin((-x)/(x)+5))/(3*cos(3.2*x+1)-

1e-10)

Page 6: Στατική Συμβολική Παραγώγιση Λάμδα Εκφράσεων στην  C++

βιβλιοθήκη>χρήση>βασική χρήση

Βεβαίως το να δηλώσουμε μια λάμδα συνάρτηση δεν είναι από μόνο του πολύ χρήσιμο. Πιθανότατα θέλουμε...να την εφαρμόσουμε κάπου,Να κάνουμε κάτι με την τιμή που θα επιστρέψει,Και όλα αυτά μέσα σε ένα πρόγραμμα.

#include <iostream>using std::wcout;using std::endl;

#include "λnd++.h"

int main(){ Arg x;

wcout << (4*x*x+3*x+2.5)(-2.5) << endl;

return 0;} // end function main

wcout << (4*x*x+3*x+2.5)(-2.5) << endl;

4*x*x+3*x+2.5(4*x*x+3*x+2.5)(-2.5)

Page 7: Στατική Συμβολική Παραγώγιση Λάμδα Εκφράσεων στην  C++

βιβλιοθήκη>χρήση>απόδωση ονόματος

Επίσης μπορεί να θέλουμε να της δώσουμε ένα όνομα ώστε να την χρησιμοποιήσουμε πολλές φορές:

#include <iostream>using std::wcout;using std::endl;

#include "λnd++.h"

int main(){ Arg x; auto f = 4*x*x+3*x+2.5;

wcout << f(-2.5) << endl; wcout << f(1) << endl;

return 0;} // end function main

Page 8: Στατική Συμβολική Παραγώγιση Λάμδα Εκφράσεων στην  C++

auto i = 1; int i = 1;auto x = 2.2; double x = 2.2;auto y = 2.2f; float y = 2.2f;

Αναγκαίο καθώς κάθε λάμδα έκφραση έχει έναν μοναδικό τύπο που κωδικοποιεί την εσωτερική δομή της έκφρασης

βιβλιοθήκη>χρήση>απόδωση ονόματος>ο καθοριστής τύπου auto

Page 9: Στατική Συμβολική Παραγώγιση Λάμδα Εκφράσεων στην  C++

βιβλιοθήκη>χρήση>αιχμαλωσία

Και αν θέλουμε να χρησιμοποιήσουμε στο σώμα της λάμδα συνάρτησης κάποια μεταβλητή από μια περιβάλουσα εμβέλεια;

#include <iostream>using std::wcout;using std::wcin;using std::endl;

#include "λnd++.h"

const int a = 4;const int b = 3;

int main(){ Arg x; float c;

wcout << "Type a number: "; wcin >> c; auto f = a*x*x+b*x+c;

wcout << f(-2.5) << endl; wcout << f(1) << endl;

return 0;} // end function main

Απλά την χρησιμοποιούμε!

Οι τιμές των ‘αιχμαλοτισμένων’ μεταβλητών αντιγράφονται στο αντικείμενο που αναπαρτιστά την λ-έκφραση και μπορούν να χρησιμοποιηθούν από αυτό.

Προς το παρόν δεν υποστιρίζεται ‘αιχμαλότιση’ κατ’ αναφορά.

Page 10: Στατική Συμβολική Παραγώγιση Λάμδα Εκφράσεων στην  C++

βιβλιοθήκη>χρήση>επιστροφή από συνάρτηση

Έτσι όμως μπορείς να επιστρέψεις με ασφάλεια μια λ-έκφραση από μια άλλη συνάρτηση. π.χ.:

#include <iostream>using std::wcout;using std::endl;

#include "λnd++.h"

template<typename T>auto sub(T x)->decltype(x-Arg()){ Arg y; return x-y;} // end function sub

int main(){ wcout << sub(2)(5) << endl; wcout << sub(2.2)(1) << endl; wcout << sub(2)(0.5) << endl; return 0;} // end function main

Page 11: Στατική Συμβολική Παραγώγιση Λάμδα Εκφράσεων στην  C++

βιβλιοθήκη>χρήση>πολυμορφισμός

Μέχρι τώρα έχουμε δεί ότι μπορούμε να εφαρμόσουμε μια λ-έκφραση σε αντικείμενα τύπου int και double.

Οι λ-συναρτήσεις μας όμως χρησιμοποιούν παραμετρικό πολυμορφισμό και μπορούμε να τις εφαρμόσουμε σε οποιοδήποτε αντικείμενο έχει νόημα για το εκάστοτε σώμα τους.

Τα λάθη ασφαλώς εντοπίζονται στον χρόνο μεταγλώττισης!

π.χ.:complex<int> i(0,1);auto f = 3*x;

unsigned long long ull = f(2ull);float fl = f(2.5f);complex<int> ic = f(2+3*i);Vector2D<> dv = f(Vector2D<>(-2.0,5));

αλλά:complex<float> fc = f(2+3*i);// illegal!

διότι κάποιος αποφάσισε ότι το παρακάτω είναι illegal!

complex<float> fc2 = 3*(2+3*i);// illegal!

Page 12: Στατική Συμβολική Παραγώγιση Λάμδα Εκφράσεων στην  C++

βιβλιοθήκη>χρήση>παραγώγιση

Για να παραγωγίσουμε μια λ-έκφραση απλώς την δίνουμε σαν όρισμα στην συνάρτηση Differentiate.

την οποία μπορούμε να εφαρμόσουμε κάπου:Differentiate(cos(3*x+2))(0.01);Differentiate(f)(0.01);

ή της δώσουμε ένα όνομα:auto g = Differentiate(cos(3*x+2));auto h = Differentiate(f);

Το αποτέλεσμα είναι μια λ-έκφραση της παραγώγου.

μπορεί να είναι ανώνυμη:Differentiate(cos(3*x+2))

ή να της έχουμε δώσει ένα όνομα:auto f = cos(3*x+2);// ..Differentiate(f)

Page 13: Στατική Συμβολική Παραγώγιση Λάμδα Εκφράσεων στην  C++

#include <iostream>using std::wcout;using std::endl;

#include "λnd++.h"

int main(){ Arg x; auto f = cos(3*x+2);

wcout << Differentiate(cos(3*x+2))(0.01) << endl; wcout << Differentiate(f)(0.01) << endl;

auto g = Differentiate(cos(3*x+2)); auto h = Differentiate(f);

wcout << g(0.01) << endl; wcout << h(0.01) << endl;

return 0;} // end function main

βιβλιοθήκη>χρήση>παραγώγιση>ονοκληρωμένο πρόγραμμα

Page 14: Στατική Συμβολική Παραγώγιση Λάμδα Εκφράσεων στην  C++

βιβλιοθήκη>γιατί στατικά;

Τί κερδίζουμε κάνοντας την παραγώγιση στατικά;

Ταχύτατη εκτέλεση!

Όχι μόνο το πρόγραμμα δεν επιβαρύνεται με την ανάγκη να εκτελέσει τους μετασχηματισμούς του δένρου έκφρασης στον χρόνο εκτέλεσης αλλά ο κώδικας της παραγώγου περνάει από τους εξαιρετικά ικανούς και επιθετικούς βελτιστοποιητές των σύγχρονων μεταγλωττιστών!

Παραδείγματα παραγόμενου κώδικα (g++ με βελτιστοποιήσεις ενεργές):

Page 15: Στατική Συμβολική Παραγώγιση Λάμδα Εκφράσεων στην  C++

βιβλιοθήκη>γιατί στατικά;>σταθερές εκφράσεις

Σταθερές εκφράσεις:το

wcout << (cos(2*x+3))(3) << endl;παράγειmovsd xmm1, QWORD PTR .LC0[rip] #,lea rcx, _ZSt5wcout[rip] #,call _ZNSt13basic_ostreamIwSt11char_traitsIwEE9_M_insertIdEERS2_T_ #mov rcx, rax #, D.28217call _ZSt4endlIwSt11char_traitsIwEERSt13basic_ostreamIT_T0_ES6_ #

Page 16: Στατική Συμβολική Παραγώγιση Λάμδα Εκφράσεων στην  C++

Μεταβλητές:

double c;wcin >> c;

lea rdx, 40[rsp] #,lea rcx, _ZSt4wcin[rip] #,call_ZNSt13basic_istreamIwSt11char_traitsIwEE10_M_extractIdEERS2_RT_

wcout << (cos(2*x+3))(c) << endl;

movsd xmm0, QWORD PTR 40[rsp] # tmp68, caddsd xmm0, xmm0 # tmp68, tmp68addsd xmm0, QWORD PTR .LC0[rip] # tmp68,call cos #lea rcx, _ZSt5wcout[rip] #,movapd xmm1, xmm0 # D.28230,call_ZNSt13basic_ostreamIwSt11char_traitsIwEE9_M_insertIdEERS2_T_ #mov rcx, rax #, D.28241call _ZSt4endlIwSt11char_traitsIwEERSt13basic_ostreamIT_T0_ES6_

#

βιβλιοθήκη>χρήση>includes>τιμές εισαγόμενες στον χρόνο εκτέλεσης

Page 17: Στατική Συμβολική Παραγώγιση Λάμδα Εκφράσεων στην  C++

Μεταβλητές:

Και οι γραμμές που μας απασχολούν:

movsd xmm0, QWORD PTR 40[rsp] # tmp68, caddsd xmm0, xmm0 # tmp68, tmp68addsd xmm0, QWORD PTR .LC0[rip] # tmp68,call cos #

είναι ταυτόσημες με αυτές που παράγονται από τον γραμμένο-με-το-χέρι κώδικα:

inline double f(double x){ return cos(2*x+3);}

// ...

wcout << f(c) << endl;

βιβλιοθήκη>χρήση>includes>τιμές εισαγόμενες στον χρόνο εκτέλεσης

Page 18: Στατική Συμβολική Παραγώγιση Λάμδα Εκφράσεων στην  C++

βιβλιοθήκη>εφαρμογές>γενικά

Και πού χρησιμεύουν οι λ-εκφράσεις και η παραγώγισή τους;

Γενικά οπουδήποτε γνωρίζουμε τον τύπο μιας συνάρτησης στον χρόνο μεταγλώττισης, χρειαζόμαστε την παράγωγό της και δεν θέλουμε να βγούμε από το περιβάλλον ανάπτυξής μας για να κάνουμε αλλού τον υπολογισμό!

Αλλά ας δούμε 2 συγγεκριμένα παραδείγματα:1. Λακωνικές λάμδα εκφράσεις για τους αλγορίθμους της STL.2. Κλήση μιας Newton-Raphson χώρίς να πιάσουμε μολύβι ή να

ανοίξουμε το Mathematica!3. Συνδυασμένες γραφικές παραστάσεις συναρτήσεων και των

παραγώγων τους ή καλύτερα σχεδίαση εφαπτομένων σε σημεία που επιλέγει ο χρήστης (δεν πρόλαβα να φτιάξω το παράδειγμα)

Page 19: Στατική Συμβολική Παραγώγιση Λάμδα Εκφράσεων στην  C++

βιβλιοθήκη>εφαρμογές>λακωνικές λάμδα εκφράσεις

Λακωνικές λάμδα εκφράσεις για τους αλγορίθμους της STL.

Όταν η έκφρασή σου είναι αρκετά μικρή, το συντακτικό ‘overhead’ των C++11 lambdas δεν είναι αποδεκτό:

transform(begin(v1),end(v1),begin(v2),x-0.5);

VS

transform(begin(v1),end(v1),begin(v2),[](int x){return x-0.5;});

Page 20: Στατική Συμβολική Παραγώγιση Λάμδα Εκφράσεων στην  C++

βιβλιοθήκη>εφαρμογές>newton-raphson>πριν

Παίρνουμε την παλιά καλή υλοποίηση της Newton-Raphson από την εργασία του 3ου εξαμήνου...

double Newton_Raphson(double (*f)(double),double (*df)(double), double new_x,double accuracy,unsigned int *iterations){ double old_x; *iterations = 0; do { (*iterations)++; old_x = new_x; new_x = old_x-(*f)(old_x)/(*df)(old_x); } while(fabs(new_x-old_x)>accuracy); return new_x;}

No comments for the pointer parameter!These were the dark ages when we wrote in C!

Page 21: Στατική Συμβολική Παραγώγιση Λάμδα Εκφράσεων στην  C++

βιβλιοθήκη>εφαρμογές>newton-raphson>μετά

...και την τροποποιούμε για να χρησιμοποιεί την λ&d++:

template<typename Func>double Newton_Raphson(Func f,double new_x,double accuracy, unsigned int *iterations){ double old_x; *iterations = 0; do { (*iterations)++; old_x = new_x; new_x = old_x-f(old_x)/Differentiate(f)(old_x); } while(fabs(new_x-old_x)>accuracy); return new_x;}

Βεβαίως μπορούσαμε να κάνουμε και άλλες αλλαγές όπως να επιτρέπουμε στον καλούντα να επιλέγει την αριθμιτική που θα χρησιμοποιηθεί...

Page 22: Στατική Συμβολική Παραγώγιση Λάμδα Εκφράσεων στην  C++

βιβλιοθήκη>εφαρμογές>newton-raphson>κλήση

και τέλος υπολογίζουμε:

int main(){ Arg x; unsigned int iterations = 0;

wcout << L"root = "<< Newton_Raphson((x-3)*(x+2),2,1e-6,&iterations)

<< L" after " << iterations << L" iterations." << endl;

wcout << L"root = "<< Newton_Raphson((x-3)*(x+2)*(x-2.2)*(x+5)*(x-7)*3.3,

10,1e-6,&iterations) << L" after " << iterations << L" iterations." << endl;

return 0;} // end function main

Ποιός θέλει να παραγωγίσει την cos(sin(cos(x*sin(x)-x)+2))*(x+5)*(x-7)*3.3-2.5 με το χέρι?!!

Page 23: Στατική Συμβολική Παραγώγιση Λάμδα Εκφράσεων στην  C++

βιβλιοθήκη>χρήση>includes

Βεβαίως όπως σε όλες τις βιβλιοθήκες υπάρχουν περιορισμοί και περιθόριο για μελλοντική βελτίωση.

Παραδείγματος χάρη πώς θα εκφράσεις μια λάμδα συνάρτηση που επιστρέφει πάντα την ίδια σταθερή τιμή;

Page 24: Στατική Συμβολική Παραγώγιση Λάμδα Εκφράσεων στην  C++

References/Further Reading:

http://matt.might.net/articles/static-closures-in-c-plus-plus/ domain specific embedded languages in C++

http://www.cplusplus.com/reference/std/complex/ complex library reference

ML for the Working Programmer by Lawrence C. Paulson §9.5

introduction to λ-calculus

http://www.boost.org/doc/libs/1_48_0/libs/numeric/ublas/doc/index.htm

Boost::uBLAS uses expression templates for efficient abstraction

http://www.boost.org/doc/libs/1_48_0/libs/spirit/doc/html/index.html

Boost::Spirit represents LL parsers as inline EBNF in C++

http://www.boost.org/doc/libs/1_48_0/libs/phoenix/doc/html/index.html

Boost::Phoenix has extensive support of polymorphic lambdas

http://www10.informatik.uni-erlangen.de/~pflaum/pflaum/ProSeminar/exprtmpl.html

original (?) Expression Templates article

http://www.boost.org/doc/libs/1_48_0/doc/html/lambda.html

Boost::Lambda provides support for lambdas as well

http://www.cc.gatech.edu/~yannis/fc++/fc++-sigplan.pdf The FC++ library supports functional programming in C++

C++ How to Program (5th Edition) by P.J. Deitel My first C++ book. A bit outdated.

Page 25: Στατική Συμβολική Παραγώγιση Λάμδα Εκφράσεων στην  C++

References/Further Reading:

Ivor Horton's Beginning Visual C++ 2010 My 2nd C++ book. Also covers C++/CLI

ISO/IEC 14882-2011 International Standard The current C++ standard

http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2009/n2927.pdf

Final proposal for C++0x Lambdas

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1720.html

Final proposal for static assertions

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2087.pdf

A Brief Introduction to Variadic Templates

http://www2.research.att.com/~bs/C++0xFAQ.html Stroustrup’s C++11 FAQ

Page 26: Στατική Συμβολική Παραγώγιση Λάμδα Εκφράσεων στην  C++

Ευχαριστώ!

Καλά Χριστούγεννα!