From aa51a89522709ec7b824fbf11f896994d2d783a0 Mon Sep 17 00:00:00 2001 From: Angel Arciniega Date: Tue, 18 Sep 2018 21:01:24 -0700 Subject: [PATCH] Add F# tutorial in Spanish New tutorial of F# in spanish from Angel Arciniega --- es-es/fsharp-es.html.markdown | 629 ++++++++++++++++++++++++++++++++++ 1 file changed, 629 insertions(+) create mode 100644 es-es/fsharp-es.html.markdown diff --git a/es-es/fsharp-es.html.markdown b/es-es/fsharp-es.html.markdown new file mode 100644 index 00000000..b7f80c44 --- /dev/null +++ b/es-es/fsharp-es.html.markdown @@ -0,0 +1,629 @@ +--- +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 + [] + 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<_>() + + [] + 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/).