V tehničnem uvodnem nagovoru za JavaOne 2013 je Mark Reinhold, glavni arhitekt skupine Java Platform Group pri podjetju Oracle, lambda izraze opisal kot največjo nadgradnjo programskega modela Java. kdajkoli. Čeprav obstaja veliko aplikacij za lambda izraze, se ta članek osredotoča na poseben primer, ki se pogosto pojavlja v matematičnih aplikacijah; in sicer potreba po posredovanju funkcije algoritmu.
Kot sivolaki geek sem v preteklih letih programiral v številnih jezikih in že od različice 1.1 intenzivno programiram na Javi. Ko sem začel delati z računalniki, skoraj nihče ni diplomiral iz računalništva. Računalniški strokovnjaki so prihajali večinoma iz drugih disciplin, kot so elektrotehnika, fizika, poslovanje in matematika. V svojem nekdanjem življenju sem bil matematik in zato ne bi smelo presenetiti, da je bil moj prvotni pogled na računalnik pogled na velikanski programirljiv kalkulator. Z leti sem precej razširil svoj pogled na računalnike, vendar vseeno pozdravljam možnost dela na aplikacijah, ki vključujejo nekatere vidike matematike.
Številne matematične aplikacije zahtevajo, da se funkcija posreduje kot parameter algoritmu. Primeri iz univerzitetne algebre in osnovnega računa vključujejo reševanje enačbe ali računanje integrala funkcije. Že več kot 15 let je bila Java izbrani programski jezik za večino aplikacij, vendar je bil prvi jezik, ki sem ga pogosto uporabljal, in mi ni dovolil, da funkcijo (tehnično kazalec ali sklic na funkcijo) prenesem kot na enostaven in enostaven način. Ta pomanjkljivost se bo kmalu spremenila s prihajajočo izdajo Jave 8.
Moč lambda izrazov seže precej dlje od primera enkratne uporabe, vendar bi vam preučevanje različnih izvedb istega primera moralo dati trden občutek, kako bodo lambde koristile vašim programom Java. V tem članku bom z običajnim primerom opisal težavo, nato ponudil rešitve, napisane v jeziku C ++, Java pred lambda izrazi in Java z lambda izrazi. Upoštevajte, da za razumevanje in razumevanje glavnih točk tega članka ni potrebno močno znanje matematike.
Spoznavanje lambdas
Lambda izrazi, znani tudi kot zapiranja, funkcijski dobesedni izrazi ali preprosto lambda, opisujejo nabor funkcij, opredeljenih v zahtevi za specifikacijo Java (JSR) 335. Manj formalni / bolj berljivi uvodi za lambda izraze so na voljo v razdelku najnovejše različice Vadnica za Java in v nekaj člankih Briana Goetza, "Stanje lambde" in "Stanje lambde: izdaja knjižnic." Ti viri opisujejo sintakso lambda izrazov in ponujajo primere primerov uporabe lambda izrazov. Če želite več informacij o lambda izrazih v Javi 8, si oglejte tehnični osrednji naslov Marka Reinholda za JavaOne 2013.
Lambda izrazi v matematičnem primeru
Primer, uporabljen v tem članku, je Simpsonovo pravilo iz osnovnega računa. Simpsonovo pravilo ali natančneje sestavljeno Simpsonovo pravilo je numerična tehnika integracije za približevanje določenemu integralu. Ne skrbite, če pojma a ne poznate določen integral; v resnici morate razumeti, da je Simpsonovo pravilo algoritem, ki izračuna realno število na podlagi štirih parametrov:
- Funkcija, ki jo želimo integrirati.
- Dve realni številki
a
inb
ki predstavljajo končne točke intervala[a, b]
na pravi številčni črti. (Upoštevajte, da mora biti zgoraj omenjena funkcija v tem intervalu neprekinjena.) - Celo celo število
n
ki določa število podintervalov. Pri izvajanju Simpsonovega pravila delimo interval[a, b]
vn
podintervali.
Za poenostavitev predstavitve se osredotočimo na programski vmesnik in ne na podrobnosti izvedbe. (Resnično upam, da nam bo ta pristop obšel argumente o najboljšem ali najučinkovitejšem načinu izvajanja Simpsonovega pravila, ki ni v središču tega članka.) Uporabili bomo tip dvojno
za parametre a
in b
, in uporabili bomo tip int
za parameter n
. Funkcija, ki jo želite integrirati, ima en sam parameter dvojno
in vrne vrednost tipa dvojno
.
Parametri funkcije v C ++
Za osnovo za primerjavo začnimo s specifikacijo C ++. Ko posredujem funkcijo kot parameter v jeziku C ++, običajno raje podpišem podpis parametra funkcije z uporabo a typedef
. Seznam 1 prikazuje poimenovano datoteko glave C ++ simpson.h
ki določa oba typedef
za parameter funkcije in programski vmesnik za funkcijo C ++ z imenom integrirati
. Telo funkcije za integrirati
je vsebovana v datoteki izvorne kode C ++ simpson.cpp
(ni prikazano) in zagotavlja izvajanje Simpsonovega pravila.
Seznam 1. Datoteka glave C ++ za Simpsonovo pravilo
#if! definirano (SIMPSON_H) #define SIMPSON_H #include using namespace std; typedef double DoubleFunction (dvojni x); dvojna integracija (DoubleFunction f, dvojna a, dvojna b, int n) metanje (neveljaven_argument); #endif
Klicanje integrirati
je v C ++ enostaven. Kot preprost primer, predpostavimo, da ste želeli uporabiti Simpsonovo pravilo za približanje integrala sinus funkcija iz 0
do π (PI
) z uporabo 30
podintervali. (Vsakdo, ki je opravil račun I, bi moral biti sposoben natančno izračunati odgovor brez pomoči kalkulatorja, kar je dober testni primer za integrirati
ob predpostavki, da ste imeli vključena ustrezne datoteke glave, kot so in
"simpson.h"
, lahko bi poklicali funkcijo integrirati
kot je prikazano v seznamu 2.
Seznam 2. Klic C ++ k integraciji funkcije
dvojni rezultat = integriraj (sin, 0, M_PI, 30);
To je vse. V C ++ prenesete sinus deluje tako enostavno, kot da prenesete ostale tri parametre.
Še en primer
Namesto Simpsonovega pravila bi lahko prav tako enostavno uporabil metodo bisekcije (aka algoritem bisekcije) za reševanje enačbe oblike f (x) = 0. Dejansko izvorna koda tega članka vključuje preproste izvedbe tako Simpsonovega pravila kot metode bisekcije.
Prenos Prenesite primere izvorne kode Java za ta članek. Ustvaril John I. Moore za JavaWorldJava brez lambda izrazov
Zdaj pa poglejmo, kako je mogoče v Javi določiti Simpsonovo pravilo. Ne glede na to, ali uporabljamo lambda izraze, namesto C ++ uporabljamo vmesnik Java, prikazan v seznamu 3 typedef
da podate podpis funkcijskega parametra.
Seznam 3. Vmesnik Java za funkcijski parameter
javni vmesnik DoubleFunction {javni dvojnik f (dvojni x); }
Za izvajanje Simpsonovega pravila v Javi ustvarimo razred z imenom Simpson
ki vsebuje metodo, vključiti
, s štirimi parametri, podobnimi tistim, ki smo jih naredili v C ++. Kot pri mnogih samostojnih matematičnih metodah (glej na primer java.lang.Math
), bomo naredili vključiti
statična metoda. Metoda vključiti
je določeno na naslednji način:
Seznam 4. Podpis Java za metodo integracije v razredu Simpson
javna statična dvojna integracija (DoubleFunction df, double a, double b, int n)
Vse, kar smo doslej naredili v Javi, je neodvisno od tega, ali bomo uporabljali lambda izraze ali ne. Glavna razlika pri lambda izrazih je v tem, kako posredujemo parametre (natančneje, kako posredujemo funkcijski parameter) v klicu metode vključiti
. Najprej bom ponazoril, kako bi to počeli v različicah Jave pred različico 8; torej brez lambda izrazov. Tako kot v primeru C ++, predpostavimo, da želimo približati integralu sinus funkcija iz 0
do π (PI
) z uporabo 30
podintervali.
Uporaba vzorca adapterja za funkcijo sinus
V Javi imamo izvedbo sinus funkcija na voljo v java.lang.Math
, vendar z različicami Java pred Java 8 ni preprostega, neposrednega načina, kako to prenesti sinus funkcijo metode integrirati
v razredu Simpson
. Eden od pristopov je uporaba vzorca adapterja. V tem primeru bi napisali preprost razred adapterja, ki implementira DoubleFunction
vmesnik in ga prilagodi klicu sinus , kot je prikazano v seznamu 5.
Seznam 5. Razred adapterja za metodo Math.sin
uvoz com.softmoore.math.DoubleFunction; javni razred DoubleFunctionSineAdapter izvaja DoubleFunction {javni dvojni f (dvojni x) {return Math.sin (x); }}
Z uporabo tega razreda adapterjev lahko zdaj pokličemo integrirati
metoda pouka Simpson
kot je prikazano v seznamu 6.
Seznam 6. Uporaba razreda adapterja za klicanje metode Simpson.integrate
DoubleFunctionSineAdapter sine = novo DoubleFunctionSineAdapter (); dvojni rezultat = Simpson.integrate (sinus, 0, Math.PI, 30);
Ustavimo se za trenutek in primerjajmo, kaj je bilo potrebno za klic integrirati
v C ++ v primerjavi s tistim, kar se je zahtevalo v prejšnjih različicah Jave. S C ++ smo preprosto poklicali integrirati
, podaja štiri parametre. Z Javo smo morali ustvariti nov razred vmesnika in nato narediti primerek tega razreda, da smo lahko izvedli klic. Če bi radi integrirali več funkcij, bi morali za vsako napisati razred adapterja.
Lahko bi skrajšali kodo, potrebno za klic integrirati
rahlo iz dveh stavkov Java na enega z ustvarjanjem novega primerka razreda adapterja v klicu vključiti
. Uporaba anonimnega razreda namesto ustvarjanja ločenega razreda adapterja bi bil še en način za nekoliko zmanjšanje celotnega napora, kot je prikazano v seznamu 7.
Seznam 7. Uporaba anonimnega razreda za klicanje metode Simpson.integrate
DoubleFunction sineAdapter = novo DoubleFunction () {javni dvojni f (dvojni x) {return Math.sin (x); }}; dvojni rezultat = Simpson.integrate (sineAdapter, 0, Math.PI, 30);
Brez lambda izrazov vidite v seznamu 7 približno najmanjšo količino kode, ki bi jo lahko napisali v Javi za klic integrirati
metoda, vendar je še vedno precej okornejša od tiste, ki je bila potrebna za C ++. Tudi z uporabo anonimnih predavanj nisem tako zadovoljen, čeprav sem jih v preteklosti že veliko uporabljal. Sintaksa mi ni všeč in sem vedno menila, da gre za nespreten, a nujen kramp v jeziku Java.
Java z lambda izrazi in funkcionalnimi vmesniki
Zdaj pa poglejmo, kako bi lahko uporabili lambda izraze v Javi 8 za poenostavitev klica vključiti
v Javi. Ker vmesnik DoubleFunction
zahteva izvajanje samo ene metode, je kandidat za lambda izraze. Če vnaprej vemo, da bomo uporabljali lambda izraze, lahko vmesnik označimo z @FunctionalInterface
, nov pripis za Javo 8, ki pravi, da imamo funkcijski vmesnik. Upoštevajte, da ta pripis ni obvezen, vendar nam daje dodaten pregled, da je vse skladno, podobno kot @Override
v prejšnjih različicah Jave.
Sintaksa lambda izraza je seznam argumentov, zajet v oklepajih, žeton puščice (->
) in funkcijsko telo. Telo je lahko bodisi blok stavkov (zaprt v oklepajih) bodisi en izraz. Seznam 8 prikazuje lambda izraz, ki implementira vmesnik DoubleFunction
in se nato posreduje metodi vključiti
.
Seznam 8. Uporaba lambda izraza za klicanje metode Simpson.integrate
DoubleFunction sinus = (dvojni x) -> Math.sin (x); dvojni rezultat = Simpson.integrate (sinus, 0, Math.PI, 30);
Upoštevajte, da nam ni bilo treba pisati razreda adapterja ali ustvariti primerka anonimnega razreda. Upoštevajte tudi, da bi lahko zgoraj zapisali v enem samem stavku, tako da nadomestimo sam lambda izraz, (dvojni x) -> Math.sin (x)
, za parameter sinus
v drugi zgornji izjavi odpravi prvo izjavo. Zdaj se približujemo preprosti skladnji, ki smo jo imeli v C ++. Ampak počakaj! Še več je!
Ime funkcionalnega vmesnika ni del lambda izraza, vendar ga je mogoče sklepati glede na kontekst. Tip dvojno
za parameter lambda izraza lahko sklepamo tudi iz konteksta. Če je v lambda izrazu samo en parameter, lahko oklepaje izpustimo. Tako lahko kodo skrajšamo na klicno metodo vključiti
v eno vrstico kode, kot je prikazano na seznamu 9.
Seznam 9. Nadomestna oblika lambda izraza v klicu Simpson.integrate
dvojni rezultat = Simpson.integrate (x -> Math.sin (x), 0, Math.PI, 30);
Ampak počakaj! Še več jih je!
Sklici na metode v Javi 8
Druga sorodna značilnost Java 8 je nekaj, kar se imenuje a sklic na metodo, ki nam omogoča, da se poimenujemo na obstoječo metodo. Namesto lambda izrazov se lahko uporabljajo sklici na metode, če izpolnjujejo zahteve funkcionalnega vmesnika. Kot je opisano v virih, obstaja več različnih vrst referenc na metode, od katerih ima vsaka nekoliko drugačno sintakso. Za statične metode je sintaksa Classname :: methodName
. Zato lahko s sklicem na metodo pokličemo vključiti
metoda v Javi tako preprosto kot v C ++. Primerjajte klic Java 8, prikazan v spodnjem seznamu 10, z originalnim klicem C ++, prikazan v zgornjem seznamu 2.
Seznam 10. Uporaba sklica na metodo za klic Simpson.integrate
dvojni rezultat = Simpson.integrate (Math :: sin, 0, Math.PI, 30);