Julia (ohjelmointikieli)
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]
Käyttöaiheet
[muokkaa|muokkaa wikitekstiä]Lineaarialgebraon yksi numeerisen laskennan keskeisistä alueista, joten tähän löytyy hyvä tuki Julian perus- ja standardikirjastosta (using LinearAlgebra
):[2]
AbstractArray
ja tämän alatyypit mahdollistavat moniulotteiset taulukot suoraanBase
-paketista- Monipuolinen vektori-
[1, 2, 3]
ja matriisisyntaksi[1 2 3; 4 5 6]
- Matriisioperaattorit
+
,-
,*
,/
,⋅
,×
- Jälki
tr(M)
,determinanttidet(M)
ja kääntämineninv(M)
- Eigen-arvot
eigvals(M)
,eigen-vektoriteigvecs(M)
ja faktorointifactorize(M)
- Matriisityypit kuten
Symmetric
,Hermitian
jaTridiagonal
- Faktorointityypit kuten
BunchKaufman
,LU
jaQR
- 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
Käyttö
[muokkaa|muokkaa wikitekstiä]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 --help
näyttää saatavilla olevat asetukset jajulia
kä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]
Esimerkki
[muokkaa|muokkaa wikitekstiä]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
Rakenne
[muokkaa|muokkaa wikitekstiä]Modularisointi
[muokkaa|muokkaa wikitekstiä]JulianPkg
-paketti mahdollistaa vakioitujen ympäristöjen luomisen, pääosinactivate
jaadd
/rm
funktioidensa 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.instantiate
asentaa 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... end
luovat uuden globaalin nimiavaruuden, johon tuodaan nimiäimport
jausing
lauseilla ja lähdetiedostojainclude()
-kutsulla ja josta paljastetaan nimiäexport
-lauseilla (vainusing
ottaaexport
-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..else
jabegin
lauseet eivät luo ollenkaan uutta näkymää, kun taasstruct
,for
,while
jatry
lauseet 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]
Tyypit
[muokkaa|muokkaa wikitekstiä]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
,AbstractChar
ja näidenUnicode-UTF-8-alityypitString
jaChar
.Substring{String}
edustaa tekstiviipaletta. Unicode UTF-8 sisältää joitakin monitavuisia merkkejä, joten turvalliseen indeksointiin voidaan käyttäänextind()
-,prevind()
- taieachindex()
-funktioita.[10]Number
on kaikkien numeroiden (Int64
,Real
) ylätyyppi.Missing
ja tämän ainoa arvomissing
.Numeerisissa ohjelmissamissing
tekee 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.passmissing
taiBase.skipmissing
funktioita. Puuttuvia sisältävälle kokoelmalle voidaan kutsua myöscollect()
.[11]- Kaikki funktiot ovat omaa tyyppiänsä ja
Function
-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 end
taas 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
,getindex
jasize
[15].
Funktiot
[muokkaa|muokkaa wikitekstiä]Funktiot määritelläänfunction nimi(x)... end
tai yhdellä rivillänimi(x) =...
.Nimettömille funktioille voidaan käyttää syntaksia(x, y) ->...
taifunction (x)... end
taix -> begin... end
.funktio(y) do x... end
tekee 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äz
onTuple
funktion 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]
kutsuuhcat
jax.nimi = y
kutsuusetproperty!
.[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)
lauseketyypinExpr
arvoksi. 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()... end
ja kutsutaan syntaksilla@ajamakro x y
tai@ajamakro(x, y)
(tai yhdelle literaalitaulukolle@ajamakro[1, 2, 3]
). Makro saa argumenttinsa vain lausekkeinaExpr
,symboleina:nimi
tai 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)
.Channel
on 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 x
tekee paikallisen klusterin, jossa on annettu määrä prosesseja, jajulia --machine-file f
tekee 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 (Future
jaRemoteChannel
) 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 kutenaddprocs
jarmprocs
.EsimerkiksiDistributedArrays
-paketinDArray
on taulukon hajautettu versio ja standardikirjastonSharedArray
mahdollistaa hajautettujen osasten jakamisen useiden prosessien välillä.[20]
Grafiikkaprosessoreiden käyttöön löytyy tukea muun muassa paketeista CUDA.jl ja MPI.jl.[20]
Muut
[muokkaa|muokkaa wikitekstiä]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
,read
jawrite
funktioita.open
-funktio tekee tiedostonimestäIOStream
-objektin jaclose
-sulkee virran, josopen
kutsuttiin ilman funktioargumenttia.Sockets
-paketti määrittelee TCP-viestintään pääfunktiotlisten
,accept
,connect
jaclose
.[22]
Lähteet
[muokkaa|muokkaa wikitekstiä]- ↑abchttps://docs.julialang.org/en/v1/#man-introduction
- ↑https://docs.julialang.org/en/v1/stdlib/LinearAlgebra/
- ↑https://github.com/JuliaLang/juliaup
- ↑https://docs.julialang.org/en/v1/manual/workflow-tips/
- ↑Pluto.jl.https://plutojl.org/.Viitattu 1.3.2023.
- ↑https://pkgdocs.julialang.org/v1/
- ↑https://docs.julialang.org/en/v1/manual/modules/
- ↑https://docs.julialang.org/en/v1/manual/variables-and-scoping/
- ↑abhttps://docs.julialang.org/en/v1/manual/types/
- ↑https://docs.julialang.org/en/v1/manual/strings/
- ↑https://docs.julialang.org/en/v1/manual/missing/
- ↑https://docs.julialang.org/en/v1/manual/constructors/
- ↑https://docs.julialang.org/en/v1/manual/conversion-and-promotion/
- ↑https://docs.julialang.org/en/v1/manual/methods/
- ↑https://docs.julialang.org/en/v1/manual/interfaces/
- ↑abcdhttps://docs.julialang.org/en/v1/manual/functions/
- ↑abchttps://docs.julialang.org/en/v1/manual/metaprogramming/
- ↑https://docs.julialang.org/en/v1/manual/asynchronous-programming/
- ↑https://docs.julialang.org/en/v1/manual/multi-threading/
- ↑abhttps://docs.julialang.org/en/v1/manual/distributed-computing/
- ↑https://docs.julialang.org/en/v1/manual/documentation/
- ↑https://docs.julialang.org/en/v1/manual/networking-and-streams/
Aiheesta muualla
[muokkaa|muokkaa wikitekstiä]- Julia-kielen kotisivu(englanniksi)
- Julia-kielen lähdekoodi(englanniksi)