EnglishFranceseSpagnolo

Favicon di OnWorks

PDL::PPp - Online nel cloud

Esegui PDL::PPp nel provider di hosting gratuito OnWorks su Ubuntu Online, Fedora Online, emulatore online Windows o emulatore online MAC OS

Questo è il comando PDL::PPp che può essere eseguito nel provider di hosting gratuito OnWorks utilizzando una delle nostre molteplici workstation online gratuite come Ubuntu Online, Fedora Online, emulatore online Windows o emulatore online MAC OS

PROGRAMMA:

NOME


PDL::PP - Genera routine PDL da descrizioni concise

SINOSSI


per esempio

pp_def(
'sumover',
Pars => 'a(n); [o]b();',
Codice => q{
doppio tmp=0;
ciclo(n) %{
tmp += $a();
%}
$b() = tempo;
},
);

pp_fatto();

FUNZIONI


Ecco un rapido elenco di riferimento delle funzioni fornite da PDL::PP.

pp_aggiungi_avvio
Aggiungi codice alla sezione BOOT del file XS generato

pp_aggiungi_esportato
Aggiungi funzioni all'elenco delle funzioni esportate

pp_aggiungi_isa
Aggiungi voci alla lista @ISA

pp_addbegin
Imposta il codice da aggiungere all'inizio del file .pm generato

pp_addhdr
Aggiungi codice e include alla sezione C del file XS generato

pp_addpm
Aggiungi codice al file .pm generato

pp_addxs
Aggiungi codice XS extra al file XS generato

pp_beginwrap
Aggiungi il wrapping del blocco BEGIN al codice per il file .pm generato

pp_benedica
Imposta il pacchetto a cui viene aggiunto il codice XS (l'impostazione predefinita è PDL)

pp_boundscheck
Controllare lo stato dell'attività di controllo dei limiti PDL

pp_core_importList
Specifica cosa viene importato da PDL::Core

pp_def
Definire una nuova funzione PDL

pp_deprecate_module
Aggiungi avvisi di runtime e POD su un modulo deprecato

pp_fatto
Segna la fine delle definizioni PDL::PP nel file

pp_export_niente
Cancella l'elenco di esportazione per il modulo generato

pp_line_numbers
Aggiungi le informazioni sul numero di riga per semplificare il debug del codice PDL::PP

pp_setversione
Imposta la versione per i file .pm e .xs

OVERVIEW


Perché abbiamo bisogno di PP? Diverse ragioni: in primo luogo, vogliamo essere in grado di generare subroutine
codice per ciascuno dei tipi di dati PDL (PDL_Byte, PDL_Short, ecc.). AUTOMATICAMENTE. In secondo luogo,
quando si fa riferimento a porzioni di array PDL in Perl (es. "$a->slice('0:10:2,:')" o altro
cose come le trasposizioni) è bello poterlo fare in modo trasparente ed essere in grado
per farlo 'sul posto' - cioè, non dover fare una copia in memoria della sezione. Maniglie in PP
tutti gli elementi necessari e l'aritmetica di compensazione per te. Ci sono anche le nozioni di
threading (richiamata ripetuta della stessa routine per più slice, vedere PDL::Indexing)
e dataflow (vedi PDL::Dataflow) consentito dall'uso di PP.

In molto di ciò che segue assumeremo che il lettore abbia familiarità con i concetti di
threading implicito ed esplicito e manipolazione dell'indice all'interno di PDL. Se non l'hai ancora fatto
sentito parlare di questi concetti o non sono molto a suo agio con loro è ora di controllare
PDL::Indicizzazione.

Come puoi apprezzare dal suo nome, PDL::PP è un pre-processore, cioè espande il codice tramite
sostituzioni per creare un vero codice C. Tecnicamente, l'output è il codice XS (vedi perlx) ma
che è molto vicino a C.

Quindi come usi la PP? Bene, per la maggior parte scrivi semplicemente codice C ordinario tranne che per
costrutti PP speciali che assumono la forma:

$qualcosa (qualcos'altro)

o:

Funzione PP %{

%}

Il costrutto PP più importante è la forma "$array()". Considera il semplicissimo PP
funzione per sommare gli elementi di un vettore 1D (in effetti questo è molto simile all'attuale
codice usato da 'sumover'):

pp_def('somma',
Pars => 'a(n); [o]b();',
Codice => q{
doppia tmp;
tomp = 0;
ciclo(n) %{
tmp += $a();
%}
$b() = tempo;
}
);

Cosa sta succedendo? La riga "Pars =>" è molto importante per PP - specifica tutte le
argomenti e la loro dimensionalità. Lo chiamiamo il firma della funzione PP (confronta
anche le spiegazioni in PDL::Indicizzazione). In questo caso la routine assume una funzione 1-D come
input e restituisce uno scalare 0-D come output. Il costrutto PP "$a()" viene utilizzato per accedere
elementi dell'array a(n) per te - PP riempie tutto il codice C richiesto.

Noterai che stiamo usando l'operatore di virgolette singole "q{}". Questo non è un
incidente. In genere si desidera utilizzare le virgolette singole per indicare le sezioni del codice PP. PDL::PP
usa "$var()" per la sua analisi e se non usi le virgolette singole, Perl proverà a farlo
interpolare "$var()". Inoltre, l'uso dell'operatore "q" con virgolette singole con parentesi graffe lo rende
sembra che tu stia creando un blocco di codice, che è ciò che intendi. (Perl è abbastanza intelligente da
cerca le parentesi graffe nidificate e non chiudere la citazione finché non trova il riccio corrispondente
brace, quindi è sicuro avere blocchi nidificati.) In altre circostanze, ad esempio quando sei
unire insieme un blocco di codice usando concatenazioni di stringhe, è spesso più facile da usare
virgolette singole reali come

Codice => 'qualcosa'.$interpolatable.'qualcos'altro;'

Nel semplice caso in cui si accede a tutti gli elementi, il costrutto PP "loop(n) %{ ...
%}" è usato per eseguire il ciclo su tutti gli elementi nella dimensione "n". Nota questa caratteristica di PP: ALL
LE DIMENSIONI SONO SPECIFICATE DAL NOME.

Questo è più chiaro se evitiamo il PP ciclo continuo() costruire e scrivere il ciclo in modo esplicito
usando la C convenzionale:

pp_def('somma',
Pars => 'a(n); [o]b();',
Codice => q{
int i,n_dimensione;
doppia tmp;
n_dimensione = $TAGLIA(n);
tomp = 0;
for(i=0; i
tmp += $a(n=>i);
}
$b() = tempo;
},
);

che fa lo stesso di prima, ma è più prolisso. Puoi vedere per ottenere l'elemento "i" di
a() diciamo "$a(n=>i)" - stiamo specificando la dimensione con il nome "n". In 2D potremmo dire:

Pars=>'a(m,n);',
...
tmp += $a(m=>i,n=>j);
...

La sintassi "m=>i" prende in prestito dagli hash Perl, che di fatto sono usati nell'implementazione
di PP. Si potrebbe anche dire "$a(n=>j,m=>i)" poiché l'ordine non è importante.

Puoi anche vedere nell'esempio sopra l'uso di un altro costrutto PP - $SIZE(n) per ottenere
la lunghezza della quota "n".

Va tuttavia notato che non dovresti scrivere un C-loop esplicito quando potresti
hanno usato il costrutto "loop" PP poiché PDL::PP controlla automaticamente i limiti del ciclo per
te, l'uso di "loop" rende il codice più conciso, ecc. Ma ci sono certamente situazioni
dove hai bisogno del controllo esplicito del ciclo e ora sai come farlo;).

Per rivisitare 'Perché PP?' - il codice sopra per somma() verrà generato per ogni tipo di dati. Esso
opererà su porzioni di array 'in-place'. Verrà eseguito automaticamente il thread, ad esempio se un 2D
viene fornito l'array verrà chiamato ripetutamente per ogni riga 1D (controlla di nuovo PDL::Indexing per
i dettagli della filettatura). E poi b() sarà un array 1D di somme di ogni riga. Potremmo
chiamalo con $a->xchg(0,1) per sommare invece le colonne. E il tracciamento del flusso di dati ecc. sarà
a disposizione.

Puoi vedere che PP salva il programmatore dalla scrittura di un sacco di codice C inutilmente ripetitivo -
secondo noi questa è una delle migliori caratteristiche del PDL che fa scrivere nuove subroutine C
per il PDL un esercizio sorprendentemente conciso. Un secondo motivo è la capacità di espandere il PP
le tue definizioni di codice concise in codice C diverso in base alle esigenze del computer
architettura in questione. Immagina per esempio di essere fortunato ad avere un supercomputer su
le tue mani; in tal caso vuoi che PDL::PP generi sicuramente codice che ne tragga vantaggio
delle caratteristiche di vettorizzazione/calcolo parallelo della vostra macchina (questo è un progetto per il
futuro). In ogni caso, la linea di fondo è che il tuo codice invariato dovrebbe comunque espandersi a
codice XS funzionante anche se gli interni di PDL sono cambiati.

Inoltre, poiché stai generando il codice in un vero script Perl, ci sono molti divertimenti
cose che puoi fare. Diciamo che devi scrivere sia sumit (come sopra) che multit.
Con un po' di creatività ce la faremo

for({Name => 'sumit', Init => '0', Op => '+='},
{Nome => 'multit', Init => '1', Op => '*='}) {
pp_def($_->{Nome},
Pars => 'a(n); [o]b();',
Codice => '
doppia tmp;
tmp = '.$_->{Init}.';
ciclo(n) %{
tmp '.$_->{Op}.' $a();
%}
$b() = tempo;
');
}

che definisce facilmente entrambe le funzioni. Ora, se in seguito hai bisogno di cambiare la firma o
dimensionalità o altro, devi solo cambiare un posto nel tuo codice. Si certo,
il tuo editor ha "taglia e incolla" e "cerca e sostituisci" ma è ancora meno
fastidioso e decisamente più difficile dimenticare un solo posto e avere strani insetti
insinuarsi. Inoltre, l'aggiunta di 'orit' (bit a bit o) in seguito è un one-liner.

E ricorda, hai davvero tutte le capacità di Perl con te - puoi leggere molto facilmente
qualsiasi file di input e creare routine dalle informazioni in quel file. Per casi semplici come
quanto sopra, l'autore (Tjl) attualmente preferisce la sintassi hash come sopra - non è troppo
molti più caratteri rispetto alla sintassi dell'array corrispondente ma molto più facile da capire e
cambiare.

Dovremmo menzionare qui anche la possibilità di ottenere il puntatore all'inizio dei dati in
memoria - un prerequisito per interfacciare PDL ad alcune librerie. Questo è gestito con il
Direttiva "$P(var)", vedere sotto.

Quando inizi a lavorare su una nuova funzione pp_def'ined, se commetti un errore, di solito lo farai
trova una pila di errori del compilatore che indicano i numeri di riga nel file XS generato. Se tu
sapere come leggere i file XS (o se vuoi imparare nel modo più duro), puoi aprire il
file XS generato e cercare il numero di riga con l'errore. Tuttavia, un recente
aggiunta a PDL::PP aiuta a riportare il numero di riga corretto dei tuoi errori:
"pp_line_numbers". Lavorando con l'esempio del vertice originale, se hai sbagliato a scrivere
tmp nel tuo codice, potresti cambiare il codice (erroneos) in qualcosa del genere e il
compilatore ti darebbe molte più informazioni utili:

pp_def('somma',
Pars => 'a(n); [o]b();',
Codice => pp_line_numbers(__LINE__, q{
doppia tmp;
tomp = 0;
ciclo(n) %{
tmp += $a();
%}
$b() = giri;
})
);

Per la situazione di cui sopra, il mio compilatore mi dice:

...
test.pd:15: errore: 'rmp' non dichiarato (primo utilizzo in questa funzione)
...

Nel mio script di esempio (chiamato test.pd), la riga 15 è esattamente la riga in cui ho creato il mio
errore di battitura: "rmp" invece di "tmp".

Quindi, dopo questa rapida panoramica del sapore generale della programmazione delle routine PDL utilizzando
PDL::PP riassumiamo in quali circostanze dovresti effettivamente usarlo
preprocessore/precompilatore. Dovresti usare PDL::PP se vuoi

· interfaccia PDL a qualche libreria esterna

· scrivere un algoritmo che sarebbe lento se codificato in Perl (non è così spesso come te
pensare; dai un'occhiata a threading e flusso di dati prima).

· essere uno sviluppatore PDL (e anche in questo caso non è obbligatorio)

AVVERTIMENTO


Grazie alla sua architettura, PDL::PP può essere sia flessibile che facile da usare da un lato,
eppure esuberantemente complicato allo stesso tempo. Attualmente, parte del problema è quell'errore
i messaggi non sono molto informativi e se qualcosa va storto, faresti meglio a sapere cosa stai
stanno facendo ed essere in grado di farsi strada attraverso gli interni (o essere in grado di capire da
tentativi ed errori cosa c'è di sbagliato con i tuoi argomenti su "pp_def"). Anche se si sta lavorando per
produrre avvisi migliori, non abbiate paura di inviare le vostre domande alla mailing list se
vai nei guai.

DESCRIZIONE


Ora che hai un'idea di come usare "pp_def" per definire nuove funzioni PDL è il momento di
spiegare la sintassi generale di "pp_def". "pp_def" prende come argomenti prima il nome del
funzione che stai definendo e quindi un elenco di hash che può contenere varie chiavi.

Sulla base di queste chiavi PP genera il codice XS e un file .pm. La funzione "pp_done" (vedi
esempio nella SINOSSI) è usato per dire a PDL::PP che non ci sono più definizioni in
questo file ed è il momento di generare il .xs e
file .pm.

Di conseguenza, potrebbero essercene diversi pp_def() chiamate all'interno di un file (per convenzione files
con codice PP hanno estensione .pd o .pp) ma generalmente solo una pp_fatto().

Esistono due tipi principali di utilizzo di pp_def(), l'"operazione sui dati" e la "fetta"
prototipi di operazione.

L'"operazione sui dati" viene utilizzata per prendere alcuni dati, modificarli ed emettere altri dati; questo
include ad esempio l'operazione '+', matrice inversa, sumover ecc e tutti gli esempi
di cui abbiamo parlato finora in questo documento. Threading implicito ed esplicito e il
la creazione del risultato è curata automaticamente in quelle operazioni. puoi anche
esegui il flusso di dati con "sumit", "sumover", ecc. (non essere costernato se non capisci il
concetto di dataflow in PDL ancora molto bene; è ancora molto sperimentale).

L'operazione "slice" è un tipo diverso di operazione: in un'operazione slice, non lo sei
cambiando qualsiasi dato, stai definendo le corrispondenze tra i diversi elementi di due
piddles (gli esempi includono le definizioni delle funzioni di manipolazione/slicing dell'indice nel file
fette.pd che fa parte della distribuzione PDL; ma attenzione, questo non è un livello introduttivo
roba).

Se PDL è stato compilato con il supporto per valori errati (ad esempio "WITH_BADVAL => 1"), allora addizionale
le chiavi sono necessarie per "pp_def", come spiegato di seguito.

Se sei solo interessato a comunicare con qualche libreria esterna (ad esempio alcune
algebra lineare/libreria matrice), di solito vorrai l'"operazione sui dati" quindi stiamo andando
discuterne prima.

Dati operazione


A semplice esempio
Nell'operazione sui dati, è necessario conoscere le dimensioni dei dati necessarie. Innanzitutto, un esempio
con scalari:

pp_def('aggiungi',
Pars => 'a(); B(); [o]c();',
Codice => '$c() = $a() + $b();'
);

Sembra un po' strano ma analizziamolo. La prima riga è facile: stiamo definendo a
routine con il nome 'add'. La seconda riga dichiara semplicemente i nostri parametri e il
le parentesi indicano che sono scalari. Chiamiamo la stringa che definisce i nostri parametri e
la loro dimensionalità firma di quella funzione. Per la sua rilevanza per quanto riguarda
il threading e le manipolazioni dell'indice controlla la pagina man di PDL::Indexing.

La terza riga è l'operazione effettiva. Devi usare i simboli del dollaro e le parentesi
per fare riferimento ai tuoi parametri (questo probabilmente cambierà in futuro, una volta a
si trova una buona sintassi).

Queste righe sono tutto ciò che è necessario per definire effettivamente la funzione per PDL (beh,
in realtà non lo è; è inoltre necessario scrivere un Makefile.PL (vedi sotto) e creare il
modulo (qualcosa come 'perl Makefile.PL; make'); ma ignoriamolo per il momento).
Quindi ora puoi farlo

usa MyModule;
$a = pdl 2,3,4;
$b = pdl 5;

$c = aggiungi($a,$b);
# o
add($a,$b,($c=nullo)); # Forma alternativa, utile se $c è stato
# preimpostato su qualcosa di grande, non utile qui.

e fare in modo che il threading funzioni correttamente (il risultato è $c == [7 8 9]).

La Pars sezione: , il firma of a PP function
Vedendo il codice di esempio sopra, molto probabilmente ti chiederai: cos'è questo strano "$c=null"
sintassi nella seconda chiamata alla nostra nuova funzione "aggiungi"? Se dai un'altra occhiata al
definizione di "aggiungi" noterai che il terzo argomento "c" è contrassegnato con il
qualificatore "[o]" che dice a PDL::PP che questo è un argomento di output. Quindi la chiamata sopra a
add significa 'crea un nuovo $c da zero con le dimensioni corrette' - "null" è uno speciale
token per 'empty piddle' (potresti chiedere perché non abbiamo usato il valore "undef" per contrassegnarlo
al posto dello specifico PDL "null"; ci stiamo attualmente pensando ;).

[Questo dovrebbe essere spiegato anche in qualche altra sezione del manuale!!] Il motivo per
avere questa sintassi come alternativa è che se hai piddles davvero enormi, puoi farlo

$c = PDL->nullo;
for (un lungo ciclo) {
# munge a, b
aggiungi($a,$b,$c);
# munge c, rimetti qualcosa su a, b
}

ed evitare di allocare e deallocare $c ogni volta. Viene assegnato una volta alla prima
Inserisci() e successivamente la memoria rimane fino a quando $c non viene distrutto.

Se solo dici

$c = aggiungi($a,$b);

il codice generato da PP compilerà automaticamente "$c=null" e restituirà il risultato. Se
vuoi saperne di più sui motivi per cui PDL::PP supporta questo stile in cui output
gli argomenti sono dati come ultimi argomenti controlla la pagina man di PDL::Indexing.

"[o]" non è l'unico qualificatore che un argomento pdl può avere nella firma. Altro
qualificatore importante è l'opzione "[t]" che contrassegna un pdl come temporaneo. Cosa fa quello
significare? Dici a PDL::PP che questo pdl viene utilizzato solo per risultati temporanei nel corso di
il calcolo e non sei interessato al suo valore dopo che il calcolo è stato
completato. Ma perché PDL::PP dovrebbe volerne sapere in primo luogo? La ragione
è strettamente correlato ai concetti di creazione automatica pdl (ne hai sentito parlare sopra) e
threading implicito. Se usi il threading implicito la dimensionalità di automaticamente
pdls creato è in realtà più grande di quello specificato nella firma. Con "[o]" contrassegnato
pdls verranno creati in modo che abbiano le dimensioni aggiuntive richieste dal numero
delle dimensioni implicite del filo. Quando si crea un pdl temporaneo, tuttavia, sarà sempre e solo
essere abbastanza grande da poter contenere il risultato per un'iterazione in un ciclo di thread, ad es
grande quanto richiesto dalla firma. Quindi si spreca meno memoria quando si contrassegna un pdl come
temporaneo. In secondo luogo, puoi utilizzare la creazione automatica dell'output con pdls temporanei anche quando
stanno utilizzando threading esplicito che è vietato per i normali pdls di output contrassegnati con "[o]"
(vedi PDL::Indicizzazione).

Ecco un esempio in cui usiamo il qualificatore [t]. Definiamo la funzione "callf" che
chiama una routine C "f" che necessita di un array temporaneo della stessa dimensione e tipo dell'array
"a" (mi dispiace per il riferimento in avanti per $P; è un accesso tramite puntatore, vedi sotto):

pp_def('callf',
Pars => 'a(n); [t] tmp(n); [o] b()',
Codice => 'int ns = $SIZE(n);
f($P(a),$P(b),$P(tmp),ns);
'
);

Argomento dimensioni e , il firma
Ora abbiamo appena parlato delle dimensioni del pdls e della firma. Come sono correlati?
Diciamo che vogliamo aggiungere uno scalare + il numero indice a un vettore:

pp_def('add2',
Pars => 'a(n); B(); [o]c(n);',
Codice => 'loop(n) %{
$c() = $a() + $b() + n;
%}'
);

Ci sono diversi punti da notare qui: primo, l'argomento "Pars" ora contiene il n
argomenti per dimostrare che abbiamo una singola dimensione in a e c. È importante notare
che le dimensioni sono entità effettive a cui si accede per nome, quindi questo dichiara a e c a
avere l' stesso prime dimensioni. Nella maggior parte delle definizioni PP la dimensione delle dimensioni nominate sarà
essere impostato dalle rispettive dimensioni di pdls non di output (quelli senza flag "[o]") ma
a volte potresti voler impostare la dimensione di una dimensione con nome esplicitamente tramite un
parametro intero. Vedi sotto nella descrizione della sezione "OtherPars" come funziona.

costante argomento dimensioni in , il firma
Supponiamo di voler creare automaticamente un piddle di output e di sapere che su ogni
chiamare la sua dimensione avrà la stessa dimensione (diciamo 9) indipendentemente dalle dimensioni del
input piddles. In questo caso si utilizza la seguente sintassi nella sezione Pars per specificare
la dimensione della dimensione:

'[o] y(n=9); '

Come previsto, se necessario verranno create dimensioni aggiuntive richieste dalla filettatura. Se tu
necessità di assegnare una dimensione denominata secondo una formula più complicata (di una costante)
è necessario utilizzare il tasto "RedoDimsCode" descritto di seguito.

Tipologia conversioni e , il firma
La firma determina anche le conversioni di tipo che verranno eseguite quando un PP
viene invocata la funzione. Quindi cosa succede quando invochiamo uno dei nostri precedentemente definiti
funzioni con pdls di tipo diverso, ad es

add2($a,$b,($ret=null));

dove $a è di tipo "PDL_Float" e $b di tipo "PDL_Short"? Con la firma come mostrato in
la definizione di "add2" sopra il tipo di dati dell'operazione (come determinato in fase di esecuzione) è
quella del pdl di tipo 'highest' (la sequenzaè byte < short < ushort < long < float
< doppio). Nell'esempio add2 il tipo di dati dell'operazione è float ($a ha che
tipo di dati). Tutti gli argomenti pdl vengono quindi convertiti in quel tipo di dati (non lo sono)
convertito sul posto ma viene creata una copia con il tipo giusto se un argomento pdl non ha
il tipo di operazione). I pdl nulli non contribuiscono in alcun modo alla determinazione del
tipo di operazione. Tuttavia, verranno creati con il tipo di dati dell'operazione;
qui, ad esempio, $ret sarà di tipo float. Dovresti essere consapevole di queste regole quando
chiamare le funzioni PP con pdls di diverso tipo per prendere lo spazio di archiviazione aggiuntivo e
requisiti di runtime in considerazione.

Queste conversioni di tipo sono corrette per la maggior parte delle funzioni che normalmente definisci con "pp_def".
Tuttavia, ci sono alcuni casi in cui il comportamento di conversione del tipo leggermente modificato è
desiderato. Per questi casi è possibile utilizzare qualificatori aggiuntivi nella firma per specificare il
proprietà desiderate per quanto riguarda la conversione del tipo. Questi qualificatori possono essere combinati con
quelli che abbiamo già incontrato (il creazione qualificazioni "[o]" e "[t]"). Andiamo
attraverso l'elenco dei qualificatori che modificano il comportamento di conversione del tipo.

Il più importante è il qualificatore "int" che torna utile quando un argomento pdl
rappresenta gli indici in un altro pdl. Diamo un'occhiata a un esempio da "PDL::Ufunc":

pp_def('maximum_ind',
Pars => 'a(n); int [o] b()',
Codice => '$GENERIC() cur;
int cagliata;
ciclo(n) %{
if (!n || $a() > cur) {cur = $a(); curnd = n;}
%}
$b() = cagliata;',
);

La funzione "maximum_ind" trova l'indice dell'elemento più grande di un vettore. Se guardi
alla firma si nota che l'argomento di output "b" è stato dichiarato con il
qualificatore aggiuntivo "int". Ciò ha le seguenti conseguenze per le conversioni di tipo:
indipendentemente dal tipo di input pdl "a" l'output pdl "b" sarà di tipo "PDL_Long"
il che ha senso poiché "b" rappresenterà un indice in "a". Inoltre, se chiami il
funzione con un output esistente pdl "b" il suo tipo non influenzerà il tipo di dati del
funzionamento (vedi sopra). Quindi, anche se "a" è di un tipo più piccolo di "b", non lo sarà
convertito in modo che corrisponda al tipo di "b" ma rimane intatto, il che consente di risparmiare memoria e cicli della CPU
ed è la cosa giusta da fare quando "b" rappresenta indici. Si noti inoltre che è possibile utilizzare il
qualificatore 'int' insieme ad altri qualificatori (i qualificatori "[o]" e "[t]"). L'ordine è
significant -- i qualificatori di tipo precedono i qualificatori di creazione ("[o]" e "[t]").

L'esempio precedente mostra anche l'utilizzo tipico della macro "$GENERIC()". si espande
al tipo corrente in un cosiddetto loop generico. Che cos'è un ciclo generico? Come già
sentito che una funzione PP ha un tipo di dati di runtime come determinato dal tipo degli argomenti pdl
è stato invocato con. Il codice XS generato da PP per questa funzione contiene quindi a
switch come "switch (type) {case PDL_Byte: ... case PDL_Double: ...}" che seleziona un caso
in base al tipo di dati di runtime della funzione (è chiamato un tipo ``loop'' perché c'è
è un ciclo nel codice PP che genera i casi). In ogni caso il tuo codice viene inserito una volta
per ogni tipo di PDL in questa istruzione switch. La macro "$GENERIC()" si espande solo in
rispettivo tipo in ogni copia del codice analizzato in questa istruzione "switch", ad es
La sezione "case PDL_Byte" "cur" si espanderà in "PDL_Byte" e così via per l'altro caso
dichiarazioni. Immagino che ti rendi conto che questa è una macro utile per contenere i valori di pdls in alcuni
codice.

Ci sono un paio di altri qualificatori con effetti simili a "int". Per il tuo
convenienza ci sono i qualificatori "flottante" e "doppio" con analoghe conseguenze su
digitare le conversioni come "int". Supponiamo che tu abbia un molto grande array per il quale si desidera
calcolare le somme di righe e colonne con un equivalente della funzione "sumover". Tuttavia, con
la normale definizione di "sumover" potresti incontrare problemi quando i tuoi dati sono, ad esempio of
digita breve. Una chiamata come

sumover($pdl_grande,($somme = nullo));

risulterà in $sums di tipo short ed è quindi soggetto a errori di overflow se
$large_pdl è un array molto grande. D'altra parte chiama

@dims = $large_pdl->dims; shift @dims;
sumover($large_pdl,($somme = zeri(doppio,@dim))));

non è neanche una buona alternativa. Ora non abbiamo problemi di overflow con $sums ma a
la spesa di una conversione di tipo di $large_pdl per raddoppiare, qualcosa di brutto se questo è davvero
un grande pdl. Ecco dove "doppio" torna utile:

pp_def('sumoverd',
Pars => 'a(n); doppio [o] b()',
Codice => 'doppio tmp=0;
ciclo(n) %{ tmp += a(); %}
$b() = tmp;',
);

Questo ci permette di aggirare la conversione del tipo e i problemi di overflow. Di nuovo, analogo al
Il qualificatore "int" "double" fa sì che "b" sia sempre di tipo double indipendentemente dal tipo
di "a" senza portare a una conversione di tipo di "a" come effetto collaterale.

Infine, ci sono i qualificatori "type+" dove type è uno tra "int" o "float". Che cosa
significherà. Illustriamo il qualificatore "int+" con l'effettiva definizione di
sommatoria:

pp_def('sumover',
Pars => 'a(n); int+ [o] b()',
Codice => '$GENERIC(b) tmp=0;
ciclo(n) %{ tmp += a(); %}
$b() = tmp;',
);

Come avevamo già visto per i qualificatori "int", "float" e "double", un pdl contrassegnato da un
Il qualificatore "type+" non influenza il tipo di dati dell'operazione pdl. Il suo significato è
"rendere questo pdl almeno di tipo "tipo" o superiore, come richiesto dal tipo di
operazione". Nell'esempio sumover questo significa che quando si chiama la funzione con una "a"
di tipo PDL_Short l'output pdl sarà di tipo PDL_Long (proprio come sarebbe stato il
caso con il qualificatore "int"). Questo cerca di nuovo di evitare problemi di overflow durante l'utilizzo
piccoli tipi di dati (es. immagini di byte). Tuttavia, quando il tipo di dati dell'operazione è maggiore
del tipo specificato nel qualificatore "tipo+" "b" verrà creato con il tipo di dati di
l'operazione, ad esempio quando "a" è di tipo double, anche "b" sarà double. Speriamo
sei d'accordo che questo è un comportamento sensato per il "sumover". Dovrebbe essere ovvio come il
Il qualificatore "float+" funziona per analogia. Potrebbe essere necessario essere in grado di specificare un insieme
di tipi alternativi per i parametri. Tuttavia, questo probabilmente non sarà implementato
finché qualcuno non ne trova un uso ragionevole.

Nota che ora dovevamo specificare la macro $GENERIC con il nome del pdl per derivare il
digitare da quell'argomento. Perché? Se hai seguito attentamente le nostre spiegazioni lo farai
si sono resi conto che in alcuni casi "b" avrà un tipo diverso rispetto al tipo di
operazione. Chiamare la macro '$ GENERIC' con "b" come argomento assicura che il tipo
sarà sempre uguale a quello di "b" in quella parte del ciclo generico.

Questo è tutto quello che c'è da dire sulla sezione "Pars" in una chiamata "pp_def". Dovresti
ricorda che questa sezione definisce il firma di una funzione definita da PP, puoi usare
diverse opzioni per qualificare determinati argomenti come output e argomenti temporanei e tutto il resto
le dimensioni a cui poi fare riferimento nella sezione "Codice" sono definite per nome.

È importante che tu capisca il significato della firma poiché nell'ultimo PDL
versioni puoi usarlo per definire funzioni con thread dall'interno di Perl, cioè ciò che chiamiamo
Perl livello threading. Si prega di controllare PDL::Indicizzazione per i dettagli.

La Code pagina
La sezione "Codice" contiene il codice XS effettivo che sarà nella parte più interna di a
thread loop (se non sai cos'è un thread loop allora non hai ancora letto
PDL::Indicizzazione; fallo ora ;) dopo che eventuali macro PP (come $ GENERIC) e funzioni PP sono state
espanso (come la funzione "loop" che spiegheremo in seguito).

Ribadiamo velocemente l'esempio di "sumover":

pp_def('sumover',
Pars => 'a(n); int+ [o] b()',
Codice => '$GENERIC(b) tmp=0;
ciclo(n) %{ tmp += a(); %}
$b() = tmp;',
);

Il costrutto "loop" nella sezione "Codice" si riferisce anche al nome della dimensione, quindi non lo fai
bisogno di specificare eventuali limiti: il ciclo è dimensionato correttamente e tutto è fatto per te,
nuovamente.

Successivamente, c'è il fatto sorprendente che "$a()" e "$b()" lo fanno non contengono l'indice. Questo
non è necessario perché stiamo effettuando il loop over n ed entrambe le variabili sanno quali dimensioni
hanno così sanno automaticamente che sono in loop.

Questa funzione è molto utile in molti posti e rende il codice molto più breve. Di
Certo, ci sono momenti in cui vuoi aggirare questo; ecco una funzione che fa a
matrice simmetrica e serve come esempio di come codificare il ciclo esplicito:

pp_def('sim',
Pars => 'a(n,n); [o]c(n,n);',
Codice => 'loop(n) %{
intero n2;
for(n2=n; n2<$SIZE(n); n2++) {
$c(n0 => n, n1 => n2) =
$c(n0 => n2, n1 => n) =
$a(n0 => n, n1 => n2);
}
%}
'
);

Analizziamo cosa sta succedendo. Innanzitutto, cosa dovrebbe fare questa funzione? Dal suo
firma si vede che ci vuole una matrice 2D con uguale numero di colonne e righe e
restituisce una matrice della stessa dimensione. Da una data matrice di input $a calcola una simmetrica
matrice di output $c (simmetrica nel senso della matrice che A^T = A dove ^T significa matrice
trasporre, o in gergo PDL $c == $c->xchg(0,1)). Lo fa usando solo i valori
sopra e sotto la diagonale di $a. Nella matrice di output $c tutti i valori sopra e sotto il
le diagonali sono le stesse di quelle in $a mentre quelle sopra la diagonale sono un'immagine speculare di
quelli sotto la diagonale (sopra e sotto sono qui interpretati nel modo in cui stampa PDL
pdl 2D). Se questa spiegazione suona ancora un po' strana, vai avanti, crea un piccolo file
in cui scrivi questa definizione, costruisci la nuova estensione PDL (vedi la sezione su
Makefile per codice PP) e provalo con un paio di esempi.

Dopo aver spiegato cosa dovrebbe fare la funzione, valgono un paio di punti
notare dal punto di vista sintattico. Innanzitutto, otteniamo la dimensione della dimensione denominata
"n" di nuovo utilizzando la macro $SIZE. In secondo luogo, ci sono improvvisamente questi divertenti "n0" e "n1"
nomi di indice nel codice sebbene la firma definisca solo la dimensione "n". Perchè questo? Il
la ragione diventa chiara quando si nota che sia la prima che la seconda dimensione di $a e $b
sono denominati "n" nella firma di "symm". Questo dice a PDL::PP che il primo e il secondo
dimensione di questi argomenti dovrebbe avere la stessa dimensione. Altrimenti la funzione generata
genererà un errore di runtime. Tuttavia, ora in un accesso a $a e $c PDL::PP non può essere visualizzato
out a quale indice "n" si riferisce più solo dal nome dell'indice. quindi, il
gli indici con nomi di dimensioni uguali vengono numerati da sinistra a destra a partire da 0, ad esempio in
l'esempio sopra "n0" si riferisce alla prima dimensione di $a e $c, "n1" alla seconda e
presto.

In tutti gli esempi fino ad ora, abbiamo usato solo i membri "Pars" e "Code" dell'hash che
è stato passato a "pp_def". Ci sono sicuramente altre chiavi che vengono riconosciute da PDL::PP e
ne sentiremo parlare alcuni nel corso di questo documento. Trova un (non esaustivo)
elenco di chiavi nell'Appendice A. Un elenco di macro e funzioni PP (abbiamo incontrato solo
alcuni di quelli negli esempi sopra) che sono espansi nei valori dell'argomento hash
a "pp_def" è riassunto nell'Appendice B.

A questo punto, potrebbe essere opportuno ricordare che PDL::PP non è completamente statico,
serie di routine ben progettate (come dice Tuomas: "smettila di pensare a PP come un insieme di
routine scolpite nella pietra") ma piuttosto una raccolta di cose che il PDL::PP autore
(Tuomas J. Lukka) ha ritenuto che avrebbe dovuto scrivere spesso nelle sue routine di estensione PDL.
PP cerca di essere espandibile in modo che in futuro, al sorgere di nuove esigenze, possa nascere un nuovo codice comune
essere riassunto in esso. Se vuoi saperne di più sul perché potresti voler cambiare
PDL::PP e come farlo controlla la sezione sugli interni di PDL::PP.

Manovrabilità male valori
Se non si dispone di supporto per valori non validi compilato in PDL, è possibile ignorare questa sezione e il
relativi tasti: "BadCode", "HandleBad", ... (prova a stampare il valore di
$PDL::Bad::Status - se è uguale a 0, vai avanti).

Esistono diversi tasti e macro utilizzati durante la scrittura del codice per gestire i valori errati. Il primo
uno è il tasto "HandleBad":

HandleBad => 0
Questo contrassegna una routine pp come NON gestire valori errati. Se questa routine viene mandata in tilt
con il loro "badflag" impostato, viene stampato un messaggio di avviso su STDOUT e sui piddles
vengono elaborati come se il valore utilizzato per rappresentare i valori errati fosse un numero valido. Il
Il valore "badflag" non viene propagato ai piddle di output.

Un esempio di quando viene utilizzato è per le routine FFT, che generalmente non hanno un modo
di ignorare parte dei dati.

HandleBad => 1
Questo fa sì che PDL::PP scriva del codice aggiuntivo che garantisce l'utilizzo della sezione BadCode e
che la macro "$ISBAD()" (e i suoi fratelli) funzionino.

HandleBad non è dato
Se uno qualsiasi dei piddle di input ha il "badflag" impostato, i piddle di output lo faranno
hanno il loro "badflag" impostato, ma qualsiasi BadCode fornito viene ignorato.

Il valore di "HandleBad" viene utilizzato per definire il contenuto della chiave "BadDoc", se non lo è
dato.

Per gestire valori errati, il codice deve essere scritto in modo leggermente diverso; ad esempio,

$c() = $a() + $b();

diventa qualcosa come

if ( $a() != VALORE VALORE && $b() != VALORE VALORE ) {
$c() = $a() + $b();
} Else {
$c() = VALORE ERRATO;
}

Tuttavia, vogliamo la seconda versione solo se sono presenti valori errati nei piddle di input
(e quel supporto di valore scadente è voluto!) - altrimenti in realtà vogliamo il codice originale.
È qui che entra in gioco la chiave "BadCode"; lo usi per specificare il codice da eseguire in caso di errore
i valori possono essere presenti e PP usa sia questo che la sezione "Codice" per creare qualcosa
piace:

se ( valori_errati_sono_presenti ) {
fancy_threadloop_stuff {
Codice errato
}
} Else {
fancy_threadloop_stuff {
Code
}
}

Questo approccio significa che non c'è praticamente alcun sovraccarico quando non sono presenti valori errati
(cioè la routine badflag restituisce 0).

La sezione BadCode può utilizzare le stesse macro e gli stessi costrutti di loop della sezione Code.
Tuttavia, non sarebbe molto utile senza le seguenti macro aggiuntive:

$ISBAD(variabile)
Per verificare se il valore di un piddle è sbagliato, usa la macro $ISBAD:

if ( $ISBAD(a()) ) { printf("a() non è valido\n"); }

Puoi anche accedere a determinati elementi di un piddle:

if ( $ISBAD(a(n=>l)) ) { printf("l'elemento %d di a() non è valido\n", l); }

$ISBUONO(var)
Questo è l'opposto della macro $ISBAD.

$SETBAD(variabile)
Per quando vuoi impostare un elemento di un piddle cattivo.

$VAR_ISB(c_var;pdl)
Se hai memorizzato nella cache il valore di un piddle "$a()" in una variabile c ("pippo" diciamo), quindi per
controlla se è sbagliato, usa "$ISBADVAR(foo,a)".

$ISGOODVAR(c_var;pdl)
Come sopra, ma questa volta controllando che il valore memorizzato nella cache non sia male.

$SETBADVAR(c_var,pdl)
Per copiare il valore errato per un piddle nella variabile ac, utilizzare "$SETBADVAR(foo,a)".

FARE: menzionare le macro "$PPISBAD()" ecc.

Utilizzando queste macro, il codice precedente potrebbe essere specificato come:

Codice => '$c() = $a() + $b();',
BadCode => '
se ( $ISBAD(a()) || $ISBAD(b()) ) {
$IMPOSTA(c());
} Else {
$c() = $a() + $b();
}',

Poiché questo è Perl, TMTOWTDI, potresti anche scrivere:

BadCode => '
if ( $VALORE(a()) && $VALORE(b()) ) {
$c() = $a() + $b();
} Else {
$IMPOSTA(c());
}',

Se vuoi accedere al valore del badflag per un dato piddle, puoi usare il
Macro "$PDLSTATExxxx()":

$PDLSTATEISBAD(PDL)
$PDLSTATEISGOOD(PDL)
$PDLSTATESETBAD(pdl)
$PDLSTATESETGOOD(pdl)

FARE: menziona anche le opzioni "FindBadStatusCode" e "CopyBadStatusCode" in "pp_def",
come chiave "BadDoc".

Interfaccia il tuo proprio/biblioteca funzioni utilizzando PP
Ora, considera quanto segue: hai la tua funzione C (che in effetti potrebbe essere parte di
qualche libreria che vuoi interfacciare a PDL) che prende come argomenti due puntatori a
vettori di doppio:

void myfunc(int n,doppio *v1,doppio *v2);

Il modo corretto di definire la funzione PDL è

pp_def('myfunc',
Pars => 'a(n); [o]b(n);',
GenericTypes => ['D'],
Codice => 'myfunc($SIZE(n),$P(a),$P(b));'
);

Il "$P("daLa sintassi ")" restituisce un puntatore al primo elemento e gli altri elementi sono
garantito di mentire dopo.

Notare che qui è possibile commettere molti errori. Innanzitutto, è necessario utilizzare $SIZE(n)
invece di "n". In secondo luogo, non dovresti inserire alcun ciclo in questo codice. Terzo, qui incontriamo
una nuova chiave hash riconosciuta da PDL::PP : la dichiarazione "GenericTypes" dice a PDL::PP di
GENERARE IL TYPELOOP FOP SOLO L'ELENCO DEI TIPI SPECIFICATI. In questo caso "doppio". Questo
ha due vantaggi. In primo luogo la dimensione del codice compilato viene notevolmente ridotta, in secondo luogo se
gli argomenti non doppi vengono passati a "myfunc()" PDL li convertirà automaticamente in
double prima di passare alla routine C esterna e convertirli nuovamente in seguito.

Si può anche usare "Pars" per qualificare i tipi di singoli argomenti. Così si potrebbe anche
scrivi questo come:

pp_def('myfunc',
Pars => 'doppio a(n); doppio [o]b(n);',
Codice => 'myfunc($SIZE(n),$P(a),$P(b));'
);

La specifica del tipo in "Pars" esenta l'argomento dalla variazione nel typeloop -
piuttosto viene convertito anche automaticamente e dal tipo specificato. Questo è ovviamente
utile in un esempio più generale, ad esempio:

void miafunzione(int n,float *v1,long *v2);

pp_def('myfunc',
Pars => 'flottante a(n); lungo [o]b(n);',
GenericTypes => ['F'],
Codice => 'myfunc($SIZE(n),$P(a),$P(b));'
);

Nota che usiamo ancora "GenericTypes" per ridurre la dimensione del ciclo del tipo, ovviamente PP potrebbe
in linea di principio individualo e fallo automaticamente anche se il codice deve ancora raggiungerlo
livello di raffinatezza!

Infine nota quando i tipi vengono convertiti automaticamente DEVE usare il qualificatore "[o]" per
le variabili di output o le modifiche difficili verranno ottimizzate tramite PP!

Se si interfaccia una libreria di grandi dimensioni è possibile automatizzare ulteriormente l'interfacciamento. Perl può
aiutarti ancora (!) a farlo. In molte biblioteche esistono determinate convenzioni di chiamata.
Questo può essere sfruttato. In breve, puoi scrivere un piccolo parser (che in realtà non lo è
difficile in Perl) che quindi genera le chiamate a "pp_def" dalle descrizioni analizzate di
le funzioni in quella libreria. Ad esempio, controlla il ardesia interfaccia nel
Albero "Lib" della distribuzione PDL. Se vuoi controllare (durante il debug) quali chiamate a
Funzioni PP il tuo codice Perl ha generato un piccolo pacchetto di supporto che è utile
sostituisce le funzioni PP con altre con nome identico che scaricano i loro argomenti su stdout.

dì solo

perl -MPDL::PP::Dump miofile.pd

per vedere le chiamate a "pp_def" e amici. Provalo con ops.pd e slatec.pd. Se tu sei
interessato (o vuole migliorarlo), la fonte è in Basic/Gen/PP/Dump.pm

Altri macro e funzioni in , il Code pagina
Macro: finora abbiamo incontrato le macro $SIZE, $GENERIC e $P. Ora stiamo per
spiegare rapidamente le altre macro che sono espanse nella sezione "Codice" di PDL::PP insieme
con esempi del loro utilizzo.

$T La macro $T viene utilizzata per gli interruttori di tipo. Questo è molto utile quando devi usare
diverse funzioni esterne (es. libreria) a seconda del tipo di input degli argomenti.
La sintassi generale è

$Ttypeletters(tipo_alternative)

dove "typeletters" è una permutazione di un sottoinsieme delle lettere "BSULFD" che stanno
per Byte, Short, Ushort, ecc. e "type_alternatives" sono le espansioni quando il tipo
dell'operazione PP è pari a quella indicata dalla rispettiva lettera. andiamo
illustrare questa descrizione incomprensibile con un esempio. Supponendo che tu abbia due C
funzioni con prototipi

void float_func(float *in, float *out);
void double_func(doppio *in, double *out);

che fanno praticamente la stessa cosa ma uno accetta float e l'altro puntatore doppio.
Potresti interfacciarli a PDL definendo una funzione generica "foofunc" (che sarà
chiamare la funzione corretta a seconda del tipo di trasformazione):

pp_def('foofunc',
Pars => 'a(n); [o] b();',
Codice => ' $TFD(float_func,double_func) ($P(a),$P(b));'
GenericTypes => [qw(FD)],
);

Si prega di notare che non si può dire

Codice => ' $TFD(float,double)_func ($P(a),$P(b));'

poiché la macro $T si espande con spazi finali, analogamente alle macro del preprocessore C.
La forma leggermente più lunga illustrata sopra è corretta. Se vuoi davvero la brevità, tu
può ovviamente fare

'$TBSULFD('.(join ',',map {"nome_identificatore_lungo_$_"}
qw/byt short unseigned lounge flotte dubble/).');'

$PP
La macro $PP è usata per un cosiddetto Fisico pointer accesso. Fisico si riferisce
alcune ottimizzazioni interne al PDL (per chi conosce il nucleo del PDL noi siamo
parlando delle ottimizzazioni vaffine). Questa macro è principalmente per uso interno e tu
non dovrebbe essere necessario utilizzarlo in nessuno dei tuoi codici normali.

$COMP (e la sezione "OtherPars")
La macro $COMP viene utilizzata per accedere a valori non pdl nella sezione del codice. Il suo nome è
derivate dall'attuazione delle trasformazioni nel PDL. Le variabili a cui puoi fare riferimento
all'utilizzo di $COMP sono membri della struttura ``compilata'' che rappresenta il PDL
trasformazione in questione ma non contiene ancora alcuna informazione sulle dimensioni
(per ulteriori dettagli controllare PDL::Internals). Tuttavia, puoi trattare $COMP come un
scatola nera senza sapere nulla sull'attuazione delle trasformazioni nel Pdl.
Quindi quando useresti questa macro? Il suo utilizzo principale è accedere ai valori degli argomenti che
sono dichiarati nella sezione "OtherPars" di una definizione "pp_def". Ma poi non l'hai fatto
hai già sentito parlare del tasto "OtherPars"?! Facciamo un altro esempio che illustra
utilizzo tipico di entrambe le nuove funzionalità:

pp_def('pnmout',
Par => 'a(m)',
OtherPars => "char* fd",
GenericTypes => [qw(BUSL)],
Codice => 'PerlIO *fp;
IO * io;

io = GvIO(gv_fetchpv($COMP(fd),FALSE,SVt_PVIO));
se (!io || !(fp = IoIFP(io)))
croak("Non riesco a capire FP");

if (PerlIO_write(fp,$P(a),len) !=len)
croak("Errore durante la scrittura del file pnm");
');

Questa funzione viene utilizzata per scrivere dati da un pdl a un file. Il descrittore di file è passato
come una stringa in questa funzione. Questo parametro non va nella sezione "Pars"
poiché non può essere trattato utilmente come un pdl ma piuttosto nel nome appropriato
sezione "Altri parametri". I parametri nella sezione "OtherPars" seguono quelli nella "Pars"
sezione quando si invoca la funzione, ad es

open FILE,">out.dat" o die "impossibile aprire out.dat";
pnmout($pdl,'FILE');

Quando vuoi accedere a questo parametro all'interno della sezione del codice devi dire a PP di
usando la macro $COMP, cioè scrivi "$COMP(fd)" come nell'esempio. Altrimenti PP
non saprei che "fd" a cui ti riferisci è lo stesso di quello specificato nel
sezione "Altri parametri".

Un altro uso della sezione "OtherPars" consiste nell'impostare una dimensione denominata nella firma.
Facciamo un esempio di come si fa:

pp_def('setdim',
Pars => '[o] a(n)',
OtherPars => 'int ns => n',
Codice => 'loop(n) %{ $a() = n; %}',
);

Questo dice che la dimensione denominata "n" sarà inizializzata dal valore di Altro
parametro "ns" che è di tipo intero (immagino che tu abbia capito che usiamo il
sintassi "CType From => named_dim"). Ora puoi chiamare questa funzione nel solito modo:

setdim(($a=nullo),5);
stampa $a;
[ 0 1 ​​2 ​​3 ​​4 ​​]

Certo questa funzione non è molto utile ma dimostra come funziona. Se tu
chiama la funzione con un pdl esistente e non è necessario specificare esplicitamente il
dimensione di "n" poiché PDL::PP può calcolarlo dalle dimensioni del pdl non nullo. In
in tal caso, dai il parametro della dimensione come "-1":

$a = storico($b);
setdim($a,-1);

Che dovrebbe farlo.

L'unica funzione PP che abbiamo usato finora negli esempi è "loop". Inoltre,
ci sono attualmente altre due funzioni che vengono riconosciute nella sezione "Codice":

cappio
Come abbiamo sentito sopra, la firma di una funzione definita da PP definisce le dimensioni di tutti
gli argomenti pdl coinvolti in a primitivo operazione. Tuttavia, spesso chiami il
funzioni che hai definito con PP con pdls che hanno più dimensioni di quelle
specificato nella firma. In questo caso l'operazione primitiva viene eseguita su all
sottosezioni di dimensionalità appropriata in ciò che viene chiamato a filo loop (Vedi anche
panoramica sopra e PDL::Indicizzazione). Supponendo che tu abbia qualche nozione di questo concetto tu
probabilmente apprezzerà che l'operazione specificata nella sezione del codice dovrebbe essere
ottimizzato poiché questo è l'anello più stretto all'interno di un anello di filo. Tuttavia, se rivedi
l'esempio in cui definiamo la funzione "pnmout", ti renderai presto conto che guardando
il descrittore di file "IO" nel ciclo del thread interno non è molto efficiente durante la scrittura
un pdl con molte righe. Un approccio migliore sarebbe cercare il descrittore "IO" una volta
all'esterno dell'anello di filo e utilizzare il suo valore quindi all'interno dell'anello di filo più stretto. Questo è
esattamente dove la funzione "threadloop" è utile. Ecco una definizione migliorata
di "pnmout" che utilizza questa funzione:

pp_def('pnmout',
Par => 'a(m)',
OtherPars => "char* fd",
GenericTypes => [qw(BUSL)],
Codice => 'PerlIO *fp;
IO * io;
lente d'ingrandimento;

io = GvIO(gv_fetchpv($COMP(fd),FALSE,SVt_PVIO));
se (!io || !(fp = IoIFP(io)))
croak("Non riesco a capire FP");

lunghezza = $TAGLIA(m) * sizeof($GENERIC());

ciclo di thread %{
if (PerlIO_write(fp,$P(a),len) !=len)
croak("Errore durante la scrittura del file pnm");
%}
');

Funziona come segue. Normalmente viene posizionato il codice C che scrivi all'interno della sezione "Codice"
all'interno di un ciclo di thread (cioè PP genera il codice XS avvolgente appropriato attorno ad esso).
Tuttavia, quando si utilizza esplicitamente la funzione "threadloop", PDL::PP lo riconosce e
non avvolge il codice con un ciclo di thread aggiuntivo. Questo ha l'effetto che ti codifica
la scrittura al di fuori del ciclo del thread viene eseguita solo una volta per trasformazione e solo il codice
con nella coppia "%{ ... %}" circostante è posizionato all'interno del loop di thread più stretto. Questo
è utile anche quando si desidera eseguire una decisione (o qualsiasi altro codice, in particolare
codice ad alta intensità di CPU) solo una volta per thread, ad es

pp_addhdr('
#definisci RAW 0
#definisci ASCII 1
');
pp_def('do_raworascii',
Pars => 'a(); B(); [o]c()',
OtherPars => 'modalità int',
Codice => ' interruttore ($COMP(modalità)) {
caso RAW:
ciclo di thread %{
/* fai cose grezze */
%}
break;
caso ASCII:
ciclo di thread %{
/* fa cose ASCII */
%}
break;
di default:
croak("modalità sconosciuta");
}'
);

Tipi di
La funzione dei tipi funziona in modo simile alla macro $T. Tuttavia, con la funzione "tipi" il
il codice nel blocco seguente (delimitato da "%{" e "%}" come al solito) viene eseguito per tutti
quei casi in cui il tipo di dato dell'operazione è in qualsiasi of i tipi rappresentati da
le lettere nell'argomento per "digitare", ad es

Codice => '...

tipi(BSUL) %{
/* esegue un'operazione di tipo intero */
%}
tipi(FD) %{
/* esegue un'operazione in virgola mobile */
%}
...'

La RifaiDimsCodice Sezione
La chiave "RedoDimsCode" è una chiave facoltativa utilizzata per calcolare le dimensioni dei piddle a
runtime nel caso in cui le regole standard per il calcolo delle dimensioni dalla firma non lo siano
sufficiente. Il contenuto della voce "RedoDimsCode" viene interpretato nello stesso modo in cui
la sezione Codice è interpretata-- vale a dire, le macro PP vengono espanse e il risultato è
interpretato come codice C. Lo scopo del codice è impostare la dimensione di alcune dimensioni che
figurare nella firma. L'allocazione dello spazio di archiviazione, i threadloop e così via verranno impostati come
se la dimensione calcolata fosse apparsa nella firma. Nel tuo codice, per prima cosa calcoli
la dimensione desiderata di una dimensione nominata nella firma in base alle tue esigenze e poi
assegnagli quel valore tramite $DIMENSIONE() macro.

Ad esempio, si consideri la seguente situazione. Stai interfacciando una libreria esterna
routine che richiede un array temporaneo per l'area di lavoro da passare come argomento. Due
gli array di dati di input che vengono passati sono p(m) e x(n). L'array dei dati di output è y(n). Il
la routine richiede un array dell'area di lavoro con una lunghezza di n+m*m e desideri l'archiviazione
creato automaticamente proprio come sarebbe per qualsiasi piddle contrassegnato con [t] o [o]. Che cosa
ti piacerebbe è dire qualcosa come

pp_def("myexternalfunc",
Par => " p(m); x(n); [o] y; [t] lavoro(n+m*m); ", ...

ma non funzionerà, perché PP non può interpretare le espressioni con l'aritmetica nel
firma. Invece scrivi

pp_def("myexternalfunc",
Par => " p(m); x(n); [o] y; [t] lavoro(wn); ",
RedoDimsCode => "
int im = $PDL(p)->dims[0];
int in = $PDL(x)->dims[0];
int min = in + im * im;
int inw = $PDL(lavoro)->dims[0];
$SIZE(wn) = inw >= min ? inw : min; ",
Codice => "
funzione esterna($P(p),$P(x),$TAGLIA(m),$SIZE(n),$P(lavoro));
";)

Questo codice funziona come segue: La macro $PDL(p) si espande in un puntatore alla struttura pdl per
il pisello p. Non vuoi un puntatore ai dati (cioè $P) in questo caso, perché tu
vuoi accedere ai metodi per il piddle a livello C. Ottieni la prima dimensione di
ciascuno dei piddles e memorizzarli in numeri interi. Quindi calcoli la lunghezza minima the
array di lavoro può essere. Se l'utente ha inviato un "lavoro" piddle con spazio di archiviazione sufficiente, lascialo
da solo. Se l'utente ha inviato, diciamo un pdl nullo o nessun pdl, la dimensione di wn sarà
zero e lo riporti al valore minimo. Prima che il codice nella sezione Codice sia
PP eseguito creerà lo spazio di archiviazione appropriato per "lavoro" se non esiste. Nota che tu
ha preso solo la prima dimensione di "p" e "x" perché l'utente potrebbe aver inviato piddle con
dimensioni di filettatura extra. Naturalmente, il piddle temporaneo "funziona" (notare il flag [t])
comunque non dovrebbe essere data alcuna dimensione del filo.

Puoi anche usare "RedoDimsCode" per impostare la dimensione di un piddle contrassegnato con [o]. In questo
caso imposti le dimensioni per la dimensione denominata nella firma usando $DIMENSIONE() come in
l'esempio precedente. Tuttavia, poiché il piddle è contrassegnato con [o] invece di [t],
le dimensioni della filettatura verranno aggiunte se necessario proprio come se la dimensione della dimensione fosse
calcolata dalla firma secondo le regole consuete. Ecco un esempio da
PDL::Matematica

pp_def("poliradici",
Pars => 'cr(n); ci(n); [o]rr(M); [o]ri(m);',
RedoDimsCode => 'int sn = $PDL(cr)->dims[0]; $TAGLIA(m) = sn-1;',

I piddle di input sono le parti reale e immaginaria di coefficienti complessi di a
polinomio. I piddles in uscita sono parti reali e immaginarie delle radici. Non ci sono"
radici a un polinomio di ordine "n"esimo e tale polinomio ha coefficienti "n+1" (il
zeoreth attraverso la "n"-esima). In questo esempio, il threading funzionerà correttamente. Questo è il
prima dimensione del piddle di uscita con la sua dimensione regolata, ma altra filettatura
le dimensioni verranno assegnate come se non ci fosse "RedoDimsCode".

mappa tipo manipolazione in , il "Altri Par" pagina
La sezione "OtherPars" discussa sopra è molto spesso assolutamente cruciale quando si
interfacciare librerie esterne con PDL. Tuttavia in molti casi anche le librerie esterne
utilizzare tipi derivati ​​o puntatori di vario tipo.

Il modo standard per gestirlo in Perl è usare un file "typemap". Questo è discusso in
alcuni dettagli in perlxs nella documentazione standard di Perl. In PP la funzionalità è molto
simile, quindi puoi creare un file "typemap" nella directory in cui risiede il tuo file PP
e quando è costruito viene automaticamente letto per capire la traduzione appropriata
tra il tipo C e il tipo integrato di Perl.

Detto questo, ci sono un paio di importanti differenze rispetto alla gestione generale dei tipi
in XS. Il primo, e probabilmente il più importante, è che al momento i puntatori ai tipi sono
non consentito nella sezione "OtherPars". Per aggirare questa limitazione è necessario utilizzare il
Tipo "IV" (grazie a Judd Taylor per aver sottolineato che questo è necessario per la portabilità).

Probabilmente è meglio illustrarlo con un paio di frammenti di codice:

Ad esempio la funzione "gsl_spline_init" ha la seguente dichiarazione C:

int gsl_spline_init(gsl_spline * spline,
const double xa[], const double ya[], size_t size);

Chiaramente gli array "xa" e "ya" sono candidati per essere passati come piddles e il
l'argomento "dimensione" è solo la lunghezza di questi piddles in modo che possa essere gestito dal
Macro "$SIZE()" in PP. Il problema è il puntatore al tipo "gsl_spline". Il naturale
la soluzione sarebbe scrivere una dichiarazione "OtherPars" del modulo

OtherPars => 'gsl_spline *spl'

e scrivi un breve file "typemap" che gestisca questo tipo. Questo non funziona al momento
tuttavia! Quindi quello che devi fare è aggirare leggermente il problema (e in qualche modo
anche questo è più facile!):

La soluzione è dichiarare "spline" nella sezione "OtherPars" utilizzando un "valore intero",
"IV". Questo nasconde la natura della variabile da PP e quindi è necessario (bene per evitare
almeno gli avvisi del compilatore!) eseguono un cast di tipo quando si utilizza la variabile nel codice.
Quindi "OtherPars" dovrebbe assumere la forma:

OtherPars => 'IV spl'

e quando lo userai nel codice scriverai

INT2PTR(gsl_spline *, $COMP(spl))

dove la macro dell'API Perl "INT2PTR" è stata utilizzata per gestire il cast del puntatore da evitare
avvisi e problemi del compilatore per macchine con Perl misto a 32 bit e 64 bit
configurazioni. Mettendo insieme questo come ha fatto Andres Jordan (con la modifica
usando "IV" di Judd Taylor) in "gsl_interp.pd" nel sorgente della distribuzione ottieni:

pp_def('init_meat',
Pars => 'doppio x(n); doppia y(n);',
OtherPars => 'IV spl',
Codice =>'
gsl_spline_init,( INT2PTR(gsl_spline *, $COMP(spl)), $P(x),$P(y),$SIZE(n)));'
);

dove ho rimosso una chiamata wrapper macro, ma ciò oscurerebbe la discussione.

L'altra piccola differenza rispetto alla gestione standard delle mappe dei tipi in Perl è che
l'utente non può specificare posizioni di typemap o nomi di file typemap non standard usando il
Opzione "TYPEMAPS" in MakeMaker... Quindi puoi usare solo un file chiamato "typemap" e/o il
Trucco "IV" sopra.

Altri utile PP Tasti in dati operazione definizioni
Hai già sentito parlare del tasto "OtherPars". Attualmente non ci sono molte altre chiavi
per un'operazione sui dati che sarà utile nella normale (qualunque cosa sia) programmazione PP. In
in effetti, sarebbe interessante sentire parlare di un caso in cui pensi di aver bisogno di più di quello che
è fornito al momento. Per favore, parla su una delle mailing list del PDL. La maggior parte degli altri
le chiavi riconosciute da "pp_def" sono davvero utili solo per ciò che chiamiamo fetta operazioni (Vedi
anche sopra).

Una cosa che è fortemente pianificata è il numero variabile di argomenti, che sarà a
poco complicato.

Un elenco incompleto delle chiavi disponibili:

A posto
L'impostazione di questo tasto contrassegna la routine come funzionante sul posto, ovvero l'input e l'output
i piselli sono gli stessi. Un esempio è "$a->inplace->sqrt()" (o "sqrt(inplace($a))").

In posizione => 1
Utilizzare quando la routine è una funzione unaria, come "sqrt".

In luogo => ['a']
Se ci sono più piddle in input, specificare il nome di quello che può essere
modificato sul posto utilizzando un riferimento di matrice.

Inplace => ['a','b']
Se sono presenti più piddle di output, specificare il nome del piddle di input e
piddle di output in un riferimento di matrice a 2 elementi. Questo probabilmente non è necessario, ma lasciato
dentro per completezza.

Se si utilizzano valori errati, occorre prestare attenzione per garantire la propagazione del
badflag quando viene utilizzato inplace; considera questo estratto da Base/Cattivo/cattivo.pd:

pp_def('replacebad',HandleBad => 1,
Pars => 'a(); [o]b();',
OtherPars => 'doppio valore nuovo',
Al posto => 1,
CopyBadStatusCode =>
'/* propaga badflag se inplace AND è cambiato */
if ( a == b && $ISPDLSTATEBAD(a) )
PDL->propogate_badflag( b, 0 );

/* assicurati sempre che l'output sia "buono" */
$SETPDLSTATEGOOD(b);
',
...

Poiché questa routine rimuove tutti i valori errati, il piddle di output aveva il suo flag errato
cancellato. Se eseguito sul posto (quindi "a == b"), allora dobbiamo dire a tutti i figli di "a"
che la cattiva bandiera è stata cancellata (per risparmiare tempo ci assicuriamo di chiamare
"PDL->propogate_badgflag" solo se il piddle di input aveva il flag errato impostato).

NOTA: un'idea è che la documentazione per la routine potrebbe essere automaticamente
contrassegnato per indicare che può essere eseguito sul posto, ovvero qualcosa di simile a come
"HandleBad" imposta "BadDoc" se non viene fornito (non è una soluzione ideale).

Altri PDL::PP funzioni a supporto conciso pacchetto definizione
Finora abbiamo descritto le funzioni "pp_def" e "pp_done". PDL::PP ne esporta un po'
altre funzioni per aiutarti a scrivere definizioni concise del pacchetto di estensione PDL.

pp_addhdr

Spesso quando si interfacciano le funzioni di libreria come nell'esempio sopra è necessario includere
file di inclusione C aggiuntivi. Poiché il file XS è generato da PP, abbiamo bisogno di qualche mezzo per
fai in modo che PP inserisca le direttive di inclusione appropriate nel posto giusto nell'XS generato
file. A tal fine esiste la funzione "pp_addhdr". Questa è anche la funzione da usare
quando vuoi definire alcune funzioni C per uso interno da parte di alcune delle funzioni XS
(che sono per lo più funzioni definite da "pp_def"). Includendo queste funzioni qui tu
assicurati che PDL::PP inserisca il tuo codice prima del punto in cui il modulo XS effettivo
sezione inizia e quindi non sarà toccata da xsubpp (cfr. perlx e perlxstut
pagine man).

Una chiamata tipica sarebbe

pp_addhdr('
#includere /* abbiamo bisogno delle definizioni di XXXX */
#include "libprotos.h" /* prototipi di funzioni di libreria */
#include "mylocaldecs.h" /* Dec locali */

static void do_the real_work(PDL_Byte * in, PDL_Byte * out, int n)
{
/* fai dei calcoli con i dati */
}
');

Ciò garantisce che tutte le costanti e i prototipi necessari saranno inclusi correttamente e
che puoi usare le funzioni interne definite qui nei "pp_def", ad esempio:

pp_def('barfoo',
Pars => 'a(n); [o] b(n)',
GenericTypes => ['B'],
Codice => ' int ns = $SIZE(n);
fare_il_lavoro_reale($P(a),$P(b),ns);
',
);

pp_addpm

In molti casi il codice PP effettivo (cioè gli argomenti delle chiamate "pp_def") è solo una parte di
il pacchetto che stai attualmente implementando. Spesso c'è codice Perl aggiuntivo e XS
codice che normalmente avresti scritto nei file pm e XS che ora sono automaticamente
generato da PP. Quindi, come ottenere questa roba in quei file generati dinamicamente?
Fortunatamente, ci sono un paio di funzioni, generalmente chiamate "pp_addXXX" che ti aiutano
nel fare questo.

Supponiamo che tu abbia codice Perl aggiuntivo che dovrebbe andare nel generato pm-file. Questo
si ottiene facilmente con il comando "pp_addpm":

pp_addpm(<<'EOD');

=head1 NOME

PDL::Lib::Mylib -- un'interfaccia PDL per la libreria Mylib

=head1 DESCRIZIONE

Questo pacchetto implementa un'interfaccia per il pacchetto Mylib con full
supporto per filettatura e indicizzazione (vedi L ).

=tagliare

usa PGPLOT;

=head2 usa_miafunzione
questa funzione applica l'operazione myfunc a tutti i
elementi dell'input pdl indipendentemente dalle dimensioni
e restituisce la somma del risultato
=tagliare

sub uso_miafunzione {
mio $pdl = shift;

myfunc($pdl->clump(-1),($res=null));

return $res->sum;
}

EOD

pp_aggiungi_esportato

Probabilmente hai avuto l'idea. In alcuni casi vuoi anche esportare il tuo aggiuntivo
funzioni. Per evitare di finire nei guai con la PP che pasticcia anche con l'@EXPORT
array devi semplicemente dire a PP di aggiungere le tue funzioni all'elenco delle funzioni esportate:

pp_add_exported('use_myfunc gethynx');

pp_aggiungi_isa

Il comando "pp_add_isa" funziona come la funzione "pp_add_exported". Gli argomenti a
"pp_add_isa" vengono aggiunti all'elenco @ISA, ad es

pp_add_isa(' Some::Other::Class ');

pp_benedica

Se le tue routine pp_def devono essere usate come metodi oggetto usa "pp_bless" per specificare il
pacchetto (cioè classe) a cui il tuo pp_defverranno aggiunti metodi ed. Per esempio,
"pp_bless('PDL::MyClass')". L'impostazione predefinita è "PDL" se viene omesso.

pp_addxs

A volte vuoi aggiungere del tuo codice XS extra (che generalmente non è coinvolto con
qualsiasi problema di threading/indicizzazione ma fornisce alcune altre funzionalità a cui si desidera accedere
dal lato Perl) al file XS generato, per esempio

pp_addxs('','

# Determina l'endianità della macchina

int
isbigendiano()
CODICE:
i breve senza segno;
Byte_PDL *b;

io = 42; b = (PDL_Byte*) (void*) &i;

se (*b == 42)
RITORNO = 0;
altrimenti se (*(b+1) == 42)
RITORNO = 1;
altro
croak("Impossibile - la macchina non è né grande né piccola endian!!\n");
USCITA:
RITIRO
');

Specialmente "pp_add_exported" e "pp_addxs" dovrebbero essere usati con cura. PP utilizza
PDL::Exporter, quindi lasciare che PP esporti la tua funzione significa che vengono aggiunti a
elenco standard di funzioni esportate di default (l'elenco definito dal tag export
``:Funzione''). Se usi "pp_addxs" non dovresti provare a fare nulla che implichi il threading
o indicizzando direttamente. PP è molto più bravo a generare il codice appropriato dal tuo
definizioni.

pp_aggiungi_avvio

Infine, potresti voler aggiungere del codice alla sezione BOOT del file XS (se non lo fai
sai cos'è controlla perlx). Questo è facilmente realizzabile con il comando "pp_add_boot":

pp_add_boot(<
descrip = mylib_initialize(MANTENERE_APERTO);

if (descrizione == NULL)
croak("Impossibile inizializzare la libreria");

GlobalStruc->descrip = descrip;
GlobalStruc->maxfiles = 200;
EOB

pp_export_niente

Per impostazione predefinita, PP.pm mette tutti i sub definiti utilizzando la funzione pp_def nell'output .pm
l'elenco EXPORT del file. Questo può creare problemi se stai creando un oggetto sottoclasse dove
non vuoi esportare alcun metodo. (cioè i metodi verranno chiamati solo usando il
sintassi $oggetto->metodo).

Per questi casi puoi chiamare pp_export_niente() per cancellare l'elenco di esportazione. Esempio (At
la fine del file .pd):

pp_export_niente();
pp_fatto();

pp_core_importList

Per impostazione predefinita, PP.pm mette 'use Core;' riga nel file di output .pm. Questo importa Core's
nomi esportati nello spazio dei nomi corrente, il che può creare problemi se si è troppo
cavalcando uno dei metodi di Core nel file corrente. Alla fine ricevi messaggi come
"Attenzione: sub sumover ridefinito nel file subclass.pm" durante l'esecuzione del programma.

Per questi casi è possibile utilizzare pp_core_importList per modificare ciò da cui viene importato
Core.pm. Per esempio:

pp_core_importList('()')

Ciò comporterebbe

usa Core();

generato nel file di output .pm. Ciò comporterebbe l'importazione di nessun nome da
Core.pm. Allo stesso modo, chiamando

pp_core_importList(' qw/ barf /')

risulterebbe in

usa Core qw/ barf/;

generato nel file di output .pm. Ciò comporterebbe solo l'importazione di "barf"
da Core.pm.

pp_setversione

Sono abbastanza sicuro che questo ti permetta di impostare contemporaneamente i file .pm e .xs'
versioni, evitando così inutili differenze di versione tra i due. Per usarlo, basta avere
la seguente riga ad un certo punto nel tuo file .pd:

pp_setversion('0.0.3');

Tuttavia, non utilizzarlo se utilizzi Module::Build::PDL. Vedi la documentazione di quel modulo per
dettagli.

pp_deprecate_module

Se un determinato modulo è ritenuto obsoleto, questa funzione può essere utilizzata per contrassegnarlo come
deprecato. Questo ha l'effetto di emettere un avviso quando un utente cerca di "usare" il
modulo. Il POD generato per questo modulo contiene anche un avviso di deprecazione. Il
il modulo sostitutivo può essere passato come argomento in questo modo:

pp_deprecate_module( infavor => "PDL::NewNonDeprecatedModule" );

Nota che la funzione influisce esclusivamente l'avviso di runtime e il POD.

Fare il tuo PP function "privato"


Supponiamo che tu abbia una funzione nel tuo modulo chiamata PDL::foo che usa il PP
funzione "bar_pp" per fare il lavoro pesante. Ma non vuoi pubblicizzare quel "bar_pp"
esiste. Per fare ciò, devi spostare la tua funzione PP all'inizio del file del modulo, quindi
chiamata

pp_export_niente()

per cancellare l'elenco "ESPORTA". Per garantire che nessuna documentazione (anche i documenti PP predefiniti) sia
generato, impostato

Doc => undef

e per evitare che la funzione venga aggiunta alla tabella dei simboli, impostare

PMFunc => ''

nella tua dichiarazione pp_def (vedi Image2D.pd per un esempio). Questo renderà effettivamente
la tua funzione PP "privata". Tuttavia, è sempre accessibile tramite PDL::bar_pp grazie a Perl's
progettazione del modulo. Ma renderlo privato farà sì che l'utente si allontani molto dal suo
modo di usarlo, così lui o lei si accolla le conseguenze!

Taglia operazione


La sezione sul funzionamento delle sezioni di questo manuale viene fornita utilizzando il flusso di dati e la valutazione pigra:
quando ne hai bisogno, chiedi a Tjl di scriverlo. una consegna in una settimana da quando ricevo l'e-mail
è probabile al 95% e il parto in due settimane è probabile al 99%.

E comunque, le operazioni slice richiedono una conoscenza molto più intima degli interni del PDL
rispetto alle operazioni sui dati. Inoltre, la complessità delle questioni coinvolte è
notevolmente superiore a quella dell'operazione dati media. Se vuoi convincere
te stesso di questo fatto dai un'occhiata al Base/Slices/slices.pd file nel PDL
distribuzione :-). Tuttavia, le funzioni generate utilizzando le operazioni slice sono al
cuore della manipolazione dell'indice e delle capacità di flusso di dati di PDL.

Inoltre, ci sono molti problemi sporchi con piddle e vaffine virtuali che faremo
salta del tutto qui.

Slices e male valori
Le operazioni di slice devono essere in grado di gestire valori non validi (se il supporto è compilato in PDL).
La cosa più semplice da fare è guardare Base/Slices/slices.pd per vedere come funziona.

Insieme a "BadCode", ci sono anche i tasti "BadBackCode" e "BadRedoDimsCode" per
"pp_def". Tuttavia, qualsiasi "EquivCPOffsCode" dovrebbe non devono essere modificati, poiché eventuali modifiche lo sono
assorbito nella definizione della macro "$EQUIVCPOFFS()" (cioè viene gestito
automaticamente da PDL::PP>.

A pochi note on scrittura a affettare routine...
I seguenti paragrafi descrivono la scrittura di una nuova routine di slicing ("intervallo"); qualunque
gli errori sono CED. (--CED 26-agosto-2002)

Manovrabilità of "avvisare" e "barf" in PP Code


Per stampare messaggi di avviso o abortire/morire, puoi chiamare "warn" o "barf" da PP
codice. Tuttavia, dovresti essere consapevole del fatto che queste chiamate sono state ridefinite utilizzando C
macro del preprocessore su "PDL->barf" e "PDL->warn". Queste ridefinizioni sono in atto
impedirti di chiamare inavvertitamente "warn" o "barf" direttamente di perl, il che può causare
segfault durante il pthreading (cioè il multi-threading del processore).

Le versioni di "barf" e "warn" di PDL si accoderanno con messaggi di avviso o barf fino a dopo
pthreading è completato e quindi chiama le versioni perl di queste routine.

Vedere PDL::ParallelCPU per maggiori informazioni sul pthreading.

UTILI ROUTINE


La struttura "Core" del PDL, definita in Base/Core/pdlcore.h.PL, contiene i puntatori a
numero di routine che potrebbero esserti utili. La maggior parte di queste routine si occupa
manipolando i piddle, ma alcuni sono più generali:

PDL->qsort_B( PDL_Byte *xx, int a, int b )
Ordina l'array "xx" tra gli indici "a" e "b". Esistono anche versioni per il
altri tipi di dati PDL, con suffisso "_S", "_U", "_L", "_F" e "_D". Qualsiasi modulo che utilizza
questo deve garantire che "PDL::Ufunc" sia caricato.

PDL->qsort_ind_B( PDL_Byte *xx, int *ix, int a, int b )
Come per "PDL->qsort_B", ma questa volta ordinando gli indici anziché i dati.

La routine "med2d" in Lib/Image2D/image2d.pd mostra come vengono utilizzate tali routine.

MAKEFILE PER PP FILE


Se hai intenzione di generare un pacchetto dal tuo file PP (le estensioni di file tipiche sono
".pd" o ".pp" per i file contenenti codice PP) è più facile e sicuro partire
generazione dei comandi appropriati al Makefile. Di seguito illustreremo
il formato tipico di un Makefile Perl da cui compilare e installare automaticamente il pacchetto
una descrizione in un file PP. La maggior parte delle regole per costruire xs, pm e altri file richiesti
dal file PP sono già predefiniti nel pacchetto PDL::Core::Dev. Dobbiamo solo
dì a MakeMaker di usarlo.

Nella maggior parte dei casi puoi definire il tuo Makefile come

# Makefile.PL per un pacchetto definito dal codice PP.

usa PDL::Core::Dev; # Raccogli le utilità di sviluppo
usa ExtUtils::MakeMaker;

$pacchetto = ["mylib.pd",Mylib,PDL::Lib::Mylib];
%hash = pdlpp_stdargs($pacchetto);
$hash{OBJECT} .= 'codice_C aggiuntivo$(OBJ_EXT) ';
$hash{pulito}->{FILES} .= 'todelete_Ccode$(OBJ_EXT) ';
$hash{'VERSION_FROM'} = 'mylib.pd';
ScriviMakefile(%hash);

sub MY::postamble { pdlpp_postamble($pacchetto); }

Qui, l'elenco in $package è: primo: il nome del file sorgente PP, quindi il prefisso per il file
file prodotti e infine l'intero nome del pacchetto. Puoi modificare l'hash in qualsiasi cosa
nel modo che preferisci, ma sarebbe ragionevole rimanere entro alcuni limiti in modo che il tuo pacchetto
continuerà a funzionare con le versioni successive di PDL.

Se non vuoi usare argomenti preconfezionati, ecco un generico Makefile.PL che tu puoi
adattare alle proprie esigenze:

# Makefile.PL per un pacchetto definito dal codice PP.

usa PDL::Core::Dev; # Raccogli le utilità di sviluppo
usa ExtUtils::MakeMaker;

ScriviMakefile(
'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(), # aggiungi le directory di inclusione come richiesto dalla tua lib
'LIBS' => [''], # aggiungi le direttive di collegamento se necessario
'clean' => {'FILES' =>
'Mylib.pm Mylib.xs Mylib$(OBJ_EXT)
codice_C aggiuntivo$(OBJ_EXT)'},
);

# Aggiungi regola genpp; questo invocherà PDL::PP sul nostro file PP
# l'argomento è un riferimento all'array in cui l'array ha tre elementi stringa:
# arg1: nome del file sorgente che contiene il codice PP
# arg2: nome base dei file xs e pm da generare
# arg3: nome del pacchetto da generare
sub MY::postamble { pdlpp_postamble(["mylib.pd",Mylib,PDL::Lib::Mylib]); }

Per rendere la vita ancora più semplice PDL::Core::Dev definisce la funzione "pdlpp_stdargs" che restituisce
un hash con valori predefiniti che possono essere passati (direttamente o dopo appropriato
modifica) a una chiamata a WriteMakefile. Attualmente, "pdlpp_stdargs" restituisce un hash dove
le chiavi sono compilate come segue:

(
'NOME' => $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)"},
)

Qui, $src è il nome del file sorgente con il codice PP, $pref il prefisso per il generato
.pm e .xs e $mod il nome del modulo di estensione da generare.

INTERNI


Gli interni della versione attuale sono costituiti da un grande tavolo che fornisce le regole
in base al quale le cose vengono tradotte e ai sottotitoli che attuano queste regole.

Successivamente, sarebbe bene rendere la tabella modificabile dall'utente in modo che sia diversa
le cose possono essere provate.

[Meta commento: si spera che ce ne saranno altri in futuro; attualmente, la soluzione migliore sarà
per leggere il codice sorgente :-( o chiedere nell'elenco (provare prima quest'ultimo)]

Appendice A: Alcuni Tasti riconosciuto by PDL::PP


Se non diversamente specificato, gli argomenti sono stringhe. Le chiavi contrassegnate con (cattivo) sono solo
utilizzato se il supporto per valori non validi è compilato in PDL.

Pars
definisci la firma della tua funzione

Altri Par
argomenti che non sono pdls. Predefinito: niente. Questo è un elenco separato da punto e virgola di
argomenti, ad esempio "OtherPars=>'int k; double value; char* fd'". Vedi $COMP(x) e anche
la stessa voce nell'Appendice B.

Code
il codice effettivo che implementa la funzionalità; diverse macro PP e funzioni PP
sono riconosciuti nel valore della stringa

HandleBad (cattivo)
Se impostata su 1, si presume che la routine supporti valori non validi e il codice in BadCode
la chiave viene utilizzata se sono presenti valori errati; imposta anche le cose in modo che "$ISBAD()"
ecc possono essere utilizzate macro. Se impostato su 0, fa in modo che la routine stampi un avviso se presente
i piddle di input hanno il flag errato impostato.

Codice errato (non valido)
Fornire il codice da utilizzare se possono essere presenti valori errati nei piddle di input. Usato solo
se "HandleBad => 1".

Tipi generici
Un riferimento all'array. L'array può contenere qualsiasi sottoinsieme delle stringhe di un carattere 'B',
`S', `U', `L', `Q', `F' e `D', che specificano quali tipi accetterà la tua operazione.
Il significato di ogni tipo è:

B - byte con segno (cioè carattere con segno)
S - breve con segno (numero intero a due byte)
U - corto senza segno
L - con segno lungo (intero a quattro byte, int su sistemi a 32 bit)
Q - long long con segno (numero intero di otto byte)
F - galleggiante
D - doppio

Questo è molto utile (e importante!) quando si interfaccia una libreria esterna. Predefinito:
[qw/BSULQFD/]

A posto
Contrassegna una funzione come in grado di lavorare sul posto.

Inplace => 1 se Pars => 'a(); [o]b();'
Inplace => ['a'] if Pars => 'a(); B(); [o]c();'
Inplace => ['a', 'b'] if Pars => 'a(); B(); [o]c(); [o]d();'

Se si utilizzano valori errati, occorre prestare attenzione per garantire la propagazione del
badflag quando viene utilizzato inplace; ad esempio, vedere il codice per "replacebad" in
Base/Cattivo/cattivo.pd.

Doc Utilizzato per specificare una stringa di documentazione in formato Pod. Vedere PDL::Doc per informazioni su
Convenzioni di documentazione PDL. Nota: nel caso speciale in cui si trova la stringa PP 'Doc'
una riga viene utilizzata implicitamente per il riferimento rapido E la documentazione!

Se il campo Doc viene omesso, PP genererà la documentazione predefinita (dopotutto sa
sulla Firma).

Se vuoi davvero che la funzione NON venga documentata in alcun modo a questo punto (es
per una routine interna o perché lo stai facendo altrove nel codice) in modo esplicito
specificare "Doc => undef".

BadDoc (cattivo)
Contiene il testo restituito dal comando "badinfo" (in "perldl") o dall'opzione "-b".
allo script di shell "pdldoc". In molti casi, non sarà necessario specificarlo, poiché
le informazioni possono essere create automaticamente da PDL::PP. Tuttavia, come si conviene al computer-
testo generato, è piuttosto artificioso; potrebbe essere molto meglio farlo da soli!

Nessun thread
Flag opzionale per indicare che la funzione PDL dovrebbe non utilizzare i thread del processore (es
pthreads o thread POSIX) per suddividere il lavoro su più core della CPU. Questa opzione è
in genere impostato su 1 se la funzione PDL sottostante non è threadsafe. Se questa opzione
non è presente, si presume che la funzione sia threadsafe. Si applica solo questa opzione
se PDL è stato compilato con thread POSIX abilitati.

Codice PM
Le funzioni PDL consentono di passare in un piddle in cui si desidera salvare l'output. Questo
è utile perché puoi allocare un piddle di output una volta e riutilizzarlo molte volte; il
l'alternativa sarebbe che PDL crei ogni volta un nuovo piddle, il che potrebbe sprecare il calcolo
cicli o, più probabilmente, RAM. Questa maggiore flessibilità ha un costo maggiore
complessità: PDL::PP deve scrivere funzioni sufficientemente intelligenti da contare le
argomenti passati ad esso e creare nuovi piddles al volo, ma solo se li vuoi.

PDL::PP è abbastanza intelligente da farlo, ma ci sono restrizioni sull'ordine degli argomenti e
simili. Se vuoi una funzione più flessibile, puoi scrivere il tuo lato Perl
wrapper e specificarlo nella chiave PMCode. La stringa che fornisci deve (dovrebbe)
definisci una funzione Perl con un nome che corrisponda a quello che hai dato a pp_def nel primo
luogo. Quando desideri eventualmente invocare la funzione generata da PP, dovrai farlo
fornire tutti i piddle nell'esatto ordine specificato nella firma: i piddle in uscita sono
non facoltativo e la funzione generata da PP non restituirà nulla. L'offuscato
il nome che chiamerai è _ _int.

Credo che questa documentazione necessiti di ulteriori chiarimenti, ma questo dovrà fare.
:-(

PMFunz
Quando pp_def genera funzioni, in genere le definisce nel pacchetto PDL. Poi,
nel file .pm che genera per il tuo modulo, in genere aggiunge una riga che
essenzialmente copia quella funzione nella tabella dei simboli del pacchetto corrente con il codice
che assomiglia a questo:

*nome_funzione = \&PDL::nome_funzione;

È un po' più intelligente di così (sa quando avvolgere quel genere di cose in un
BEGIN, ad esempio, e se hai specificato qualcosa di diverso per pp_bless), ma
questo è il succo. Se non ti interessa importare la funzione nella tua corrente
tabella dei simboli del pacchetto, è possibile specificare

PMFunc => '',

PMFunc non ha altri effetti collaterali, quindi potresti usarlo per inserire codice Perl arbitrario
nel tuo modulo, se lo desideri. Tuttavia, dovresti usare pp_addpm se vuoi aggiungere Perl
codice al tuo modulo.

Appendice B: PP macro e funzioni


Macro
Le macro etichettate con (non valido) vengono utilizzate solo se il supporto per valori non validi viene compilato in PDL.

$nome_variabile_da_sig()
accedere a un pdl (con il suo nome) che è stato specificato nella firma

$ COMP(x)
accedere a un valore nella struttura dei dati privati ​​di questa trasformazione (utilizzata principalmente per
utilizzare un argomento specificato nella sezione "OtherPars")

$ TAGLIA(n)
sostituito in fase di esecuzione dalla dimensione effettiva di a detto dimensione (come specificato nel
firma)

$GENERICO()
sostituito dal tipo C uguale al tipo di runtime dell'operazione

$P(a) un puntatore di accesso al PDL denominato "a" nella firma. Utile per interfacciarsi con C
funzioni

$PP(a) un puntatore fisico di accesso a pdl "a"; principalmente per uso interno

$TXXX(Alternativo,Alternativo)
alternative di espansione in base al tipo di operazione di runtime, dove XXX è alcune
stringa che corrisponde a "/[BSULFD+]/".

$PDL(a)
restituisce un puntatore alla struttura dati pdl (pdl *) di piddle "a"

$ISBAD(a()) (non valido)
restituisce true se il valore memorizzato in "a()" è uguale al valore errato per questo piddle.
Richiede che "HandleBad" sia impostato su 1.

$ISBUONO(a()) (non valido)
restituisce true se il valore memorizzato in "a()" non è uguale al valore errato per questo
pisello. Richiede che "HandleBad" sia impostato su 1.

$SETBAD(a()) (non valido)
Imposta "a()" per uguagliare il valore errato per questo piddle. Richiede l'impostazione di "HandleBad".
a 1.

funzioni
"loop(DIMS) %{ ... %}"
loop su dimensioni con nome; i limiti sono generati automaticamente da PP

"threadloop %{ ... %}"
racchiudere il codice seguente in un ciclo di thread

"tipi(TIPI) %{ ... %}"
eseguire il codice seguente se il tipo di operazione è uno dei "TIPI"

Appendice C: funzioni importati by PDL::PP


Quando si "usa PDL::PP" vengono importate numerose funzioni. Questi includono funzioni che
controllare il codice C o XS generato, le funzioni che controllano il codice Perl generato e
funzioni che manipolano i pacchetti e le tabelle dei simboli in cui viene creato il codice.

Generazione C e XS Code
Lo scopo principale di PDL::PP è semplificare l'avvolgimento del motore di threading attorno al file
possiedi il codice C, ma puoi fare anche altre cose.

pp_def
Utilizzato per avvolgere il motore di threading attorno al codice C. Praticamente tutto questo documento
discute l'uso di pp_def.

pp_fatto
Indica che hai finito con PDL::PP e che dovrebbe generare i suoi file .xs e .pm
in base alle altre funzioni pp_* che hai chiamato. Questa funzione richiede n
argomenti.

pp_addxs
Ciò ti consente di aggiungere codice XS al tuo file .xs. Questo è utile se vuoi creare Perl-
funzioni accessibili che invocano il codice C ma non possono o non devono invocare il threading
motore. XS è il mezzo standard con cui avvolgere il codice C accessibile da Perl. Puoi
scopri di più su perlxs.

pp_aggiungi_avvio
Questa funzione aggiunge qualsiasi stringa tu passi alla sezione XS BOOT. La sezione BOOT
è il codice C che viene chiamato da Perl quando il modulo viene caricato ed è utile per
inizializzazione automatica. Puoi saperne di più su XS e sulla sezione BOOT su perlxs.

pp_addhdr
Aggiunge codice C puro al tuo file XS. I file XS sono strutturati in modo tale che il codice C puro debba
venire prima delle specifiche XS. Ciò consente di specificare tale codice C.

pp_boundscheck
PDL normalmente controlla i limiti dei tuoi accessi prima di effettuarli. Puoi girarlo
on o off in fase di esecuzione impostando MyPackage::set_boundscheck. Questa funzione ti consente
per rimuovere quella flessibilità di runtime e mai fai il controllo dei limiti. Restituisce anche il
stato corrente del boundscheck se chiamato senza argomenti.

NOTA: non ho trovato nulla sul controllo dei limiti in altra documentazione. Quella
deve essere affrontato

Generazione Perl Code
Molte funzioni importate quando si utilizza PDL::PP consentono di modificare il contenuto del file
file .pm generato. Oltre a pp_def e pp_done, il ruolo di queste funzioni è
principalmente per aggiungere codice a varie parti del file .pm generato.

pp_addpm
Aggiunge il codice Perl al file .pm generato. PDL::PP in realtà tiene traccia di tre
diverse sezioni del codice generato: la parte superiore, la parte centrale e la parte inferiore. Puoi aggiungere
Codice Perl nella sezione centrale usando la forma a un argomento, dove l'argomento è il
Codice Perl che vuoi fornire. Nella forma a due argomenti, il primo argomento è an
hash anonimo con una sola chiave che specifica dove inserire il secondo argomento,
che è la stringa che vuoi aggiungere al file .pm. L'hash è uno di questi
tre:

{At => 'In alto'}
{At => 'Mezzo'}
{At => 'Bot'}

Per esempio:

pp_addpm({At => 'Bot'}, <

=head1 Un po' di documentazione

So che sto scrivendo questo nel mezzo del mio file, ma andrà a
il fondo.

=tagliare

POD

Avvertenza: se, nel mezzo del tuo file .pd, metti la documentazione destinata al
nella parte inferiore del tuo pod, confonderai completamente CPAN. D'altra parte, se nel
a metà del tuo .pd fil, aggiungi del codice Perl destinato alla parte inferiore o superiore del tuo
.pm, devi solo confondere te stesso. :-)

pp_beginwrap
Aggiunge il wrapping del blocco BEGIN. Tuttavia, alcune dichiarazioni possono essere racchiuse nei blocchi BEGIN
il comportamento predefinito è di non avere tale avvolgimento.

pp_addbegin
Imposta il codice da aggiungere all'inizio del file .pm, anche sopra il codice specificato
con "pp_addpm({At => 'Top'}, ...)". A differenza di pp_addpm, chiamare questo sovrascrive qualsiasi cosa
c'era prima. In generale, probabilmente non dovresti usarlo.

Tracking linea numeri
Quando ricevi errori di compilazione, dal tuo codice simile al C o dal tuo codice Perl, può essere d'aiuto
per riportare quegli errori ai numeri di riga nel file di origine in cui si è verificato l'errore
si è verificato.

pp_line_numbers
Accetta un numero di riga e una stringa di codice (solitamente lunga). Il numero di riga dovrebbe
indicare la riga in cui inizia la citazione. Di solito è "__LINE__" di Perl
letterale, a meno che tu non stia usando heredocs, nel qual caso è "__LINE__ + 1". Il
la stringa restituita ha le direttive #line intervallate per aiutare il compilatore a segnalare gli errori
sulla riga corretta.

Modifica , il Simbolo Table e Esportare Comportamento
PDL::PP di solito esporta tutte le funzioni generate usando pp_def e di solito le installa
nella tabella dei simboli PDL. Tuttavia, è possibile modificare questo comportamento con queste funzioni.

pp_benedica
Imposta il pacchetto (tabella dei simboli) a cui viene aggiunto il codice XS. L'impostazione predefinita è PDL,
che è generalmente quello che vuoi. Se usi la benedizione predefinita e crei a
funzione myfunc, quindi puoi fare quanto segue:

$piddle->miafunzione( );
PDL::myfunc($piddle, );

D'altra parte, se benedici le tue funzioni in un altro pacchetto, non puoi invocare
come metodi PDL e deve invocarli come:

MyPackage::myfunc($piddle, );

Ovviamente puoi sempre usare il tasto PMFunc per aggiungere la tua funzione al simbolo PDL
tavolo, ma perché farlo?

pp_aggiungi_isa
Aggiunge all'elenco dei moduli da cui il tuo modulo eredita. L'elenco predefinito è

qw(PDL::Esportatore DynaLoader)

pp_core_importlist
Nella parte superiore del file .pm generato c'è una riga simile a questa:

usa PDL::Nucleo;

Puoi modificarlo specificando una stringa in pp_core_importlist. Per esempio,

pp_core_importlist('::Blarg');

si tradurrà in

usa PDL::Core::Blarg;

Puoi usarlo, ad esempio, per aggiungere un elenco di simboli da importare da PDL::Core. Per
esempio:

pp_core_importlist(" ':Interno'");

porterà alla seguente dichiarazione d'uso:

usa PDL::Core ':Interno';

pp_setversione
Imposta la versione del modulo. La versione deve essere coerente tra .xs e .pm
file, e viene utilizzato per garantire che le librerie del tuo Perl non soffrano di versione
storto.

pp_aggiungi_esportato
Aggiunge all'elenco di esportazione qualsiasi nome gli venga assegnato. Funzioni create usando pp_def
vengono aggiunti automaticamente all'elenco. Questa funzione è utile se si definisce qualsiasi Perl
funzioni utilizzando pp_addpm o pp_addxs che si desidera esportare.

pp_export_niente
Questo azzera l'elenco dei simboli esportati. Questo è probabilmente meglio chiamato
"pp_export_clear", poiché puoi aggiungere simboli esportati dopo aver chiamato
"pp_export_niente". Quando viene chiamato appena prima di chiamare pp_done, questo assicura che il tuo
module non esporta nulla, ad esempio, se vuoi che solo i programmatori utilizzino il tuo
funziona come metodi.

Usa PDL::PPp online usando i servizi onworks.net


Server e workstation gratuiti

Scarica app per Windows e Linux

  • 1
    packfilemanager
    packfilemanager
    Questo è il file manager del pacchetto Total War
    progetto, a partire dalla versione 1.7. UN
    breve introduzione a Warscape
    mod:...
    Scarica packfilemanager
  • 2
    IPerf2
    IPerf2
    Uno strumento di misurazione del traffico di rete
    Prestazioni TCP e UDP con metriche
    intorno sia al throughput che alla latenza. Il
    gli obiettivi includono il mantenimento di un attivo
    merluzzo iperf...
    Scarica IPerf2
  • 3
    fre:ac - convertitore audio gratuito
    fre:ac - convertitore audio gratuito
    fre:ac è un convertitore audio e CD gratuito
    ripper per vari formati ed encoder.
    È dotato di MP3, MP4/M4A, WMA, Ogg
    Formato Vorbis, FLAC, AAC e Bonk
    sostegno, ...
    Scarica fre:ac - convertitore audio gratuito
  • 4
    matplotlib
    matplotlib
    Matplotlib è una libreria completa
    per creare statici, animati e
    visualizzazioni interattive in Python.
    Matplotlib rende le cose facili facili e
    cosa difficile...
    Scarica Matplotlib
  • 5
    Bone Man
    Bone Man
    Scrivi la tua logica chatbot una volta e
    collegarlo a uno dei disponibili
    servizi di messaggistica, incluso Amazon
    Alexa, Facebook Messenger, Slack,
    Telegram o anche tu...
    Scarica Botman
  • 6
    Joplin
    Joplin
    Joplin è un software gratuito e open source
    applicazione per prendere appunti e cose da fare che
    può gestire un gran numero di note in
    Formato Markdown, organizzali in
    quaderni e...
    Scarica Joplin
  • Di Più "

Comandi Linux

Ad