// 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!")