Buscar..
Introducción a los pliegues, con un puñado de ejemplos.
Los pliegues son funciones (de orden superior) usadas con secuencias de elementos. Se colapsan seq<'a>
en 'b
donde 'b
es cualquier tipo (posiblemente todavía 'a
). Esto es un poco abstracto, así que veamos ejemplos prácticos concretos.
Cálculo de la suma de todos los números.
En este ejemplo, 'a
es un int
. Tenemos una lista de números y queremos calcular la suma de todos los números. Para sumar los números de la lista [1; 2; 3]
escribimos
List.fold (fun x y -> x + y) 0 [1; 2; 3] // val it : int = 6
Permítanme explicar, porque estamos tratando con listas, usamos fold
en el módulo List
, por List.fold
tanto List.fold
. El primer argumento que fold
quita es una función binaria, la carpeta . El segundo argumento es el valor inicial . fold
comienza a plegar la lista aplicando consecutivamente la función de carpeta a los elementos de la lista comenzando con el valor inicial y el primer elemento. Si la lista está vacía, se devuelve el valor inicial!
La vista general esquemática de un ejemplo de ejecución se ve así:
let add x y = x + y // this is our folder (a binary function, takes two arguments)
List.fold add 0 [1; 2; 3;]
=> List.fold add (add 0 1) [2; 3]
// the binary function is passed again as the folder in the first argument
// the initial value is updated to add 0 1 = 1 in the second argument
// the tail of the list (all elements except the first one) is passed in the third argument
// it becomes this:
List.fold add 1 [2; 3]
// Repeat untill the list is empty -> then return the "inital" value
List.fold add (add 1 2) [3]
List.fold add 3 [3] // add 1 2 = 3
List.fold add (add 3 3) []
List.fold add 6 [] // the list is empty now -> return 6
6
La función List.sum
es aproximadamente List.fold add LanguagePrimitives.GenericZero
donde el cero genérico lo hace compatible con enteros, flotadores, enteros grandes, etc.
Contando elementos en una lista ( count
implementación)
Esto se hace casi de la misma manera que antes, pero al ignorar el valor real del elemento en la lista y en su lugar agregar 1.
List.fold (fun x y -> x + 1) 0 [1; 2; 3] // val it : int = 3
Esto también se puede hacer así:
[1; 2; 3]
|> List.map (fun x -> 1) // turn every elemet into 1, [1; 2; 3] becomes [1; 1; 1]
|> List.sum // sum [1; 1; 1] is 3
Para que puedas definir el count
siguiente manera:
let count xs =
xs
|> List.map (fun x -> 1)
|> List.fold (+) 0 // or List.sum
Encontrando el máximo de la lista.
Esta vez usaremos List.reduce
que es como List.fold
pero sin un valor inicial, como en este caso donde no sabemos cuál es el tipo de los valores que comparamos:
let max x y = if x > y then x else y
// val max : x:'a -> y: 'a -> 'a when 'a : comparison, so only for types that we can compare
List.reduce max [1; 2; 3; 4; 5] // 5
List.reduce max ["a"; "b"; "c"] // "c", because "c" > "b" > "a"
List.reduce max [true; false] // true, because true > false
Encontrar el mínimo de una lista.
Al igual que al encontrar el máximo, la carpeta es diferente
let min x y = if x < y then x else y
List.reduce min [1; 2; 3; 4; 5] // 1
List.reduce min ["a"; "b"; "c"] // "a"
List.reduce min [true; false] // false
Listas de concatenacion
Aquí estamos tomando la lista de listas La función de carpeta es el operador @
// [1;2] @ [3; 4] = [1; 2; 3; 4]
let merge xs ys = xs @ ys
List.fold merge [] [[1;2;3]; [4;5;6]; [7;8;9]] // [1;2;3;4;5;6;7;8;9]
O podría usar operadores binarios como su función de carpeta:
List.fold (@) [] [[1;2;3]; [4;5;6]; [7;8;9]] // [1;2;3;4;5;6;7;8;9]
List.fold (+) 0 [1; 2; 3] // 6
List.fold (||) false [true; false] // true, more on this below
List.fold (&&) true [true; false] // false, more on this below
// etc...
Cálculo del factorial de un número.
La misma idea que al sumar los números, pero ahora los multiplicamos. si queremos el factorial de n
, multiplicamos todos los elementos de la lista [1 .. n]
. El código se convierte en:
// the folder
let times x y = x * y
let factorial n = List.fold times 1 [1 .. n]
// Or maybe for big integers
let factorial n = List.fold times 1I [1I .. n]
Implementando forall
, exists
y contains
la función forall
comprueba si todos los elementos en una secuencia satisfacen una condición. exists
verificación si al menos un elemento en la lista cumple con la condición. Primero necesitamos saber cómo contraer una lista de valores bool
. Bueno, ¡usamos pliegues por supuesto! Los operadores booleanos serán nuestras funciones de carpeta.
Para verificar si todos los elementos en una lista son true
, los &&
con la función &&
con true
como valor inicial.
List.fold (&&) true [true; true; true] // true
List.fold (&&) true [] // true, empty list -> return inital value
List.fold (&&) true [false; true] // false
Del mismo modo, para verificar si un elemento es true
en una lista booleanos, lo colapsamos con el ||
operador con false
como valor inicial:
List.fold (||) false [true; false] // true
List.fold (||) false [false; false] // false, all are false, no element is true
List.fold (||) false [] // false, empty list -> return inital value
Volver a forall
y exists
. Aquí tomamos una lista de cualquier tipo, usamos la condición para transformar todos los elementos a valores booleanos y luego la colapsamos:
let forall condition elements =
elements
|> List.map condition // condition : 'a -> bool
|> List.fold (&&) true
let exists condition elements =
elements
|> List.map condition
|> List.fold (||) false
Para comprobar si todos los elementos en [1; 2; 3; 4] son más pequeños que 5:
forall (fun n -> n < 5) [1 .. 4] // true
Definir el método contains
con exists
:
let contains x xs = exists (fun y -> y = x) xs
O incluso
let contains x xs = exists ((=) x) xs
Ahora verifique si la lista [1 .. 5] contiene el valor 2:
contains 2 [1..5] // true
Implementación reverse
:
let reverse xs = List.fold (fun acc x -> x :: acc) [] xs
reverse [1 .. 5] // [5; 4; 3; 2; 1]
Implementando map
y filter
let map f = List.fold (fun acc x -> List.append acc [f x]) List.empty
// map (fun x -> x * x) [1..5] -> [1; 4; 9; 16; 25]
let filter p = Seq.fold (fun acc x -> seq { yield! acc; if p(x) then yield x }) Seq.empty
// filter (fun x -> x % 2 = 0) [1..10] -> [2; 4; 6; 8; 10]
¿Hay algo que el fold
no pueda hacer? No lo sé realmente
Cálculo de la suma de todos los elementos de una lista
Para calcular la suma de términos (de tipo float, int o entero grande) de una lista de números, es preferible usar List.sum. En otros casos, List.fold es la función que mejor se adapta para calcular dicha suma.
- Suma de numeros complejos
En este ejemplo, declaramos una lista de números complejos y calculamos la suma de todos los términos en la lista.
Al comienzo del programa, agregue una referencia a System.Numerics
abrir System.Numerics
Para calcular la suma, inicializamos el acumulador al número complejo 0.
let clist = [new Complex(1.0, 52.0); new Complex(2.0, -2.0); new Complex(0.0, 1.0)]
let sum = List.fold (+) (new Complex(0.0, 0.0)) clist
Resultado:
(3, 51)
- Suma de números de tipo sindical.
Supongamos que una lista se compone de números de tipo union (float o int) y desea calcular la suma de estos números.
Declara antes del siguiente tipo de número:
type number =
| Float of float
| Int of int
Calcule la suma de números de número de tipo de una lista:
let list = [Float(1.3); Int(2); Float(10.2)]
let sum = List.fold (
fun acc elem ->
match elem with
| Float(elem) -> acc + elem
| Int(elem) -> acc + float(elem)
) 0.0 list
Resultado:
13.5
El primer parámetro de la función, que representa el acumulador, es de tipo float y el segundo parámetro, que representa un elemento en la lista es de tipo de número. Pero antes de agregar, necesitamos usar una coincidencia de patrón y convertir a tipo float cuando elem es de tipo Int.