Translate

lunes, 9 de diciembre de 2019

Que es Python Global Interpreter Lock?


Antes de empezar necesitamos unos conceptos importantes. Dado que concurrencia y paralelismo son dos conceptos relacionados y mucha gente los confunde debemos empezar marcando las diferencias.

La concurrencia significa, esencialmente, que tanto la tarea A como la B deben suceder independientemente una de otra, y A comienza a ejecutarse, y luego B comienza antes de que A termine.

Hay varias formas diferentes de lograr la concurrencia. Uno de ellos es el paralelismo: tener varias CPU trabajando en las diferentes tareas al mismo tiempo. Pero esa no es la única manera. Otro es por cambio de tareas, que funciona así: la tarea A funciona hasta cierto punto, luego la CPU que trabaja en ella se detiene y cambia a la tarea B, trabaja en ella por un tiempo y luego vuelve a la tarea A. Si los intervalos de tiempo son lo suficientemente pequeños, puede parecerle al usuario que ambas cosas se ejecutan en paralelo, a pesar de que en realidad están siendo procesadas en serie por una CPU multitarea. Se entendió? es como que concurrencia es la sensación de paralelismo, pero no necesariamente es paralelismo. En cambio paralelismo es hacer cosas en paralelo posta.

Y dada esta aclaración, hablemos de GIL,  es el mecanismo utilizado en CPython para impedir que múltiples threads modifiquen los objetos de Python a la vez en una aplicación multi hilo. Esto no evita que tengamos que utilizar primitivas de sincronización en nuestras aplicaciones en Python.

Si en nuestras aplicaciones tenemos varios threads accediendo a una sección de código con datos mutables, tendremos un problema si no utilizamos primitivas de sincronización. Veamos un ejemplo de  thread :

from threading import Thread

def una_funcion:
    print “¡Hola Mundo!”

thread1 = Thread(target=una_funcion)

thread1.start()

thread1.join()

Se puede notar que importamos la clase Thread del módulo threading e instanciamos un nuevo objeto de tipo Thread al que le pasamos la funcion una_funcion. Lo ejecutamos y bloqueamos el hilo de ejecución principal del script hasta que el thread1 regrese de la sección crítica.

Al igual que en otros lenguajes, si queremos que solo un hilo de ejecución haga cambios en los datos de la sección crítica, debemos hacer uso de la clase Lock que nos permite adquirir una sección crítica.

El GIL es un bloqueo a nivel de intérprete. Este bloqueo previene la ejecución de múltiples hilos a la vez en un mismo intérprete de Python. Cada hilo debe esperar a que el GIL sea liberado por otro hilo.

Aunque CPython utiliza el soporte nativo del sistema operativo donde se ejecute a la hora de manejar hilos, y la implementación nativa del sistema operativo permite la ejecución de múltiples hilos de forma simultánea, el intérprete CPython fuerza a los hilos a adquirir el GIL antes de permitirles acceder al intérprete, la pila y puedan así modificar los objetos Python en memoria.

En definitiva, el GIL protege la memoria del intérprete que ejecuta nuestras aplicaciones y no a las aplicaciones en sí. El GIL también mantiene el recolector de basura en un correcto y saneado funcionamiento.

El recolector de basura de Python, como todos los recolectores de basura de diferentes lenguajes de programación, se encarga de liberar la memoria cuando terminamos de usar un objeto. En Python, este mecanismo hace uso de un concepto denominado conteo de referencias.

Cada vez que se hace referencia a un objeto instanciado (un int, una cadena o cualquier otro tipo de objeto nativo o propio) el recolector de basura lo monitorea y suma uno al contador de referencias al objeto. Cuando este número llega al cero, significa que el objeto no está más en uso y el recolector de basura procede a su eliminación de la memoria.

De esta forma no debemos preocuparnos nosotros mismos por liberar la memoria y limpiar los objetos que van a dejar de ser usados como si tenemos que hacer por ejemplo en C o C++. El GIL impide que un thread decremente el valor del conteo de referencia de un objeto a cero mientras otro thread está haciendo uso del mismo. Solo un thread puede acceder a un objeto Python a la vez.

El GIL permite que la implementación de CPython sea extremadamente sencilla a la vez que incrementa la velocidad de ejecución de aplicaciones con un único hilo y la ejecución de aplicaciones multi hilo en sistemas que cuentan con un único procesador. Facilita el mantenimiento del intérprete así como la escritura de módulos y extensiones para el mismo.

Esto es genial, pero también impide la ejecución de múltiples hilos de procesamiento en paralelo en sistemas con múltiples procesadores.

El GIL no es tan malo como puede aparentar a primera vista. Los módulos que realizan tareas de computación intensiva como la compresión o la codificación liberan el GIL mientras operan. También es liberado en todas las operaciones de E/S.

Lo cierto es que si. En 1999 Greg Stein, director de la Apache Software Foundation, y mantenedor de Python y sus librerías desde 1999 al 2003, creó un parche para el intérprete que eliminaba completamente el GIL y añadía bloqueo granular alrededor de operaciones sensibles en el intérprete.

Este parche incrementaba la velocidad de ejecución de aplicaciones multi-hilo pero la decrementaba a la mitad en aplicaciones que ejecutaban un único hilo, lo cual, no era aceptable. Por supuesto esa rama de desarrollo de CPython no tiene ningún tipo de mantenimiento y es hoy inaccesible :(

Por ende con GIL solo tenemos concurrencia y no paralelismo...



Seguimos con Docker


Creo que recuerdan los post anteriores... 

Docker trae muchas herramientas para trabajar de forma eficiente con contenedores. 

Un comando muy usado es docker top, que un usuario de linux o unix le hace pensar en el comando top, el cual muestra los procesos y los recursos que consumen se forma interactiva.

En docker a este comando se le pasa el id del contenedor, de la siguiente manera :

docker top ContainerID

De esta manera podemos saber información de contenedor.

Con docker stop podemos parar una contenedor que esta corriendo:

docker stop ContainerID

Con rm borramos un contenedor:

docker rm ContainerID

Con docker stats, podemos saber estadísticas de nuestro contenedor :

docker stats ContainerID

Con pause, pausamos un contenedor, lo que significa que docker va a pausar todos los proceso del contenedor, para luego poder volverlos a iniciar con unpause :

docker pause ContainerID
docker unpause ContainerID

Con kill matamos el contenedor, sirve para matar el proceso cuando tenemos un problema o no responde :

docker kill ContainerID


jueves, 5 de diciembre de 2019

Libros Gratuitos de Java geeks

Quiero compartir estos libros de java geeks:

Download IT Guides!

 
Hadoop has become the de-facto tool used for Distributed computing. For this reason we have provided an abundance of tutorials here at Java Code Geeks.
 
 
Drools supports the JSR-94 standard for its business rule engine and enterprise framework for the construction, maintenance, and enforcement of business policies in an organization,...
 
 
In the following sections we will discuss about object-oriented programming and its characteristics, general questions regarding Java and its functionality, collections in Java, garbage...
 
 
We are going to explain what reflection is in general and how can be used in Java. Real uses cases about different reflection uses are listed in the next chapters.
 

lunes, 2 de diciembre de 2019

Seguimos con Docker

Seguimos con Docker...

Nosotros podemos correr un ubuntu con el siguiente comando :

Docker run –it Ubuntu bash

Docker hub es el lugar donde se pueden registrar imagenes de docker, las cuales se pueden bajar posteriormente.

https://www.docker.com/products/docker-hub

En esta pagina podemos buscar una imagen que necesitamos, y luego con el comando pull podemos descargar esta imagen para luego poder correrla. Veamos un ejemplo con una imagen de jenskins :

sudo docker pull jenkins

sudo docker run -p 8080:8080 -p 50000:50000 jenkins

con la opción -p estamos indicando que publique un puerto determinado indicando el puerto origen y el destino separado por dos puntos “:”

Docker se basa en imagenes, todas son imagenes que podemos bajar y correr, por ejemplo con :

sudo docker run centos –it /bin/bash

Estamos corriendo una imagen llamada centos y lo hace de forma interactiva, y cuando levante va a utilizar el bash de este sistema.

Si deseamos saber que imágenes tenemos debemos hacer :

docker images

Cuando ejecutamos este comando obtendremos:

  • El Tag o nombre de la imagen,
  • El Id de la imagen que es un dato externo. 
  • La fecha de creación 
  • y la cantidad de byte que ocupa la imagen. 

La imagen puede bajarse utilizando el comando run y el nombre de la imagen. Con este comando bajamos la imagen y la corremos.

Nosotros podemos remover las imágenes con docker rmi , pasandole el id de la imagen, por ejemplo :

docker rmi ImageID

Los contenedores son instancias de imágenes Docker que se pueden ejecutar con el comando Docker run. El propósito básico de Docker es ejecutar contenedores.

La ejecución de contenedores se gestiona con el comando Docker run. Para ejecutar un contenedor en modo interactivo, primero inicie el contenedor Docker.

sudo docker run –it centos /bin/bash

Uno puede enumerar todos los contenedores a través del comando docker ps. Este comando se utiliza para devolver los contenedores actualmente en ejecución.

docker ps -a se utiliza para enumerar todos los contenedores en el sistema.

Y creo que por ahora estamos bien!!

domingo, 1 de diciembre de 2019

Empezando con Rust, parte 2


Seguimos con Rust...

Cargo es el sistema de construcción y administrador de paquetes de Rust. La mayoría de los Rustaceanos usan esta herramienta para administrar sus proyectos de Rust porque Cargo maneja muchas tareas, como compilar el código, descargar dependencias y construir esas dependencias.

Los programas Rust más simples, como el que hemos escrito hasta ahora, no tienen dependencias. Entonces, si hubiéramos construido Hello, world! proyecto con Cargo, solo usaría la parte de Cargo que maneja la compilación. A medida que escriba programas Rust más complejos, agregará dependencias, y si comienza un proyecto con Cargo, será mucho más fácil agregar dependencias.

Cargo viene instalado con Rust podemos verificar si Cargo está instalado escribiendo lo siguiente en la terminal:

$ cargo --version

Si ve un número de versión, ¡lo tiene! Si ve un error, como “command not found”, consulte la documentación de su método de instalación para determinar cómo instalar Cargo por separado.

Creemos un nuevo proyecto con Cargo y veamos cómo se diferencia de nuestro Hello, world original. Vamos a la carpeta donde pondremos el código. Luego, ejecutamos lo siguiente:

$ cargo new hello_cargo
$ cd hello_cargo

El primer comando crea un nuevo directorio llamado hello_cargo. Hemos llamado a nuestro proyecto hello_cargo, y Cargo crea sus archivos en un directorio del mismo nombre.

Con "cd hello_cargo" nos hubicamos en el directorio hello_cargo y si ejecutamos ls podremos ver los archivos. Cargo ha generado dos archivos y un directorio: un archivo Cargo.toml y un directorio src con un archivo main.rs dentro. También ha inicializado un nuevo repositorio de Git junto con un archivo .gitignore.

Si abrimos Cargo.toml con un editor de texto. Debería verse algo así :

[package]
name = "hello_cargo"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"

[dependencies]

Este archivo está en el formato TOML (Tom's Obvious, Minimal Language), que es el formato de configuración de Cargo.

La primera línea, [paquete], es un encabezado de sección que indica que las siguientes declaraciones están configurando un paquete. A medida que agreguemos más información a este archivo, agregaremos otras secciones.

Las siguientes cuatro líneas establecen la información de configuración que Cargo necesita para compilar su programa: el nombre, la versión, quién lo escribió y la edición de Rust para usar. Cargo obtiene su nombre e información de correo electrónico de su entorno, por lo que si esa información no es correcta, se puede correjir y guarde el archivo.

La última línea, [dependencias], es el comienzo de una sección de dependencias del proyecto. En Rust, los paquetes de código se denominan cajas. No necesitaremos ninguna otra caja para este proyecto, por lo que no usaremos esta sección de dependencias.

Ahora abra src / main.rs y veremos lo siguiente:

fn main() {
    println!("Hello, world!");
}

Cargo ha generado un Hola, mundo! programa similar al post anterior. Hasta ahora, las diferencias entre nuestro proyecto anterior y el proyecto que genera Cargo son que Cargo colocó el código en el directorio src, y tenemos un archivo de configuración Cargo.toml en el directorio superior.

Cargo espera que sus archivos de origen vivan dentro del directorio src. El directorio de proyectos de nivel superior es solo para archivos README, información de licencia, archivos de configuración y cualquier otra cosa que no esté relacionada con su código. Usar Cargo te ayuda a organizar tus proyectos. Hay un lugar para todo, y todo está en su lugar.

Si comenzó un proyecto que no usa Cargo, como hicimos con Hello, world! proyecto, puede convertirlo en un proyecto que use Cargo. Lo que tenemos que hacer es mover el código del proyecto al directorio src y crear un archivo Cargo.toml apropiado.

Desde el directorio hello_cargo, compilaremos el proyecto ingresando el siguiente comando:

$ cargo build
   Compiling hello_cargo v0.1.0 (file:///projects/hello_cargo)
    Finished dev [unoptimized + debuginfo] target(s) in 2.85 secs

Este comando crea un archivo ejecutable en target/debug/hello_cargo (o target \debug\hello_cargo.exe en Windows) y luego podemos ejecurar dicho programa.

$ ./target/debug/hello_cargo
Hello, world!

Si todo va bien, "Hello, world!" debe ser impreso en la terminal. La ejecución de la construcción por primera vez también hace que Cargo cree un nuevo archivo en el nivel superior: Cargo.lock. Este archivo realiza un seguimiento de las versiones exactas de las dependencias en su proyecto. Este proyecto no tiene dependencias, por lo que el archivo va a estar vacio. Nunca necesitará cambiar este archivo manualmente; Cargo gestiona sus contenidos.

Acabamos de construir un proyecto con Cargo Build y lo ejecutamos con ./target/debug/hello_cargo, pero también podemos usar "Cargo Run" para compilar el código y luego ejecutarlo :

$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
     Running `target/debug/hello_cargo`
Hello, world!

Tenga en cuenta que esta vez no hemos visto resultados que indiquen que Cargo estaba compilando hello_cargo. Cargo descubrió que los archivos no habían cambiado, por lo que simplemente ejecutó el binario. Si hubiera modificado su código fuente, Cargo habría reconstruido el proyecto antes de ejecutarlo, y habría visto este resultado:

$ cargo run
   Compiling hello_cargo v0.1.0 (file:///projects/hello_cargo)
    Finished dev [unoptimized + debuginfo] target(s) in 0.33 secs
     Running `target/debug/hello_cargo`
Hello, world!

"cargo check" verifica rápidamente su código para asegurarse de que se compila pero no produce un ejecutable:

$ cargo check
   Checking hello_cargo v0.1.0 (file:///projects/hello_cargo)
    Finished dev [unoptimized + debuginfo] target(s) in 0.32 secs

¿Por qué no querrías un ejecutable? A menudo, la verificación de Cargo es mucho más rápida que la compilación, ya que omite el paso de producir un ejecutable. Si continuamente revisas tu trabajo mientras escribes el código, ¡usar el control de carga acelerará el proceso! Como tal, muchos Rustaceanos realizan controles de Cargo periódicamente mientras escriben su programa para asegurarse de que se compila. Luego ejecutan la construcción de carga cuando están listos para usar el ejecutable.

Recapitulemos lo que hemos aprendido hasta ahora sobre Cargo:

  • Podemos construir un proyecto utilizando "cargo build" o "cargo check".
  • Podemos construir y ejecutar un proyecto en un solo paso utilizando "cargo run".
  • En lugar de guardar el resultado de la compilación en el mismo directorio que nuestro código, Cargo lo almacena en el directorio de /target/debug.

Una ventaja adicional de usar Cargo es que los comandos son los mismos sin importar en qué sistema operativo esté trabajando.

Cuando su proyecto finalmente esté listo para su lanzamiento, puede usar la construcción Cargo de tipo release para compilarlo con optimizaciones. Este comando creará un ejecutable en target/release en lugar de target/debug. Las optimizaciones hacen que su código Rust se ejecute más rápido, pero activarlas alarga el tiempo que tarda su programa en compilarse. Es por eso que hay dos perfiles diferentes: uno para el desarrollo, cuando desea reconstruir rápida y frecuentemente, y otro para construir el programa final que le dará a un usuario que no se reconstruirá repetidamente y que se ejecutará tan rápido como posible.

Con proyectos simples, Cargo no proporciona mucho valor con solo usar rustc, pero demostrará su valor a medida que sus programas se vuelvan más complejos. Con proyectos complejos compuestos de múltiples cajas, es mucho más fácil dejar que Cargo coordine la construcción.

Aunque el proyecto hello_cargo es simple, ahora utilizamos gran parte de las herramientas reales que usará en el resto de su carrera en Rust. De hecho, para trabajar en cualquier proyecto existente, puede usar los siguientes comandos para verificar el código usando Git, cambiar al directorio de ese proyecto y compilar:

$ git clone someurl.com/someproject
$ cd someproject
$ cargo buildm

Y por este post, demasiado...

Dejo link:
https://doc.rust-lang.org/book

Microservices for Java Developers eBook

Quiero compartir el siguiente libro sobre microservice en Java de Java Code Geeks :

Microservices for Java Developers eBook


Dear fellow geek,

in case you haven't done already, check out our ebook on Microservices for Java Developers. You can access it for FREE here.

Microservices for Java Developers eBook
Microservices are a software development technique – a variant of the service-oriented architecture (SOA) structural style – that arranges an application as a collection of loosely coupled services.

In a microservices architecture, services are fine-grained and the protocols are lightweight. Computer microservices can be implemented in different programming languages and might use different infrastructures.

In this ebook, we cover a wide range of topics, from Microservices Communication and Implementing microservices to Managing Security, Testing, Monitoring and Alerting. With
this guide you will be able to get your own projects up and running in minimum time. Enjoy!

jueves, 28 de noviembre de 2019

Defer, Panic, y Recover en Go


La visión de Go es particular, la verdad es que no se si es buena o mala pero claramente es particular. Para Go un error es algo que puede pasar y es parte del resultado. Para manejar esta visión tiene 3 palabras reservadas : Defer, Panic, y Recover.

Defer nos permiten pensar en cerrar cada archivo (por ejemplo) justo después de abrirlo, garantizando que, independientemente del número de declaraciones return en la función, los archivos se cerrarán. Es como el final de java, veamos un ejemplo :

func CopyFile(dstName, srcName string) (written int64, err error) {
    src, err := os.Open(srcName)
    if err != nil {
        return
    }
    defer src.Close()

    dst, err := os.Create(dstName)
    if err != nil {
        return
    }
    defer dst.Close()

    return io.Copy(dst, src)
}

El comportamiento de las declaraciones diferidas es sencillo y predecible. Hay tres reglas simples:

1. Los argumentos de una función diferida se evalúan cuando se evalúa la declaración diferida.

En este ejemplo, la expresión "i" se evalúa cuando se llama a Println. La llamada diferida imprimirá "0" después de que regrese la función.

func a() {
    i := 0
    defer fmt.Println(i)
    i++
    return
}

2. Las llamadas a funciones diferidas se ejecutan en el orden último en entrar, primero en salir después de que vuelve la función circundante.

Esta función imprime "3210":

func b() {
    for i := 0; i < 4; i++ {
        defer fmt.Print(i)
    }
}

3.Las funciones diferidas pueden leer y asignar a los valores de retorno nombrados de la función de retorno.

En este ejemplo, una función diferida incrementa el valor de retorno i después de que regresa la función circundante. Por lo tanto, esta función devuelve 2:

func c() (i int) {
    defer func() { i++ }()
    return 1
}

Esto es conveniente para modificar el valor de retorno de error de una función; Veremos un ejemplo de esto en breve.

Panic es una función incorporada que detiene el flujo de control ordinario y comienza a entrar en pánico. Cuando la función F llama Panic, la ejecución de F se detiene, las funciones diferidas en F se ejecutan normalmente y luego F vuelve a su llamador. Para la persona que llama, F entonces se comporta como una llamada al pánico. El proceso continúa en la pila hasta que todas las funciones en la rutina actual han regresado, momento en el cual el programa falla. El pánico puede iniciarse invocando el pánico directamente. También pueden ser causados ​​por errores de tiempo de ejecución, como accesos de matriz fuera de los límites.

Recover es una función incorporada que recupera el control de una gorutina en pánico. La recuperación solo es útil dentro de las funciones diferidas. Durante la ejecución normal, una llamada a recuperar devolverá nulo y no tendrá otro efecto. Si la rutina actual está en pánico, una llamada a Recover capturará el valor dado al pánico y reanudará la ejecución normal.

Veamos un ejemplo todo mezclado:

package main

import "fmt"

func main() {
    f()
    fmt.Println("Returned normally from f.")
}

func f() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered in f", r)
        }
    }()
    fmt.Println("Calling g.")
    g(0)
    fmt.Println("Returned normally from g.")
}

func g(i int) {
    if i > 3 {
        fmt.Println("Panicking!")
        panic(fmt.Sprintf("%v", i))
    }
    defer fmt.Println("Defer in g", i)
    fmt.Println("Printing in g", i)
    g(i + 1)
}

La función g toma el int i, y entra en pánico si i es mayor que 3, o se llama a sí mismo con el argumento i + 1. La función f difiere una función que llama a recovery e imprime el valor recuperado (si no es nulo).

El programa generará:

Calling g.
Printing in g 0
Printing in g 1
Printing in g 2
Printing in g 3
Panicking!
Defer in g 3
Defer in g 2
Defer in g 1
Defer in g 0
Recovered in f 4
Returned normally from f.

Y eso es todo amigos!

miércoles, 27 de noviembre de 2019

DataStax publica un libro gratuito de Apache Cassandra

Me llego el siguiente mail de DataStax, sobre un libro de Cassandra:

constellation
Fortune 500 companies have fallen in love with Apache Cassandra for its scalability and high availability. But what’s the difference between Cassandra’s distributed database technology and relational databases? And where do you even begin with Cassandra? In this new e-book, foremost NoSQL experts answer those questions and more.

domingo, 24 de noviembre de 2019

Empezando con Rust

Rust es un lenguaje que ha tomado relevancia este ultimo tiempo. En la ultima encuesta de stackoverflow gano la consideración de lenguaje más amado. Pero que tiene de especial? Vamos a dar juntos los primeros pasos.

Antes de empezar tenemos que instalar rust, como uso linux esto es tan fácil como ir a Synaptic y seleccionar los paquetes de rust o ir por consola y poner :

sudo apt update
sudo apt upgrade
sudo apt install rustc
sudo apt install cargo

Luego vamos a hablar de cargo.

Luego debemos hacer :

rustup install stable
rustup default stable

Crearemos un nuevo archivo fuente y llamado main.rs.

Los archivos de Rust siempre terminan con la extensión .rs. Si usa más de una palabra en su nombre de archivo, se debe usar un guión bajo para separarlos. Por ejemplo, hello_world.rs en lugar de helloworld.rs.

Ahora abramos el archivo main.rs que acaba de crear e ingresemos el siguiente código :

fn main () {
     println! ("¡Hola, mundo!");
}

Guardemos el archivo y vamos a la terminal. En Linux, ingrese los siguientes comandos para compilar y ejecutar el archivo:

$ rustc main.rs
$ ./main
Hello, world!

Si te imprimió "Hello, world!" , esta mal porque escribimos ¡Hola, mundo! (jeje)

Vamos a seguir con Rust y pero por ahora les dejo link: https://www.rust-lang.org/




Más de Prolog

Seguimos con Prolog

Veamos los operadores que nos trae prolog :


El operador "is" permite evaluar expresiones: X is Y

Veamos un ejemplo:

longitud([],0).
longitud([_|Resto],N):-
   longitud(Resto,N1), N is N1+1.

Luego tenemos los operadores lógicos


Con estos operadores podemos hacer lo siguiente: 










Predicado de control. Corte (!) : El corte es un predicado predefinido que se denota mediante un punto de exclamación (!), no tiene argumentos, y cuya evaluación es siempre cierta. 

Los cortes permiten al programador intervenir en el control del programa, puesto que su presencia hace que el sistema ignore ciertas ramas del árbol.

Su utilidad básica, dado que reduce el espacio de búsqueda de soluciones, es mejorar la eficiencia de los programas, evitando la exploración de partes del árbol de resolución de las que se sabe de antemano que no conducirán a ninguna nueva solución.

Veamos un ejemplo :

La representación anterior calcula correctamente los valores de la función, pero tiene el siguiente inconveniente.
Supóngase que se realiza la consulta:
?- f(0,Z), Z>1.

La respuesta de Prolog será “falso”, pero para llegar a dicha conclusión el sistema tiene que recorrer las 3 posibles ramas del árbol

Lo anterior es poco eficiente, puesto que, al ser las tres reglas que describen el predicado f mutuamente excluyentes, una vez que se ha encontrado una solución con una de ellas no tiene sentido probar con el resto. En efecto, la función que se esta calculando tiene la siguiente estructura condicional:

si X ≤ 10 entonces Y = 0; 
si no: si X ≤ 20, entonces Y = 1; 
si no: Y = 2

Esto se puede espresar de la siguiente manera con el operador ! : 


Con esta nueva versión, la respuesta de Prolog a la consulta 

?- f(0,Z), Z>1.

será también “falso”, pero ahora, gracias a la introducción del corte en la primera regla, el sistema solo tendrá que explorar la primera rama del árbol.


Primeros pasos en Docker


Docker es un container management service, que en castellano sería un servicio administrador de contenedores. Es sin duda la tecnología más de moda en estos tiempos.

La idea es desarrollar en contenedores que pueden ser desplegados en cualquier equipo de esta manera la aplicación puede funcionar en cualquier pc y no tenemos el tipico problema de que una aplicación funcione en un computador y en otro no.

Docker esta formado por los siguientes componentes :

  • Docker for Linux, Mac, Windows : este componente permite correr Docker sobre estas plataformas
  • Docker Engine : Es usado para crear imágenes y contenedores
  • Docker Hub : Se utiliza para registrar las imágenes y luego poder utilizarlas
  • Docker Compose : Es utilizado para definir aplicaciones a partir de múltiples containers.


Para instalar docker podemos hacerlo del manual de la pagina oficial: https://docs.docker.com/install/

Una forma de ver si docker quedo bien instalado es hacer :

docker version

De esta forma si docker nos retorna la version, bueno… Anda!

Si necesitamos información de nuestro docker podemos hacer:

docker info

De esta manera vamos a tener información de nuestro contenedor.

Pero si de verdad queremos probar si funciona nuestro docker podemos probar una imagen por defecto, esto sería como un “Hola Mundo” de docker. Empecemos revisando la sintasis, para correr una imagen debemos hacer :

docker run image

Donde image es el nombre de la imagen que queremos correr.

Por ende si queremos correr la imagen “hello-world” :

docker run image hello-world

Este comando lo que va hacer es bajarte la imagen de “hello-world” y luego la va a ejecutar.

Creo que para empezar, bien!

Luego seguimos en posteriores post.

sábado, 23 de noviembre de 2019

Estado del Octoverso


GitHub ha publicado Estado del Octoverso que es un informe anual detallando, una serie de cifras y datos interesantes sobre los desarrolladores, repositorios, proyectos y lenguajes de programación que alberga la plataforma.

Con más de 40 millones de desarrolladores, incluyendo 10 millones de nuevos usuarios registrados en 2019, GitHub ya alcanza más de 44 millones de repositorios. En lo que va de año esos desarrolladores colaboraron usando más de 370 lenguajes de programación.

Lo que más me llamo la atención este año es que Python ha pasado a Java. Esto claramente se debe a que han aumentado los proyectos en maching learnig y sin duda en esta disciplina, python es el rey.



Entre los lenguajes que más crecieron esta dart y Rust :


Sin más dejo link : https://octoverse.github.com/


jueves, 14 de noviembre de 2019

Listas en prolog

Seguimos con Prolog

Una lista es una secuencia de elementos tales como:
[clara,tomas,jose,isabel]

La representación interna de las listas en PROLOG es con árboles binarios, donde la rama de la izquierda es el primer elemento –o cabeza– de la lista y la rama de la derecha es el resto –o cola– de la lista.

[]. Es una lista vacía. Y es el final de la lista (igual que funcional)


En el ejemplo anterior, la cabeza será clara y la cola
[tomas,jose,isabel].

El último elemento siempre es la lista vacía ([]).

La cabeza y la cola de una lista se pueden separar con el símbolo “|”.

[a,b,c]
[a|[b,c]]
[a,b|[c]]
[a,b,c|[]]
[a|X],[Y|[b,c]]

?- [a,b,c] == [a,b|[c]].
true.

El orden de los elementos en la lista importa y un elemento se puede repetir en una lista.

Supongamos que queremos determinar si un elemento es miembro de una lista. Los siguientes serían ejemplos del funcionamiento de la relación “miembro”.

miembro(b,[a,b,c]). %PROLOG respondería sí.
miembro(b,[a,[b,c]]). %PROLOG respondería no.
miembro([b,c],[a,[b,c]]). %PROLOG respondería sí.

Siendo miembro :

miembro(X,[X|_]).
miembro(X,[_|R]):-miembro(X,R).


sábado, 9 de noviembre de 2019

Sincronizar canales en Go


Podemos usar canales para sincronizar la ejecución entre goroutines. Veamos un ejemplo de un programa que espero a que termine una gorutine:

package main
import (
    "fmt"
    "time"
)

func worker(done chan bool) {
    fmt.Print("working...")
    time.Sleep(time.Second)
    fmt.Println("done")
    done <- true
}

func main() {
    done := make(chan bool, 1)
    go worker(done)
    <-done
}

$ go run channel-synchronization.go   
working...done     

Cuando debemos esperar a que terminen varias gorutinas, podemos usar un WaitGroup.

Si eliminamos la línea <- done de este programa, el programa se cerraría incluso antes de que el worker comenzara.

Dejo link: https://gobyexample.com/channel-synchronization

viernes, 8 de noviembre de 2019

Libros de Java Code Geeks

Download IT Guides!

 
The web server is used in products such as Apache ActiveMQ, Alfresco, Apache Geronimo, Apache Maven, Apache Spark, Google App Engine, Eclipse, FUSE, iDempiere, Twitter’s Streaming API and...
 
 
The first specification of JAXB was done in March 2003 and the work process is tracked in the Java Specification Request 31. In this specification request you can find a lot of...
 
 
JavaFX 2.0 and later is implemented as a native Java library, and applications using JavaFX are written in native Java code. JavaFX Script has been scrapped by Oracle, but development is...
 
 
A servlet can almost be thought of as an applet that runs on the server side-without a face. Java servlets make many Web applications possible.
Java...