Per quanto riguarda il calo di prestazioni (da quando la prima questione è stata risolta già) - con tutti i mezzi, fare i controlli , ma nelle funzioni di primo livello (che ricevono i dati direttamente dall'utente della tua funzionalità. L'utente può anche essere un altro modulo indipendente, scritto da te o da qualcun altro). Non mettere questi controlli in tutte le tue funzioni intermedie, dal momento che tali controlli saranno duplicati e in effetti ingiustificati.
EDIT
Per affrontare il problema degli errori nelle funzioni intermedie, cresciuto da @Nasser nei commenti: v'è una tecnica molto semplice che permette di passare modello controlli e spegnimento in "one click ". È possibile memorizzare i modelli in variabili all'interno del pacchetto, definite prima delle definizioni delle funzioni.
Ecco un esempio, dove f
è una funzione di primo livello, mentre g
e h
sono "funzioni interne". Definiamo due modelli: per la funzione principale e per quelle interne, in questo modo:
Clear[nlPatt,innerNLPatt ];
nlPatt= _?(!VectorQ[#,NumericQ]&);
innerNLPatt = nlPatt;
Ora, definiamo le nostre funzioni:
ClearAll[f,g,h];
f[vector:nlPatt]:=g[vector]+h[vector];
g[nv:innerNLPatt ]:=nv^2;
h[nv:innerNLPatt ]:=nv^3;
Nota che i modelli vengono sostituiti dentro le definizioni alla definizione tempo, non in fase di esecuzione, quindi questo è esattamente equivalente alla codifica di quei pattern a mano.Una volta che si prova, basta cambiare una sola riga: da
innerNLPatt = nlPatt
a
innerNLPatt = _
e ricaricare il pacchetto.
Un'ultima domanda è: come si trovano rapidamente gli errori? Ho risposto a quello here, nelle sezioni "Invece di restituire $Failed
, si può generare un'eccezione, utilizzando Throw." e "Meta-programmazione e automazione".
FINE EDIT
ho inserito una breve discussione di questo problema nel mio libro here. In questo esempio, il successo della performance era al livello del 10% di aumento del tempo di esecuzione, che IMO è al limite accettabile. Nel caso in esame, il controllo è più semplice e la penalizzazione delle prestazioni è molto inferiore. In genere, per una funzione che è qualsiasi controllo di tipo intensivo a livello di calcolo, correttamente scritto costa solo una piccola frazione del tempo di esecuzione totale.
Alcuni trucchi che sono bene sapere:
- Pattern-matcher può essere molto veloce, se usato sintatticamente (senza
Condition
o PatternTest
presente nel modello).
Ad esempio:
randomString[]:[email protected][{97,122},5];
rstest = Table[randomString[],{1000000}];
In[102]:= MatchQ[rstest,{__String}]//Timing
Out[102]= {0.047,True}
In[103]:= MatchQ[rstest,{__?StringQ}]//Timing
Out[103]= {0.234,True}
Solo perché in quest'ultimo caso il PatternTest
stato utilizzato, il controllo è molto più lento, perché valutatore viene richiamato dal pattern matcher per ogni elemento, mentre nella prima caso, tutto è puramente sintattico e tutto viene fatto all'interno del pattern-matcher.
- Lo stesso vale per imballati elenchi numerici (la differenza di temporizzazione è simile). Tuttavia, per gli elenchi numerici,
MatchQ
e altre funzioni di verifica del modello non vengono decompressi per determinati motivi speciali, inoltre, per alcuni di essi il controllo è istantaneo.
Ecco un esempio:
In[113]:=
test = RandomInteger[100000,1000000];
In[114]:= MatchQ[test,{__?IntegerQ}]//Timing
Out[114]= {0.203,True}
In[115]:= MatchQ[test,{__Integer}]//Timing
Out[115]= {0.,True}
In[116]:= Do[MatchQ[test,{__Integer}],{1000}]//Timing
Out[116]= {0.,Null}
Lo stesso, a quanto pare, sembra essere vero per le funzioni come VectorQ
, MatrixQ
e ArrayQ
con alcuni predicati (NumericQ
) - questi test sono estremamente efficienti.
- Molto dipende da come si scrive il test, vale a dire fino a che punto si riutilizza le strutture Mathematica efficienti.
Per esempio, vogliamo mettere alla prova che abbiamo una vera e propria matrice numerica:
In[143]:= rm = RandomInteger[10000,{1500,1500}];
Ecco il modo più straight-forward e lento:
In[144]:= MatrixQ[rm,NumericQ[#]&&Im[#]==0&]//Timing
Out[144]= {4.125,True}
Questo è meglio, dal momento che riutilizziamo meglio il pattern-matcher:
In[145]:= MatrixQ[rm,NumericQ]&&FreeQ[rm,Complex]//Timing
Out[145]= {0.204,True}
Non abbiamo utilizzare tuttavia la natura imballata della matrice. Questo è ancora meglio:
In[146]:= MatrixQ[rm,NumericQ]&&Total[Abs[Flatten[Im[rm]]]]==0//Timing
Out[146]= {0.047,True}
Tuttavia, questa non è la fine. Il successivo è quasi istantaneo:
In[147]:= MatrixQ[rm,NumericQ]&&Re[rm]==rm//Timing
Out[147]= {0.,True}
anche io a volte mi trovo chiedendo circa il calo di prestazioni di questo, * + 1 * per quella domanda – Szabolcs
Devo fare la parte domanda performance come una domanda separata o semplicemente lasciare qui ? – Nasser
Lo lascerei qui. Li ho evidenziati in grassetto perché penso che sia molto più facile leggere e * notare * in questo modo. Altrimenti molte persone non leggeranno mai così lontano. Se non accetti la mia risposta, la domanda attirerà più punti di vista. – Szabolcs