Aceasta este comanda PDL::PPp 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::PP - Generați rutine PDL din descrieri concise
REZUMAT
de exemplu
pp_def(
„sumover”,
Pars => 'a(n); [o]b();',
Cod => q{
dublu tmp=0;
buclă(n) %{
tmp += $a();
%}
$b() = tmp;
},
);
pp_terminat();
FUNCȚII
Iată o listă de referință rapidă a funcțiilor oferite de PDL::PP.
pp_add_boot
Adăugați cod în secțiunea BOOT a fișierului XS generat
pp_add_exported
Adăugați funcții la lista de funcții exportate
pp_add_isa
Adăugați intrări la lista @ISA
pp_addbegin
Setează codul care urmează să fie adăugat în partea de sus a fișierului generat .pm
pp_addhdr
Adăugați cod și includeți în secțiunea C a fișierului XS generat
pp_addpm
Adăugați cod în fișierul .pm generat
pp_addxs
Adăugați cod XS suplimentar la fișierul XS generat
pp_beginwrap
Adăugați împachetarea blocului BEGIN la codul pentru fișierul .pm generat
pp_binecuvântează
Setează pachetul la care se adaugă codul XS (implicit este PDL)
pp_boundscheck
Starea de control a activității de verificare a limitelor PDL
pp_core_importList
Specificați ce este importat din PDL::Core
pp_def
Definiți o nouă funcție PDL
pp_deprecate_module
Adăugați avertismente de timp de execuție și POD despre un modul care este depreciat
pp_terminat
Marcați sfârșitul definițiilor PDL::PP în fișier
pp_export_nimic
Ștergeți lista de export pentru modulul dvs. generat
pp_line_numbers
Adăugați informații despre numărul de linie pentru a simplifica depanarea codului PDL::PP
pp_setversion
Setați versiunea pentru fișierele .pm și .xs
ÎNSCRIERI
De ce avem nevoie de PP? Mai multe motive: în primul rând, dorim să putem genera subrutine
cod pentru fiecare dintre tipurile de date PDL (PDL_Byte, PDL_Short etc.). AUTOMAT. În al doilea rând,
când se referă la felii de matrice PDL în Perl (de exemplu, „$a->slice('0:10:2,:')” sau alte
lucruri precum transpuneri) este frumos să poți face asta în mod transparent și să poți
pentru a face acest lucru „la loc” - adică, pentru a nu fi necesar să faceți o copie de memorie a secțiunii. Mânere din PP
toate elementele necesare și aritmetica offset pentru tine. Există și noțiunile de
threading (apelare repetată a aceleiași rutine pentru mai multe felii, vezi PDL::Indexare)
și fluxul de date (vezi PDL::Fluxul de date) care permite utilizarea PP.
În multe din cele ce urmează vom presupune familiaritatea cititorului cu conceptele de
threading implicit și explicit și manipulări de index în cadrul PDL. Dacă nu ai încă
auzit de aceste concepte sau nu sunt foarte confortabil cu ele este timpul să verificați
PDL::Indexare.
După cum puteți aprecia din numele său, PDL::PP este un pre-procesor, adică extinde codul prin
substituții pentru a face codul C real. Din punct de vedere tehnic, rezultatul este codul XS (vezi perlxs) dar
este foarte aproape de C.
Deci, cum folosești PP? Ei bine, în cea mai mare parte, doar scrieți cod C obișnuit, cu excepția
constructe speciale PP care iau forma:
$ceva (altceva)
sau:
PPfuncție %{
%}
Cel mai important construct PP este forma „$array()”. Luați în considerare PP foarte simplu
funcție pentru a însuma elementele unui vector 1D (de fapt, acesta este foarte similar cu cel real
cod folosit de „sumover”):
pp_def('sumit',
Pars => 'a(n); [o]b();',
Cod => q{
tmp dublu;
tmp = 0;
buclă(n) %{
tmp += $a();
%}
$b() = tmp;
}
);
Ce se întâmplă? Linia „Pars =>” este foarte importantă pentru PP - specifică toate
argumente și dimensionalitatea lor. Noi numim asta semnătură a funcției PP (comparați
de asemenea explicatiile din PDL::Indexing). În acest caz, rutina ia o funcție 1-D ca
intrare și returnează un scalar 0-D ca ieșire. Construcția PP „$a()” este folosită pentru a accesa
elemente ale matricei a(n) pentru tine - PP completează tot codul C necesar.
Veți observa că folosim operatorul „q{}” cu ghilimele simple. Acesta nu este un
accident. În general, doriți să utilizați ghilimele simple pentru a indica secțiunile dvs. de cod PP. PDL::PP
folosește „$var()” pentru analizarea sa și dacă nu folosiți ghilimele simple, Perl va încerca să
interpolați „$var()”. De asemenea, utilizarea operatorului „q” cu ghilimele simple cu acolade face acest lucru
arătați de parcă creați un bloc de cod, ceea ce înseamnă. (Perl este suficient de inteligent pentru a
căutați acolade imbricate și nu închideți ghilimele până când nu găsește cretul potrivit
acolade, deci este sigur să aveți blocuri imbricate.) În alte circumstanțe, cum ar fi atunci când sunteți
împletind un bloc de cod folosind concatenări de șiruri, este adesea cel mai ușor de utilizat
ghilimele simple reale ca
Cod => 'ceva'.$interpolabil.'ceva altceva;'
În cazul simplu de aici în care toate elementele sunt accesate, constructul PP „buclă(n) %{...
%}" este folosit pentru a bucla peste toate elementele din dimensiunea "n". Rețineți această caracteristică a PP: ALL
DIMENSIUNILE SUNT SPECIFICATE PRIN NUME.
Acest lucru este clar dacă evităm PP buclă() construiți și scrieți bucla în mod explicit
folosind C convențional:
pp_def('sumit',
Pars => 'a(n); [o]b();',
Cod => q{
int i,n_size;
tmp dublu;
n_size = $SIZE(n);
tmp = 0;
pentru(i=0; i
tmp += $a(n=>i);
}
$b() = tmp;
},
);
care face la fel ca înainte, dar este mai lung. Puteți vedea pentru a obține elementul „i” al
a() spunem „$a(n=>i)” - specificăm dimensiunea cu numele „n”. În 2D am putea spune:
Pars => 'a(m,n);',
...
tmp += $a(m=>i,n=>j);
...
Sintaxa „m => i” împrumută din hashuri Perl, care sunt de fapt folosite în implementare
de PP. S-ar putea spune și „$a(n=>j,m=>i)” deoarece ordinea nu este importantă.
De asemenea, puteți vedea în exemplul de mai sus utilizarea unui alt construct PP - $SIZE(n) pentru a obține
lungimea dimensiunii „n”.
Cu toate acestea, trebuie remarcat faptul că nu ar trebui să scrieți o buclă C explicită atunci când ați putea
au folosit construcția „buclă” PP, deoarece PDL::PP verifică automat limitele buclei pentru
tu, utilizarea lui „buclă” face codul mai concis, etc. Dar cu siguranță există situații
unde ai nevoie de control explicit al buclei și acum știi cum să o faci ;).
Pentru a revizui „De ce PP?” - codul de mai sus pt sumit() va fi generat pentru fiecare tip de date. Aceasta
va opera pe secțiuni de matrice „in loc”. Se va file automat - de exemplu, dacă este 2D
matrice este dat, acesta va fi apelat în mod repetat pentru fiecare rând 1D (bifați din nou PDL::Indexing pentru
detaliile filetării). Și apoi b() va fi o matrice 1D de sume ale fiecărui rând. Am putea
apelați-l cu $a->xchg(0,1) pentru a însuma coloanele. Și urmărirea Dataflow etc. va fi
disponibile.
Puteți vedea că PP îl salvează pe programator de la scrierea multor coduri C repetitive inutil --
în opinia noastră, aceasta este una dintre cele mai bune caracteristici ale PDL-ului care face scrierea de noi subrutine C
pentru PDL un exercițiu uimitor de concis. Un al doilea motiv este capacitatea de a face PP să se extindă
definițiile dvs. concise ale codului în cod C diferit, în funcție de nevoile computerului
arhitectura in cauza. Imaginează-ți, de exemplu, că ești norocos să ai un supercomputer
mainile tale; în acest caz, doriți ca PDL::PP să genereze cod care profită
a caracteristicilor de vectorizare/calculare paralelă ale mașinii dvs. (acesta este un proiect pentru
viitor). În orice caz, concluzia este că codul tău neschimbat ar trebui să se extindă în continuare la
cod XS funcțional, chiar dacă elementele interne ale PDL s-au schimbat.
De asemenea, pentru că generați codul într-un script Perl real, există multe distracție
lucruri pe care le poți face. Să presupunem că trebuie să scrieți atât sumit (ca mai sus) cât și multit.
Cu puțină creativitate, putem face
for({Nume => 'sumit', Init => '0', Op => '+='},
{Name => 'multit', Init => '1', Op => '*='}) {
pp_def($_->{Nume},
Pars => 'a(n); [o]b();',
Cod => '
tmp dublu;
tmp = '.$_->{Init}.';
buclă(n) %{
tmp '.$_->{Op}.' $a();
%}
$b() = tmp;
„);
}
care definește cu ușurință ambele funcții. Acum, dacă mai târziu trebuie să schimbați semnătura sau
dimensionalitate sau orice altceva, trebuie doar să schimbați un loc în cod. Da, sigur,
editorul dvs. are „cut and paste” și „căutare și înlocuire”, dar este încă mai puțin
deranjant și cu siguranță mai dificil să uiți un singur loc și să ai bug-uri ciudate
De asemenea, adăugarea „orit” (pe biți sau) mai târziu este o singură linie.
Și amintiți-vă, aveți cu adevărat toate abilitățile lui Perl cu dvs. - puteți citi foarte ușor
orice fișier de intrare și faceți rutine din informațiile din acel fișier. Pentru cazuri simple precum
cele de mai sus, autorul (Tjl) favorizează în prezent sintaxa hash ca cea de mai sus - nu este prea
mult mai multe caractere decât sintaxa matricei corespunzătoare dar mult mai ușor de înțeles și
schimba.
Ar trebui să menționăm aici și capacitatea de a obține indicatorul la începutul datelor în
memorie - o condiție prealabilă pentru interfațarea PDL cu unele biblioteci. Acest lucru este tratat cu
Directiva „$P(var)”, vezi mai jos.
Când începeți să lucrați la o nouă funcție pp_def'ined, dacă faceți o greșeală, de obicei o veți face
găsiți o grămadă de erori ale compilatorului care indică numerele de rând în fișierul XS generat. daca tu
știți să citiți fișierele XS (sau dacă doriți să învățați pe calea grea), puteți deschide fișierul
fișier XS generat și căutați numărul liniei cu eroarea. Cu toate acestea, un recent
adăugarea la PDL::PP vă ajută să raportați numărul corect de linie al erorilor dvs.:
„pp_line_numbers”. Lucrul cu exemplul original al summit-ului, dacă ați greșit ortografierea
tmp în codul dvs., puteți schimba codul (erroneos) la ceva de genul acesta și
compilatorul vă va oferi mult mai multe informații utile:
pp_def('sumit',
Pars => 'a(n); [o]b();',
Cod => pp_line_numbers(__LINE__, q{
tmp dublu;
tmp = 0;
buclă(n) %{
tmp += $a();
%}
$b() = rmp;
})
);
Pentru situația de mai sus, compilatorul meu îmi spune:
...
test.pd:15: eroare: „rmp” nedeclarat (prima utilizare în această funcție)
...
În exemplul meu de script (numit test.pd), linia 15 este exact linia la care am făcut-o
greșeală: „rmp” în loc de „tmp”.
Deci, după această privire de ansamblu rapidă a aromei generale a programării rutinelor PDL folosind
PDL::PP haideți să rezumam în ce circumstanțe ar trebui să utilizați de fapt acest lucru
preprocesor/precompilator. Ar trebui să utilizați PDL::PP dacă doriți
· interfață PDL cu o bibliotecă externă
· scrie un algoritm care ar fi lent dacă ar fi codat în Perl (acesta nu este la fel de des ca tine
gândi; aruncați o privire mai întâi la threading și dataflow).
· fii dezvoltator PDL (și chiar și atunci nu este obligatoriu)
AVERTISMENT
Datorită arhitecturii sale, PDL::PP poate fi atât flexibil, cât și ușor de utilizat, pe de o parte,
totuși exuberant de complicat în același timp. În prezent, o parte a problemei este acea eroare
mesajele nu sunt foarte informative și dacă ceva nu merge bine, ar fi bine să știi ce ești
fac și puteți să vă spargeți drumul prin elementele interne (sau să vă puteți da seama prin
încercare și eroare ce este în neregulă cu argumentele dvs. la "pp_def"). Deși se lucrează la
produceți avertismente mai bune, nu vă fie teamă să trimiteți întrebările dvs. pe lista de corespondență dacă
ai probleme.
DESCRIERE
Acum că aveți o idee despre cum să utilizați „pp_def” pentru a defini noi funcții PDL, este timpul să faceți acest lucru
explicați sintaxa generală a lui „pp_def”. „pp_def” ia ca argumente mai întâi numele
funcția pe care o definiți și apoi o listă hash care poate conține diverse chei.
Pe baza acestor chei, PP generează cod XS și un fișier .pm. Funcția „pp_done” (vezi
exemplu în SINOPSIS) este folosit pentru a spune PDL::PP că nu mai există definiții în
acest fișier și este timpul să generați fișierul .xs și
fișier .pm.
În consecință, pot fi mai multe pp_def() apeluri în interiorul unui fișier (prin convenție fișiere
cu cod PP au extensia .pd sau .pp) dar in general doar una pp_terminat().
Există două tipuri principale diferite de utilizare a pp_def(), „operația de date” și „slice
prototipuri de operare.
„Operațiunea de date” este folosită pentru a prelua unele date, a le altera și a scoate alte date; acest
include de exemplu operația „+”, inversul matricei, suma etc. și toate exemplele
despre care am vorbit până acum în acest document. Threading implicit și explicit și
crearea rezultatului sunt îngrijite automat în acele operațiuni. Poți chiar
face fluxul de date cu „sumit”, „sumover”, etc (nu fi consternat dacă nu înțelegi
conceptul de flux de date în PDL încă foarte bine; este încă foarte mult experimental).
„Operația slice” este un alt tip de operație: într-o operațiune slice, nu ești
schimbând orice date, definiți corespondențe între diferite elemente ale două
piddles (exemplele includ definițiile funcției de manipulare/slicing ale indexului din fișier
felii.pd care face parte din distribuția PDL; dar atenție, acesta nu este un nivel introductiv
chestie).
Dacă PDL a fost compilat cu suport pentru valori greșite (adică „WITH_BADVAL => 1”), atunci
cheile sunt necesare pentru „pp_def”, după cum se explică mai jos.
Dacă sunteți doar interesat să comunicați cu o bibliotecă externă (de exemplu, unele
algebră liniară/biblioteca de matrice), de obicei veți dori „operația de date”, așa că mergem
pentru a discuta asta mai întâi.
Date operaţie
A simplu exemplu
În operațiunea de date, trebuie să știți de ce dimensiuni de date aveți nevoie. În primul rând, un exemplu
cu scalari:
pp_def('adăugați',
Pars => 'a(); b(); [o]c();',
Cod => '$c() = $a() + $b();'
);
Pare puțin ciudat, dar hai să-l disecăm. Prima linie este simplă: definim a
rutină cu numele „adăugați”. A doua linie declară pur și simplu parametrii noștri și
parantezele înseamnă că sunt scalari. Numim șirul care ne definește parametrii și
dimensionalitatea lor semnătură a acelei funcţii. Pentru relevanța sa în ceea ce privește
manipulările de threading și index verificați pagina de manual PDL::Indexing.
A treia linie este operațiunea reală. Trebuie să utilizați semnele dolarului și parantezele
pentru a vă referi la parametrii dvs. (acest lucru se va schimba probabil la un moment dat în viitor, odată a
se găsește o sintaxă bună).
Aceste linii sunt tot ceea ce este necesar pentru a defini efectiv funcția pentru PDL (bine,
de fapt nu este; în plus, trebuie să scrieți un Makefile.PL (vezi mai jos) și să construiți
modul (ceva de genul „perl Makefile.PL; make”); dar să ignorăm asta pentru moment).
Deci acum poți face
utilizați MyModule;
$a = pdl 2,3,4;
$b = pdl 5;
$c = adauga($a,$b);
# sau
adauga($a,$b,($c=null)); # Formă alternativă, utilă dacă $c a fost
# presetat la ceva mare, nu este util aici.
și au firul să funcționeze corect (rezultatul este $c == [7 8 9]).
Pars secțiune: il semnătură of a PP funcţie
Văzând exemplul de cod de mai sus, cel mai probabil vă veți întreba: ce este acest ciudat „$c=null”
sintaxă în al doilea apel la noua noastră funcție „adăugați”? Dacă aruncați o altă privire la
Definiția lui „adăugați” veți observa că al treilea argument „c” este marcat cu
calificativ „[o]” care spune PDL::PP că acesta este un argument de ieșire. Deci apelul de mai sus la
add înseamnă „creați un nou $c de la zero cu dimensiunile corecte” - „null” este o specialitate
indicativ pentru „empty piddle” (s-ar putea să întrebați de ce nu am folosit valoarea „undef” pentru a semnala acest lucru
în locul specificului PDL „null”; momentan ne gandim la asta ;).
[Acest lucru ar trebui explicat și în altă secțiune a manualului!!] Motivul pentru
având această sintaxă ca alternativă este că, dacă aveți probleme foarte mari, puteți face
$c = PDL->null;
for(neva buclă lungă) {
# munge a,b
adauga($a,$b,$c);
# munge c, pune ceva înapoi la a,b
}
și evitați alocarea și dealocarea $c de fiecare dată. Se alocă o dată la prima
adăuga() iar apoi memoria rămâne până când $c este distrus.
Dacă spui doar
$c = adauga($a,$b);
codul generat de PP va completa automat „$c=null” și va returna rezultatul. Dacă
doriți să aflați mai multe despre motivele pentru care PDL::PP acceptă acest stil în care iese
argumentele sunt date ca ultimele argumente verificați pagina de manual PDL::Indexing.
„[o]” nu este singurul calificativ pe care un argument pdl îl poate avea în semnătură. Un alt
calificativul important este opțiunea „[t]” care semnalează un pdl ca temporar. Ce înseamnă asta
Rău? Spuneți PDL::PP că acest pdl este folosit doar pentru rezultate temporare în cursul
calculul și nu sunteți interesat de valoarea lui după ce a fost calculat
efectuat. Dar de ce ar vrea PDL::PP să știe despre asta în primul rând? Motivul
este strâns legat de conceptele de creare automată pdl (ați auzit despre asta mai sus) și
threading implicit. Dacă utilizați threading implicit, dimensionalitatea automat
create pdls este de fapt mai mare decât cea specificată în semnătură. Cu „[o]” marcat
pdls vor fi create astfel încât să aibă dimensiunile suplimentare cerute de număr
de dimensiuni implicite ale firului. Cu toate acestea, atunci când creați un pdl temporar, acesta va fi întotdeauna numai
să fie suficient de mare încât să poată păstra rezultatul pentru o iterație într-o buclă de fir, de exemplu
cât de mare este cerut de semnătură. Deci, se irosește mai puțină memorie atunci când semnalați un pdl ca
temporar. În al doilea rând, puteți utiliza crearea automată a ieșirii cu pdls temporare chiar și atunci când dvs
folosesc threading explicit, care este interzis pentru pdl-uri de ieșire normale marcate cu „[o]”
(vezi PDL::Indexare).
Iată un exemplu în care folosim calificativul [t]. Definim functia "callf" care
apelează o rutină C „f” care are nevoie de o matrice temporară de aceeași dimensiune și tip ca și matricea
„a” (îmi pare rău pentru referința înainte pentru $P; este un acces pointer, vezi mai jos):
pp_def('callf',
Pars => 'a(n); [t] tmp(n); [o] b()',
Cod => 'int ns = $SIZE(n);
f($P(a),$P(b),$P(tmp),ns);
'
);
Argument Dimensiuni si il semnătură
Acum tocmai am vorbit despre dimensiunile pdls și despre semnătură. Cum sunt ele legate?
Să presupunem că vrem să adăugăm un scalar + numărul index la un vector:
pp_def('add2',
Pars => 'a(n); b(); [o]c(n);',
Cod => „buclă(n) %{
$c() = $a() + $b() + n;
%}'
);
Există câteva puncte de observat aici: mai întâi, argumentul „Pars” conține acum n
argumente pentru a arăta că avem o singură dimensiune în a si c. Este important de remarcat
acele dimensiuni sunt entități reale care sunt accesate după nume, așa că aceasta declară a si c la
au acelaşi primele dimensiuni. În majoritatea definițiilor PP, dimensiunea dimensiunilor numite va fi
fie setate din dimensiunile respective ale pdl-urilor non-output (cele fără steag „[o]”) dar
uneori este posibil să doriți să setați dimensiunea unei dimensiuni denumite în mod explicit printr-un
parametru întreg. Vedeți mai jos în descrierea secțiunii „OtherPars” cum funcționează.
Constant argument Dimensiuni in il semnătură
Să presupunem că doriți ca un piddle de ieșire să fie creat automat și știți asta la fiecare
numiți dimensiunea sa va avea aceeași dimensiune (să zicem 9), indiferent de dimensiunile
piddles de intrare. În acest caz, utilizați următoarea sintaxă în secțiunea Pars pentru a specifica
dimensiunea dimensiunii:
' [o] y(n=9); '
După cum era de așteptat, dimensiunile suplimentare necesare prin filetare vor fi create dacă este necesar. daca tu
trebuie să atribuiți o dimensiune numită conform unei formule mai complicate (decât o constantă)
trebuie să utilizați tasta „RedoDimsCode” descrisă mai jos.
Tip conversii si il semnătură
Semnătura determină și conversiile de tip care vor fi efectuate atunci când un PP
funcția este invocată. Deci, ce se întâmplă când invocăm una dintre cele definite anterior
funcții cu pdls de tip diferit, de ex
add2($a,$b,($ret=null));
unde $a este de tipul „PDL_Float” și $b de tipul „PDL_Short”? Cu semnătura așa cum se arată în
definiția „add2” deasupra tipului de date al operațiunii (așa cum este determinată în timpul execuției) este
cea a pdl-ului cu tipul „cel mai mare” (secvența este byte < short < ushort < long < float
< dublu). În exemplul add2, tipul de date al operației este float ($a are asta
tipul de date). Toate argumentele pdl sunt apoi convertite la acel tip de date (nu sunt
convertit în loc, dar o copie cu tipul potrivit este creată dacă nu are un argument pdl
tipul operației). Pdls nul nu contribuie cu un tip la determinarea
tipul operației. Cu toate acestea, acestea vor fi create cu tipul de date al operației;
aici, de exemplu, $ret va fi de tip float. Ar trebui să fii conștient de aceste reguli când
apelarea funcțiilor PP cu pdls de diferite tipuri pentru a lua stocarea suplimentară și
țin cont de cerințele de rulare.
Aceste conversii de tip sunt corecte pentru majoritatea funcțiilor pe care le definiți în mod normal cu „pp_def”.
Cu toate acestea, există anumite cazuri în care comportamentul de conversie a tipului este ușor modificat
dorit. În aceste cazuri, pot fi utilizați calificativi suplimentari din semnătură pentru a specifica
proprietățile dorite în ceea ce privește conversia tipului. Aceste calificative pot fi combinate cu
cei pe care i-am întâlnit deja ( creaţie Calificări „[o]” și „[t]”). Să mergem
prin lista de calificative care modifică comportamentul de conversie de tip.
Cel mai important este calificativul „int”, care este util atunci când un argument pdl
reprezintă indici într-un alt pdl. Să aruncăm o privire la un exemplu din „PDL::Ufunc”:
pp_def('maximum_ind',
Pars => 'a(n); int [o] b()',
Cod => '$GENERIC() cur;
int curind;
buclă(n) %{
dacă (!n || $a() > cur) {cur = $a(); curind = n;}
%}
$b() = curind;',
);
Funcția „maximum_ind” găsește indicele celui mai mare element al unui vector. Daca te uiti
la semnătură observați că argumentul de ieșire „b” a fost declarat cu
calificativ suplimentar „int”. Acest lucru are următoarele consecințe pentru conversiile de tip:
indiferent de tipul de intrare pdl „a”, ieșirea pdl „b” va fi de tip „PDL_Long”
ceea ce are sens deoarece „b” va reprezenta un index în „a”. În plus, dacă suni la
funcția cu o ieșire existentă pdl „b” tipul acesteia nu va influența tipul de date al
operare (vezi mai sus). Prin urmare, chiar dacă „a” este de tip mai mic decât „b”, nu va fi
convertit pentru a se potrivi cu tipul „b”, dar rămâne neatins, ceea ce economisește memoria și ciclurile CPU
și este lucrul corect de făcut atunci când „b” reprezintă indici. De asemenea, rețineți că puteți utiliza
calificativul „int” împreună cu alți calificativi (calificatorii „[o]” și „[t]”). Ordinea este
semnificativ -- calificatorii de tip preced calificatorii de creare ("[o]" și "[t]").
Exemplul de mai sus demonstrează, de asemenea, utilizarea tipică a macrocomenzii „$GENERIC()”. Se extinde
la tipul curent într-o așa-numită buclă generică. Ce este o buclă generică? Așa cum tu deja
am auzit că o funcție PP are un tip de date de rulare determinat de tipul argumentelor pdl
a fost invocat cu. Codul XS generat de PP pentru această funcție conține, prin urmare, a
comutator de genul „switch (type) {case PDL_Byte: ... case PDL_Double: ...}” care selectează un caz
bazat pe tipul de date din timpul de execuție al funcției (se numește tip „buclă” pentru că acolo
este o buclă în codul PP care generează cazurile). În orice caz, codul dvs. este introdus o dată
pentru fiecare tip PDL în această instrucțiune switch. Macrocomanda „$GENERIC()” se extinde doar la
tastați respectivul în fiecare copie a codului dvs. analizat în această declarație „comutator”, de exemplu, în
„case PDL_Byte” secțiunea „cur” se va extinde la „PDL_Byte” și așa mai departe pentru celălalt caz
declarații. Cred că vă dați seama că aceasta este o macrocomandă utilă pentru a păstra valorile pdls în unele
cod.
Există câțiva alți calificativi cu efecte similare cu „int”. Pentru tine
conveniență există calificativele „float” și „dublu” cu consecințe analoge asupra
tastați conversii ca „int”. Să presupunem că aveți un foarte matrice mare pentru care doriți
calculați sumele de rânduri și coloane cu un echivalent al funcției „sumover”. Cu toate acestea, cu
definiția normală a „sumover” s-ar putea să întâmpinați probleme atunci când datele dvs. sunt, de exemplu, de
tip scurt. Un apel ca
sumover($large_pdl,($sums = null));
va avea ca rezultat $sums să fie de tip scurt și, prin urmare, este predispus la erori de depășire dacă
$large_pdl este o matrice foarte mare. Pe de altă parte, sunând
@dims = $large_pdl->dims; shift @dims;
sumover($large_pdl,($sums = zeroes(double,@dims)));
nici nu este o alternativă bună. Acum nu avem probleme de overflow cu $sums, ci la
cheltuiala unei conversii de tip de $large_pdl la dublu, ceva rău dacă acest lucru este într-adevăr
un pdl mare. Iată unde „dublu” este util:
pp_def('sumoverd',
Pars => 'a(n); dublu [o] b()',
Cod => 'tmp dublu=0;
bucla(n) %{ tmp += a(); %}
$b() = tmp;',
);
Acest lucru ne ajută să rezolvăm problemele de conversie de tip și de depășire. Din nou, analog cu
„int” calificativul „double” are ca rezultat „b” fiind întotdeauna de tip double, indiferent de tip
a „a” fără a duce la o conversie de tip a „a” ca efect secundar.
În cele din urmă, există calificatorii „type+” în care tipul este unul dintre „int” sau „float”. Ce
asta înseamnă. Să ilustrăm calificativul „int+” cu definiția reală a
sumover:
pp_def('sumover',
Pars => 'a(n); int+ [o] b()',
Cod => '$GENERIC(b) tmp=0;
bucla(n) %{ tmp += a(); %}
$b() = tmp;',
);
După cum am văzut deja pentru calificativele „int”, „float” și „double”, un pdl marcat cu un
calificativul „type+” nu influențează tipul de date al operației pdl. Sensul lui este
„faceți acest pdl cel puțin de tip „tip” sau mai mare, după cum cere tipul de
Operațiunea". În exemplul sumover, aceasta înseamnă că atunci când apelați funcția cu un "a"
de tip PDL_Short ieșirea pdl va fi de tip PDL_Long (la fel cum ar fi fost
caz cu calificativul „int”). Acest lucru încearcă din nou să evite problemele de depășire la utilizare
tipuri de date mici (de exemplu, imagini cu octeți). Cu toate acestea, atunci când tipul de date al operației este mai mare
decât tipul specificat în calificativul „tip+” „b” va fi creat cu tipul de date al
operația, de exemplu, când „a” este de tip dublu, atunci „b” va fi și el dublu. Noi speram
sunteți de acord că acesta este un comportament sensibil pentru „sumover”. Ar trebui să fie evident cum
calificativul „float+” funcționează prin analogie. Poate deveni necesar să se poată specifica un set
de tipuri alternative pentru parametri. Cu toate acestea, probabil că acest lucru nu va fi implementat
până când cineva vine cu o utilizare rezonabilă pentru el.
Rețineți că acum a trebuit să specificăm macrocomanda $GENERIC cu numele pdl-ului pentru a deriva
tip din acel argument. De ce este asta? Dacă ai urmat cu atenție explicațiile noastre, o vei face
am realizat că în unele cazuri „b” va avea un alt tip decât tipul de
Operațiune. Apelarea macrocomenzii „$GENERIC” cu „b” ca argument asigură că tipul
va fi întotdeauna același cu cel al lui „b” în acea parte a buclei generice.
Acesta este cam tot ce se poate spune despre secțiunea „Pars” într-un apel „pp_def”. Tu ar trebui
amintiți-vă că această secțiune definește semnătură a unei funcții definite de PP, puteți utiliza
mai multe opțiuni pentru a califica anumite argumente drept ieșire și argumente temporare și toate
dimensiunile la care vă puteți referi ulterior în secțiunea „Cod” sunt definite prin nume.
Este important să înțelegeți semnificația semnăturii, deoarece în ultimul PDL
versiuni îl puteți folosi pentru a defini funcții threaded din Perl, adică ceea ce numim noi
Perl nivel filetat. Vă rugăm să verificați PDL::Indexing pentru detalii.
Cod secțiune
Secțiunea „Cod” conține codul XS real care va fi în partea cea mai interioară a unui
buclă de fir (dacă nu știți ce este o buclă de fir, atunci încă nu ați citit
PDL::Indexare; fă-o acum ;) după ce toate macrocomenzile PP (cum ar fi $GENERIC) și funcțiile PP au fost
extins (cum ar fi funcția „buclă” pe care o vom explica în continuare).
Să reiterăm rapid exemplul „sumover”:
pp_def('sumover',
Pars => 'a(n); int+ [o] b()',
Cod => '$GENERIC(b) tmp=0;
bucla(n) %{ tmp += a(); %}
$b() = tmp;',
);
Construcția „buclă” din secțiunea „Cod” se referă și la numele dimensiunii, așa că nu
trebuie să specificați orice limită: bucla este dimensionată corect și totul este făcut pentru dvs.,
din nou.
În continuare, există faptul surprinzător că „$a()” și „$b()” fac nu conţine indexul. Acest
nu este necesar pentru că trecem în buclă n și ambele variabile știu ce dimensiuni
au, astfel încât să știe în mod automat că sunt trecute în buclă.
Această caracteristică este foarte utilă în multe locuri și face un cod mult mai scurt. De
desigur, există momente când doriți să ocoliți acest lucru; aici este o funcție care face a
matrice simetrică și servește ca exemplu de codificare a buclei explicite:
pp_def('symm',
Pars => 'a(n,n); [o]c(n,n);',
Cod => „buclă(n) %{
int n2;
pentru(n2=n; n2<$SIZE(n); n2++) {
$c(n0 => n, n1 => n2) =
$c(n0 => n2, n1 => n) =
$a(n0 => n, n1 => n2);
}
%}
'
);
Să analizăm ce se întâmplă. În primul rând, ce ar trebui să facă această funcție? Din ea
semnătură vezi că este nevoie de o matrice 2D cu un număr egal de coloane și rânduri și
scoate o matrice de aceeași dimensiune. Dintr-o matrice de intrare dată $a calculează o simetrică
matricea de ieșire $c (simetrică în sensul matricei că A^T = A unde ^T înseamnă matrice
transpune, sau în limbajul PDL $c == $c->xchg(0,1)). Face acest lucru folosind doar valorile
pe si sub diagonala lui $a. În matricea de ieșire $c toate valorile de pe și de sub
diagonala sunt aceleasi cu cele din $a in timp ce cele de deasupra diagonalei sunt o imagine in oglinda a
cele de sub diagonală (de sus și dedesubt sunt aici interpretate în felul în care PDL tipărește
pdls 2D). Dacă această explicație încă sună puțin ciudat, mergeți mai departe, faceți un mic fișier
în care scrieți această definiție, construiți noua extensie PDL (vezi secțiunea despre
Makefiles pentru codul PP) și încercați-l cu câteva exemple.
După ce am explicat ce ar trebui să facă funcția, merită câteva puncte
notând din punct de vedere sintactic. În primul rând, obținem dimensiunea dimensiunii numite
„n” din nou utilizând macrocomanda $SIZE. În al doilea rând, apar brusc aceste „n0” și „n1” amuzante
nume de index în cod, deși semnătura definește doar dimensiunea „n”. De ce asta? The
motivul devine clar când observați că atât prima cât și cea de-a doua dimensiune a lui $a și $b
sunt denumite „n” în semnătura lui „symm”. Aceasta îi spune PDL::PP că primul și al doilea
dimensiunea acestor argumente ar trebui să aibă aceeași dimensiune. În caz contrar, funcția generată
va genera o eroare de rulare. Cu toate acestea, acum într-un acces la $a și $c PDL::PP nu poate figura
la care index „n” se mai referă doar din numele indexului. De aceea
indicii cu nume de dimensiuni egale sunt numerotați de la stânga la dreapta începând cu 0, de exemplu în
exemplul de mai sus „n0” se referă la prima dimensiune a lui $a și $c, „n1” la a doua și
curând.
În toate exemplele de până acum, am folosit doar membrii „Pars” și „Code” ai hashului that
a fost transmis la „pp_def”. Cu siguranță există și alte chei care sunt recunoscute de PDL::PP și
despre unele dintre ele vom auzi pe parcursul acestui document. Găsiți un (neexhaustiv)
lista de taste din Anexa A. O listă de macrocomenzi și funcții PP (am întâlnit doar
unele dintre cele din exemplele de mai sus încă) care sunt extinse în valori ale argumentului hash
la „pp_def” este rezumat în Anexa B.
În acest moment, ar putea fi potrivit să menționăm că PDL::PP nu este un program complet static,
set bine conceput de rutine (cum spune Tuomas: „nu mai gândi la PP ca la un set de
rutine cioplite în piatră”) ci mai degrabă o colecție de lucruri pe care autorul PDL::PP
(Tuomas J. Lukka) a considerat că va trebui să scrie des în rutinele sale de extensie PDL.
PP încearcă să fie extensibil, astfel încât în viitor, pe măsură ce apar noi nevoi, noul cod comun să poată face acest lucru
fi abstractizat înapoi în ea. Dacă doriți să aflați mai multe despre motivul pentru care ați dori să vă schimbați
PDL::PP și cum se face, verificați secțiunea despre elementele interne PDL::PP.
Manipularea Rău Valorile
Dacă nu aveți suport de valoare proastă compilat în PDL, puteți ignora această secțiune și
chei aferente: „BadCode”, „HandleBad”, ... (încercați să imprimați valoarea lui
$PDL::Bad::Stare - dacă este egal cu 0, treceți direct mai departe).
Există mai multe chei și macrocomenzi folosite la scrierea codului pentru a gestiona valorile proaste. Primul
una este tasta „HandleBad”:
HandleBad => 0
Aceasta semnalează o rutină pp ca NU manipularea valorilor proaste. Dacă această rutină este trimisă piddles
cu „badflag” setat, apoi un mesaj de avertizare este tipărit către STDOUT și piddles
sunt procesate ca și cum valoarea utilizată pentru a reprezenta valorile proaste ar fi un număr valid. The
Valoarea „badflag” nu este propagată la piddle-urile de ieșire.
Un exemplu de utilizare este pentru rutinele FFT, care în general nu au o modalitate
de a ignora o parte din date.
HandleBad => 1
Acest lucru face ca PDL::PP să scrie cod suplimentar care asigură utilizarea secțiunii BadCode și
că macro-ul „$ISBAD()” (și frații săi) funcționează.
HandleBad nu este dat
Dacă vreuna dintre piddle-urile de intrare are „badflag-ul” setat, atunci piddle-urile de ieșire vor fi
au "badflag" setat, dar orice BadCode furnizat este ignorat.
Valoarea „HandleBad” este folosită pentru a defini conținutul cheii „BadDoc”, dacă nu este
dat.
Pentru a gestiona valorile proaste, codul trebuie scris oarecum diferit; de exemplu,
$c() = $a() + $b();
devine ceva de genul
dacă ( $a() != BADVAL && $b() != BADVAL ) {
$c() = $a() + $b();
} Else {
$c() = BADVAL;
}
Cu toate acestea, vrem a doua versiune numai dacă valorile proaste sunt prezente în piddle-urile de intrare
(și se dorește acel suport de valoare proastă!) - altfel vrem de fapt codul original.
Aici intervine tasta „BadCode”; îl folosiți pentru a specifica codul de executat dacă este rău
valorile pot fi prezente, iar PP îl folosește atât pe acesta, cât și secțiunea „Cod” pentru a crea ceva
ca:
if ( bad_values_are_present ) {
fancy_threadloop_stuff {
BadCode
}
} Else {
fancy_threadloop_stuff {
Cod
}
}
Această abordare înseamnă că nu există practic nicio suprasarcină atunci când nu sunt prezente valori proaste
(adică rutina badflag returnează 0).
Secțiunea BadCode poate folosi aceleași macrocomenzi și constructe de buclă ca și secțiunea Code.
Cu toate acestea, nu ar fi de mare folos fără următoarele macrocomenzi suplimentare:
$ISBAD(var)
Pentru a verifica dacă valoarea unui piddle este proastă, utilizați macrocomanda $ISBAD:
if ( $ISBAD(a()) ) { printf("a() este rău\n"); }
De asemenea, puteți accesa elementele date ale unui piddle:
if ( $ISBAD(a(n=>l)) ) { printf("elementul %d din a() este rău\n", l); }
$ISGOOD(var)
Acesta este opusul macrocomenzii $ISBAD.
$SETBAD(var)
Pentru atunci când doriți să setați un element de piddle rău.
$ISBADVAR(c_var,pdl)
Dacă ați stocat în cache valoarea unui piddle „$a()” într-o variabilă c („foo”, să spunem), atunci
verificați dacă este rău, utilizați „$ISBADVAR(foo,a)”.
$ISGOODVAR(c_var,pdl)
Ca mai sus, dar de data aceasta verificând dacă valoarea din cache nu este rea.
$SETBADVAR(c_var,pdl)
Pentru a copia valoarea proastă pentru un piddle într-o variabilă ac, utilizați „$SETBADVAR(foo,a)”.
A FACE: menționați macrocomenzile „$PPISBAD()” etc.
Folosind aceste macrocomenzi, codul de mai sus ar putea fi specificat ca:
Cod => '$c() = $a() + $b();',
BadCode => '
dacă ( $ISBAD(a()) || $ISBAD(b()) ) {
$SETBAD(c());
} Else {
$c() = $a() + $b();
}',
Deoarece acesta este Perl, TMTOWTDI, puteți scrie și:
BadCode => '
if ( $ISGOOD(a()) && $ISGOOD(b()) ) {
$c() = $a() + $b();
} Else {
$SETBAD(c());
}',
Dacă doriți acces la valoarea badflag-ului pentru un anumit piddle, puteți utiliza
Macrocomenzi „$PDLSTATExxxx()”:
$PDLSTATEISBAD(pdl)
$PDLSTATEISGOOD(pdl)
$PDLSTATESETBAD(pdl)
$PDLSTATESETGOOD(pdl)
A FACE: menționați opțiunile „FindBadStatusCode” și „CopyBadStatusCode” la „pp_def”, de asemenea
ca tasta „BadDoc”.
interfațare ta propriu/biblioteca funcții folosind PP
Acum, luați în considerare următoarele: aveți propria dvs. funcție C (care poate face, de fapt, parte din
vreo bibliotecă pe care doriți să o interfațați cu PDL) care ia ca argumente doi pointeri către
vectori de dublu:
void myfunc(int n,double *v1,double *v2);
Modul corect de definire a funcției PDL este
pp_def('functia mea',
Pars => 'a(n); [o]b(n);',
GenericTypes => ['D'],
Cod => 'functia mea($SIZE(n),$P(a),$P(b));'
);
„$P(”deSintaxa „)” returnează un pointer la primul element, iar celelalte elemente sunt
garantat că va minți după aceea.
Observați că aici este posibil să faceți multe greșeli. În primul rând, trebuie folosit $SIZE(n).
în loc de „n”. În al doilea rând, nu ar trebui să puneți bucle în acest cod. În al treilea rând, aici ne întâlnim
o nouă cheie hash recunoscută de PDL::PP : declarația „GenericTypes” îi spune PDL::PP să
GENERAȚI NUMAI TYPELOOP FOP LISTA DE TIPURI SPECIFICATE. În acest caz „dublu”. Acest
are doua avantaje. În primul rând, dimensiunea codului compilat este redusă considerabil, în al doilea rând, dacă
argumentele non-duble sunt transmise „myfunc()” PDL le va converti automat în
dublați înainte de a trece la rutina externă C și convertiți-le înapoi după aceea.
De asemenea, se poate folosi „Pars” pentru a califica tipurile de argumente individuale. Astfel s-ar putea de asemenea
scrie asta ca:
pp_def('functia mea',
Pars => 'dublu a(n); dublu [o]b(n);',
Cod => 'functia mea($SIZE(n),$P(a),$P(b));'
);
Specificația tipului din „Pars” scutește argumentul de la variația tipului buclei -
mai degrabă este convertit automat și de la tipul specificat. Acest lucru este evident
util într-un exemplu mai general, de exemplu:
void myfunc(int n,float *v1,long *v2);
pp_def('functia mea',
Pars => 'float a(n); lung [o]b(n);',
GenericTypes => ['F'],
Cod => 'functia mea($SIZE(n),$P(a),$P(b));'
);
Rețineți că folosim în continuare „GenericTypes” pentru a reduce dimensiunea buclei de tip, evident PP ar putea
în principiu, identificați acest lucru și faceți-o automat, deși codul încă nu a atins asta
nivel de sofisticare!
În cele din urmă, rețineți că atunci când tipurile sunt convertite automat, TREBUIE să utilizați calificativul „[o]” pentru
variabilele de ieșire sau modificările cele mai dificile vor fi optimizate de PP!
Dacă interfațați o bibliotecă mare, puteți automatiza interfața și mai mult. Perl poate
te ajută din nou(!) în a face asta. În multe biblioteci aveți anumite convenții de apelare.
Acest lucru poate fi exploatat. Pe scurt, puteți scrie un mic parser (ceea ce chiar nu este
dificil în Perl) care generează apoi apelurile la „pp_def” din descrierile analizate ale
funcțiile din acea bibliotecă. Pentru un exemplu, vă rugăm să verificați Slatec interfață în
Arborele „Lib” al distribuției PDL. Dacă doriți să verificați (în timpul depanării) către care apelează
Funcțiile PP codul dvs. Perl generat un mic pachet de ajutor este util, care
înlocuiește funcțiile PP cu unele cu nume identic care își transferă argumentele la stdout.
Doar spune
perl -MPDL::PP::Dump myfile.pd
pentru a vedea apelurile către „pp_def” și prieteni. Incearca cu ops.pd si slatec.pd. Daca esti
interesat (sau doriți să o îmbunătățiți), sursa este în Basic/Gen/PP/Dump.pm
Altele macro-uri si funcții in il Cod secțiune
Macrocomenzi: Până acum am întâlnit macrocomenzile $SIZE, $GENERIC și $P. Acum mergem
explicați rapid celelalte macrocomenzi care sunt extinse în secțiunea „Cod” din PDL::PP
cu exemple de utilizare a acestora.
$T Macrocomanda $T este folosită pentru comutatoarele de tip. Acest lucru este foarte util atunci când trebuie să utilizați
diferite funcții externe (de ex. bibliotecă) în funcție de tipul de intrare al argumentelor.
Sintaxa generală este
$Ttypeletters(type_alternatives)
unde „typeletters” este o permutare a unui subset de litere „BSULFD” care stau
pentru Byte, Short, Ushort etc. și „type_alternatives” sunt expansiunile atunci când tipul
a operațiunii PP este egală cu cea indicată de litera respectivă. hai sa
ilustrează această descriere de neînțeles printr-un exemplu. Presupunând că aveți două C
funcţionează cu prototipuri
void float_func(float *in, float *out);
void double_func(double *in, double *out);
care fac practic același lucru, dar unul acceptă float și ceilalți pointers duble.
Le puteți interfața cu PDL definind o funcție generică „foofunc” (care va
apelați funcția corectă în funcție de tipul transformării):
pp_def('foofunc',
Pars => ' a(n); [o] b();',
Cod => ' $TFD(float_func,double_func) ($P(a),$P(b));'
GenericTypes => [qw(FD)],
);
Vă rugăm să rețineți că nu puteți spune
Cod => ' $TFD(float,double)_func ($P(a),$P(b));'
deoarece macro-ul $T se extinde cu spații finale, în mod analog macrocomenzilor C preprocesor.
Forma puțin mai lungă ilustrată mai sus este corectă. Dacă vrei cu adevărat concizie, tu
poate desigur
„$TBSULFD('.(alăturați-vă „,',hartă {"nume_identificator_lung_$_"}
qw/byt short unseigned lounge flotte dubble/).');'
$PP
Macrocomanda $PP este folosită pentru așa-numitul fizic indicatorul acces. fizic se referă la
unele optimizări interne ale PDL (pentru cei care sunt familiarizați cu nucleul PDL suntem
vorbind despre optimizările vaffine). Această macrocomandă este în principal pentru uz intern și pentru dvs
nu ar trebui să-l folosească în niciunul dintre codurile dvs. obișnuite.
$COMP (și secțiunea „OtherPars”)
Macrocomanda $COMP este folosită pentru a accesa valori non-pdl în secțiunea de cod. Numele lui este
derivate din implementarea transformărilor în PDL. Variabilele pe care le puteți referi
la utilizarea $COMP sunt membri ai structurii ``compilate'' care reprezintă PDL
transformarea în cauză, dar nu conține încă nicio informație despre dimensiuni
(pentru mai multe detalii verificați PDL::Internals). Cu toate acestea, puteți trata $COMP doar ca pe un
cutie neagră fără să știe nimic despre implementarea transformărilor în PDL.
Deci, când ați folosi această macrocomandă? Utilizarea sa principală este de a accesa valorile argumentelor care
sunt declarate în secțiunea „OtherPars” a unei definiții „pp_def”. Dar atunci nu ai făcut-o
ai auzit încă de cheia „OtherPars”?! Să avem un alt exemplu care ilustrează
utilizarea tipică a ambelor funcții noi:
pp_def('pnmout',
Pars => 'a(m)”,
OtherPars => "char* fd",
GenericTypes => [qw(BUSL)],
Cod => 'PerlIO *fp;
IO *io;
io = GvIO(gv_fetchpv($COMP(fd),FALSE,SVt_PVIO));
dacă (!io || !(fp = IoIFP(io)))
croak("Nu pot da seama FP");
if (PerlIO_write(fp,$P(a),len) != len)
croak("Eroare la scrierea fișierului pnm");
„);
Această funcție este folosită pentru a scrie date dintr-un pdl într-un fișier. Descriptorul de fișier este trecut
ca șir în această funcție. Acest parametru nu intră în secțiunea „Pars”.
deoarece nu poate fi tratat util ca un pdl, ci mai degrabă în numele potrivit
Secțiunea „OtherPars”. Parametrii din secțiunea „OtherPars” îi urmează pe cei din „Pars”
secțiunea la invocarea funcției, de exemplu
deschide FILE,">out.dat" sau die "nu s-a putut deschide out.dat";
pnmout($pdl,'FIȘIER');
Când doriți să accesați acest parametru în secțiunea de cod, trebuie să spuneți PP prin
folosind macro-ul $COMP, adică scrieți „$COMP(fd)” ca în exemplu. Altfel PP
nu ar ști că „fd” la care vă referiți este același cu cel specificat în
Secțiunea „OtherPars”.
O altă utilizare a secțiunii „OtherPars” este să setați o dimensiune numită în semnătură.
Să avem un exemplu cum se face asta:
pp_def('setdim',
Pars => '[o] a(n)',
OtherPars => 'int ns => n',
Cod => 'bucla(n) %{ $a() = n; %}',
);
Aceasta spune că dimensiunea numită „n” va fi inițializată din valoarea lui alte
parametru „ns” care este de tip întreg (presupun că v-ați dat seama că folosim
Sintaxa „CType From => named_dim”). Acum puteți apela această funcție în modul obișnuit:
setdim(($a=null),5);
imprima $a;
[ 0 1 2 3 4 ]
Desigur, această funcție nu este foarte utilă, dar demonstrează cum funcționează. daca tu
apelați funcția cu un pdl existent și nu trebuie să specificați în mod explicit
dimensiunea lui „n” deoarece PDL::PP o poate da seama din dimensiunile pdl-ului non-null. În
în acest caz, dați doar parametrul de dimensiune ca „-1”:
$a = hist($b);
setdim($a,-1);
Asta ar trebui să o facă.
Singura funcție PP pe care am folosit-o în exemple până acum este „buclă”. În plus,
În prezent, există alte două funcții care sunt recunoscute în secțiunea „Cod”:
buclă de fir
După cum am auzit mai sus, semnătura unei funcții definite de PP definește dimensiunile tuturor
argumentele pdl implicate într-un primitiv Operațiune. Cu toate acestea, apelați adesea la
funcții pe care le-ați definit cu PP cu pdls care au mai multe dimensiuni decât acelea
specificat în semnătură. În acest caz operația primitivă este efectuată asupra tuturor
subslice de dimensionalitate adecvată în ceea ce se numește a fir buclă (Vezi si
prezentare generală de mai sus și PDL::Indexare). Presupunând că aveți o anumită noțiune despre acest concept
va aprecia probabil că operația specificată în secțiunea de cod ar trebui să fie
optimizat deoarece aceasta este cea mai strânsă buclă din interiorul unei bucle de fir. Totuși, dacă revedeți
exemplul în care definim funcția „pnmout”, vă veți da repede seama că uitându-vă
sus descriptorul de fișier „IO” în bucla firului interior nu este foarte eficient la scriere
un pdl cu multe rânduri. O abordare mai bună ar fi să căutați o dată descriptorul „IO”.
în afara buclei de fir și utilizați valoarea acesteia apoi în interiorul buclei de fir cel mai strâns. Aceasta este
exact unde funcția „threadloop” este utilă. Iată o definiție îmbunătățită
de „pnmout” care folosește această funcție:
pp_def('pnmout',
Pars => 'a(m)”,
OtherPars => "char* fd",
GenericTypes => [qw(BUSL)],
Cod => 'PerlIO *fp;
IO *io;
int len;
io = GvIO(gv_fetchpv($COMP(fd),FALSE,SVt_PVIO));
dacă (!io || !(fp = IoIFP(io)))
croak("Nu pot da seama FP");
len = $MĂRIMEA(m) * sizeof($GENERIC());
fireloop %{
if (PerlIO_write(fp,$P(a),len) != len)
croak("Eroare la scrierea fișierului pnm");
%}
„);
Acest lucru funcționează după cum urmează. În mod normal, codul C pe care îl scrieți în secțiunea „Cod” este plasat
în interiorul unei bucle de fir (adică PP generează codul XS corespunzător de înfășurare în jurul acestuia).
Cu toate acestea, atunci când utilizați în mod explicit funcția „threadloop”, PDL::PP recunoaște acest lucru și
nu vă înglobează codul cu o buclă suplimentară de fir. Acest lucru are efectul de a vă codifica
scrierea în afara buclei de fir este executată o singură dată pe transformare și doar codul
cu perechea „%{ ... %}” din jur este plasată în cea mai strânsă buclă de fir. Acest
de asemenea, este util atunci când doriți să efectuați o decizie (sau orice alt cod, în special
Cod intensiv CPU) o singură dată pe fir, adică
pp_addhdr('
#definiți RAW 0
#define ASCII 1
„);
pp_def('do_raworascii',
Pars => 'a(); b(); [o]c()',
OtherPars => „mod int”,
Cod => ' comutator ($COMP(modul)) {
carcasă RAW:
fireloop %{
/* fac lucruri brute */
%}
rupe;
caz ASCII:
fireloop %{
/* fac chestii ASCII */
%}
rupe;
implicit:
croak ("mod necunoscut");
}'
);
Tipuri
Funcția de tipuri funcționează similar cu macrocomanda $T. Cu toate acestea, cu funcția „tipuri”
codul din următorul bloc (delimitat de „%{” și „%}” ca de obicei) este executat pentru toate
acele cazuri în care tipul de date al operației este Orice of tipurile reprezentate de
literele din argument pentru a „tasta”, de ex
Cod => '...
tipuri (BSUL) %{
/* efectuează operația de tip întreg */
%}
tipuri(FD) %{
/* efectuează operația în virgulă mobilă */
%}
... '
RedoDimsCode Secțiune
Tasta „RedoDimsCode” este o cheie opțională care este utilizată pentru a calcula dimensiunile pidurilor la
runtime în cazul în care regulile standard pentru calcularea dimensiunilor din semnătură nu sunt
suficient. Conținutul intrării „RedoDimsCode” este interpretat în același mod ca
se interpretează secțiunea Cod -- de exemplu, macrocomenzile PP sunt extinse și rezultatul este
interpretat ca cod C. Scopul codului este de a seta dimensiunea unor dimensiuni care
apar în semnătură. Alocarea spațiului de stocare și fireloop-urile și așa mai departe vor fi configurate ca
dacă dimensiunea calculată ar fi apărut în semnătură. În codul tău, mai întâi calculezi
dimensiunea dorită a unei dimensiuni numite în semnătură în funcție de nevoile dvs. și apoi
atribuiți-i această valoare prin intermediul $MĂRIMEA() macro.
Ca exemplu, luați în considerare următoarea situație. Interfațați o bibliotecă externă
rutină care necesită o matrice temporară pentru ca spațiul de lucru să fie transmis ca argument. Două
matricele de date de intrare care sunt transmise sunt p(m) și x(n). Matricea de date de ieșire este y(n). The
rutina necesită o matrice de spațiu de lucru cu o lungime de n+m*m și ați dori stocarea
creat automat la fel cum ar fi pentru orice piddle marcat cu [t] sau [o]. Ce
ai vrea este să spui ceva de genul
pp_def( "functia meaexterna",
Pars => " p(m); x(n); [o] y; [t] lucru(n+m*m); ",...
dar asta nu va funcționa, pentru că PP nu poate interpreta expresii cu aritmetică în
semnătură. In schimb tu scrii
pp_def( "functia meaexterna",
Pars => " p(m); x(n); [o] y; [t] lucrare(wn); ",
RedoDimsCode => "
int im = $PDL(p)->dims[0];
int in = $PDL(x)->dims[0];
int min = în + im * im;
int inw = $PDL(work)->dims[0];
$SIZE(wn) = inw >= min ? inw : min; ",
Cod => "
externalfunc($P(p),$P(x),$MĂRIMEA(m),$SIZE(n),$P(lucrare));
";)
Acest cod funcționează după cum urmează: macrocomanda $PDL(p) se extinde la un pointer către structura pdl pentru
piagul p. Nu doriți un pointer către date ( adică $P ) în acest caz, deoarece dvs
vrei să accesezi metodele pentru piddle la nivelul C. Obții prima dimensiune a
fiecare dintre piddles și depozitați-le în numere întregi. Apoi calculezi lungimea minimă
matrice de lucru poate fi. Dacă utilizatorul a trimis un piddle „lucrare” cu spațiu de stocare suficient, atunci lăsați-l
singur. Dacă utilizatorul a trimis, să spunem un pdl nul sau deloc, atunci dimensiunea lui wn va fi
zero și îl resetați la valoarea minimă. Înainte de codul din secțiunea Cod este
PP executat va crea stocarea adecvată pentru „lucrare” dacă nu există. Rețineți că dvs
a luat doar prima dimensiune a lui „p” și „x”, deoarece utilizatorul poate să fi trimis piddles cu
dimensiuni suplimentare de filetare. Desigur, piddle-ul temporar „funcționează” (rețineți steagul [t])
oricum nu ar trebui să i se dea nicio dimensiune a firului.
Puteți folosi, de asemenea, „RedoDimsCode” pentru a seta dimensiunea unui piddle marcat cu [o]. In acest
în cazul în care setați dimensiunile pentru dimensiunea numită în semnătură folosind $MĂRIMEA() ca în
exemplul precedent. Cu toate acestea, deoarece piddle-ul este marcat cu [o] în loc de [t],
dimensiunile filetului vor fi adăugate dacă este necesar, la fel ca și în cazul în care dimensiunea dimensiunii ar fi
calculate din semnătură conform regulilor uzuale. Iată un exemplu din
PDL::Matematică
pp_def("polyroots",
Pars => 'cr(n); ci(n); [o]rr(m); [o]ri(m);',
RedoDimsCode => 'int sn = $PDL(cr)->dims[0]; $MĂRIMEA(m) = sn-1;',
Părțile de intrare sunt părțile reale și imaginare ale coeficienților complecși ai lui a
polinom. Părțile de ieșire sunt părți reale și imaginare ale rădăcinilor. Există „n”
rădăcinile unui polinom de ordinul „n” și un astfel de polinom are coeficienți „n+1” (
zeoreth prin „n”-lea). În acest exemplu, threading-ul va funcționa corect. Adică
prima dimensiune a piddle-ului de ieșire cu dimensiunea sa ajustată, dar alte filete
dimensiunile vor fi alocate ca și cum nu ar exista „RedoDimsCode”.
Typemap manipulare in il „AlteParți” secțiune
Secțiunea „OtherPars” discutată mai sus este deseori absolut crucială atunci când dvs
interfață biblioteci externe cu PDL. Cu toate acestea, în multe cazuri, fie bibliotecile externe
utilizați tipuri derivate sau pointeri de diferite tipuri.
Modul standard de a gestiona acest lucru în Perl este utilizarea unui fișier „typemap”. Acest lucru este discutat în
unele detalii în perlxs în documentația standard Perl. În PP funcționalitatea este foarte
similar, astfel încât să puteți crea un fișier „typemap” în directorul în care se află fișierul PP
iar când este construit, este citit automat pentru a afla traducerea adecvată
între tipul C și tipul încorporat al lui Perl.
Acestea fiind spuse, există câteva diferențe importante față de manipularea generală a tipurilor
în XS. Prima, și probabil cea mai importantă, este că în acest moment sunt indicatorii către tipuri
nu este permis în secțiunea „OtherPars”. Pentru a ocoli această limitare, trebuie să utilizați
Tipul „IV” (mulțumesc lui Judd Taylor pentru că a subliniat că acest lucru este necesar pentru portabilitate).
Cel mai bine este probabil să ilustrați acest lucru cu câteva fragmente de cod:
De exemplu, funcția „gsl_spline_init” are următoarea declarație C:
int gsl_spline_init(gsl_spline * spline,
const double xa[], const double ya[], size_t size);
În mod clar, matricele „xa” și „ya” sunt candidate pentru a fi transmise ca piddles și
Argumentul „dimensiune” este doar lungimea acestor piddles, astfel încât să poată fi gestionat de către
Macrocomandă „$SIZE()” în PP. Problema este indicatorul către tipul „gsl_spline”. Naturalul
soluția ar fi să scrieți o declarație „OtherPars” a formularului
OtherPars => 'gsl_spline *spl'
și scrieți un scurt fișier „typemap” care a gestionat acest tip. Acest lucru nu funcționează în prezent
in orice caz! Deci, ceea ce trebuie să faceți este să ocoliți ușor problema (și în unele moduri
si asta e mai usor!):
Soluția este să declari „spline” în secțiunea „OtherPars” folosind o „Integer Value”,
„IV”. Acest lucru ascunde natura variabilei de PP și apoi trebuie să (ei bine, pentru a evita
cel puțin avertismentele compilatorului!) Efectuați o distribuție de tip atunci când utilizați variabila în codul dvs.
Astfel, „OtherPars” ar trebui să ia forma:
OtherPars => 'IV spl'
iar când îl vei folosi în cod vei scrie
INT2PTR(gsl_spline *, $COMP(spl))
unde macrocomanda Perl API „INT2PTR” a fost folosită pentru a gestiona proiectarea pointerului pentru a evita
avertismente și probleme ale compilatorului pentru mașinile cu Perl mixt pe 32 de biți și 64 de biți
configuratii. Adunând acest lucru așa cum a făcut Andres Jordan (cu modificarea
folosind „IV” de Judd Taylor) în „gsl_interp.pd” din sursa de distribuție, obțineți:
pp_def('init_meat',
Pars => 'dublu x(n); dublu y(n);',
OtherPars => 'IV spl',
Cod =>'
gsl_spline_init,( INT2PTR(gsl_spline *, $COMP(spl)), $P(x),$P(y),$SIZE(n)));'
);
unde am eliminat un apel macro wrapper, dar asta ar întuneca discuția.
Cealaltă diferență minoră în comparație cu gestionarea hărții de tip standard în Perl este aceea
utilizatorul nu poate specifica locații non-standard typemap sau nume de fișiere typemap folosind
Opțiunea „TYPEMAPS” din MakeMaker... Astfel, puteți utiliza doar un fișier numit „typemap” și/sau
Trucul „IV” de mai sus.
Altele util PP chei in de date operaţie Definitii
Ați auzit deja despre cheia „OtherPars”. În prezent, nu există multe alte chei
pentru o operațiune de date care va fi utilă în programarea PP normală (oricare ar fi aceasta). În
De fapt, ar fi interesant să auziți despre un caz în care credeți că aveți nevoie de mai mult decât de ce
este furnizat momentan. Vă rugăm să vorbiți pe una dintre listele de corespondență PDL. Cele mai multe altele
cheile recunoscute de „pp_def” sunt cu adevărat utile doar pentru ceea ce numim felie operațiuni (A se vedea
tot mai sus).
Un lucru care este puternic planificat este numărul variabil de argumente, care va fi a
putin complicat.
O listă incompletă a cheilor disponibile:
La loc
Setarea acestei taste marchează rutina ca funcțională în loc - adică intrarea și ieșirea
stropii sunt la fel. Un exemplu este „$a->inplace->sqrt()” (sau „sqrt(inplace($a))”).
Pe loc => 1
Utilizați atunci când rutina este o funcție unară, cum ar fi „sqrt”.
Pe loc => ['a']
Dacă există mai multe coduri de intrare, specificați numele celui care poate fi
schimbat în locul utilizând o referință de matrice.
Inplace => ['a','b']
Dacă există mai mult de un piddle de ieșire, specificați numele codului de intrare și
piddle de ieșire într-o referință de matrice cu 2 elemente. Probabil că nu este necesar, dar a plecat
pentru completitudine.
Dacă se folosesc valori proaste, trebuie avut grijă să se asigure propagarea
badflag atunci când este utilizat inplace; luați în considerare acest fragment din De bază/Rău/rău.pd:
pp_def('replacebad',HandleBad => 1,
Pars => 'a(); [o]b();',
OtherPars => 'dublu newval',
În loc => 1,
CopyBadStatusCode =>
'/* propagă badflag dacă este la locul lui ȘI s-a schimbat */
dacă ( a == b && $ISPDLSTATEBAD(a) )
PDL->propogate_badflag( b, 0 );
/* asigurați-vă întotdeauna că rezultatul este „bun” */
$SETPDLSTATEGOD(b);
',
...
Deoarece această rutină elimină toate valorile proaste, atunci piddle-ul de ieșire a avut steag-ul său prost
degajat. Dacă rulați pe loc (deci „a == b”), atunci trebuie să le spunem tuturor copiilor lui „a”
că steagul rău a fost șters (pentru a economisi timp, ne asigurăm că sunăm
„PDL->propogate_badgflag” numai dacă piddle-ul de intrare avea steag-ul prost setat).
NOTĂ: o idee este că documentația pentru rutină ar putea fi automată
marcat pentru a indica faptul că poate fi executat în loc, adică ceva similar cu cum
„HandleBad” setează „BadDoc” dacă nu este furnizat (nu este o soluție ideală).
Altele PDL::PP funcții la a sustine concis pachet definiție
Până acum, am descris funcțiile „pp_def” și „pp_done”. PDL::PP exportă câteva
alte funcții pentru a vă ajuta să scrieți definiții concise ale pachetelor de extensie PDL.
pp_addhdr
Adesea, atunci când interfațați funcțiile bibliotecii ca în exemplul de mai sus, trebuie să includeți
fișiere suplimentare C includ. Deoarece fișierul XS este generat de PP, avem nevoie de anumite mijloace
faceți PP să introducă directivele de includere corespunzătoare în locul potrivit în XS generat
fişier. În acest scop există funcția „pp_addhdr”. Aceasta este și funcția de utilizat
când doriți să definiți unele funcții C pentru uz intern de către unele dintre funcțiile XS
(care sunt în mare parte funcții definite de „pp_def”). Prin includerea acestor funcții aici, dvs
asigurați-vă că PDL::PP introduce codul înainte de punctul în care modulul XS real
secțiunea începe și, prin urmare, va fi lăsată neatinsă de xsubpp (cf. perlxs si perlxstut
pagini de manual).
Un apel tipic ar fi
pp_addhdr('
#include /* avem nevoie de definiții pentru XXXX */
#include "libprotos.h" /* prototipuri ale funcțiilor bibliotecii */
#include "mylocaldecs.h" /* Local decs */
static void do_the real_work (PDL_Byte * in, PDL_Byte * out, int n)
{
/* faceți niște calcule cu datele */
}
„);
Acest lucru vă asigură că toate constantele și prototipurile de care aveți nevoie vor fi incluse în mod corespunzător și
că puteți utiliza funcțiile interne definite aici în „pp_def”, de exemplu:
pp_def('barfoo',
Pars => ' a(n); [o] b(n)',
GenericTypes => ['B'],
Cod => ' int ns = $SIZE(n);
face_the_real_work($P(a),$P(b),ns);
',
);
pp_addpm
În multe cazuri, codul PP real (adică argumentele pentru apelurile „pp_def”) este doar o parte din
pachetul pe care îl implementați în prezent. Adesea există cod Perl suplimentar și XS
cod pe care l-ați fi scris în mod normal în fișierele pm și XS, care sunt acum automat
generate de PP. Deci, cum să introduceți aceste lucruri în acele fișiere generate dinamic?
Din fericire, există câteva funcții, numite în general „pp_addXXX” care vă ajută
în a face asta.
Să presupunem că aveți cod Perl suplimentar care ar trebui să intre în generat pm-fişier. Acest
este ușor de realizat cu comanda „pp_addpm”:
pp_addpm(<<'EOD');
=head1 NUME
PDL::Lib::Mylib -- o interfață PDL către biblioteca Mylib
=head1 DESCRIERE
Acest pachet implementează o interfață la pachetul Mylib cu full
suport de filetare și indexare (vezi L ).
=tăiat
utilizați PGPLOT;
=head2 use_myfunc
această funcție aplică operația myfunc tuturor
elemente ale pdl de intrare indiferent de dimensiuni
și returnează suma rezultatului
=tăiat
sub use_myfunc {
my $pdl = shift;
myfunc($pdl->clump(-1),($res=null));
returnează $res->sum;
}
EOD
pp_add_exported
Probabil ai prins ideea. În unele cazuri, doriți și să exportați suplimentar
funcții. Pentru a evita să intri în probleme cu PP care se încurcă și cu @EXPORT
matrice, îi spuneți lui PP să vă adauge funcțiile la lista de funcții exportate:
pp_add_exported('use_myfunc gethynx');
pp_add_isa
Comanda „pp_add_isa” funcționează ca funcția „pp_add_exported”. Argumentele pentru
„pp_add_isa” sunt adăugate listei @ISA, de ex
pp_add_isa(' Unii::Altele::Clasa ');
pp_binecuvântează
Dacă rutinele dvs. pp_def vor fi utilizate ca metode obiect, utilizați „pp_bless” pentru a specifica
pachet (adică clasa) la care dvs pp_defvor fi adăugate metode ed. De exemplu,
„pp_bless('PDL::MyClass')”. Valoarea implicită este „PDL” dacă aceasta este omisă.
pp_addxs
Uneori doriți să adăugați cod XS suplimentar propriu (care, în general, nu este implicat cu
orice probleme de threading/indexare, dar furnizează alte funcționalități pe care doriți să le accesați
din partea Perl) la fișierul XS generat, de exemplu
pp_addxs('','
# Determinați caracterul mașinii
int
isbigendian()
COD:
i scurt nesemnat;
PDL_Byte *b;
i = 42; b = (PDL_Byte*) (void*) &i;
dacă (*b == 42)
RETVAL = 0;
altfel dacă (*(b+1) == 42)
RETVAL = 1;
altfel
croak("Imposibil - mașina nu este nici mare, nici mică!!\n");
IEȘIRE:
RETVAL
„);
În special „pp_add_exported” și „pp_addxs” trebuie folosite cu grijă. PP utilizeaza
PDL::Exporter, prin urmare, lăsați PP să vă exporte funcția înseamnă că acestea sunt adăugate la
lista standard de funcții exportate implicit (lista definită de eticheta de export
``:Func`'). Dacă utilizați „pp_addxs” nu ar trebui să încercați să faceți nimic care implică threading
sau indexarea directă. PP este mult mai bun la generarea codului potrivit de la dvs
definiții.
pp_add_boot
În cele din urmă, poate doriți să adăugați un cod la secțiunea BOOT a fișierului XS (dacă nu o faceți
stiu ce este verifica perlxs). Acest lucru se face cu ușurință cu comanda „pp_add_boot”:
pp_add_boot(<
descrip = mylib_initialize(KEEP_OPEN);
dacă (descriere == NULL)
croak("Nu se poate inițializa biblioteca");
GlobalStruc->descrip = descrip;
GlobalStruc->maxfiles = 200;
EOB
pp_export_nimic
În mod implicit, PP.pm pune toate sub-urile definite folosind funcția pp_def în ieșirea .pm
lista de EXPORT a fișierului. Acest lucru poate crea probleme dacă creați un obiect subclasat unde
nu doriți să exportați nicio metodă. (adică metodele vor fi apelate numai folosind
$obiect->sintaxa metodei).
Pentru aceste cazuri puteti suna pp_export_nothing() pentru a șterge lista de export. Exemplu (la
sfârșitul fișierului .pd):
pp_export_nimic();
pp_terminat();
pp_core_importList
În mod implicit, PP.pm pune „utilizați Core;” linie în fișierul de ieșire .pm. Aceasta importă Core
nume exportate în spațiul de nume curent, ceea ce poate crea probleme dacă sunteți peste-
călărind una dintre metodele Core din fișierul curent. Ajungi prin a primi mesaje de genul
„Atenție: sub sumover redefinit în fișierul subclass.pm” când rulați programul.
În aceste cazuri, pp_core_importList poate fi folosit pentru a schimba ceea ce este importat
Core.pm. De exemplu:
pp_core_importList('()')
Acest lucru ar avea ca rezultat
folosește Core();
fiind generat în fișierul de ieșire .pm. Acest lucru ar duce la importarea niciunui nume din
Core.pm. În mod similar, sunând
pp_core_importList(' qw/ barf /')
ar avea ca rezultat
utilizați Core qw/ barf/;
fiind generat în fișierul de ieșire .pm. Acest lucru ar duce la importarea doar a „barf”.
din Core.pm.
pp_setversion
Sunt destul de sigur că acest lucru vă permite să setați simultan fișierele .pm și .xs.
versiuni, evitând astfel distorsiunile inutile între cele două. Pentru a utiliza acest lucru, pur și simplu aveți
următoarea linie la un moment dat din fișierul dvs. .pd:
pp_setversion('0.0.3');
Cu toate acestea, nu utilizați acest lucru dacă utilizați Module::Build::PDL. Consultați documentația acelui modul pentru
Detalii.
pp_deprecate_module
Dacă un anumit modul este considerat învechit, această funcție poate fi folosită pentru a-l marca ca
depreciat. Acest lucru are ca efect emiterea unui avertisment atunci când un utilizator încearcă să „utilizeze” fișierul
modul. POD-ul generat pentru acest modul are, de asemenea, o notificare de depreciere. The
modulul de înlocuire poate fi transmis ca un argument ca acesta:
pp_deprecate_module( infavor => "PDL::NewNonDeprecatedModule" );
Rețineți că funcția afectează avertismentul de rulare și POD-ul.
Efectuarea ta PP funcţie "privat"
Să presupunem că aveți o funcție în modulul dumneavoastră numită PDL::foo care utilizează PP
funcția „bar_pp” pentru a face sarcini grele. Dar nu vrei să faci publicitate acelui „bar_pp”
există. Pentru a face acest lucru, trebuie să mutați funcția PP în partea de sus a fișierului modulului
apel
pp_export_nothing()
pentru a șterge lista „EXPORT”. Pentru a vă asigura că nu există nicio documentație (chiar și documentele PP implicite).
generat, stabilit
Doc => undef
și pentru a preveni adăugarea funcției la tabelul de simboluri, setați
PMFunc => ''
în declarația dvs. pp_def (vezi Image2D.pd pentru un exemplu). Acest lucru va face în mod eficient
funcția ta PP „privată”. Cu toate acestea, este mereu accesibil prin PDL::bar_pp datorită lui Perl
proiectarea modulelor. Dar a face ca acesta să fie privat îl va determina pe utilizator să iasă foarte departe de el sau ea
mod de a-l folosi, astfel încât el sau ea să-și asume consecințele!
Felie operaţie
Secțiunea de operare a feliei din acest manual este furnizată folosind fluxul de date și evaluarea leneșă:
când ai nevoie, roagă-l pe Tjl să-l scrie. o livrare intr-o saptamana de cand primesc emailul
este 95% probabilă și livrarea în două săptămâni este 99% probabilă.
Și oricum, operațiunile de slice necesită o cunoaștere mult mai intimă a interiorului PDL
decât operațiunile de date. În plus, complexitatea problemelor implicate este
considerabil mai mare decât cea din operațiunea medie de date. Daca vrei sa convingi
voi înșivă de acest fapt aruncați o privire la Basic/Slices/slices.pd dosar în PDL
distributie :-). Cu toate acestea, funcțiile generate folosind operațiunile slice sunt la
centrul de manipulare a indexului și a capabilităților de flux de date ale PDL.
De asemenea, există o mulțime de probleme murdare cu piddles și vaffines virtuale pe care le vom face
săriți complet aici.
Felii si Rău Valorile
Operațiunile slice trebuie să poată gestiona valori proaste (dacă suportul este compilat în PDL).
Cel mai ușor lucru de făcut este să te uiți Basic/Slices/slices.pd pentru a vedea cum funcționează asta.
Alături de „BadCode”, există și tastele „BadBackCode” și „BadRedoDimsCode” pentru
„pp_def”. Cu toate acestea, orice „EquivCPOffsCode” ar trebui nu trebuie schimbate, deoarece orice modificări sunt
absorbită în definiția macrocomenzii „$EQUIVCPOFFS()” (adică este gestionată
automat prin PDL::PP>.
A puțini notiţe on scris a feliere rutină...
Următoarele câteva paragrafe descriu scrierea unei noi rutine de tăiere („gamă”); orice
erorile sunt CED. (--CED 26-aug-2002)
Manipularea of "a avertiza" si "barf" in PP Cod
Pentru tipărirea mesajelor de avertizare sau avortarea/moarea, puteți apela „warn” sau „barf” de la PP
cod. Cu toate acestea, ar trebui să știți că aceste apeluri au fost redefinite folosind C
macrocomenzile de preprocesor la „PDL->barf” și „PDL->warn”. Aceste redefiniri sunt în vigoare pentru
vă împiedică să apelați din greșeală „warn” sau „barf” al lui Perl, ceea ce poate cauza
segfaults în timpul pthreading (adică procesor multi-threading).
Versiunile proprii ale PDL de „barf” și „warn” vor pune în coadă mesajele de avertizare sau barf până după
pthreading este finalizat și apoi apelați versiunile perl ale acestor rutine.
Consultați PDL::ParallelCPU pentru mai multe informații despre pthreading.
UTILE RUTINELE
Structura „Core” PDL, definită în Basic/Core/pdlcore.h.PL, conține indicii către a
număr de rutine care vă pot fi utile. Majoritatea acestor rutine se ocupă de
manipularea piddle-urilor, dar unele sunt mai generale:
PDL->qsort_B( PDL_Byte *xx, int a, int b)
Sortați matricea „xx” între indicii „a” și „b”. Există și versiuni pentru
alte tipuri de date PDL, cu postfix „_S”, „_U”, „_L”, „_F” și „_D”. Orice modul care folosește
aceasta trebuie să se asigure că „PDL::Ufunc” este încărcat.
PDL->qsort_ind_B( PDL_Byte *xx, int *ix, int a, int b)
Cât despre „PDL->qsort_B”, dar de data aceasta sortând indici mai degrabă decât datele.
Rutina „med2d” în Lib/Image2D/image2d.pd arată cum sunt utilizate astfel de rutine.
MAKEFILES PENTRU PP DOSARE
Dacă intenționați să generați un pachet din fișierul PP (extensiile de fișiere tipice sunt
„.pd” sau „.pp” pentru fișierele care conțin cod PP) este cel mai ușor și mai sigur să părăsiți
generarea comenzilor adecvate pentru Makefile. În cele ce urmează vom schița
formatul tipic al unui Perl Makefile din care să construiți și să instalați automat pachetul dvs
o descriere într-un fișier PP. Majoritatea regulilor pentru a construi fișierele xs, pm și alte fișiere necesare
din fișierul PP sunt deja predefinite în pachetul PDL::Core::Dev. Trebuie doar să
spuneți MakeMaker să-l folosească.
În cele mai multe cazuri, vă puteți defini Makefile like
# Makefile.PL pentru un pachet definit de codul PP.
utilizați PDL::Core::Dev; # Preluați utilități de dezvoltare
utilizați ExtUtils::MakeMaker;
$pachet = ["mylib.pd",Mylib,PDL::Lib::Mylib];
%hash = pdlpp_stdargs($pachet);
$hash{OBJECT} .= 'cod_addițional$(OBJ_EXT) ';
$hash{clean}->{FILES} .= ' pentru a șterge_Ccode$(OBJ_EXT) ';
$hash{'VERSION_FROM'} = 'mylib.pd';
WriteMakefile(%hash);
sub MY::postamble { pdlpp_postamble($pachet); }
Aici, lista din $package este: mai întâi: numele fișierului sursă PP, apoi prefixul pentru
fișierele produse și în cele din urmă numele întregului pachet. Puteți modifica hash-ul în orice
cum doriți, dar ar fi rezonabil să rămâneți în anumite limite, astfel încât pachetul dvs
va continua să lucreze cu versiunile ulterioare ale PDL.
Dacă nu doriți să utilizați argumente preambalate, iată un generic Makefile.PL că poți
adaptați-vă propriilor nevoi:
# Makefile.PL pentru un pachet definit de codul PP.
utilizați PDL::Core::Dev; # Preluați utilități de dezvoltare
utilizați ExtUtils::MakeMaker;
WriteMakefile(
'NAME' => 'PDL::Lib::Mylib',
'VERSION_FROM' => 'mylib.pd',
„TYPEMAPS” => [&PDL_TYPEMAP()],
'OBJECT' => 'mylib$(OBJ_EXT) additional_Ccode$(OBJ_EXT)',
'PM' => { 'Mylib.pm' => '$(INST_LIBDIR)/Mylib.pm'},
'INC' => &PDL_INCLUDE(), # adăugați directori de includere așa cum este cerut de lib
'LIBS' => [''], # adăugați directive de legătură după cum este necesar
'clean' => {'FIȘIERE' =>
„Mylib.pm Mylib.xs Mylib$(OBJ_EXT)
suplimentar_Ccode$(OBJ_EXT)'},
);
# Adăugați regula genpp; aceasta va invoca PDL::PP pe fișierul nostru PP
# argumentul este o referință de matrice în care matricea are trei elemente șir:
# arg1: numele fișierului sursă care conține codul PP
# arg2: numele de bază al fișierelor xs și pm care urmează să fie generate
# arg3: numele pachetului care urmează să fie generat
sub MY::postamble { pdlpp_postamble(["mylib.pd",Mylib,PDL::Lib::Mylib]); }
Pentru a face viața și mai ușoară, PDL::Core::Dev definește funcția „pdlpp_stdargs” care returnează
un hash cu valori implicite care pot fi transmise (fie direct, fie după caz
modificare) la un apel la WriteMakefile. În prezent, „pdlpp_stdargs” returnează un hash unde
cheile se completează după cum urmează:
(
„NUME” => $mod,
„TYPEMAPS” => [&PDL_TYPEMAP()],
„OBJECT” => „$pref\$(OBJ_EXT)”,
PM => {"$pref.pm" => "\$(INST_LIBDIR)/$pref.pm"},
MAN3PODS => {"$src" => "\$(INST_MAN3DIR)/$mod.\$(MAN3EXT)"},
„INC” => &PDL_INCLUDE(),
'LIBS' => [''],
'clean' => {'FILES' => "$pref.xs $pref.pm $pref\$(OBJ_EXT)"},
)
Aici, $src este numele fișierului sursă cu cod PP, $pref prefixul pentru fișierul generat
Fișierele .pm și .xs și $mod numele modulului de extensie de generat.
INTERNE
Elementele interne ale versiunii actuale constau dintr-un tabel mare care oferă regulile
conform cărora lucrurile sunt traduse și subs care implementează aceste reguli.
Mai târziu, ar fi bine să faci tabelul modificabil de către utilizator, astfel încât să fie diferit
lucrurile pot fi încercate.
[Meta comentariu: aici sper să fie mai multe în viitor; în prezent, cel mai bun pariu al tău va fi
pentru a citi codul sursă :-( sau întrebați pe listă (încercați mai întâi pe acesta din urmă) ]
Apendice A: niste chei recunoscut by PDL::PP
Dacă nu se specifică altfel, argumentele sunt șiruri de caractere. Cheile marcate cu (rele) sunt numai
folosit dacă suportul cu valoare proastă este compilat în PDL.
Pars
definiți semnătura funcției dvs
OtherPars
argumente care nu sunt pdls. Implicit: nimic. Aceasta este o listă separată prin punct și virgulă
argumente, de exemplu, "OtherPars => 'int k; valoare dublă; char* fd'". Vezi $COMP(x) și, de asemenea
aceeași intrare în Anexa B.
Cod
codul propriu-zis care implementează funcționalitatea; mai multe macro-uri PP și funcții PP
sunt recunoscute în valoarea șirului
Manevrare prost (prost)
Dacă este setată la 1, se presupune că rutina acceptă valori proaste și codul din BadCode
cheia este folosită dacă sunt prezente valori greșite; de asemenea, stabilește lucrurile astfel încât „$ISBAD()”
pot fi folosite macrocomenzi etc. Dacă este setată la 0, faceți ca rutina să imprime un avertisment dacă există
piddle-urile de intrare au steagul lor prost setat.
BadCode (rău)
Dați codul care urmează să fie utilizat dacă valorile proaste pot fi prezente în piddle-urile de intrare. Numai folosit
dacă „HandleBad => 1”.
GenericTypes
O referință de matrice. Matricea poate conține orice subset de șiruri de un singur caracter „B”,
„S”, „U”, „L”, „Q”, „F” și „D”, care specifică ce tipuri va accepta operația dvs.
Semnificația fiecărui tip este:
B - octet semnat (adică caracter semnat)
S - scurt cu semn (număr întreg de doi octeți)
U - scurt nesemnat
L - semnat lung (întreg de patru octeți, int pe sisteme pe 32 de biți)
Q - semnat lung lung (întreg de opt octeți)
F - plutitor
D - dublu
Acest lucru este foarte util (și important!) atunci când interfațați o bibliotecă externă. Mod implicit:
[qw/BSULQFD/]
La loc
Marcați o funcție ca fiind capabilă să lucreze la locul său.
Inplace => 1 dacă Pars => 'a(); [o]b();'
Inplace => ['a'] if Pars => 'a(); b(); [o]c();'
Inplace => ['a','b'] if Pars => 'a(); b(); [o]c(); [o]d();'
Dacă se folosesc valori proaste, trebuie avut grijă să se asigure propagarea
badflag atunci când este utilizat în loc; de exemplu, vedeți codul pentru „replacebad” în
De bază/Rău/rău.pd.
Doc Folosit pentru a specifica un șir de documentație în format Pod. Consultați PDL::Doc pentru informații despre
Convenţiile de documentare PDL. Notă: în cazul special în care este șirul PP „Doc”.
o linie aceasta este folosită implicit pentru referință rapidă ȘI pentru documentație!
Dacă câmpul Doc este omis, PP va genera documentație implicită (după tot ce știe
despre Semnătura).
Dacă doriți cu adevărat ca funcția să NU fie documentată în niciun fel în acest moment (de ex
pentru o rutină internă sau pentru că o faceți în altă parte a codului) în mod explicit
specificați „Doc =>undef”.
BadDoc (rău)
Conține textul returnat de comanda „badinfo” (în „perldl”) sau comutatorul „-b”.
la scriptul shell „pdldoc”. În multe cazuri, nu va trebui să specificați acest lucru, deoarece
informatia poate fi creata automat de PDL::PP. Totuși, așa cum se cuvine computerului-
textul generat, este mai degrabă stilat; poate fi mult mai bine să o faci singur!
NoPthread
Flag opțional pentru a indica funcția PDL nu utilizați fire de execuție a procesorului (de ex
pthreads sau fire POSIX) pentru a împărți munca în mai multe nuclee CPU. Această opțiune este
de obicei, setată la 1 dacă funcția PDL de bază nu este sigură pentru fire. Dacă această opțiune
nu este prezent, atunci se presupune că funcția este threadsafe. Această opțiune se aplică numai
dacă PDL a fost compilat cu firele de execuție POSIX activate.
PMCode
Funcțiile PDL vă permit să treceți într-un piddle în care doriți să fie salvată rezultatul. Acest
este la îndemână deoarece puteți aloca o dată un piddle de ieșire și îl puteți reutiliza de mai multe ori; cel
alternativa ar fi ca PDL să creeze un nou piddle de fiecare dată, ceea ce poate risipi calculul
cicluri sau, mai probabil, RAM. Această flexibilitate suplimentară vine cu costul a mai mult
complexitate: PDL::PP trebuie să scrie funcții care sunt suficient de inteligente pentru a număra
Argumentele trecute la ea și creează noi zgârieturi din mers, dar numai dacă le dorești.
PDL::PP este suficient de inteligent pentru a face asta, dar există restricții privind ordinea argumentelor și
asemenea. Dacă doriți o funcție mai flexibilă, puteți scrie propria dvs. latură Perl
wrapper și specificați-l în cheia PMCode. Șirul pe care îl furnizați trebuie (ar trebui)
definiți o funcție Perl cu un nume care se potrivește cu ceea ce i-ați dat lui pp_def la prima
loc. Când doriți să invocați în cele din urmă funcția generată de PP, va trebui
furnizați toate piddle-urile în ordinea exactă specificată în semnătură: output piddles are
nu este opțional, iar funcția generată de PP nu va returna nimic. Cele obscucate
numele pe care îl vei numi este _ _int.
Cred că această documentație are nevoie de clarificări suplimentare, dar acest lucru va trebui.
:-(
PMFunc
Când pp_def generează funcții, de obicei le definește în pachetul PDL. Atunci,
în fișierul .pm pe care îl generează pentru modulul dvs., adaugă de obicei o linie care
în esență, copiază acea funcție în tabelul de simboluri al pachetului curent cu cod
care arata cam asa:
*func_name = \&PDL::func_name;
Este puțin mai inteligent decât atât (știe când să învelească așa ceva într-un
bloc BEGIN, de exemplu, și dacă ați specificat ceva diferit pentru pp_bless), dar
asta e esenta. Dacă nu vă interesează să importați funcția în curentul dvs
tabelul de simboluri al pachetului, puteți specifica
PMFunc => '',
PMFunc nu are alte efecte secundare, așa că l-ați putea folosi pentru a introduce cod Perl arbitrar
în modulul dvs., dacă doriți. Cu toate acestea, ar trebui să utilizați pp_addpm dacă doriți să adăugați Perl
cod pentru modulul dvs.
Apendice B: PP macro-uri si funcții
Macrocomenzi
Macro-urile etichetate cu (proastă) sunt utilizate numai dacă suportul pentru valori proaste este compilat în PDL.
$variablename_from_sig()
accesați un pdl (după numele său) care a fost specificat în semnătură
$COMP(x)
accesați o valoare în structura datelor private a acestei transformări (obișnuită în principal pentru
utilizați un argument care este specificat în secțiunea „OtherPars”)
$SIZE(n)
înlocuit în timpul execuției cu dimensiunea reală a unui numit dimensiunea (după cum este specificat în
semnătură)
$GENERIC()
înlocuit cu tipul C care este egal cu tipul de rulare al operației
$P(a) un pointer de acces la PDL numit „a” în semnătură. Util pentru interfața cu C
funcții
$PP(a) un pointer fizic acces la pdl "a"; în principal pentru uz intern
$TXXX(alternativă, alternativă)
alternative de extindere în funcție de tipul operațiunii de rulare, unde XXX este unele
șir care se potrivește cu „/[BSULFD+]/”.
$PDL(a)
returnează un pointer către structura de date pdl (pdl *) a piddle „a”
$ISBAD(a()) (prost)
returnează adevărat dacă valoarea stocată în „a()” este egală cu valoarea proastă pentru acest piddle.
Necesită ca „HandleBad” să fie setat la 1.
$ISGOOD(a()) (rău)
returnează adevărat dacă valoarea stocată în „a()” nu este egală cu valoarea proastă pentru aceasta
face pipi. Necesită ca „HandleBad” să fie setat la 1.
$SETBAD(a()) (prost)
Setează „a()” să egaleze valoarea proastă pentru acest piddle. Necesită setarea „HandleBad”.
la 1.
funcții
„buclă(DIMS) %{ ... %}”
bucla peste dimensiunile numite; limitele sunt generate automat de PP
"threadloop %{ ... %}"
include următorul cod într-o buclă de fir
„tipuri(TIPURI) %{ ... %}”
executați următorul cod dacă tipul de operație este oricare dintre „TIPURI”
Apendice C: funcţii importate by PDL::PP
O serie de funcții sunt importate atunci când „utilizați PDL::PP”. Acestea includ funcții care
controlează codul C sau XS generat, funcțiile care controlează codul Perl generat și
funcții care manipulează pachetele și tabelele de simboluri în care este creat codul.
Generator C si XS Cod
Scopul principal al PDL::PP este de a vă facilita înfășurarea motorului de filetare în jurul dumneavoastră
propriul cod C, dar puteți face și alte lucruri.
pp_def
Folosit pentru a include motorul de threading în jurul codului dvs. C. Practic tot acest document
discută despre utilizarea lui pp_def.
pp_terminat
Indică că ați terminat cu PDL::PP și că ar trebui să genereze fișierele .xs și .pm
pe baza celorlalte funcții pp_* pe care le-ați apelat. Această funcție ia nr
argumente.
pp_addxs
Acest lucru vă permite să adăugați cod XS la fișierul dvs. .xs. Acest lucru este util dacă doriți să creați Perl-
funcții accesibile care invocă codul C, dar nu pot sau nu ar trebui să invoce threading-ul
motor. XS este mijlocul standard prin care împachetați codul C accesibil în Perl. Puteți
afla mai multe la perlxs.
pp_add_boot
Această funcție adaugă orice șir pe care îl treceți în secțiunea XS BOOT. Secțiunea BOOT
este codul C care este apelat de Perl atunci când modulul este încărcat și este util pentru
inițializare automată. Puteți afla mai multe despre XS și secțiunea BOOT la perlxs.
pp_addhdr
Adaugă cod pur-C la fișierul dvs. XS. Fișierele XS sunt structurate astfel încât codul C pur trebuie
vin înaintea specificațiilor XS. Acest lucru vă permite să specificați un astfel de cod C.
pp_boundscheck
PDL verifică în mod normal limitele acceselor dumneavoastră înainte de a le face. Poți întoarce asta
activat sau dezactivat în timpul rulării setând MyPackage::set_boundscheck. Această funcție vă permite
pentru a elimina acea flexibilitate de rulare și nu faceți verificarea limitelor. De asemenea, returnează
starea curentă de verificare a limitelor dacă este apelată fără argumente.
NOTĂ: Nu am găsit nimic despre verificarea limitelor în altă documentație. Acea
trebuie abordată.
Generator Perl Cod
Multe funcții importate atunci când utilizați PDL::PP vă permit să modificați conținutul fișierului
fișier .pm generat. Pe lângă pp_def și pp_done, rolul acestor funcții este
în primul rând pentru a adăuga cod în diferite părți ale fișierului dvs. .pm generat.
pp_addpm
Adaugă cod Perl la fișierul .pm generat. PDL::PP de fapt ține evidența a trei
diferite secțiuni ale codului generat: de sus, de mijloc și de jos. Poti sa adaugi
Codul Perl la secțiunea Middle folosind forma cu un singur argument, unde argumentul este
Codul Perl pe care doriți să îl furnizați. În forma cu două argumente, primul argument este an
hash anonim cu o singură cheie care specifică unde se pune al doilea argument,
care este șirul pe care doriți să-l adăugați la fișierul .pm. Hașul este unul dintre acestea
trei:
{La => 'Sus'}
{La => „Mijloc”}
{La => „Bot”}
De exemplu:
pp_addpm({La => „Bot”}, <
=head1 Câteva documentații
Știu că scriu asta în mijlocul fișierului meu, dar va merge la
fundul.
=tăiat
POD
Avertisment: Dacă, în mijlocul fișierului .pd, puneți documentație destinată
partea de jos a capsulei dvs., veți confunda complet CPAN. Pe de altă parte, dacă în
mijlocul fișierului dvs. .pd, adăugați un cod Perl destinat pentru partea de jos sau de sus a fișierului dvs
Fișierul .pm, trebuie doar să vă confundați. :-)
pp_beginwrap
Adaugă împachetarea blocului BEGIN. Totuși, anumite declarații pot fi împachetate în blocuri BEGIN
comportamentul implicit este să nu aibă o astfel de împachetare.
pp_addbegin
Setează codul să fie adăugat în partea de sus a fișierului .pm, chiar și deasupra codului pe care îl specificați
cu „pp_addpm({La => „Sus”}, ...)”. Spre deosebire de pp_addpm, apelarea acestui lucru suprascrie orice
a fost acolo înainte. În general, probabil că nu ar trebui să-l folosești.
Urmărire Linie Numere
Când primiți erori de compilare, fie din codul dvs. asemănător C, fie din codul dvs. Perl, vă poate ajuta
pentru a face acele erori înapoi la numerele de rând din fișierul sursă la care a apărut eroarea
a avut loc.
pp_line_numbers
Preia un număr de linie și un șir de cod (de obicei lung). Numărul liniei ar trebui
indicați linia la care începe citatul. Acesta este de obicei „__LINE__” al lui Perl
literal, cu excepția cazului în care utilizați heredocs, caz în care este „__LINE__ + 1”. The
șirul returnat are directive #line intercalate pentru a ajuta compilatorul să raporteze erori
pe linia potrivită.
Modificatoare il Simbol Tabel si Export Comportament
PDL::PP exportă de obicei toate funcțiile generate folosind pp_def și, de obicei, le instalează
în tabelul cu simboluri PDL. Cu toate acestea, puteți modifica acest comportament cu aceste funcții.
pp_binecuvântează
Setează pachetul (tabelul de simboluri) la care se adaugă codul XS. Valoarea implicită este PDL,
care este în general ceea ce vrei. Dacă utilizați binecuvântarea implicită și creați un
funcția myfunc, atunci puteți face următoarele:
$piddle->myfunc( );
PDL::myfunc($piddle, );
Pe de altă parte, dacă vă binecuvântați funcțiile într-un alt pachet, nu puteți invoca
acestea ca metode PDL și trebuie să le invoce ca:
MyPackage::myfunc($piddle, );
Desigur, puteți utiliza întotdeauna tasta PMFunc pentru a adăuga funcția la simbolul PDL
masă, dar de ce să faci asta?
pp_add_isa
Se adaugă la lista de module din care dvs modul moștenește. Lista implicită este
qw(PDL::Exporter DynaLoader)
pp_core_importlist
În partea de sus a fișierului dvs. .pm generat este o linie care arată astfel:
utilizați PDL::Core;
Puteți modifica asta specificând un șir în pp_core_importlist. De exemplu,
pp_core_importlist('::Blarg');
va avea ca rezultat
utilizați PDL::Core::Blarg;
Puteți folosi acest lucru, de exemplu, pentru a adăuga o listă de simboluri de importat din PDL::Core. Pentru
exemplu:
pp_core_importlist(" ':Intern'");
va conduce la următoarea declarație de utilizare:
utilizați PDL::Core ':Internal';
pp_setversion
Setează versiunea modulului dvs. Versiunea trebuie să fie consecventă între .xs și .pm
fișier și este folosit pentru a vă asigura că bibliotecile dvs. Perl nu suferă de versiune
oblic.
pp_add_exported
Adaugă la lista de export orice nume îi dați. Funcții create folosind pp_def
sunt adăugate automat la listă. Această funcție este utilă dacă definiți orice Perl
funcțiile care utilizează pp_addpm sau pp_addxs pe care doriți să le exportați, de asemenea.
pp_export_nimic
Aceasta resetează lista de simboluri exportate la nimic. Acesta este probabil mai bine numit
„pp_export_clear”, deoarece puteți adăuga simboluri exportate după apel
„pp_export_nothing”. Când este sunat chiar înainte de a apela pp_done, acest lucru vă asigură că dvs
modulul nu exportă nimic, de exemplu, dacă doriți doar ca programatorii să vă folosească
funcţionează ca metode.
Utilizați PDL::PPp online folosind serviciile onworks.net