(Editado de this post en fshub)
La primera vez que fui a alcanzar para pausa/continuar en OCaml/F #, me lanzó para un (infinita) loop, por así decirlo, ¡porque no existe tal cosa! En OCaml, se pueden usar excepciones para romper un bucle porque son muy barato, pero en F # (pulg.NET) la sobrecarga es bastante alta y no es útil para el control de flujo "normal".
Esto apareció cuando se jugaba con algoritmos de ordenación un tiempo atrás (para matar el tiempo), que hace un uso intensivo de repetir/hasta y romper. Me llamó la atención que las funciones de llamada de cola recursivas pueden lograr exactamente el mismo resultado, con solo un ligero toque de legibilidad. Entonces, lancé 'mutable bDone' y 'while not bDone' e intenté escribir el código sin estos constructos imperativos. Lo siguiente destila solo las partes en bucle y muestra cómo escribir repetir/hasta, do/while, while/do, break/continue, y test-in-the-middle código de estilo usando tailcalls. Todo esto parece ejecutarse exactamente a la misma velocidad que una instrucción tradicional F # 'while', pero su kilometraje puede variar (algunas plataformas pueden no implementar correctamente tailcall y, por lo tanto, pueden acumular fallas hasta que se remenquen). Al final hay un algoritmo de clasificación (malo) escrito en ambos estilos, para comparar.
Comencemos con un ciclo 'do/while', escrito en estilo imperativo F # tradicional, luego observemos variaciones funcionales que proporcionan el mismo tipo de bucle, así como semánticas diferentes como while/do, repeat/until, prueba en el medio, e incluso romper/continuar (sin mónadas ... um, flujos de trabajo!).
#light
(* something to work on... *)
let v = ref 0
let f() = incr v;
let g() = !v;
let N = 100000000
let imperDoWhile() =
let mutable x = 0
let mutable bDone = false
while not bDone do
f()
x <- x + 1
if x >= N then bDone <- true
Ok, eso es bastante fácil. Tenga en cuenta que f() siempre se llama al menos una vez (do/while).
Aquí está el mismo código, pero en un estilo funcional. Tenga en cuenta que no necesitamos declarar un mutable aquí.
let funDoWhile() =
let rec loop x =
f() (*Do*)
if x < N then (*While*)
loop (x+1)
loop 0
Nos puede girar que a un do tradicional/mientras poniendo la llamada a la función dentro del bloque if.
let funWhileDo() =
let rec loop x =
if x < N then (*While*)
f() (*Do*)
loop (x+1)
loop 0
¿Qué le parece repetir un bloque hasta que alguna condición sea verdadera (repetir/hasta)? Lo suficientemente fácil ...
let funRepeatUntil() =
let rec loop x =
f() (*Repeat*)
if x >= N then() (*Until*)
else loop (x+1)
loop 0
¿Qué fue eso de un descanso de mónada? Bueno, solo introduce una expresión condicional que devuelve 'unidad', como en:
let funBreak() =
let rec loop() =
let x = g()
if x > N then() (*break*)
else
f()
loop()
loop()
¿Qué tal, continuar? Bueno, ¡eso es solo otra llamada a loop! En primer lugar, con una muleta sintaxis:
let funBreakContinue() =
let break'() =()
let rec continue'() =
let x = g()
if x > N then break'()
elif x % 2 = 0 then
f(); f(); f();
continue'()
else
f()
continue'()
continue'()
Y, de nuevo sin el (feo) muleta sintaxis:
let funBreakContinue'() =
let rec loop() =
let x = g()
if x > N then()
elif x % 2 = 0 then
f(); f(); f();
loop()
else
f()
loop()
loop()
fácil como pastel!
Un buen resultado de estas formas de bucle es que facilita la detección e implementación de estados en sus bucles. Por ejemplo, un tipo de burbuja continuamente realiza un bucle en una matriz completa, intercambiando valores que están fuera de lugar a medida que los encuentra. Realiza un seguimiento de si un pase sobre la matriz produjo algún intercambio. Si no, entonces cada valor debe estar en el lugar correcto, por lo que el género puede finalizar. Como optimización, en cada pasada a través de la matriz, el último valor de la matriz termina ordenado en el lugar correcto. Por lo tanto, el ciclo se puede acortar en uno cada vez. La mayoría de los algoritmos buscan un intercambio y actualizan un indicador "bModified" cada vez que hay uno. Sin embargo, una vez que se realiza el primer intercambio, no hay necesidad de esa asignación; ya está establecido en verdadero!
Aquí está el código F # que implementa un tipo de burbuja (sí, el tipo de burbuja es un algoritmo terrible; las rocas de quicksort). Al final es una implementación imperativa que no cambia el estado; actualiza el indicador bModified para cada intercambio.Curiosamente, la solución imperativa es más rápida en arreglos pequeños y solo un uno o dos por ciento más lenta en los grandes. (Hecho para un buen ejemplo, sin embargo).
let inline sort2 f i j (a:'a array) =
let i' = a.[ i ]
let j' = a.[ j ]
if f i' j' > 0 then
a.[ i ] <- j'
a.[ j ] <- i'
let bubble f (xs:'a array) =
if xs.Length = 0
then()
let rec modified i endix =
if i = endix then
unmodified 0 (endix-1)
else
let j = i+1
sort2 f i j xs
modified j endix
and unmodified i endix =
if i = endix then
()
else
let j = i+1
let i' = xs.[ i ]
let j' = xs.[ j ]
if f i' j' > 0 then
xs.[ i ] <- j'
xs.[ j ] <- i'
modified j endix
else
unmodified j endix
in unmodified 0 (xs.Length-1)
let bubble_imperitive f (xs:'a array) =
let mutable bModified = true
let mutable endix = xs.Length - 1
while bModified do
bModified <- false
endix <- endix - 1
for i in 0..endix do
let j = i+1
let i' = xs.[ i ]
let j' = xs.[ j ]
if f i' j' > 0 then
xs.[ i ] <- j'
xs.[ j ] <- i'
bModified <- true
done
done
¿Debería ser probablemente una wiki de la comunidad? ¿No hay "respuesta" per se? – Anthony
@ Anthony, espero que haya un sitio web, pero si no existe, entonces lo haré. – Unknown
Parece que has hecho esta comunidad demasiado pronto - echa un vistazo a pleac-ocaml – Thelema