Julia (ohjelmointikieli)

Wikipediasta
Siirry navigaatioon Siirry hakuun
Julia
Paradigma monia; imperatiivinen, funktionaalinen, olioperusteinen
Tyypitys dynaaminen, vahva, nominatiivinen, parametrinen, vapaaehtoinen
Yleinen suoritusmalli ajonaikaisesti käännettävä (tyyppikoodi,LLVM)
Muistinhallinta roskienkeruu
Julkaistu 14. helmikuu 2012
Vakaa versio 1.11.0
Vaikutteet R, MATLAB, Python, Lisp, Perl, Lua, Ruby[1]
Käyttöjärjestelmä alustariippumaton
Verkkosivu julialang.org,github.com/JuliaLang/julia

Juliaonohjelmointikieli,jota on kehitetty erityisesti tieteelliseen laskentaan, tavoitteenaan yhdistää hitaiden dynaamisten kielten helppokäyttöisyys ja perinteisten staattisten kielten suorituskyky.[1]

Julian keskeisin piirre ja ohjelmointitapa onmultiple dispatcheli funktion koko tyyppijälki määrittää, mitä toteutusta eli metodia tietystä funktiosta kutsutaan. Monissa muissa kielissä metodit kuuluvat yhdelle objektille, eli vain ensimmäisen parametrin (esim.self) tyypillä on merkitystä (engl.single dispatch).[1]

Lineaarialgebraon yksi numeerisen laskennan keskeisistä alueista, joten tähän löytyy hyvä tuki Julian perus- ja standardikirjastosta (using LinearAlgebra):[2]

  • AbstractArrayja tämän alatyypit mahdollistavat moniulotteiset taulukot suoraanBase-paketista
  • Monipuolinen vektori-[1, 2, 3]ja matriisisyntaksi[1 2 3; 4 5 6]
  • Matriisioperaattorit+,-,*,/,,×
  • Jälkitr(M),determinanttidet(M)ja kääntämineninv(M)
  • Eigen-arvoteigvals(M),eigen-vektoriteigvecs(M)ja faktorointifactorize(M)
  • Matriisityypit kutenSymmetric,HermitianjaTridiagonal
  • Faktorointityypit kutenBunchKaufman,LUjaQR
  • Lyhennelmät[x*y for x=1:5, y=5:10],jotka tekevät taulukon
  • Generaattorit(x^5 for x=1:10),jotka voidaan iteroida

Julia-komentoriviohjelman voi ladata kielen virallisilta web-sivuilta valmiina ajotiedostona ohjeiden mukaan. Kieli voidaan asentaa myös juliaup-työkalulla[3],joka helpottaa myös kielen päivittämistä ja useiden eri versioiden rinnakkaista käyttöä.

julia --helpnäyttää saatavilla olevat asetukset jajuliakäynnistää REPL-konsolin. Komentoriviä voidaan käyttää poistumatta REPL-tilasta;-komennolla ja paketinhallintatilaan päästään]-komennolla.]?näyttää kaikki Pkg-pakettienhallinnan REPL-tilan komennot, jotka vastaavat Pkg-paketin funktioita. REPL-ohjelmasta poistutaan<CTRL>-D.Julia-kääntäjää käytetään yhdelle lähdetiedostolle yksinkertaisestijulia hei-wikipedia.jl.(Tämä Julia-kääntäjä toimii ns. JAOT-periaatteella (engl.just-ahead-of-time) eli Julia-koodi käännetään tyypitetyn ja LLVM-välikoodin kautta konekielelle, mutta tämä tapahtuu ajon aikana eikä erillisessä käännösvaiheessa.)

Julia REPL mahdollistaa interaktiivisemman ohjelmointitavan, missä kehitettävät ohjelmat ovat omassa moduulissaan ja tiedostossaan, testit ovat omassa moduulissaan ja tiedostossaan, testit ajetaan Julia-konsolissa tyylilläinclude( "testit.jl" )ja tämän lisäksi tehdään interaktiivista testaamista konsolissa. Kun interaktiivisesti löydetään jotain uutta testaamisen arvoista, tämä voidaan lisätä testimoduuliin. Muuten kehityksen aikana tehdään moduuleihin muutoksia palautteen mukaan ja toistetaan kehityssilmukkaa.Revise-paketti mahdollistaa päivitettyjen ohjelmien testaamisen käynnistämättä Juliaa uudelleen.[4]

Pluto.jl on Julialla Julialle kehitetty,Jupyter-työkirjojen kaltainen web-selaimeen ja ajettaviin soluihin perustuva kehitysympäristö, joka mahdollistaa interaktiivisenkirjallisen ohjelmoinnin(engl.literate programming). Työkirjat ovat aina toistettavia, sillä Pluto automatisoi pakettienhallinnanimport- jausing-lauseiden perusteella.[5]

Fibonaccin luvut.

functionfibonacci(n)
f=[0,1]
# Julia-listojen indeksit alkavat ykkösestä.
nin[0,1]&&returnf[n+1]
# Ajon fibonacci(n = 2) tulos tulee listan indeksiin 3, jne.
fori=3:(n+1)
# Huutomerkkiin päättyvät funktiot muokkaavat parametrejaan.
append!(f,f[i-1]+f[i-2])
end
# end-sana viittaa listan vimeiseen indeksiin.
returnf[end]
end

JulianPkg-paketti mahdollistaa vakioitujen ympäristöjen luomisen, pääosinactivatejaadd/rmfunktioidensa avulla. Tietty pakettiversio voidaan vaatia syntaksillaPkg.add( "[email protected]" )(semanttinen versiointi) taiPkg.add( "Nimi#master" )(Git). Myös suora URL toimii, jos kyseinen paketti ei ole käytetyssä rekisterissä. Nämä komennot muokkaavat kahta projektikansion juuressa olevaa tiedostoa:Project.toml(metadata) jaManifest.toml(riippuvuudet).Pkg.instantiateasentaa halutun Julia-ympäristön uudelleen näiden tiedostojen pohjalta. Julia-koodin lisäksi muiden riippuvuuksien käsittelemiseksi voidaan käyttääArtifacts.toml-tiedostoa, jonka avulla kunkin riippuvuuden paikallinen tiedostopolku saadaan käyttöön projektin Julia-koodissa syntaksillapolku = artifact "nimi"(engl.non-standard string literal).[6]

Kun ympäristö koostuu useista paketeista, niin paketti muodostuu yleensä useiden moduulien hierarkiasta. Moduulitmodule Nimet... endluovat uuden globaalin nimiavaruuden, johon tuodaan nimiäimportjausinglauseilla ja lähdetiedostojainclude()-kutsulla ja josta paljastetaan nimiäexport-lauseilla (vainusingottaaexport-lauseet huomioon). Paikalliseen moduuliin viittaamiseksi tarvitaan pistesyntaksiausing.Nimi,koska pisteetön syntaksi viittaa Julia-paketteihin.[7]

Juliassa on moduulin luoma globaali näkymä ja tämän sisällä ns. heikkoja ja vahvoja paikallisia näkymiä.if..elsejabeginlauseet eivät luo ollenkaan uutta näkymää, kun taasstruct,for,whilejatrylauseet luovat paikallisen näkymän, jossa globaalien nimien uudelleenkäyttö on kielletty (heikko näkymä). Funktiot ja makrot luovat vahvan paikallisen näkymän eli ulkonäkymien nimiin sijoittaminen on sallittua eikä muokkaa niitä. Kullakin paikallisella näkymällä on kuitenkin lukuoikeus sen ulkonäkymissä määriteltyihin muuttujiin. Esim. funktiot perivät arvot määrittelympäristöstään, eli esim. tietyn moduulin funktiot voivat lukea samoja globaaleja muuttujia moduulistaan ilman erityisiä avainsanoja.[8]

Julian tyyppijärjestelmä on dynaaminen, nominatiivinen, parametrinen ja yhtenäinen. Kaikilla rakenteilla on jokin tyyppi ajon aikana.[9]

Kielen mukana tulevia tietotyyppejä (typeof()) ovat muun muassa seuraavat:

  • AbstractString,AbstractCharja näidenUnicode-UTF-8-alityypitStringjaChar.Substring{String}edustaa tekstiviipaletta. Unicode UTF-8 sisältää joitakin monitavuisia merkkejä, joten turvalliseen indeksointiin voidaan käyttäänextind()-,prevind()- taieachindex()-funktioita.[10]
  • Numberon kaikkien numeroiden (Int64,Real) ylätyyppi.
  • Missingja tämän ainoa arvomissing.Numeerisissa ohjelmissamissingtekee tuloksesta epävarman, joten odotettu tulos on yleensämissing.Tämä ei pidä välttämättä paikkaansa muissa tilanteissa, jolloin voidaan käyttää esim.Missings.passmissingtaiBase.skipmissingfunktioita. Puuttuvia sisältävälle kokoelmalle voidaan kutsua myöscollect().[11]
  • Kaikki funktiot ovat omaa tyyppiänsä jaFunction-tyypin alatyyppejä. Funktioita voidaan syöttää funktioihin, käsitellä funktioissa ja palauttaa funktioista muiden arvotyyppien tapaan.

Uusi yhdistelmätyyppi luodaan sanoillastruct Nimi... end.Instansseja voidaan muokata vain, kun on käytettymutable struct Nimi... end,ellei tyypin alkiota olla määritelty sanallaconst.Tyyppejä voidaan yhdistää myös tyylilläMaybeInt = Union{Int, Nothing}.Tyyppiparametreja käytetään syntaksillastruct Tyyppi{T}... end,mikä määrittelee kerralla joukon tyyppejä. Parametrina olevien tyyppien joukko voidaan rajoittaa alatyypeihin tyylilläTyyppi{T<:YlinTyyppi}.Tyyppi voidaan myös parametrisoida vain osittain syntaksillaArray{T,1} where T.Abstrakteja tyyppejäabstract type Tyyppi <: Supertyyppi endtaas ei voida käyttää suoraan vaan ne täytyy ensin konkretisoida. Tämän jälkeen abstraktia tyyppiä voidaan käyttää viittaamaan kaikkiin tämän alatyyppeihin. (Juliassa ei ole olioperusteisemmille kielille tyypillistä perimismekanismia.)[9]

Luodut tyyppiobjektit toimivat itse alustajinaanstruct Tyyppi... end; t = Tyyppi(...).Tämä alustaja on tavallinen funktio, joten sille voidaan määritellä useita metodejaTyyppi(...) =....Tyypin alustajaa voidaan käyttää myös tyypin omassa määritelmässä erityisennew()-funktion kautta.[12]Alustettujen arvojen tyyppiä voidaan selvissä tapauksissa muuttaaconvert(Tyyppi, arvo)- taipromote(arvo, arvo)-funktiolla. (Numeroita edustavilleString-arvoille ("2.54") löytyy omaparse()-funktionsa.)[13]

Tietylle tyypille määritellään metodeja Juliassa tavallisten funktioiden tapaanfunction f(x::Tyyppi)... end.Metodi ei kuitenkaan kuulu ainoastaan kyseiselle tyypille vaan kutsuttava metodi valitaan koko funktion tyyppijäljen perusteella (engl.multiple dispatch). Funktionkin tyyppijälki voidaan parametrisoida tyylilläfunktio(x::T, y::T, z::T) where {T}... end(eli tämä metodi valitaan, kun kolmen argumentin tyypit ovat samoja). Näin myös objekteista itsestään voidaan tehdä kutsuttavia (ns. funktoreita) antamalla niidentyypillemetodi tyylilläfunction (x::Tyyppi)()... end.[14]Olemassaolevien rajapintojen toteuttaminen tapahtuu toteuttamalla tarvittavien funktioiden metodit – uudesta tyypistä voidaan tehdä muun muassa iteroitava, indeksoitava ja listamainen määrittelemällä sille metodit muutamista erityisistä funktioista kuteniterate,getindexjasize[15].

Funktiot määritelläänfunction nimi(x)... endtai yhdellä rivillänimi(x) =....Nimettömille funktioille voidaan käyttää syntaksia(x, y) ->...taifunction (x)... endtaix -> begin... end.funktio(y) do x... endtekee anonyymin funktiondo-lauseen avulla ja syöttää sen edellä olevan funktion ensimmäiseksi argumentiksi. Funktion voi ns. vektorisoida eli kutsua jonkin kokoelman jokaiselle alkiolle käyttämällä pistesyntaksia kutenx.= sin.(y)(tai sama pistemakrolla@. x = sin(y),kun kaikki operaatiot vektorisoidaan). Funktioiden yhdistäminen on mahdollista muun muassa putkioperaattorillax = 1:3.|> sum |> sqrt.[16]

Funktioiden parametrit ovat Juliassa uusia muuttujia, mutta ne vain viittaavat annettuun arvoon eli kopioita ei synny. Funktiot voidaan tyypittää tyylilläfunction funktio(x::Tyyppi=0, args...; y::Tyyppi= "oletus", kwargs...)::Tyyppi end,mutta yleensä Julian tyypinpäättelyn vuoksi korkeintaan parametrien tyyppien merkitseminen on tarpeen. Funktio voi hyväksyä vaihtelevan määrän parametreja tyylilläfunktio(x,y,z...),missäzonTuplefunktion sisällä tai mikä tahansa iteroitava tyyppi funktion ulkona. Funktioiden tulisi palauttaa vain yhden tyypin arvoja, jolloin tulostyyppi on Julia-kääntäjän pääteltävissä. Tyyppien merkitseminen voi kuitenkin parantaa koodin luettavuutta. Destrukturointi onnistuu taas tyylilläa, b, _ = iteraattori(x).[16]

Sivuvaikutusten takia kutsutut funktiot palauttavat käytännön mukaannothing.Parametrejaan muuttavien funktioiden nimi päättyy huutomerkkiinteejotain!.Funktioiden nimeämiskäytäntö on ilman alaviivoja kutenteenytjotain(),kun tämä vain on luettavissa – muussa tapauksessa sanat erotetaan alaviivalla.[16]

Juliassa myös kaikki operaattorit ovat funktioita: esim.x[i]kutsuugetindex,[1 2 3]kutsuuhcatjax.nimi = ykutsuusetproperty!.[16]

Metaohjelmointi

[muokkaa|muokkaa wikitekstiä]

Metaohjelmointi onnistuu suoraan käsittelemällä Julia-ohjelmien abstraktia syntaksipuuta. Tämä mahdollistaa täyden metaohjelmoinnin. Julia-koodin tyyppi on aluksi tyyppiäString,joka voidaan jäsentääMeta.parse(koodi)lauseketyypinExprarvoksi. Lausekkeita voidaan määritellä myös syntaksilla:( 1 + 2 + $numero )ja usean rivin lausekkeita syntaksillaquote... end.Kaksoispisteellä tehdään myösSymbol-tyypin arvoja:nimi(engl.string interning). Lausekkeet voidaan lopuksi ajaaeval-funktiolla. Lausekkeita voidaan siis käsitellä normaaleissa Julia-funktioissa, mutta nämä funktiot ajetaan vasta ajon aikana, kun taas metaohjelmoinnissa on yleensä tehokkaampaa tuottaa halutut lausekkeet jo käännösvaiheessa.[17]

Käännösaikaiseen metaohjelmointiin tarvitaan makroja. Makrot sijoittavat argumenttinsa suoraan tuloksena tuotettuun lausekkeeseen, joka käännetään sitten normaalin Julia-koodin tapaan. Makrot määritellään tyylillämacro nimi()... endja kutsutaan syntaksilla@ajamakro x ytai@ajamakro(x, y)(tai yhdelle literaalitaulukolle@ajamakro[1, 2, 3]). Makro saa argumenttinsa vain lausekkeinaExpr,symboleina:nimitai literaaleina. Koska Julia on dynaamisesti tyypittävä, makrot eivät tiedä saamiensa arvojen tyyppejä. Makrot noudattelevat muuten paljolti samoja sääntöjä kuin funktiot.[17]

Normaalin funktion ja makron lisäksi voidaan määritellä myös ns. tuotettu funktio tyylillä@generated function nimi()... end,joka asettuu ominaisuuksiltaan funktion ja makron välille. Tällainen funktio ajetaan vasta, kun argumenttien tyypit tiedetään, mutta ennen kuin funktio on käännetty. Makrojen tapaan nämä erityisfunktiot lisäävät abstraktiokykyä ja suorityskykyä siirtämällä laskentaa ajovaiheesta käännösvaiheeseen.[17]

Rinnakkaisohjelmointi

[muokkaa|muokkaa wikitekstiä]

@async-makro tekee annetusta lausekkeesta asynkronisesti ajettavanTask-tyypin objektin, jolle voidaan kutsuaschedule(),wait()jafetch().Vastaavasti@sync-makron avulla voidaan odottaa kaikkien makron sisältämien@async-lausekkeiden valmistumista.Task-objekteja voidaan luoda myös käyttämällä@task-makroa tai tyypin alustajaaTask(() -> x).Channelon taas ikäänkuin rinnakkainen versio generaattorista; kanavan tuottaja kutsuuput-funktiota ja kuluttajatake-funktiota tai iteroi kanavaa muiden iteroitavien tyyppien mukaan.[18]

Julia käyttää oletuksena yhtä säiettä (julia --threads=1). Julia ei myöskään varmista muistiturvallisuutta (vrt.Rust (ohjelmointikieli)) vaan tämä on ohjelmoijan vastuulla. Esim. datakisavirheiden ehkäisemiseksi tulee käyttää lukkomekanismia, kutenReentrantLock(),lock()jaunlock(),tai ns. atomisia arvoja, kutenThreads.Atomic{Tyyppi}(arvo)jaThreads.atomic_add!().Threads-paketti määrittelee helppokäyttöisen@threads-makron, joka tekee annetusta silmukkalausekkeesta automaattisesti monisäikeisen.[19]

julia -p xtekee paikallisen klusterin, jossa on annettu määrä prosesseja, jajulia --machine-file ftekee usean koneen klusterin käyttämällä salasanatonta ssh-viestintää näiden välillä.Distributed-paketti mahdollistaa hajautetun laskennan useissa prosesseissa ja perustuu etäreferensseihin (FuturejaRemoteChannel) ja etäkutsuihin (@spawnat:any...,remotecall()).Future-tyypille kutsutaan lopultafetch()tuloksen saamiseksi pääprosessiin. Lähdetiedosto voidaan lisätä jokaiseen prosessiin tyylillä@everywhere include( "Nimi.jl" )tai tekemällä tästä Julia-paketti. Prosesseja voidaan lisätä, poistaa ja muokata funktioilla kutenaddprocsjarmprocs.EsimerkiksiDistributedArrays-paketinDArrayon taulukon hajautettu versio ja standardikirjastonSharedArraymahdollistaa hajautettujen osasten jakamisen useiden prosessien välillä.[20]

Grafiikkaprosessoreiden käyttöön löytyy tukea muun muassa paketeista CUDA.jl ja MPI.jl.[20]

Dokumentointi onnistuu Juliassa tekstiliteraaleilla kohteena olevan määritelmän yläpuolella (engl.docstring). Teksti voi sisältää Markdown- ja LateX-merkintöjä.[21]

Julian tärkeimpiin siirrännän työkaluihin kuuluvat ensinnäkinstdin- jastdout-virrat, jotka tukevatprint,show,readjawritefunktioita.open-funktio tekee tiedostonimestäIOStream-objektin jaclose-sulkee virran, josopenkutsuttiin ilman funktioargumenttia.Sockets-paketti määrittelee TCP-viestintään pääfunktiotlisten,accept,connectjaclose.[22]

Aiheesta muualla

[muokkaa|muokkaa wikitekstiä]