Translate

miércoles, 27 de septiembre de 2023

Libros gratuitos

 Me llegaron ests libros de web code geeks :

Get Schooled By Web Code Geeks

Download FREE IT Guides!

 

Starting with Kotlin Cheatsheet

Welcome to the Kotlin Cheatsheet! This document aims to provide you with a quick reference guide to Kotlin programming language. Whether you are a beginner getting started with Kotlin or...

 
 

Querying Graphs with Neo4j Cheatsheet

This cheatsheet is your guide to effectively querying graphs using Neo4j. Whether you’re a seasoned database professional looking to expand your skills or a curious enthusiast eager to...

 
 

Getting Started with GraphQL Cheatsheet

In this cheatsheet, we will embark on a journey to explore the core principles of GraphQL and its ecosystem. We'll cover topics such as schema design, querying and mutation operations,...

 
 

Starting with Windows PowerShell Cheatsheet

This cheatsheet provides an overview of the most commonly used PowerShell commands, grouped by category. Whether you’re new to PowerShell or an experienced user, this cheatsheet will...

 
 

Core Python Cheatsheet

This cheatsheet is intended to serve as a quick reference guide for Python programming. It covers some of the most commonly used syntax and features of the language, including data types,...

 
 

Starting with Docker Cheatsheet

This guide aims to provide an overview of the key concepts and features of Docker, along with practical examples and tips for using it effectively. It covers topics such as Docker...

 

martes, 26 de septiembre de 2023

Explorando la Programación Funcional en Scala


Los quiero invitar a una charla que voy a dar para scaLatin, sobre programación funcional. 

Exploraremos el mundo de la programación funcional en Scala a través de escenarios concretos de resolución de problemas. Nos sumergiremos en la elegancia de las funciones lambda y aprenderemos cómo crearlas y aplicarlas para optimizar nuestro código.

Luego aplicaremos pattern matching y descubriremos cómo simplifica el manejo de datos complejos y mejora la legibilidad del código.

A lo largo de nuestro viaje, aprovecharemos el poder de la recursividad para resolver problemas de manera eficiente y demostraremos cómo las mónadas pueden ayudarnos a controlar la complejidad de nuestros programas.

Ya sea que sea nuevo en estos conceptos o busque conocimientos avanzados, esta sesión promete equiparlo con habilidades prácticas y una comprensión más profunda de la programación funcional en Scala.

Dejo links: 

https://www.linkedin.com/events/7112416236554137600/

https://www.meetup.com/scalatin/events/296351847/

lunes, 25 de septiembre de 2023

gofmt de Go


Las cuestiones de formato son las más polémicas pero las menos trascendentes. Las personas pueden adaptarse a diferentes estilos de formato, pero es mejor si no es necesario y se dedica menos tiempo al tema si todos siguen el mismo estilo. El problema es cómo abordar esta utopía sin una larga guía de estilo prescriptiva.

Con Go adoptaron un enfoque inusual y dejaron que la máquina se encargue de la mayoría de los problemas de formato. El programa gofmt (también disponible como go fmt, que opera a nivel de paquete en lugar de a nivel de archivo fuente) lee un programa Go y emite el código fuente en un estilo estándar de sangría y alineación vertical, reteniendo y, si es necesario, reformateando los comentarios. Si desea saber cómo manejar alguna situación de diseño nueva, ejecute gofmt; Si la respuesta no parece correcta, reorganice su programa (o registre un error sobre gofmt), no lo solucione.

Por ejemplo, no es necesario perder tiempo alineando los comentarios en los campos de una estructura. Gofmt lo hará. Dada la declaración


type T struct {

    name string // name of the object

    value int // its value

}


gofmt alineará las columnas:


type T struct {

    name    string // name of the object

    value   int    // its value

}


Todo el código Go en los paquetes estándar ha sido formateado con gofmt.


Quedan algunos detalles de formato:


Sangría: Se usa tabs para la sangría y gofmt las emite de forma predeterminada. Utilice espacios sólo si es necesario.

Longitud de la línea: Go no tiene límite de longitud de línea. Si una línea parece demasiado larga, envuélvala y sangra con una tab adicional.

Paréntesis: Go necesita menos paréntesis que C y Java: las estructuras de control (if, for, switch) no tienen paréntesis en su sintaxis. Además, la jerarquía de precedencia de operadores es más corta y clara, por lo que

x<<8 + y<<16

significa lo que implica el espacio, a diferencia de los otros lenguajes.

Juego de Serie en Clojure


Cuando quiero aprender un nuevo lenguaje desarrollo un juego de series, es decir aparece una serie con un valor faltante y el jugador debe completarlo.

Uno de los requerimientos no funcionales es que se pueda agregar una serie nueva fácilmente.

Vamos a desarrollar este juego en Clojure:

Empecemos desarrollo de la serie, la serie tiene como responsabilidad generarse y si lo hacemos de esta manera podemos utilizar la potencia del polimorfismo, para que no quede acoplado la generación de la serie con el desarrollo del juego:

(ns secuencia-clojure.core

  (:gen-class))


(import '(java.util Scanner))

(def scan (Scanner. *in*))


   

(defn generateSecEven []

  (def seed (rand-int 30))

  (for [x (range seed (+ seed 4))] (* x 2))

)

   

(defn generateSecOdd []

  (def seed (rand-int 30))

  (for [x (range seed (+ seed 4))] (+ (* x 2) 1))

)


(defn generateSecFibo []

  (def seed (rand-int 30))

  (def seed2 (* seed 2))

  (def seed3 (* seed 3))

  (list seed seed seed2 seed3)

)

Y listo!! ahora en el main tenemos que llamar a esta función y ver si esta bien el resultado: 



(defn generateSec []
  (def seed (rand-int 3))
  (cond 
    (= seed 0) (generateSecEven)
    (= seed 1) (generateSecOdd)
    (= seed 2) (generateSecFibo)   
  )
)

(defn printSec [sec]
  (print (nth sec 0))
  (print " ")
  (print (nth sec 1))
  (print " ")
  (print " __ ")
  (print " ")
  (println (nth sec 3))
)

(defn game
  [points]
  (def sec (generateSec))
  (def sol (nth sec 2))
  
  (printSec sec)
  
  (let [data (read-line)]
  (if (= data (str sol))
    (do 
      (println "Correct!! the point is " (str (+ points 1)))
      (game (+ points 1))
    )
    (do 
      (println "Wrong!! the point is " (str (- points 1)))
      (game (- points 1))
    )))
)


(defn -main
  [& args]
  (game 0))

Y eso es todo a jugar se a dicho!!

Dejo el repositorio git: 

viernes, 22 de septiembre de 2023

Módulos en Rust


Hemos visto cómo los bloques impl nos permiten asignar funciones de espacio de nombres a un tipo.

De manera similar, mod nos permite tipos de espacios de nombres y funciones:

mod foo {

    pub fn do_something() {

        println!("In the foo module");

    }

}


mod bar {

    pub fn do_something() {

        println!("In the bar module");

    }

}


fn main() {

    foo::do_something();

    bar::do_something();

}

Los paquetes brindan funcionalidad e incluyen un archivo Cargo.toml que describe cómo crear un paquete de más de 1 caja.

Las cajas son un árbol de módulos, donde una caja binaria crea un ejecutable y una caja de biblioteca se compila en una biblioteca.

Los módulos definen la organización y alcance.


martes, 19 de septiembre de 2023

Cell y RefCell en Rust


Cell y RefCell implementan lo que Rust llama mutabilidad interior: mutación de valores en un contexto inmutable.

La celda se usa normalmente para tipos simples, ya que requiere copiar o mover valores. Los tipos de interiores más complejos suelen utilizar RefCell, que rastrea referencias compartidas y exclusivas en tiempo de ejecución y entra en pánico si se utilizan incorrectamente.


use std::cell::RefCell;

use std::rc::Rc;


#[derive(Debug, Default)]

struct Node {

    value: i64,

    children: Vec<Rc<RefCell<Node>>>,

}


impl Node {

    fn new(value: i64) -> Rc<RefCell<Node>> {

        Rc::new(RefCell::new(Node { value, ..Node::default() }))

    }


    fn sum(&self) -> i64 {

        self.value + self.children.iter().map(|c| c.borrow().sum()).sum::<i64>()

    }

}


fn main() {

    let root = Node::new(1);

    root.borrow_mut().children.push(Node::new(5));

    let subtree = Node::new(10);

    subtree.borrow_mut().children.push(Node::new(11));

    subtree.borrow_mut().children.push(Node::new(12));

    root.borrow_mut().children.push(subtree);


    println!("graph: {root:#?}");

    println!("graph sum: {}", root.borrow().sum());

}


Si estuviéramos usando Cell en lugar de RefCell en este ejemplo, tendríamos que sacar el Nodo de Rc para empujar a los elementos secundarios y luego volver a colocarlo. Esto es seguro porque siempre hay un valor sin referencia en la celda, pero no es ergonómico.

Para hacer cualquier cosa con un Nodo, debe llamar a un método RefCell, generalmente borrow o borrow_mut.

sábado, 16 de septiembre de 2023

Rc en Rust


Rc es un puntero compartido, Rc es de contado por referencias, es decir que va contando las referencias. Se usa cuando se necesita hacer referencia a los mismos datos de varios lugares:

use std::rc::Rc;


fn main() {

    let mut a = Rc::new(10);

    let mut b = Rc::clone(&a);


    println!("a: {a}");

    println!("b: {b}");

}


Rc garantiza que el valor contenido sea válido mientras haya referencias.

Rc en Rust es como std::shared_ptr en C++.

Rc::clone es económico: crea un puntero a la misma asignación y aumenta el recuento de referencias. No realiza una clonación profunda y generalmente se puede ignorar cuando se buscan problemas de rendimiento en el código.

make_mut en realidad clona el valor interno si es necesario (“clonar en escritura”) y devuelve una referencia mutable.

Utilice Rc::strong_count para comprobar el recuento de referencia.

Rc::downgrade le brinda un objeto con recuento débil de referencias para crear ciclos que se eliminarán correctamente (probablemente en combinación con RefCell, en la siguiente post).

viernes, 15 de septiembre de 2023

Y es Erlang tan bueno?


Actualmente, Erlang está ganando mucha popularidad debido a conversaciones entusiastas que pueden llevar a la gente a creer que es más de lo que realmente es. El primer caso de esto está relacionado con las enormes capacidades de escalamiento de Erlang debido a sus procesos livianos. Es cierto que los procesos de Erlang son muy ligeros: puedes tener cientos de miles de ellos existentes al mismo tiempo, pero esto no significa que tengas que usarlos de esa manera sólo porque puedas. Por ejemplo, crear un juego de disparos en el que todo, incluidas las balas, sea su propio actor, es una locura. Lo único que dispararás en un juego como este será tu propio pie. Todavía hay un pequeño costo al enviar un mensaje de actor a actor, y si divides demasiado las tareas, ¡harás las cosas más lentas!

También se dice que Erlang puede escalar de manera directamente proporcional a la cantidad de núcleos que tiene su computadora, pero esto generalmente no es cierto. Es posible, pero la mayoría de los problemas no se comportan. de una manera que te permita ejecutar todo al mismo tiempo.

Hay algo más a tener en cuenta: si bien Erlang hace algunas cosas muy bien, técnicamente todavía es posible obtener los mismos resultados en otros lenguajes. Lo opuesto también es cierto; evaluar cada problema como debe ser y elegir la herramienta adecuada según el problema que se aborda. Erlang no es una solución mágica y será particularmente malo en cosas como procesamiento de imágenes y señales, controladores de dispositivos de sistemas operativos, etc. y brillará en cosas como software grande para uso de servidor (es decir, colas, reducción de mapas), haciendo algo de levantamiento junto con otros lenguajes, implementación de protocolos de nivel superior, etc. Las áreas intermedias dependerán de usted. No necesariamente debes encerrarte en un software de servidor con Erlang: ha habido casos de personas que hacen cosas inesperadas y sorprendentes. Un ejemplo es IANO, un robot creado por el equipo UNICT, que utiliza Erlang por su inteligencia artificial y ganó la medalla de plata en el concurso eurobot de 2009. Otro ejemplo es Wings 3D, un modelador 3D de código abierto (pero no un renderizador) escrito en Erlang y, por lo tanto, multiplataforma.

Dejo link: https://learnyousomeerlang.com/introduction

Transformar el mundo de las aplicaciones de mensajería

 Me llego este mail y se los quiero compartir: 

¿Qué es Erlang?


Erlang es un lenguaje de programación funcional. Si alguna vez ha trabajado con lenguajes imperativos, declaraciones como i++ pueden resultarle normales; en programación funcional no están permitidos. De hecho, ¡está estrictamente prohibido cambiar el valor de cualquier variable! Esto puede sonar extraño al principio, pero si recuerdas tus clases de matemáticas, en realidad así es como las aprendiste:

y = 2

x = y + 3

x = 2 + 3

x = 5

Si hubiera agregado lo siguiente:

x = 5 + 1

x = x

∴ 5 = 6

Habrías estado muy confundido. La programación funcional reconoce esto: si digo que x es 5, entonces lógicamente no puedo afirmar que también sea 6. Esto sería deshonesto. Esta es también la razón por la que una función con el mismo parámetro siempre debería devolver el mismo resultado:

x = add_two_to(3) = 5

∴ x = 5

Las funciones que siempre devuelven el mismo resultado para el mismo parámetro se denominan transparencia referencial. Es lo que nos permite reemplazar add_two_to(3) con 5, ya que el resultado de 3+2 siempre será 5. Eso significa que luego podemos unir docenas de funciones para resolver problemas más complejos mientras nos aseguramos de que nada se rompa. Lógico y limpio ¿no? Sin embargo, hay un problema:


x = today() = 2009/10/22

-- wait a day --

x = today() = 2009/10/23

x = x

∴ 2009/10/22 = 2009/10/23


¡Oh, no! ¡Mis hermosas ecuaciones! ¡De repente todos se equivocaron! ¿Cómo es que mi función arroja un resultado diferente cada día?

Evidentemente, hay algunos casos en los que resulta útil romper la transparencia referencial. Erlang tiene este enfoque muy pragmático con la programación funcional: obedece sus principios más puros (transparencia referencial, evitar datos mutables, etc.), pero aléjate de ellos cuando surgen problemas del mundo real.

Ahora, definimos Erlang como un lenguaje de programación funcional, pero también hay un gran énfasis en la concurrencia y la alta confiabilidad. Para poder realizar docenas de tareas al mismo tiempo, Erlang utiliza el modelo de actor, y cada actor es un proceso separado en la máquina virtual. En pocas palabras, si fueras un actor en el mundo de Erlang, serías una persona solitaria, sentada en una habitación oscura sin ventanas, esperando junto a tu buzón para recibir un mensaje. Una vez que recibes un mensaje, reaccionas de una manera específica: pagas las facturas al recibirlas, respondes a las tarjetas de cumpleaños con una carta de agradecimiento e ignoras las cartas que no puedes entender.

El modelo de actor de Erlang puede imaginarse como un mundo en el que todos están sentados solos en su propia habitación y pueden realizar algunas tareas distintas. Todos se comunican estrictamente escribiendo cartas y listo. Si bien suena como una vida aburrida (y una nueva era para el servicio postal), significa que puedes pedirle a muchas personas que realicen tareas muy específicas por ti, y ninguna de ellas hará algo mal o cometerá errores que tendrán repercusiones en tu vida. el trabajo de otros; es posible que ni siquiera conozcan la existencia de otras personas además de ti (y eso es genial).

Para escapar de esta analogía, Erlang te obliga a escribir actores (procesos) que no compartirán información con otros bits de código a menos que se pasen mensajes entre sí. Cada comunicación es explícita, rastreable y segura.

Cuando definimos Erlang, lo hicimos a nivel de lenguaje, pero en un sentido más amplio, esto no es todo: Erlang es también un entorno de desarrollo en su conjunto. El código se compila en código de bytes y se ejecuta dentro de una máquina virtual. Entonces Erlang, al igual que Java, puede ejecutarse en cualquier lugar. La distribución estándar incluye (entre otras) herramientas de desarrollo (compilador, depurador, generador de perfiles, framework de prueba), el framework Open Telecom Platform (OTP), un servidor web, un generador de analizadores y la base de datos mnesia, un sistema de almacenamiento de valores clave capaz de replicarse en muchos servidores, admitiendo transacciones anidadas y permitiéndole almacenar cualquier tipo de datos de Erlang.

La VM y las librería también le permiten actualizar el código de un sistema en ejecución sin interrumpir ningún programa, distribuir su código con facilidad en muchas computadoras y administrar errores y fallas de una manera simple pero poderosa.

Una política general relacionada en Erlang: dejar que se explote. No como un avión con decenas de pasajeros muriendo, sino más bien como un equilibrista con una red de seguridad debajo. Si bien debes evitar cometer errores, en la mayoría de los casos no será necesario verificar cada tipo o condición de error.

La capacidad de Erlang para recuperarse de errores, organizar el código con actores y hacerlo escalar con la distribución y la concurrencia suena increíble!

Dejo link: https://learnyousomeerlang.com/introduction


jueves, 14 de septiembre de 2023

Prueba Erlang


Quiero recomendarles el tutorial tryerlang que encontre de casualidad. Si bien es basico, es muy interactivo y esta bueno. 

Dejo link: https://www.tryerlang.org/

Box en Rust parte 2


Y siguiendo con Box, los tipos de datos recursivos o tipos de datos con tamaños dinámicos necesitan usar un Box:

#[derive(Debug)]

enum List<T> {

    Cons(T, Box<List<T>>),

    Nil,

}


fn main() {

    let list: List<i32> = List::Cons(1, Box::new(List::Cons(2, Box::new(List::Nil))));

    println!("{list:?}");

}

Si no se usaba Box e intentábamos incrustar una Lista directamente en la Lista, el compilador no calcularía un tamaño fijo de la estructura en la memoria (la Lista sería de tamaño infinito).

Box resuelve este problema ya que tiene el mismo tamaño que un puntero normal y solo apunta al siguiente elemento de la Lista.

Veamos que pasa si lo eliminamos: 

enum List<T> {

    Cons(T, List<T>),

    Nil,

}


fn main() {

    let list: List<i32> = List::Cons(1, List::Cons(2, List::Nil));

    println!("{list:?}");

}

Y si ejecutamos esto: 

cargo run

   Compiling hello_cargo v0.1.0 

error[E0072]: recursive type `List` has infinite size

 --> src/main.rs:2:1

  |

2 | enum List<T> {

  | ^^^^^^^^^^^^

3 |     Cons(T, List<T>),

  |             ------- recursive without indirection

  |

help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle

  |

3 |     Cons(T, Box<List<T>>),

  |             ++++       +


For more information about this error, try `rustc --explain E0072`.

error: could not compile `hello_cargo` due to previous error


Un Box no puede estar vacío, por lo que el puntero siempre es válido y no nulo. Esto permite al compilador optimizar el diseño de la memoria.

sábado, 9 de septiembre de 2023

Como crear un nuevo proyecto con Clojure y Leiningen?



Bueno, todo tiene un principio y vamos a empezar a crear un proyectito de consola con Clojure y Leiningen

$ lein new app my-project

Fijate vos que si queres utilizar nombre de proyecto con mayuscula, no te deja, ojo ahi. 

Ahora vamos a ejecutarlo :

$ lein run

podemos generar un jar con : 

$ lein jar

Si estamos trabajando en un proyecto de aplicación, Leiningen nos brinda la posibilidad de construir lo que se llama un uberjar. Este es un archivo JAR que contiene el proyecto en sí y todas las dependencias y está configurado para permitir que se ejecute tal cual.

$ lein uberjar

Compiling secuencia-clojure.core

$ java -jar target/uberjar/el-jar-standalone.jar 

Y ya podemos empezar!!

viernes, 8 de septiembre de 2023

Box en Rust


Box es un puntero a los datos del monticulo:

fn main() {

    let five = Box::new(5);

    println!("five: {}", *five);

}

Box<T> implementa Deref<Target = T>, lo que significa que puedes llamar métodos desde T directamente en un Box<T>.

Box es como std::unique_ptr en C++, excepto que se garantiza que el elemento no será nulo.

En el ejemplo anterior, incluso podemos omitir el * en println! gracias a Deref.

Un Box puede resultar útil cuando:

  • tiene un tipo cuyo tamaño no se puede conocer en el momento de la compilación, pero el compilador de Rust quiere saber un tamaño exacto.
  • desea transferir una propiedad de una gran cantidad de datos. Para evitar copiar grandes cantidades de datos en la pila, almacene los datos en el monticulo en un Box de modo que solo se mueva el puntero.

lunes, 4 de septiembre de 2023

HashMap en Rust


Veamos un ejemplo: 


use std::collections::HashMap;

fn main() {

    let mut page_counts = HashMap::new();

    page_counts.insert("Adventures of Huckleberry Finn".to_string(), 207);

    page_counts.insert("Grimms' Fairy Tales".to_string(), 751);

    page_counts.insert("Pride and Prejudice".to_string(), 303);


    if !page_counts.contains_key("Les Misérables") {

        println!("We know about {} books, but not Les Misérables.",

                 page_counts.len());

    }


    for book in ["Pride and Prejudice", "Alice's Adventure in Wonderland"] {

        match page_counts.get(book) {

            Some(count) => println!("{book}: {count} pages"),

            None => println!("{book} is unknown.")

        }

    }


    // Use the .entry() method to insert a value if nothing is found.

    for book in ["Pride and Prejudice", "Alice's Adventure in Wonderland"] {

        let page_count: &mut i32 = page_counts.entry(book.to_string()).or_insert(0);

        *page_count += 1;

    }


    println!("{page_counts:#?}");

}


Si ejecutamos este programa: 

Finished dev [unoptimized + debuginfo] target(s) in 0.05s

     Running `target/debug/hello_cargo`

We know about 3 books, but not Les Misérables.

Pride and Prejudice: 303 pages

Alice's Adventure in Wonderland is unknown.

{

    "Pride and Prejudice": 304,

    "Grimms' Fairy Tales": 751,

    "Adventures of Huckleberry Finn": 207,

    "Alice's Adventure in Wonderland": 1,

}


HashMap no está definido por defecto y es necesario incluirlo : use std::collections::HashMap;

Desde Rust 1.56, HashMap implementa From<[(K, V); N]>, que nos permite inicializar fácilmente un mapa hash a partir de una matriz literal:

  let page_counts = HashMap::from([

    ("Harry Potter and the Sorcerer's Stone".to_string(), 336),

    ("The Hunger Games".to_string(), 374),

  ]);

Alternativamente, HashMap se puede construir a partir de cualquier iterador que produzca tuplas de valores clave.

Mostramos HashMap<String, i32> y evitamos usar &str como clave para facilitar los ejemplos. Por supuesto, se puede utilizar referencias en colecciones, pero puede generar complicaciones con el verificador de préstamos.

Podemos agregar estas lineas : 

  let pc1 = page_counts

      .get("Harry Potter and the Sorcerer's Stone ")

      .unwrap_or(&336);

  let pc2 = page_counts

      .entry("The Hunger Games".to_string())

      .or_insert(374);

La primera línea verá si un libro está en el HashMap y, en caso contrario, devolverá un valor alternativo. La segunda línea insertará el valor alternativo en el HashMap si no se encuentra el libro.

 


Vec en Rust


Vec es el búfer estándar de tamaño variable de Rust, veamos un ejemplo:


fn main() {

    let mut v1 = Vec::new();

    v1.push(42);

    println!("v1: len = {}, capacity = {}", v1.len(), v1.capacity());


    let mut v2 = Vec::with_capacity(v1.len() + 1);

    v2.extend(v1.iter());

    v2.push(9999);

    println!("v2: len = {}, capacity = {}", v2.len(), v2.capacity());


    // Canonical macro to initialize a vector with elements.

    let mut v3 = vec![0, 0, 1, 2, 3, 4];


    // Retain only the even elements.

    v3.retain(|x| x % 2 == 0);

    println!("{v3:?}");


    // Remove consecutive duplicates.

    v3.dedup();

    println!("{v3:?}");

}


Vec implementa Deref<Target = [T]>, lo que significa que puedes llamar a métodos de slice.

  • Vec es un tipo de colección, junto con String y HashMap. Los datos que contiene se almacenan en el monticulo. Esto significa que no es necesario conocer la cantidad de datos en el momento de la compilación. Puede crecer o reducirse en tiempo de ejecución.
  • Observe que Vec<T> también es un tipo genérico, pero no es necesario especificar T explícitamente. Como siempre con la inferencia de tipo Rust, la T se estableció durante la primera llamada push.
  • vec![...] es una macro canónica para usar en lugar de Vec::new() y admite agregar elementos iniciales al vector.
  • Para indexar el vector se puede utilizar [], pero entrarán en pánico si están fuera de los límites. Alternativamente, usar get devolverá un Option. La función pop eliminará el último elemento.

domingo, 3 de septiembre de 2023

String en Rust


 String es el búfer de cadena UTF-8 estándar:


fn main() {

    let mut s1 = String::new();

    s1.push_str("Hello");

    println!("s1: len = {}, capacity = {}", s1.len(), s1.capacity());


    let mut s2 = String::with_capacity(s1.len() + 1);

    s2.push_str(&s1);

    s2.push('!');

    println!("s2: len = {}, capacity = {}", s2.len(), s2.capacity());


    let s3 = String::from("🇨🇭");

    println!("s3: len = {}, number of chars = {}", s3.len(),

             s3.chars().count());

}


Si ejecutamos este código: 


Compiling hello_cargo v0.1.0 

    Finished dev [unoptimized + debuginfo] target(s) in 0.65s

     Running `target/debug/hello_cargo`

s1: len = 5, capacity = 8

s2: len = 6, capacity = 6

s3: len = 8, number of chars = 2

String implementa Deref<Target = str>, lo que significa que puedes llamar a todos los métodos str en un String.

  • String::new devuelve una nueva cadena vacía, se usa String::with_capacity cuando se sabe cuántos caracteres tiene la cadena.
  • String::len devuelve el tamaño de la cadena en bytes (que puede ser diferente de su longitud en caracteres).
  • String::chars devuelve un iterador sobre los caracteres reales. Un carácter puede ser diferente de lo que un humano considerará un "carácter" debido a los grupos de grafemas.
  • Cuando las personas se refieren a cadenas, podrían estar hablando de &str o String.
  • Cuando un tipo implementa Deref<Target = T>, el compilador le permitirá llamar de forma transparente a métodos desde T.
  • String implementa Deref<Target = str> que le da acceso de forma transparente a los métodos de str.
  • String se implementa como un contenedor alrededor de un vector de bytes; muchas de las operaciones que ve admitidas en vectores también se admiten en String, pero con algunas garantías adicionales.


sábado, 2 de septiembre de 2023

Rust Standard Library


Rust tiene una biblioteca estándar que ayuda a establecer un conjunto de tipos comunes utilizados por las bibliotecas y los programas de Rust. De esta manera, dos bibliotecas pueden funcionar juntas sin problemas porque ambas usan el mismo tipo de cadena, por ejemplo.

Los tipos de vocabulario comunes incluyen:

  • Option y Result: se utilizan para valores opcionales y manejo de errores.
  • String: el tipo de cadena predeterminado utilizado para los datos propios.
  • Vec: un vector extensible estándar.
  • HashMap: un tipo de mapa hash con un algoritmo hash configurable.
  • Box: un puntero propio para datos asignados al monticulo.
  • Rc: un puntero compartido.

De hecho, Rust contiene varias capas de la biblioteca estándar: core, alloc y std.

core incluye los tipos y funciones más básicos que no dependen de libc, asignador o incluso de la presencia de un sistema operativo.

alloc incluye tipos que requieren un asignador de monticulo global, como Vec, Box y Arc.

Option y Result en Rust


Los tipos representan datos opcionales:


fn main() {

    let numbers = vec![10, 20, 30];

    let first: Option<&i8> = numbers.first();

    println!("first: {first:?}");


    let idx: Result<usize, usize> = numbers.binary_search(&10);

    println!("idx: {idx:?}");

}

Si ejecuto este codigo : 

cargo run

   Compiling hello_cargo v0.1.0 (/home/emanuel/Projects/rust/hello_cargo)

    Finished dev [unoptimized + debuginfo] target(s) in 0.75s

     Running `target/debug/hello_cargo`

first: Some(10)

idx: Ok(0)


Si por ejemplo busco el 39 : 

    let idx: Result<usize, usize> = numbers.binary_search(&39); 

    println!("idx: {idx:?}");


Si corremos esto : 

cargo run

   Compiling hello_cargo v0.1.0 (/home/emanuel/Projects/rust/hello_cargo)

    Finished dev [unoptimized + debuginfo] target(s) in 0.27s

     Running `target/debug/hello_cargo`

first: Some(10)

idx: Err(3)

El resultado es Err y no Ok. 

  • Option y Result se utilizan ampliamente no solo en la biblioteca estándar.
  • La Option <&T> tiene cero espacio adicional en comparación con &T.
  • El resultado es el tipo estándar para implementar el manejo de errores.
  • binario_search devuelve Result<usize, usize>.
    • Si se encuentra, Result::Ok contiene el índice donde se encuentra el elemento.
    • De lo contrario, Result::Err contiene el índice donde se debe insertar dicho elemento.

viernes, 1 de septiembre de 2023

Metodos en Rust parte 3


Veamos un ejemplo de metodos:

#[derive(Debug)]

struct Race {

    name: String,

    laps: Vec<i32>,

}


impl Race {

    fn new(name: &str) -> Race {  // No receiver, a static method

        Race { name: String::from(name), laps: Vec::new() }

    }


    fn add_lap(&mut self, lap: i32) {  // Exclusive borrowed read-write access to self

        self.laps.push(lap);

    }


    fn print_laps(&self) {  // Shared and read-only borrowed access to self

        println!("Recorded {} laps for {}:", self.laps.len(), self.name);

        for (idx, lap) in self.laps.iter().enumerate() {

            println!("Lap {idx}: {lap} sec");

        }

    }


    fn finish(self) {  // Exclusive ownership of self

        let total = self.laps.iter().sum::<i32>();

        println!("Race {} is finished, total lap time: {}", self.name, total);

    }

}


fn main() {

    let mut race = Race::new("Monaco Grand Prix");

    race.add_lap(70);

    race.add_lap(68);

    race.print_laps();

    race.add_lap(71);

    race.print_laps();

    race.finish();

    // race.add_lap(42);

}

Lo ejecutamos : 

cargo run

   Compiling hello_cargo v0.1.0 

     Finished dev [unoptimized + debuginfo] target(s) in 0.62s

     Running `target/debug/hello_cargo`

Recorded 2 laps for Monaco Grand Prix:

Lap 0: 70 sec

Lap 1: 68 sec

Recorded 3 laps for Monaco Grand Prix:

Lap 0: 70 sec

Lap 1: 68 sec

Lap 2: 71 sec

Race Monaco Grand Prix is finished, total lap time: 209


Los cuatro métodos aquí utilizan un receptor de método diferente.

Si intentamos llamar a finalizar dos veces o descomentamos // race.add_lap(42); , lanza este error : 

cargo run

   Compiling hello_cargo v0.1.0 

error[E0382]: borrow of moved value: `race`

  --> src/main.rs:37:5

   |

30 |     let mut race = Race::new("Monaco Grand Prix");

   |         -------- move occurs because `race` has type `Race`, which does not implement the `Copy` trait

...

36 |     race.finish();

   |          -------- `race` moved due to this method call

37 |     race.add_lap(42);

   |     ^^^^^^^^^^^^^^^^ value borrowed here after move

   |

note: this function takes ownership of the receiver `self`, which moves `race`

  --> src/main.rs:23:15

   |

23 |     fn finish(self) {  // Exclusive ownership of self

   |               ^^^^

For more information about this error, try `rustc --explain E0382`.

error: could not compile `hello_cargo` due to previous error

Y es porque finish toma todo el control del registro. 

Tenga en cuenta que, aunque los receptores de métodos son diferentes, las funciones no estáticas se denominan de la misma manera en el cuerpo principal. Rust permite la referencia y desreferenciación automática al llamar a métodos. Rust agrega automáticamente los cambios &, *, para que ese objeto coincida con la firma del método.

Ebook: Estos 4 patrones van a modernizar tu estrategia multinube

 Me llego este mail y quería compartirlo: 

Las empresas modernas eligen Oracle Cloud Infrastructure (OCI)

En este ebook te contamos las razones
Conoce cómo las empresas crean y ejecutan aplicaciones seguras y escalables gracias a la nube de Oracle.

martes, 29 de agosto de 2023

Metodos en Rust parte 2


Veamos este código: 

#[derive(Debug)]

struct Person {

    name: String,

    age: u8,

}


impl Person {

    fn say_hello(&self) {

        println!("Hello, my name is {}", self.name);

    }

}


fn main() {

    let peter = Person {

        name: String::from("Peter"),

        age: 27,

    };

    peter.say_hello();

}

El &self del post anterior indica que el método toma prestado el objeto de forma inmutable. Hay otros posibles receptores para un método:

  • &self: toma prestado el objeto de tipo Person que llama utilizando una referencia compartida e inmutable. El objeto se puede volver a utilizar posteriormente.
  • &mut self: toma prestado el objeto de tipo Person que llama utilizando una referencia única y mutable. El objeto se puede volver a utilizar posteriormente.
  • self: toma posesión del objeto. El método se convierte en propietario del objeto. El objeto se eliminará (desasignará) cuando el método regrese, a menos que su propiedad se transmita explícitamente. La propiedad total no significa automáticamente mutabilidad.
  • mut self: igual que el anterior, pero el método puede mutar el objeto.
  • Sin receptor: esto se convierte en un método estático en la estructura. Normalmente se utiliza para crear constructores que se denominan new por convención.

Más allá de las variantes de self, también hay tipos de contenedores especiales que pueden ser tipos de receptores, como Box<Self>.

Estas restricciones siempre se juntan en Rust debido a las reglas del verificador prestado, y self no es una excepción. No es posible hacer referencia a una estructura desde múltiples ubicaciones y llamar a un método mutante (&mut self) en ella.

viernes, 25 de agosto de 2023

Metodos en Rust

 


Rust te permite asociar funciones con tus nuevos tipos. Hacemos esto con un bloque impl:

#[derive(Debug)]

struct Person {

    name: String,

    age: u8,

}


impl Person {

    fn say_hello(&self) {

        println!("Hello, my name is {}", self.name);

    }

}


fn main() {

    let peter = Person {

        name: String::from("Peter"),

        age: 27,

    };

    peter.say_hello();

}

Los métodos se llaman en una instancia de un tipo (como una estructura o enumeración), el primer parámetro representa la instancia como self.

Los desarrolladores pueden optar por utilizar métodos para aprovechar la sintaxis del receptor de métodos y ayudar a mantenerlos más organizados. Al utilizar métodos, podemos mantener todo el código de implementación en un lugar predecible.

self es un término abreviado y es de tipo &Person, por lo tanto podemos escribir esto como : 

impl Person {

    fn say_hello(self : &Person) {

        println!("Hello, my name is {}", self.name);

    }

}