Realización de operaciones en listas

Completado

Si almacena muchos elementos en una lista, a menudo realizará operaciones en parte o en la totalidad de la lista. El módulo de lista contiene muchas operaciones útiles que le permiten hacerlo.

Funciones del módulo de lista

Además de las propiedades, hay un módulo repleto de funciones para realizar en una lista. Las funciones pueden realizar operaciones de uso frecuente, como buscar, filtrar, ordenar, realizar operaciones matemáticas, etc.

Iteración

"Iterar" significa recorrer cada elemento de una lista desde un punto inicial hasta un punto final. Para la iteración hay dos funciones especialmente interesantes:

  • iter(): esta función le permite recorrer en iteración cada elemento de una lista, como se muestra aquí:

    let cards = [ 1 .. 5 ]
    List.iter(fun i -> printfn "%i" i) cards // 1 2 3 4 5
    

    La función iter() toma una función. En el código anterior, se proporciona una función anónima mediante la palabra clave fun. Esta función toma un parámetro que representa el elemento actual a medida que se itera. Este código equivale a escribir el siguiente con un bucle:

    for i in cards do printfn "%i" i
    
  • map(): esta función es similar a iter(), pero permite transformar lo que tiene. Este es un ejemplo:

    type Person = { FirstName: string; LastName: string  }
    let people = [
       { FirstName="Albert"; LastName= "Einstein" }
       { FirstName="Marie"; LastName="Curie" }
    ]
    let nobelPrizeWinners = List.map (fun person -> person.FirstName + person.LastName) people 
    printfn "%A" nobelPrizeWinners // ["Albert Einstein"; "Marie Curie"]
    

    En el código anterior, la lista de objetos Person se transforma en una lista de cadenas.

Filter

La función filter() también toma una función como parámetro, pero su propósito es definir los elementos que se conservarán. Si la evaluación de la expresión es true, se mantiene el elemento. Si la expresión es false, el elemento no formará parte de la lista filtrada. En el ejemplo siguiente, se filtra una lista para mantener solo los elementos cuyo valor es divisible por dos:

let cards = [ 1 .. 5 ]
let filteredList = List.filter(fun i-> i % 2 = 0) cards
List.iter(fun i -> printfn "item %i" i) filteredList // item 2 item 4

Ahora, la lista filteredList solo contiene los elementos que han devuelto true al evaluarse según i % 2 = 0, es decir, 2 y 4.

Sort

Ordenar listas es algo que probablemente haga con frecuencia. Estas son tres funciones que pueden resultar útiles al ordenar listas:

  • sort() ordena de forma ascendente. Este es un ejemplo:

    let list = [2; 1; 5; 3]
    let sortedList = List.sort list // 1 2 3 5 
    
  • sortBy(): con esta función, la idea es indicar una clave por la que ordenar. Supongamos que tiene una lista de personas y cada registro tiene los campos name y age. A continuación, puede indicar que se ordene por age, por ejemplo. Esta función toma una función en la que se señala la clave. Otra clave podría ser la longitud de una cadena, como en este ejemplo:

    let fruits = ["Banana"; "Apple"; "Pineapple"]
    let sortedFruits = List.sortBy (fun (fruit : string) -> fruit.Length) fruits // Apple, Banana, Pineapple
    
  • sortWith(): con esta función, puede proporcionar una función de comparador, ya que es posible que no sea evidente al principio cuál de varios elementos debe ordenarse antes que cualquier otro. A continuación, se ponen ejemplos de código:

    // assume a type like so
    type MagicCreature = { Name : string; Level: int; Attack: int }
    let creatures = [
       { Name="Dragon"; Level=2; Attack=20 }
       { Name="Orc"; Level=1; Attack=5 }
       { Name="Demon"; Level=2; Attack=10 } 
    ]
    
    // comparison function, -1 = less than, 1 = larger than, 0 = equal
    let compareCreatures c1 c2 =
         if c1.Level < c2.Level then -1
         else if c1.Level > c2.Level then 1
         else if c1.Attack < c2.Attack then -1
         else if c1.Attack > c2.Attack then 1
         else 0
    
    let sorted = List.sortWith compareCreatures creatures // { Name="Orc"; Level=1; Attack=5 }, { Name="Demon"; Level=2; Attack=10 }, { Name="Dragon"; Level=2; Attack=20 }
    

    La función de comparación anterior, compareCreatures(), intenta comparar primero mediante Level. Si el nivel es igual, intenta comparar mediante Attack. Devuelve -1 si algo se considera más pequeño, 1 si es mayor y 0 si es igual.

Otra cosa que puede querer hacer es buscar un elemento específico. Para ello, puede elegir entre las funciones siguientes:

  • find(): esta función busca el primer elemento que coincide con una condición determinada. Para usar find(), debe proporcionar una función (predicado) que exprese cómo encontrar el elemento. Este es un ejemplo:

    let list = [1; 2; 3; 4]
    let found = List.find( fun x -> x % 2 = 0) list // 2 - Only the first element that matches the condition is returned.
    
  • tryFind(). Esta función toma una función (predicado) que indica cómo buscar el valor y una lista en la que buscar. Devuelve una opción. A continuación, se muestra cómo puede usarla:

    let findValue aValue aList =
         let found = aList |> List.tryFind(fun item -> item = aValue)
    
         match found with
         | Some value -> printfn "%i" value
         | None -> printfn "Not found"
    
    findValue 1 list // 1
    findValue 5 list // Not found
    

    En el código anterior, envía un valor para compararlo con su lista. Si se encuentra, devuelve Some. Si no se encuentra, devuelve None.

  • tryFindIndex(). Al igual que tryFind(), esta función devuelve una opción y toma una función (predicado) que se evalúa como valor booleano. El código tendría el aspecto siguiente:

    let found = List.tryFindIndex(fun x -> x = 4) list
    match found with
       | Some index -> printfn "%i" index
       | None -> printfn "Not found"
    

Operaciones aritméticas

Realizar operaciones matemáticas en una lista puede ser útil. De las muchas funciones entre las que elegir en la API de listas, estas son las tres más útiles:

  • sum(): con esta función, se recorre en iteración cada elemento para sumar todos los valores de la lista. A continuación, se muestra cómo puede usarla:

    let sum = List.sum [1 .. 5] // sum = 15 
    
  • sumBy(): con esta función, la idea es indicar cómo sumar los valores. Una forma de hacerlo es indicar qué campos sumar, como en el ejemplo siguiente:

    type OrderItem = { Name: string; Cost:int }
    
    let orderItems = [
           { Name="XBox"; Cost=500 }
           { Name="Book"; Cost=10 }
           { Name="Movie ticket"; Cost=7 }
         ]
    
    let sum = List.sumBy(fun item -> item.Cost) orderItems
    printfn "%i" sum // 517
    

    En el código anterior, se indica el campo Cost y cada elemento de ese campo se suma al total.

  • average(): esta función es similar a sum() en la medida que funciona en una lista de números, pero presenta dos diferencias:

    • Espera que los datos sean números de punto flotante, no enteros.
    • Calcula un promedio en lugar de una suma.

    Este es un ejemplo:

    let numbers = [ 1.0; 2.5; 3.0 ]
    let avg = List.average numbers
    printfn "%f" avg // 2.166667
    
  • averageBy(): sumBy(), al igual que averageBy(), toma una función donde se especifica el valor que desea. Este es un ejemplo:

    type WeatherMeasurement = { Date: string; Temperature: float }
    let measurements = [
        { Date="07/20/2021"; Temperature=21.3 }
        { Date="07/21/2021"; Temperature=23.2 }
        { Date="07/22/2021"; Temperature=20.7 }
    ]
    
    let avgBy = List.averageBy(fun m -> m.Temperature) measurements
    printfn "%f" avgBy // 21.733333