Reed ya explicó por qué las excepciones .NET se comportan de forma diferente que las excepciones OCaml. En general, las excepciones de .NET son adecuadas solo para situaciones excepcionales y están diseñadas para ese propósito. OCaml tiene un modelo más liviano, por lo que también se utilizan para implementar algunos patrones de flujo de control.
Para dar un ejemplo concreto, en OCaml puede usar la excepción para implementar la ruptura de un bucle. Por ejemplo, supongamos que tiene una función test
que prueba si un número es el número que queremos. Los siguientes itera sobre números del 1 al 100 y los retornos que emparejan primer número:
// Simple exception used to return the result
exception Returned of int
try
// Iterate over numbers and throw if we find matching number
for n in 0 .. 100 do
printfn "Testing: %d" n
if test n then raise (Returned n)
-1 // Return -1 if not found
with Returned r -> r // Return the result here
Para implementar esto sin excepciones, usted tiene dos opciones. Se podría escribir una función recursiva que tiene el mismo comportamiento - si llama find 0
(y se compila a esencialmente el mismo código IL como el uso de return n
dentro for
bucle en C#):
let rec find n =
printfn "Testing: %d" n
if n > 100 then -1 // Return -1 if not found
elif test n then n // Return the first found result
else find (n + 1) // Continue iterating
La codificación usando funciones recursivas puede sea un poco largo, pero también puede usar funciones estándar proporcionadas por la biblioteca F #. A menudo, esta es la mejor manera de reescribir el código que usaría excepciones OCaml para el flujo de control. En este caso, se puede escribir:
// Find the first value matching the 'test' predicate
let res = seq { 0 .. 100 } |> Seq.tryFind test
// This returns an option type which is 'None' if the value
// was not found and 'Some(x)' if the value was found.
// You can use pattern matching to return '-1' in the default case:
match res with
| None -> -1
| Some n -> n
Si no está familiarizado con los tipos de opción, a continuación, echar un vistazo a algún material introductorio. F# wikibook has a good tutorial y MSDN documentation has useful examples también.
El uso de una función apropiada del módulo Seq
a menudo hace que el código sea mucho más corto, por lo que es preferible. Puede ser un poco menos eficiente que usar recursividad directamente, pero en la mayoría de los casos, no necesita preocuparse por eso.
EDIT: Tenía curiosidad sobre el rendimiento real. La versión que usa Seq.tryFind
es más eficiente si la entrada se genera de forma lenta secuencia seq { 1 .. 100 }
en lugar de una lista [ 1 .. 100 ]
(debido a los costos de asignación de lista). Con estos cambios, y test
función que devuelve el elemento 25, el tiempo necesario para ejecutar el código 100000 veces en mi máquina es:
exceptions 2.400sec
recursion 0.013sec
Seq.tryFind 0.240sec
Ésta es la muestra extremadamente trivial, así que creo que la solución usando Seq
no funcionarán generalmente 10 veces más lento que el código equivalente escrito mediante recursión. La desaceleración probablemente se deba a la asignación de estructuras de datos adicionales (objetos que representan la secuencia, cierres, ...) y también a la indirección adicional (el código necesita numerosas llamadas a métodos virtuales, en lugar de simples operaciones y saltos numéricos). Sin embargo, las excepciones son aún más costosas y no hacen que el código sea más corto o más legible ...
Hubo una [discusión previa sobre la ligereza de las excepciones OCaml] (http://stackoverflow.com/questions/8564025/ocaml-internals-exceptions) –
@JeffreyScofield gracias, sin embargo, estoy más interesado en los aspectos relacionados con CLR . – gliderkite