2011-02-06 13 views
5

ho in precedenza asked come fare allTrue[{x,list},test] funzione che protegge il simbolo segnaposto x dalla valutazione nel contesto attuale nello stesso modo come Table[expr,{x,...}] protegge xvalutazione non standard e PackedArray

La ricetta che ho finito per usare riuscito ad intermittenza e ho scoperto che il problema era causato dalla conversione automatica degli elenchi in PackedArrays. Ecco un esempio in mancanza

SetAttributes[allTrue, HoldAll]; 
allTrue[{var_, lis_}, expr_] := 
    LengthWhile[lis, 
    TrueQ[ReleaseHold[Hold[expr] /. HoldPattern[var] -> #]] &] == 
    Length[lis]; 
allTrue[{y, Developer`ToPackedArray[{1, 1, 1}]}, y > 0] 

voglio allTrue[{x,{1,2,3}},x>0] tornare True indipendentemente dal fatto che {1,2,3} viene convertito automaticamente in PackedArray, che cosa è un modo migliore per attuarlo?

+0

Non sono sicuro delle implicazioni sulle prestazioni, ma quasi sicuro 'LengthWhile [FromPackedArray @ lis', ...funziona –

+0

Sì, è quello che sto usando ora, ma sono più interessato a un buon stile generale per questo tipo di attività –

+2

Dai documenti ** L'uso di ToPackedArray non cambierà i risultati generati da Mathematica ** ... lier! lier! pantaloni in fiamme! –

risposta

7

Questa è una versione che uso da un po 'di tempo (l'ho scritta per una seconda edizione del mio libro in origine, ma ho finito per usarla molto). Se gli argomenti rappresentano un codice non valutato, la funzione di test deve avere gli attributi HoldAll o HoldFirst se si desidera che un singolo pezzo di codice che rappresenta una clausola specifica venga passato ad esso nella sua forma non valutata (che può essere o non essere desiderabile).

ClearAll[fastOr]; 
Attributes[fastOr] = {HoldRest}; 
fastOr[test_, {args___}] := fastOr[test, args]; 
fastOr[test_, args___] := 
TrueQ[Scan[ 
     Function[arg, If[test[arg], Return[True]], HoldAll], 
     Hold[args]]]; 

Edit: Ho appena notato che la soluzione da Daniel Reeves nella parte inferiore della pagina collegata in questione, è molto simile a questo. La differenza principale è che mi interessa sia cortocircuitare che mantenere argomenti non valutati (vedi sotto), mentre Daniel si concentra solo sulla parte di cortocircuito.

Ha un comportamento di cortocircuito. Abbiamo bisogno dell'attributo HoldRest dal momento che vogliamo preservare gli argomenti nella loro forma non valutata. Abbiamo anche bisogno dell'attributo HoldAll (o HoldFirst) in una funzione pura per conservare ciascuno degli argomenti non valutati fino a quando non viene passato a test. Sia che venga valutato prima che venga utilizzato nel corpo di test dipende ora dagli attributi di test. Per fare un esempio:

Clear[fullSquareQ]; 
fullSquareQ[x_Integer] := IntegerQ[Sqrt[x]]; 

In[13]:= Or @@ Map[fullSquareQ, Range[50000]] // Timing 

Out[13]= {0.594, True} 

In[14]:= fastOr[fullSquareQ, Evaluate[Range[10000]]] // Timing 

Out[14]= {0., True} 

Ecco un esempio in cui si passa come argomenti alcuni pezzi di codice indurre effetti collaterali (stampa). Il codice del l'ultimo argomento non ha alcuna possibilità di eseguire, in quanto il risultato sia già stato determinato al comma precedente:

In[15]:= fastOr[# &, Print["*"]; False, Print["**"]; False, 
    Print["***"]; True, Print["****"]; False] 

During evaluation of In[15]:= * 

During evaluation of In[15]:= ** 

During evaluation of In[15]:= *** 

Out[15]= True 

Si noti che, dal momento che fastOr accetta pezzi generali di codice non valutata come clausole di Or, si deve avvolgere l'elenco di valori in Evaluate se non si cura che verranno valutati all'inizio (come nel caso dell'esempio Range precedente).

Infine, illustrerò la costruzione programmatica del codice di attesa per fastOr, per mostrare come può essere utilizzato (considerarlo un piccolo corso accelerato su come lavorare con le espressioni in attesa, se lo si desidera).La seguente funzione è molto utile quando si lavora con le espressioni in attesa:

joinHeld[a___Hold] := Hold @@ Replace[Hold[a], Hold[x___] :> Sequence[x], {1}]; 

Esempio:

In[26]:= joinHeld[Hold[Print[1]], Hold[Print[2], Print[3]], Hold[], Hold[Print[4]]] 

Out[26]= Hold[Print[1], Print[2], Print[3], Print[4]] 

Ecco come lo usiamo per costruire a livello di codice gli argomenti detenuti che sono stati utilizzati nell'esempio con Print-s sopra:

In[27]:= 
held = joinHeld @@ MapThread[Hold[Print[#]; #2] &, 
     {NestList[# <> "*" &, "*", 3], {False, False, True, False}}] 

Out[27]= Hold[Print["*"]; False, Print["**"]; False, Print["***"]; True, Print["****"]; False] 

passarlo al fastOr, useremo altro linguaggio utile: aggiungere (o prepend) per Hold[args] fino ad arrivare tutti argomenti della funzione, e quindi utilizzare Apply (si noti che, in generale, se non vogliamo che il pezzo che stiamo aggiungendo/anteponendo a valutare, bisogna avvolgerla in Unevaluated, in modo che il linguaggio generale assomiglia Append[Hold[parts___],Unevaluated[newpart]]):

In[28]:= fastOr @@ Prepend[held, # &] 

During evaluation of In[28]:= * 

During evaluation of In[28]:= ** 

During evaluation of In[28]:= *** 

Out[28]= True 

Per quanto riguarda l'implementazione originale a cui fai riferimento, puoi guardare il mio commento ad esso che ho fatto qualche tempo fa. Il problema è che TakeWhile e LengthWhile hanno bug per gli array compressi in v. 8.0.0, sono corretti nei sorgenti di 8.0.1 - così, a partire dalla 8.0.1, puoi usare la mia o la versione di Michael.

HTH

Edit:

Ho appena notato che nel post lei ha fatto riferimento a, si voleva una sintassi diversa. Anche se non sarebbe molto difficile adottare l'approccio adottato in questo caso in fastOr, ecco un'altra implementazione, che probabilmente si trova in una più stretta corrispondenza con i costrutti del linguaggio esistenti per questa particolare sintassi. Suggerisco di usare Table ed eccezioni, dal momento che gli iteratori nello Table accettano la stessa sintassi che si desidera. Eccolo:

ClearAll[AnyTrue, AllTrue]; 
SetAttributes[{AnyTrue, AllTrue}, HoldAll]; 
Module[{exany, exall}, 
AnyTrue[iter : {var_Symbol, lis_List}, expr_] := 
    TrueQ[Catch[Table[If[TrueQ[expr], Throw[True, exany]], iter], exany]]; 
AllTrue[iter : {var_Symbol, lis_List}, expr_] := 
    Catch[Table[If[! TrueQ[expr], Throw[False, exall]], iter], exall] =!= False; 
]; 

Qualche parola di spiegazione: Io uso il modulo al livello superiore in quanto i tag eccezione personalizzata che abbiamo bisogno di definire una sola volta, può anche fare che alla definizione del tempo. Il modo per uscire da una tabella è attraverso le eccezioni. Non è molto elegante e provoca un piccolo calo di prestazioni, ma compriamo la localizzazione dinamica automatica delle variabili iteratore effettuate da Table e la semplicità. Per fare ciò in modo sicuro, dobbiamo taggare un'eccezione con un tag univoco, quindi non rileviamo altre eccezioni per errore. Trovo che usare il modulo per creare tag eccezioni persistenti sia un trucco molto utile in generale. Ora, alcuni esempi:

In[40]:= i = 1 

Out[40]= 1 

In[41]:= AnyTrue[{i, {1, 2, 3, 4, 5}}, i > 3] 

Out[41]= True 

In[42]:= AnyTrue[{i, {1, 2, 3, 4, 5}}, i > 6] 

Out[42]= False 

In[43]:= AllTrue[{i, {1, 2, 3, 4, 5}}, i > 3] 

Out[43]= False 

In[44]:= AllTrue[{i, {1, 2, 3, 4, 5}}, i < 6] 

Out[44]= True 

In[45]:= AllTrue[{a, {1, 3, 5}}, AnyTrue[{b, {2, 4, 5}}, EvenQ[a + b]]] 

Out[45]= True 

In[46]:= AnyTrue[{a, {1, 3, 5}}, AllTrue[{b, {2, 4, 5}}, EvenQ[a + b]]] 

Out[46]= False 

ho iniziato con l'incarico di i per dimostrare che i possibili valori globali delle variabili iteratore non hanno importanza - questo è preso cura di Table. Infine, ricordiamo che (come ho commentato altrove), le vostre firme originali per AllTrue e AnyTrue sono un po 'troppo restrittiva, nel senso che il seguente non funziona:

In[47]:= lst = Range[5]; 
AllTrue[{i, lst}, i > 3] 

Out[48]= AllTrue[{i, lst}, i > 3] 

(poiché il fatto che lst rappresenta un elenco non è noto al momento della corrispondenza del motivo, a causa dell'attributo HoldAll). Non c'è una buona ragione per mantenere questo comportamento, quindi è sufficiente rimuovere i controlli _List: AnyTrue[iter : {var_Symbol, lis_}, expr_] e allo stesso modo per AllTrue e questa classe di casi d'uso sarà coperta.

+0

Questo è letteralmente fantastico. Sembra che i costrutti di programmazione funzionale incorporati in Mathematica siano un po 'impoveriti se si deve fare tutto questo per ottenere 'ogni' e' qualcosa'. – Harold

+0

@Harold Questi non sono usuali 'every' e' some', perché si occupano di blocchi di codice non valutato, piuttosto che con espressioni già calcolate. E questo non era tanto lavoro, la maggior parte del post è pedagogico e dedicato alle spiegazioni piuttosto che alle implementazioni. Inoltre, potrebbe benissimo essere che il mio codice stia copiando troppo le cose. –

+0

Oh? Ci sono più definizioni 'normali' per 'every' e' some'? Non ho visto nulla di integrato nella documentazione ufficiale. (Tuttavia, sto ancora migliorando nel trovare le cose lì) Forse dovrei aggiungere questa come una domanda a Mathematica.stackexchange.com ... – Harold

Problemi correlati