Questo è un piccolo problema divertente, e volevo verificare con gli esperti qui se c'è un modo migliore/funzionale per affrontare Mathematica rispetto a quello che ho fatto. Non sono molto contento della mia soluzione poiché utilizzo il grande IF THEN ELSE ma non riesco a trovare un comando Mathematica da usare facilmente (come Select
, Cases
, Sow/Reap
, Map
.. ecc ...)Come sostituire ogni 0 con l'elemento precedente in una lista in modo idiomatico in Mathematica?
Ecco il problema, dati i valori di una lista (numeri o simboli), ma per semplicità, per ora assumiamo una lista di numeri. L'elenco può contenere zeri e l'obiettivo è sostituire ogni zero con l'elemento visto prima.
Alla fine, l'elenco non deve contenere zeri.
Ecco un esempio, dato
a = {1, 0, 0, -1, 0, 0, 5, 0};
il risultato dovrebbe essere
a = {1, 1, 1, -1, -1, -1, 5, 5}
Si deve naturalmente essere fatto in modo più efficiente.
Questo è ciò che ho potuto venire con
Scan[(a[[#]] = If[a[[#]] == 0, a[[#-1]], a[[#]]]) &, Range[2, Length[a]]];
Volevo vedere se posso usare Sow/Reap su questo, ma non sapeva come fare.
domanda: può essere risolto in un modo più funzionale/matematico? Più corto è meglio naturalmente :)
aggiornamento 1 Grazie a tutti per la risposta, tutti sono molto buona per imparare. Questo è il risultato di test di velocità, su V 8.04, utilizzando finestre 7, 4 GB RAM, processore Intel 930 @ 2.8 Ghz:
Ho testato metodi da applicare per n
da 100,000
a 4 million
. Il metodo ReplaceRepeated
non funziona bene per elenchi di grandi dimensioni.
aggiornamento 2
Rimosso risultato in precedenza che è stato mostrato in precedenza in Update1 a causa del mio errore nella copia di una delle prove.
I risultati aggiornati sono di seguito. Il metodo Leonid è il più veloce. Congratulazioni Leonid. Un metodo molto veloce.
Il programma di test è la seguente:
(*version 2.0 *)
runTests[sizeOfList_?(IntegerQ[#] && Positive[#] &)] :=
Module[{tests, lst, result, nasser, daniel, heike, leonid, andrei,
sjoerd, i, names},
nasser[lst_List] := Module[{a = lst},
Scan[(a[[#]] = If[a[[#]] == 0, a[[# - 1]], a[[#]]]) &,
Range[2, Length[a]]]
];
daniel[lst_List] := Module[{replaceWithPrior},
replaceWithPrior[ll_, n_: 0] :=
Module[{prev}, Map[If[# == 0, prev, prev = #] &, ll]
];
replaceWithPrior[lst]
];
heike[lst_List] := Flatten[Accumulate /@ Split[lst, (#2 == 0) &]];
andrei[lst_List] := Module[{x, y, z},
ReplaceRepeated[lst, {x___, y_, 0, z___} :> {x, y, y, z},
MaxIterations -> Infinity]
];
leonid[lst_List] :=
FoldList[If[#2 == 0, #1, #2] &, [email protected]#, [email protected]#] & @lst;
sjoerd[lst_List] :=
FixedPoint[(1 - Unitize[#]) RotateRight[#] + # &, lst];
lst = RandomChoice[Join[ConstantArray[0, 10], Range[-1, 5]],
sizeOfList];
tests = {nasser, daniel, heike, leonid, sjoerd};
names = {"Nasser","Daniel", "Heike", "Leonid", "Sjoerd"};
result = Table[0, {Length[tests]}, {2}];
Do[
result[[i, 1]] = names[[i]];
Block[{j, r = Table[0, {5}]},
Do[
r[[j]] = [email protected][tests[[i]][lst]], {j, 1, 5}
];
result[[i, 2]] = Mean[r]
],
{i, 1, Length[tests]}
];
result
]
Per eseguire i test per lunghezza 1000 il comando è:
Grid[runTests[1000], Frame -> All]
Grazie a tutti per le risposte.
Solo una nota che l'uso di "Se" è * non * non funziona. I condizionali sono una parte essenziale della programmazione funzionale e non richiedono effetti collaterali. Pensa a 'Se' come una funzione matematica che mappa i boolan (il set {True, False}) a qualcos'altro. Altrimenti mi è venuta in mente la stessa soluzione di Andrei, che ritengo sia la più semplice, ma sicuramente non la più veloce (quindi non la più pratica se si elaborano grandi dati!) – Szabolcs
replaceWithPrior [ll_, n_: 0]: = Modulo [{ prev}, Mappa [Se [# == 0, prev, prev = #] &, ll]] In [12]: = replaceWithPrior [a] Out [12] = {1, 1, 1, -1 , -1, -1, 5, 5} –
BTW cosa dovrebbe accadere se il primo elemento è 0? – Szabolcs