Techniniame pagrindiniame „JavaOne 2013“ pagrindiniame pranešime Markas Reinholdas, „Oracle“ „Java“ platformos grupės vyriausiasis architektas, apibūdino „lambda“ išraiškas kaip didžiausią „Java“ programavimo modelio atnaujinimą. kada nors. Nors lambda išraiškoms yra daugybė programų, šiame straipsnyje daugiausia dėmesio skiriama konkrečiam pavyzdžiui, kuris dažnai pasitaiko matematinėse programose; būtent poreikis perduoti funkciją algoritmui.
Būdamas žilaplaukis geekas per daugelį metų programavau daugybe kalbų ir nuo 1.1 versijos plačiai programavau „Java“. Kai pradėjau dirbti su kompiuteriais, beveik niekas nebuvo baigęs informatikos mokslų. Kompiuterių profesionalai daugiausia buvo iš kitų sričių, tokių kaip elektrotechnika, fizika, verslas ir matematika. Ankstesniame gyvenime buvau matematikas, todėl nieko keista, kad mano pradinis kompiuterio vaizdas buvo milžiniškas programuojamas skaičiuotuvas. Per daugelį metų aš labai išplėtiau savo požiūrį į kompiuterius, bet vis tiek džiaugiuosi galimybe dirbti su programomis, kurios apima tam tikrą matematikos aspektą.
Daugelis matematikos programų reikalauja, kad funkcija būtų perduota kaip parametras algoritmui. Kolegijos algebros ir pagrindinio skaičiavimo pavyzdžiai yra lygties sprendimas arba funkcijos integralo apskaičiavimas. Daugiau nei 15 metų „Java“ buvo mano pasirinkta programavimo kalba daugumai programų, tačiau tai buvo pirmoji dažnai naudojama kalba, kuri neleido man perduoti funkcijos (techniškai žymiklio ar nuorodos į funkciją) kaip parametrą paprastai ir paprastai. Šis trūkumas netrukus pasikeis, kai bus išleista „Java 8“.
„Lambda“ išraiškų galia viršija vienkartinio naudojimo atvejus, tačiau, tiriant įvairius to paties pavyzdžio variantus, turėtumėte suprasti, kaip „lambdas“ bus naudinga jūsų „Java“ programoms. Šiame straipsnyje aš naudosiu įprastą pavyzdį, kuris padės apibūdinti problemą, tada pateiksiu sprendimus, parašytus C ++, „Java“ prieš „lambda“ išraiškas ir „Java“ su „lambda“ išraiškomis. Atkreipkite dėmesį, kad norint suprasti ir įvertinti pagrindines šio straipsnio nuostatas, nebūtina turėti tvirtos matematikos žinios.
Sužinokite apie lambdas
„Lambda“ posakiai, dar vadinami uždarymo, funkcijų pažodžiui ar tiesiog „lambdas“, apibūdina funkcijų rinkinį, apibrėžtą „Java Specification Request“ (JSR) 335. Mažiau oficialūs / lengviau skaitomi lambda posakių įvadai pateikiami naujausios versijos skyriuje. „Java Tutorial“ ir keliuose Briano Goetzo straipsniuose „Lambda būklė“ ir „Lambda būsena: bibliotekų leidimas“. Šie šaltiniai apibūdina lambda išraiškų sintaksę ir pateikia naudojimo atvejų, kai lambda išraiškos yra taikomos, pavyzdžius. Norėdami sužinoti daugiau apie „lambda“ išraiškas „Java 8“, žiūrėkite Marko Reinholdo techninį pagrindinį „JavaOne 2013“ adresą.
Lambda išraiškos matematiniame pavyzdyje
Šiame straipsnyje naudojamas pavyzdys yra Simpsono taisyklė iš pagrindinio skaičiavimo. „Simpson“ taisyklė arba, tiksliau, „Composite Simpson“ taisyklė, yra skaitmeninė integravimo technika, skirta apytiksliai apibrėžtam integralui. Nesijaudinkite, jei nesate susipažinę su a apibrėžtas integralas; ką jūs iš tikrųjų turite suprasti, yra tai, kad „Simpson“ taisyklė yra algoritmas, apskaičiuojantis realų skaičių pagal keturis parametrus:
- Funkcija, kurią norime integruoti.
- Du realūs skaičiai
a
irb
kurie atspindi intervalo taškus[a, b]
tikrojo skaičiaus eilutėje. (Atkreipkite dėmesį, kad aukščiau nurodyta funkcija šiame intervale turėtų būti nepertraukiama.) - Lyginis sveikasis skaičius
n
tai nurodo daugybę subintervalų. Įgyvendindami Simpsono taisyklę, mes padalijame intervalą[a, b]
įn
subintervalai.
Norėdami supaprastinti pristatymą, sutelkime dėmesį į programavimo sąsają, o ne į įgyvendinimo detales. (Tiesą sakant, tikiuosi, kad šis metodas leis apeiti argumentus apie geriausią ar efektyviausią būdą įgyvendinti Simpsono taisyklę, kuri nėra šio straipsnio tema.) Mes naudosime tipą dvigubai
parametrams a
ir b
, ir mes naudosime tipą tarpt
parametrui n
. Integruotai funkcijai reikės vieno tipo parametro dvigubai
ir grąžina tipo vertę dvigubai
.
Funkcijos parametrai C ++
Norėdami pateikti palyginimo pagrindą, pradėkime nuo C ++ specifikacijos. Perduodamas funkciją kaip parametrą C ++, aš dažniausiai norėčiau nurodyti funkcijos parametro parašą naudodamas a typedef
. 1 sąraše rodomas C ++ antraštės failas, pavadintas simpson.h
kad nurodo tiek typedef
funkcijos parametrui ir C ++ funkcijos pavadinimo programavimo sąsajai integruotis
. Funkcijos kūnas integruotis
yra C ++ šaltinio kodo faile, pavadintame simpson.cpp
(neparodytas) ir pateikia „Simpson“ taisyklės įgyvendinimą.
Sąrašas 1. Simpsono taisyklės C ++ antraštės failas
#if! define (SIMPSON_H) #define SIMPSON_H #įtraukti naudojant vardų srities standartą; typedef double DoubleFunction (dvigubas x); dvigubas integravimas („DoubleFunction f“, dvigubas a, dvigubas b, int n) metimas (invalid_argument); #endif
Skambinama integruotis
yra nesudėtinga C ++. Tarkime, kad kaip paprastą pavyzdį norėjote apipavidalinti „Simpson“ taisyklę sinusas funkcija nuo 0
iki π (PI
) naudojant 30
subintervalai. (Kiekvienas, kuris baigė skaičiavimą, turėčiau sugebėti tiksliai apskaičiuoti atsakymą be skaičiuoklės pagalbos, todėl tai būtų geras integruotis
funkcija.) Darant prielaidą, kad turėjote įskaitant tinkamus antraštės failus, tokius kaip ir
"simpson.h"
, galėtumėte iškviesti funkciją integruotis
kaip parodyta 2 sąraše.
Sąrašas 2. C ++ kvietimas integruoti funkciją
dvigubas rezultatas = integruoti (sin, 0, M_PI, 30);
Tai viskas. C ++ programoje jūs praeinate sinusas veikia taip pat lengvai, kaip perduodate kitus tris parametrus.
Kitas pavyzdys
Vietoj „Simpson“ taisyklės aš taip pat lengvai galėčiau naudoti „Bisection“ metodą (dar žinomas Bisection Algorithm) formos lygčiai išspręsti f (x) = 0. Tiesą sakant, šio straipsnio šaltinio kodas apima paprastą Simpsono taisyklės ir dalijimo metodo įgyvendinimą.
Atsisiųsti Atsisiųskite šio straipsnio „Java“ šaltinio kodo pavyzdžius. Sukūrė Johnas I. Moore'as, skirtas „JavaWorld“Java be lambda posakių
Dabar pažiūrėkime, kaip „Simpson“ taisyklė gali būti nurodyta „Java“. Nepriklausomai nuo to, ar naudojame lambda išraiškas, vietoj C ++ naudojame „Java“ sąsają, parodytą 3 sąraše. typedef
nurodyti funkcijos parametro parašą.
Sąrašas 3. Funkcijos parametro „Java“ sąsaja
viešoji sąsaja „DoubleFunction“ {viešoji dviguba f (dviguba x); }
Norėdami įgyvendinti „Simpson“ taisyklę „Java“, mes sukuriame klasę pavadinimu Simpsonas
kuriame yra metodas, integruotis
, turėdami keturis parametrus, panašius į tai, ką darėme C ++. Kaip ir naudojant daugybę savarankiškų matematinių metodų (žr., Pvz., java.lang.Math
), mes padarysime integruotis
statinis metodas. Metodas integruotis
nurodoma taip:
Sąrašas 4. „Java“ parašas, skirtas integruoti „Simpson“ klasėje
viešasis statinis dvigubas integravimas („DoubleFunction df“, „double a“, „double b“, „int n“)
Viskas, ką iki šiol darėme „Java“, nepriklauso nuo to, ar naudosime „lambda“ išraiškas, ar ne. Pagrindinis skirtumas tarp lambda išraiškų yra tai, kaip mes perduodame parametrus (tiksliau, kaip mes perduodame funkcijos parametrą) kvietime į metodą integruotis
. Pirmiausia parodysiu, kaip tai būtų daroma „Java“ versijose iki 8 versijos; y., be lambda posakių. Kaip ir C ++ pavyzdyje, tarkime, kad mes norime apytiksliai įvertinti integralą sinusas funkcija nuo 0
iki π (PI
) naudojant 30
subintervalai.
„Adapter“ modelio naudojimas sinuso funkcijai
„Java“ sistemoje turime sinusas funkcija prieinama java.lang.Math
, tačiau naudojant „Java“ versijas prieš „Java 8“, nėra paprasto, tiesioginio būdo tai perduoti sinusas metodo funkciją integruotis
klasėje Simpsonas
. Vienas iš būdų yra naudoti „Adapter“ modelį. Tokiu atveju parašytume paprastą adapterių klasę, kuri įgyvendina „DoubleFunction“
sąsają ir pritaiko ją skambinti sinusas funkcija, kaip parodyta 5 sąraše.
Sąrašas 5. Adapterio klasė metodui Math.sin
importuoti com.softmoore.math.DoubleFunction; viešoji klasė „DoubleFunctionSineAdapter“ įgyvendina „DoubleFunction“ {public double f (double x) {return Math.sin (x); }}
Naudodami šią adapterių klasę, dabar galime skambinti integruotis
klasės metodas Simpsonas
kaip parodyta 6 sąraše.
Sąrašas 6. Adapterio klasės naudojimas skambinant metodui „Simpson.integrate“
„DoubleFunctionSineAdapter sine“ = naujas „DoubleFunctionSineAdapter“ (); dvigubas rezultatas = Simpson.integrate (sinusas, 0, Math.PI, 30);
Sustabdykime akimirką ir palyginkime, ko reikėjo norint paskambinti integruotis
C ++, palyginti su tuo, ko reikėjo ankstesnėse „Java“ versijose. Su C ++ mes tiesiog paskambinome integruotis
, perduodamas keturis parametrus. Naudodami „Java“ turėjome sukurti naują adapterių klasę, o po to šią klasę iš karto užmegzti, kad galėtume skambinti. Jei norėtume integruoti kelias funkcijas, kiekvienai iš jų turėtume parašyti adapterio klasę.
Galėtume sutrumpinti kodą, reikalingą skambinti integruotis
šiek tiek iš dviejų „Java“ sakinių į vieną, sukurdami naują adapterių klasės egzempliorių kvietime integruotis
. Anoniminės klasės naudojimas, o ne atskiros adapterių klasės sukūrimas būtų dar vienas būdas šiek tiek sumažinti bendras pastangas, kaip parodyta 7 sąraše.
Sąrašas 7. Anoniminės klasės naudojimas skambinant metodui Simpson.integrate
DoubleFunction sineAdapter = new DoubleFunction () {public double f (double x) {return Math.sin (x); }}; dvigubas rezultatas = Simpson.integrate (sineAdapter, 0, Math.PI, 30);
Be „lambda“ išraiškų, 7 sąraše matote apie mažiausią kodo kiekį, kurį galėtumėte parašyti „Java“, kad paskambintumėte integruotis
metodas, tačiau jis vis tiek yra daug sudėtingesnis, nei buvo reikalaujama C ++. Aš taip pat nesu patenkinta naudodama anoniminius užsiėmimus, nors anksčiau juos naudojau daug. Aš nemėgstu sintaksės ir visada maniau, kad tai nerangus, bet būtinas „Java“ įsilaužimas.
„Java“ su „lambda“ išraiškomis ir funkcinėmis sąsajomis
Dabar pažiūrėkime, kaip galėtume naudoti „Java“ išraiškas „Java 8“, kad supaprastintumėte skambutį integruotis
Java. Nes sąsaja „DoubleFunction“
reikalingas tik vieno metodo įgyvendinimas, jis yra kandidatas į lambda išraiškas. Jei iš anksto žinome, kad naudosime lambda išraiškas, galime sąsają komentuoti @ Funkcinė sąsaja
, nauja „Java 8“ anotacija, kurioje sakoma, kad turime funkcinė sąsaja. Atkreipkite dėmesį, kad šios anotacijos nereikia, tačiau tai suteikia mums papildomą patikrinimą, ar viskas yra nuosekliai, panašiai kaip @ Nepaisyti
anotacija ankstesnėse „Java“ versijose.
Lambda išraiškos sintaksė yra argumentų sąrašas, uždarytas skliausteliuose, rodyklės ženklas (->
) ir funkcinį kūną. Kūnas gali būti sakinių blokas (uždarytas petnešomis) arba vienas posakis. 8 sąraše rodoma lambda išraiška, įgyvendinanti sąsają „DoubleFunction“
ir tada perduodamas metodui integruotis
.
8. Lambda išraiškos naudojimas norint paskambinti metodui Simpson.integrate
„DoubleFunction“ sinusas = (dvigubas x) -> Math.sin (x); dvigubas rezultatas = Simpson.integrate (sinusas, 0, Math.PI, 30);
Atkreipkite dėmesį, kad mums nereikėjo rašyti adapterio klasės ar kurti anoniminės klasės egzemplioriaus. Taip pat atkreipkite dėmesį, kad aukščiau išvardintus dalykus galėjome parašyti viename sakinyje, pakeisdami pačią lambda išraišką, (dvigubas x) -> Math.sin (x)
parametrui sinusas
antrame aukščiau pateiktame teiginyje, pašalindamas pirmąjį teiginį. Dabar mes artėjame prie paprastos sintaksės, kurią turėjome C ++. Bet palauk! Yra dar daugiau!
Funkcinės sąsajos pavadinimas nėra „lambda“ išraiškos dalis, tačiau jį galima spręsti remiantis kontekstu. Tipas dvigubai
nes lambda išraiškos parametrą taip pat galima spręsti iš konteksto. Galiausiai, jei lambda išraiškoje yra tik vienas parametras, skliaustus galime praleisti. Taigi mes galime sutrumpinti kodą skambinti metodą integruotis
į vieną kodo eilutę, kaip parodyta 9 sąraše.
Sąrašas 9. Alternatyvus lambda išraiškos formatas skambinant į Simpson.integrate
dvigubas rezultatas = Simpson.integrate (x -> Math.sin (x), 0, Math.PI, 30);
Bet palauk! Yra dar daugiau!
Metodo nuorodos „Java 8“
Kita susijusi „Java 8“ funkcija yra tai, kas vadinama a metodo nuoroda, kuris leidžia mums įvardyti esamą metodą vardu. Metodo nuorodos gali būti naudojamos vietoje lambda išraiškų, jei jos atitinka funkcinės sąsajos reikalavimus. Kaip aprašyta šaltiniuose, yra keletas skirtingų metodų nuorodų, kurių kiekviena turi šiek tiek skirtingą sintaksę. Statinių metodų sintaksė yra „Classname :: methodName“
. Todėl, naudodami metodo nuorodą, galime iškviesti integruotis
metodą „Java“ taip paprastai, kaip galėtume „C ++“. Palyginkite „Java 8“ skambutį, parodytą žemiau esančiame 10 sąraše, su originaliu „C ++“ skambučiu, parodytu aukščiau esančiame 2 sąraše.
Sąrašas 10. Naudojant metodo nuorodą paskambinti Simpson.integrate
dvigubas rezultatas = Simpson.integrate (Math :: sin, 0, Math.PI, 30);