Este es un pequeño problema divertido, y quería consultar con los expertos aquí si hay una mejor manera funcional/Mathematica para abordar la solución de lo que hice. No estoy muy contento con mi solución ya que utilizar grandes si entonces si no en ella, pero no pudo encontrar un comando Mathematica para utilizar fácilmente para hacerlo (como Select
, Cases
, Sow/Reap
, Map
.. etc ...)¿Cómo reemplazar cada 0 con el elemento precedente en una lista de manera idiomática en Mathematica?
Este es el problema, dados los valores de una lista (números o símbolos), pero por simplicidad, supongamos una lista de números por el momento. La lista puede contener ceros y el objetivo es reemplazar cada cero con el elemento visto antes.
Al final, la lista no debe contener ceros.
Aquí es un ejemplo, dado
a = {1, 0, 0, -1, 0, 0, 5, 0};
el resultado debe ser
a = {1, 1, 1, -1, -1, -1, 5, 5}
Se debe por supuesto ser hecho de la manera más eficiente.
Esto es lo que podría llegar a
Scan[(a[[#]] = If[a[[#]] == 0, a[[#-1]], a[[#]]]) &, Range[2, Length[a]]];
quería ver si puedo usar Sow/Reap con esto, pero no sabía cómo.
pregunta: ¿esto se puede resolver de una manera más funcional/Mathematica? Cuanto más corto sea el mejor por supuesto :)
actualización 1 Gracias a todos por la respuesta, todos son muy buenos para aprender. Este es el resultado de la prueba de velocidad, en V 8.04, utilizando Windows 7, 4 GB de RAM, Intel 930 @ 2,8 Ghz:
He probado los métodos dados para n
de 100,000
a 4 million
. El método ReplaceRepeated
no funciona bien para listas grandes.
actualización 2
Eliminado resultado anterior que se muestra arriba en Update1 debido a mi error en la copia de una de las pruebas.
Los resultados actualizados están por debajo. El método Leonid es el más rápido. Felicidades Leonid. Un método muy rápido.
El programa de prueba es el siguiente:
(*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
]
para ejecutar las pruebas de longitud 1000 del comando es:
Grid[runTests[1000], Frame -> All]
Gracias a todos por las respuestas.
Solo una nota que el uso de 'If' es * not * no funcional. Los condicionales son una parte esencial de la programación funcional y no requieren efectos secundarios. Piense en 'si' como un mapeo de función matemática boolans (el conjunto {True, False}) a otra cosa. De lo contrario, se me ocurrió la misma solución que Andrei, que creo que es la más simple, pero definitivamente no la más rápida (¡no es la más práctica si procesas datos grandes!) – Szabolcs
replaceWithPrior [ll_, n_: 0]: = Module [{ prev}, Mapa [If [# == 0, prev, prev = #] &, ll]] En [12]: = replaceWithPrior [a] Fuera [12] = {1, 1, 1, -1 , -1, -1, 5, 5} –
Por cierto, ¿qué pasaría si el primer elemento es 0? – Szabolcs