A Vectors fogalomban azt mondtuk, hogy „a tömbök a Julia nyelv szívében vannak”, és a vektor egy egydimenziós tömb.
Ezt figyelembe véve joggal remélhetjük, hogy a nyelv sok sokoldalú és hatékony módot kínál arra, hogy dolgokat csináljunk a vektorokkal, bármit is jelentsen ez.
Egy megjegyzés a terminológiához: bár ez a dokumentum sokat beszél a „vektorokról”, nagy része bármilyen iterálható típusra is vonatkozik: tartományok, tuple-ök, halmazok és egyéb másokra.
Néhány nagyon egyszerű függvény vektor bemenetet vesz, és (egydimenziós bemenet esetén) skalár kimenetet ad vissza.
v = [2, 3, 4]
length(v) # => 3
sum(v) # => 9
Amikor eljutunk a többdimenziós tömbök fogalmához, világosabb lesz, hogy ez dimenziócsökkentés, nem pedig feltétlenül skalár visszaadása. Ha ez most nem világos, egyelőre ne aggódj miatta.
Van még sok ilyen típusú függvény.
Nézd meg a Statistics fogalmat néhány példáért.
Vannak olyan függvények is, amelyek több vektoron működnek, például a (nagyon hasznos) zip.
julia> z = zip( 1:3, ['a', 'b', 'c'], ["I", "make", "tuples"] )
zip([1, 2, 3], ['a', 'b', 'c'], ["I", "make", "tuples"])
# convert iterator to vector
julia> collect(z)
3-element Vector{Tuple{Int64, Char, String}}:
(1, 'a', "I")
(2, 'b', "make")
(3, 'c', "tuples")
A zip() tetszőleges számú vektorszerű bemenetet vesz, és tuple-ök iterátorát adja vissza.
A bemenetek általában ugyanolyan hosszúak. Ha az egyik rövidebb, a többit a legrövidebb hosszára vágja: lehet, hogy ezt akartad, de gyakrabban ez hiba a kódodban.
Tegyük fel, hogy van egy numerikus vektorod, és minden értékből le akarsz vonni 0,5-öt.
julia> v = [1.2, 1.5, 1.7]
3-element Vector{Float64}:
1.2
1.5
1.7
julia> v - 0.5
ERROR: MethodError: no method matching -(::Vector{Float64}, ::Float64)
Ez nem működik, szóval mi a helyzet egy másik vektor kivonásával?
julia> v - [0.5, 0.5, 0.5]
3-element Vector{Float64}:
0.7
1.0
1.2
Sikeres, de elég unalmas és memóriaigényes, ahogy a vektorok hosszabbak lesznek.
Attól függően, hogy hol tartasz a tantervben, valószínűleg más megközelítéseket is ki tudsz találni:
[x - 0.5 for x in v] megadja a kívánt eredményt (Python-stílus).map(x -> x - 0.5, v) szintén működik (Haskell-stílus, bár sok nyelvben gyakori).Szerencsére a Juliának van egy „varázslatos” pontja, amellyel nagyon egyszerűen megoldható ez a probléma: csak v .- 0.5 kell.
A következő szakasz elmagyarázza, miért.
Tehát a v - 0.5 hibát ad, a v .- 0.5 viszont sikeres, és meg kell értenünk, mit csinál a pont.
Két dolog, amelyek együtt adják a kívánt eredményt.
Először is, ha egy pontot bármely infix operátor elé teszel, az azt jelenti, hogy „ezt a műveletet külön-külön minden elemre alkalmazd”.
Hasonlóképpen, ha egy függvénynév után teszel egy pontot, az „vektorizálja” a függvényt, még akkor is, ha skalár bemenetekre írták.
julia> sqrt.([1, 4, 9])
3-element Vector{Float64}:
1.0
2.0
3.0
Zárójelben jegyezzük meg, hogy az infix operátorok valójában csak szintaktikai cukor a mögöttes függvényhez.
Ez azt jelenti, hogy például a [1, 5, 10] .% 3 kifejezést az értelmező mod.([1, 5, 10], 3)-ra fordítja, majd a mod. szintaxis lefut (mindkét változat [1, 2, 1]-t ad vissza).
Egy korábbi példában láttuk, hogy ki tudunk vonni egyenlő hosszú vektorokat, de kérlek, értsd meg, hogy a .- biztonságosabb operátor, mint a -, mert egyértelművé teszi az elemenkénti szándékot.
julia> v .- [0.5, 0.5, 0.5]
3-element Vector{Float64}:
0.7
1.0
1.2
Mi a helyzet a nem egyenlő hosszú vektorokkal?
julia> v .- [0.5, 0.5]
ERROR: DimensionMismatch: arrays could not be broadcast to a common size
julia> v .- [0.5,]
3-element Vector{Float64}:
0.7
1.0
1.2
Általában a nem egyenlő hosszúságok hiba, kivéve, ha az egyik hossza 1 (technikailag „egyelemű” dimenzió).
Az olyan egyelemű értékeket, mint a [0.5,] vagy egyszerűen a 0.5, a rendszer automatikusan kiterjeszti a szükséges hosszra ismétléssel.
Ez van a broadcasting szívében.
Aki aggódik az ebből az „ismétlésből” származó memóriahasználat miatt, megnyugodhat: nagyon hatékony módon van megvalósítva, amely valójában nem másolja a memóriában az értékeket.
A más nyelvekbeli broadcastingot ismerő programozók vegyék észre, hogy a Julia megközelítése (nagyrészt) hasonló a NumPy-hoz, de sokkal kevésbé tűri a méretek eltérését, mint az R.
Ha a memóriahasználat aggaszt, akkor a helyben végzett műveletek gyakori módot jelentenek az allokációk csökkentésére.
A fenti példákban szereplő broadcasting műveletek azonban új Vector-t hoznak létre ahelyett, hogy az eredetit módosítanák.
julia> v = [1, 2, 3]
3-element Vector{Int64}:
1
2
3
julia> v .+ 1
3-element Vector{Int64}:
2
3
4
julia> v
3-element Vector{Int64}:
1
2
3
A v helyben történő módosításához a módosítást broadcastolni kell.
julia> v .= v .+ 1
3-element Vector{Int64}:
2
3
4
julia> v .+= 1 # equivalent operation to above
3-element Vector{Int64}:
3
4
5
julia> v
3-element Vector{Int64}:
3
4
5
De légy óvatos! A pont az értékadó operátor, a .= előtt fontos.
julia> v = [1, 2, 3];
julia> v = v .+ 1
3-element Vector{Int64}:
2
3
4
julia> v
3-element Vector{Int64}:
2
3
4
Ez úgy tűnik, ugyanúgy működött, de itt a v .+ 1 létrehozott egy új [2, 3, 4] vektort, majd hozzárendelte a v változóhoz, így a kezdeti [1, 2, 3] vektor a memóriában maradt, hogy a szemétgyűjtő eltakarítsa.
Ez végül kétszer annyi memóriát használ, mint az előző példa, amely újrahasznosítja a kezdeti vektor számára lefoglalt memóriát.
Hasonlóképpen, vannak kapcsolódó finomságok, amikor ugyanolyan méretű vektorokkal broadcastolunk.
Például adott két ugyanolyan méretű vektor, v és w:
v .= w a w egy copy példányát hozza létre a v memóriahelyén. A további változtatások a v-ben nem érintik a w-t, és viszont.v = w egy másik mutatót hoz létre a w memóriahelyére v néven. A további változtatások a v-ben megjelennek a w-ben, és viszont.Ez az alszakasz eléggé matematikaigényes, ezért a legtöbb tanulótól nem várjuk el, hogy igazán megértse. Mindazonáltal hasznos figyelmeztetés, amely segíthet a hibakeresésben, amikor váratlan hibaüzeneteket látsz.
julia> v = [1, 2, 3]
3-element Vector{Int64}:
1
2
3
julia> v * v
ERROR: MethodError: no method matching *(::Vector{Int64}, ::Vector{Int64})
# look, no commas
julia> u = [1 2 3]
1×3 Matrix{Int64}:
1 2 3
julia> u * v
1-element Vector{Int64}:
14
julia> v * u
3×3 Matrix{Int64}:
1 2 3
2 4 6
3 6 9
Ha véletlenül lineáris algebrai háttérrel rendelkezel, akkor (1) nem vagy tipikus Exercism-felhasználó (de nagyon szívesen látunk itt!) és (2) felismerheted, hogy a v oszlopvektor, az u sorvektor, az u * v belső szorzat, a v * u pedig külső szorzat.
A Julia követi a matematika szabályait, ebben is, mint mindenben.
Mindenki másnak: kérlek, értsd meg, miért javasoljuk, hogy elemenkénti számításokhoz mindig pontozott operátorokat használj: a v .* v pontosan úgy működik, ahogy várnád, és [1, 4, 9]-et ad.
A vektorok elemeinek indexszám szerinti kiválasztásáról korábbi fogalmakban volt szó.
a = collect('A':'Z') # => 26-element Vector{Char}
# index with an integer
a[2] # => 'B'
# index with a range
a[12:2:18] # => ['L', 'N', 'P, 'R']
# index with another vector
a[ [1, 3, 5] ] # => ['A', 'C', 'E']
Lehetőség van olyan elemek kiválasztására is, amelyek kielégítenek valamilyen logikai kifejezést (technikailag egy „predikátumot”). Ez általában broadcastingot igényel.
julia> a[a .< 'D']
3-element Vector{Char}:
'A': ASCII/Unicode U+0041 (category Lu: Letter, uppercase)
'B': ASCII/Unicode U+0042 (category Lu: Letter, uppercase)
'C': ASCII/Unicode U+0043 (category Lu: Letter, uppercase)
Bonyolultabb kifejezéseknél a pontok hajlamosak elszaporodni (de kicsik és könnyű őket beírni).
julia> a[a .< 'D' .|| a .> 'W']
6-element Vector{Char}:
'A': ASCII/Unicode U+0041 (category Lu: Letter, uppercase)
'B': ASCII/Unicode U+0042 (category Lu: Letter, uppercase)
'C': ASCII/Unicode U+0043 (category Lu: Letter, uppercase)
'X': ASCII/Unicode U+0058 (category Lu: Letter, uppercase)
'Y': ASCII/Unicode U+0059 (category Lu: Letter, uppercase)
'Z': ASCII/Unicode U+005A (category Lu: Letter, uppercase)
Emlékeztetőül: a „vektor” valójában bármilyen megfelelő rendezett iterálható lehet, például egy tartomány:
julia> n = 3:10
3:10
julia> n[isodd.(n)]
4-element Vector{Int64}:
3
5
7
9
Belül a predikátum BitVector-té alakul, amelyet aztán indexként használunk.
julia> condition = a .< 'D'
26-element BitVector:
1
1
1
0
# display truncated
julia> a[condition]
3-element Vector{Char}:
'A': ASCII/Unicode U+0041 (category Lu: Letter, uppercase)
'B': ASCII/Unicode U+0042 (category Lu: Letter, uppercase)
'C': ASCII/Unicode U+0043 (category Lu: Letter, uppercase)