Kurzusok
/
Julia
Julia
/
Feladatok
/
Exercism mátrix
Exercism mátrix

Exercism mátrix

Tanulófeladat

Bevezetés

Már régen, a Vectors fogalomban megjegyeztük, hogy „a tömbök tetszőleges méretűek lehetnek (csak a hardver memóriakorlátai szabnak határt), és tetszőleges számú dimenziójuk lehet.”

Azóta nagyrészt figyelmen kívül hagytuk az egynél több dimenziós tömböket, hogy egyszerűen tartsuk a dolgokat. Ez a döntés érthetőbb lesz, ha megpróbálod elolvasni a Julia referenciadokumentumokat, teljes bonyolultságukban.

Ugyanakkor a magasabb dimenziós tömbök nagyon-nagyon fontosak a tudományos számításokban, ezért meg kell értenünk őket.

Nevezéktan: A matematika évszázados hagyományát követve az 1-D tömböket Vectors-nak, a 2-D tömböket Matrices-nak nevezzük.

Ebben a dokumentumban a példák többnyire mátrixok. A 3 vagy több dimenzióval való munka szintaktikailag szinte azonos, de a kimenet nehezen és zavaróan olvasható (egy 2-D képernyőn).

Tömbök létrehozása

Rengeteg vektort hoztunk létre úgy, hogy vesszővel elválasztott listát tettünk szögletes zárójelbe. A pontosvessző is használható elválasztóként.

julia> v = [1, 2, 3]
3-element Vector{Int64}:
 1
 2
 3

julia> w = [1; 2; 3]
3-element Vector{Int64}:
 1
 2
 3

julia> v == w
true

Ha szóközt (vagy tabulátort) használunk elválasztóként, az eredmény más lesz.

julia> u = [1 2 3]
1×3 Matrix{Int64}:
 1  2  3

Ezt a Julia most 1×3-as mátrixként definiálja (más összefüggésben row vector-nak, azaz sorevektornak neveznénk).

Általában a szóközök vízszintesen fűzik össze a dolgokat, a pontosvesszők (vagy az újsorok) pedig függőlegesen.

A „dolgok” kifejezés szándékosan homályos, mivel a Julia megpróbál bármivel dolgozni, amit adsz neki.

julia> [v 2v]
3×2 Matrix{Int64}:
 1  2
 2  4
 3  6

julia> [v; 2v]
6-element Vector{Int64}:
 1
 2
 3
 2
 4
 6

Léteznek hcat() és vcat() függvények, amelyek ugyanezt teszik, világosabbá téve, hogy ezek vízszintes és függőleges összefűzések. A magasabb dimenziós általánosítás a cat() függvény.

jjulia> hcat(v, 2v)
3×2 Matrix{Int64}:
 1  2
 2  4
 3  6

julia> vcat(v, 2v)
6-element Vector{Int64}:
 1
 2
 3
 2
 4
 6

Explicit mátrixok beírása kényelmesen sor-major sorrendben történik, mert ez illeszkedik az emberi intuícióhoz (könnyebb ránézni, a vízszintes írású kultúrákban):

julia> m = [1 2 3; 4 5 6]
2×3 Matrix{Int64}:
 1  2  3
 4  5  6

Jegyezd meg azonban, hogy a Julia (a Fortranhoz, R-hez és Matlabhoz hasonlóan, de ellentétben a C/C++-szal vagy a NumPy-val) oszlop-major sorrendben tárolja az N-dimenziós tömböket, és ez hatalmas teljesítménykülönbséget jelenthet, ha végigiterálsz az elemeken. Segíts a CPU cache-nek, hogy segíthessen neked!

# put these integers in 2 rows and 3 columns

julia> reshape(collect(1:6), 2, 3)
2×3 Matrix{Int64}:
 1  3  5
 2  4  6

A fenti példa az 1-től 6-ig terjedő egészeket veszi, és oszloponként tölti fel velük a 2×3-as mátrixot.

Különféle segédfüggvények léteznek gyakori tömbtípusok létrehozására (egyenletes vagy véletlen).

julia> zeros(2, 3)  # see also ones()
2×3 Matrix{Float64}:
 0.0  0.0  0.0
 0.0  0.0  0.0

julia> falses(2, 2)  # booleans, see also trues()
2×2 BitMatrix:
 0  0
 0  0

julia> rand(Float32, 2, 3)  # random numbers in the interval [0, 1)
2×3 Matrix{Float32}:
 0.768823  0.169633  0.632565
 0.388451  0.109176  0.850381

Indexelés

Egy 2-D tömb esetén általában két indexet használunk [row, col] sorrendben.

julia> m
2×3 Matrix{Int64}:
 1  2  3
 4  5  6

julia> m[1, 2] # row 1, col 2
2

# Stay within bounds! There is no row 3.
julia> m[3, 1]
ERROR: BoundsError: attempt to access 2×3 Matrix{Int64} at index [3, 1]

julia> m[3]
2

Az utolsó példa talán meglepő: egy egyszerű index nem hiba, és egyetlen elemet ad vissza.

A magyarázat visszavezet a column-major sorrendről szóló megjegyzéshez: a Julia lefelé halad az 1. oszlopban, majd a 2. oszlopban, amíg meg nem találja a 3. elemet a memóriában.

Légy óvatos: ennek vannak bizonyos felhasználásai általános célú könyvtárak írásakor, de nagyobb valószínűséggel zavaró!

Hasonló probléma merül fel egy tömb méretének lekérdezésekor. A length() az elemek teljes számát adja, a size() pedig egy ndims() elemű tuplet ad vissza az egyes dimenziók hosszával.

julia> m
2×3 Matrix{Int64}:
 1  2  3
 4  5  6

julia> length(m)
6

julia> size(m)  # 2 rows, 3 cols
(2, 3)

julia> ndims(m)  # how many dimensions? Like `m |> size |> length`
2

Indexelés tartományokkal és tömbökkel

Könnyedén másolhatunk részmátrixot.

Az alábbi példában a reshape() függvény a megadott dimenziókra kényszeríti a tömböt, oszloponként feltöltve, majd felszeleteljük.

julia> m12 = reshape(collect(1:12), 4, 3)
4×3 Matrix{Int64}:
 1  5   9
 2  6  10
 3  7  11
 4  8  12

julia> m12[2:4, 1:2]
3×2 Matrix{Int64}:
 2  6
 3  7
 4  8

# some rows, all columns
julia> m12[2:4, :]
3×3 Matrix{Int64}:
 2  6  10
 3  7  11
 4  8  12

Egy önmagában álló : azt jelenti, hogy „másolj le mindent ebben a dimenzióban”.

Nem egymást követő sorokhoz/oszlopokhoz vektorokat használj:

julia> m12[[1, 3], :]  # rows 1 and 3
2×3 Matrix{Int64}:
 1  5   9
 3  7  11

Függvények alkalmazása tömbökre

Korábban már láttunk olyan aggregációs függvényeket, mint a sum() és a maximum(), 1-D gyűjteményekre alkalmazva, ahol az összes elemen működnek, és skaláris eredményt adnak vissza.

Ez magasabb dimenziókban is működik. Előfordulhat azonban, hogy a függvényt csak egy dimenzióra szeretnénk alkalmazni, például lefelé vagy keresztben összegezve, hogy egy 1-es méretű singleton dimension-nel rendelkező tömböt kapjunk.

Ehhez van egy opcionális dims kulcsszó argumentum.

julia> m
2×3 Matrix{Int64}:
 1  2  3
 4  5  6

julia> sum(m)  # sum everything
21

julia> sum(m; dims=1)  # sum down
1×3 Matrix{Int64}:
 5  7  9

julia> sum(m; dims=2)  # sum across
2×1 Matrix{Int64}:
  6
 15

# other aggregation functions are similar
julia> maximum(m; dims=2)
2×1 Matrix{Int64}:
 3
 6

A dims értéke az a dimenzió, amely singletonná válik, tehát a fenti példákban dims=1 => 1×3 és dims=2 => 2×1.

Kiterjesztésképpen a magasabb dimenziós tömbök több dimenziót is redukálhatnak, ha a dims értéke tömb vagy tartomány (a kimenet megértése ismét némi gondolkodást igényelhet!).

Dimenzió-tudatos függvények írása

A dims kulcsszó argumentum gyakori a beépített függvényekben, mint a sum(), de hogyan írjunk ehhez hasonlót a saját kódunkban?

Az egyik jó válasz a magasabb rendű függvények, például a reduce() használata, amit egy későbbi fogalomban részletesebben is tárgyalunk.

Alternatívaként a Julia több függvényt is kínál, amelyek segítségével az N-dimenziós tömböket úgy kezelhetjük, mintha vektorok egymásba ágyazott vektorai lennének.

Mátrixokhoz a eachrow() és az eachcol() kényelmes, de az általánosabb függvény az eachslice(), amely tetszőleges dimenziókat képes kezelni.

# m is as in the previous examples

julia> eachrow(m)
2-element RowSlices{Matrix{Int64}, Tuple{Base.OneTo{Int64}}, SubArray{Int64, 1, Matrix{Int64}, Tuple{Int64, Base.Slice{Base.OneTo{Int64}}}, true}}:
 [1, 2, 3]
 [4, 5, 6]

julia> eachcol(m)
3-element ColumnSlices{Matrix{Int64}, Tuple{Base.OneTo{Int64}}, SubArray{Int64, 1, Matrix{Int64}, Tuple{Base.Slice{Base.OneTo{Int64}}, Int64}, true}}:
 [1, 4]
 [2, 5]
 [3, 6]

A típus kissé ijesztő, de csak azért, mert ez egy nézet az eredeti tömbbe, ami lehetővé teszi, hogy másolás nélkül dolgozzunk rajta. A Julia tömbök akár több terabájt méretűek is lehetnek, így a másolás potenciális teljesítményrémálom!

Egy ilyen nézet használható ciklusokhoz, broadcastinghez, vagy bármely más művelethez, amit eddig a tantervben láttunk.

A tömbkifejezések (comprehensions) is hatékonyak és sokoldalúak lehetnek tömbbemenettel. Az egyszerű eseteket a Loops fogalomban említettük, de egy későbbi fogalomban bővebb tárgyalás következik.

Broadcasting több dimenzióban

Az 1-D esetet a Vector Operations fogalomban tárgyaltuk.

julia> v = [1.2, 1.5, 1.7]
3-element Vector{Float64}:
 1.2
 1.5
 1.7

julia> v .- 0.5
3-element Vector{Float64}:
 0.7
 1.0
 1.2

A pontozott .- operátor a 0.5 singletont [0.5, 0.5, 0.5]-tel egyenértékűnek tekinti úgy, hogy broadcasting-gal a dimenziókhoz igazítja, majd elemenként elvégzi a kivonást.

Ennek kiterjesztése magasabb dimenziókra valójában csak ugyanaz ismételve.

Például amikor egy 2x1-es sorevektort [1.0 1.5 2.0] összeszorzunk a 2x3-as mátrixszal [1 2 3; 4 5 6], az egyenértékű azzal, hogy a [1.0 1.5 2.0]-t [1.0 1.5 2.0; 1.0 1.5 2.0]-re broadcastoljuk, majd a szorzást elemenként végezzük el.

julia> m
2×3 Matrix{Int64}:
 1  2  3
 4  5  6

julia> m .* [1.0 1.5 2.0]  # broadcast a 1x3 row vector down
2×3 Matrix{Float64}:
 1.0  3.0   6.0
 4.0  7.5  12.0

julia> m .* [1.0 1.5 2.0; 1.0 1.5 2.0]  # 2x3 elementwise multiplication
2×3 Matrix{Float64}:
 1.0  3.0   6.0
 4.0  7.5  12.0

julia> m ./ [1, 2]  # broadcast a 2x1 column vector across
2×3 Matrix{Float64}:
 1.0  2.0  3.0
 2.0  2.5  3.0

julia> m ./ [1 1 1; 2 2 2]  # 2x3 elementwise division
2×3 Matrix{Float64}:
 1.0  2.0  3.0
 2.0  2.5  3.0

Megjegyzés: A nem-singleton dimenzióknak egyezniük kell méretben (pl. 2x3 Matrix .* 2x1 Matrix) a broadcastinghez.

Továbbá egy függvény ugyanúgy broadcastolható egy Matrix minden elemére, mint ahogy egy Vector esetében tesszük.

julia> m
2×3 Matrix{Int64}:
 1  2  3
 4  5  6

julia> (x -> x^2).(m)  # broadcast a function to all elements
2×3 Matrix{Int64}:
  1   4   9
 16  25  36

Eleinte úgy érezheted, hogy a programozói zavar lehetősége exponenciálisan nő a dimenziók számával, de a gyakorlat sokat segít. Továbbá a NumPy tömbökkel való ismeretség meglehetősen jól átültethető Juliára, a szintaxis eltérései ellenére.

Utasítások

A pontmátrix egy kétdimenziós kép, amely pontokból és üres helyekből áll. A pontok alkotják a kívánt képet az üres helyek hátterén.

A pontmátrixos kép a memóriában kétdimenziós Matrix-ként tárolható és manipulálható. A következőkben a mátrixokban az „üres helyeket” 0 jelöli, a „pontok” pedig nem nullák.

1. Definiáld az Exercism logó Matrix-ot

A Matrix így néz ki, ahol a 0 az üres hely, az 1 pedig a pont:

[
    0 0 1 1 0 0 0 0 0 0 0 0 0 0 1 1 0 0;
    0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0;
    0 1 0 0 0 1 0 0 0 0 0 0 1 0 0 0 1 0;
    0 1 0 0 1 0 1 0 0 0 0 1 0 1 0 0 1 0;
    0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0;
    1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1;
    0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0;
    0 1 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1 0;
    0 1 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0;
    0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0;
    0 0 1 1 0 0 0 0 0 0 0 0 0 0 1 1 0 0;
]

Ha „rendereled”, így néz ki:

  XX          XX  
 X              X 
 X   X      X   X 
 X  X X    X X  X 
 X              X 
X                X
 X    X    X    X 
 X     X  X     X 
 X      XX      X 
 X              X 
  XX          XX

2. Definiálj függvényeket, amelyektől a logó elkomorodik

Definiáld a frown!() és frown() függvényeket, amelyek az Exercism logó Matrix-ot kapják. Adj vissza egy Matrix-ot, amelyben a mosolygó száj helyett komor száj szerepel.

A kapott Matrix renderelve így néz ki:

  XX          XX
 X              X
 X   X      X   X
 X  X X    X X  X
 X              X
X                X
 X      XX      X
 X     X  X     X
 X    X    X    X
 X              X
  XX          XX

3. Rakj össze egy matricafalat

Definiáld a stickerwall() függvényt, amely bemenetként az Exercism mátrixot kapja. Adj vissza egy Matrix-ot, amely renderelve így néz ki:

  XX          XX    XX          XX
 X              X  X              X
 X   X      X   X  X   X      X   X
 X  X X    X X  X  X  X X    X X  X
 X              X  X              X
X                XX                X
 X    X    X    X  X      XX      X
 X     X  X     X  X     X  X     X
 X      XX      X  X    X    X    X
 X              X  X              X
  XX          XX    XX          XX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  XX          XX    XX          XX
 X              X  X              X
 X   X      X   X  X   X      X   X
 X  X X    X X  X  X  X X    X X  X
 X              X  X              X
X                XX                X
 X      XX      X  X    X    X    X
 X     X  X     X  X     X  X     X
 X    X    X    X  X      XX      X
 X              X  X              X
  XX          XX    XX          XX

4. Alakítsd a pontokat oszloponkénti pixelszámokká

Nem vagyunk arra korlátozva, hogy a pont értéke 1 legyen, így ha akarjuk, más hasznos információt is kódolhatunk vele. Definiáld a colpixelcount() függvényt, amely bemenetként bármilyen, 1-es pontokból álló pontmátrixot kaphat. Adj vissza egy ugyanolyan méretű pontmátrixot, amelyben minden oszlop pontjainak értéke az adott oszlopban lévő pontok száma.

Ha az Exercism logó Matrix-ot adjuk be bemenetként, a kimenet a következő Matrix:

[
    0 0 2 2 0 0 0 0 0 0 0 0 0 0 2 2 0 0;
    0 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0;
    0 8 0 0 0 1 0 0 0 0 0 0 1 0 0 0 8 0;
    0 8 0 0 1 0 2 0 0 0 0 2 0 1 0 0 8 0;
    0 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0;
    1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1;
    0 8 0 0 0 0 2 0 0 0 0 2 0 0 0 0 8 0;
    0 8 0 0 0 0 0 1 0 0 1 0 0 0 0 0 8 0;
    0 8 0 0 0 0 0 0 1 1 0 0 0 0 0 0 8 0;
    0 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0;
    0 0 2 2 0 0 0 0 0 0 0 0 0 0 2 2 0 0;
]

5. Renderelj egy pontmátrixot

Ahelyett, hogy csak beszélnénk a kreációink rendereléséről, szeretnénk ezt ténylegesen is meg tudni tenni, hogy könnyen megnézhessük őket. Definiáld a render() függvényt, amely bemenetként egy pontmátrixot kap. Adj vissza egy stringet, amelyben a pontok 'X'-ként, a 0-k ' '-ként jelennek meg, és az egyes sorokat újsorok kötik össze.

julia> render(E)
"  XX          XX  \n X              X \n X   X      X   X \n X  X X    X X  X \n X              X \nX                X\n X    X    X    X \n X     X  X     X \n X      XX      X \n X              X \n  XX          XX  "

Kiírva az Exercism Matrix a várt módon jelenik meg.

julia> print(render(E))
  XX          XX
 X              X
 X   X      X   X
 X  X X    X X  X
 X              X
X                X
 X    X    X    X
 X     X  X     X
 X      XX      X
 X              X
  XX          XX

Ennek akkor is működnie kell, ha a „pontok” nem 1-esek:

julia> print(render(colpixelcount(E)))
  XX          XX
 X              X
 X   X      X   X
 X  X X    X X  X
 X              X
X                X
 X    X    X    X
 X     X  X     X
 X      XX      X
 X              X
  XX          XX
Szerkesztés GitHubon A hivatkozás új ablakban vagy lapon nyílik meg
Julia Exercism

Készen állsz elkezdeni a(z) Exercism mátrix feladatot?

Iratkozz fel az Exercism-re, hogy megtanuld és elsajátítsd a(z) Julia nyelvet 35 fogalom128 feladat segítségével, valódi emberi mentorálással, mindez ingyen.