Descarga el código: learnfsharp-es.fs
F# es un lenguaje de programación funcional y orientado a objetos. Es gratis y su código fuente está abierto. Se ejecuta en Linux, Mac, Windows y más.
Tiene un poderoso sistema de tipado que atrapa muchos errores de tiempo de compilación, pero usa inferencias de tipados que le permiten ser leídos como un lenguaje dinámico.
La sintaxis de F# es diferente de los lenguajes que heredan de C.
Si quiere probar el siguiente código, puede ir a tryfsharp.org y pegarlo en REPL.
// Los comentarios de una línea se escibren con una doble diagonal
(* Los comentarios multilínea usan parentesis (* . . . *)
-final del comentario multilínea- *)
// ================================================
// Syntaxis básica
// ================================================
// ------ "Variables" (pero no realmente) ------
// La palabra reservada "let" define un valor (inmutable)
let miEntero = 5
let miFlotante = 3.14
let miCadena = "hola" // Tenga en cuenta que no es necesario ningún tipado
// ------ Listas ------
let dosACinco = [2;3;4;5] // Los corchetes crean una lista con
// punto y coma para delimitadores.
let unoACinco = 1 :: dosACinco // :: Crea una lista con un nuevo elemento
// El resultado es [1;2;3;4;5]
let ceroACinco = [0;1] @ dosACinco // @ Concatena dos listas
// IMPORTANTE: las comas no se usan para delimitar,
// solo punto y coma !
// ------ Funciones ------
// La palabra reservada "let" también define el nombre de una función.
let cuadrado x = x * x // Tenga en cuenta que no se usa paréntesis.
cuadrado 3 // Ahora, ejecutemos la función.
// De nuevo, sin paréntesis.
let agregar x y = x + y // ¡No use add (x, y)! Eso significa
// algo completamente diferente.
agregar 2 3 // Ahora, ejecutemos la función.
// Para definir una función en varias líneas, usemos la sangría.
// Los puntos y coma no son necesarios.
let pares lista =
let esPar x = x%2 = 0 // Establece "esPar" como una función anidada
List.filter esPar lista // List.filter es una función de la biblioteca
// dos parámetros: una función que devuelve un
// booleano y una lista en la que trabajar
pares unoACinco // Ahora, ejecutemos la función.
// Puedes usar paréntesis para aclarar.
// En este ejemplo, "map" se ejecuta primero, con dos argumentos,
// entonces "sum" se ejecuta en el resultado.
// Sin los paréntesis, "List.map" se pasará como argumento a List.sum.
let sumaDeCuadradosHasta100 =
List.sum ( List.map cuadrado [1..100] )
// Puedes redirigir la salida de una función a otra con "|>"
// Redirigir datos es muy común en F#, como con los pipes de UNIX.
// Aquí está la misma función sumOfSquares escrita usando pipes
let sumaDeCuadradosHasta100piped =
[1..100] |> List.map cuadrado |> List.sum // "cuadrado" se declara antes
// Puede definir lambdas (funciones anónimas) gracias a la palabra clave "fun"
let sumaDeCuadradosHasta100ConFuncion =
[1..100] |> List.map (fun x -> x*x) |> List.sum
// En F#, no hay palabra clave "return". Una función siempre regresa
// el valor de la última expresión utilizada.
// ------ Coincidencia de patrones ------
// Match..with .. es una sobrecarga de la condición de case/ switch.
let coincidenciaDePatronSimple =
let x = "a"
match x with
| "a" -> printfn "x es a"
| "b" -> printfn "x es b"
| _ -> printfn "x es algo mas" // guion bajo corresponde con todos los demás
// F# no permite valores nulos por defecto - debe usar el tipado de Option
// y luego coincide con el patrón.
// Some(..) y None son aproximadamente análogos a los envoltorios Nullable
let valorValido = Some(99)
let valorInvalido = None
// En este ejemplo, match..with encuentra una coincidencia con "Some" y "None",
// y muestra el valor de "Some" al mismo tiempo.
let coincidenciaDePatronDeOpciones entrada =
match entrada with
| Some i -> printfn "la entrada es un int=%d" i
| None -> printfn "entrada faltante"
coincidenciaDePatronDeOpciones validValue
coincidenciaDePatronDeOpciones invalidValue
// ------ Viendo ------
// Las funciones printf/printfn son similares a las funciones
// Console.Write/WriteLine de C#.
printfn "Imprimiendo un int %i, a float %f, a bool %b" 1 2.0 true
printfn "Un string %s, y algo generico %A" "hola" [1;2;3;4]
// También hay funciones printf/sprintfn para formatear datos
// en cadena. Es similar al String.Format de C#.
// ================================================
// Mas sobre funciones
// ================================================
// F# es un verdadero lenguaje funcional - las funciones son
// entidades de primer nivel y se pueden combinar fácilmente
// para crear construcciones poderosas
// Los módulos se utilizan para agrupar funciones juntas.
// Se requiere sangría para cada módulo anidado.
module EjemploDeFuncion =
// define una función de suma simple
let agregar x y = x + y
// uso básico de una función
let a = agregar 1 2
printfn "1+2 = %i" a
// aplicación parcial para "hornear en" los parámetros (?)
let agregar42 = agregar 42
let b = agregar42 1
printfn "42+1 = %i" b
// composición para combinar funciones
let agregar1 = agregar 1
let agregar2 = agregar 2
let agregar3 = agregar1 >> agregar2
let c = agregar3 7
printfn "3+7 = %i" c
// funciones de primer nivel
[1..10] |> List.map agregar3 |> printfn "la nueva lista es %A"
// listas de funciones y más
let agregar6 = [agregar1; agregar2; agregar3] |> List.reduce (>>)
let d = agregar6 7
printfn "1+2+3+7 = %i" d
// ================================================
// Lista de colecciones
// ================================================
// Il y a trois types de collection ordonnée :
// * Les listes sont les collections immutables les plus basiques
// * Les tableaux sont mutables et plus efficients
// * Les séquences sont lazy et infinies (e.g. un enumerator)
//
// Des autres collections incluent des maps immutables et des sets
// plus toutes les collections de .NET
module EjemplosDeLista =
// las listas utilizan corchetes
let lista1 = ["a";"b"]
let lista2 = "c" :: lista1 // :: para una adición al principio
let lista3 = lista1 @ lista2 // @ para la concatenación
// Lista de comprensión (alias generadores)
let cuadrados = [for i in 1..10 do yield i*i]
// Generador de números primos
let rec tamiz = function
| (p::xs) -> p :: tamiz [ for x in xs do if x % p > 0 then yield x ]
| [] -> []
let primos = tamiz [2..50]
printfn "%A" primos
// coincidencia de patrones para listas
let listaDeCoincidencias unaLista =
match unaLista with
| [] -> printfn "la lista esta vacia"
| [primero] -> printfn "la lista tiene un elemento %A " primero
| [primero; segundo] -> printfn "la lista es %A y %A" primero segundo
| _ -> printfn "la lista tiene mas de dos elementos"
listaDeCoincidencias [1;2;3;4]
listaDeCoincidencias [1;2]
listaDeCoincidencias [1]
listaDeCoincidencias []
// Récursion en utilisant les listes
let rec suma unaLista =
match unaLista with
| [] -> 0
| x::xs -> x + suma xs
suma [1..10]
// -----------------------------------------
// Funciones de la biblioteca estándar
// -----------------------------------------
// mapeo
let agregar3 x = x + 3
[1..10] |> List.map agregar3
// filtrado
let par x = x % 2 = 0
[1..10] |> List.filter par
// mucho más - consulte la documentación
module EjemploDeArreglo =
// los arreglos usan corchetes con barras.
let arreglo1 = [| "a";"b" |]
let primero = arreglo1.[0] // se accede al índice usando un punto
// la coincidencia de patrones de los arreglos es la misma que la de las listas
let coincidenciaDeArreglos una Lista =
match unaLista with
| [| |] -> printfn "la matriz esta vacia"
| [| primero |] -> printfn "el arreglo tiene un elemento %A " primero
| [| primero; second |] -> printfn "el arreglo es %A y %A" primero segundo
| _ -> printfn "el arreglo tiene mas de dos elementos"
coincidenciaDeArreglos [| 1;2;3;4 |]
// La biblioteca estándar funciona como listas
[| 1..10 |]
|> Array.map (fun i -> i+3)
|> Array.filter (fun i -> i%2 = 0)
|> Array.iter (printfn "el valor es %i. ")
module EjemploDeSecuencia =
// Las secuencias usan llaves
let secuencia1 = seq { yield "a"; yield "b" }
// Las secuencias pueden usar yield y
// puede contener subsecuencias
let extranio = seq {
// "yield" agrega un elemento
yield 1; yield 2;
// "yield!" agrega una subsecuencia completa
yield! [5..10]
yield! seq {
for i in 1..10 do
if i%2 = 0 then yield i }}
// prueba
extranio |> Seq.toList
// Las secuencias se pueden crear usando "unfold"
// Esta es la secuencia de fibonacci
let fib = Seq.unfold (fun (fst,snd) ->
Some(fst + snd, (snd, fst + snd))) (0,1)
// prueba
let fib10 = fib |> Seq.take 10 |> Seq.toList
printf "Los primeros 10 fib son %A" fib10
// ================================================
// Tipos de datos
// ================================================
module EejemploDeTipoDeDatos =
// Todos los datos son inmutables por defecto
// las tuplas son tipos anónimos simples y rápidos
// - Usamos una coma para crear una tupla
let dosTuplas = 1,2
let tresTuplas = "a",2,true
// Combinación de patrones para desempaquetar
let x,y = dosTuplas // asignado x=1 y=2
// ------------------------------------
// Los tipos de registro tienen campos con nombre
// ------------------------------------
// Usamos "type" con llaves para definir un tipo de registro
type Persona = {Nombre:string; Apellido:string}
// Usamos "let" con llaves para crear un registro
let persona1 = {Nombre="John"; Apellido="Doe"}
// Combinación de patrones para desempaquetar
let {Nombre=nombre} = persona1 // asignado nombre="john"
// ------------------------------------
// Los tipos de unión (o variantes) tienen un conjunto de elección
// Solo un caso puede ser válido a la vez.
// ------------------------------------
// Usamos "type" con barra/pipe para definir una unión estándar
type Temp =
| GradosC of float
| GradosF of float
// Una de estas opciones se usa para crear una
let temp1 = GradosF 98.6
let temp2 = GradosC 37.0
// Coincidencia de patrón en todos los casos para desempaquetar (?)
let imprimirTemp = function
| GradosC t -> printfn "%f gradC" t
| GradosF t -> printfn "%f gradF" t
imprimirTemp temp1
imprimirTemp temp2
// ------------------------------------
// Tipos recursivos
// ------------------------------------
// Los tipos se pueden combinar recursivamente de formas complejas
// sin tener que crear subclases
type Empleado =
| Trabajador of Persona
| Gerente of Empleado lista
let jdoe = {Nombre="John";Apellido="Doe"}
let trabajador = Trabajador jdoe
// ------------------------------------
// Modelado con tipados (?)
// ------------------------------------
// Los tipos de unión son excelentes para modelar el estado sin usar banderas (?)
type DireccionDeCorreo =
| DireccionDeCorreoValido of string
| DireccionDeCorreoInvalido of string
let intentarEnviarCorreo correoElectronico =
match correoElectronico with // uso de patrones de coincidencia
| DireccionDeCorreoValido direccion -> () // enviar
| DireccionDeCorreoInvalido direccion -> () // no enviar
// Combinar juntos, los tipos de unión y tipos de registro
// ofrece una base excelente para el diseño impulsado por el dominio.
// Puedes crear cientos de pequeños tipos que reflejarán fielmente
// el dominio.
type ArticuloDelCarrito = { CodigoDelProducto: string; Cantidad: int }
type Pago = Pago of float
type DatosActivosDelCarrito = { ArticulosSinPagar: ArticuloDelCarrito lista }
type DatosPagadosDelCarrito = { ArticulosPagados: ArticuloDelCarrito lista; Pago: Pago}
type CarritoDeCompras =
| CarritoVacio // sin datos
| CarritoActivo of DatosActivosDelCarrito
| CarritoPagado of DatosPagadosDelCarrito
// ------------------------------------
// Comportamiento nativo de los tipos
// ------------------------------------
// Los tipos nativos tienen el comportamiento más útil "listo para usar", sin ningún código para agregar.
// * Inmutabilidad
// * Bonita depuración de impresión
// * Igualdad y comparación
// * Serialización
// La impresión bonita se usa con %A
printfn "dosTuplas=%A,\nPersona=%A,\nTemp=%A,\nEmpleado=%A"
dosTuplas persona1 temp1 trabajador
// La igualdad y la comparación son innatas
// Aquí hay un ejemplo con tarjetas.
type JuegoDeCartas = Trebol | Diamante | Espada | Corazon
type Rango = Dos | Tres | Cuatro | Cinco | Seis | Siete | Ocho
| Nueve | Diez | Jack | Reina | Rey | As
let mano = [ Trebol,As; Corazon,Tres; Corazon,As;
Espada,Jack; Diamante,Dos; Diamante,As ]
// orden
List.sort mano |> printfn "la mano ordenada es (de menos a mayor) %A"
List.max mano |> printfn "la carta más alta es%A"
List.min mano |> printfn "la carta más baja es %A"
// ================================================
// Patrones activos
// ================================================
module EjemplosDePatronesActivos =
// F# tiene un tipo particular de coincidencia de patrón llamado "patrones activos"
// donde el patrón puede ser analizado o detectado dinámicamente.
// "clips de banana" es la sintaxis de los patrones activos
// por ejemplo, definimos un patrón "activo" para que coincida con los tipos de "caracteres" ...
let (|Digito|Latra|EspacioEnBlanco|Otros|) ch =
if System.Char.IsDigit(ch) then Digito
else if System.Char.IsLetter(ch) then Letra
else if System.Char.IsWhiteSpace(ch) then EspacioEnBlanco
else Otros
// ... y luego lo usamos para hacer que la lógica de análisis sea más clara
let ImprimirCaracter ch =
match ch with
| Digito -> printfn "%c es un Digito" ch
| Letra -> printfn "%c es una Letra" ch
| Whitespace -> printfn "%c es un Espacio en blanco" ch
| _ -> printfn "%c es algo mas" ch
// ver una lista
['a';'b';'1';' ';'-';'c'] |> List.iter ImprimirCaracter
// -----------------------------------------
// FizzBuzz usando patrones activos
// -----------------------------------------
// Puede crear un patrón de coincidencia parcial también
// Solo usamos un guión bajo en la definición y devolvemos Some si coincide.
let (|MultDe3|_|) i = if i % 3 = 0 then Some MultDe3 else None
let (|MultDe5|_|) i = if i % 5 = 0 then Some MultDe5 else None
// la función principal
let fizzBuzz i =
match i with
| MultDe3 & MultDe5 -> printf "FizzBuzz, "
| MultDe3 -> printf "Fizz, "
| MultDe5 -> printf "Buzz, "
| _ -> printf "%i, " i
// prueba
[1..20] |> List.iter fizzBuzz
// ================================================
// concisión
// ================================================
module EjemploDeAlgoritmo =
// F# tiene una alta relación señal / ruido, lo que permite leer el código
// casi como un algoritmo real
// ------ Ejemplo: definir una función sumaDeCuadrados ------
let sumaDeCuadrados n =
[1..n] // 1) Tome todos los números del 1 al n
|> List.map cuadrado // 2) Elevar cada uno de ellos al cuadrado
|> List.sum // 3) Realiza su suma
// prueba
sumaDeCuadrados 100 |> printfn "Suma de cuadrados = %A"
// ------ Ejemplo: definir una función de ordenación ------
let rec ordenar lista =
match lista with
// Si la lista está vacía
| [] ->
[] // devolvemos una lista vacía
// si la lista no está vacía
| primerElemento::otrosElementos -> // tomamos el primer elemento
let elementosMasPequenios = // extraemos los elementos más pequeños
otrosElementos // tomamos el resto
|> List.filter (fun e -> e < primerElemento)
|> ordenar // y los ordenamos
let elementosMasGrandes = // extraemos el mas grande
otrosElementos // de los que permanecen
|> List.filter (fun e -> e >= primerElemento)
|> ordenar // y los ordenamos
// Combinamos las 3 piezas en una nueva lista que devolvemos
List.concat [elementosMasPequenios; [primerElemento]; elementosMasGrandes]
// prueba
ordenar [1;5;23;18;9;1;3] |> printfn "Ordenado = %A"
// ================================================
// Código asíncrono
// ================================================
module AsyncExample =
// F# incluye características para ayudar con el código asíncrono
// sin conocer la "pirámide del destino"
//
// El siguiente ejemplo descarga una secuencia de página web en paralelo.
open System.Net
open System
open System.IO
open Microsoft.FSharp.Control.CommonExtensions
// Recuperar el contenido de una URL de forma asincrónica
let extraerUrlAsync url =
async { // La palabra clave "async" y llaves
// crear un objeto "asincrónico"
let solicitud = WebRequest.Create(Uri(url))
use! respuesta = solicitud.AsyncGetResponse()
// use! es una tarea asincrónica
use flujoDeDatos = resp.GetResponseStream()
// "use" dispara automáticamente la funcion close()
// en los recursos al final de las llaves
use lector = new IO.StreamReader(flujoDeDatos)
let html = lector.ReadToEnd()
printfn "terminó la descarga %s" url
}
// una lista de sitios para informar
let sitios = ["http://www.bing.com";
"http://www.google.com";
"http://www.microsoft.com";
"http://www.amazon.com";
"http://www.yahoo.com"]
// ¡Aqui vamos!
sitios
|> List.map extraerUrlAsync // crear una lista de tareas asíncrona
|> Async.Parallel // decirle a las tareas que se desarrollan en paralelo
|> Async.RunSynchronously // ¡Empieza!
// ================================================
// Compatibilidad .NET
// ================================================
module EjemploCompatibilidadNet =
// F# puede hacer casi cualquier cosa que C# pueda hacer, y se ajusta
// perfectamente con bibliotecas .NET o Mono.
// ------- Trabaja con las funciones de las bibliotecas existentes -------
let (i1success,i1) = System.Int32.TryParse("123");
if i1success then printfn "convertido como %i" i1 else printfn "conversion fallida"
// ------- Implementar interfaces sobre la marcha! -------
// Crea un nuevo objeto que implemente IDisposable
let crearRecurso name =
{ new System.IDisposable
with member this.Dispose() = printfn "%s creado" name }
let utilizarYDisponerDeRecursos =
use r1 = crearRecurso "primer recurso"
printfn "usando primer recurso"
for i in [1..3] do
let nombreDelRecurso = sprintf "\tinner resource %d" i
use temp = crearRecurso nombreDelRecurso
printfn "\thacer algo con %s" nombreDelRecurso
use r2 = crearRecurso "segundo recurso"
printfn "usando segundo recurso"
printfn "hecho."
// ------- Código orientado a objetos -------
// F# es también un verdadero lenguaje OO.
// Admite clases, herencia, métodos virtuales, etc.
// interfaz de tipo genérico
type IEnumerator<'a> =
abstract member Actual : 'a
abstract MoverSiguiente : unit -> bool
// Clase base abstracta con métodos virtuales
[<AbstractClass>]
type Figura() =
// propiedades de solo lectura
abstract member Ancho : int with get
abstract member Alto : int with get
// método no virtual
member this.AreaDelimitadora = this.Alto * this.Ancho
// método virtual con implementación de la clase base
abstract member Imprimir : unit -> unit
default this.Imprimir () = printfn "Soy una Figura"
// clase concreta que hereda de su clase base y sobrecarga
type Rectangulo(x:int, y:int) =
inherit Figura()
override this.Ancho = x
override this.Alto = y
override this.Imprimir () = printfn "Soy un Rectangulo"
// prueba
let r = Rectangulo(2,3)
printfn "La anchura es %i" r.Ancho
printfn "El area es %i" r.AreaDelimitadora
r.Imprimir()
// ------- extensión de método -------
// Al igual que en C#, F# puede extender las clases existentes con extensiones de método.
type System.String with
member this.EmpiezaConA = this.EmpiezaCon "A"
// prueba
let s = "Alice"
printfn "'%s' empieza con una 'A' = %A" s s.EmpiezaConA
// ------- eventos -------
type MiBoton() =
let eventoClick = new Event<_>()
[<CLIEvent>]
member this.AlHacerClick = eventoClick.Publish
member this.PruebaEvento(arg) =
eventoClick.Trigger(this, arg)
// prueba
let miBoton = new MiBoton()
miBoton.AlHacerClick.Add(fun (sender, arg) ->
printfn "Haga clic en el evento con arg=%O" arg)
miBoton.PruebaEvento("Hola Mundo!")
Para más demostraciones de F#, visite el sitio Try F#, o sigue la serie why use F#.
Aprenda más sobre F# en fsharp.org.
¿Tienes una sugerencia o rectificación? Abre un issue en el repositorio de GitHub, o haz un pull request tu mismo
Originalmente contribuido por Scott Wlaschin, y actualizado por 0 colaborador(es).