Me llegaron ests libros de web code geeks :
|
Me llegaron ests libros de web code geeks :
|
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:
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.
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)
)
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.
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.
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).
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
Me llego este mail y se los quiero compartir:
|
Hola Emanuel, |
¿Sabías que tan solo en México hay más de 60 millones de smartphones? Y, de acuerdo al informe de tendencias de Internet 2019 de la Asociación Mexicana de Internet, las personas que utilizan estos dispositivos pasan el 78% del tiempo en aplicaciones de mensajería instantánea. |
Con esto en mente, Yalo ha estado transformando los medios de comunicación empresariales. Desde 2015, la empresa desarrolla soluciones que les permiten a grandes empresas utilizar aplicaciones de mensajería para manejar sus procesos críticos, en sectores como ventas y atención al cliente. |
Además de usar la mensajería instantánea para mantener a las personas en contacto con familiares y amigos, los usuarios también usan aplicaciones de mensajería para hablar con las empresas que más les importan. Si alguna vez enviaste un mensaje a tu empresa favorita a través de una aplicación de mensajería, ya conoces esta evolución. |
En su recorrido de transformación digital, Yalo ha implementado las soluciones de Google Cloud para producir cambios sustanciales en los procesos de producción y desarrollo de sus servicios, mejorar la atención de los usuarios y fomentar el trabajo colaborativo en la compañía. Para lograrlo, incorporó herramientas como Google Kubernetes Engine. |
Hoy Yalo cuenta con una herramienta que facilita las tareas de los desarrolladores gracias a un manejo de contenedores sencillo e intuitivo, y además ha conseguido: |
|
Conoce todos los detalles de cómo Yalo, en asociación con Google Cloud, está cambiando la vida de sus clientes. |
|
Nos vemos en la nube, Equipo de Google Cloud |
|
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
Dejo link: https://www.tryerlang.org/
#[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.
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
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:
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.
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.