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
jab
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]
osaksin
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
.
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 JavaWorldilleJava 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);