Aceasta este comanda PDL::Threadingp care poate fi rulată în furnizorul de găzduire gratuit OnWorks folosind una dintre multiplele noastre stații de lucru online gratuite, cum ar fi Ubuntu Online, Fedora Online, emulator online Windows sau emulator online MAC OS
PROGRAM:
NUME
PDL::Threading - Tutorial pentru caracteristica PDL Threading
INTRODUCERE
Una dintre cele mai puternice caracteristici ale PDL este filetat, care poate produce foarte compact și
cod PDL foarte rapid prin evitarea mai multor bucle imbricate pentru care pot fi utilizatorii C și BASIC
obișnuit cu. Problema este că poate dura ceva să te obișnuiești, iar utilizatorii noi s-ar putea să nu
apreciați beneficiile filetării.
Alte limbaje bazate pe vectori, cum ar fi MATLAB, folosesc un subset de tehnici de threading, dar
PDL strălucește prin generalizarea completă a acestora pentru tot felul de aplicații bazate pe vectori.
TERMINOLOGIE: FACE PIPI
MATLAB se referă de obicei la vectori, matrice și matrice. Perl are deja matrice, iar
termenii "vector" și "matrice" se referă în mod obișnuit la colecții uni- și bidimensionale de
date. Neavând un termen bun pentru a descrie obiectul lor, dezvoltatorii PDL au inventat termenul
"face pipi" pentru a da un nume tipului lor de date.
A face pipi constă dintr-o serie de numere organizate ca un set de date N-dimensional. Pădule
asigură stocarea eficientă și calcularea rapidă a matricelor mari N-dimensionale. Sunt
foarte optimizat pentru lucru numeric.
GÂNDIRE IN TERMENI OF FILETAT
Dacă ați folosit deja PDL de puțin timp, este posibil să fi folosit threading fără
dându-și seama. Porniți shell-ul PDL (tastați „perldl” sau „pdl2” pe un terminal). Cele mai multe exemple
în acest tutorial utilizați shell-ul PDL. Asigurați-vă că PDL::NiceSlice și PDL::AutoLoader sunt
activat. De exemplu:
% pdl2
perlDL shell v1.352
...
ReadLines, NiceSlice, MultiLines activate
...
Notă: AutoLoader nu este activat (se recomandă „utilizați PDL::AutoLoader”)
pdl>
În acest exemplu, NiceSlice a fost activat automat, dar AutoLoader nu a fost. Pentru a permite
acesta, tastați „utilizați PDL::AutoLoader”.
Să începem cu un bidimensional face pipi:
pdl> $a = secvență(11,9)
pdl> p $a
[
[ 0 1 2 3 4 5 6 7 8 9 10]
[11 12 13 14 15 16 17 18 19 20 21]
[22 23 24 25 26 27 28 29 30 31 32]
[33 34 35 36 37 38 39 40 41 42 43]
[44 45 46 47 48 49 50 51 52 53 54]
[55 56 57 58 59 60 61 62 63 64 65]
[66 67 68 69 70 71 72 73 74 75 76]
[77 78 79 80 81 82 83 84 85 86 87]
[88 89 90 91 92 93 94 95 96 97 98]
]
Metoda „informații” vă oferă informații de bază despre a face pipi:
pdl> p $a->info
PDL: Dublu D [11,9]
Acest lucru ne spune că $a este un 11 x 9 face pipi compus din numere de dublă precizie. Dacă noi
am vrut să adauge 3 la toate elementele dintr-un piddle „nxm”, o limbă tradițională ar folosi două
bucle for imbricate:
# Pseudo cod. Mod tradițional de a adăuga 3 la o matrice.
pentru (x=0; x < n; x++) {
pentru (y=0; y < m; y++) {
a(x,y) = a(x,y) + 3
}
}
notițe: Observați că indicii încep de la 0, ca în Perl, C și Java (și spre deosebire de MATLAB și IDL).
Dar cu PDL, putem doar să scriem:
pdl> $b = $a + 3
pdl> p $b
[
[ 3 4 5 6 7 8 9 10 11 12 13]
[ 14 15 16 17 18 19 20 21 22 23 24]
[ 25 26 27 28 29 30 31 32 33 34 35]
[ 36 37 38 39 40 41 42 43 44 45 46]
[ 47 48 49 50 51 52 53 54 55 56 57]
[ 58 59 60 61 62 63 64 65 66 67 68]
[ 69 70 71 72 73 74 75 76 77 78 79]
[ 80 81 82 83 84 85 86 87 88 89 90]
[ 91 92 93 94 95 96 97 98 99 100 101]
]
Acesta este cel mai simplu exemplu de threading și este ceva ce toate programele numerice
instrumentele fac. Operația „+ 3” a fost aplicată automat pe două dimensiuni. Acum să presupunem
vrei să scazi o linie din fiecare rând din $a:
pdl> $line = secvenţă(11)
pdl> p $line
[0 1 2 3 4 5 6 7 8 9 10]
pdl> $c = $a - $linie
pdl> p $c
[
[ 0 0 0 0 0 0 0 0 0 0 0]
[11 11 11 11 11 11 11 11 11 11 11]
[22 22 22 22 22 22 22 22 22 22 22]
[33 33 33 33 33 33 33 33 33 33 33]
[44 44 44 44 44 44 44 44 44 44 44]
[55 55 55 55 55 55 55 55 55 55 55]
[66 66 66 66 66 66 66 66 66 66 66]
[77 77 77 77 77 77 77 77 77 77 77]
[88 88 88 88 88 88 88 88 88 88 88]
]
Două lucruri de remarcat aici: În primul rând, valoarea lui $a este în continuare aceeași. Încercați „p $a” pentru a verifica.
În al doilea rând, PDL a scăzut automat $line din fiecare rând din $a. De ce a făcut asta? hai sa
uitați-vă la dimensiunile lui $a, $line și $c:
pdl> p $line->info => PDL: Double D [11]
pdl> p $a->info => PDL: Double D [11,9]
pdl> p $c->info => PDL: Double D [11,9]
Deci, atât $a cât și $line au același număr de elemente în dimensiunea 0! Ce PDL atunci
did a fost trecerea peste dimensiunile superioare din $a și a repetat aceeași operațiune de 9 ori pentru
toate rândurile de pe $a. Acesta este threadingul PDL în acțiune.
Ce se întâmplă dacă doriți să scădeți $line din prima linie numai în $a? Puteți face asta prin
specificând linia în mod explicit:
pdl> $a(:,0) -= $line
pdl> p $a
[
[ 0 0 0 0 0 0 0 0 0 0 0]
[11 12 13 14 15 16 17 18 19 20 21]
[22 23 24 25 26 27 28 29 30 31 32]
[33 34 35 36 37 38 39 40 41 42 43]
[44 45 46 47 48 49 50 51 52 53 54]
[55 56 57 58 59 60 61 62 63 64 65]
[66 67 68 69 70 71 72 73 74 75 76]
[77 78 79 80 81 82 83 84 85 86 87]
[88 89 90 91 92 93 94 95 96 97 98]
]
Consultați PDL::Indexing și PDL::NiceSlice pentru a afla mai multe despre specificarea subseturilor din piddles.
Adevărata putere a firului vine atunci când îți dai seama că piddle-ul poate avea orice număr de
dimensiuni! Să facem un piddle cu 4 dimensiuni:
pdl> $piddle_4D = secvență(11,3,7,2)
pdl> $c = $piddle_4D - $line
Acum $c este un piddle de aceeași dimensiune ca $piddle_4D.
pdl> p $piddle_4D->info => PDL: Double D [11,3,7,2]
pdl> p $c->info => PDL: Double D [11,3,7,2]
De data aceasta, PDL a trecut automat peste trei dimensiuni superioare, scăzând $line
la tot pasul.
Dar, poate nu vrei să scazi din rânduri (dimensiunea 0), ci din coloane
(dimensiunea 1). Cum scad o coloană de numere din fiecare coloană din $a?
pdl> $cols = secvenţă(9)
pdl> p $a->info => PDL: Double D [11,9]
pdl> p $cols->info => PDL: Double D [9]
Desigur, nu putem introduce doar „$a - $cols”. Dimensiunile nu se potrivesc:
pdl> p $a - $cols
PDL: PDL::Ops::minus(a,b,c): Parametrul „b”
PDL: dimensiunea firului implicit nepotrivită 0: ar trebui să fie 11, este 9
Cum îi spunem PDL că vrem să scădem din dimensiunea 1?
MANIPULARE DIMENSIUNI
Există multe funcții PDL care vă permit să rearanjați dimensiunile matricelor PDL. Sunt
acoperite în mare parte în PDL::Slices. Cele mai frecvente trei sunt:
xchg
mv
reordonează
Metodă: "xchg"
Metoda „xchg” „schimburi„două dimensiuni într-o piatră:
pdl> $a = secvență(6,7,8,9)
pdl> $a_xchg = $a->xchg(0,3)
pdl> p $a->info => PDL: Double D [6,7,8,9]
pdl> p $a_xchg->info => PDL: Double D [9,7,8,6]
| |
VV
(dim 0) (dim 3)
Observați că dimensiunile 0 și 3 au fost schimbate fără a afecta celelalte dimensiuni.
Observați, de asemenea, că „xchg” nu modifică $a. Variabila originală $a rămâne neatinsă.
Metodă: "mv"
Metoda "mv""mişcă„o dimensiune, într-un joc, schimbând alte dimensiuni ca
necesar.
pdl> $a = secvență(6,7,8,9) (dim 0)
pdl> $a_mv = $a->mv(0,3) |
pdl> V _____
pdl> p $a->info => PDL: Double D [6,7,8,9]
pdl> p $a_mv->info => PDL: Double D [7,8,9,6]
----- |
V
(dim 3)
Observați că atunci când dimensiunea 0 a fost mutată în poziția 3, toate celelalte dimensiuni trebuiau să fie
mutat de asemenea. Observați, de asemenea, că „mv” nu modifică $a. Variabila originală $a rămâne
neatins.
Metodă: "reordoneaza"
Metoda „reordonare” este o generalizare a metodelor „xchg” și „mv”. Aceasta "recomenzi"
dimensiunile în orice mod pe care le specificați:
pdl> $a = secvență(6,7,8,9)
pdl> $a_reorder = $a->reorder(3,0,2,1)
pdl>
pdl> p $a->info => PDL: Double D [6,7,8,9]
pdl> p $a_reorder->info => PDL: Double D [9,6,8,7]
| | | |
VV contra V
dimensiuni: 0 1 2 3
Observă ce sa întâmplat. Când am scris „reorder(3,0,2,1)” am instruit PDL să:
* Puneți dimensiunea 3 pe primul loc.
* Pune dimensiunea 0 în continuare.
* Pune dimensiunea 2 în continuare.
* Pune dimensiunea 1 în continuare.
Când utilizați metoda „reordonare”, toate dimensiunile sunt amestecate. Observați că „reordonați”
nu modifică $a. Variabila originală $a rămâne neatinsă.
Îți înțeleg: LEGARE VS MISIUNE
Legarea
În mod implicit, piddles sunt legate de împreună astfel încât modificările asupra unuia vor reveni și vor afecta
original as bine.
pdl> $a = secvență(4,5)
pdl> $a_xchg = $a->xchg(1,0)
Aici, $a_xchg is nu a distinct obiect. Este doar un mod diferit de a privi $a. Orice
modificarea în $a_xchg va apărea și în $a.
pdl> p $a
[
[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]
[12 13 14 15]
[16 17 18 19]
]
pdl> $a_xchg += 3
pdl> p $a
[
[ 3 4 5 6]
[ 7 8 9 10]
[11 12 13 14]
[15 16 17 18]
[19 20 21 22]
]
Cesiune
Uneori, conectarea nu este comportamentul dorit. Dacă vreți să faceți piddles
independent, utilizați metoda „copiere”:
pdl> $a = secvență(4,5)
pdl> $a_xchg = $a->copiere->xchg(1,0)
Acum $a și $a_xchg sunt obiecte complet separate:
pdl> p $a
[
[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]
[12 13 14 15]
[16 17 18 19]
]
pdl> $a_xchg += 3
pdl> p $a
[
[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]
[12 13 14 15]
[16 17 18 19]
]
pdl> $a_xchg
[
[ 3 7 11 15 19]
[ 4 8 12 16 20]
[ 5 9 13 17 21]
[ 6 10 14 18 22]
]
PUNÂND IT Toate colectiile ÎMPREUNĂ
Acum suntem pregătiți să rezolvăm problema care a motivat toată această discuție:
pdl> $a = secvență(11,9)
pdl> $cols = secvenţă(9)
pdl>
pdl> p $a->info => PDL: Double D [11,9]
pdl> p $cols->info => PDL: Double D [9]
Cum îi spunem lui PDL să scadă $ cols de-a lungul dimensiunii 1 în loc de dimensiunea 0? The
Cel mai simplu mod este să utilizați metoda „xchg” și să vă bazați pe legătura PDL:
pdl> p $a
[
[ 0 1 2 3 4 5 6 7 8 9 10]
[11 12 13 14 15 16 17 18 19 20 21]
[22 23 24 25 26 27 28 29 30 31 32]
[33 34 35 36 37 38 39 40 41 42 43]
[44 45 46 47 48 49 50 51 52 53 54]
[55 56 57 58 59 60 61 62 63 64 65]
[66 67 68 69 70 71 72 73 74 75 76]
[77 78 79 80 81 82 83 84 85 86 87]
[88 89 90 91 92 93 94 95 96 97 98]
]
pdl> $a->xchg(1,0) -= $cols
pdl> p $a
[
[ 0 1 2 3 4 5 6 7 8 9 10]
[10 11 12 13 14 15 16 17 18 19 20]
[20 21 22 23 24 25 26 27 28 29 30]
[30 31 32 33 34 35 36 37 38 39 40]
[40 41 42 43 44 45 46 47 48 49 50]
[50 51 52 53 54 55 56 57 58 59 60]
[60 61 62 63 64 65 66 67 68 69 70]
[70 71 72 73 74 75 76 77 78 79 80]
[80 81 82 83 84 85 86 87 88 89 90]
]
Strategia generala:
Mutați dimensiunile pe care doriți să operați la începutul dimensiunii piddle-ului dvs
listă. Apoi lăsați PDL să treacă peste dimensiunile superioare.
EXEMPLU: CONWAY'S JOC OF VIAŢĂ
Bine, destulă teorie. Să facem ceva mai interesant: Vom scrie Al lui Conway Joc
of Viaţă în PDL și vezi cât de puternic poate fi PDL!
Joc of Viaţă este o rulare de simulare pe o grilă mare bidimensională. Fiecare celulă din grilă
poate fi viu sau mort (reprezentat prin 1 sau 0). Următoarea generație de celule din
grila este calculată cu reguli simple în funcție de numărul de celule vii din ea
vecinătate imediată:
1) Dacă o celulă goală are exact trei vecini, se generează o celulă vie.
2) Dacă o celulă vie are mai puțin de doi vecini, moare din cauza supraalimentării.
3) Dacă o celulă vie are 4 sau mai mulți vecini, moare de foame.
Doar prima generație de celule este determinată de programator. După aceea,
simularea rulează complet conform acestor reguli. Pentru a calcula următoarea generație, tu
trebuie să vă uitați la fiecare celulă din câmpul 2D (care necesită două bucle), calculați numărul de
celule vii adiacente acestei celule (care necesită încă două bucle) și apoi umpleți următoarea
grila de generare.
Clasic implementarea
Iată un mod clasic de a scrie acest program în Perl. Folosim doar PDL pentru adresare
celule individuale.
#!/usr/bin/perl -w
utilizați PDL;
utilizați PDL::NiceSlice;
# Faceți o tablă pentru jocul vieții.
my $nx = 20;
my $ny = 20;
# Generația actuală.
my $a = zeroes($nx, $ny);
# Generație următoare.
my $n = zeroes($nx, $ny);
# Puneți un planor simplu.
$a(1:3,1:3) .= pdl ( [1,1,1],
[0,0,1],
[0,1,0]);
pentru ($i meu = 0; $i < 100; $i++) {
$n = zerouri($nx, $ny);
$nou_a = $a->copie;
pentru ($x = 0; $x < $nx; $x++) {
pentru ($y = 0; $y < $ny; $y++) {
# Pentru fiecare celulă, uitați-vă la vecinii din jur.
pentru ($dx = -1; $dx <= 1; $dx++) {
pentru ($dy = -1; $dy <= 1; $dy++) {
$px = $x + $dx;
$py = $y + $dy;
# Înfășurați la margini.
dacă ($px < 0) {$px = $nx-1};
dacă ($py < 0) {$py = $ny-1};
dacă ($px >= $nx) {$px = 0};
dacă ($py >= $ny) {$py = 0};
$n($x,$y) .= $n($x,$y) + $a($px,$py);
}
}
# Nu numărați celula centrală în sine.
$n($x,$y) -= $a($x,$y);
# Determinați dacă celula trăiește sau moare:
# Celula moartă trăiește dacă n = 3
# Celula vie moare dacă n nu este 2 sau 3
dacă ($a($x,$y) == 1) {
dacă ($n($x,$y) < 2) {$new_a($x,$y) .= 0};
dacă ($n($x,$y) > 3) {$new_a($x,$y) .= 0};
} Else {
dacă ($n($x,$y) == 3) {$new_a($x,$y) .= 1}
}
}
}
imprima $a;
$a = $nou_a;
}
Dacă rulați acest lucru, veți vedea un mic planor care se târăște în diagonală peste grila de zerouri.
Pe aparatul meu, imprimă câteva generații pe secundă.
Filetat PDL implementarea
Și iată versiunea threaded în PDL. Doar patru linii de cod PDL, iar una dintre acestea este
tipărirea de ultimă generație!
#!/usr/bin/perl -w
utilizați PDL;
utilizați PDL::NiceSlice;
my $a = zeroes(20,20);
# Puneți un planor simplu.
$a(1:3,1:3) .= pdl ( [1,1,1],
[0,0,1],
[0,1,0]);
$n-ul meu;
pentru ($i meu = 0; $i < 100; $i++) {
# Calculați numărul de vecini pe celulă.
$n = $a->range(ndcoords($a)-1,3,"periodic")->reorder(2,3,0,1);
$n = $n->sumover->sumover - $a;
# Calculați următoarea generație.
$a = ((($n == 2) + ($n == 3))* $a) + (($n===3) * !$a);
imprima $a;
}
Versiunea PDL cu fire este mult mai rapidă:
Clasic => 32.79 secunde.
Filet => 0.41 secunde.
Explicație
Cum funcționează versiunea threaded?
Există multe funcții PDL concepute pentru a vă ajuta să efectuați threading PDL. In acest
de exemplu, funcțiile cheie sunt:
Metodă: "gamă"
La cel mai simplu nivel, metoda „interval” este o modalitate diferită de a selecta o porțiune din a
face pipi. În loc să folosim notația „$a(2,3)”, folosim un alt piddle.
pdl> $a = secvență(6,7)
pdl> p $a
[
[ 0 1 2 3 4 5]
[ 6 7 8 9 10 11]
[12 13 14 15 16 17]
[18 19 20 21 22 23]
[24 25 26 27 28 29]
[30 31 32 33 34 35]
[36 37 38 39 40 41]
]
pdl> p $a->range( pdl [1,2] )
13
pdl> p $a(1,2)
[
[13]
]
În acest moment, metoda „interval” arată foarte asemănătoare cu o porțiune PDL obișnuită. Cu exceptia
metoda „interval” este mai generală. De exemplu, puteți selecta mai multe componente simultan:
pdl> $index = pdl [ [1,2],[2,3],[3,4],[4,5] ]
pdl> p $a->interval ($index)
[13 20 27 34]
În plus, „interval” ia un al doilea parametru care determină dimensiunea bucății la
întoarcere:
pdl> $size = 3
pdl> p $a->range( pdl([1,2]) , $size )
[
[13 14 15]
[19 20 21]
[25 26 27]
]
Putem folosi aceasta pentru a selecta una sau mai multe casete 3x3.
În cele din urmă, „interval” poate lua un al treilea parametru numit condiția „limită”. Spune PDL
ce trebuie să faceți dacă dimensiunea casetei pe care o solicitați depășește marginea piddle-ului. Nu vom merge
peste toate opțiunile. Vom spune doar că opțiunea „periodic” înseamnă că piddle
„se înfășoară”. De exemplu:
pdl> p $a
[
[ 0 1 2 3 4 5]
[ 6 7 8 9 10 11]
[12 13 14 15 16 17]
[18 19 20 21 22 23]
[24 25 26 27 28 29]
[30 31 32 33 34 35]
[36 37 38 39 40 41]
]
pdl> $size = 3
pdl> p $a->range( pdl([4,2]) , $size , "periodic" )
[
[16 17 12]
[22 23 18]
[28 29 24]
]
pdl> p $a->range( pdl([5,2]) , $size , "periodic" )
[
[17 12 13]
[23 18 19]
[29 24 25]
]
Observați modul în care cutia se înfășoară în jurul limitei piddle-ului.
Metodă: "ndcoords"
Metoda „ndcoords” este o metodă convenabilă care returnează o listă enumerată de
coordonate potrivite pentru utilizarea cu metoda „interval”.
pdl> p $piddle = secvență(3,3)
[
[0 1 2]
[3 4 5]
[6 7 8]
]
pdl> p ndcoords($piddle)
[
[
[0 0]
[1 0]
[2 0]
]
[
[0 1]
[1 1]
[2 1]
]
[
[0 2]
[1 2]
[2 2]
]
]
Acest lucru poate fi puțin greu de citit. Practic, se spune că coordonatele pentru fiecare
elementul din $piddle este dat de:
(0,0) (1,0) (2,0)
(1,0) (1,1) (2,1)
(2,0) (2,1) (2,2)
Combinând "gamă" și "ndcoords"
Ceea ce contează cu adevărat este că „ndcoords” este conceput să funcționeze împreună cu „range”, cu nr
Parametrul $size, veți primi același lucru înapoi.
pdl> p $piddle
[
[0 1 2]
[3 4 5]
[6 7 8]
]
pdl> p $piddle->range( ndcoords($piddle) )
[
[0 1 2]
[3 4 5]
[6 7 8]
]
De ce ar fi util acest lucru? Pentru că acum putem cere o serie de „cutii” pentru întreg
face pipi. De exemplu, cutii 2x2:
pdl> p $piddle->range( ndcoords($piddle) , 2 , "periodic" )
Ieșirea acestei funcții este greu de citit, deoarece „cutiile” de-a lungul ultimelor două
dimensiune. Putem face rezultatul mai lizibil prin rearanjarea dimensiunilor:
pdl> p $piddle->range( ndcoords($piddle) , 2 , "periodic" )->reorder(2,3,0,1)
[
[
[
[0 1]
[3 4]
]
[
[1 2]
[4 5]
]
...
]
Aici puteți vedea mai clar că
[0 1]
[3 4]
Este caseta 2x2 care începe cu elementul (0,0) al lui $piddle.
Încă nu am terminat. Pentru jocul vieții, vrem cutii 3x3 de la $a:
pdl> p $a
[
[ 0 1 2 3 4 5]
[ 6 7 8 9 10 11]
[12 13 14 15 16 17]
[18 19 20 21 22 23]
[24 25 26 27 28 29]
[30 31 32 33 34 35]
[36 37 38 39 40 41]
]
pdl> p $a->range( ndcoords($a) , 3 , "periodic" )->reorder(2,3,0,1)
[
[
[
[0 1 2]
[6 7 8]
[12 13 14]
]
...
]
Putem confirma că aceasta este caseta 3x3 care începe cu elementul (0,0) al lui $a. Dar acolo
este o problemă. De fapt vrem ca cutia 3x3 să fie centrat pe (0,0). Asta nu este o
problemă. Doar scădeți 1 din toate coordonatele din „ndcoords($a)”. Amintiți-vă că
Opțiunea „periodică” are grijă să facă totul să se înfășoare.
pdl> p $a->range( ndcoords($a) - 1 , 3 , "periodic" )->reorder(2,3,0,1)
[
[
[
[41 36 37]
[5 0 1]
[11 6 7]
]
[
[36 37 38]
[0 1 2]
[6 7 8]
]
...
Acum vedem o casetă 3x3 cu elementul (0,0) în centrul casetei.
Metodă: "sumover"
Metoda „sumover” adaugă doar prima dimensiune. Dacă îl aplicăm de două ori, vom fi
adăugând toate elementele fiecărei cutii de 3x3.
pdl> $n = $a->range(ndcoords($a)-1,3,"periodic")->reorder(2,3,0,1)
pdl> p $n
[
[
[
[41 36 37]
[5 0 1]
[11 6 7]
]
[
[36 37 38]
[0 1 2]
[6 7 8]
]
...
pdl> p $n->sumover->sumover
[
[144 135 144 153 162 153]
[ 72 63 72 81 90 81]
[126 117 126 135 144 135]
[180 171 180 189 198 189]
[234 225 234 243 252 243]
[288 279 288 297 306 297]
[216 207 216 225 234 225]
]
Utilizați un calculator pentru a confirma că 144 este suma tuturor elementelor din prima casetă 3x3
iar 135 este suma tuturor elementelor din a doua casetă 3x3.
Socoteală vecini
Aproape am ajuns!
Adunarea tuturor elementelor într-o cutie 3x3 nu este cu totul ceea ce vrem. Nu vrem să numărăm
caseta centrală. Din fericire, aceasta este o soluție ușoară:
pdl> p $n->sumover->sumover - $a
[
[144 134 142 150 158 148]
[ 66 56 64 72 80 70]
[114 104 112 120 128 118]
[162 152 160 168 176 166]
[210 200 208 216 224 214]
[258 248 256 264 272 262]
[180 170 178 186 194 184]
]
Când se aplică la Conway’s Game of Life, acest lucru ne va spune câți vecini vii fiecare
celula are:
pdl> $a = zerouri(10,10)
pdl> $a(1:3,1:3) .= pdl ( [1,1,1],
..( > [0,0,1],
..( > [0,1,0] )
pdl> p $a
[
[0 0 0 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 1 1 1 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 0 1 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 1 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 0 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 0 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 0 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 0 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 0 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 0 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
]
pdl> $n = $a->range(ndcoords($a)-1,3,"periodic")->reorder(2,3,0,1)
pdl> $n = $n->sumover->sumover - $a
pdl> p $n
[
[1 2 3 2 1 0 0 0 0 0 XNUMX XNUMX XNUMX
[1 1 3 2 2 0 0 0 0 0 XNUMX XNUMX XNUMX
[1 3 5 3 2 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 1 1 2 1 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 1 1 1 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 0 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 0 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 0 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 0 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 0 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
]
De exemplu, aceasta ne spune că celula (0,0) are 1 vecin viu, în timp ce celula (2,2) are 5
vecini vii.
De calculat il următor generaţie
În acest moment, variabila $n are numărul de vecini vii pentru fiecare celulă. Acum noi
aplică regulile jocului vieții pentru a calcula următoarea generație.
Dacă o celulă goală are exact trei vecini, se generează o celulă vie.
Obțineți o listă de celule care au exact trei vecini:
pdl> p ($n == 3)
[
[0 0 1 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 1 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 1 0 1 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 0 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 0 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 0 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 0 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 0 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 0 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 0 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
]
Obțineți o listă cu gol celule care au exact trei vecini:
pdl> p ($n == 3) * !$a
Dacă o celulă vie are mai puțin de 2 sau mai mult de 3 vecini, ea moare.
Obțineți o listă de celule care au exact 2 sau 3 vecini:
pdl> p (($n == 2) + ($n == 3))
[
[0 1 1 1 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 1 1 1 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 1 0 1 1 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 0 1 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 0 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 0 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 0 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 0 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 0 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 0 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
]
Obțineți o listă cu viaţă celule care au exact 2 sau 3 vecini:
pdl> p (($n == 2) + ($n == 3)) * $a
Punând totul cap la cap, următoarea generație este:
pdl> $a = ((($n == 2) + ($n == 3)) * $a) + (($n == 3) * !$a)
pdl> p $a
[
[0 0 1 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 1 1 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 1 0 1 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 0 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 0 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 0 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 0 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 0 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 0 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
[0 0 0 0 0 0 0 0 0 0 XNUMX XNUMX XNUMX
]
Primă Caracteristica: Grafică!
Dacă aveți instalat PDL::Graphics::TriD, puteți crea o versiune grafică a programului
doar schimbând trei rânduri:
#!/usr/bin/perl
utilizați PDL;
utilizați PDL::NiceSlice;
utilizați PDL::Graphics::TriD;
my $a = zeroes(20,20);
# Puneți un planor simplu.
$a(1:3,1:3) .= pdl ( [1,1,1],
[0,0,1],
[0,1,0]);
$n-ul meu;
pentru ($i meu = 0; $i < 100; $i++) {
# Calculați numărul de vecini pe celulă.
$n = $a->range(ndcoords($a)-1,3,"periodic")->reorder(2,3,0,1);
$n = $n->sumover->sumover - $a;
# Calculați următoarea generație.
$a = ((($n == 2) + ($n == 3))* $a) + (($n===3) * !$a);
# Afișare.
nokeeptwiddling3d();
imagrgb [$a];
}
Dar dacă vrem cu adevărat să vedem ceva interesant, ar trebui să mai facem câteva modificări:
1) Începeți cu o colecție aleatorie de 1 și 0.
2) Faceți grila mai mare.
3) Adăugați un mic timeout, astfel încât să vedem că jocul evoluează mai bine.
4) Utilizați o buclă while, astfel încât programul să poată rula atâta timp cât este necesar.
#!/usr/bin/perl
utilizați PDL;
utilizați PDL::NiceSlice;
utilizați PDL::Graphics::TriD;
utilizați Time::HiRes qw(usleep);
my $a = random(100,100);
$a = ($a < 0.5);
$n-ul meu;
în timp ce (1) {
# Calculați numărul de vecini pe celulă.
$n = $a->range(ndcoords($a)-1,3,"periodic")->reorder(2,3,0,1);
$n = $n->sumover->sumover - $a;
# Calculați următoarea generație.
$a = ((($n == 2) + ($n == 3))* $a) + (($n===3) * !$a);
# Afișare.
nokeeptwiddling3d();
imagrgb [$a];
# Dormiți 0.1 secunde.
tu dormi(100000);
}
CONCLUZIE: GENERAL STRATEGIA
Strategia generală este: Muta il Dimensiuni tu vrea la funcionar on la il Începe of ta
de piddle dimensiune listă. "Atunci lăsa PDL fir peste il superior dimensiuni.
Threadingul este un instrument puternic care ajută la eliminarea buclelor for și poate face codul mai mult
concis. Sperăm că acest tutorial a arătat de ce merită să vă familiarizați cu filetarea
în PDL.
DREPTURI DE AUTOR
Copyright 2010 Matthew Kenworthy ([e-mail protejat]) și Daniel Carrera
([e-mail protejat]). Puteți distribui și/sau modifica acest document în aceleași condiții
ca licență Perl actuală.
A se vedea: http://dev.perl.org/licenses/
Utilizați PDL::Threadingp online folosind serviciile onworks.net