Ohjelmointi

Java-ohjelmointi lambda-lausekkeilla

Oracle: n Java Platform Group -yksikön pääarkkitehti Mark Reinhold kuvasi JavaOne 2013: n teknisessä pääpuheenvuorossa lambda-lausekkeita yhtenä suurimpana päivityksenä Java-ohjelmointimalliin. koskaan. Vaikka lambda-lausekkeisiin on monia sovelluksia, tässä artikkelissa keskitytään tiettyyn esimerkkiin, jota esiintyy usein matemaattisissa sovelluksissa; nimittäin tarve siirtää funktio algoritmille.

Harmaa tukkainen nörtti olen ohjelmoinut lukuisilla kielillä vuosien varrella ja olen ohjelmoinut laajasti Java-versiossa version 1.1 jälkeen. Kun aloin työskennellä tietokoneiden kanssa, melkein kenelläkään ei ollut tutkintoa tietojenkäsittelytieteestä. Tietokoneiden ammattilaiset tulivat lähinnä muilta aloilta, kuten sähkötekniikasta, fysiikasta, liike-elämästä ja matematiikasta. Omassa entisessä elämässäni olin matemaatikko, joten ei pitäisi olla yllätys, että alkuperäinen näkymäni tietokoneesta oli valtava ohjelmoitava laskin. Olen laajentanut näkemystäni tietokoneista vuosien varrella, mutta olen silti tyytyväinen mahdollisuuteen työskennellä sellaisten sovellusten parissa, joihin liittyy matematiikkaa.

Monet matematiikan sovellukset edellyttävät funktion välittämistä parametrina algoritmille. Esimerkkejä yliopiston algebrasta ja peruslaskelmasta ovat yhtälön ratkaiseminen tai funktion integraalin laskeminen. Java on ollut valitsemani ohjelmointikieli useimmissa sovelluksissa yli 15 vuoden ajan, mutta se oli ensimmäinen usein käyttämäni kieli, joka ei antanut minun siirtää funktiota (teknisesti osoitinta tai viittausta toimintoon) parametri yksinkertaisella, suoraviivaisella tavalla. Tämä puute on muuttumassa tulevan Java 8 -version myötä.

Lambda-lausekkeiden voima ulottuu huomattavasti yhden käyttötapauksen ulkopuolelle, mutta saman esimerkin eri toteutusten tutkimisen pitäisi antaa sinulle vankka käsitys siitä, kuinka lambdas hyödyttää Java-ohjelmiasi. Tässä artikkelissa käytän yleistä esimerkkiä ongelman kuvaamiseen, sitten tarjoan ratkaisuja, jotka on kirjoitettu C ++: lla, Java ennen lambda-lausekkeita ja Java ja lambda-lausekkeet. Huomaa, että vahvaa matemaattista taustaa ei tarvita tämän artikkelin pääkohtien ymmärtämiseen ja arvostamiseen.

Oppiminen lambdasta

Lambda-lausekkeet, jotka tunnetaan myös nimellä sulkemiset, funktiolitraalit tai yksinkertaisesti lambdas, kuvaavat joukkoa Java Specification Request (JSR) 335: ssä määriteltyjä ominaisuuksia. Lambda-lausekkeiden vähemmän muodollisia / helpommin luettavia esittelyjä on osiossa viimeisintä versiota. Java-opetusohjelma ja Brian Goetzin pari artikkelia "State of the lambda" ja "State of the lambda: Libraries edition". Nämä resurssit kuvaavat lambda-lausekkeiden syntaksia ja tarjoavat esimerkkejä käyttötapauksista, joissa lambda-lausekkeita voidaan käyttää. Lisätietoja lambda-lausekkeista Java 8: ssa on Mark Reinholdin JavaOne 2013: n teknisessä pääosoitteessa.

Lambda-lausekkeet matemaattisessa esimerkissä

Tässä artikkelissa käytetty esimerkki on Simpsonin sääntö peruslaskusta. Simpsonin sääntö tai tarkemmin sanottuna Composite Simpsonin sääntö on numeerinen integraatiotekniikka, jolla arvioidaan tietty integraali. Älä huoli, jos et tunne a: n käsitettä selvä integraali; mitä sinun on todella ymmärrettävä, on se, että Simpsonin sääntö on algoritmi, joka laskee reaaliluvun neljän parametrin perusteella:

  • Toiminto, jonka haluamme integroida.
  • Kaksi todellista lukua a ja b jotka edustavat aikavälin päätepisteitä [a, b] reaalilukurivillä. (Huomaa, että yllä mainitun toiminnon tulisi olla jatkuva tällä aikavälillä.)
  • Tasainen kokonaisluku n joka määrittelee useita aliväliä. Simpsonin säännön toteuttamisessa jaamme intervalli [a, b] osaksi n osa-alueet.

Esityksen yksinkertaistamiseksi keskitytään ohjelmointirajapintaan eikä toteutuksen yksityiskohtiin. (Totisesti, toivon, että tämä lähestymistapa antaa meidän ohittaa argumentit parhaasta tai tehokkaimmasta tavasta toteuttaa Simpsonin sääntö, jota ei käsitellä tässä artikkelissa.) Käytämme tyyppiä kaksinkertainen parametreille a ja b, ja käytämme tyyppiä int parametrille n. Integroitava toiminto ottaa yhden tyyppisen parametrin kaksinkertainen ja palauttaa tyypin arvon kaksinkertainen.

Lataa Lataa tämän artikkelin C ++ -lähdekoodiesimerkki. Luonut John I.Moore JavaWorldille

Toimintaparametrit C ++: ssa

Aloittaaksemme vertailuperustan C ++ -määrityksestä. Kun välitän funktion parametrina C ++: ssa, mieluummin määritän funktion parametrin allekirjoituksen käyttämällä a: ta typedef. Luettelossa 1 näkyy C ++ -otsikkotiedosto nimeltä simpson.h joka määrittelee molemmat typedef toimintoparametrille ja C ++ -funktion ohjelmointirajapinnalle integroida. Toiminnon runko integroida sisältyy C ++ - lähdekooditiedostoon nimeltä simpson.cpp (ei näy) ja tarjoaa Simpsonin säännön toteutuksen.

Listaus 1. Simpsonin säännön C ++ -otsikkotiedosto

 #if! define (SIMPSON_H) #define SIMPSON_H # sisällyttää nimiavaruuden vakio; typedef double DoubleFunction (kaksinkertainen x); kaksinkertainen integrointi (DoubleFunction f, double a, double b, int n) heitto (invalid_argument); #loppu Jos 

Kutsumus integroida on suoraviivaista C ++: ssa. Oletetaan yksinkertaisena esimerkkinä, että halusit käyttää Simpsonin sääntöä lähentämään integraalia sini toiminto 0 pisteeseen π (PI) käyttämällä 30 osa-alueet. (Kuka tahansa, joka on suorittanut laskennan, minun pitäisi pystyä laskemaan vastaus tarkalleen ilman laskimen apua, mikä tekee tästä hyvän testitapauksen integroida olettaen, että sinulla oli mukana oikeat otsikkotiedostot, kuten ja "simpson.h", voit soittaa toimintoon integroida kuten luettelossa 2 on esitetty.

Listaus 2. C ++ - kutsu toiminnon integrointiin

 kaksoistulos = integroi (sin, 0, M_PI, 30); 

Siinä kaikki siinä on. C ++: ssa ohitat sini toimii yhtä helposti kuin välität kolme muuta parametria.

Toinen esimerkki

Simpsonin säännön sijasta olisin voinut käyttää yhtä helposti Bisection-menetelmää (alias Bisection Algorithm) muodon yhtälön ratkaisemiseksi f (x) = 0. Itse asiassa tämän artikkelin lähdekoodi sisältää yksinkertaisia ​​toteutuksia sekä Simpsonin säännöstä että Bisection-menetelmästä.

Lataa Lataa tämän artikkelin Java-lähdekoodiesimerkit. Luonut John I.Moore JavaWorldille

Java ilman lambda-lausekkeita

Katsotaan nyt, miten Simpsonin sääntö voidaan määritellä Java-ohjelmassa. Riippumatta siitä, käytämmekö lambda-lausekkeita, käytämme luettelossa 3 esitettyä Java-käyttöliittymää C ++: n sijaan typedef Määritä toimintoparametrin allekirjoitus.

Listaus 3. Java-käyttöliittymä toimintoparametrille

 julkinen käyttöliittymä DoubleFunction {public double f (double x); } 

Simpsonin säännön toteuttamiseksi Javassa luomme luokan nimeltä Simpson joka sisältää menetelmän, integroida, neljällä parametrilla, jotka ovat samanlaisia ​​kuin mitä teimme C ++: ssa. Kuten monien itsenäisten matemaattisten menetelmien kohdalla (katso esimerkiksi java.lang.Math), tulemme tekemään integroida staattinen menetelmä. Menetelmä integroida määritetään seuraavasti:

Listaus 4. Java-allekirjoitus menetelmän integroimiseksi luokassa Simpson

 staattinen staattinen kaksoisintegraatio (DoubleFunction df, double a, double b, int n) 

Kaikki, mitä olemme tähän mennessä tehneet Javassa, on riippumatonta siitä, käytämmekö lambda-lausekkeita vai ei. Ensisijainen ero lambda-lausekkeiden kanssa on siinä, miten parametrit välitetään (tarkemmin sanottuna funktioparametri) menetelmän kutsussa integroida. Ensin kuvaan, miten tämä tapahtuisi Java-versioissa ennen versiota 8; ts. ilman lambda-ilmauksia. Kuten C ++ -esimerkissä, oletetaan, että haluamme lähentää kokonaisuuden integraalia sini toiminto 0 pisteeseen π (PI) käyttämällä 30 osa-alueet.

Sovitinmallin käyttäminen sinitoiminnossa

Java: ssa meillä on sini käytettävissä oleva toiminto java.lang.Math, mutta Java 8: n aikaisempien versioiden kanssa ei ole yksinkertaista, suoraa tapaa välittää tämä sini toiminto menetelmälle integroida luokassa Simpson. Yksi tapa on käyttää Adapter-mallia. Tässä tapauksessa kirjoitamme yksinkertaisen sovitinluokan, joka toteuttaa DoubleFunction käyttöliittymä ja mukauttaa sitä kutsumaan sini kuten luettelossa 5 on esitetty.

Listaus 5. Adapteriluokka menetelmälle Math.sin

 tuo com.softmoore.math.DoubleFunction; public class DoubleFunctionSineAdapter toteuttaa DoubleFunction {public double f (double x) {return Math.sin (x); }} 

Tämän sovitinluokan avulla voimme nyt soittaa integroida luokan menetelmä Simpson kuten luettelossa 6 on esitetty.

Listaus 6. Sovitinluokan käyttäminen soittotapaan Simpson.integrate

 DoubleFunctionSineAdapter sine = uusi DoubleFunctionSineAdapter (); kaksoistulos = Simpson. integroi (sini, 0, Math.PI, 30); 

Lopetetaan hetki ja verrataan puheluun vaadittua integroida C ++ -sovelluksessa verrattuna aikaisempiin Java-versioihin vaadittuun. C ++: lla soitimme yksinkertaisesti integroida, välittää neljä parametria. Java-sovelluksella meidän piti luoda uusi sovitinluokka ja sen jälkeen instantisoida tämä luokka puhelun soittamiseksi. Jos haluaisimme integroida useita toimintoja, meidän on kirjoitettava jokaiselle sovitinluokka.

Voisimme lyhentää soittamiseen tarvittavaa koodia integroida hiukan kahdesta Java-käskystä yhteen luomalla uusi sovitinluokan kutsu integroida. Anonyymin luokan käyttäminen erillisen sovitinluokan luomisen sijasta olisi toinen tapa vähentää kokonaisponnistelua, kuten luettelossa 7 on esitetty.

Listaus 7. Anonyymin luokan käyttäminen soittotapaan Simpson.integrate

 DoubleFunction sineAdapter = uusi DoubleFunction () {public double f (double x) {return Math.sin (x); }}; kaksoistulos = Simpson.integrate (sineAdapter, 0, Math.PI, 30); 

Ilman lambda-lausekkeita Listauksessa 7 on kyse vähimmäismäärästä koodia, jonka voisit kirjoittaa Java-järjestelmään soittaaksesi integroida menetelmä, mutta se on silti paljon hankalampi kuin mitä vaadittiin C ++: lle. En ole myöskään niin tyytyväinen tuntemattomien luokkien käyttämiseen, vaikka olen käyttänyt niitä paljon aiemmin. En pidä syntaksista ja olen aina pitänyt sitä kömpelänä, mutta välttämättömänä hakkerointina Java-kielellä.

Java lambda-lausekkeilla ja toiminnallisilla rajapinnoilla

Katsotaan nyt, kuinka voisimme käyttää lambda-lausekkeita Java 8: ssa yksinkertaistamaan puhelua integroida Java-kielellä. Koska käyttöliittymä DoubleFunction vaatii vain yhden menetelmän toteuttamista, se on ehdokas lambda-lausekkeisiin. Jos tiedämme etukäteen, että aiomme käyttää lambda-lausekkeita, voimme merkitä käyttöliittymän @FunctionalInterface, uusi huomautus Java 8: lle, jonka mukaan meillä on toiminnallinen käyttöliittymä. Huomaa, että tätä merkintää ei vaadita, mutta se antaa meille ylimääräisen tarkistuksen siitä, että kaikki on yhdenmukaista, samanlainen kuin @Ohittaa huomautus aikaisemmissa Java-versioissa.

Lambda-lausekkeen syntaksi on sulkeisiin suljettu argumenttiluettelo, nuolen tunnus (->) ja funktion runko. Runko voi olla joko lauselohko (sulkeissa) tai yksittäinen lauseke. Luettelossa 8 näkyy lambda-lauseke, joka toteuttaa rajapinnan DoubleFunction ja siirretään sitten menetelmään integroida.

Listaus 8. Lambda-lausekkeen käyttäminen kutsumalla menetelmä Simpson.integrate

 DoubleFunction sinus = (kaksinkertainen x) -> Math.sin (x); kaksoistulos = Simpson. integroi (sini, 0, Math.PI, 30); 

Huomaa, että meidän ei tarvinnut kirjoittaa sovitinluokkaa tai luoda nimettömän luokan esiintymää. Huomaa myös, että olisimme voineet kirjoittaa edellä mainitun yhteen lausekkeeseen korvaamalla itse lambda-lausekkeen, (kaksinkertainen x) -> Math.sin (x), parametrille sini toisessa yllä olevassa lausekkeessa eliminoiden ensimmäisen lausuman. Nyt olemme tulossa paljon lähemmäksi yksinkertaista syntaksia, joka meillä oli C ++: ssa. Mutta odota! Siellä on enemmän!

Toiminnallisen rajapinnan nimi ei ole osa lambda-lauseketta, mutta se voidaan päätellä kontekstin perusteella. Tyyppi kaksinkertainen lambda-lausekkeen parametrille voidaan päätellä myös kontekstista. Lopuksi, jos lambda-lausekkeessa on vain yksi parametri, voimme sulkea sulkut. Siten voimme lyhentää koodin kutsumenetelmää integroida yhdelle koodiriville, kuten luettelossa 9 on esitetty.

Listaus 9. Vaihtoehtoinen muoto lambda-lausekkeelle puhelussa Simpson.integrate

 kaksinkertainen tulos = Simpson.integrate (x -> Math.sin (x), 0, Math.PI, 30); 

Mutta odota! Siellä on vielä enemmän!

Menetelmäviitteet Java 8: ssa

Toinen Java 8: n liittyvä ominaisuus on jotain, jota kutsutaan a menetelmän viite, jonka avulla voimme viitata olemassa olevaan menetelmään nimellä. Menetelmäviitteitä voidaan käyttää lambda-lausekkeiden sijasta, kunhan ne täyttävät toiminnallisen rajapinnan vaatimukset. Kuten lähteissä on kuvattu, on olemassa useita erilaisia ​​menetelmäviittauksia, joista jokaisella on hieman erilainen syntaksin muoto. Staattisten menetelmien syntaksi on Luokan nimi :: methodName. Siksi, käyttämällä menetelmäviittausta, voimme kutsua integroida menetelmä yksinkertaisesti kuin voimme C ++: ssa. Vertaa Java 8 -puhelua, joka näkyy alla olevassa luettelossa 10, alkuperäiseen C ++ -puheluun, joka näkyy yllä olevassa luettelossa 2.

Listaus 10. Menetelmäviitteen käyttäminen Simpson.integraten kutsumiseen

 kaksoistulos = Simpson.integrate (Math :: sin, 0, Math.PI, 30); 
$config[zx-auto] not found$config[zx-overlay] not found