f#

Elegantes fragmentos de F#



(8)

Actualmente estoy aprendiendo F # bastante intensamente. Realmente me encanta como un lenguaje, simplemente se siente bien y parece que te permite producir un código elegante.

Estoy interesado en encontrar algunos fragmentos de código F # muy agradables que demuestren la elección del idioma, especialmente en comparación con C #. Por ejemplo, me gusta mucho:

#light let ListProduct l = List.fold_left (*) 1 l

Que ingresa una lista de entradas y multiplica cada elemento en la lista, es decir, obtiene el producto de la lista (por ejemplo, una lista de 1,2,3 se calcularía como 1 * 2 * 3 = 6). El equivalente de C # más cercano, utilizando LINQ y conceptos funcionales es el siguiente:

using System; using System.Collections.Generic; using System.Linq; ... public static class ListHelper { public static int ListProduct(List<int> l) { return l.Aggregate(1, (i, j) => i * j); } }

Antes de LINQ eso hubiera sido:

using System; using System.Collections.Generic; ... public static class ListHelper { public static int ListProduct(List<int> l) { int ret = 1; foreach (int i in l) ret *= i; return ret; } }

Ciertamente, no estoy tratando de criticar a C # aquí, creo que es un lenguaje maravilloso, es agradable ver cómo se compara F # y ver cómo puede hacer algunas cosas de manera más elegante. ¿Alguien tiene algo realmente bueno?


F # es un lenguaje de programación funcional y, por lo tanto, es excelente con listas y recursiones.

El siguiente código es una pequeña modificación de parte del tutorial predeterminado F # Project incluido con el paquete de descarga F #. No es nada especial, pero demuestra el mismo código que usted pone arriba para aquellos que se preguntan.

let rec ListProduct xs = match xs with //If xs is an empty list, we have a match with an empty list. Return 1 | [] -> 1 //Otherwise match with an item + the rest of the list. //Return the first item * the rest of the list. | y::ys -> y * ListProduct ys

Este código obviamente no está destinado a dar ningún factor sorpresa como usted mencionó. Pero puedes ver algunos usos geniales de F # en este sitio. Echa un vistazo al solucionador de sudoku en F #. Compare este código con una implementación de C # de un solucionador de Sudoku . El sitio también muestra cómo codificar fácilmente una GUI con F #.

Este sitio le mostrará cómo integrar F # con ASP .Net


Mi favorito es enumerar recursivamente todos los archivos debajo de una carpeta en una expresión de secuencia de cuatro líneas:

open System.IO let rec filesUnderFolder basePath = seq { for file in Directory.GetFiles(basePath) do yield file for subDir in Directory.GetDirectories(basePath) do yield! filesUnderFolder subDir }


Creo que lo elegante del plegado es que puedes "alimentarlo" con cualquier cosa:

//takes a max/min tuple + new value, returns expanded max/min tuple let limits (mn, mx) a = (min mn a, max mx a) //Initialise a test list let lst = [1; 3; 5; -1; -9; 0] //feeds each value in lst to limits - for first call, uses (0, 0) for (mn,mx) List.fold_left limits (0, 0) lst //(two extra functions for following example) let cube x = x * x * x //does this need explaining? let range (a, b) = b - a //returns range of a tuple //particularly sexy with pipe forward operator lst |> List.map cube |> List.fold_left limits (0, 0) |> range



A pesar de la frecuente negatividad que rodea a los ejemplos de Fibonacci. Me gustan bastante. Resulta que la programación "práctica" es a menudo una composición de muchas cosas "poco prácticas". El ejemplo de Fibonacci nos muestra cómo puede ser una declaración doblemente recurrente. Haskell tiene una solución particularmente elegante que nos puede enseñar algo sobre F #:

fibonacci = 0 : 1 : zipWith (+) fibonacci (tail fibonacci)

Antes que nada, estudie este fragmento y consígalo . Fibs se define en términos de sí mismo perezosamente. Podemos obtener un poco de pereza de F # usando secuencias. Usando FSI, aquí está cómo:

> let fibonacci = Seq.unfold (fun (x, y) -> Some(x, (y, x + y))) (0I,1I);;

val fibonacci: seq

> #time;;

-> Temporización ahora

> fibonacci |> Seq.nth 10000;;

Real: 00: 00: 00.027, CPU: 00: 00: 00.031, GC gen0: 0, gen1: 0, gen2: 0

val it: System.Numerics.BigInteger =

{IsEven = falso; IsOne = falso; IsPowerOfTwo = falso; IsZero = falso; Sign = 1;}

Eso es bueno. Incluso para mi modesta estación de trabajo. Es práctico porque hemos definido algo elegante en una línea que supera las implementaciones ingenuas de varias líneas. Hemos aprovechado algunos conceptos prácticos mientras estamos en ello. Currying (como se demostró con el pipeline a Seq.nth) , evaluación perezosa (como se demostró por Seq.unfold) y lambdas (la función anónima dada a Seq.unfold) . También hemos aprovechado la "forma" del cálculo de la secuencia de Fibonacci de una manera no muy parecida a la de Haskell, pero lo suficientemente similar.



Recientemente descubrí cómo usar la sum en colecciones de tipos implementando el operador (+) y la propiedad Zero :

// create record with (+) operator and Zero member type myRec = { v1 : float v2 : float } with static member (+) (l, r) = { v1=l.v1+r.v1; v2=l.v2+r.v2 } static member Zero = { v1=0.0; v2=0.0}

Eso ahorra en serio el tipeo y aumenta la legibilidad en mi código donde uso mucho los registros. fold siempre hizo que mi cabeza girara con tipos más complicados.

El beneficio adicional es que si tiene que agregar un campo, necesita cambiar solo la definición del registro, donde quiera que se use, simplemente sigue.

// list to test it with let l = [{ v1=1.0; v2=2.0}; { v1=2.0; v2=3.0}; { v1=1.0; v2=2.0}] // simple test ************************** l |> List.sum // equivalent old ways l |> List.fold (fun t i -> {v1=t.v1+i.v1; v2=t.v1+i.v1}) {v1=0.0; v2=0.0} // groupBy test *************************** // be aware v1 now holds bogus data though l |> Seq.groupBy (fun r -> r.v1) |> Seq.map (fun (v, s) -> (v, s |> Seq.sum)) // equivalent old ways l |> Seq.groupBy (fun r -> r.v1) |> Seq.map (fun (v, s) -> (v, s |> Seq.fold (fun t i -> {v1=t.v1+i.v1; v2=t.v1+i.v1}) {v1=0.0; v2=0.0}))

Eso asegura un código más legible que tener que usar fold cada vez y mantenerlo más cerca de cómo pienso.