mirror of
https://github.com/adambard/learnxinyminutes-docs.git
synced 2024-11-23 06:03:07 +03:00
630 lines
23 KiB
Markdown
630 lines
23 KiB
Markdown
|
---
|
|||
|
language: F#
|
|||
|
lang: es-es
|
|||
|
contributors:
|
|||
|
- ['Scott Wlaschin', 'http://fsharpforfunandprofit.com/']
|
|||
|
translators:
|
|||
|
- ['Angel Arciniega', 'https://github.com/AngelsProjects']
|
|||
|
filename: 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.
|
|||
|
|
|||
|
- Las llaves no se usan para delimitar bloques de código. En cambio, se usa sangría (como en Python).
|
|||
|
- Los espacios se usan para separar parámetros en lugar de comas.
|
|||
|
|
|||
|
Si quiere probar el siguiente código, puede ir a [tryfsharp.org](http://www.tryfsharp.org/Create) y pegarlo en [REPL](https://es.wikipedia.org/wiki/REPL).
|
|||
|
|
|||
|
```fsharp
|
|||
|
// 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!")
|
|||
|
```
|
|||
|
|
|||
|
## Más información
|
|||
|
|
|||
|
Para más demostraciones de F#, visite el sitio [Try F#](http://www.tryfsharp.org/Learn), o sigue la serie [why use F#](http://fsharpforfunandprofit.com/why-use-fsharp/).
|
|||
|
|
|||
|
Aprenda más sobre F# en [fsharp.org](http://fsharp.org/).
|