Programmēšana

Java programmēšana ar lambda izteiksmēm

JavaOne 2013 tehniskajā galvenajā adresē Marks Reinholds, Oracle Java platformas grupas galvenais arhitekts, lambda izteiksmes aprakstīja kā lielāko Java programmēšanas modeļa jauninājumu kādreiz. Lai gan lambda izteiksmēm ir daudz lietojumu, šis raksts koncentrējas uz konkrētu piemēru, kas bieži sastopams matemātiskās lietojumprogrammās; proti, nepieciešamība nodot funkciju algoritmam.

Kā pelēks matains geek esmu gadu gaitā ieprogrammējis daudzās valodās, un kopš 1.1 versijas esmu plaši ieprogrammējis Java. Kad sāku strādāt ar datoriem, gandrīz nevienam nebija grāda datorzinātnēs. Datoru profesionāļi pārsvarā ieradās no citām disciplīnām, piemēram, elektrotehnikas, fizikas, biznesa un matemātikas. Savā bijušajā dzīvē es biju matemātiķis, un tāpēc nevajadzētu pārsteigt, ka mans sākotnējais skats uz datoru bija milzīgs programmējams kalkulators. Gadu gaitā esmu ievērojami paplašinājis savu viedokli par datoriem, taču joprojām atzinīgi vērtēju iespēju strādāt pie lietojumprogrammām, kas saistītas ar kādu matemātikas aspektu.

Daudzās matemātikas lietojumprogrammās ir jānodod funkcija kā parametrs algoritmam. Koledžas algebras un pamata aprēķina piemēri ietver vienādojuma atrisināšanu vai funkcijas integrāla aprēķināšanu. Vairāk nekā 15 gadus Java ir bijusi mana izvēlētā programmēšanas valoda lielākajai daļai lietojumprogrammu, taču tā bija pirmā bieži izmantotā valoda, kas neļāva man nodot funkciju (tehniski rādītāju vai atsauci uz funkciju) kā parametru vienkāršā, tiešā veidā. Šis trūkums drīz mainīsies līdz ar gaidāmo Java 8 izlaišanu.

Lambda izteiksmju spēks pārsniedz vienreizējas lietošanas gadījumus, taču, pētot dažādas viena un tā paša piemēra realizācijas, jums vajadzētu pārliecināties, kā lambdas dos labumu jūsu Java programmām. Šajā rakstā es izmantošu izplatītu piemēru, lai palīdzētu aprakstīt problēmu, pēc tam sniegšu risinājumus, kas rakstīti C ++, Java pirms lambda izteicieniem un Java ar lambda izteiksmēm. Ņemiet vērā, ka, lai saprastu un novērtētu šī raksta galvenos punktus, nav nepieciešama spēcīga matemātikas pieredze.

Mācīšanās par lambdas

Lambda izteiksmes, kas pazīstamas arī kā slēgšanas, funkciju literāļi vai vienkārši lambdas, apraksta funkciju specifikāciju, kas definēta Java specifikācijas pieprasījumā (JSR) 335. Mazāk formāli / lasāmāki lambda izteicienu ievadi ir sniegti jaunākās versijas sadaļā. Java apmācība un pāris Braiena Geca rakstu "Lambda stāvoklis" un "Lambda stāvoklis: Libraries izdevums". Šie resursi apraksta lambda izteicienu sintaksi un sniedz piemērus lietošanas gadījumiem, kad lambda izteiksmes ir piemērojamas. Lai uzzinātu vairāk par lambda izteiksmēm Java 8, skatiet Marka Reinholda tehnisko galveno adresi JavaOne 2013.

Lambda izteicieni matemātiskā piemērā

Šajā rakstā izmantotais piemērs ir Simpsona likums no pamata aprēķina. Simpsona likums vai, precīzāk, saliktais Simpsona noteikums, ir skaitliska integrācijas tehnika, lai tuvinātu noteiktu integrālu. Neuztraucieties, ja jums nav pazīstams jēdziens a noteikts neatņemams; kas jums patiešām jāsaprot, ir tas, ka Simpsona likums ir algoritms, kas aprēķina reālu skaitli, pamatojoties uz četriem parametriem:

  • Funkcija, kuru mēs vēlamies integrēt.
  • Divi reālie skaitļi a un b kas apzīmē intervāla galapunktus [a, b] reālā skaitļa līnijā. (Ņemiet vērā, ka iepriekš minētajai funkcijai šajā intervālā jābūt nepārtrauktai.)
  • Vienmērīgs vesels skaitlis n kas norāda vairākus apakšintervālus. Īstenojot Simpsona likumu, mēs dalām intervālu [a, b] vērā n apakšintervali.

Lai vienkāršotu prezentāciju, pievērsīsimies programmēšanas saskarnei, nevis ieviešanas detaļām. (Patiesi, es ceru, ka šī pieeja ļaus mums apiet argumentus par labāko vai visefektīvāko veidu, kā ieviest Simpsona likumu, kas nav šī raksta uzmanības centrā.) Mēs izmantosim veidu dubultā parametriem a un b, un mēs izmantosim veidu int parametram n. Integrējamajai funkcijai būs viens tipa parametrs dubultā un atgriež veida vērtību dubultā.

Lejupielādēt Lejupielādējiet šī raksta C ++ avota koda piemēru. Izveidoja Džons I. Mūrs programmai JavaWorld

Funkcijas parametri C ++

Lai nodrošinātu salīdzināšanas pamatu, sāksim ar C ++ specifikāciju. Pārejot funkciju kā parametru C ++, es parasti dodu priekšroku norādīt parametra parakstu, izmantojot a typedef. 1. sarakstā tiek parādīts C ++ galvenes fails ar nosaukumu simpson.h kas norāda gan typedef funkcijas parametram un C ++ funkcijas nosauktajai programmēšanas saskarnei integrēt. Funkcijas ķermenis integrēt ir ietverts C ++ avota koda failā ar nosaukumu simpson.cpp (nav parādīts) un nodrošina Simpson's Rule ieviešanu.

Saraksts 1. C ++ galvenes fails Simpsona likumam

 #if! definēts (SIMPSON_H) #define SIMPSON_H #iekļauj, izmantojot vārdu vietas std; typedef double DoubleFunction (double x); dubultā integrācija (DoubleFunction f, double a, double b, int n) throw (invalid_argument); #endif 

Zvanīšana integrēt ir vienkāršs C ++. Pieņemsim, ka kā vienkāršu piemēru vēlaties izmantot Simpsona likumu, lai tuvinātu sinusa funkcija no 0 līdz π (PI) izmantojot 30 apakšintervali. (Ikvienam, kurš ir pabeidzis Calculus I, vajadzētu būt iespējai precīzi aprēķināt atbildi bez kalkulatora palīdzības, padarot to par labu testa gadījumu integrēt funkcija.) Pieņemot, ka jums bija iekļauts pareizi galvenes faili, piemēram, un "simpson.h", jūs varētu izsaukt funkciju integrēt kā parādīts 2. sarakstā.

Saraksts 2. C ++ izsaukums, lai integrētu funkciju

 divkāršs rezultāts = integrēt (grēks, 0, M_PI, 30); 

Tas ir viss, kas tam ir. Programmā C ++ jūs nokārtojat sinusa darbojas tikpat viegli, kā jūs nododat pārējos trīs parametrus.

Vēl viens piemērs

Simpsona noteikuma vietā es tikpat viegli būtu varējis izmantot Bisection metodi (aka Bisection Algorithm) formas vienādojuma atrisināšanai f (x) = 0. Faktiski šī raksta avota kods ietver vienkāršu gan Simpsona likuma, gan dalīšanas metodes ieviešanu.

Lejupielādēt Lejupielādējiet šī raksta Java pirmkodu piemērus. Izveidoja Džons I. Mūrs programmai JavaWorld

Java bez lambda izteicieniem

Tagad aplūkosim, kā Simpsona likums varētu tikt norādīts Java. Neatkarīgi no tā, vai mēs izmantojam lambda izteicienus, C ++ vietā mēs izmantojam Java saskarni, kas parādīta 3. sarakstā typedef lai norādītu funkcijas parametra parakstu.

Saraksts 3. Java interfeiss funkcijas parametram

 publiskā saskarne DoubleFunction {public double f (double x); } 

Lai ieviestu Simpsona likumu Java, mēs izveidojam klasi ar nosaukumu Simpsons kas satur metodi, integrēt, ar četriem parametriem, kas ir līdzīgi tam, ko mēs darījām C ++. Tāpat kā ar daudzām pašpietiekamām matemātiskām metodēm (sk., Piemēram, java.lang.Math), mēs darīsim integrēt statiska metode. Metode integrēt ir noteikts šādi:

Saraksts 4. Java paraksts metodes integrēšanai Simpsona klasē

 publiskā statiskā dubultā integrācija (DoubleFunction df, double a, double b, int n) 

Viss, ko līdz šim esam darījuši Java valodā, nav atkarīgs no tā, vai izmantosim lambda izteicienus. Galvenā atšķirība ar lambda izteiksmēm ir tajā, kā mēs nododam parametrus (precīzāk, kā mēs nododam funkcijas parametru) aicinājumā uz metodi integrēt. Vispirms es ilustrēšu, kā tas tiktu darīts Java versijās pirms 8. versijas; i., bez lambda izteicieniem. Tāpat kā ar C ++ piemēru, pieņemsim, ka mēs vēlamies tuvināt sinusa funkcija no 0 līdz π (PI) izmantojot 30 apakšintervali.

Adaptera modeļa izmantošana sinusa funkcijai

Java mums ir ieviests sinusa funkcija pieejama java.lang.Math, bet ar Java versijām pirms Java 8 nav vienkārša, tieša veida, kā to nodot sinusa metodi integrēt klasē Simpsons. Viena pieeja ir izmantot Adapter modeli. Šajā gadījumā mēs uzrakstīsim vienkāršu adapteru klasi, kas ievieš DoubleFunction interfeisu un pielāgo to, lai izsauktu sinusa funkciju, kā parādīts 5. sarakstā.

Saraksts 5. Adaptera klase metodei Math.sin

 importēt com.softmoore.math.DoubleFunction; publiskā klase DoubleFunctionSineAdapter īsteno DoubleFunction {public double f (double x) {return Math.sin (x); }} 

Izmantojot šo adapteru klasi, tagad mēs varam izsaukt integrēt klases metode Simpsons kā parādīts 6. sarakstā.

Saraksts 6. Adaptera klases izmantošana, lai izsauktu metodi Simpson.integrate

 DoubleFunctionSineAdapter sine = jauns DoubleFunctionSineAdapter (); divkāršs rezultāts = Simpson.integrate (sine, 0, Math.PI, 30); 

Pārtrauksim brīdi un salīdzināsim to, kas bija nepieciešams, lai piezvanītu integrēt C ++ versijā salīdzinājumā ar iepriekšējās Java versijās prasīto. Ar C ++ mēs vienkārši piezvanījām integrēt, ievadot četrus parametrus. Izmantojot Java, mums bija jāizveido jauna adaptera klase un pēc tam jāpieliek šī klase, lai veiktu zvanu. Ja mēs vēlētos integrēt vairākas funkcijas, mums katrai no tām būs jāuzraksta adaptera klase.

Mēs varētu saīsināt kodu, kas nepieciešams, lai piezvanītu integrēt nedaudz no diviem Java paziņojumiem uz vienu, izveidojot jauno adapteru klases instanci zvanā integrēt. Anonīmas klases izmantošana, nevis atsevišķas adapteru klases izveidošana būtu vēl viens veids, kā nedaudz samazināt kopējo piepūli, kā parādīts 7. sarakstā.

Saraksts 7. Anonīmās klases izmantošana, lai izsauktu metodi Simpson.integrate

 DoubleFunction sineAdapter = new DoubleFunction () {public double f (double x) {return Math.sin (x); }}; dubultā rezultāts = Simpson.integrate (sineAdapter, 0, Math.PI, 30); 

Bez lambda izteiksmēm 7. sarakstā redzamais ir vismazākais koda daudzums, ko jūs varētu rakstīt Java valodā, lai izsauktu integrēt metodi, taču tā joprojām ir daudz apgrūtinošāka nekā tas, kas tika prasīts C ++. Es arī neesmu tik apmierināta ar anonīmu nodarbību izmantošanu, lai gan agrāk tās esmu daudz izmantojusi. Man nepatīk sintakse un vienmēr to esmu uzskatījis par neveiklu, bet nepieciešamu uzlaušanu Java valodā.

Java ar lambda izteiksmēm un funkcionālajām saskarnēm

Tagad apskatīsim, kā mēs varētu izmantot lambda izteicienus Java 8, lai vienkāršotu zvanu uz integrēt Java valodā. Jo interfeiss DoubleFunction nepieciešama tikai vienas metodes ieviešana, tā var pretendēt uz lambda izteicieniem. Ja mēs iepriekš zinām, ka izmantosim lambda izteicienus, mēs varam anotēt saskarni ar @ Funkcionālā saskarne, jauna Java 8 anotācija, kurā teikts, ka mums ir funkcionālā saskarne. Ņemiet vērā, ka šī anotācija nav nepieciešama, taču tā dod mums papildu pārbaudi, vai viss ir konsekvents, līdzīgs @ Pārvarēt anotācija Java iepriekšējās versijās.

Lambda izteiksmes sintakse ir argumentu saraksts, kas ir iekavās, bultiņas marķieris (->) un funkcijas ķermenis. Pamatteksts var būt vai nu paziņojuma bloks (ievietots iekavās), vai arī viena izteiksme. 8. saraksts parāda lambda izteiksmi, kas īsteno saskarni DoubleFunction un pēc tam tiek nodota metodei integrēt.

8. saraksta izmantošana, lai izsauktu metodi Simpson.integrate

 DoubleFunction sinusā = (dubultā x) -> Math.sin (x); divkāršs rezultāts = Simpson.integrate (sine, 0, Math.PI, 30); 

Ņemiet vērā, ka mums nebija jāraksta adaptera klase vai jāizveido anonīmas klases eksemplārs. Ņemiet vērā arī to, ka mēs varējām ierakstīt iepriekš minēto vienā paziņojumā, aizstājot pašu lambda izteicienu, (dubultā x) -> Math.sin (x), parametram sinusa otrajā paziņojumā iepriekš, izslēdzot pirmo paziņojumu. Tagad mēs esam daudz tuvāk vienkāršajai sintaksei, kas mums bija C ++. Bet pagaidi! Tur ir vairāk!

Funkcionālās saskarnes nosaukums nav daļa no lambda izteiksmes, bet to var secināt, pamatojoties uz kontekstu. Veids dubultā par lambda izteiksmes parametru var secināt arī no konteksta. Visbeidzot, ja lambda izteiksmē ir tikai viens parametrs, tad iekavas var izlaist. Tādējādi mēs varam saīsināt kodu, lai izsauktu metodi integrēt uz vienu koda rindiņu, kā parādīts 9. sarakstā.

Saraksts 9. Alternatīvs lambda izteiksmes formāts zvanam uz Simpson.integrate

 dubults rezultāts = Simpson.integrate (x -> Math.sin (x), 0, Math.PI, 30); 

Bet pagaidi! Tur ir vēl vairāk!

Metodes atsauces Java 8

Vēl viena saistīta Java 8 iezīme ir tā sauktā a metodes atsauce, kas ļauj mums atsaukties uz esošu metodi pēc nosaukuma. Metodes atsauces var izmantot lambda izteicienu vietā, ja vien tās atbilst funkcionālās saskarnes prasībām. Kā aprakstīts resursos, ir vairākas dažādu veidu atsauces uz metodēm, katrai no tām ir nedaudz atšķirīga sintakse. Statiskām metodēm sintakse ir Klases nosaukums :: methodName. Tāpēc, izmantojot metodes atsauci, mēs varam izsaukt integrēt metodi Java, kā vienkārši C ++. Salīdziniet Java 8 zvanu, kas parādīts zemāk 10. sarakstā, ar sākotnējo C ++ zvanu, kas parādīts iepriekš 2. sarakstā.

Saraksts 10. Izmantojot metodes atsauci, lai izsauktu Simpson.integrate

 dubultā rezultāts = Simpson.integrate (Math :: grēks, 0, Math.PI, 30); 
$config[zx-auto] not found$config[zx-overlay] not found