Translate

miércoles, 4 de diciembre de 2024

C++ : range-based for loop (o foreach para los amigos)


C++ 11 ofrece una forma más sencilla y legible de iterar sobre los elementos de un contenedor, como un vector, list o set. Se introdujo en C++11 con el nombre de "range-based for loop" (pero yo le voy a llamar foreach) y ha ganado popularidad por su simplicidad y expresividad.

La estructura básica es:


for (declaración : contenedor) {

    // cuerpo del ciclo

}

  • declaración: Representa cada elemento del contenedor.
  • contenedor: Es una estructura iterable, como std::vector, std::list, o incluso un arreglo C++.


Imaginemos un vector de enteros. Con foreach, iteramos de esta forma:


#include <iostream>

#include <vector>


int main() {

    std::vector<int> numeros = {1, 2, 3, 4, 5};


    for (int numero : numeros) {

        std::cout << numero << " ";

    }

    return 0;

}


Salida: 1 2 3 4 5


Si deseas modificar los elementos dentro del bucle, usa una referencia (`&`):



#include <iostream>

#include <vector>


int main() {

    std::vector<int> numeros = {1, 2, 3, 4, 5};


    for (int& numero : numeros) {

        numero *= 2; // Duplicar cada valor

    }


    for (int numero : numeros) {

        std::cout << numero << " ";

    }

    return 0;

}



Salida: 2 4 6 8 10


En un std::map, cada elemento es un par clave-valor (std::pair). Puedes acceder a ellos directamente:


#include <iostream>

#include <map>


int main() {

    std::map<std::string, int> edades = {{"Alice", 30}, {"Bob", 25}, {"Charlie", 35}};


    for (const auto& [nombre, edad] : edades) {

        std::cout << nombre << ": " << edad << "\n";

    }

    return 0;

}

Salida:

Alice: 30

Bob: 25

Charlie: 35


Como limitaciones tenemos:

  1. Elementos no modificables: Sin una referencia explícita, no puedes modificar los elementos del contenedor.
  2. No apto para todos los contenedores: Aunque funciona con la mayoría de las estructuras estándar, asegúrate de que el contenedor sea compatible con iteradores.
  3. Iteración sobre arreglos estáticos: También es compatible con arreglos tradicionales:


   int numeros[] = {1, 2, 3, 4};

   for (int numero : numeros) {

       std::cout << numero << " ";

   }


El foreach en C++ simplifica la iteración y mejora la legibilidad del código. 

martes, 3 de diciembre de 2024

Ecto = bases de datos + Elixir



Ecto es la herramienta principal para interactuar con bases de datos en el ecosistema de Elixir. Más que un simple ORM, Ecto combina la construcción de consultas, validaciones y migraciones, brindando una experiencia robusta y funcional.

Ecto es un toolkit para bases de datos en Elixir que se compone de tres partes principales:

  • Ecto.Schema: Define la estructura de los datos.
  • Ecto.Changeset: Maneja validaciones y transformaciones.
  • Ecto.Query: Facilita la construcción de consultas.


Para empezar debemos añadir las dependencias en mix.exs:


defp deps do

  [

    {:ecto_sql, "~> 3.10"},

    {:postgrex, ">= 0.0.0"} # Adaptador para PostgreSQL

  ]

end


Luego debemos configurar el repositorio en config/config.exs:


config :mi_app, MiApp.Repo,

  database: "mi_app_db",

  username: "usuario",

  password: "contraseña",

  hostname: "localhost"


config :mi_app, ecto_repos: [MiApp.Repo]


Crear el repositorio:


mix ecto.gen.repo -r MiApp.Repo


Para luego ejecutar migraciones iniciales:


mix ecto.create


Creamos un módulo para tu esquema, que representa una tabla en la base de datos:


defmodule MiApp.Usuario do

  use Ecto.Schema

  schema "usuarios" do

    field :nombre, :string

    field :email, :string

    field :edad, :integer

    timestamps()

  end

end


Y ahora hagamos consultas básicas con Ecto.Query. Ecto ofrece una sintaxis declarativa para construir consultas SQL. Ejemplo:


import Ecto.Query


# Obtener todos los usuarios

query = from u in "usuarios", select: u

MiApp.Repo.all(query)


# Filtrar usuarios mayores de 18 años

query = from u in MiApp.Usuario, where: u.edad > 18, select: u.nombre

MiApp.Repo.all(query)


Los cambios en los datos se gestionan con Ecto.Changeset, lo que facilita aplicar validaciones.


defmodule MiApp.Usuario do

  use Ecto.Schema

  import Ecto.Changeset


  schema "usuarios" do

    field :nombre, :string

    field :email, :string

    field :edad, :integer

    timestamps()

  end


  def cambios_usuario(usuario, attrs) do

    usuario

    |> cast(attrs, [:nombre, :email, :edad])

    |> validate_required([:nombre, :email])

    |> validate_format(:email, ~r/@/)

    |> validate_number(:edad, greater_than_or_equal_to: 0)

  end

end


Ecto permite definir relaciones como has_many, belongs_to y many_to_many. Veamos un ejemplo: 


defmodule MiApp.Usuario do

  use Ecto.Schema


  schema "usuarios" do

    field :nombre, :string

    has_many :posts, MiApp.Post

    timestamps()

  end

end


defmodule MiApp.Post do

  use Ecto.Schema


  schema "posts" do

    field :titulo, :string

    belongs_to :usuario, MiApp.Usuario

    timestamps()

  end

end


Consultas relacionales:


query = from u in MiApp.Usuario, preload: [:posts]

usuarios = MiApp.Repo.all(query)


Ahora actualización de registros:


usuario = MiApp.Repo.get(MiApp.Usuario, 1)

cambioset = MiApp.Usuario.cambios_usuario(usuario, %{nombre: "Nuevo Nombre"})

MiApp.Repo.update(cambioset)


Y Borramos:


usuario = MiApp.Repo.get(MiApp.Usuario, 1)

MiApp.Repo.delete(usuario)


Las migraciones permiten gestionar cambios en el esquema de la base de datos.


Crear una migración:


mix ecto.gen.migration crea_usuarios


Editar la migración:


defmodule MiApp.Repo.Migrations.CreaUsuarios do

  use Ecto.Migration


  def change do

    create table(:usuarios) do

      add :nombre, :string

      add :email, :string

      add :edad, :integer

      timestamps()

    end

  end

end


Ejecutar migración:

mix ecto.migrate


Ecto transforma la interacción con bases de datos en una experiencia declarativa, segura y extensible. Ya sea que estés manejando datos simples o esquemas complejos, Ecto te da las herramientas para hacerlo de manera eficiente.


lunes, 2 de diciembre de 2024

Hola mundo en Pony


¡Comencemos a programar! Nuestro primer programa será uno muy tradicional. Vamos a imprimir “¡Hola, mundo!”. Primero, crea un directorio llamado helloworld:


mkdir helloworld

cd helloworld

¿Importa el nombre del directorio? Sí, importa. ¡Es el nombre de tu programa! De manera predeterminada, cuando tu programa es compilado, el binario ejecutable resultante tendrá el mismo nombre que el directorio en el que se encuentra tu programa. También puedes establecer el nombre usando las opciones –bin-name o -b en la línea de comandos.

Luego, crea un archivo en ese directorio llamado main.pony.

¿Importa el nombre del archivo? No para el compilador, no. A Pony no le importan los nombres de archivo, excepto que terminen en .pony. ¡Pero podría importarte a ti! Al darle buenos nombres a los archivos, puede ser más fácil encontrar el código que estás buscando más tarde.

En el archivo, pongamos el siguiente código:


actor Main

    new create(env: Env) =>

    env.out.print("¡Hola, mundo!")



Ahora compílarlo:


$ ponyc

Building .

Building builtin

Generating

Optimising

Writing ./helloworld.o

Linking ./helloworld


¡Mira eso! Compiló el directorio actual, ., más las cosas que están incorporadas en Pony, builtin, generó algo de código, lo optimizó, creó un archivo de objeto (no te preocupes si no sabes qué es eso) y lo vinculó a un ejecutable con las bibliotecas que se necesitaban. Si eres un programador de C/C++, todo esto tendrá sentido para ti, de lo contrario, probablemente no lo tenga, pero no importa, puedes ignorarlo.

Espera, ¿también se vinculó? Sí. No necesitarás un sistema de compilación (como make) para Pony. Se encarga de eso por ti (incluso de manejar el orden de las dependencias cuando te vinculas a bibliotecas de C, pero llegaremos a eso más adelante).


Ahora podemos ejecutar el programa:


$ ./helloworld

¡Hola, mundo!

¡Felicitaciones, hemos escrito nuestro primer programa Pony! 

domingo, 1 de diciembre de 2024

Creación de pruebas en Elixir con ExUnit


ExUnit es el framework de pruebas integrado en Elixir, diseñado para ayudarte a escribir pruebas claras y efectivas. Desde pruebas unitarias hasta pruebas más complejas, ExUnit ofrece las herramientas necesarias para asegurar que tu código funcione como esperas.

ExUnit viene incluido con Elixir, por lo que no necesitas instalar dependencias adicionales. Solo asegúrate de que tu entorno de desarrollo esté configurado para ejecutarlas.


ExUnit.start()


Los archivos de pruebas suelen estar en el directorio test/ y deben tener el sufijo _test.exs.


Veamos un ejemplo: 


defmodule MiApp.MiModuloTest do

  use ExUnit.Case


  test "una prueba simple" do

    assert 1 + 1 == 2

  end

end


Para ejecutar las pruebas, utiliza:


mix test


Las aserciones son fundamentales para comprobar el comportamiento esperado. ExUnit ofrece varias:


- assert: Verifica que una condición sea verdadera.

- refute: Verifica que una condición sea falsa.

- assert_raise: Verifica que se lance una excepción específica.


Veamos algunos ejemplos: 


assert String.length("Hola") == 4

refute String.contains?("Hola", "mundo")

assert_raise ArgumentError, fn -> String.to_integer("no_numero") end


ExUnit permite convertir ejemplos en la documentación en pruebas automáticas.


defmodule MiModulo do

  @doc """

  Duplica un número.


  ## Ejemplo


      iex> MiModulo.duplicar(2)

      4


  """

  def duplicar(n), do: n * 2

end


defmodule MiModuloTest do

  use ExUnit.Case

  doctest MiModulo

end


Para organizar tus pruebas, podemos usar describe:


defmodule MiApp.MiModuloTest do

  use ExUnit.Case


  describe "función suma/2" do

    test "suma números positivos" do

      assert MiApp.MiModulo.suma(2, 3) == 5

    end


    test "suma números negativos" do

      assert MiApp.MiModulo.suma(-2, -3) == -5

    end

  end

end


Y podemos usar setup para definir configuraciones comunes:


defmodule MiApp.MiModuloTest do

  use ExUnit.Case


  setup do

    {:ok, numero: 42}

  end


  test "usa datos de setup", %{numero: numero} do

    assert numero == 42

  end

end


Elixir soporta pruebas concurrentes por defecto. Si necesitas pruebas asincrónicas, podemos indícarlo:


defmodule MiApp.AsyncTest do

  use ExUnit.Case, async: true


  test "prueba concurrente" do

    Task.async(fn -> :ok end)

    |> Task.await()

    |> assert == :ok

  end

end


ExUnit facilita capturar salidas a consola y logs:


import ExUnit.CaptureIO


test "captura salida de IO.puts" do

  salida = capture_io(fn -> IO.puts("Hola, mundo!") end)

  assert salida == "Hola, mundo!\n"

end


import ExUnit.CaptureLog


test "captura logs" do

  salida = capture_log(fn -> Logger.info("Esto es un log") end)

  assert salida =~ "Esto es un log"

end


Para ejecutar pruebas individuales, podemos usar:


mix test test/mi_modulo_test.exs:10


ExUnit es una herramienta flexible y poderosa para escribir pruebas en Elixir. Desde las pruebas más básicas hasta configuraciones avanzadas, te ayuda a mantener un código confiable y fácil de mantener.