martes, 10 de septiembre de 2024

Descubre y Mejora tus Habilidades de Programación con Exercism.org


Si estás buscando una forma práctica y desafiante de mejorar tus habilidades de programación, Exercism.org es una plataforma que no querrás pasar por alto. Diseñada tanto para principiantes como para desarrolladores experimentados, Exercism ofrece ejercicios de codificación en más de 50 lenguajes de programación.

Cada ejercicio te permite resolver problemas reales y recibir feedback de mentores experimentados. Lo mejor de todo es que puedes aprender a tu propio ritmo, sin plazos ni restricciones.

Ya sea que estés aprendiendo un nuevo lenguaje o profundizando en tus conocimientos actuales, Exercism.org es un recurso invaluable para cualquier programador.

Dejo link:  https://exercism.org/

lunes, 9 de septiembre de 2024

Comprendiendo `ref`, `out` e `in` en C#


ref, out, e in permiten controlar cómo se pasan los parámetros a los métodos, pero no es fácil recordar las diferencias de cada uno. Bueno, vamos a ver si este post puede dar luz a este asunto. 

Cuando llamamos a un método en C#, los parámetros se pueden pasar de diferentes maneras: por valor o por referencia. Entender la diferencia es crucial para controlar cómo los métodos interactúan con las variables que reciben.

  • Por Valor: El método recibe una copia del valor, por lo que los cambios dentro del método no afectan la variable original.
  • Por Referencia: El método recibe una referencia a la variable original, permitiendo modificar su valor.


ref: Pasar Parámetros por Referencia para Modificar


La palabra clave `ref` se utiliza cuando se quiere que un método tenga la capacidad de modificar la variable que se le pasa como parámetro. El parámetro debe estar inicializado antes de pasar al método.


void Incrementar(ref int numero) {

    numero++;

}


int valor = 5;

Incrementar(ref valor);

// valor ahora es 6


`ref` en C# es similar a pasar un parámetro por referencia utilizando `&` en C++, lo que permite la modificación del valor original dentro del método.

`out` se usa cuando un método necesita devolver un valor a través de un parámetro. A diferencia de `ref`, el parámetro no necesita estar inicializado antes de pasarse al método.

Por ejemplo: 

void AsignarValor(out int resultado) {

    resultado = 10;

}


int valor;

AsignarValor(out valor);

// valor ahora es 10


in: Pasar Parámetros por Referencia para Solo Lectura

La palabra clave `in` se usa para pasar un parámetro por referencia de manera que el método pueda leer el valor, pero no modificarlo. Esto es útil para evitar copias innecesarias de grandes estructuras de datos, mientras se asegura que no se alterarán.


void MostrarValor(in int valor) {

    Console.WriteLine(valor);

}


int numero = 5;

MostrarValor(in numero);

// Imprime 5


`in` se puede comparar con el uso de referencias constantes en C++ (`const int&`), donde la función puede leer el valor, pero no modificarlo.


ref` y out permiten que un método modifique el valor de un parámetro, pero `ref` requiere que la variable esté inicializada antes de ser pasada, mientras que `out` no.

in permite la pasada por referencia para mejorar la eficiencia (especialmente con grandes estructuras), pero no permite la modificación del valor, mientras que `ref` sí.

Debemos usar ref` cuando necesites que el método pueda leer y modificar el valor original.  Y out cuando quieras devolver un valor a través de un parámetro que no necesita estar inicializado.

Y  in, cuando se quiere pasar grandes estructuras de datos por referencia para evitar copias, pero asegurándote de que el método no pueda modificar el valor.

Este conocimiento permite a los desarrolladores de C# escribir métodos más flexibles y eficientes, optimizando el rendimiento y controlando cómo se manipulan los datos.

viernes, 6 de septiembre de 2024

Módulo de opciones



import gleam/io

import gleam/option.{type Option, None, Some}


pub type Person {

  Person(name: String, pet: Option(String))

}


pub fn main() {

  let person_with_pet = Person("Al", Some("Nubi"))

  let person_without_pet = Person("Maria", None)


  io.debug(person_with_pet)

  io.debug(person_without_pet)

}


Person(name: "Al", pet: Some("Nubi"))
Person(name: "Maria", pet: None)


Los valores en Gleam no son nulos, por lo que el módulo de la biblioteca estándar gleam/option define el tipo Option de Gleam, que se puede utilizar para representar un valor que está presente o ausente.

El tipo option es muy similar al tipo result, pero no tiene un valor de error. Algunos lenguajes tienen funciones que devuelven una opción cuando no hay detalles de error adicionales que proporcionar, pero Gleam siempre utiliza result. Esto hace que todas las funciones falibles sean consistentes y elimina cualquier código repetitivo que se requeriría al mezclar funciones que utilizan cada tipo.

seekg, seekp, tellg y tellp de C++




No soy mucho de utilizar archivos pero cuando tengo que hacer algo tengo que investigar las funciones seek* y tell*. Este post tiene como objetivo que me aprenda esto, por favooor!!


seekg` y `tellg:  Estas funciones se utilizan para manejar la posición del cursor de lectura en un flujo de entrada (`istream`).


seekg (seek get):

Cambia la posición del cursor de lectura en un flujo de entrada.

Su sintaxis es :  stream.seekg(position); o stream.seekg(offset, direction);

Y los Parámetros:

  1.     position: Especifica la nueva posición absoluta.
  2.     offset: Desplazamiento en bytes desde el punto indicado por `direction`.
  3.     direction: Puede ser `std::ios::beg` (inicio del archivo), `std::ios::cur` (posición actual), o `std::ios::end` (final del archivo).

Veamos un ejemplo: 

std::ifstream file("example.txt");

file.seekg(10, std::ios::beg); // Mueve el cursor de lectura al décimo byte desde el inicio del archivo


tellg (tell get):

Retorna la posición actual del cursor de lectura en un flujo de entrada.

Su sintaxis es :stream.tellg();

Devuelve la posición actual como un valor de tipo std::streampos.

Veamos un ejemplo: 

std::ifstream file("example.txt");

std::streampos pos = file.tellg(); // Obtiene la posición actual del cursor de lectura


seekp y tellp: Estas funciones se utilizan para manejar la posición del cursor de escritura en un flujo de salida (`ostream`).


seekp (seek put): Cambia la posición del cursor de escritura en un flujo de salida.

Su sintaxis es stream.seekp(position); o stream.seekp(offset, direction);

Los parámetros son:

  1.    position: Especifica la nueva posición absoluta.
  2.    offset: Desplazamiento en bytes desde el punto indicado por `direction`.
  3.    direction: Puede ser `std::ios::beg` (inicio del archivo), `std::ios::cur` (posición actual), o `std::ios::end` (final del archivo).

Veamos un ejemplo: 


std::ofstream file("example.txt");

file.seekp(5, std::ios::beg); // Mueve el cursor de escritura al quinto byte desde el inicio del archivo


tellp (tell put): Retorna la posición actual del cursor de escritura en un flujo de salida.

Su sintaxis: stream.tellp();

Devuelve la posición actual como un valor de tipo `std::streampos`.


Un ejemplo: 

std::ofstream file("example.txt");

std::streampos pos = file.tellp(); // Obtiene la posición actual del cursor de escritura


Ambos pares de funciones permiten manipular y consultar la posición de los cursores dentro de los archivos o flujos, pero seekg/tellg están destinados a la lectura, mientras que seekp/tellp están destinados a la escritura. Vamos a ver si me lo aprendo. 

Módulo Dict de Gleam

 


import gleam/dict

import gleam/io


pub fn main() {

  let scores = dict.from_list([#("Lucy", 13), #("Drew", 15)])

  io.debug(scores)


  let scores =

    scores

    |> dict.insert("Bushra", 16)

    |> dict.insert("Darius", 14)

    |> dict.delete("Drew")

  io.debug(scores)

}

El resultado : 

dict.from_list([#("Drew", 15), #("Lucy", 13)])
dict.from_list([#("Darius", 14), #("Bushra", 16), #("Lucy", 13)])


El módulo estándar gleam/dict define el tipo Dict de Gleam y las funciones para trabajar con él. Un dict es una colección de claves y valores que otros lenguajes pueden llamar un mapa hash o una tabla.

new y from_list se pueden utilizar para crear nuevos dicts.

insert y delete se utilizan para agregar y eliminar elementos de un dict.

Al igual que las listas, los dicts son inmutables. Insertar o eliminar un elemento de un dict devolverá un nuevo dict con el elemento agregado o eliminado.

Los dicts no están ordenados. Si parece que los elementos de un dict están en un orden determinado, es incidental y no se debe confiar en ello. Cualquier orden puede cambiar sin previo aviso en futuras versiones o en diferentes entornos de ejecución.

martes, 3 de septiembre de 2024

Punteros inteligentes en C++


En C++, el manejo de memoria es crucial para el rendimiento y la estabilidad de las aplicaciones. Los punteros tradicionales (`raw pointers`) requieren que los desarrolladores gestionen manualmente la asignación y liberación de memoria, lo que puede llevar a errores como fugas de memoria, doble liberación, y accesos a memoria inválida.

Para facilitar esta gestión y reducir errores, C++ introduce los punteros inteligentes o smart pointers, que son clases que actúan como punteros, pero con capacidades adicionales para manejar automáticamente la memoria. Desde C++11, la Biblioteca Estándar de C++ incluye varias implementaciones de punteros inteligentes, que simplifican la vida del desarrollador.

Los punteros inteligentes son objetos que encapsulan un puntero nativo (`raw pointer`) y aseguran que la memoria se libere adecuadamente cuando ya no se necesita. Se encargan automáticamente de la destrucción del objeto apuntado, previniendo fugas de memoria.

En C++ estándar, existen tres tipos principales de punteros inteligentes:

  • std::unique_ptr
  • std::shared_ptr
  • std::weak_ptr

std::unique_ptr: Representa la propiedad exclusiva de un objeto. Solo un std::unique_ptr puede apuntar a un objeto en un momento dado.

Cuando el `std::unique_ptr` se destruye (por ejemplo, cuando sale del ámbito), la memoria del objeto que apunta se libera automáticamente.

Es ideal para recursos que no deben ser compartidos entre diferentes partes del programa.


#include <iostream>

#include <memory>


void uniquePtrExample() {

    std::unique_ptr<int> ptr = std::make_unique<int>(42);

    std::cout << "Valor: " << *ptr << std::endl;

}


En este ejemplo, `ptr` es un puntero exclusivo a un entero que contiene el valor 42. No se puede copiar o compartir, solo se puede transferir mediante `std::move`.


std::shared_ptr permite que varios punteros compartan la propiedad de un objeto. La memoria se libera cuando el último `std::shared_ptr` que apunta al objeto se destruye.

Utiliza un contador de referencias para rastrear cuántos `std::shared_ptr` apuntan al mismo objeto. Cuando el contador llega a cero, se libera la memoria.

Es  útil cuando múltiples partes del código necesitan acceso compartido a un recurso.


#include <iostream>

#include <memory>


void sharedPtrExample() {

    std::shared_ptr<int> ptr1 = std::make_shared<int>(42);

    std::shared_ptr<int> ptr2 = ptr1; // Compartir propiedad


    std::cout << "Valor: " << *ptr1 << std::endl;

    std::cout << "Referencia count: " << ptr1.use_count() << std::endl;

}


En este ejemplo, `ptr1` y `ptr2` comparten la propiedad del mismo entero. La memoria se liberará solo cuando ambos punteros se destruyan.


Y std::weak_ptr es un puntero no propietario que se utiliza junto con `std::shared_ptr` para evitar ciclos de referencia que podrían impedir la liberación de memoria.

No incrementa el contador de referencias del objeto. Se utiliza principalmente para obtener acceso temporal al objeto sin prolongar su vida.

Ideal para resolver problemas de ciclos de referencias que pueden ocurrir con `std::shared_ptr`.


#include <iostream>

#include <memory>


void weakPtrExample() {

    std::shared_ptr<int> ptr1 = std::make_shared<int>(42);

    std::weak_ptr<int> weakPtr = ptr1;


    if (auto sharedPtr = weakPtr.lock()) {

        std::cout << "Valor: " << *sharedPtr << std::endl;

    } else {

        std::cout << "El objeto ya no existe." << std::endl;

    }

}


En este ejemplo, `weakPtr` no impide que el `shared_ptr` destruya el objeto cuando sale del ámbito, pero permite acceder temporalmente al objeto si aún existe.

Los punteros inteligentes son una característica poderosa de C++ que facilita la gestión de memoria y ayuda a prevenir errores comunes asociados con los punteros tradicionales. `std::unique_ptr`, `std::shared_ptr` y `std::weak_ptr` cubren una variedad de casos de uso, desde la propiedad exclusiva hasta la compartida y el acceso temporal, ofreciendo soluciones robustas para la administración de recursos en C++.

domingo, 1 de septiembre de 2024

El comando dotnet parte 2


Antes de empezar lee la parte 1. No te adelantes. 

dotnet new: Crear nuevos proyectos a partir de plantillas

El comando `dotnet new` se utiliza para crear un nuevo proyecto o solución desde una plantilla predeterminada. .NET ofrece una variedad de plantillas que cubren diferentes tipos de aplicaciones, como aplicaciones de consola, aplicaciones web, bibliotecas de clases, y más.

Veamos unos ejemplos: 

dotnet new console -n MyConsoleApp

Este comando crea un nuevo proyecto de consola en C# con el nombre `MyConsoleApp`.

  -n o --name: Especifica el nombre del proyecto o solución.
  -o o --output: Define el directorio donde se creará el proyecto.
  --list: Muestra todas las plantillas disponibles.

Plantillas comunes:
  • console: Aplicación de consola.
  • classlib: Biblioteca de clases.
  • web: Aplicación web ASP.NET Core.
  • mvc: Aplicación ASP.NET Core MVC.
  • blazorserver: Aplicación Blazor Server.

El comando `dotnet build` compila el proyecto y todas sus dependencias, generando los binarios necesarios para la ejecución. Es útil para verificar que el código se puede compilar correctamente y que no hay errores de compilación.

dotnet build

Este comando compila el proyecto en el directorio actual.

-c o --configuration: Especifica la configuración de compilación (por ejemplo, `Debug` o `Release`). El valor predeterminado es `Debug`.
-o o --output: Especifica el directorio de salida para los archivos compilados.

Veamos un ejemplo: 

dotnet build -c Release

dotnet run: Ejecutar un proyecto

El comando `dotnet run` permite compilar y ejecutar una aplicación directamente desde la línea de comandos. Es particularmente útil durante el desarrollo, ya que simplifica el ciclo de construir y ejecutar.
Se escribe: 

dotnet run

Este comando compila y ejecuta el proyecto en el directorio actual.
Podemos agregar: 
--project: Permite especificar un proyecto o solución para ejecutar si estás en un directorio con múltiples proyectos.
 -c o --configuration: Ejecuta el proyecto en la configuración especificada (`Debug`, `Release`, etc.).

Por ejemplo: 

dotnet run --project MyConsoleApp/MyConsoleApp.csproj

dotnet test: Ejecutar pruebas unitarias

El comando `dotnet test` se utiliza para ejecutar pruebas unitarias en un proyecto de pruebas. Este comando ejecuta todas las pruebas definidas en el proyecto y proporciona un informe con los resultados.

Un ejemplo de uso : 

dotnet test

Ejecuta todas las pruebas en el proyecto o solución del directorio actual.
Podemos agregar:
--filter: Filtra las pruebas que se ejecutarán basado en criterios específicos (por ejemplo, nombre del test, categoría).
-l o --logger: Especifica un logger para formatear los resultados de las pruebas.
 --no-build: Evita la compilación del proyecto antes de ejecutar las pruebas, útil si el proyecto ya está compilado.


dotnet test --filter "FullyQualifiedName~MyNamespace.MyTestClass"

dotnet publish: Publicar un proyecto

El comando `dotnet publish` compila el proyecto y empaqueta los archivos necesarios para su despliegue en un entorno específico. Es el paso final antes de implementar la aplicación en producción.

Por ejemplo:

dotnet publish -c Release -o ./publish


Compila el proyecto en modo `Release` y publica los archivos en el directorio `./publish`.

Podemos agregar:
-r o --runtime: Especifica el runtime de destino para la publicación (por ejemplo, `win-x64`, `linux-x64`).
 --self-contained: Genera una publicación que incluye el runtime .NET, lo que permite ejecutar la aplicación en una máquina sin .NET instalado.
-p o --property: Define propiedades adicionales de MSBuild durante la publicación.

Por ejemplo:

dotnet publish -c Release -r win-x64 --self-contained

Estos comandos cubren las operaciones básicas más comunes que los desarrolladores necesitan realizar durante el desarrollo de aplicaciones .NET. Con esta base, podremos gestionar fácilmente proyectos, compilarlos, ejecutarlos, probarlos y publicarlos.

El Módulo Result de Gleam



import gleam/int

import gleam/io

import gleam/result


pub fn main() {

  io.println("=== map ===")

  let _ = io.debug(result.map(Ok(1), fn(x) { x * 2 }))

  let _ = io.debug(result.map(Error(1), fn(x) { x * 2 }))


  io.println("=== try ===")

  let _ = io.debug(result.try(Ok("1"), int.parse))

  let _ = io.debug(result.try(Ok("no"), int.parse))

  let _ = io.debug(result.try(Error(Nil), int.parse))


  io.println("=== unwrap ===")

  io.debug(result.unwrap(Ok("1234"), "default"))

  io.debug(result.unwrap(Error(Nil), "default"))


  io.println("=== pipeline ===")

  int.parse("-1234")

  |> result.map(int.absolute_value)

  |> result.try(int.remainder(_, 42))

  |> io.debug

}

=== map ===
Ok(2)
Error(1)
=== try ===
Ok(1)
Error(Nil)
Error(Nil)
=== unwrap ===
"1234"
"default"
=== pipeline ===
Ok(16)

El módulo de la biblioteca estándar gleam/result contiene funciones para trabajar con resultados. Los programas Gleam harán un uso intensivo de este módulo para evitar expresiones de caso anidadas excesivas al llamar a múltiples funciones que pueden fallar.

  • map actualiza un valor contenido dentro de Ok de un resultado llamando a una función dada sobre él. Si el resultado es un error, no se llama a la función.
  • try ejecuta una función que devuelve un resultado sobre el valor contenido dentro de Ok de un resultado. Si el resultado es un error, no se llama a la función. Esto es útil para encadenar varias llamadas de función que pueden fallar, una tras otra, deteniéndose en el primer error.
  • unwrap extrae el valor de éxito de un resultado o devuelve un valor predeterminado si el resultado es un error.

Las funciones de resultado se utilizan a menudo con canalizaciones para encadenar varias llamadas a funciones que devuelven resultados.

viernes, 30 de agosto de 2024

Primary constructors in C# 12 very similar to Scala constructors


With the arrival of C# 12, the language has introduced several new features that make programming more concise and expressive. Among them, Primary Constructors stand out for simplifying the way classes initialize their members. This feature is similar to what Scala has long offered with its primary constructors.

Primary Constructors allow a constructor to be defined directly in the class declaration, which reduces the need for repetitive code and simplifies the definition of immutable classes.


public class Person(string name, int age)

{

    public string Name { get; } = name;

    public int Age { get; } = age;

}


In this example, the `Person` class has a primary constructor that takes two parameters: name and age. And the Name and Age properties are initialized directly from the constructor parameters, making it cleaner and more concise.

Scala has offered a similar concept since its earliest versions. In Scala, the main constructor parameters are defined along with the class and can be used to initialize the class members directly.


class Person(val name: String, val age: Int)


Both C# 12 and Scala eliminate the need to define a separate constructor and assign the parameters to the class properties manually.

But in C#, properties are assigned inside the class body using an explicit assignment (= name;), while in Scala, this assignment is implicit. And in Scala, you can control the visibility of constructor parameters (val or var) more directly. In C#, the default pattern is to create immutable properties with get; only.

Scala, being a more functional programming oriented language, offers features such as eliminating the need for {} braces for simple class bodies, while C# remains more verbose in its syntax.

In conclusion, the addition of Primary Constructors in C# 12 is a step in the right direction, making the language more expressive and less verbose, approaching the simplicity that Scala has offered for years. This parallel not only demonstrates the influence of functional languages on more traditional languages like C#, but also highlights the trend toward more concise, declarative programming.

And with each passing day I see C# becoming more like Scala ...

martes, 27 de agosto de 2024

Lambdas en C++


El lenguaje C++ es conocido por su potencia y flexibilidad, y una de las características que refuerza esta reputación es la introducción de las expresiones lambda. Estas funciones anónimas y ligeras permiten escribir código más limpio y conciso, especialmente en escenarios donde se necesitan funciones pequeñas y de un solo uso. 

Una lambda es una función anónima que se puede definir en línea en el lugar donde se utiliza. Las lambdas permiten crear pequeñas funciones sin necesidad de nombrarlas o declararlas previamente. Fueron introducidas en C++11 y han sido una herramienta clave para el desarrollo moderno en C++.

La sintaxis básica de una lambda en C++ es :


[captura](parametros) -> tipo_retorno {

    // cuerpo de la lambda

};



  • [captura]: Define cómo la lambda captura las variables del entorno en el que se declara.
  • (parametros): Lista de parámetros que la lambda acepta.
  • -> tipo_retorno: Especifica el tipo de retorno de la lambda (puede omitirse si el compilador puede inferirlo).
  • { cuerpo }: El código que define la función de la lambda.


Veamos un ejemplo simple de una lambda que suma dos números:


#include <iostream>


int main() {

    auto suma = [](int a, int b) -> int {

        return a + b;

    };


    std::cout << "La suma de 3 y 4 es: " << suma(3, 4) << std::endl;

    return 0;

}


En este ejemplo, la lambda captura dos parámetros `a` y `b`, y retorna su suma.

Una de las características más poderosas de las lambdas es su capacidad para capturar variables del entorno donde son definidas. Hay varias formas de hacerlo:

  • Captura por valor: `[x]` captura `x` por valor.
  • Captura por referencia: `[&x]` captura `x` por referencia.
  • Captura todo por valor: `[=]` captura todas las variables que se usan en la lambda por valor.
  • Captura todo por referencia: `[&]` captura todas las variables que se usan en la lambda por referencia.


#include <iostream>


int main() {

    int x = 10;

    int y = 20;


    auto suma = [x, &y]() {

        y = x + y;

    };


    suma();

    std::cout << "El nuevo valor de y es: " << y << std::endl;  // Imprime 30

    return 0;

}


Aquí, `x` se captura por valor, y `y` se captura por referencia, lo que significa que cualquier modificación de `y` dentro de la lambda afecta a `y` fuera de la lambda.

Las lambdas son particularmente útiles cuando se combinan con las funciones de la STL (Standard Template Library) como `std::sort`, `std::for_each`, etc.

Por ejemplo, ordenar un vector de enteros en orden descendente usando `std::sort` y una lambda:


#include <iostream>

#include <vector>

#include <algorithm>


int main() {

    std::vector<int> vec = {3, 1, 4, 1, 5, 9};


    std::sort(vec.begin(), vec.end(), [](int a, int b) {

        return a > b;  // Orden descendente

    });


    for (int n : vec) {

        std::cout << n << " ";  // Imprime: 9 5 4 3 1 1

    }

    return 0;

}


Por defecto, las variables capturadas por valor dentro de una lambda no pueden ser modificadas. Sin embargo, si necesitas modificar las variables capturadas por valor, puedes declarar la lambda como `mutable`:


#include <iostream>


int main() {

    int x = 10;


    auto incrementa = [x]() mutable {

        x++;

        std::cout << "Valor dentro de la lambda: " << x << std::endl;

    };


    incrementa();  // Imprime 11

    std::cout << "Valor fuera de la lambda: " << x << std::endl;  // Imprime 10


    return 0;

}


En este caso, `x` se incrementa dentro de la lambda, pero fuera de ella permanece inalterado.

Las lambdas en C++ son una herramienta poderosa para escribir código más claro y conciso. Facilitan la escritura de funciones pequeñas y de un solo uso y son especialmente útiles cuando se trabaja con funciones de la STL y otras APIs que aceptan funciones como parámetros.

domingo, 25 de agosto de 2024

Módulo List de Gleam


import gleam/io

import gleam/list


pub fn main() {

  let ints = [0, 1, 2, 3, 4, 5]


  io.println("=== map ===")

  io.debug(list.map(ints, fn(x) { x * 2 }))


  io.println("=== filter ===")

  io.debug(list.filter(ints, fn(x) { x % 2 == 0 }))


  io.println("=== fold ===")

  io.debug(list.fold(ints, 0, fn(count, e) { count + e }))


  io.println("=== find ===")

  let _ = io.debug(list.find(ints, fn(x) { x > 3 }))

  io.debug(list.find(ints, fn(x) { x > 13 }))

}

=== map ===
[0, 2, 4, 6, 8, 10]
=== filter ===
[0, 2, 4]
=== fold ===
15
=== find ===
Ok(4)
Error(Nil)

El módulo de la biblioteca estándar gleam/list contiene funciones para trabajar con listas. Es probable que un programa Gleam haga un uso intensivo de este módulo, ya que las distintas funciones sirven como diferentes tipos de bucles sobre listas.

  • map crea una nueva lista ejecutando una función en cada elemento de la lista.
  • filter crea una nueva lista que contiene solo los elementos para los que una función devuelve verdadero.
  • fold combina todos los elementos de una lista en un único valor ejecutando una función de izquierda a derecha en cada elemento, pasando el resultado de la llamada anterior a la siguiente llamada.
  • find devuelve el primer elemento de una lista para el que una función devuelve verdadero.

Vale la pena familiarizarse con todas las funciones de este módulo al escribir código Gleam, ¡las usará mucho!

viernes, 23 de agosto de 2024

lunes, 19 de agosto de 2024

Se encuentran abiertas las inscripciones para los cursos Gugler!!!

¡Tengo grandes noticias! Estoy emocionado de anunciar que ya están abiertas las inscripciones para los tan esperados cursos Gugler. Si estás buscando avanzar en tu carrera, aprender nuevas habilidades, o simplemente profundizar tus conocimientos en áreas tecnológicas, ¡estos cursos son para ti!







Inscripciones abiertas del segundo cuatrimestre 2024. Inscripciones.gugler.com.ar

domingo, 18 de agosto de 2024

El libro de Python


"Te damos la bienvenida a El Libro De Python, un espacio en el que podrás aprender y consultar dudas acerca del lenguaje de programación Python. Nuestro libro es totalmente gratis y abierto, por lo que te invitamos a colaborar con nosotros a través de GitHub."

De esta forma se presenta la pagina "El libro de python", un libro o web (como quieras llamarlo) super recomendado para aprender python. 

Dejo link: 

https://ellibrodepython.com/

viernes, 16 de agosto de 2024

Scripts de Lua en Redis


Redis, conocido por ser un sistema de almacenamiento de datos en memoria altamente rápido, no tiene soporte directo para procedimientos almacenados o funciones como los sistemas de bases de datos relacionales tradicionales. Sin embargo, Redis ofrece características que permiten realizar operaciones complejas y reutilizables de manera similar a los procedimientos almacenados y funciones, principalmente a través de scripts en Lua.

Lua es un lenguaje de scripting ligero y potente, y Redis permite la ejecución de scripts Lua en su entorno. Esto brinda la posibilidad de realizar operaciones más complejas que las que se pueden lograr con los comandos básicos de Redis.


Por qué usar Lua en Redis: 

  • Atomicidad: Los scripts Lua se ejecutan de manera atómica en Redis, lo que significa que ninguna otra operación puede interferir con la ejecución del script.
  • Reutilización: Puedes almacenar y reutilizar scripts Lua para realizar operaciones complejas, lo que es análogo a los procedimientos almacenados.
  • Flexibilidad: Lua te permite hacer uso de la lógica de programación, como condicionales y bucles, directamente dentro de Redis.


Supongamos que queremos implementar un procedimiento que incremente el valor de una clave solo si la clave existe y su valor es mayor que un umbral dado. Este es un típico ejemplo donde un procedimiento almacenado sería útil en un sistema de bases de datos relacional.


-- Script Lua para incrementar un valor si es mayor que un umbral

local current = redis.call('GET', KEYS[1])

if current and tonumber(current) > tonumber(ARGV[1]) then

    return redis.call('INCRBY', KEYS[1], ARGV[2])

else

    return nil

end


Para ejecutar este script en Redis, puedes usar el comando `EVAL`:


EVAL "local current = redis.call('GET', KEYS[1])

if current and tonumber(current) > tonumber(ARGV[1]) then

    return redis.call('INCRBY', KEYS[1], ARGV[2])

else

    return nil

end" 1 mykey 10 5


Este comando recibe los siguientes parametros :

  • 1 indica el número de claves (`mykey`) que el script recibirá.
  • mykey es la clave en Redis que el script verificará y posiblemente incrementará.
  • 10 es el umbral; si el valor actual de `mykey` es mayor que este valor, se incrementará.
  • 5 es la cantidad por la cual se incrementará el valor de `mykey` si la condición se cumple.


Aunque Redis no tiene una noción de funciones al estilo SQL, puedes pensar en los scripts Lua como funciones reutilizables. Si bien Redis no permite definir funciones Lua en el mismo sentido que los procedimientos almacenados en SQL, puedes almacenar el script en Redis y llamarlo repetidamente.


Para almacenar un script:


SCRIPT LOAD "local current = redis.call('GET', KEYS[1])

if current and tonumber(current) > tonumber(ARGV[1]) then

    return redis.call('INCRBY', KEYS[1], ARGV[2])

else

    return nil

end


Esto te devolverá un `sha1` hash del script, que puedes usar para invocarlo nuevamente:


EVALSHA <sha1> 1 mykey 10 5


Mientras que Redis no soporta procedimientos almacenados y funciones en el sentido tradicional de bases de datos relacionales, su capacidad para ejecutar scripts Lua te permite realizar operaciones avanzadas y reutilizables de manera similar. Esta funcionalidad es extremadamente útil cuando necesitas lógica compleja o atomicidad en tus operaciones con Redis.