<rss version="2.0">
    <channel>
        <title>Adrianistán</title>
        <link>https://blog.adrianistan.eu</link>
        <description>El blog de Adrián Arroyo</description>
        <language>es</language>
        <generator>Diario, a fast and safe blog engine</generator>
        
            <item>
                <title>Nuevos horizontes (Pfizer)</title>
                <link>https://blog.adrianistan.eu/nuevos-horizontes-pfizer</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/nuevos-horizontes-pfizer</guid>
                <description><![CDATA[<p>Es un honor comenzar una nueva etapa profesional y más en este año de locos</p>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/Pfizer.jpg" width="300">
</div>
<p>Justo a punto de acabar el año me incorporo a la multinacional Pfizer para trabajar en nanobots en Prolog</p>
<p>Se trata de un proyecto muy interesante, que lleva desarrollándose desde hace un tiempo gracias a la ayuda de la Fundación Bill Gates. Además tendremos también que usar tecnología 5G.</p>
<p>¿Lo malo del cambio? Que voy a tener que mudarme, pero de momento puedo seguir teletrabajando para evitar sospechas.</p>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<p>PD: Evidentemente este post es una inocentada. ¡Feliz día de los inocentes!</p>]]></description>
                <comments>https://blog.adrianistan.eu/nuevos-horizontes-pfizer</comments>
                <pubDate>Mon, 28 Dec 2020 21:21:12 +0000</pubDate>
            </item>
        
            <item>
                <title>Space Pipes</title>
                <link>https://blog.adrianistan.eu/space-pipes</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/space-pipes</guid>
                <description><![CDATA[<p>Acabo de terminar un nuevo minijuego de puzzles llamado Space Pipes. El objetivo es unir todas las piezas del tablero con la casilla central. Para ello podemos rotar las piezas. Existen dos modos de juego: con piezas cuadradas y con piezas hexágonales.</p>
<div style="text-align:center">
<img width="600" src="https://files.adrianistan.eu/SpacePipesBanner.png">
</div>
<p>El juego está programado en TypeScript y Prolog (usando React y Tau-Prolog respectivamente). La librería usada para las interfaces futurísticas es Arwes. Al estar realizado con tecnología web podéis jugar desde cualquier navegador. También lo he publicado como app de Android (pero es exactamente igual).</p>
<div style="text-align:center">
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/XGmP2zt6m6U" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<p>Os animo a que juguéis y lo disfrutéis</p>
<div style="text-align:center">
<a href="https://space-pipes.adrianistan.eu/">Jugar en el navegador</a>
<br>
<a href="https://play.google.com/store/apps/details?id=eu.adrianistan.space_pipes.twa">Google Play</a>
<br>
<a href="https://github.com/aarroyoc/space-pipes">Código fuente</a>
</div>]]></description>
                <comments>https://blog.adrianistan.eu/space-pipes</comments>
                <pubDate>Sun, 22 Nov 2020 16:25:17 +0000</pubDate>
            </item>
        
            <item>
                <title>Tutorial de CHR (Constraint Handling Rules)</title>
                <link>https://blog.adrianistan.eu/tutorial-chr</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/tutorial-chr</guid>
                <description><![CDATA[<p><a href="http://constraint-handling-rules.org/">CHR</a> es un lenguaje de programación lógico basado en reglas, pero a diferencia de Prolog o miniKanren, se aplican "hacia delante". Diseñado en Alemania en 1991 por Thom Frühwirth, se trata de un lenguaje muy pequeño (tiene solo tres casos) y que normalmente se usa a través de otro lenguaje que le aporta expresividad (como Prolog, Haskell, C, Java o JavaScript). En este tutorial aprenderemos a usar CHR y usaremos Prolog como lenguaje base. </p>
<div style="text-align:center">
<img width="300" src="https://files.adrianistan.eu/chr-logo.gif"><br>
<small>El símbolo de CHR es el símbolo chino "chr"</small>
</div>
<h2>Almacén de hechos</h2>
<p>Lo primero que hay que saber, es que a diferencia de otros lenguajes lógicos, CHR mantiene estado de los hechos. Un programa CHR se compone de reglas y hechos. Las reglas manipulan los hechos. Cualquier programa CHR empieza cuando introducimos hechos a ese almacén. Una vez han sido añadidos, se revisa si hay reglas que se cumplen para esos hechos, si es así, se ejecutan, eliminando y/ añadiendo nuevos hechos, volviéndose a ejecutar la búsqueda de reglas a aplicar. Cuando ya no se puedan aplicar reglas, el programa habrá acabado.</p>
<p>He aquí un pseudocódigo:</p>
<pre><code>
HECHOS = lista de hechos
REGLAS = lista de reglas

loop:
    for REGLA in REGLAS:
        if REGLA es aplicable con HECHOS:
            ejecutar REGLA y modifica HECHOS
    if no se ha aplicado ninguna REGLA:
        salir
</code></pre>
<p>Este sistema de funcionamiento es similar a <a href="http://clipsrules.net/">CLIPS</a> o a <a href="https://www.drools.org/">Drools</a> y es lo que se llama "forward-chaining" o procesado de reglas hacia delante. Prolog en comparación es "backward-chaining" o hacia atrás.</p>
<h2>Las reglas</h2>
<p>CHR solo tiene tres tipos de reglas.</p>
<ul>
<li>Simpagation (\ <=>)</li>
<li>Simplifcation (<=>)</li>
<li>Propagation (==>)</li>
</ul>
<p>La sintaxis general se puede resumir en esto:</p>
<pre><code>
name @ retained \ discarded <=> guard | body.    Simpagation
name @ discarded <=> guard | body.      Simplification
name @ retained ==> guard | body.         Propagation
</code></pre>
<p>El campo name es puramente para el programador y es optativo.</p>
<p>A continuación van los hechos que se tienen que cumplir para disparar la regla. Es la parte izquierda de la regla. Dependiendo de la regla que estemos usando, los hechos se eliminan (discarded) o se mantienen (retained) del almacén. La regla Simpagation permite tener hechos que se descartan a la vez que hechos que no, mediante el separador \.</p>
<p>A la derecha de la regla, se ubica el guard, un elemento opcional que sirve para hacer comprobaciones más precisas sobre los hechos. Si se ejecuta satisfactoriamente, la regla continúa, si no, se cancela.</p>
<p>A la derecha del guard se ubica el body, donde podemos definir los nuevos hechos que se van guardar en el almacén.</p>
<h2>Un ejemplo tortillero</h2>
<p>Veamos un ejemplo en acción. Voy a usar la implementación de CHR de SWI Prolog, por lo que necesitaremos este programa antes.</p>
<pre><code>
:- use_module(library(chr)).

:- chr_constraint patata/0, huevo/0, cebolla/0, tortilla/0.

patata, huevo, cebolla <=> tortilla.
</code></pre>
<p>Que quiere decir, si tienes patata, huevo y cebolla en el almacén, quítalos y añade tortilla. Para empezar a ejecutarlo, vamos añadiendo hechos al almacén.</p>
<pre><code>
➜  chr git:(master) ✗ swipl example1.pl 
Welcome to SWI-Prolog (threaded, 64 bits, version 8.2.2)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
Please run ?- license. for legal details.

For online help and background, visit https://www.swi-prolog.org
For built-in help, use ?- help(Topic). or ?- apropos(Word).

?- cebolla, patata, huevo.
tortilla.

?- cebolla, patata.
patata,
cebolla.

?- cebolla, patata, huevo, huevo, patata.
patata,
huevo,
tortilla.

?- cebolla, patata, huevo, huevo, patata, cebolla.
tortilla,
tortilla.


</code></pre>
<p>Como veis, si existen los tres hechos en almacén, se dispara la regla y ese es el resultado que vemos en la terminal. Si no se puede aplicar la regla, se mantienen los hechos introducidos.</p>
<h2>Un ejemplo con Fibonacci</h2>
<p>Vamos a ver como se implementaría la típica función de obtener el número correspondiente a la secuencia de Fibonacci. Para ello vamos a usar dos tipos de hechos, fib, que guardan el índice y el valor y upto, que guarda hasta que índice queremos seguir calculando nuevos fib (este programa si no, sería infinito)</p>
<pre><code>
:- use_module(library(chr)).

:- chr_constraint fib/2, upto/1.

fib(A, AV), fib(B, BV), upto(N) ==> B is A+1, B < N | X is AV+BV, K is B+1, fib(K, X).
</code></pre>
<p>Básicamente lo que hacemos es coger un fib con índice A y valor AV, otro fib con índice B y valor BV y un upto con valor N. No eliminamos ninguno de esos hechos pero si la regla se dispara, ejecutamos primero el guard. El primero comprueba que B es el elemento siguiente de A, y por otro lado, que no nos estamos pasando con el upto. Una vez hecho eso, hacemos las sumas y añadimos otro fib. El uso de is no viene dado por CHR sino por Prolog, que hace de lenguaje host.</p>
<p>Para ejecutarlo, introduciríamos los primeros dos elementos de la secuencia en el almacén y un upto.</p>
<pre><code>
➜  chr git:(master) ✗ swipl example2.pl
Welcome to SWI-Prolog (threaded, 64 bits, version 8.2.2)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
Please run ?- license. for legal details.

For online help and background, visit https://www.swi-prolog.org
For built-in help, use ?- help(Topic). or ?- apropos(Word).

?- fib(1,1), fib(2,1), upto(10).
fib(10, 55),
fib(9, 34),
fib(8, 21),
fib(7, 13),
fib(6, 8),
fib(5, 5),
fib(4, 3),
fib(3, 2),
fib(2, 1),
fib(1, 1),
upto(10).

?- 
</code></pre>
<h2>Un ejemplo de caminos</h2>
<p>Para acabar CHR, vamos a ver un ejemplo más elaborado.</p>
<p>Imaginemos que tenemos un grafo tal que así. Los cables rojos representan conexiones entre los nodos. Cada nodo tiene una posición X e Y. Dado un punto, ¿hasta qué puntos podemos llegar siguiendo las líneas rojas?</p>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/ChrNetwork.png">
</div>
<pre><code>
:- use_module(library(chr)).

:- chr_constraint connected/2, origin/2, edge/4, node/2, world/0.


world <=> 
    node(1, 1),
    node(1, 2),
    node(2, 1),
    node(2, 2),
    node(3, 1),
    node(3, 2),
    node(4, 1),
    node(4, 2),
    edge(1, 1, 2, 1),
    edge(2, 1, 3, 1),
    edge(3, 1, 4, 1),
    edge(2, 1, 2, 2),
    edge(4, 1, 4, 2).

origin(X, Y) <=> connected(X, Y).
connected(X1, Y1), edge(X1, Y1, X2, Y2) \ node(X2, Y2) <=> connected(X2, Y2). 
</code></pre>
<p>Definimos varios tipos de hechos. connected sería un nodo conectado (representado por sus posiciones X e Y), origin el punto de origen, edge es una conexión entre dos puntos, node es un listado de puntos y world lo definimos para simplificar las consultas.</p>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/ChrExample.png">
</div>
<p>Para iniciar el cómputo, indicamos world (para cargar el resto de hechos del "mundo") y especificamos el punto de origen. Obtendremos bajo los hechos connected aquellos puntos conectados al punto origin.</p>

<p>Hasta aquí el tutorial de CHR, los ejemplos de código están subidos al <a href="https://github.com/aarroyoc/blog-ejemplos">repositorio de ejemplos de código del blog</a>. ¿Qué te parece este lenguaje? ¿Te parece sencillo? ¿Le ves aplicaciones? ¡Deja tu opinión en los comentarios!</p>


]]></description>
                <comments>https://blog.adrianistan.eu/tutorial-chr</comments>
                <pubDate>Wed, 18 Nov 2020 21:38:35 +0000</pubDate>
            </item>
        
            <item>
                <title>Teletexto #008</title>
                <link>https://blog.adrianistan.eu/teletexto-008</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/teletexto-008</guid>
                <description><![CDATA[<p>Bienvenidos a una nueva edición del Teletexto, la octava, cargada de enlaces interesantes que he ido recopilando.</p>
<div style="text-align: center">
<img width="500" src="https://files.adrianistan.eu/FranceTeletext.gif">
</div>
<ul>
<li><a href="https://vega.github.io/">Vega</a> y Vega Lite son unos lenguajes declarativos para expresar visualizaciones de datos, usando JSON y generando resultados en Canvas o SVG. Vega Lite es la versión de más alto nivel, ideal para empezar. Existen librerías para generar estos JSON compatibles con Vega desde diferentes lenguajes, por ejemplo, Altair con Python.</li>
<li>SomosBinarios escribe <a href="https://www.somosbinarios.es/elige-tecnologia-clasica/">Elige tecnología clásica</a> y tiene muy buenos puntos. Yo soy el primero que le gusta experimentar con tecnología novedosa pero a la hora de construir un producto de forma rápida y fiable, lo mejor es usar tecnologías ya establecidas.</li>
<li>No todos los sistemas operativos son UNIX-like o compatibles POSIX. Hoy en día son minoritarios (salvo Windows) pero tienen peculiaridades muy interesantes y características diferentes. En este artículo se repasan <a href="https://weinholt.se/articles/non-posix-filesystems/">diferentes sistemas de archivos no-POSIX.</a></li>
<li>SQL es el lenguaje más usado para operar con bases de datos relacionales. Lo que quizá no sepas es que antes de convertirse en un estándar (gracia a la presión de IBM y Oracle) existió otro lenguaje, que mucha gente consideraba mejor, QUEL. La base de datos más conocida que usaba QUEL era Ingres, que al sustituir QUEL por SQL pasó a llamarse Postgres. <a href="https://www.holistics.io/blog/quel-vs-sql/">Aquí podrás ver una comparación SQL vs QUEL</a>.</li>
<li>¿Estás empezando con Rust? Con este <a href="https://wiki.alopex.li/RustStarterKit2020">starter kit</a> sabrás que librerías son las más recomendables para usar en tus proyectos.</li>
<li>¿Cuántos proyectos ha cerrado Mozilla? En <a href="https://killedbymozilla.com/">Killed by Mozilla</a> encontrarás un interesante listado.</li>
<li>¿Cuántas bases de datos existen? En la <a href="https://dbdb.io/">base de datos de bases de datos</a> encontrarás respuesta.</li>
<li><a href="https://engineering.kablamo.com.au/posts/2020/falsehoods-about-map-coordinates/">Falsedades que los programadores creen sobre las coordenadas.</a></li>
<li><a href="https://www.hownormalami.eu/">¿Cómo eres de normal?</a> Esta web usa las técnicas más avanzadas de IA para enseñarte cómo las usan las compañías mientras las aplica sobre ti.</li>
<li>¿Quiéres un nombre de dominio libre con un nombre original? Encuentra el tuyo en <a href="https://www.domainsfortherestofus.com/#">Domains for the rest of us</a></li>
</ul>
<p>La canción para cerrar este teletexto es Rydeen de los japoneses Yellow Magic Orchestra.</p>
<div style="text-align:center">
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/Nv5jMLJQ9f4" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>]]></description>
                <comments>https://blog.adrianistan.eu/teletexto-008</comments>
                <pubDate>Sat, 07 Nov 2020 16:14:43 +0000</pubDate>
            </item>
        
            <item>
                <title>Mapaquiz, ¿y si la respuesta estuviera sobre el mapa?</title>
                <link>https://blog.adrianistan.eu/mapaquiz-pregunta-sobre-mapa</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/mapaquiz-pregunta-sobre-mapa</guid>
                <description><![CDATA[<p>Responder a una pregunta señalando un mapa. Quizá no todas las preguntas del mundo se puedan resolver mirando a un mapa, pero sí las suficientes como para prestarlas atención. <a href="https://mapaquiz.com">Mapaquiz</a> es una aplicación web para jugar y crear mapaquizs. ¿Qué es un mapaquiz? Un mapaquiz es un juego de preguntas y respuestas donde las respuestas están en el mapa. Además, Mapaquiz es el último proyecto que he estado desarrollando en mi tiempo libre, ¿echamos un vistazo?</p>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/MapaquizWeb.png" width="600">
</div>
<h2>Aprender jugando</h2>
<p>Podemos aprender sobre geografía con los mapaquizs más clásicos. También podemos probar los mapaquizs temáticos sobre Olimpiadas o Famosos del Cine. Jugar es muy simple, simplemente buscamos nuestro mapaquiz, vía página principal o mediante el buscador. Los mapaquizs tiene un idioma, actualmente se soportan dos, inglés y español, aunque en un futuro podría haber más idiomas. Podemos filtrar el idioma en la parte superior.</p>
<p>Una vez hayamos seleccionado nuestro mapaquiz se nos cargará un mapa y una pregunta a la derecha (en pantallas anchas) o abajo (en pantallas estrechas, tipo móvil). Tenemos una pregunta, un tiempo y una puntuación.</p>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/MapaquizOlimpiadas.png" width="600">
</div>
<p>A las preguntas que van apareciendo, podemos ir haciendo click en los diferentes territorios del mapa. Si nuestra respuesta es correcta, avanzaremos, si fallamos, se nos restará puntuación y deberemos volverlo a intentar. </p>
<p>Una vez hayamos acabado se nos mostrará nuestra puntuación final y el tiempo que hemos utilizado. Si has iniciado sesión en ese momento, y tu puntuación es alta, esta se quedará gradaba en el marcador para que el resto admire tu rapidez e inteligencia (o facilidad de buscar en Google).</p>
<p>Por último, podemos dejar un comentario sobre el mapaquiz en la parte más inferior.</p>

<h2>Creando nuestros propios mapaquizs</h2>
<p>Si tenemos una cuenta en Mapaquiz (que no requiere datos personales, solo se pide un correo electrónico, un nombre de usuario y una contraseña), podremos crear nuestros propios mapaquizs.</p>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/MapaquizDashboard.png" width="600">
</div>
<p>Para ello iremos a la sección "Mi Perfil", donde veremos los mapaquizs que hemos creado, así como la opción de crear uno nuevo y editar o borrar uno existente.</p>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/MapaquizCreate.png" width="600">
</div>
<p>Al crear un mapaquiz deberemos elegir un mapa base en el idioma en el que vayamos a crear el mapaquiz.</p>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/MapaquizEditor1.png" width="600">
<img src="https://files.adrianistan.eu/MapaquizEditor2.png" width="600">
</div>
<p>El editor nos permite añadir y eliminar preguntas. En la parte superior tendremos el mapa base. A la derecha introduciremos el texto de la pregunta y sobre el mapa seleccionaremos el territorio que queremos que sea la respuesta. No existe respuesta múltiple de momento. Al hacer click en "Añadir pregunta", añadiremos la pregunta al mapaquiz.</p>
<p>Abajo podremos ver las preguntas ya añadidas así como borrarlas. También está la sección de Ajustes Generales, que debemos rellenar con una descripción del mapaquiz, así como una foto para la portada.</p>
<p>Si todo está bien, veremos un botón de "¡Publicar en Mapaquiz!" y el mapaquiz estará disponible para el resto de usuarios.</p>
<h2>Detalles técnicos</h2>
<p><i>Muy bien Adrián todo esto que cuentas, pero ¿cómo funciona?</i></p>
<p>Realmente lo que hay detrás de Mapaquiz es bastante simple, ya que he intentado no añadir complejidad innecesaria. La base de datos es SQLite y allí se guardan los GeoJSON de los mapas base. El framework principal es Django, con alguna API en Django Rest Framework para el código JavaScript. El código JavaScript en realidad está hecho en TypeScript y usa la librería Leaflet para representar los mapas. Para alojar las imágenes se ha usado un Azure Storage Account. Para las búsquedas se usa la librería Lunr.py. Todo esto es empaquetable en Docker listo para usar con un nginx sobre una Raspberry Pi.</p>
<h2>¿Te gusta lo que ves?</h2>
<p>Pues comparte con tus amigos, crea mapaquizs, encuentra errores, propón mejoras,... </p>
<div style="text-align:center">
<a href="https://mapaquiz.com">Visita Mapaquiz</a>
</div>]]></description>
                <comments>https://blog.adrianistan.eu/mapaquiz-pregunta-sobre-mapa</comments>
                <pubDate>Fri, 30 Oct 2020 19:26:21 +0000</pubDate>
            </item>
        
            <item>
                <title>¿Qué es Idris y por qué es un lenguaje de programación tan interesante?</title>
                <link>https://blog.adrianistan.eu/que-es-idris-y-por-que-es-un-lenguaje-de-programacion-tan-interesante</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/que-es-idris-y-por-que-es-un-lenguaje-de-programacion-tan-interesante</guid>
                <description><![CDATA[<p><a href="https://www.idris-lang.org/">Idris</a> es un lenguaje de programación funcional donde los tipos tenían envidia de las funciones, de primer orden en un lenguaje funcional, y se convirtieron en elementos de primer orden también. Se trata de uno de los primeros lenguajes con soporte a tipos de primer orden. De esta forma podemos implementar <i>dependent types</i> y <i>quantitative types</i>, todo ello con una sintaxis muy limpia inspirada por Haskell. Quizá Idris no sea el próximo lenguaje que debas aprender, solo te lo recomendaría si estás suficientemente interesado, pero las cosas que propone pueden acabar llegando a otros lenguajes de programación en el futuro, y eso es lo verdaderamente interesante.</p>
<div style="text-align: center">
<img src="https://files.adrianistan.eu/Idris.png">
</div>
<p>La versión de Idris al momento de escribir esto es Idris2 0.2.1 y no se recomienda usar en nada serio. Idris está escrito en Idris y genera código nativo a través de Chez Scheme o Racket. También puede compilar a JavaScript. Sin embargo, vamos a ignorar como se instala, ya que al ser experimental, puede cambiar mucho. Si estáis interesados, decídmelo en los comentarios. Aquí nos vamos a fijar simplemente en como funciona.</p>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/IdrisCli.png">
</div>
<h2>Hola Mundo</h2>
<p>Para no perder las costumbres empezaremos con el típico Hola Mundo en Idris.</p>
<pre><code class="language-haskell">
main : IO ()
main = putStrLn "¡Hola mundo desde Idris!"
</code></pre>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/IdrisHelloWorld.png">
</div>
<p>Si conoces Haskell seguro que te resulta familiar el código (con una pequeña variación, : en vez de ::, es exactamente igual). Para los que no conozcan Haskell, la idea es muy simple. En la primera línea definimos una función main cuyo tipo es IO (), es decir, instrucciones para realizar entrada/salida. No toma ningún parámetro de entrada. En la segunda línea implementamos la función, llamamos a putStrLn que es una función compatible con IO () y que nos deja imprimir un texto por pantalla. En la foto se ve como se puede ejecutar el código desde el intérprete y desde el código nativo.</p>
<p>Un ejemplo más avanzado sería el siguiente código, que obtiene datos de teclado:</p>
<pre><code class="language-haskell">
module Main

main : IO ()
main = do
    putStr "¿Cuál es tu nombre?: "
    x <- getLine
    putStrLn ("¡Hola " ++ x ++ "!")
</code></pre>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/IdrisHelloInput.png">
</div>
<p>Al igual que en Haskell, cuando queremos combinar varias funciones que devuelven IO () (mónadas en general), combinadas con otras funciones si queremos, podemos usar la sintaxis do. Básicamente se convierte todo en una especie de función grande que combina en orden todas las llamadas a funciones que pongamos debajo. Usaremos el operador <- para obtener un valor de una función que devuelva una mónada (como IO ()), siempre dentro de la sintaxis do. En funciones que no son mónadas, podemos usar el operador let ... = </p>

<p>Vamos a ver como se definen las funciones sin mónadas. En Idris es obligatorio marcar el tipado de la función, y a continuación la implementación. Los dependent types no se pueden inferir, por tanto deberemos escribirlos siempre. Los tipos de los argumentos se separan mediante -> y el último es el tipo del valor de retorno. Para llamar a una función se escribe su nombre y separado por espacios sus argumentos. Todas las funciones en Idris devuelven algo. Podemos usar if/else, pero a diferencia de otros lenguajes, el else es obligatorio, y ambas sentencias deben devolver un valor.</p>
<p>Veamos un ejemplo con la función longer. Esta función nos devuelve la longitud de la cadena de texto más larga. La longitud de una cadena de texto se calcula mediante la función length.</p>
<pre><code class="language-haskell">
longer : String -> String -> Nat
longer str1 str2
	= let len1 = length str1
	      len2 = length str2 in
	  if len1 > len2 then len1 else len2
</code></pre>
<p>String es el tipo de las cadenas de texto, Nat el de los números naturales.</p>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/IdrisLonger.png">
</div>
<p>Tip: podemos usar :t en la consola para obtener el tipo de una función.</p>
<p>Con esto espero que entiendas, de forma básica, la parte no-novedosa de Idris, antes de entrar en la parte innovadora.</p>
<h2>Dependent Types</h2>
<p>¿Qué es un dependent type? Un dependent type es un tipo que depende del valor concreto que traiga consigo para poder ser completo. El ejemplo más fácil de ver es con listas. En muchos lenguajes tenemos un tipo para listas dinámicas. En Idris también, se llama List. Con este tipo sabemos cuando hacemos una función que vamos a tener una lista de un tipo en concreto, pero podríamos ir más allá y expresar en el tipo la longitud de la lista. Esto no quiere decir que la lista esté fijada a un tamaño, sino que en un determinado momento el tamaño de una lista puede ser n, y al salir de la función debe ser n+1. Veamos un ejemplo</p>
<pre><code class="language-haskell">
module Main

add : List Int -> Int -> List Int
add list new = new :: list
</code></pre>
<p>La función add, toma una lista de enteros, un entero, y devuelve un entero. El código es muy simple, simplemente usa el operador :: para añadir un nuevo elemento enfrente de la lista, y devuelve la lista.</p>
<p>Este código funciona, los tipos cuadran y el compilador no se queja. Sin embargo este código también funciona:</p>
<pre><code class="language-haskell">
module Main

add : List Int -> Int -> List Int
add list new = list
</code></pre>
<p>La filosofía detrás de Idris es que los tipos que usemos deben ser lo más precisos posible para reducir el número de bugs que pasan desapercibidos. Además, normalmente es más sencillo definir una relación de tipos que buscar una implementación que cumpla con ello.</p>
<p>Este ejemplo tonto lo podríamos resolver con dependent types, usando Vect:</p>
<pre><code class="language-haskell">
module Main

import Data.Vect

addV : Vect n Int -> Int -> Vect (1+n) Int
addV list new = new :: list
</code></pre>
<p>El tipo Vect se compone de la longitud de la lista y del tipo de la lista. Fíjate como en la lista de entrada la longitud de la lista es n, y en la lista de salida es 1+n. Realmente podemos introducir cualquier expresión que devuelva un número positivo (Nat) ahí, ya que, los tipos son elementos de primer orden, y como tal estos pueden ejecutar código, llamar a otras funciones, etc con tal de construir el tipo definitivo.</p>
<p>Con este tipado, ya es imposible cometer el fallo de antes:</p>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/IdrisDependentTypes.png">
</div>
<p>Fíjate como en el código que he intentado compilar, ambas implementaciones son iguales, pero solo la que usa Vect falla al compilar.</p>
<p>Hemos dicho que los tipos pueden llamar a funciones que definan los tipos, ya que los tipos son elementos de primer orden. Veamos un ejemplo:</p>
<p>Imagina que tenemos código para manipular sobre posiciones GPS. Estas son solamente una tupla de números decimales, la latitud y la longitud.</p>
<pre><code class="language-haskell">
module Main

getHomePlace : (Double, Double)
getHomePlace = (42.0, -4.0)
</code></pre>
<p>Esto sería totalmente válido, pero ¿podríamos hacer una función que nos devolviese (Double, Double) directamente? Por supuesto, ya que las funciones pueden devolver el tipo maestro Type. Estas funciones se suelen empezar por mayúsculas a diferencia de las demás.</p>
<pre><code class="language-haskell">
module Main

Position : Type
Position = (Double, Double)

getHomePlace : Position
getHomePlace = (42.0, -4.0)

sumPositions : Position -> Position -> Position
sumPositions (x1, y1) (x2, y2) = (x1 + x2, y1 + y2)
</code></pre>
<p>Pero esto solo es una parte de las posibilidades que nos abren los tipos dependientes. Otra cosa que podemos hacer es funciones con argumentos ilimitados, ya que el tipo se genera en función de lo que se encuentra el compilador. Sin entrar en muchos detalles, este código genera una función el cual el primer argumento es el número de argumentos que vas a sumar. Luego una primera variable (la cero) y luego tantos argumentos como hayamos indicado. Si algo falla, el código no compilará</p>
<pre><code class="language-haskell">
module Main

AdderType : Nat -> Type
AdderType Z = Int
AdderType (S k) = Int -> AdderType k

sumar : (numargs: Nat) -> Int -> AdderType numargs
sumar Z acc = acc
sumar (S k) acc = \next => sumar k (next + acc)
</code></pre>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/IdrisSumar.png">
</div>
<p>Con un poco más de trabajo pueden llegar a implementarse funciones como printf, con argumentos variables dependientes de una cadena de texto, de forma totalmente tipada.</p>
<h2>Esto no es todo</h2>
<p>Ya hemos visto bastante cosas interesantes. Esto no pretende ser un tutorial sobre Idris, que es un lenguaje grande, sino una introducción a alguna de las cosas novedosas que trae. En Idris 2 se han añadido los quantitative types, que viene a ser como representar mediante tipos las veces que se puede usar un valor. Por ejemplo, si tenemos una función que toma un valor cuyo tipo dice que solo se puede usar una vez, y lo usamos, ya no podremos usarlo más adelante, muy útil para, por ejemplo, gestionar ficheros o sockets.</p>
<p>Si queréis saber más sobre Idris, visitad su página y/o leed el libro <i>Type Driven Development with Idris</i>, que explica de forma muy didáctica el lenguaje.</p>

]]></description>
                <comments>https://blog.adrianistan.eu/que-es-idris-y-por-que-es-un-lenguaje-de-programacion-tan-interesante</comments>
                <pubDate>Sun, 27 Sep 2020 22:31:38 +0000</pubDate>
            </item>
        
            <item>
                <title>El Algoritmo Simplex</title>
                <link>https://blog.adrianistan.eu/algoritmo-simplex</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/algoritmo-simplex</guid>
                <description><![CDATA[<p>El algoritmo Simplex es el método más conocido para resolver problemas de programación lineal. Diseñado en 1947 por George Dantzig, nos ofrece una forma eficiente y genérica de resolver este tipo de problemas, de gran utilidad en la industria. Se trata de uno de <a href="http://www.uta.edu/faculty/rcli/TopTen/topten.pdf">los diez algoritmos más importantes del siglo XX</a>, según IEEE y el American Institute of Physics.</p>

<div style="text-align:center">
<img src="https://files.adrianistan.eu/Simplex.png">
</div>

<p>En este post vamos a explicar el algoritmo Simplex. Al final, encontraréis el código de la versión en Rust. Quería comentar el código directamente, pero creo que es un algoritmo demasiado complejo como para entenderlo directamente.</p>

<h2>¿Qué es la programación lineal?</h2>
<p>La programación lineal es un método para obtener el mejor resultado posible dado un modelo matemático que usa relaciones lineales. Un ejemplo de programación lineal sería, ¿cómo hay que distribuir las antenas de móvil para tener una cobertura completa gastando el menor dinero posible? Como este hay miles de problemas que pueden modelarse con programación lineal. Si quieres saber más sobre esto, hace tiempo hice una entrada explicando <a href="https://blog.adrianistan.eu/programacion-lineal-alcanzar-perfeccion">qué es la programación lineal más en detalle.</a></p>

<h2>Elaboración de la tabla</h2>
<p>El algoritmo Simplex se compone de dos etapas: encontrar una solución factible y después, mejorarla. Con solución factible nos referimos a una solución que cumpliría todas las ecuaciones que vamos a añadir pero que no tiene por qué ser óptima. Una vez tengamos eso, vamos a mejorar la solución hasta que ya no sea posible y siempre transitando entre soluciones factibles, pero mejores.</p>

<p>El algoritmo se basa en la existencia de una tabla, anotada en los extremos. En la primera fila tendremos un 1 seguido del renglón Z y el valor óptimo.</p>
<p>Las siguientes filas representan a las restricciones. Tienen anotada a la izquierda una variable, en el centro las restricciones en sí y en la última columna el valor de esa variable que hace óptimo el resultado.</p>
<p>En esta tabla puedes ver de forma esquemática la tabla:</p>
<div style="display: flex; justify-content: center">
<table border>
<tbody>
  <tr>
    <td>1</td>
    <td>Renglón Z</td>
    <td>Valor Óptimo</td>
  </tr>
  <tr>
    <td>Base</td>
    <td>Restricciones</td>
    <td>Valor Variable</td>
  </tr>
</tbody>
</table>
</div>
<p>El renglón Z se forma con la función objetivo cambiada de signo. El valor óptimo comienza en cero. Las restricciones no tienen modificaciones y el valor de variable inicial es el valor al otro lado de la ecuación de la restricción. Lo más complicado es la elección de variables de la base. Tiene que formar una solución factible desde el principio. Manualmente pueden hacerse muchas cosas, pero de forma automatizada es más complejo.</p>

<p>Una forma fácil de encontrar una solución factible inicial es traducir las inecuaciones mayor o igual que y menor o igual que en igualdades, añadiendo variables de holgura (o slack). Esto en muchos casos podría ser suficiente, pero para asegurarnos, podemos introducir variables artificiales.</p>
<p>El problema de añadir variables artificiales es que los resultados pueden ser falsos, ya que amplían el "número de soluciones". Existen varias formas de añadir variables artificiales sin dar respuestas falsas. Una es el método de la doble fase, que consiste en realizar dos tablas Simplex. Otra solución es el método de la gran M, que consiste en penalizar fuertemente las variables artificiales, para que nunca puedan entrar a la base.</p>
<p>Veamos un ejemplo real:</p>
<pre><code>
minimize -3x +  y - 2z
    with  2x - 2y + 3z <= 5
           x +  y -  z <= 3
           x -  y +  z <= 2
</code></pre>
<p>La tabla correspondiente sería la siguiente:</p>
<div style="display:flex;justify-content:center">
<table border>
<tbody>
  <tr>
    <td></td>
    <td>z</td>
    <td>x1</td>
    <td>x2</td>
    <td>x3</td>
    <td>x4</td>
    <td>x5</td>
    <td>x6</td>
    <td>LD</td>
  </tr>
  <tr>
    <td>z</td>
    <td>1</td>
    <td>3</td>
    <td>-1</td>
    <td>2</td>
    <td>0</td>
    <td>0</td>
    <td>0</td>
    <td>0</td>
  </tr>
  <tr>
    <td>x4</td>
    <td>0</td>
    <td>2</td>
    <td>-2</td>
    <td>3</td>
    <td>1</td>
    <td>0</td>
    <td>0</td>
    <td>5</td>
  </tr>
  <tr>
    <td>x5</td>
    <td>0</td>
    <td>1</td>
    <td>1</td>
    <td>-1</td>
    <td>0</td>
    <td>1</td>
    <td>0</td>
    <td>3</td>
  </tr>
  <tr>
    <td>x6</td>
    <td>0</td>
    <td>1</td>
    <td>-1</td>
    <td>1</td>
    <td>0</td>
    <td>0</td>
    <td>1</td>
    <td>2</td>
  </tr>
</tbody>
</table>
</div>
<p>En este ejemplo, hemos creado 3 variables de slack, para convertir los menor o igual en igual. Estas variables de slack hacen la matriz identidad en su parte de las restricciones, por lo que pueden entrar a la base. Ahora bien, estas variables no aparecen en la función objetivo, así que las marcamos a cero en el renglón Z. Con variables artificiales, se haría todo igual, salvo que en vez de cero, se debe poner un M muy muy grande.</p>
<h2>Pivotando</h2>
<p>El segundo paso es mejorar la solución hasta llegar al óptimo. ¿Cuándo tenemos un óptimo? Cuando todo el renglón Z sea menor o igual a cero. Si además, todos los elementos de Z restados de su valor en la función objetivo son estrictamente menores que cero, la solución es única.</p>
<p>Nuestro objetivo, por tanto, es conseguir valores negativos en el renglón Z.</p>
<p>Para ello vamos a introducir una variable en la base (que no estuviese ya) y vamos a sacar una. La variable de entrada puede ser cualquiera en cuyo renglón Z sea positiva.</p>

<p>En nuestro caso concreto, vamos a elegir introducir x1, cuyo renglón Z es 3.</p>
<p>La variable de salida es aquella que en la columna de la variable de entrada es positiva y tiene el valor más pequeño en la división de la columna LD por el propio valor. Si no se encuentran valores positivos, el problema no tiene límite y la solución es infinita.</p>
<p>En nuestro ejemplo, tendríamos que calcular para x4=5/2, para x5=3/1 y para x6=2/1. El valor más pequeño el de x6, así que x6 sale de la base.</p>
<p>Ese punto, marcado con negrita, va a ser el pivote.</p>
<div style="display:flex;justify-content:center">
<table border>
<tbody>
  <tr>
    <td></td>
    <td>z</td>
    <td>x1</td>
    <td>x2</td>
    <td>x3</td>
    <td>x4</td>
    <td>x5</td>
    <td>x6</td>
    <td>LD</td>
  </tr>
  <tr>
    <td>z</td>
    <td>1</td>
    <td>3</td>
    <td>-1</td>
    <td>2</td>
    <td>0</td>
    <td>0</td>
    <td>0</td>
    <td>0</td>
  </tr>
  <tr>
    <td>x4</td>
    <td>0</td>
    <td>2</td>
    <td>-2</td>
    <td>3</td>
    <td>1</td>
    <td>0</td>
    <td>0</td>
    <td>5</td>
  </tr>
  <tr>
    <td>x5</td>
    <td>0</td>
    <td>1</td>
    <td>1</td>
    <td>-1</td>
    <td>0</td>
    <td>1</td>
    <td>0</td>
    <td>3</td>
  </tr>
  <tr>
    <td>x6</td>
    <td>0</td>
    <td><strong>1</strong></td>
    <td>-1</td>
    <td>1</td>
    <td>0</td>
    <td>0</td>
    <td>1</td>
    <td>2</td>
  </tr>
</tbody>
</table>
</div>
<p>Se pivota de la siguiente forma:</p>
<p>La fila del pivote se tiene que dividir entre el valor del pivote (en este caso hay que dividir entre 1, se queda todo igual). Después se va fila por fila restándolas el valor de la fila del pivote por el elemento de la misma columna de esa fila que el pivote. Es decir, en la primera fila, la del renglón Z, hay que restar la fila de x6 multiplicada por 3. Debería quedarnos al finalizar una columna con todo ceros, salvo el pivote.</p>
<p>El resultado final es el siguiente:<p>
<div style="display:flex;justify-content:center">
<table border>
<tbody>
  <tr>
    <td></td>
    <td>z</td>
    <td>x1</td>
    <td>x2</td>
    <td>x3</td>
    <td>x4</td>
    <td>x5</td>
    <td>x6</td>
    <td>LD</td>
  </tr>
  <tr>
    <td>z</td>
    <td>1</td>
    <td>0</td>
    <td>2</td>
    <td>-1</td>
    <td>0</td>
    <td>0</td>
    <td>-3</td>
    <td>-6</td>
  </tr>
  <tr>
    <td>x4</td>
    <td>0</td>
    <td>0</td>
    <td>0</td>
    <td>1</td>
    <td>1</td>
    <td>0</td>
    <td>-2</td>
    <td>1</td>
  </tr>
  <tr>
    <td>x5</td>
    <td>0</td>
    <td>0</td>
    <td>2</td>
    <td>-2</td>
    <td>0</td>
    <td>1</td>
    <td>-1</td>
    <td>1</td>
  </tr>
  <tr>
    <td>x1</td>
    <td>0</td>
    <td>1</td>
    <td>-1</td>
    <td>1</td>
    <td>0</td>
    <td>0</td>
    <td>1</td>
    <td>2</td>
  </tr>
</tbody>
</table>
</div>
<p>Ahora debería entrar la variable x2 y saldría x5.</p>
<div style="display:flex;justify-content:center">
<table border>
<tbody>
  <tr>
    <td></td>
    <td>z</td>
    <td>x1</td>
    <td>x2</td>
    <td>x3</td>
    <td>x4</td>
    <td>x5</td>
    <td>x6</td>
    <td>LD</td>
  </tr>
  <tr>
    <td>z</td>
    <td>1</td>
    <td>0</td>
    <td>0</td>
    <td>1</td>
    <td>0</td>
    <td>-1</td>
    <td>2</td>
    <td>-7</td>
  </tr>
  <tr>
    <td>x4</td>
    <td>0</td>
    <td>0</td>
    <td>0</td>
    <td>1</td>
    <td>1</td>
    <td>0</td>
    <td>-2</td>
    <td>1</td>
  </tr>
  <tr>
    <td>x2</td>
    <td>0</td>
    <td>0</td>
    <td>1</td>
    <td>-1</td>
    <td>0</td>
    <td>0.5</td>
    <td>-0.5</td>
    <td>0.5</td>
  </tr>
  <tr>
    <td>x1</td>
    <td>0</td>
    <td>1</td>
    <td>0</td>
    <td>0</td>
    <td>0</td>
    <td>0.5</td>
    <td>0.5</td>
    <td>2.5</td>
  </tr>
</tbody>
</table>
</div>
<p>Por último, debe entrar x3 y salir x4.</p>
<div style="display:flex;justify-content:center">
<table border>
<tbody>
  <tr>
    <td></td>
    <td>z</td>
    <td>x1</td>
    <td>x2</td>
    <td>x3</td>
    <td>x4</td>
    <td>x5</td>
    <td>x6</td>
    <td>LD</td>
  </tr>
  <tr>
    <td>z</td>
    <td>1</td>
    <td>0</td>
    <td>0</td>
    <td>0</td>
    <td>-1</td>
    <td>-1</td>
    <td>0</td>
    <td>-8</td>
  </tr>
  <tr>
    <td>x3</td>
    <td>0</td>
    <td>0</td>
    <td>0</td>
    <td>1</td>
    <td>1</td>
    <td>0</td>
    <td>-2</td>
    <td>1</td>
  </tr>
  <tr>
    <td>x2</td>
    <td>0</td>
    <td>0</td>
    <td>1</td>
    <td>0</td>
    <td>1</td>
    <td>0.5</td>
    <td>-2.5</td>
    <td>1.5</td>
  </tr>
  <tr>
    <td>x1</td>
    <td>0</td>
    <td>1</td>
    <td>0</td>
    <td>0</td>
    <td>0</td>
    <td>0.5</td>
    <td>0.5</td>
    <td>2.5</td>
  </tr>
</tbody>
</table>
</div>
<p>En este punto ya hemos alcanzado el óptimo, -8, aunque no es la única solución posible que da este valor. La solución del problema es x1=2.5, x2=1.5 y x3=1.</p>

<h2>Resolviendo con la librería simplex de Rust</h2>
<p>Estas mismas operaciones (y alguna más) son las que realiza por debajo la librería simplex, disponible para Rust. He intentado que la librería sea lo más sencilla de usar. Con el siguiente código se resuelve el mismo problema:</p>
<pre><code class="language-rust">
use simplex::*;

fn main(){
    let program = Simplex::minimize(&vec![-3.0, 1.0, -2.0])
    .with(vec![
        SimplexConstraint::LessThan(vec![2.0, -2.0, 3.0], 5.0),
        SimplexConstraint::LessThan(vec![1.0, 1.0, -1.0], 3.0),
        SimplexConstraint::LessThan(vec![1.0, -1.0, 1.0], 2.0),
    ]);
    let mut simplex = program.unwrap();
    match simplex.solve() {
        SimplexOutput::UniqueOptimum(x) => println!("{}", x),
        SimplexOutput::MultipleOptimum(x) => println!("{}", x),
        _ => panic!("No solution or unbounded"),
    }
    println!("{:?}", simplex.get_var(1));
    println!("{:?}", simplex.get_var(2));
    println!("{:?}", simplex.get_var(3));
}
</code></pre>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/SimplexRustCLI.png">
</div>
<p>Existe un algoritmo similar llamado Revised Simplex. Este ocupa menos memoria y las implementaciones más potentes como CPLEX o Xpress se suelen basar en él. Échale un ojo si te interesa.</p> ]]></description>
                <comments>https://blog.adrianistan.eu/algoritmo-simplex</comments>
                <pubDate>Sat, 12 Sep 2020 20:10:22 +0000</pubDate>
            </item>
        
            <item>
                <title>Supertutorial de Prolog: aprende Prolog en español</title>
                <link>https://blog.adrianistan.eu/supertutorial-prolog</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/supertutorial-prolog</guid>
                <description><![CDATA[<p>Si estás aquí es porque has decidido aprender <a href="https://es.wikipedia.org/wiki/Prolog">Prolog</a> o por lo menos, te ha entrado la curiosidad sobre este lenguaje del paradigma lógico. ¡Sabia elección! En este artículo y los siguientes (enlazados al final) podrás aprender todo lo básico de Prolog.</p>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/SWIPrologLogo.png">
</div>
<p>Antes de nada, avisar que ya tenía <a href="https://blog.adrianistan.eu/introduccion-a-prolog-tutorial-en-espanol">un tutorial de Prolog aquí</a>, sin embargo, creo que comete varias malas prácticas. En vez de editarlo, prefiero empezar de cero y además explicar más cosas.</p>
<h2>Hola Mundo</h2>
<p>Antes de entrar en la teoría, abre tu editor y copia/pega lo siguiente a un fichero llamado socrates.pl:</p>
<pre><code class="language-prolog">
human(socrates).
mortal(X) :- human(X).
</code></pre>
<p>La explicación es sencilla. En la primera línea afirmamos que socrates es humano, en la segunda afirmamos que para que X sea mortal, antes tiene que ser humano. Todo esto lo detallaré más adelante.</p>
<p>Ahora necesitarás un intérprete de Prolog. El más popular y el que actualmente recomiendo es <a href="https://www.swi-prolog.org/">SWI-Prolog</a> disponible para Windows, Mac, Linux y más.
<p>Con SWI-Prolog instalado ejecuta desde un terminal:</p>
<pre><code>
swipl socrates.pl
</code></pre>
<p>Ahora podemos realizar consultas. Prolog es un lenguaje inicialmente diseñado en base a consultas lógicas. Vamos a pedir a Prolog que nos diga si socrates es humano.</p>
<pre><code>
?- human(socrates).
true.
</code></pre>
<p>A lo que responde true, es decir, es cierto, socrates es humano.</p>
<p>¿socrates es mortal?</p>
<pre><code>
?- mortal(socrates).
true.
</code></pre>
<p>¿Y adrian?</p>
<pre><code>
?- mortal(adrian).
false.
</pre></code>
<p>¿Cuántos humanos hay?</p>
<pre><code>
?- human(X).
   X = socrates.
</code></pre>
<p>Solo socrates. ¿Y cuantos mortales?</p>
<pre><code>
?- mortal(X).
   X = socrates.
</code></pre>
<p>Y si queremos un hola mundo más clásico, podemos usar write y nl.</p>
<pre><code>
?- write('Hola Mundo'), nl.
Hola Mundo
   true.
</code></pre>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/PrologHelloWorld.png">
</div>
<h2>Historia de Prolog</h2>
<p>Prolog (PROgrammation en LOGique) nació en 1972 en la Universidad de Provenza en la ciudad francesa de Marsella (mismo año que C). Diseñado por Alan Colmerauer y Philip Roussel para trabajar en inteligencia artificial, siempre ha estado ligado a este campo. En 1995 fue estandarizado, bajo ISO (como C++, Ada o Fortran). Durante este tiempo se han creado muchas implementaciones. Algunas las puedes ver en <a href="">Universo Prolog</a></p>
<h2>Sintaxis de Prolog</h2>
<p>La sintaxis de Prolog es muy simple. Solo existen tres elementos:</p>
<ul>
<li>Términos</li>
<li>Comentarios</li>
<li>Quasiquotes</li>
</ul>
<p>Los dos últimos son elementos excepcionales. Un comentario empieza por el símbolo <code>%</code> seguido del comentario. Los comentarios son textos que se eliminan al procesar la entrada y son útiles para anotar cosas. Los quasiquotes son elementos que permiten introducir sintaxis diferente a la de Prolog en Prolog (HTML, JSON, LISP, JavaScript, ...) pero es algo bastante avanzado.</p>
<p>Los términos, son entonces, el 95% del código Prolog. Estos llevan un punto al final si no forman parte de ningún otro término de forma recursiva. Los términos se dividen a su vez en:</p>
<ul>
<li>Constantes</li>
<li>Variables</li>
<li>Compuestos</li>
</ul>
<p>Los constantes son términos que pueden ser átomos, números (y en algunas implementaciones también hay strings). Los átomos son cadenas de texto que empiezan por letra minúscula o llevan comillas simples. Los átomos son elementos inmutables que representan algo. La cualidad del átomo es que solo pueden ser iguales a ellos mismos. Los números son bastante explicativos, cadenas de texto compuestas de dígitos. Prolog no distingue entre ints, floats, etc y muchas implementaciones disponen de precisión arbitraria en los números. Los strings empiezan por comillas dobles y representan cadenas de texto "manipulables". Saber donde se usa string y donde atom en sistemas que usan ambos elementos es algo que se aprende con la práctica.</p>
<p>Las variables son cadenas de texto que empiezan por una letra mayúscula. El funcionamiento de las variables es ligeramente diferente al de los lenguajes de programación tradicionales. Una variable puede entenderse como la solución de un problema (que será un término). Al principio del programa, cuando todavía no se han recorrido condiciones del problema, la variable no estará ligada, eso querrá decir, que puede ser cualquier cosa todavía. Según vaya avanzando el programa, Prolog unificará (más adelante esto) la variable con algún otro término y puede que la variable acabe ligada, es decir, a partir de ese momento, se comporta como si fuese el otro término. Esto sería, en esencia, añadir una restricción sobre la solución.</p>
<p>En el apartado de unificación veremos esto más en detalle.</p>
<p>Los compuestos son términos recursivos, se componen de un átomo, que llamaremos functor, y entre paréntesis y separados por comas, más términos. Esto puede parecer una llamada a una función, pero no. En Prolog no hay funciones. Simplemente es un término compuesto varios términos dentro de él.</p>
<pre><code class="language-prolog">
% Un comentario
socrates. % Un átomo
15. % Un número
libro(don_quijote, cervantes). % Un compuesto. libro es functor, don_quijote y cervantes son los otros términos.
Variable. % Una variable
serie('Los Simpson', autor(Nombre, Apellido)). % Un compuesto más complejo
</code></pre>
<p>Al conjunto de compuestos con el mismo functor se le llama predicado y tiene una aridad, que es el número de términos que tiene entre paréntesis.</p>
<p>Además de esto, los términos pueden llevar asociado un cuerpo. El cuerpo es un conjunto de predicados que tienen que ser verdaderos para poder afirmar que el término es verdadero. A esto se le llama regla. Si el término no lleva cuerpo, se dice que es un hecho y es verdadero siempre.</p>
<pre><code class="language-prolog">
human(socrates). % Afirmamos que socrates es humano (hecho)
mortal(X) :- human(X). % Afirmamos que para que X sea mortal, antes tiene que ser humano (regla)
% Otra forma de verlo, todos los humanos son mortales
</code></pre>
<h2>Semántica de Prolog</h2>
<p>Al revisar la sintaxis ha surgido un poco algo de la semántica, es decir, ¿qué significa lo que hacemos? Los programas Prolog son una base de conocimiento (como una base de datos pero más potente). Esta base de conocimiento es dinámica y el código fuente es simplemente el estado inicial de la base de "datos".</p>
<p>Inicialmente Prolog se diseña como un lenguaje orientado a consultas, como SQL, donde tenemos un terminal donde ir escribiendo pequeñas sentencias.</p>
<p>En el terminal escribimos predicados para ver si son ciertos o no, dado el conocimiento del programa. Cuando introducimos variables, busca valores para las variables que cumplan el predicado.</p>
<p>Prolog implementa un algoritmo de búsqueda (backtracking) por nosotros.</p>
<h3>Backtracking</h3>
<p>El algoritmo de backtracking es bastante simple y consiste en lo siguiente:</p>
<ol>
<li>Busca algún compuesto con el mismo functor, en la base de conocimiento (de arriba a abajo).</li>
<li>Si es un hecho, unifica con él. (explicación más adelante)</li>
<li>Si es una regla, pasa a tratar de demostrar el cuerpo de la regla.</li>
<li>Si llega a un punto en el que no puede continuar, falla. Vuelve hacia atrás, deshace todas las unificaciones realizadas y si hay algún punto de elección, toma otra ruta.</li>
</ol>
<p>Podemos pensar que el algoritmo va haciendo un árbol intentando encontrar algo en la base de conocimiento que satisfaga el predicado de entrada.</p>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/Backtracking.png">
</div>
<p>O viendo cada predicado, podemos ver como puede pasar Prolog en un sentido hacia delante o hacia atrás.</p>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/PrologFlow.png">
</div>
<h2>Hola mundo, otra vez</h2>
<p>Ahora que sabemos todo esto de como funciona Prolog por debajo podemos volver a ejecutar el Hola Mundo del principio y tratar de ver como funciona. Para ello te recomiendo activar el modo trace, que nos deja ver que va haciendo Prolog paso a paso. Escribe trace antes de ejecutar la consulta. Por ejemplo:
<pre><code>
?- trace, mortal(X).
   Call: (11) mortal(_6364) ? creep
   Call: (12) human(_6364) ? creep
   Exit: (12) human(socrates) ? creep
   Exit: (11) mortal(socrates) ? creep
X = socrates.
</code></pre>
<h2>Unificación</h2>
<p>… o ¿pueden juntarse dos expresiones en una sin contradecirse?</p>
<p>En Prolog, la operación más básica es la unificación, que podemos realizar mediante el operador <code>=</code> pero que igualmente se realiza cuando busca en la base de conocimiento. La idea es muy simple, ¿podemos asumir que dos términos pueden juntarse en uno solo? Para ello se realiza lo siguiente.</p>
<ul>
<li>Si hay una variable y una constante, la variable queda ligada a la constante.</li>
<li>Si hay dos constantes, se comprueba que sean iguales.</li>
<li>Si hay dos variables, se recuerda que ambas tienen que valer lo mismo, a partir de ese momento.</li>
<li>Y esto se forma recursiva para los compuestos.</li>
</ul>
<p>Veamos algunos ejemplos:</p>
<pre><code class="language-prolog">
?- a = b.
   false.
?- a = a.
   true.
?- a = X.
   X = a.
?- X = Y.
   X = Y.
?- 4+5=3+6.
   false.
?- book(Name, Author) = book('Don Quijote', 'Cervantes').
   Name = 'Don Quijote', Author = 'Cervantes'.
</code></pre>
<p>Es interesante prestar atención a que que una cosa esté en un lado u otro es indiferente. Además, es interesante como 4+5=3+6 no es true. Esto es debido a que Prolog, con unificación simplemente, compara términos pero no ejecuta operaciones aritméticas. Así pues 4+5 no es el mismo término que 3+6, ya que se escriben de forma diferente.</p>
<h2>Operadores</h2>
<p>Ya hemos visto como funciona el operador unificación (=). Veamos el resto de los operadores de Prolog:</p>
<ul>
<li><code>\=</code> es el operador de no unificación. Si tenemos <code>a \= b</code>, será cierto si a y b no unifican.</li>
<li><code>,</code> es el operador lógico AND. Si tenemos <code>a,b</code> , será cierto si tanto a como b lo son.</li>
<li><code>;</code> es el operador lógico OR. Si tenemos <code>a;b</code>, será cierto si a o b son ciertos. Ambos ciertos también valdría.</li>
<li><code>\+</code> es el operador lógico NOT. Si tenemos <code>\+a</code>, será cierto si a no lo es.</li>
<li><code>is</code> es el operador <code>=</code> pero además evalúa el lado derecho antes, permitiendo realizar aritmética. <code>X is 2+2.</code> es el equivalente a <code>X = 4.</code></li>
<li><code>=:=</code> es el operador <code>=</code> pero evalúa ambos lados antes. Aquí <code>4+5 =:= 3+6</code> es equivalente a <code>9 = 9</code>.
</li>
</ul>
<h2>Ejemplo: rutas aéreas</h2>
<p>Vamos a ver un ejemplo más avanzado de programa Prolog. Se trata de un programa que tiene un listado de vuelos configuradas, con un precio entre dos aeropuertos. Vamos a hacer un programa que nos calcule cuanto nos costaría hacer el viaje de ida y vuelta (si es que es posible).</p>
<pre><code class="language-prolog">
vuelo(mad,bcn,200).
vuelo(bcn,mad,100).

idavuelta(X, Y, Precio) :-
    vuelo(X, Y, P1),
    vuelo(Y, X, P2),
    Precio is P1+P2.
</code></pre>
<p>En el terminal de Prolog:</p>
<pre><code>
?- idavuelta(mad,bcn,X).
   X = 300.
?- idavuelta(bcn,mad,X).
   X = 300.
?- idavuelta(Origen,Destino,Precio).
   Origen = mad, Destino = bcn, Precio = 300
;  Origen = bcn, Destino = mad, Precio = 300.
?- idavuelta(mad,nyc,X).
   false.
</code></pre>
<p>Vamos a añadir un poco más de complejidad al programa. En la vida real, no existen vuelos directos que conecten todas las ciudades con todas, muchas veces hay que hacer escala. Por ello vamos a añadir que vuelo tenga una regla para vuelos que realizan escalas.</p>
<pre><code class="language-prolog">
vuelo(mad,bcn,200).
vuelo(bcn,mad,100).
vuelo(bcn,vll,30).
vuelo(vll,bcn,30).
vuelo(mad,vlc,50).
vuelo(vlc,mad,50).

vuelo(X, Y, Precio) :-
    vuelo(X, Z, P1),
    vuelo(Z, Y, P2),
    Precio is P1+P2.

idavuelta(X, Y, Precio) :-
    vuelo(X, Y, P1),
    vuelo(Y, X, P2),
    Precio is P1+P2.
</code></pre>
<p>Hemos añadido vuelos de ida y vuelta entre BCN y VLL y entre MAD y VLC. Ahora podemos preguntar por el precio del vuelo entre VLL y VLC, que lógicamente será, VLL->BCN->MAD->VLC (30+100+50). También el precio de ida y vuelta que será 180 más la vuelta, que son 280.</p>
<pre><code>
?- vuelo(vll, vlc, X).
   X = 180
?- idavuelta(vll, vlc, X).
   X = 460
</code></pre>
<p>Aunque, claro, estas soluciones no son únicas. Podemos dar vueltas entre MAD y BCN el tiempo que queramos. Por eso Prolog, nos ofrece más soluciones, que podemos obtener presionando la letra N</p>
<pre><code>
?- vuelo(vll, vlc, X).
   X = 180
;  X = 480
;  X = 780
;  X = 1080
;  X = 1380
;  X = 1680
;  X = 1980
;  X = 2280
;  X = 2580
;  X = 2880
;  X = 3180
;  X = 3480
;  X = 3780
;  X = 4080
;  ...
</code></pre>
<h2>Listas en Prolog</h2>
<p>La lista en Prolog es una estructura de datos básica. Es una lista enlazada, implementada mediante términos compuestos, pero a la que se le aplica azúcar sintáctico para que sea más legible. Las listas se definen con corchetes y se puede usar la barra vertical | para separar el primer elemento del resto. Además existen diversos predicados estándar para trabajar con listas como length/2 (longitud de una lista), member/2 (pertenencia a una lista), append/3 (combinar listas), nth0 (elemento N contando desde cero en una lista), nth1 (elemento N contando desde 1 en una lista).</p>
<p>Como son predicados, pueden trabajar en varios sentidos. Por ejemplo con length/2:</p>
<ul>
<li><code>length([a,b,c], 3).</code> nos permite comprobar que la lista [a,b,c] tiene 3 elementos.</li>
<li><code>length([a,b,c], N).</code> nos permite obtener la longitud de la lista.</li>
<li><code>length([a,b,c], N), N < 5.</code> nos permite comprobar que la longitud de la lista es menor que 5.</li>
<li><code>length(X, 3).</code> nos permite obtener una lista con tres variables no ligadas en X.</li>
</ul>
<p>Veamos algunos ejemplos más:</p>
<pre><code>
?- length([a,b,c], N).
   N = 3.
?- length(X, 5).
   X = [_A,_B,_C,_D,_E].
?- member(X, [socrates,platon,aristoteles]).
   X = socrates
;  X = platon
;  X = aristoteles
?- member(platon, [socrates, platon, aristoteles]).
   true
?- append([a,b],[c,d], X).
   X = "abcd".
?- append([a,b],X,[a,b,c,d]).
   X = "cd".
?- append(X,Y,[a,b,c,d]).
   X = [], Y = "abcd"
?- append(X,Y,[a,b,c,d]).
   X = [], Y = "abcd"
;  X = "a", Y = "bcd"
;  X = "ab", Y = "cd"
;  X = "abc", Y = "d"
;  X = "abcd", Y = []
</code></pre>
<p>Un ejemplo de uso del operador | es el siguiente, para implementa la suma de toda una lista de números.</p>
<pre><code class="language-prolog">
suma([], 0).
suma([H|T], S) :-
    suma(T, S1),
    S is H+S1.
</code></pre>
<p>H es una variable que unifica con el primer elemento de la lista, mientras que T unifica con el resto de la lista. Esto lo podemos usar como:</p>
<pre><code>
?- suma([1,2,3,4], X).
X = 10.
</code></pre>
<h2>Metapredicados</h2>
<p>Por último, conviene repasar algunos metapredicados de Prolog. Los metapredicados son predicados que trabajan con otros predicados y son extremadamente útiles.</p>
<ul>
<li><code>asserta/1 y assertz/1</code> nos permiten añadir términos a la base de conocimiento, tanto hechos como reglas. Dicho de otra forma, nos permite añadir código en tiempo de ejecución.</li>
<li><code>retractall/1</code> nos permite eliminar términos de la base de conocimiento que unifiquen con el término que indiquemos.</li>
<li><code>findall/3</code> encuentra todas las posibles soluciones a un predicado y las almacena en una lista.</li>
<li><code>forall/2</code> es un predicado que es cierto si para todos las posibles soluciones de un predicado se da una condición que es cierta.</li>
<li><code>maplist/2...8</code> es un predicado que permite operar con hasta 8 listas en paralelo llamando a un predicado auxiliar.</li>
</ul>
<p>Hagamos un ejemplo con assertz y retractall:</p>
<pre><code>
?- human(X).
false.
?- assertz(human(socrates)).
   true.
?- assertz(human(platon)).
   true.
?- human(X).
   X = socrates
;  X = platon.
?- retractall(human(X)).
?- human(X).
   false.
</code></pre>
<p>Ahora veamos un ejemplo del resto de predicados. Teniendo este fichero:</p>
<pre><code class="language-prolog">
parent(juancarlos, felipe).
parent(juancarlos, cristina).
parent(juancarlos, elena).

married(felipe).
married(cristina).
divorced(elena).
</code></pre>
<p>Podemos ejecutar los siguientes comandos:</p>
<pre><code class="language-prolog">
?- forall(parent(juancarlos, X), married(X)).
   false.
?- forall(parent(juancarlos, X), (married(X);divorced(X))).
   true.
?- findall(X, married(X), Married).
   Married = [felipe,cristina].
?- maplist(write, [felipe, cristina]).
felipecristina   true
</code></pre>

<h2>Continúa...</h2>
<p>Con esto ya tienes suficiente como para realizar programas simples y medianos en Prolog, ya que, en esencia, Prolog es un lenguaje compacto. Sin embargo el lenguaje tiene muchas más cosas interesantes y que merece la pena ver, he aquí algunos enlaces de interés dentro de este blog:</p>
<ul>
<li><a href="https://blog.adrianistan.eu/sudoku-prolog">Sudoku en Prolog</a></li>
<li><a href="https://blog.adrianistan.eu/prolog-dcg-gramaticas-clausula-definida">DCGs en Prolog</a></li>
<li><a href="https://blog.adrianistan.eu/debug-grafico-prolog">Debug gráfico en Prolog</a></li>
<li><a href="https://blog.adrianistan.eu/programacion-web-prolog">Programación web en Prolog</a></li>
<li><a href="https://blog.adrianistan.eu/universo-prolog">El Universo Prolog</a></li>
</ul>


 ]]></description>
                <comments>https://blog.adrianistan.eu/supertutorial-prolog</comments>
                <pubDate>Sat, 29 Aug 2020 09:37:47 +0000</pubDate>
            </item>
        
            <item>
                <title>Teletexto #007</title>
                <link>https://blog.adrianistan.eu/teletexto-007</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/teletexto-007</guid>
                <description><![CDATA[<p>No me he olvidado de vosotros. El blog ha estado un poco huérfano debido a mi nuevo trabajo y a tener que haber realizado el trabajo de fin de grado para acabar la carrera. No obstante, esto ya ha pasado y espero ir subiendo el ritmo. Para comenzar, nada mejor que un par de enlaces en la sección de teletexto.</p>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/TeletextRTP.jpeg">
</div>

<ul>
<li><a href="https://retrocomputing.stackexchange.com/questions/15685/what-are-the-major-technical-difference-between-multics-and-unix">¿Cuáles son las diferencias entre UNIX y MULTICS?</a> Para quién no lo sepa, MULTICS fue el sistema operativo anterior y UNIX fue una versión simplificada para equipos menos potentes. ¿Pero qué cambios realizaron respecto a MULTICS?</li>
<li>En una página web actual encontramos HTML, imágenes, muchas veces JavaScript y en casi todas, CSS, un lenguaje para dotar de estilo y maquetación a las webs. Pero CSS no fue el primer lenguaje diseñado para cumplir esta función. <a href="https://eager.io/blog/the-languages-which-almost-were-css/">Este artículo hace un repaso por aquellos lenguajes se propusieron antes de CSS.</a></li>
<li><a href="https://datavizcatalogue.com/index.html">DataViz Catalogue</a> es una web que recoge un montón de tipos de visualizaciones de datos que se pueden realizar para mostrar información de forma visual.</li>
<li>¿Qué tal vas de soft skills? Las soft skills son igualmente importante que las hard skills para tu desarrollo profesional y personal. Dentro de este mundillo hay bastantes gurús de dudosa credibilidad, pero esta página llamada <a href="https://beta.skillsmatch.eu/">SkillsMatch</a> me ha parecido interesante ya que se trata de un proyecto respaldado por la universidad de Estocolmo para tratar de establecer un marco común europeo en materia de soft skills. En la web realizas un cuestionario que irá determinando tus nivel en diferentes habilidades. Además te propone cursos para mejorar esas habilidades.</li>
<li>Una de las cosas que estoy haciendo este verano es aprender Idris, concretamente Idris 2, un lenguaje funcional donde los tipos son elementos de primer orden y se pueden construir los llamados <em>dependent types</em>. El lenguaje a primera vista se parece mucho a Haskell, sin embargo, hay <a href="https://deque.blog/2017/06/14/10-things-idris-improved-over-haskell/">10 cosas que Idris mejora sobre Haskell.</a></li>
<li>Si quieres hacer un juego en Rust, tienes varias librerías: ggez o Amethyst eran las opciones nativas más populares. También tenías Godot-Rust para integrate con Godot y bindings con SDL2 y SFML. Ahora acaba de anunciarse, <a href="https://bevyengine.org/">Bevy</a>, un motor ECS, con muy buena pinta. Tiene ya muchas cosas implementadas ya e intenciones de seguir. Merece la pena darle un vistazo.</li>
<li><a href="https://github.com/vadimdemedes/ink">Ink</a> es una librería que es como React pero para aplicaciones en la línea de comandos. Podrás hacer interfaces de línea de comandos con JSX. No es la librería para crear UIs de terminal más completa ni mucho menos, pero esta es bastante simple si vienes del mundo React.</li>
<li><a href="https://github.com/oxigraph/oxigraph">Oxigraph</a> es una base de datos muy nueva, hecha en Rust y basada en tripletas RDF. Trata de implementar el estándar SPARQL al completo, así como diferentes formatos de ingesta RDF (RDF-XML, Turtle, N3, ...)</li>
<li>Seguimos con bases de datos. <a href="https://questdb.io/">QuestDB</a> es una base de datos para series temporales que también acaba de salir. Implementada en Java, usa el lenguaje SQL más ciertas extensiones para realizar consultas. Sale muy bien parada en ciertos benchmarks por lo que puede ser una alternativa interesante a InfluxDB o a TimescaleDB.</li>
<li><a href="https://www.thisworddoesnotexist.com/">Esta palabra no existe</a>. El algoritmo funciona con GPT-2, seguramente con GPT-3, se puedan conseguir resultados más espectaculares todavía.</li>
<li><a href="https://f1zz.org/">f1zz</a> es una propuesta novedosa de lenguaje basado en lógica difusa. Veremos como evoluciona.</li>
</ul>
<p>Con estos enlaces me despido y os traigo la canción de este teletexto:</p>
<div style="text-align:center">
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/icGlk7InEkU" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>]]></description>
                <comments>https://blog.adrianistan.eu/teletexto-007</comments>
                <pubDate>Sat, 15 Aug 2020 22:36:28 +0000</pubDate>
            </item>
        
            <item>
                <title>Regex para torpes</title>
                <link>https://blog.adrianistan.eu/regex-torpes</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/regex-torpes</guid>
                <description><![CDATA[<p>Las expresiones regulares, o de forma abreviada, regex, son pequeños programas que buscan y extraen ciertas cadenas de texto dentro de una cadena mayor. Las expresiones regulares no son Turing-completas, pero son ideales para trabajar con texto. Prácticamente todos los lenguajes de programación incluyen soporte a regex, con diferentes niveles de soporte. Sin embargo, habitualmente mucha gente al ver una expresión regular piensa que son indescifrables e imposibles de entender. Nada más lejos de la realidad. Con este post intento explicar lo más importante de Regex</p>

<div style="text-align:center">
<img src="https://files.adrianistan.eu/DonQuijoteRegex.png">
<small>No importa si no entiendes esto ahora. Cuando acabes de leer el post espero que sí puedas.</small>
</div>

<p>Para el código de ejemplo usaré Python, pero todo lo que voy a explicar es compatible con cualquier motor de regex.</p>

<h2>Las regex son patrones de texto</h2>
<p>Lo primero que hay que tener claro es que las expresiones regulares son patrones. El motor regex va a intentar hacer que el patrón coincida con la entrada <em>en cualquier punto</em>. Los patrones se definen escribiendo los caracteres que queremos que aparezcan.</p>
<pre><code class="language-python">
import re

TEXT = """
Always remember my friend
The world will changes again
And you may have to come back
Through everywhere you have been
When your life was low
You had nowhere to go
People turned there backs on you
And everybody said that you through
I took you in
I made you strong again
Put you back together
Out of all the dreams
You left along the way
You left me shining
Now your doing well
From story's I hear tell
You own the world again
Everyone's your friend
Although I never hear from you
Still its nice to know
You used to love me so
When your life was low
"""

x = re.search(r"friend", TEXT)
if x is not None:
    print("La palabra friend aparece en la letra de la canción")
</code></pre>
<p>En este primer ejemplo, usamos caracteres "normales" para crear la expresión regular. El motor regex va a buscar si existe en algún punto de la cadena TEXT la expresión "friend", la cuál es cierta. El procedimiento es el siguiente, el motor va buscando la primera letra, la f en el texto. Si la encuentra, comprueba que el siguiente caracter cumpla también el patrón, es decir, que sea una r. La complejidad, aparente que no real, de las regex, viene cuando introducimos condiciones más avanzadas para estos patrones. Normalmente, además de obtener si se ha podido aplicar el patrón o no, obtendremos el match, que es el texto en concreto que cumple la regla.</p>

<p>Tal y como hemos visto, el procedimiento de búsqueda de patrones es un AND implícito. Si queremos añadir un OR, es decir, varias alternativas, usaremos la barra vertical |.</p>
<pre><code class="language-python">
x = re.search(r"left|right", TEXT)
</code></pre>
<p>En este caso, podrá encontrar el texto ya que left existe, aunque right no aparezca.</p>

<h2>Grupos de caracteres</h2>
<p>Lo anterior nos podría servir para muchos casos de uso, pero muchos serían tremendamente verbosos. Algunos caracteres especiales nos ayudarán.</p>
<p>Los corchetes [ y ] no permiten definir grupos de caracteres. Dentro de estos grupos podemos poner caracteres que podrían ir en esa posición. A la hora de buscar se seleccionará uno.</p>
<pre><code class="language-python">
x = re.search(r"[Tt]he", TEXT)
</code></pre>
<p>En el ejemplo, buscará tanto la palabra the como The.</p>
<p>Los grupos de caracteres soportan intervalos, que nos permite definir un rango de caracteres amplio de forma reducida. Los más importantes son 0-9 (dígitos), A-Z (mayúsculas ASCII) y a-z (minúsculas ASCII).</p>
<pre><code class="language-python">
x = re.search(r"[A-Za-z]", TEXT)
</code></pre>
<p>El ejemplo anterior sería un patrón para todas las letras ASCII. Hago hincapié en lo de ASCII porque si tuviesemos caracteres con tildes o eñes, no funcionaría, ya que no están dentro del intervalo.</p>
<p>Los grupos de caracteres pueden funcionar de forma inversa, admitiendo cualquier caracter salvo los que están dentro de él. Los grupos de caracteres <em>complementarios</em> empiezan con el caracter ^.</p>
<pre><code class="language-python">
x = re.search(r"[^ ]", TEXT)
</code></pre>
<p>En el ejemplo anterior se admite todo menos el espacio.</p>
<p>Los grupos de caracteres también sirven para expresar caracteres que no podemos expresar de otra forma por tener otro significado en regex, como los puntos o los paréntesis.</p>

<h2>Multiplicidad</h2>
<p>Todo lo que hemos visto está muy bien pero seguimos teniendo patrones que en ocasiones pueden ser muy verbosos. Para ello entran los operadores de multiplicidad, que son cuatro y nos permiten definir las repeticiones admitidas de lo que va inmediatamente antes.</p>
<p>El operador * indica que lo que va antes se puede repetir de 0 a N veces</p>
<p>El operador + indica que lo que va antes se puede repetir de 1 a N veces</p>
<p>El operador ? indica que lo que va antes se puede repetir de 0 a 1 veces</p>
<p>El operador {M} indica que lo que va antes se puede repetir M veces</p>
<pre><code class="language-python">
x = re.search(r"[0-9]*", "hola") # OK
x = re.search(r"[0-9]*", "123") # OK
x = re.search(r"[0-9]+", "hola") # NOPE
x = re.search(r"[0-9]+", "123") # OK
x = re.search(r"[0-9]?", "hola") # NOPE
x = re.search(r"[0-9]?", "123") # OK (pero cada número por separado)

x = re.search(r"[0-9]{4}-[0-9]{2}-[0-9]{2}", "2020-07-26") # OK
</code></pre>
<p>Hay que tener en cuenta que el motor regex va a buscar el <em>match</em> más largo que pueda encontrar, por eso usando * o +, buscará siempre el patrón que cumpla la regla que más caracteres lleve. Esto es lógico si tenemos en cuenta que el patrón más largo suele ser el buscado, pero se puede cambiar este comportamiento.</p>

<h2>Caracteres especiales</h2>
<p>Existen algunos caracteres especiales en regex. El punto . representa cualquier caracter (salvo saltos de línea). Por ejemplo, usando la variable TEXT de arriba:</p>
<pre><code class="language-python">
x = re.search(r".*", TEXT)
</code></pre>
<p>Va a ser un patrón dentro del texto y el match va a ser: "Always remember my friend".</p>
<p>Otros caracteres especiales son ^ y $. Representan respectivamente, inicio de cadena y final. Son útiles cuando queremos delimitar el patrón a que necesariamente empiece donde empieza la cadena de texto y cuando queremos que toda la cadena de texto cumpla con el patrón.</p>
<pre><code class="language-python">
x = re.search(r"^(\n|.)*$", TEXT) # OK

x = re.search(r"^[0-9]{4}-[0-9]{2}-[0-9]{2}$", "2020-07-26") # Así evitaríamos que se colase texto antes o después de la fecha.
</code></pre>

<h2>Extraer texto (capturas)</h2>
<p>Una de las mejores características de regex es que podemos extraer contenido dado unos patrones. Para ello, usamos paréntesis ( y ). Los grupos de captura, que es como se llaman, se numeran internamente de izquierda a derecha empezando por el número 1 (número 0 es el match completo). </p>
<pre><code class="language-python">
x = re.search(r"^([0-9]{4})-([0-9]{2})-([0-9]{2})$", "2020-07-26")

print(x.group(0)) # 2020-07-26
print(x.group(1)) # 2020
print(x.group(2)) # 07
print(x.group(3)) # 26
</code></pre>
<p>Con esto ya sabemos lo básico de expresiones regulares. Existen más caracteres especiales y cosas más avanzadas como los lookaheads, pero en la gran mayoría de casos no necesitarás más. Ahora intenta leer el gráfico del comienzo del post.</p>
<p>Por último, una pregunta para los comentarios, ¿utilizas regex normalmente o intentas evitarlo?</p>
]]></description>
                <comments>https://blog.adrianistan.eu/regex-torpes</comments>
                <pubDate>Sun, 26 Jul 2020 18:51:55 +0000</pubDate>
            </item>
        
            <item>
                <title>Un nuevo comienzo</title>
                <link>https://blog.adrianistan.eu/nuevo-comienzo</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/nuevo-comienzo</guid>
                <description><![CDATA[<p>El 1 de julio va a ser un día importante en mi carrera profesional. Hace justamente un año empecé a trabajar como becario en Telefónica y ahora voy a pasar a forma parte de la plantilla con un contrato de verdad.</p>

<div style="text-align:center">
<img src="https://files.adrianistan.eu/Telefonica.jpg" width="700">
</div>

<h2>Como becario</h2>
<p>...estuve en un proyecto de una plataforma de Big Data para Telefónica. Era un proyecto con bastante gente, con varios subproyectos dentro del gran proyecto. Mi squad era el que se encargaba de que la plataforma se ejecutase correctamente en la nube (primero AWS, después Azure). Para ello usábamos Kubernetes, Ansible y posteriormente Terraform y un programa propio en Python.</p>

<p>Terraform precisamente es una de las herramientas en las que me "especialicé", aunque durante todo el año, mis tareas fueron bastante variadas, ya que pude toquetear bastantes cosas, desde Grafana hasta Elasticsearch pasando por Sentry.</p>

<div style="text-align:center">
<img src="https://files.adrianistan.eu/telefonica-boecillo.jpg">
</div>

<p>A nivel personal, me sentí bastante a gusto. Mi oficina era la de Boecillo (Valladolid) pero muchos miembros del equipo eran de Madrid, Huesca o Valencia y no podía verles más que por Slack. Sin embargo, la gente de Boecillo era la más cercana a mis tareas. Me ayudaban en todo en lo que me atascaba y tenían buen sentido del humor. Además, dentro del equipo había gente de mentalidad curiosa, siempre descubriendo cosas nuevas. Aprendí mucho de ellos. La empresa por su parte también me dedicó horas con cursos, tanto presenciales como online. Si bien había alguno mejor que otro, hubo sesiones muy aprovechables. Todo esto sin olvidar que durante mi estancia hubo una pandemia global.</p>

<p>Tras casi un año, ya conocía bastantes cosas de la empresa, sus pros y sus contras y si bien no podía comparar con otras empresas, decidí aplicar a un puesto. Después de unas cuantas entrevistas, finalmente llegué al puesto que empiezo el 1 de julio.</p>

<h2>El futuro</h2>
<p>El sitio al que me incorporo es en otro proyecto diferente, más enfocado a IoT y a hogares inteligentes. Sin embargo, no dejo de lado el backend y la nube. Espero que en esta nueva etapa pueda aprender más todavía y seguir conociendo gente tan buena como la que había en mi etapa de becario.</p>
<p>¿Qué nos deparará el futuro?</p>
<p>PD: Durante mi estancia usé MacOS, pero no me ha acabado de convencer y ahora he pedido Ubuntu. Veremos si a nivel profesional cumple.</p>]]></description>
                <comments>https://blog.adrianistan.eu/nuevo-comienzo</comments>
                <pubDate>Mon, 29 Jun 2020 22:50:50 +0000</pubDate>
            </item>
        
            <item>
                <title>Agregador de enlaces en la nube con AWS Lambda y Terraform</title>
                <link>https://blog.adrianistan.eu/agregador-enlaces-aws-lambda-terraform</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/agregador-enlaces-aws-lambda-terraform</guid>
                <description><![CDATA[<p>Siempre había querido hacer una especie de planeta o agregador de enlaces ya que personalmente, son sitios que visito mucho. En este post veremos como hacer un agregador de enlaces en la nube (concretamente Amazon Web Services) de forma gratuita usando los servicios Lambda, CloudWatch y S3. Todo lo juntaremos usando <a href="https://blog.adrianistan.eu/terraform-cloud-declarativa">Terraform</a></p>

<div style="text-align:center">
<img src="https://files.adrianistan.eu/AWS.png">
</div>

<h2>El agregador de enlaces aka la lambda</h2>
<p>El agregador de enlaces será un código que visitará diferentes fuentes, definidas en el formato estándar OPML, filtrará las noticias que son de los últimos 30 días y las combinará con una plantilla para sacar un HTML con los títulos y enlaces a las páginas originales.</p>
<p>AWS Lambda nos permite que este código se ejecute como máximo durante 15 minutos y nos deja varios lenguajes: Ruby, Node.js, Java, Go, C#, PowerShell y por supuesto, Python, que es el que vamos a usar. En AWS Lambda tenemos disponible Python 3.8, que será la versión que usaremos.</p>
<p>Para descargar los feeds usaremos aiohttp, que permite hacer requests de forma asíncrona. feedparser se encargará de leer los feeds. Las plantillas las renderizaremos con Jinja. Finalmente, subiremos el resultado de la web a S3, otro servicio de AWS.</p>
<p>Vamos a ir viendo el código. Primero importamos aquello que vamos a usar y ajustamos el logger a que saque eventos hasta de nivel INFO. Estos logs los podremos ver en CloudWatch más adelante.</p>
<pre><code class="language-python">
import aiohttp
import feedparser
import boto3
from boto3.s3.transfer import S3Transfer
from jinja2 import Environment, FileSystemLoader, select_autoescape

import asyncio
import itertools
import xml.etree.ElementTree as ET
import logging
import mimetypes
import shutil
import urllib
import tempfile
from datetime import datetime
from dataclasses import dataclass
from pathlib import Path

logging.getLogger().setLevel(logging.INFO)
</code></pre>
<p>A continuación creamos la clase Item, que contiene cada noticia de un feed en nuestro formato. Tendrá un orden definido por la fecha, así como un constructor desde elementos item de feedparser (podría entenderse como un conversor de items de feedparser a items propios).</p>
<pre><code class="language-python">
@dataclass
class Item:
    title: str
    url: str
    date: datetime
    source: str


    def __lt__(self, other):
        return self.date < other.date

    @property
    def readable_date(self):
        return self.date.strftime("%d/%m/%Y")

    @staticmethod
    def from_feeditem(item, source):
        try:
            pdate = item["published_parsed"]
            published_date = datetime(year=pdate.tm_year, month=pdate.tm_mon, day=pdate.tm_mday)
            return Item(item["title"], item["link"], published_date, source)
        except:
            return None

    def is_in_last_month(self):
        return (datetime.today() - self.date).days < 30
</code></pre>
<p>A continuación definimos la función fetch_items, que realiza la conversión desde feed de feedparser hasta un listado de items propios. Está diseñada alrededor de map/filter.</p>
<pre><code class="language-python">
def fetch_items(feed, source):
    all_items = map(lambda x: Item.from_feeditem(x, source), feed["items"])
    all_items = filter(lambda x: x is not None, all_items)
    return filter(lambda x: x.is_in_last_month(), all_items)
</code></pre>
<p>Ahora definimos parse_source, que dado el texto de un feed, llama a feedparser y obtiene los elementos correspondientes.</p>
<pre><code class="language-python">
def parse_source(feed_text, source):
    feed = feedparser.parse(feed_text)
    return fetch_items(feed, source)
</code></pre>
<p>Llega el turno de la función main, que será asíncrona para poder usar aiohttp. En la primera parte leemos el fichero OPML, obtenemos las URLs y vamos almacenando los items que deberán aparacer en la página web en una lista.</p>
<pre><code class="language-python">
async def main():
    tree = ET.parse("sources.opml")
    body = tree.find("body")

    sources = [ outline.get("title") for outline in body.iter("outline")]
    feeds_url = [ outline.get("xmlUrl") for outline in body.iter("outline")]

    items = list()

    async with aiohttp.ClientSession() as session:
        feeds = map(session.get, feeds_url)
        for feed_task in asyncio.as_completed(feeds):
            try:
                feed = await feed_task
                url = str(feed.url)
                logging.info(f"Downloaded {url} with success")
                feed_text = await feed.text()
                i = feeds_url.index(url)
                source = sources[i]
                items.extend(parse_source(feed_text, source))
            except:
                pass
</code></pre>
<p>Allí en el except se podría hacer un control de errores más fino para detectar cuando una URL está caída e informar. El bucle for está gobernado por una función llamada asyncio.as_completed. Esta función pertenece al módulo estándar asyncio y permite tener un bucle que va sacando elementos asíncronos de la lista inicial según se van completando.</p>
<p>El siguiente paso es construir el código HTML con Jinja. No tiene mucho misterio salvo que es importante usar directorios temporales, ya que no podemos escribir en otras carpetas que no sean esas en AWS Lambda.</p>
<pre><code class="language-python">
env = Environment(
            loader=FileSystemLoader("templates"),
            autoescape=select_autoescape(["html"])
    )

    items.sort(reverse=True)

    temp_dir = tempfile.mktemp()
    output_folder = Path(temp_dir) / "_site"
    if not output_folder.exists():
        output_folder.mkdir(parents=True)

    output_file = output_folder / "index.html"

    static_folder = Path.cwd() / "static"
    shutil.copytree(static_folder, output_folder, dirs_exist_ok=True)

    with open(output_file, "w") as f:
        f.write(env.get_template("index.html").render(items=items))

    print(f"OK: {output_folder}")
</code></pre>
<p>Finalmente subimos el resultado a S3. No hay que preocuparse por credenciales, ya que la Lambda tendrá credenciales automáticas en su entorno de ejecución. Si quieres hacer pruebas deberás exportar unas variables de entorno eso sí. Importante: marcamos la ACL como public-read para que se pueda leer desde fuera y asignamos mime types a mano, ya que de otro modo, S3 tendrá problemas en servir el HTML. Esto será lo último del main.</p>
<pre><code class="language-python">
    s3 = boto3.client("s3")
    transfer = S3Transfer(s3)
    for path in output_folder.iterdir():
        transfer.upload_file(str(path), "lector.adrianistan.eu", path.name, extra_args={
            "ACL": "public-read",
            "ContentType": mimetypes.guess_type(path.name)[0]
        })
</code></pre>
<p>Finalmente, definimos una función que será el punto de entrada de la Lambda, en este caso llamará a main a través de asyncio, para que la asincronía funcione.</p>
<pre><code class="language-python">
def handler(event, context):
    asyncio.run(main())
</code></pre>
<p>Con esto ya tendríamos la lambda, pero falta la configuración de AWS</p>
<h2>Infraestructura en Terraform</h2>
<p>Vamos a crear un fichero amazon.tf en la misma carpeta. Contendrá la infraestructura en AWS como código. Primero definimos que vamos a usar AWS y seleccionamos una región (yo uso eu-west-3, París). También podemos crear un resource group. No es más que una forma de agrupar recursos que contengan el mismo tag, no tiene ningún coste. Vamos a poner que sean recursos del grupo todos los recursos de AWS que tengan el tag app con valor "lector".</p>
<pre><code>
provider "aws" {
  region  = "eu-west-3"
  version = "2.60.0"
}

resource "aws_resourcegroups_group" "lector" {
  name = "lector"
  resource_query {
    query = &lt;&lt;JSON
{
  "ResourceTypeFilters": [
    "AWS::AllSupported"
  ],
  "TagFilters": [
    {
      "Key": "app",
      "Values": ["lector"]
    }
  ]
}
JSON
  }
}
</code></pre>
<div style="text-align:center">
<img width="700" src="https://files.adrianistan.eu/AWSResourceGroup.png">
</div>
<h3>Amazon S3</h3>
<p>S3 es el servicio de almacenamiento de "objetos" de AWS. Se diferencia de otros almacenamientos en que principalmente está diseñado para acceder mediante una API en contraste con otros almacenamientos de "bloques" que básicamente son discos duros y hay que formatear. Las ventajas de este modelo es que es muy sencillo de mantener. S3 dispone de un modo website, que permite mostrar HTML directamente y también de redirecciones CNAME, de modo que responde a peticiones DNS de dominios que se llamen igual que el bucket.</p>
<p>El bucket por tanto se llamará lector.adrianistan.eu, que es el dominio que voy a apuntar hacia allí. Tiene una ACL de lectura pública y el punto de entrada es "index.html". Además defino un output, para que me muestre por consola la URL del bucket. Esto hay que ponerlo en el registro CNAME del subdominio en el proveedor del dominio que tengamos. Amazon tiene uno llamado Route53, pero para este proyecto no lo he usado.</p>
<pre><code>
resource "aws_s3_bucket" "lector-www" {
    bucket = "lector.adrianistan.eu"
    acl = "public-read"

    website {
        index_document = "index.html"
    }

    tags = {
      app = "lector"
    }
}

output "website_cname" {
    value = aws_s3_bucket.lector-www.website_endpoint
}
</code></pre>
<h3>IAM</h3>
<p>AWS está altamente protegido y salvo los usuarios administradores, los demás tienen que pedir permisos de forma explícita. Nuestra lambda necesita permisos, para ser Lambda, para usar S3, y para usar CloudWatch. Estos credenciales son los que usará además nuestra lambda desde el código Python.</p>
<pre><code>
resource "aws_iam_role" "lector" {
  name = "lector"

  tags = {
    app = "lector"
  }

  assume_role_policy = &lt;&lt;EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}

resource "aws_iam_policy" "lector" {
  name = "lector"
  policy = file("policy.json")
}

resource "aws_iam_role_policy_attachment" "lector" {
  role       = aws_iam_role.lector.name
  policy_arn = aws_iam_policy.lector.arn
}
</pre></code>
<p>El fichero policy.json contiene los permisos de la lambda.</p>
<pre><code>
{
  "Version": "2012-10-17",
  "Statement": [
      {
        "Effect": "Allow",
        "Action": "s3:*",
        "Resource": "*"
      },
      {
        "Action": [
          "logs:*"
        ],
        "Effect": "Allow",
        "Resource": "*"
      }
  ]
}
</code></pre>
<p>En este caso son permisos bastante abiertos, ya que dentro de S3 y CloudWatch Logs pueden hacer lo que quieran sobre cualquier recurso.</p>
<h3>AWS Lambda</h3>
<p>Las lambdas son programas de código pequeño sin estado que se ejecutan sin necesidad de configurar servidores por cortos periodos de tiempo (máximo 15 minutos). Para configurarlas solo tenemos que elegir permisos, subir el código (en un fichero Zip), la función de entrada, memoria RAM que podrá usar (por defecto 128 MB), timeout (por defecto son 3 segundos solo) y entorno de ejecución.</p>
<pre><code>
resource "aws_lambda_function" "lector" {
    filename = "function.zip"
    function_name = "lector"
    role = aws_iam_role.lector.arn
    handler = "main.handler"

    source_code_hash = filebase64sha256("function.zip")
    runtime = "python3.8"

    memory_size = 256
    timeout = 900

    tags = {
      app = "lector"
    }
}
</code></pre>
<p>El código lo tenemos en el fichero function.zip. Este fichero se elabora de forma diferente según el lenguaje de programación que usemos. En el caso de Python, si queremos incluir la dependencias deberemos hacer un zip así:</p>
<pre><code>
pip install --target ./package -r requirements.txt
cd package
zip -r9 ../function.zip .
cd ..
zip -g function.zip static/*
zip -g function.zip templates/*
zip -g function.zip sources.opml
zip -g function.zip main.py
</code></pre>
<p>Básicamente instalamos los paquetes de Pip al mismo nivel que el resto de ficheros. AWS Lambda se ejecuta sobre Amazon Linux, por lo que no hay problema en usar el código nativo que se genera en las instalaciones de Pip desde Ubuntu o Debian.</p>
<div style="text-align:center">
<img width="700" src="https://files.adrianistan.eu/AWSLambda.png">
</div>
<h3>AWS CloudWatch</h3>
<p>CloudWatch es un servicio de AWS con varias finalidades. Por un lado es una plataforma de recolección de logs y métricas. Por otro lado, también permite disparar eventos, por ejemplo, de tipo cron. En Terraform crearemos una regla cron, la asociaremos con la Lambda (¡hay que asignar permisos también!) y por otro lado crearemos una carpeta para los logs, con una retención de 7 días únicamente.</p>
<pre><code>
resource "aws_cloudwatch_event_rule" "lector-daily" {
    name = "lector-daily"
    description = "Run Lector Lambda every day"

    schedule_expression = "cron(45 8 * * ? *)"

    tags = {
      app = "lector"
    }
}

resource "aws_cloudwatch_event_target" "lector-daily" {
    rule = aws_cloudwatch_event_rule.lector-daily.name
    target_id = "lector-daily"
    arn = aws_lambda_function.lector.arn
}

resource "aws_cloudwatch_log_group" "lector" {
  name              = "/aws/lambda/${aws_lambda_function.lector.function_name}"
  retention_in_days = 7

  tags = {
    app = "lector"
  }
}

resource "aws_lambda_permission" "cloudwatch-call-lector" {
    statement_id = "AllowExecutionFromCloudWatch"
    action = "lambda:InvokeFunction"
    function_name = aws_lambda_function.lector.function_name
    principal = "events.amazonaws.com"
    source_arn = aws_cloudwatch_event_rule.lector-daily.arn
}
</code></pre>
<div style="text-align:center">
<img width="700" src="https://files.adrianistan.eu/AWSCloudWatch.png">
<img width="700" src="https://files.adrianistan.eu/AWSCloudWacthRule.png">
</div>
<h2>Conclusión</h2>
<div style="text-align:center">
<img width="700" src="https://files.adrianistan.eu/Lector.png">
</div>
<p>Con todo esto ya tenemos ya la aplicación acabada, un "terraform apply", nos permitirá lanzar la aplicación a la red. He de decir que a priori parece todo muy complejo. La razón es principalmente el tema de permisos de AWS, que es muy granular y restrictivo por defecto. También tenemos que considerar que una vez hecho esto, el mantenimiento de la aplicación es nulo. Esto que he hecho también se puede hacer en Microsoft Azure y en Google Cloud con Azure Functions y Google Cloud Functions respectivamente. Sin embargo, en Azure están limitadas a 10 minutos y creo que este programa puede llegar a tardar alguna vez más de 10 minutos. Finalmente el coste de todo esto. Esto que hemos hecho cuesta 0€. La capa gratuita de ejecución de Lambdas es muy elevada y ni aunque gastásemos los 15 minutos con 3GB de RAM todos los días llegaríamos a gastarlo. Lo mismo para los eventos. Los logs tienen un límite gratuito de 5GB. S3 es el único servicio que tendremos que pagar, pero si solo tenemos estos datos seguramente el redondeo sea 0€.</p>
<p>Puedes visitar la web final en <a href="http://lector.adrianistan.eu">http://lector.adrianistan.eu</a></p>]]></description>
                <comments>https://blog.adrianistan.eu/agregador-enlaces-aws-lambda-terraform</comments>
                <pubDate>Tue, 26 May 2020 15:45:20 +0000</pubDate>
            </item>
        
            <item>
                <title>Teletexto #006</title>
                <link>https://blog.adrianistan.eu/teletexto-006</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/teletexto-006</guid>
                <description><![CDATA[<p>Bienvenidos otra vez a Teletexto, esa sección donde os pongo enlaces interesantes y que no tiene calendario de publicación (sale cuando hay enlaces suficientes y no hay más temas de los que pueda escribir)</p>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/TeletextDivisas.jpg">
</div>

<ul>
<li>COBOL es uno de los lenguajes más veteranos de la informática. Era por tanto cuestión de tiempo de que COBOL se adaptase al mundo serverless. Gracias a los <a href="https://blog.cloudflare.com/cloudflare-workers-now-support-cobol/">CloudFlare Workers</a>, ya puedes usar COBOL en la nube (¡usando entre medias WebAssembly!).</li>
<li><a href="https://www.2ndquadrant.com/en/blog/postgresql-is-the-worlds-best-database">PostgreSQL es la mejor base de datos del mundo</a></li>
<li>Ya hemos hablado de las virtudes de PostgreSQL, pero también tiene partes malas. <a href="https://medium.com/@rbranson/10-things-i-hate-about-postgresql-20dbab8c2791">En este post vemos 10 cosas malas de PostgreSQL.</a> Spoiler: no te emociones, seguramente PostgreSQL siga siendo tu mejor opción dentro de las bases de datos.</li>
<li>JavaScript ha ido evolucionando con los años de ser un lenguaje raro diseñado en poco tiempo a ser uno de los lenguajes más expresivos e innovadores. Una novedad reciente que no conocía son los <a href="https://www.arbazsiddiqui.me/javascript-proxies-real-world-use-cases/">proxies.</a></li>
<li><a href="https://spin.atomicobject.com/2020/03/12/why-i-dont-use-classes/">No uso clases para programar</a> (en JavaScript). Interesante lectura. La verdad es que antes la gente se quejaba mucho de que JavaScript no tuviese clases (si tenía objetos, pero su OOP era de prototipos no de clases) y luego al final tampoco se ven tanto como en otros lenguajes.</li>
<li>¿Cómo funciona la red en Kubernetes? <a href="https://sookocheff.com/post/kubernetes/understanding-kubernetes-networking-model/">Un excelente artículo</a></li>
<li>¿Quieres administrar tus filtros de Gmail como código? Este programa parecido a <a href="https://blog.adrianistan.eu/terraform-cloud-declarativa">Terraform</a> pero adaptado a Gmail es <a href="https://github.com/mbrt/gmailctl">gmailctl</a></li>
<li><a href="https://paulosman.me/2019/12/30/production-oriented-development.html">¿Estás programando orientado a producción?</a> Una serie de pautas sobre como debemos gestionar los proyectos para que haya la menor fricción posible entre desarrollo y producción.</li>
<li>Los problemas de colas están por todas partes en nuestra sociedad y un buen análisis permite ahorrar mucho tiempo y dinero. Existen muchas formas de modelarlos y resolverlos, y me gustaría hacer algún post explicando esto más en detalle. Como adelanto, una forma de hacerlo es usar <a href="https://realpython.com/simpy-simulating-with-python/">SimPy</a>, una librería perfecta para realizar simulaciones de este tipo.</li>
<li>Si buscas un motor de búsqueda sobre texto, ElasticSearch es quizá tu mejor opción, pero es un software complejo y consume gran cantidad de recursos. Una solución muy interesante es usar los módulos FTS3, FTS4 y <a href="https://www.sqlite.org/fts5.html">FTS5 de SQLite</a>. Estos potentes módulos nos permiten realizar búsquedas de texto complejas.</li>
<li>¿Necesitas un editor de texto enriquecido para tu web? Una opción es <a href="https://quilljs.com/">Quill.js</a>. Personalmente antes usaba TinyMCE en este blog y acabé quitándolo porque a veces hacía cosas raras. Actualmente no tengo nada y escribo HTML directamente (con ayudas), pero Quill parece una buena opción a tener en cuenta.</li>
<li>Usar máquinas de estado es una opción muy buena para resolver ciertos problemas. En este post explica una forma de <a href="https://blog.yoshuawuyts.com/state-machines/">implementar máquinas de estados en Rust.</a></li>
</ul>
<p>Por último, la canción de hoy pertenece a una de las dictaduras más peculiares del mundo: Turkmenistán.</p>
<div style="text-align:center">
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/w9R9DcQOnss" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
]]></description>
                <comments>https://blog.adrianistan.eu/teletexto-006</comments>
                <pubDate>Wed, 13 May 2020 18:52:54 +0000</pubDate>
            </item>
        
            <item>
                <title>El Universo Prolog</title>
                <link>https://blog.adrianistan.eu/universo-prolog</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/universo-prolog</guid>
                <description><![CDATA[<p><a href="https://blog.adrianistan.eu/tag/prolog">Prolog</a> es un lenguaje de programación dentro del paradigma lógico. Desde su invención en 1972 en Marsella, Francia, Prolog ha sido un lenguaje base para otros lenguajes ya que Prolog prácticamente inventó el paradigma. Las ventajas originales de Prolog son su sintaxis reducida y tersa, altamente modificable ya que es un lenguaje <a href="https://es.wikipedia.org/wiki/Homoiconicidad">homoicónico</a> donde el código y los datos son lo mismo. De esta forma se permite expresar el conocimiento fácilmente. En el lenguaje solo existen comentarios, términos y variables. El lenguaje funciona mediante conceptos como la unificación SLD y la búsqueda. Pero como todo lenguaje, la gente no se quedó con Prolog a secas, sino que ha habido modificaciones respecto a la versión de 1972. Adentrémonos en el universo de lenguajes inspirados y versiones de Prolog.</p>

<div style="text-align:center">
<img width="700" src="https://files.adrianistan.eu/PrologDebug.png">
</div>

<p>Primero exploraremos los que se podrían considerar descendientes más directos y con el mismo espíritu que Prolog original, después veremos cambios más grandes.</p>

<h2>ISO Prolog</h2>
<p>En 1997, Prolog pasó a ser un lenguaje estandarizado por ISO bajo el nombre de <a href="https://www.iso.org/standard/21413.html">ISO/IEC 13211</a> y se unió a la lista de lenguajes ya estandarizados por ISO tales como C, SQL, Ada, C++, Fortran, PL/I, ALGOL, COBOL y BASIC. Hoy en día la estandarización de un lenguaje es algo menos importante ya que suele haber implementaciones de software libre, pero antiguamente era muy importante. ISO Prolog define lo que hasta entonces era la evolución más común del lenguaje Prolog original. ISO Prolog se implementa en muchos sitios, veamos los más relevantes.</p>

<h3>SICstus</h3>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/Sicstus.png">
</div>
<p><a href="https://sicstus.sics.se/">SICstus</a> es una implementación de ISO Prolog de manos de RISE, unos laboratorios de investigación del gobierno de Suecia. Tiene fama de ser una de las implementaciones más fieles al estándar y una de las más rápidas. Pero este intérprete es de pago por lo que tiene menos usuarios de los que debería tener por su calidad técnica</p>


<h3>GNU Prolog</h3>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/gprolog.png">
</div>
<p><a href="http://gprolog.org/">GNU Prolog</a> es la versión del proyecto GNU de Prolog. Se trata de otra implementación fiel de ISO Prolog y rápida, ya que es capaz de generar ejecutables nativos. Esto último no es nada habitual en los sistemas Prolog. Sin embargo, GNU Prolog no es de los entornos más amigables ni cuenta con muchas más librerías además de las definidas por ISO Prolog, por lo que en la práctica tampoco es tan usado.</p>

<h3>Scryer Prolog</h3>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/scryer.png">
</div>
<p><a href="https://github.com/mthom/scryer-prolog">Scryer Prolog</a> es una implementación, todavía en desarrollo, de un entorno fiel a ISO Prolog. Está implementado en Rust y pretende ser un entorno completo con gran cantidad de librerías. Al ser un desarrollo desde cero, pretende alcanzar gran rendimiento usando técnicas modernas.</p>

<h3>XSB Prolog</h3>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/XSBProlog.png">
</div>
<p><a href="http://xsb.sourceforge.net/">XSB Prolog</a> es una variante de Prolog comunitaria especialmente enfocada en <i>Tabled Logic Programming</i>. Si quieres explorar esta parte de la programación lógica XSB es el mejor sistema, aunque otros sistemas también soportan tabling. El tabling es especialmente interesante en problemas de <i>programación dinámica</i>.</p>

<h3>YAP</h3>
<p><a href="https://github.com/vscosta/yap-6.3">YAP</a> significa Yet Another Prolog, y sí, es otro Prolog más. Bastante ligero, no termina de implementar ISO Prolog pero no introduce incompatibilidades para que en un futuro pueda serlo. Es mantenido por la Universidad de Porto en Portugal.</p>

<h3>B-Prolog</h3>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/BProlog.png">
</div>
<p><a href="http://www.picat-lang.org/bprolog/">B-Prolog</a> es otra implementación estándar de Prolog. La razón de ser de B-Prolog es la incorporación de bucles foreach, soporte para tabling, list comprehensions, matching clauses y action rules que permiten realizar una programación donde ciertas condiciones se retrasan, dando lugar a una especie de programación orientada a eventos.</p>

<h3>Jekejeke</h3>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/Jekejeke.png">
</div>
<p><a href="http://jekejeke.ch">Jekejeke</a> es una implementación de Prolog 100% escrita en Java. Como muchos otros, intenta seguir ISO Prolog.</p>

<h3>ECLiPSe</h3>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/Eclipse.png">
</div>
<p><a href="https://eclipseclp.org/">ECLiPSe</a> es un Prolog especializado en CHR, o programación por restricciones. La mayoría de Prolog incluyen soporte a CHR así que no lo he mencionado, pero este está especializado en este tipo concreto de programación.</p>


<h3>Tau Prolog</h3>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/TauProlog.png">
</div>
<p><a href="http://tau-prolog.org/">Tau Prolog</a> es quizá una de las implementaciones de Prolog más usadas en el mundo, aunque mucha gente no lo sepa. Se trata de una implementación de ISO Prolog escrita en JavaScript, por lo que puede ejecutarse sin problema en los navegadores y en Node.js. Precisamente ahí, dentro de Yarn, ha encontrado un hueco y es como dependencia de este gestor de paquetes, de donde vienen la mayoría de sus descargas. Se trata de un proyecto del español José Antonio Riaza Valverde.</p>

<h3>Todavía más</h3>
<p>Otras implementaciones, menos usadas e interesantes son <a href="https://people.cs.kuleuven.be/~bart.demoen/hProlog/">hProlog</a>, <a href="https://quintus.sics.se/">Quintus Prolog</a>, <a href="https://www.nuget.org/packages/CSProlog/">CSProlog</a> o <a href="https://www.info.ucl.ac.be/~pvr/aquarius.html">Aquarius Prolog</a>, especialmente eficiente en arquitecturas MIPS y SPARC.</p>


<h2>SWI Prolog</h2>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/SWIPrologLogo.png">
</div>
<p><a href="https://www.swi-prolog.org/">SWI Prolog</a> es una implementación comunitaria de Prolog, aunque su principal desarrollador, Jan Wielemaker, está respaldado por la Vrije Universiteit Amsterdam. Inicialmente era una implementación más de ISO Prolog, pero a partir de la versión 7 rompe la compatibilidad con el estándar, así pues hoy día no es una implementación del estándar, si bien cumple con gran parte de este.</p>

<p>SWI Prolog se centra en ser práctico y usable como un lenguaje de programación más, altamente productivo. Prueba de ello lo tenemos en las inmensa cantidad de librerías, herramientas y documentación que tiene el proyecto. Librerías de APIs web, GUIs multiplataforma, regex, acceso a bases de datos JDBC, JSON, XML, RDF, PlDoc, PlUnit, debug gráfico, packs, diccionarios, etc. No es la implementación más rápida, aunque está relativamente optimizada. Se autodefine como el Prolog para los grandes proyectos. Actualmente, es mi implementación Prolog predilecta.</p>

<h2>Ciao</h2>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/Ciao.png">
</div>
<p><a href="https://ciao-lang.org">Ciao</a> es un lenguaje derivado de Prolog. Es posible usarlo como un ISO Prolog más, pero añade bastantes cosas extra y de hecho esta compatibilidad con ISO es simplemente un modo más. La primera diferencia es su propia definición. Ciao no es un lenguaje lógico sino multiparadigma. Introduce varias características de otros lenguajes tales como: funciones, mutabilidad, diccionarios, grafos, concurrencia, paralelismo y ejecución distribuida. También cuenta con librerías para desarrollo web. Además dispone de numerosas herramientas que lo convierten en un entorno productivo de programación. El proyecto Ciao está coordinado por la Universidad Politécnica de Madrid y el centro de investigación español IMDEA y es bastante menos popular que SWI Prolog.</p>

<h2>Visual Prolog</h2>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/VisualPrologLogo.png">
</div>
<p><a href="https://www.visual-prolog.com/">Visual Prolog</a> es el heredero de Turbo Prolog de Borland. Este dialecto de Prolog nunca fue cercano al estándar, ya que realizó modificaciones fuertes para que fuese más práctico para desarrollar interfaces de usuario para Windows. Incorpora orientación a objetos, tipado fuerte, llamadas directas a la API Win32, APIs de base de datos y APIs para programación web. Genera ejecutables nativos aunque de forma exclusiva para Windows. Es un producto de pago, aunque cuenta con una edición gratuita limitada.</p>

<h2>Logtalk</h2>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/Logtalk.png">
</div>
<p><a href="https://logtalk.org/">Logtalk</a> es el lenguaje que surgiría si Smalltalk y Prolog tuvieran un hijo. Se trata de un lenguaje cuya principal característica es añadir programación orientada a objetos a Prolog. Pero no la típica implementación popularizada por C++ y Java, sino una más cercana a Smalltalk. Diseñado por Paulo Moura, uno de los artífices de ISO Prolog, el lenguaje incluye módulos, prototipos, clases, protocolos (interfaces), categorías, programación orientada a eventos y programación concurrente.</p>
<p>Quizá la característica más chocante de Logtalk es que no es un entorno de ejecución, es simplemente una capa intermedia. El código Logtalk tiene que ser ejecutado en algún intérprete como SWI, SICstus, XSB o YAP.</p>

<h2>Mercury</h2>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/MercuryLang.png">
</div>
<p><a href="https://www.mercurylang.org/">Mercury</a> es el lenguaje que surgiría si Haskell y Prolog tuvieran un hijo. Se trata de un lenguaje que añade un tipado estático, fuerte y muy potente. Añade además otras características de lenguajes funcionales. Se trata de un lenguaje de muy buen rendimiento que compila  a código nativo. Es usado por el producto comercial <a href="https://www.princexml.com/">PrinceXML</a> de la empresa australiana YesLogic.</p>

<h2>Erlang</h2>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/erlang.png">
</div>
<p><a href="https://www.erlang.org/">Erlang</a> es quizá el lenguaje más conocido de esta lista y no es un lenguaje de programación lógica. ¿Entonces por qué está aquí? Porque la tan odiada por algunos sintaxis de Erlang es prácticamente la misma de Prolog. Los diseñadores de Erlang que trabajaban en Ericsson eran grandes fans de Prolog y de hecho los primeros compiladores de Erlang estaban hechos en Prolog. Es por ello que se puede considerar parte del universo Prolog.</p>

<h2>Picat</h2>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/Picat.png">
</div>
<p><a href="http://picat-lang.org">Picat</a> es un lenguaje, en esencia muy similar a Prolog, pero que incorpora una sintaxis incompatible. Picat incorpora estructuras de datos adicionales, funciones, notación de funciones OOP, list comprehensions, asignaciones, bucles y tabling.</p>

<h2>Datalog</h2>
<p><a href="https://en.wikipedia.org/wiki/Datalog">Datalog</a> es un subconjunto de Prolog. Se le eliminan características para hacerlo puramente declarativo y de esta forma se consigue que acabe siempre en tiempo finito (por tanto no es Turing completo). Existen muchas implementaciones de este lenguaje más sencillo que Prolog pero surgido posteriormente.</p>

<h2>Teyjus</h2>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/Teyjus.png">
</div>
<p><a href="http://teyjus.cs.umn.edu/">Teyjus</a> es una implementación de un lenguaje llamado LambdaProlog. LambdaProlog es un lenguaje que incorpora las ideas de Church de la Teoría Simple de Tipos. Este lenguaje añade tipos de datos abstractos y la sintaxis lambda-árbol para representar variables. Es bastante experimental todavía.</p>

<h2>LPS</h2>

<p><a href="http://lps.doc.ic.ac.uk/">LPS</a> es un interesante lenguaje que parte de Prolog y añade funcionalidad imperativa para que sea más fácil de utilizar. Tiene especial aplicación en el mundo financiero. Es un proyecto mantenido por el Imperial College de Londres.</p>

<h2>Bousi~Prolog</h2>
<p><a href="https://dectau.uclm.es/bousi-prolog/">Bousi~Prolog</a> es un Prolog adaptado para lógica difusa. Bastante experimental.</p>

<h2>Tamgu</h2>
<p><a href="https://github.com/naver/tamgu">Tamgu</a> es un lenguaje de programación creado por Naver, multiparadigma. Soporta los paradigmas lógico, imperativo y funcional. La parte lógica es extremadamente similar a Prolog, aunque no compatible. No existen átomos, todo funciona vía strings y las variables lógicas deben usar '?' como prefijo.</p>


<h2>Otros proyectos interesantes</h2>
<p>Otros proyectos relacionados son Flora-2, PARLOG, KL1, DR-PROLOG, P#, N-PROLOG, FASILL, Yield Prolog,... Muchos de estos proyectos son/fueron experimentales y muchos ya se han abandonado o tienen un uso muy minoritario. También existen lenguajes como miniKanren, Curry o Twelf, los cuales no derivan de Prolog, pero sí son parte del paradigma lógico.</p>]]></description>
                <comments>https://blog.adrianistan.eu/universo-prolog</comments>
                <pubDate>Thu, 30 Apr 2020 21:02:11 +0000</pubDate>
            </item>
        
            <item>
                <title>Construye un Renault 4</title>
                <link>https://blog.adrianistan.eu/construye-renault-4</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/construye-renault-4</guid>
                <description><![CDATA[<p>El Renault 4 fue un coche tremendamente popular y rentable para la empresa francesa Renault. A día de hoy ya no se fabrica, pero muchas lecciones sobre su diseño se pueden tener en cuenta y ayudarnos a construir nuestro Renault 4 personal, pero en software.</p>
<div style="text-align:center">
<img width="700" src="https://files.adrianistan.eu/Renault4.jpeg">
</div>
<h2>Historia del Renault 4</h2>
<p>El Renault 4 se presentó en 1961 en el salón de Frankfurt con la intención de ser el coche más práctico jamás diseñado. Toma muchas ideas del Citröen 2CV, que habiendo sido diseñado antes de la Segunda Guerra Mundial, seguía siendo un coche extremadamente popular. Fue un coche innovador a la vez práctico y económico, gracias a un diseño industrial muy bien logrado. Se vendió en más de 100 países y se mantuvo su producción hasta 1994 en Marruecos y Eslovenia, por lo que estuvo en el mercado unos 33 años (algo impensable hoy en día). Fue tan popular que en España se le llamó cariñosamente cuatro latas y en Argentina, la renoleta. Mucha gente lo ha comparado con los pantalones vaqueros por su flexibilidad.</p>

<p>Fue el primer coche de Renault en tener el motor en la parte delantera y el primero con tracción delantera. Históricamente los coches en ese momento llevaban el motor atrás, aunque Mini había demostrado que se podía perfectamente poner el motor delante sin problema. Fue tal el éxito que hoy en día la mayoría de coches son así y en el caso concreto de Renault, nunca han vuelto a hacer coches que no cumpliesen estos dos puntos.</p>

<p>También fue el primer coche de la historia en tener cinco puertas, es decir, el maletero trasero compartiendo el mismo compartimento que las personas. Además fue el primer coche que incorporaba la función de que los asientos traseros se pudiesen echar atrás para ampliar el espacio del maletero (ya muy amplio de por sí).</p>

<p>Aunque sin duda, la mayor curiosidad del diseño industrial del Renault 4 se encuentra en el eje trasero, donde la distancia entre ejes es diferente a un lado y a otro del coche, teniendo 5 cm más en el lado derecho. Esto es ya que la suspensión se diseño con barras de torsión que son unas barras en forma de L, pero son demasiado largas y no se pueden poner una enfrente de la otra porque chocan. Así pues, se pone una ligeramente delante de otra para que la suspensión entre. De este modo, se consigue que una buena suspensión sin perder espacio interior en el maletero. Además, todas las piezas de la chapa están atornilladas, por lo que se pueden reemplazar por otras fácilmente.</p>

<div style="text-align:center">
<video src="https://files.adrianistan.eu/Renault4-FaisUnTour.webm" controls>
</div>

<p>Además de esto, el Renault 4 tenía unos ángulos de entrada y salida de las ruedas muy buenos y una altura sobre el suelo de 27 cm, lo que efectivamente lo convertía en un coche perfecto para ir por el campo. Estamos ante un coche que es a la vez un compacto, un familiar y un todoterreno (sin tener tracción a las cuatro ruedas eso sí).</p>

<p>Las versiones originales llevaban un motor de entre 23 y 32 CV, este último motor proporcionaba una velocidad bastante elevada para la época, de 115 km/h con un consumo de 6L a los 100. Esto era gracias a su reducido peso, solo 660 kg. Este consumo logró ser muy competitivo durante muchos años con otros vehículos similares. Además es bastante corto y estrecho para los estándares actuales.</p>

<div style="text-align:center">
<video src="https://files.adrianistan.eu/Renault4-BellezaMecanica.webm" controls>
</div>

<p>Una de las cosas más llamativas del coche es su interior, donde brilla la simplicidad máxima. El interior usa la misma pieza como tirador que para el propio bloqueo de la puerta. No tiene ventanilla vertical, solo una pestaña horizontal. El cuadro de mandos solo indica la velocidad y la cantidad de gasolina que queda. Tampoco existe palanca de cambios, sino una varilla que se conecta directamente a la caja de cambios.</p>

<p>Originalmente el coche permitía arrancar con manivela por si la batería fallaba, aunque este detalle se eliminó en versiones posteriores.</p>

<p>Los prototipos del coche recorrieron gran cantidad de kilómetros por territorios inhóspitos como montañas nevadas o desiertos, sin olvidar la carretera y la ciudad.</p>

<p>Sin embargo, el coche fue bastante criticado. Se decía que no aislaba del sonido, que era feo con la chapa directamente expuesta, que no era estable, etc. El tiempo finalmente le dio la razón</p>

<h2>¿Qué podemos aprender en el software?</h2>
<p>No hay que tener miedo a utilizar soluciones tecnológicas que son minoritarias en el mercado (por ejemplo el motor delantero). Pero más importante, primero hay que conocer bien estas alternativas y como están funcionando. Por ello es vital conocer y analizar el mercado a fondo, realizar una investigación exhaustiva, incluyendo productos que a priori no son tu competencia (Mini).</p>
<p><i>La mejor pieza es aquella que ha podido ser eliminada.</i> Esta fue una máxima que impera en todo el coche, para ganar espacio, ahorrar costo y tener mayor fiabilidad. En el software ocurre lo mismo, menos es más. Tenemos que buscar código que sea lo más simple posible, pero sin dejar de cumplir los requisitos.</p>
<p>Tests desde el primer momento. Los Renault 4 recorrieron miles de kilómetros antes de salir al mercado, para validar que las ideas eran correctas desde el principio. Cuando salió, el Renault 4 no era el coche perfecto, pero era suficientemente bueno.</p>
<p>La mejora continua. El Renault 4 tuvo muchas variaciones que fue mejorando poco a poco. Aunque de partida fue buen coche, con los años fue mejorando, aumentando la potencia de los motores
<p>Usa componentes fácilmente reemplazables. El Renault 4 tenía fama de fiable y no solo era por su simple pero efectiva mecánica sino porque muchas piezas eran fácilmente sustituibles. En el software deberíamos buscar justamente eso, código fácilmente actualizable para solventar fallos y que no requieran un gran conocimiento del resto del sistema.</p>
<p>El Renault 4 era un coche flexible, tenía gran cantidad de usos. De hecho, con ligeras modificaciones también sirvió para competiciones de motor, quedando en muy buenos puestos en el rally Dakar. El software </p>
<p>El Renault 4 era un coche económico. A parte de ser barato al comprarlo, tampoco consumía mucha gasolina. En el software deberíamos no solo procurar precios asequibles de compra (si aplica) sino un consumo de recursos bajo, no gastar más memoria o ciclos si no está justificado. También debemos considerar el costo de aprender a usar un programa. Los programas deben ser lo más autodescriptivos posibles y permitir la exploración sin riesgo para aprender a usar más funcionalidades poco a poco. </p>

<div style="text-align:center">
<img width="700" src="https://files.adrianistan.eu/R4Vatican.jpeg">
</div>]]></description>
                <comments>https://blog.adrianistan.eu/construye-renault-4</comments>
                <pubDate>Sat, 25 Apr 2020 20:39:22 +0000</pubDate>
            </item>
        
            <item>
                <title>Debug gráfico en Prolog</title>
                <link>https://blog.adrianistan.eu/debug-grafico-prolog</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/debug-grafico-prolog</guid>
                <description><![CDATA[<p>Hace ya un tiempo hice un pequeño <a href="https://blog.adrianistan.eu/introduccion-a-prolog-tutorial-en-espanol">tutorial de Prolog</a>. Desde entonces hasta hoy he aprendido mucho y seguramente rehaga el tutorial en algún momento, de forma más práctica. Mientras tanto voy a repasar una sección que tenía el tutorial, el debugging</p>

<div style="text-align:center">
<img width="700" src="https://files.adrianistan.eu/PrologDebug.png">
</div>


<p>Prolog dispone de un modo de depuración textual, basado en el término <i>trace/0</i>. Cuando aparece, Prolog entra en modo debug y nos va informando de como se van ejecutando las cosas. Qué unificaciones realiza, cuando falla, etc Pero tiene pocas opciones de control</p>
<p>Sin embargo es extremadamente verboso y difícil de manipular. Es por ello que en bastantes entorno gráficos podemos hacer una llamada a <i>gtrace/0</i>, que abrirá un debugger gráfico.</p>

<p>El debugger gráfico nos permite ver el código en un editor tipo Emacs. Nos marca con un color la regla que ha probado, y si su resultado ha sido satisfactorio (color verde) o no (color rojo). Después una regla que no se puede cumplir, salta hasta el punto anterior de decisión con color amarillo. Todos los puntos de decisión que Prolog detecta se marcan arriba a la derecha, en el <i>Call Stack</i>, como caminos alternativos.</p>

<div style="text-align:center">
<img width="700" src="https://files.adrianistan.eu/PrologDebugPoints.png">
<br>
<small>Arriba a la derecha se ve el camino alternativo que ha generado between</small>
</div>

<p>Arriba a la izquierda tenemos las variables en ese momento, que podemos inspeccionar con detalle</p>

<p>Por último, el control de ejecución. Podemos avanzar paso a paso sobre nuestro código, paso a paso sobre todo el código (para inspeccionar por dentro de una librería por ejemplo), podemos saltarnos una regla y lo más importante, podemos ir marcha atrás en el código para volverlo a inspeccionar. Esto último es algo que muy pocos entornos de depuración tienen y es algo extremadamente útil por si nos saltamos por error algo o queremos mirar detalladamente algo otra vez sin tener que parar y volver a empezar.</p>
<ul>
<li>La flecha hacia abajo nos permite entrar en el código de las librerías de Prolog</li>
<li>La flecha recta hacia la derecha sigue todos los pasos de nuestro código</li>
<li>La flecha bombeada hacia la derecha avanza el código sin meterse en ninguna regla que no sea la actual.</li>
<li>La bandera deja acabar la regla actual</li>
<li>La flecha bombeada hacia la izquierda nos deja volver a empezar la regla actual</li>
</ul>
<div style="text-align:center">
<img width="700" src="https://files.adrianistan.eu/PrologDebugUnification.png">
<br>
<small>La flecha hacia abajo nos permite entrar en el código de las librerías de Prolog</small>
</div>


<p>El editor gráfico también nos permite añadir breakpoint o spy. Los spy se pueden añadir previamente con <i>tspy/1</i> que necesita el nombre de una regla</p>

<div style="text-align:center">
<img width="700" src="https://files.adrianistan.eu/PrologTspy.png">
<br>
<small>Usando tspy y el botón del espía para ir hasta el el inicio de la regla con tspy</small>
</div>

<h2>Code swapping</h2>
<p>Prolog además nos permite hacer code swapping, es decir, cambiar el código que se está ejecutando sin tener que volver a iniciar el programa. Tiene algunas limitaciones no obstante. Para ello podemos activar el modo edición dentro del debugger (el lápiz) y empezar a modificar reglas siempre que no sea una que no esté en el call stack (en ese caso Prolog se guarda una copia internamente hasta se salga de ella).</p>
<p>Para cargar el código hay que salir del modo edición, e ir a Compile->Make. En el caso de estar editando la regla que estábamos debugeando, caso muy típico, deberemos volver hacia atrás también (flecha bombeada hacia la izquierda)</p>
<div style="text-align: center">
<video width="700" src="https://files.adrianistan.eu/PrologDebugCodeSwap-2.webm" controls></video>
<br>
<small>El vídeo muestra un ejemplo de code swap. Sin embargo, lo mejor es que lo pruebes por ti mismo</small>
</div>
<p>Aprendiendo a usar estas herramientas lograremos una fantástica <b>prolugtividad</b></p>


]]></description>
                <comments>https://blog.adrianistan.eu/debug-grafico-prolog</comments>
                <pubDate>Thu, 16 Apr 2020 21:18:22 +0000</pubDate>
            </item>
        
            <item>
                <title>Sudoku en Prolog</title>
                <link>https://blog.adrianistan.eu/sudoku-prolog</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/sudoku-prolog</guid>
                <description><![CDATA[<p><a href="https://blog.adrianistan.eu/introduccion-a-prolog-tutorial-en-espanol">Prolog</a> es el lenguaje más importante dentro del paradigma lógico. Uno de los puntos claves de Prolog es su expresividad para modelar un problema, y de la misma forma que ha sido modelado, resolverlo. Veremos como resolver el clásico <a href="https://es.wikipedia.org/wiki/Sudoku">Sudoku</a> usando Prolog.</p>

<div style="text-align: center">
<img width="300" src="https://files.adrianistan.eu/Sudoku.jpeg">
</div>

<h2>El tablero</h2>
<p>Lo primero que tenemos que pensar es que estructura de datos va a tener el sudoku internamente. En el caso del sudoku, en la vida real sabemos que es una especie de matriz. Por simplicidad, vamos a implementarlo como una lista de Prolog. Las listas en Prolog son estructuras de datos plenamente integradas con el lenguaje. Será una lista plana de 81 (9x9) elementos. La lista contiene inicialmente los números iniciales del tablero y x donde no tenemos solución. Los números iniciales no van a cambiar nunca, así que ya sabemos que son parte de la solución definitiva.</p>
<pre><code>
        [4,x,x,x,6,x,9,1,x,
         2,x,x,x,x,7,x,5,x,
         x,9,x,8,x,x,x,2,x,
         x,x,1,6,x,9,x,x,2,
         x,8,x,x,x,x,x,6,3,
         x,7,x,x,4,x,x,x,x,
         7,x,3,x,x,8,x,9,x,
         x,x,x,x,3,x,4,x,5,
         x,4,x,9,x,x,6,x,x].
</code></pre>
<p>En el sudoku hay tres subdivisiones principales: filas, columnas y cuadrados. Vamos a hacer un código en Prolog para definir cada una de estas subdivisiones. Las filas son fáciles de definir:
<pre><code class="language-prolog">
row(Sudoku, N, Row) :-
    N0 is N-1,
    N1 is N0*9+1, nth1(N1, Sudoku, X1),
    N2 is N0*9+2, nth1(N2, Sudoku, X2),
    N3 is N0*9+3, nth1(N3, Sudoku, X3),
    N4 is N0*9+4, nth1(N4, Sudoku, X4),
    N5 is N0*9+5, nth1(N5, Sudoku, X5),
    N6 is N0*9+6, nth1(N6, Sudoku, X6),
    N7 is N0*9+7, nth1(N7, Sudoku, X7),
    N8 is N0*9+8, nth1(N8, Sudoku, X8),
    N9 is N0*9+9, nth1(N9, Sudoku, X9),
    Row = [X1, X2, X3, X4, X5, X6, X7, X8, X9].
</code></pre>
<p>Las filas se componen de 9 valores, los cuáles accedemos a ellos calculando el índice de la lista y accediendo al valor con nth1. Con nth1, las listas empiezan a contar en 1 (¡sacrilegio! ¡blasfemia!). Si usásemos nth0, las listas empezarían a contar en 0. ¡Prolog lo tiene todo!</p>
<p>Las columnas son iguales pero modificando el cálculo del índice lógicamente</p>
<pre><code class="language-prolog">
column(Sudoku, N, Column) :-
    N1 is 0*9+N, nth1(N1, Sudoku, X1),
    N2 is 1*9+N, nth1(N2, Sudoku, X2),
    N3 is 2*9+N, nth1(N3, Sudoku, X3),
    N4 is 3*9+N, nth1(N4, Sudoku, X4),
    N5 is 4*9+N, nth1(N5, Sudoku, X5),
    N6 is 5*9+N, nth1(N6, Sudoku, X6),
    N7 is 6*9+N, nth1(N7, Sudoku, X7),
    N8 is 7*9+N, nth1(N8, Sudoku, X8),
    N9 is 8*9+N, nth1(N9, Sudoku, X9),
    Column = [X1, X2, X3, X4, X5, X6, X7, X8, X9].
</pre></code>
<p>Por último, los cuadrados tienen un pelín más de complicación de calcular los índices, pero nada que no se pueda resolver con un poco de aritmética extra.</p>
<pre><code class="language-prolog">
square(Sudoku, N, Square) :-
    O is (N-1) // 3,
    P is (N-1) mod 3,
    N1 is O*27+P*3+1 , nth1(N1, Sudoku, X1),
    N2 is O*27+P*3+2 , nth1(N2, Sudoku, X2),
    N3 is O*27+P*3+3 , nth1(N3, Sudoku, X3),
    N4 is O*27+P*3+10 , nth1(N4, Sudoku, X4),
    N5 is O*27+P*3+11 , nth1(N5, Sudoku, X5),
    N6 is O*27+P*3+12 , nth1(N6, Sudoku, X6),
    N7 is O*27+P*3+19 , nth1(N7, Sudoku, X7),
    N8 is O*27+P*3+20 , nth1(N8, Sudoku, X8),
    N9 is O*27+P*3+21 , nth1(N9, Sudoku, X9),
    Square = [X1, X2, X3, X4, X5, X6, X7, X8, X9].
</code></pre>
<h2>Condiciones del Sudoku</h2>
<p>Una vez hemos definido estos términos, podemos pasar a comprobar como tenemos un sudoku válido. La norma dice que en las casillas de cada subdivisión deben estar todos los números, del 1 al 9, y como tienen un tamaño de 9, eso quiere decir que tampoco se pueden repetir.</p>
<p>Un enfoque simple podría ser comprobar que están todos los números en una subdivisión dada. Algo tal que así:</p>
<pre><code class="language-prolog">
valid0(R) :-
    proper_length(R, 9),
    member(1, R),
    member(2, R),
    member(3, R),
    member(4, R),
    member(5, R),
    member(6, R),
    member(7, R),
    member(8, R),
    member(9, R).
</code></pre>
<p>Esta comprobación es correcta, pero cuando queramos resolver el sudoku va a ser extremadamente lenta. La razón es que cuando un número no existe en la subdivisión, lo añadirá para intentar hacer cumplir la regla. El inconveniente es que lo hace con todos los números que no están asignados a la vez, esto de cara a la búsqueda de la solución es muy lento. Es mucho mejor ir añadiendo un número, comprobar todo, luego volver a añadir otro.</p>
<p>Una validación mucho más simple y rápida es comprobar que si la subdivisión, que es una lista, es además un set, es decir, no hay elementos repetidos. Esto no nos añadirá números innecesariamente (¡de hecho no nos añadirá ninguno, lo tendremos que hacer en otro lado!).
</p>
<pre><code class="language-prolog">
valid(R) :-
    is_set(R).
</code></pre>
<h2>Resolviendo</h2>
<p>Vamos a la parte final, vamos a resolver el sudoku, que gracias a las cualidades de Prolog, también nos dice si el tablero es correcto o no.</p>
<p>Lo primero que vamos a hacer es crear variables nuevas en cada X que encontremos en la entrada original. Para esta tarea he decidido usar <a href="https://blog.adrianistan.eu/prolog-dcg-gramaticas-clausula-definida">DCGs</a>, que ya he explicado anteriormente. Si quieres entender el código deberás leerlo antes, si simplemente te lo crees, aquí está:
<pre><code class="language-prolog">
program([H|T]) --> digit(H), program(T).
program([]) --> [].
digit(N) --> [N], { number(N) } .
digit(X) --> [x].
</code></pre>
<p>El código de <b>sudoku</b> se encarga de realizar la transformación de la lista con la DCG y de ejecutar un maplist sobre el tablero. ¿Qué función realiza maplist? Simplemente itera por todos los elementos de la lista, números y variables, ejecutando para ellos una comprobación. Esta comprobación será la que compruebe que los números están bien situados, y nos genere los números que sustituyan a las variables.</p>
<pre><code class="language-prolog">
sudoku(Sudoku, SolvedSudoku) :-
    phrase(program(SolvedSudoku), Sudoku),!,
    maplist(check(SolvedSudoku), SolvedSudoku).
</code></pre>
<p>El último código que nos falta es el de check</p>
<pre><code class="language-prolog">
check(SolvedSudoku, N) :-
    between(1,9,N),
    row(SolvedSudoku, 1, R1), valid(R1),
    row(SolvedSudoku, 2, R2), valid(R2),
    row(SolvedSudoku, 3, R3), valid(R3),
    row(SolvedSudoku, 4, R4), valid(R4),
    row(SolvedSudoku, 5, R5), valid(R5),
    row(SolvedSudoku, 6, R6), valid(R6),
    row(SolvedSudoku, 7, R7), valid(R7),
    row(SolvedSudoku, 8, R8), valid(R8),
    row(SolvedSudoku, 9, R9), valid(R9),
    column(SolvedSudoku, 1, C1), valid(C1),
    column(SolvedSudoku, 2, C2), valid(C2),
    column(SolvedSudoku, 3, C3), valid(C3),
    column(SolvedSudoku, 4, C4), valid(C4),
    column(SolvedSudoku, 5, C5), valid(C5),
    column(SolvedSudoku, 6, C6), valid(C6),
    column(SolvedSudoku, 7, C7), valid(C7),
    column(SolvedSudoku, 8, C8), valid(C8),
    column(SolvedSudoku, 9, C9), valid(C9),
    square(SolvedSudoku, 1, S1), valid(S1),
    square(SolvedSudoku, 2, S2), valid(S2),
    square(SolvedSudoku, 3, S3), valid(S3),
    square(SolvedSudoku, 4, S4), valid(S4),
    square(SolvedSudoku, 5, S5), valid(S5),
    square(SolvedSudoku, 6, S6), valid(S6),
    square(SolvedSudoku, 7, S7), valid(S7),
    square(SolvedSudoku, 8, S8), valid(S8),
    square(SolvedSudoku, 9, S9), valid(S9).
</code></pre>
<p>Aquí juntamos todo lo que hemos creado antes. N será el valor de la casilla de la lista. Si N es ya un número fijado, simplemente validará todas las condiciones, si es una variable, between elegirá un número entre 1 y 9 para que sea N y comprobará las condiciones. Si falla alguna condición, elegirá otro número entre 1 y 9. Si ningún número entre 1 y 9 puede cumplir las condiciones, Prolog irá marcha atrás en el maplist y le dará otro número al elemento anterior, y si no fuese posible, anterior y al anterior y al anterior, así hasta que pueda volver a avanzar. Esto es claro ejemplo de algoritmo de backtracking, que Prolog implementa en su ejecución. Gracias a esto, hemos podido modelar el sudoku fácilmente.</p>
<h2>Conclusión</h2>
<p>El código completo es el siguiente:</p>
<pre><code class="language-prolog">
row(Sudoku, N, Row) :-
    N0 is N-1,
    N1 is N0*9+1, nth1(N1, Sudoku, X1),
    N2 is N0*9+2, nth1(N2, Sudoku, X2),
    N3 is N0*9+3, nth1(N3, Sudoku, X3),
    N4 is N0*9+4, nth1(N4, Sudoku, X4),
    N5 is N0*9+5, nth1(N5, Sudoku, X5),
    N6 is N0*9+6, nth1(N6, Sudoku, X6),
    N7 is N0*9+7, nth1(N7, Sudoku, X7),
    N8 is N0*9+8, nth1(N8, Sudoku, X8),
    N9 is N0*9+9, nth1(N9, Sudoku, X9),
    Row = [X1, X2, X3, X4, X5, X6, X7, X8, X9].

column(Sudoku, N, Column) :-
    N1 is 0*9+N, nth1(N1, Sudoku, X1),
    N2 is 1*9+N, nth1(N2, Sudoku, X2),
    N3 is 2*9+N, nth1(N3, Sudoku, X3),
    N4 is 3*9+N, nth1(N4, Sudoku, X4),
    N5 is 4*9+N, nth1(N5, Sudoku, X5),
    N6 is 5*9+N, nth1(N6, Sudoku, X6),
    N7 is 6*9+N, nth1(N7, Sudoku, X7),
    N8 is 7*9+N, nth1(N8, Sudoku, X8),
    N9 is 8*9+N, nth1(N9, Sudoku, X9),
    Column = [X1, X2, X3, X4, X5, X6, X7, X8, X9].

square(Sudoku, N, Square) :-
    O is (N-1) // 3,
    P is (N-1) mod 3,
    N1 is O*27+P*3+1 , nth1(N1, Sudoku, X1),
    N2 is O*27+P*3+2 , nth1(N2, Sudoku, X2),
    N3 is O*27+P*3+3 , nth1(N3, Sudoku, X3),
    N4 is O*27+P*3+10 , nth1(N4, Sudoku, X4),
    N5 is O*27+P*3+11 , nth1(N5, Sudoku, X5),
    N6 is O*27+P*3+12 , nth1(N6, Sudoku, X6),
    N7 is O*27+P*3+19 , nth1(N7, Sudoku, X7),
    N8 is O*27+P*3+20 , nth1(N8, Sudoku, X8),
    N9 is O*27+P*3+21 , nth1(N9, Sudoku, X9),
    Square = [X1, X2, X3, X4, X5, X6, X7, X8, X9].

valid(R) :-
    is_set(R).

program([H|T]) --> digit(H), program(T).
program([]) --> [].
digit(N) --> [N], { number(N) } .
digit(X) --> [x].

check(SolvedSudoku, N) :-
    between(1,9,N),
    row(SolvedSudoku, 1, R1), valid(R1),
    row(SolvedSudoku, 2, R2), valid(R2),
    row(SolvedSudoku, 3, R3), valid(R3),
    row(SolvedSudoku, 4, R4), valid(R4),
    row(SolvedSudoku, 5, R5), valid(R5),
    row(SolvedSudoku, 6, R6), valid(R6),
    row(SolvedSudoku, 7, R7), valid(R7),
    row(SolvedSudoku, 8, R8), valid(R8),
    row(SolvedSudoku, 9, R9), valid(R9),
    column(SolvedSudoku, 1, C1), valid(C1),
    column(SolvedSudoku, 2, C2), valid(C2),
    column(SolvedSudoku, 3, C3), valid(C3),
    column(SolvedSudoku, 4, C4), valid(C4),
    column(SolvedSudoku, 5, C5), valid(C5),
    column(SolvedSudoku, 6, C6), valid(C6),
    column(SolvedSudoku, 7, C7), valid(C7),
    column(SolvedSudoku, 8, C8), valid(C8),
    column(SolvedSudoku, 9, C9), valid(C9),
    square(SolvedSudoku, 1, S1), valid(S1),
    square(SolvedSudoku, 2, S2), valid(S2),
    square(SolvedSudoku, 3, S3), valid(S3),
    square(SolvedSudoku, 4, S4), valid(S4),
    square(SolvedSudoku, 5, S5), valid(S5),
    square(SolvedSudoku, 6, S6), valid(S6),
    square(SolvedSudoku, 7, S7), valid(S7),
    square(SolvedSudoku, 8, S8), valid(S8),
    square(SolvedSudoku, 9, S9), valid(S9).

sudoku(Sudoku, SolvedSudoku) :-
    phrase(program(SolvedSudoku), Sudoku),!,
    maplist(check(SolvedSudoku), SolvedSudoku).
</code></pre>
<p>Para usarlo, lo guardamos en un fichero sudoku.pl y ejecutamos SWI-Prolog (swipl)</p>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/PrologSudoku.png">
</div>
<p>En la lista S queda la solución del Sudoku para que la inspeccionemos. Esto es un poco feo, pero con SWI-Prolog podemos hacer <a href="https://blog.adrianistan.eu/programacion-web-prolog">una API</a> que a través de JSON pueda recibir y devolver sudokus, que luego podemos usar en otro sitio con una interfaz más trabajada.</p>]]></description>
                <comments>https://blog.adrianistan.eu/sudoku-prolog</comments>
                <pubDate>Sun, 12 Apr 2020 22:19:57 +0000</pubDate>
            </item>
        
            <item>
                <title>Teletexto #005</title>
                <link>https://blog.adrianistan.eu/teletexto-005</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/teletexto-005</guid>
                <description><![CDATA[<p>Bienvenidos a una nueva edición de Teletexto. Un repaso por la red de enlaces que pueden ser interesantes.</p>
<div style="text-align: center">
<img width="800" src="https://files.adrianistan.eu/TeletextoExperimental.jpg">
</div>
<ul>
<li>Ogg es un formato contenedor de audio y vídeo, software libre y sin patentes. Promovido por la fundación Xiph, se ha usado extensivamente con los códecs Vorbis, Theora y Opus. Sin embargo, <a href="https://hardwarebug.org/2010/03/03/ogg-objections/">Ogg tiene muchas objeciones técnicas.</a></li>
<li>Cada día que pasa el BigData se asienta más en las empresas y organizaciones. Existen multitud de frameworks destinados a trabajar con ingentes cantidades de datos de forma distribuida, tanto en local como en la nube: Spark, Hadoop, Flink, Dataflow, ... Dominar estos frameworks puede ser complicado, por eso Yelp hizo <a href="https://github.com/Yelp/mrjob">MrJob</a>, una librería Python con la simplicidad por bandera y que compatible por detrás con estos frameworks.</li>
<li><a href="https://pijul.com/">Pijul</a> es un sistema de control de versiones, como Git o Subversion, distribuido y que aplica conceptos de teoría de parches presentes en Darcs. No funciona exactamente igual que Darcs (lo mejora supuestamente) y tiene un mayor rendimiento al estar escrito en Rust. De momento hay una forja abierta, Pijul Nest.</li>
<li>¿Programas JavaScript? ¿Haces tests verdad? Espero que sí. Pero siempre viene bien una pequeña <a href="https://github.com/goldbergyoni/javascript-testing-best-practices">guía sobre testing en JavaScript y las mejores prácticas</a></li>
<li>Si has hecho algún parser, ya sea de un lenguaje de programación o de algún otro lenguaje, te sonará, puede que hasta hayas usado, Lex y Yacc. Son dos herramientas clásicas de Unix como lo puede ser GCC, sin embargo hay gente que opina que <a href="https://tomassetti.me/why-you-should-not-use-flex-yacc-and-bison/">ya es hora de dejar de usar Lex y Yacc.</a></li>
<li>Si buscas datasets para realizar Machine Learning seguramente conozcas Kaggle, o el repositorio UCI. Hace relativamente poco descubrí <a href="https://www.openml.org">OpenML</a>, una alternativa abierta muy interesante, con gran cantidad de datasets. Además, se integra con ciertas librerías como sklearn, que pueden descargar los datasets automáticamente de allí.</li>
<li>"Los astros definen tu futuro". La verdad es que yo no creo en la astrología, pero me hizo gracia esta web llamada <a href="https://www.costarastrology.com">Co-Star</a> que te calcula con gran precisión tu carta astral. ¿Cómo lo descubrí? Resulta que esta web funciona en Haskell.</li>
<li><a href="https://danluu.com/corp-eng-blogs/">¿Cómo deben ser los blogs de ingeniería de las empresas?</a> Una respuesta interesante, basada en la experiencia en Cloudflare y otras empresas</li>
<li>En Python es importante que nuestro código pase por linters que nos avisen de posibles fallos, malas prácticas y de cuando nos desviamos del estilo (usualmente PEP8). Pero no existen herramientas estándar. <a href="http://books.agiliq.com/projects/essential-python-tools/en/latest/linters.html">Esta página recoge un listado de linters y sus ventajas e inconvenientes entre sí.</a></li>
<li>Por último, un vídeo de <a href="https://www.youtube.com/watch?v=G_eYTctGZw8 ">una charla sobre Prolog en Producción</a>. Muy interesante para todo el mundo, aunque la disfrutarás más si ya tienes alguna noción básica de Prolog. Me ha dejado muchas ideas pendientes de probar.</li>
</ul>
<p>Por último la canción del Teletexo de hoy es <i>Living in the Plastic Age</i> de The Buggles. La historia musical de este grupo y más en concreto de Trevor Horn es mucho más amplia que <i>Video Killed the Radio Star</i> y os invito a que investiguéis.</p>
<div style="text-align: center">
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/QFqo8WkP14k" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>]]></description>
                <comments>https://blog.adrianistan.eu/teletexto-005</comments>
                <pubDate>Mon, 30 Mar 2020 21:56:18 +0000</pubDate>
            </item>
        
            <item>
                <title>Rustizando tu Linux</title>
                <link>https://blog.adrianistan.eu/rustizando-linux</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/rustizando-linux</guid>
                <description><![CDATA[<p><i>Bienvenidos al primer artículo de Adrianistán desde la instauración del estado de alarma, desde aquí mando un reconocimiento a todas aquellas personas que trabajan en esta crisis que esperemos acabe pronto.</i></p>
<p>Hace unos días leí <a href="https://twitter.com/jesusprubio/status/1237752138069094400">un tuit de Jesús Rubio</a> que básicamente mostraba como había sustituido una cantidad importante de comandos típicos de Unix con variantes escritas en Rust. Rust es un lenguaje capaz de sustituir a C en cualquier sitio, sin penalización de rendimiento y con beneficios extra, aunque a la gente que se sienta cómoda con el funcionamiento de C les costará adaptarse a un lenguaje como Rust. Os recuerdo que tenéis un <a href="https://blog.adrianistan.eu/rust-101-tutorial-rust-espanol">tutorial de Rust en este blog</a>, útil para iniciarse.</p>
<p>En este post veremos algunas de estas herramientas y como se instalan, además de ver si son sustitutos capaces o no a las implementaciones clásicas.</p>
<div style="text-align: center">
<img src="https://files.adrianistan.eu/RustAlias.jpg">
<br>
<small>La foto original de Jesús Rubio</small>
</div>
<h2>ls -> exa</h2>
<p>ls es el famoso comando Unix de listar un directorio. Es el equivalente a dir de MS-DOS. Cualquier persona que haya tocado la línea de comandos alguna vez lo conoce. <a href="https://the.exa.website/">exa</a> es la versión hecha en Rust. Es compatible con la mayoría de opciones de ls aunque el creador admite que no es un reemplazo directo. La razón es que dice que exa tiene mejores opciones por defecto que ls, aunque eso implicase romper la compatibilidad.</p>
<p>Otras características de exa es que se adapta mejor a pantallas grandes, se integra con Git, puede mostrar los atributos extendidos de los ficheros (no solo los estandar), tiene colorines, puede seguir los enlaces simbólicos y mostrar vistas de árbol. Todo ello sin perder velocidad.</p>
<p>Unas cuantas distros como Fedora, Arch, Debian o Ubuntu ya lo empaquetan en sus últimas versiones, así que usa el gestor de paquetes nativo o Nix si prefieres.</p>
<p>En Debian o Ubuntu es tan simple como:</p>
<pre><code>
sudo apt install exa
</code></pre>
<div style="text-align: center">
<img src="https://files.adrianistan.eu/LsExa.png">
</div>
<p>A priori no parece haber mucha diferencia, aunque por debajo funcionan ya de forma diferente. exa hace más llamadas al sistema que ls, y esto es por diseño. ls en su día se diseñó para ordenadores lentos, exa se diseña para ordenadores modernos y por ello, intenta recabar más información. La diferencia, en los ordenadores actuales, de rendimiento es mínima.</p>
<div style="text-align: center">
<img src="https://files.adrianistan.eu/ExaLong.png">
</div>
<p>Para consultar todas las opciones, revisar el manual o ejecutar la ayuda con --help</p>
<h2>grep -> ripgrep (rg)</h2>
<p>ripgrep es el reemplazo moderno a grep, la utilidad para buscar sobre texto. Posiblemente ya lo estés utilizando sin saberlo ya que Visual Studio Code lo utiliza para su función de búsqueda.</p>
<p><a href="https://blog.burntsushi.net/ripgrep/">ripgrep se define</a> como el programa que combina la usabilidad de The Silver Searcher con el rendimiento de grep. ripgrep es 100% Unicode, y tiene algunas características específicas para buscar código, como ignorar ficheros en .gitignore, filtrar búsquedas a ficheros de cierto lenguaje de programación, soporta las regex más potentes (PCRE2), permite buscar en ficheros comprimidos zip, bzip2, lzma, etc. La mala noticia es que debido a estas mejoras, ripgrep no cumple con el estándar POSIX, aunque la importancia de eso, en mi opinión, es debatible.</p>
<p>ripgrep ya está disponible en muchas distribuciones como Debian o Fedora. Una cosa a tener en cuenta es que aunque el programa se llama ripgrep, por comodidad el comando suele ser rg.</p>
<pre><code>
sudo apt install ripgrep
</code></pre>
<div style="text-align: center">
<img src="https://files.adrianistan.eu/RgSearch.png">
</div>
<h2>{cat, less} -> batcat</h2>
<p>batcat es un clon de cat y otros programas más avanzados para mostrar texto como less o more. Soporta resaltado de sintaxis, integración con Git para mostrar cambios, permite mostrar caracteres no legibles, soporta búsquedas y mucho más.</p>
<div style="text-align: center">
<img src="https://files.adrianistan.eu/Batcat.png">
</div>
<div style="text-align: center">
<img src="https://files.adrianistan.eu/BatcatGit.png">
<br>
<small>Observad los símbolos + y ~ en el lateral, es la integración con Git</small>
</div>
<p>Está disponible en la mayoría de distribuciones, aunque en algunas como Debian el ejecutable no se llama bat sino batcat</p>
<pre><code>
sudo apt install bat
</code></pre>
<h2>{ps, top} -> procs</h2>
<p>procs es el sustituo de ps. Añade alguna información extra como los puertos TCP/UDP abiertos, métricas de entrada/salida, integración con Docker, además de modos de búsqueda mejorados y soporte para funcionar como sustituto de top.</p>
<div style="text-align: center">
<img src="https://files.adrianistan.eu/Procs.png">
</div>
<p>Si ejecutamos procs sin ningún parámetro, entrará en un modo similar a top.</p>
<p>procs no está en ninguna distro pero se puede instalar via Snap o via Nix</p>
<pre><code>
sudo snap install procs
nix-env -i procs
</code></pre>
<h2>nano -> kibi</h2>
<p>kibi es un editor de texto para línea de comandos escrito en menos de 1024 líneas de código Rust. Si bien no es tan potente como Vim o Emacs, puede ser un buen sustituto a Nano. Soporta Unicode, búsquedas, resaltado de sintaxis, numeración, ...</p>
<div style="text-align: center">
<img src="https://files.adrianistan.eu/Kibi.png">
</div>
<p>Este programa es el más complejo de instalar, necesitarás tener Cargo instalado y ejecutar:</p>
<pre><code>
cargo install kibi
</code></pre>
<p>Y asegurarte de que el binario generado por Cargo se encuentra en el PATH. Además el resaltado de sintaxis no te funcionará porque no tienes los ficheros de configuración. Para seguir con esto, <a href="https://github.com/ilai-deutel/kibi">recomiendo ver el proyecto en GitHub.</a></p>
<h2>{find, parallel} -> fd-find</h2>
<p>fd-find es una alternativa moderna a find, la herramienta para buscar archivos y opcionalmente ejecutar comandos al encontrarlos, no compatible al 100% ya que al igual que exa, intenta mejorar las opciones por defecto. He de reconocer que find es uno de los programas que menos uso ya que me custa mucho acordarme de sus parámetros y que muchas veces no son coherentes con otras herramientas Unix. </p>
<div style="text-align: center">
<img src="https://files.adrianistan.eu/FdFind.png">
</div>
<p>Para ejecutar comandos en paralelo, por ejemplo, convertir todas las imágenes jpg en png (en paralelo):</p>
<pre><code>
fd -e jpg -x convert {} {.}.png
</code></pre>
<p>El comando fd está ya cogido en muchas distros, así que en Debian viene como fdfind. Se puede instalar desde apt.</p>
<pre><code>
sudo apt install fd-find
</code></pre>
<h2>sh, bash, zsh, fish -> nu</h2>
<p><a href="https://www.nushell.sh">Nu</a> es una shell escrita en Rust que daría para artículos y artículos enteros. Todavía es algo inestable pero promete mucho. En Nu todos los comandos se comunican mediante tablas y formatos de datos estructurados, en lugar del texto plano tradicional de Unix. En ciertos momentos, parece que estamos ante una base de datos. Esta idea no es nueva, ciertos sistemas operativos minoritarios (IBM i) tienen conceptos similares, pero es bastante innovadora en Linux. La única shell para Linux de características similares es PowerShell de Microsoft.</p>
<p>Por ejemplo, de un simple ls (todavía no hay integración con exa) podemos filtrar con where, obtener solo los primeros campos, etc</p>
<div style="text-align: center">
<img src="https://files.adrianistan.eu/NuShell.png">
</div>
<p>Además Nu reconoce de forma nativa formatos como JSON, TOML, XML o YAML y permite recorrer sus campos. Por ejemplo, de un fichero de Rust Cargo.toml podemos acceder a la versión de una dependencia fácilmente con open y get.</p>
<div style="text-align: center">
<img src="https://files.adrianistan.eu/NuOpenGet.png">
</div>
<p>Nu es en definitiva una shell muy prometedora y espero que siga mejorando en el futuro, aunque todavía no está lista para trabajar a diario con ella. También el soporte a scripts está un poco verde, pero ya se sabe que serán incompatibles con Bash.</p>
<p>Nu lo podéis instalar desde Nix o desde Cargo:</p>
<pre><code>
cargo install nu
nix-env -i nushell
</code></pre>
<p>Estas han sido solo algunas de la multitud de herramientas hechas en Rust que clonan y mejoran la funcionalidad de las herramientas clásicas de Unix. ¿Crees que llegarán a sustituir a las herramientas clásicas de Unix?</p>]]></description>
                <comments>https://blog.adrianistan.eu/rustizando-linux</comments>
                <pubDate>Tue, 17 Mar 2020 14:36:18 +0000</pubDate>
            </item>
        
            <item>
                <title>Teletexto #004</title>
                <link>https://blog.adrianistan.eu/teletexto-004</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/teletexto-004</guid>
                <description><![CDATA[<p>Bienvenidos a una nueva edición de Teletexto, ya sabéis, esa sección donde recopilo enlaces de interés para vuestro uso y disfrute.</p>

<div style="text-align:center">
<img src="https://files.adrianistan.eu/Ceefax.jpeg">
</div>

<ul>
<li>¿Cómo nos definimos? ¿Qué es nuestra identidad? <a href="http://www.paulgraham.com/identity.html">Paul Graham escribió sobre esto en 2009</a> y me ha parecido muy interesante compartirlo.</li>
<li>Si te interesa la legalidad en el mundo de la informática, pero no eres experto, quizá te haya echado atrás la cantidad de documentación dispersa que existe. Afortunadamente el BOE recopiló un <a href="https://www.boe.es/biblioteca_juridica/codigos/codigo.php?id=173_Codigo_de_Derecho_de_la_Ciberseguridad&modo=1">Código de Derecho de la Ciberseguridad</a> a finales del año pasado, con todos los textos legales vigentes sobre la materia. Es un buen tocho aviso.</li>
<li>Si participas en Kaggle, seguro que más de una vez te has preguntado como lo han hecho los que han ganado. <a href="http://kagglesolutions.com/">Kaggle Winning Solutions</a> contiene las soluciones para cada competición, y así poder aprender de los mejores.</li>
<li><a href="http://darcs.net/">Darcs</a> es un sistema de control de versiones hecho en Haskell y usando un concepto matemático novedoso y diferente a Git. Lleva existiendo bastantes años y se descubrió un fallo teórico en el algoritmo, pero sigue siendo una opción interesante para curiosear.</li>
<li>¿Los programadores se dividen en <a href="https://josephg.com/blog/3-tribes/">tres tribus</a>? Interesante reflexión. ¿En qué tribu me meteríais?</li>
<li>En <a href="https://www.ivoox.com/undefined-v9-retos-concursos-codigo-audios-mp3_rf_48087085_1.html">el podcast de Undefined v9</a>, he estado hablando sobre retos y concursos de programación.</li>
<li>El invierno de la IA ocurrió cuando se diseñaron modelos teóricos de inteligencia artificial pero no existía suficiente capacidad de cómputo como para que fuesen útiles. Algunos de estos modelos con el tiempo se han podido llevar a cabo, como las redes neuronales. <a href="https://ai.stackexchange.com/questions/15986/computationally-expensive-ai-techniques-that-are-promising">En StackExchange AI se preguntan qué modelos téoricos siguen siendo demasiado intensivos computacionalmente como para realizarse de forma práctica</a>. Muy interesante por si en el futuro de repente tenemos esa capacidad.</li>
<li>¿Necesita JavaScript un tipo numérico de alta precisión? <a href="https://docs.google.com/presentation/d/1qceGOynkiypIgvv0Ju8uPqXP4GsWHoY2IVYSWE8SA4Y/edit#slide=id.p">La propuesta a debate</a></li>
<li><a href="http://blog.rfox.eu/en/Programmer_s_critique_of_missing_structure_of_oper.html">Los sistemas operativos se han quedado estancados y apenas cuentan con una estructura más haya de los ficheros.</a> Este post es muy interesante (y largo) y habla sobre la estructura perdida en los sistemas operativos más modernos. Es cierto que en Haiku y en IBM i esto es diferente, pero son sistemas muy minoritarios. Los comentarios de <a href="https://lobste.rs/s/08jcgq/programmer_s_critique_missing_structure">Lobste.rs</a> también son buenos.</li>
<li>¿Hay alternativas de hasheado a la familia SHA? Sí, <a href="https://en.wikipedia.org/wiki/RIPEMD">RIPEMD</a></li>
</ul>
<p>Para acabar una canción, en este caso un clásico de Frank T.</p>
<div style="text-align: center">
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/ccMN8r7NzY0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>

]]></description>
                <comments>https://blog.adrianistan.eu/teletexto-004</comments>
                <pubDate>Tue, 03 Mar 2020 22:33:44 +0000</pubDate>
            </item>
        
            <item>
                <title>Testing en lenguaje natural con Gherkin y Behave</title>
                <link>https://blog.adrianistan.eu/testing-lenguaje-natural-behave-python</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/testing-lenguaje-natural-behave-python</guid>
                <description><![CDATA[<p>El testing es fundamental para asegurar que nuestras aplicaciones cumplen con unos requisitos de calidad mínimos. Existen muchos tipos de test y formas de hacer tests. En este artículo vamos a ver el Behavior Driven Development y como lo podemos aplicar en Python con <a href="https://behave.readthedocs.io">Behave.</a></p>
<h2>Behavior Driven Development</h2>
<p>El BDD es una técnica que surge a raíz del TDD (Test Driven Development). La esencia del TDD es que tenemos que escribir los tests antes que el propio código. BDD va un paso más allá, tenemos que hacer tests que sirvan para describir las especificaciones en un lenguaje que pueda editar un no-programador. La idea es que pueda haber alguien de producto, que defina las especificaciones y que a su vez son tests de aceptación.</p>
<p>El lenguaje más usado en BDD es <a href="https://cucumber.io/docs/gherkin/">Gherkin</a>, cuya base son las sentencias Given, When y Then. Una de las implementaciones de Gherkin en Python más usadas es Behave. Con Given debemos añadir todos los pasos necesarios para llegar hasta el punto donde queremos realizar el test. En When realizamos un estímulo, lo que se prueba. Idealmente sería una única sentencia. Con Then comprobamos que el resultado del estímulo es el esperado. Veamos un ejemplo. Todos ellos admiten combinaciones mediante And y But</p>

<pre><code>
Feature: comments

Scenario: add a comment in blog post
Given a post page is loaded
When I add a comment
Then the page is reloaded
And the page shows a confirmation message
And the comment is registered in the database

Scenario: approve comment
Given the comment is registered in the database
And I can't see the comment in the post page
When I approve the comment
Then I can see the comment in the post page
</code></pre>

<p>Cada paquete de Give/When/Then se denomina escenario y una feature o característica se compone de varios escenarios. Las features se guardan en un fichero .feature dentro de la carpeta features. Dentro de la carpeta features, si usamos Behave, tendremos una carpeta llamada steps que tendrá dentro código Python para implementar los tests correctamente. Para ello usamos el decorador step. Las funciones admiten un parámetro siempre, llamado context, que es propio de Behave y que puede usarse para mover estado entre steps.</p>
<pre><code class="language-python">
from behave import *

@step("a post page is loaded")
def post_page_load(context):
    pass

@step("I add a comment")
def add_comment(context):
    pass

@step("the page is reloaded")
def page_reloaded(context):
    assert True is True

@step("the page shows a confirmation message")
def page_shows_confirmation_message(context):
    assert True

@step("the comment is registered in the database")
def comment_registered_in_database(context):
    assert not False

@step("I can't see the comment in the post page")
def cant_see_comment(context):
    assert True

@step("I can see the comment in the post page")
def can_see_comment(context):
    assert True

@step("I approve the comment")
def approve_comment(context):
    assert True
</code></pre>
<p>En este caso vamos a dejar los tests vacíos, pero aquí habría que implementar una lógica real de test, por ejemplo, con Selenium para interactuar con la web y comprobar que efectivamente la funcionalidad está implementada y funciona tal cual se ha especificado.</p>
<p>Para ejecutar los tests, escribimos behave</p>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/Behave.png">
</div>
<h2>Variables y tablas</h2>
<p>Estos steps deben describir en lenguaje natural las precondiciones, la interacción y las postcondiciones pero puede que queramos reutilizar steps o parametrizarlos. Para ello tenemos variables y tablas</p>
<p>Las variables se definen en el step rodeadas de llaves y en la función del step aparecen como una variable más en los argumentos de entrada. Por ejemplo podemos modificar el siguiente escenario para añadir un texto de ejemplo.</p>
<pre><code>
Scenario: add a comment in blog post
Given a post page is loaded
When I add a comment with text "your post looks insteresting"
Then the page is reloaded
And the page shows a confirmation message
And the comment is registered in the database with text "your post looks interesing"
</code></pre>
<p>Y en los steps: </p>
<pre><code class="language-python">
@step("I add a comment with text \"{text}\"")
def add_comment(context, text):
    pass

@step("the comment is registered in the database with text \"{text}\"")
def comment_registered_in_database(context, text):
    assert not False
</code></pre>
<p>Sin embargo si nuestro texto es muy grande podemos añadirlo de forma adjunta y accesible a través de la variable <code>context.text</code></p>
<pre><code>
Scenario: add a comment in blog post
Given a post page is loaded
    """
    Lorem Ipsum
    """
When I add a comment 
Then the page is reloaded
And the page shows a confirmation message
And the comment is registered in the database
</code></pre>
<pre><code class="language-python">
@step("a post page is loaded")
def post_page_load(context):
    print(context.text)
</code></pre>

<p>También existen tablas, que son accesibles a través de la variable <code>context.table</code></p>
<pre><code>
Scenario: add a comment in blog post
Given a post page is loaded
When I add a comment 
    | text | username |
    | Hey! | aarroyoc |
    | Bye! | marlogui |
Then the page is reloaded
And the page shows a confirmation message
And the comment is registered in the database with text "your post looks interesing"
</code></pre>
<pre><code class="language-python">
@step("I add a comment")
def add_comment(context):
    for row in context.table:
        print(row["text"])
</code></pre>

<h2>Environment, context, tags y hooks</h2>
<p>Podemos controlar el entorno a través del fichero <code>environment.py</code> que tiene que estar en la carpeta features. En este fichero podemos definir hooks, que son funciones que se ejecutan antes o después de un determinado paso/escenario/feature. Pueden servirnos para realizar tareas de limpieza o de setup.</p>
<pre><code class="language-python">
class WebClient:
    def __init__(self):
        pass
    def stop(self):
        pass

def before_all(context):
    context.client = WebClient()

def after_all(context):
    context.client.stop()
</code></pre>
<p>Los hooks disponibles son before_{all, step, scenario, feature} y after_{all, step, scenario, feature}.</p>
<p>Tanto en los hooks como en los steps vemos la variable context siempre. Se trata de una variable controlada por Behave que puede servirnos para pasar información entre los steps y los hooks. Podemos crear por ejemplo, un cliente web con un hook y luego acceder a él en el step gracias a la variable context. O entre distintos steps, pasar información más precisa que no se define en el fichero con Gherkin. Además, context tiene algunas variables predefinidas, como los tags.</p>
<p>Los tags son anotaciones que se ponen sobre los steps, escenarios o features. Si bien en general son texto libre, algunos tienen efecto sobre Behave</p>
<pre><code>
Feature: comments

@data-1
Scenario: add a comment in blog post
Given a post page is loaded
When I add a comment 
Then the page is reloaded
And the page shows a confirmation message
And the comment is registered in the database

@wip
Scenario: approve comment
Given the comment is registered in the database
And I can't see the comment in the post page
When I approve the comment
Then I can see the comment in the post page
</code></pre>
<p>En el primer caso de tag (data-1), es algo que me he inventado, en un hook podemos comprobar los tags y actuar en consecuencia. En este caso, este tag quiere decir que para este escenario carguemos en la base de datos el conjunto de prueba 1.</p>
<pre><code class="language-python">
def before_scenario(context, scenario):
    if "data-1" in context.tags:
        # Load Data-1
        pass
</code></pre>
<p>En el segundo caso, el tag wip es reconocido por Behave y sirve para marcar tests que estamos probando. Estos los podemos ejecutar por separado indicando <code>-w</code>  al llamar a Behave.</p>
<pre><code>
behave -w
</code></pre>
<p>¡Con esto ya tenemos lo suficiente para ir desarrollando tests al estilo BDD! Por supuesto, existen frameworks similares en otros lenguajes, como Cucumber en Java, pero dependiendo de los test que queramos hacer no será necesario usar el mismo lenguaje que la propia aplicación (podemos usarlo para testear desde fuera, por ejemplo, a través una API REST o desde una web).</p>

]]></description>
                <comments>https://blog.adrianistan.eu/testing-lenguaje-natural-behave-python</comments>
                <pubDate>Thu, 20 Feb 2020 17:05:26 +0000</pubDate>
            </item>
        
            <item>
                <title>Teletexto #003</title>
                <link>https://blog.adrianistan.eu/teletexto-003</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/teletexto-003</guid>
                <description><![CDATA[<p>Esta semana he estado en el evento <a href="https://wecodefest.com/">WeCode</a> pero aun así no podía olvidarme de vosotros, y he aquí un listado de enlaces interesantes como viene siendo habitual en Teletexto.</p>
<div style="text-align: center">
<img src="https://files.adrianistan.eu/MapaValladolid.jpg" width="500">
<br>
<i>Mapa de Valladolid en 1943</i>
</div>
<ul>
<li><a href="https://blog.discordapp.com/why-discord-is-switching-from-go-to-rust-a190bbca2b1f">Discord se mueve de Go a Rust</a> (o mejor dicho, un servicio de los muchos que tienen) y en el artículo explican los motivos.</li>
<li>CSS es un lenguaje al que mucha gente le tiene un gran odio, ¿por qué es tan complicado se preguntan? Este artículo <a href="https://eev.ee/blog/2020/02/01/old-css-new-css/">Old CSS, New CSS</a> explica con bastantes detalles la historia de CSS y por qué se fueron tomando las decisiones que se tomaron hasta llegar a hoy día, donde CSS es una herramienta muy conveniente para casi todo.</li>
<li>Hablando de navegadores web, a partir de ahora, Microsoft Edge usará el mismo motor que Chrome. Eso supone el fin de la era de los motores Trident (y sus spin-off Tasman y EdgeHTML). En <a href="https://schepp.dev/posts/today-the-trident-era-ends/">Hoy la era Trident acaba</a> hacen un repaso por algunas de las tecnologías más innovadoras que tenía Trident. He de decir que estoy muy sorprendido por la potencia de muchas de sus propuestas, aunque luego no llegasen a ser usadas masivamente.</li>
<li>¿Te aburre tu música habitual? Date una vuelta por <a href="http://forgotify.com">Forgotify</a> y escucha las canciones más olvidadas dentro de la inmensidad del catálogo de Spotify.</li>
<li>Aquí servidor usa GNOME. Aunque en general me siento cómodo, no podría estar más de acuerdo con las mejoras que <a href="https://jatan.blog/2020/02/08/gnome-3-34-is-awesome-but-still-needs-improvements-in-key-areas/">propone Jattan</a></li>
<li>¿Os gusta escribir bien? Para escribir en inglés existe una famosísima extensión llamada Grammarly, pero para una lengua con tantos hablantes como el español no había nada. Hasta ahora, <a href="https://lorcaeditor.com/">LorcaEditor</a> es contraparte hispanohablante, y aunque es mucho más básico y sencillo, estoy seguro de que mejorará.</li>
<li>Ha salido Pandas 1.0, la librería de tratamiento de datos más popular para Python. <a href="https://pandas.pydata.org/pandas-docs/stable/whatsnew/v1.0.0.html">Aquí el changelog oficial</a> y <a href="https://towardsdatascience.com/whats-new-in-pandas-1-0-ffa99bd43a58">aquí un artículo explicativo</a>.</li>
<li>Durante la Segunda Guerra Mundial, EEUU sospechaba que España podía unirse a la contienda en cualquier momento, por eso realizó mapas de las principales ciudades por si era necesario atacarlas. Estos mapas, realizados en 1943, son una maravilla para explorar las ciudades en aquella época. <a href="https://legacy.lib.utexas.edu/maps/ams/spain_city_plans/">Aquí el listado de mapas</a>.</li>
<li>La teoría de categorías se ha vuelto famosa, y <a href="https://elpais.com/elpais/2019/11/06/ciencia/1573042148_224789.html">en este artículo de El País</a>, hablan sobre qué es.</li>
<li>Haskell es un lenguaje nacido en 1994 (un año antes que Java) pero quiere seguir estando a la vanguardia de lenguajes de investigación y experimentación. En <a href="http://www.stephendiehl.com/posts/decade.html">Haskell problems for a new decade</a> hablan justamente de los retos que tiene Haskell para la nueva década.</li>
<li>¿El rectángulo cordobés? <a href="https://es.wikipedia.org/wiki/Rect%C3%A1ngulo_cordob%C3%A9s">El rectángulo cordobés</a></li>
<li>¿Ruby es lento? Probablemente, pero <a href="https://m.signalvnoise.com/only-15-of-the-basecamp-operations-budget-is-spent-on-ruby/">solo representa el 15% de los costes operaciones de Basecamp</a>. Si quisieras optimizar estos costes, deberías apuntar a otros problemas antes.</li>
<li>Schema.org, una de las ontologías más populares ha lanzado la versión 6.0, <a href="https://schema.org/docs/releases.html#v6.0">cambios aquí.</a></li>
<li>¿Alguna vez has tenido curiosidad por Smalltalk? <a href="https://www.youtube.com/watch?v=HOuZyOKa91o">En este vídeo de 7 minutos</a>, se ve un flujo de trabajo con Smalltalk para no iniciados.</li>
<li>Google domina el mercado de las búsquedas, con Bing, Cliqz, Yandex y Baidu como motores minoritarios o regionales. ¿Qué pasa si queremos escapas de esto? Una solución muy interesante es <a href="https://www.yacy.net/">YaCy</a>, un motor de búsqueda descentralizado, que funciona gracias a la capacidad de cómputo combinada de toda la red.</li>
<li>Un podcast, en concreto <a href="https://www.ivoox.com/undefined-v4-testing-audios-mp3_rf_41970521_1.html">Undefined hablando de testing</a>.</li>
</ul>
<p>La canción de este Teletexto es instrumental pero un maravillosa</p>
<div style="text-align: center">
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/0yqfTXT0Dl0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
]]></description>
                <comments>https://blog.adrianistan.eu/teletexto-003</comments>
                <pubDate>Sun, 09 Feb 2020 12:16:48 +0000</pubDate>
            </item>
        
            <item>
                <title>Desplegar una página estática con GitHub Actions y Netlify</title>
                <link>https://blog.adrianistan.eu/desplegar-web-estatica-github-netlify</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/desplegar-web-estatica-github-netlify</guid>
                <description><![CDATA[<p>Hace unos días desplegué una nueva versión de mi página de inicio: <a href="https://adrianistan.eu">https://adrianistan.eu</a>. Se trata de un sitio estática, hecha con Lektor, alojado en Netlify y que gracias a GitHub Actions se publica una nueva versión cada vez que hay un commit nuevo. En este post explicaré como construí el despliegue continuo para que tú también puedas hacerlo.</p>

<h2>Herramientas</h2>

<p>El sitio web lo construí con <a href="https://www.getlektor.com/">Lektor</a>, un generador de sitios estáticos hecho en Python por el <a href="https://lucumr.pocoo.org/">creador de Flask</a>, que cuenta con un panel de administración estilo WordPress para poder editarlo. Es de los mejores generadores estáticos que he probado, ya que personalmente se ajusta a muchas de mis preferencias personales, más que Hugo o Jekyll.</p>

<div style="text-align:center">
<img src="https://files.adrianistan.eu/LektorEdit.png" width="600">
</div>

<p>Usaré <a href="https://www.netlify.com/">Netlify</a> para alojar mi paǵina, es un servicio de una calidad excelente, con una capa gratuita muy generosa. Otras alternativas eran Amazon S3, Firebase Hosting, Zeit, GitHub Pages, GitLab, ... pero las descarté porque además ya uso Netlify en otros proyectos.</p>

<p>Para alojar el código usaré GitHub, mi opción por defecto, y usaré el nuevo servicio de GitHub Actions para realizar la compilación del sitio y subirlo a Netlify</p>

<p>Entre medias usaré Docker para poder probar en mi ordenador comportamientos muy similares a los que obtendremos en GitHub Actions.</p>

<h2>Netlify</h2>

<p>De la página de Netlify necesitamos dos variables: el Site ID y un token para poder desplegar. El token lo podemos generar en los ajustes de usuario, en la sección de Personal Access Tokens.</p>

<div style="text-align:center">
<img src="https://files.adrianistan.eu/NetlifyPAT.png" width="600">
</div>

<p>Este token lo almacenamos en GitHub, yendo a Ajustes y desde allí a la sección de Secretos. Lo llamamos NETLIFY_AUTH_TOKEN.</p>

<div style="text-align:center">
<img src="https://files.adrianistan.eu/GitHubSecret.png" width="600">
</div>

<p>Para obtener el Site ID, necesitamos crear un sitio en Netlify (sube cualquier cosa para que se cree) y copia el Site ID desde la pantalla de ajustes generales. Estos Site ID son públicos, así que los podemos dejar en el código fuente.</p>

<div style="text-align:center">
<img src="https://files.adrianistan.eu/NetlifySiteID.png" width="600">
</div>

<p>Ya hemos acabado con Netlify. Desde Netlify también podemos configurar las DNS y HTTPS pero eso es algo sencillo, que veréis en los ajustes como hacer.</p>

<h2>Docker</h2>
<p>Vamos a crear una imagen con el entorno de ejecución que tiene todo lo necesario para construir y publicar la página. Usaremos docker-compose para definir dos comandos: los dos operando sobre la carpeta del proyecto, builder y publisher. El fichero Dockerfile es el siguiente:</p>

<pre><code>
FROM debian:buster

RUN apt-get update && apt-get install -y python3.7-dev python3-pip nodejs npm && \
    rm -rf /var/lib/apt/lists/*

COPY requirements.txt /tmp/requirements.txt
RUN pip3 install -r /tmp/requirements.txt
RUN npm install -g netlify-cli@2.30.0
WORKDIR /opt/adrianistan.eu
</code></pre>
<p>Partimos de Debian Buster e instalamos Python 3.7 y Node.js (que en Buster es la versión 10, suficiente por ahora). Para instalar los paquetes de Python usamos un fichero requirements.txt usado por Pip</p>
<pre><code>
Lektor==3.1.3
</code></pre>
<p>Creamos además un fichero docker-compose.yml para guardar los comandos.</p>
<pre><code>
version: "3.6"
services:
  builder:
    build: .
    command: lektor build -O out
    volumes:
    - ./:/opt/adrianistan.eu
  publisher:
    build: .
    command: netlify deploy --dir=out --prod
    environment: 
      NETLIFY_AUTH_TOKEN:
      NETLIFY_SITE_ID: 76f0a0da-c560-431e-a5c2-743809bf96e0
    volumes:
    - ./:/opt/adrianistan.eu
</code></pre>
<p>Aquí es importante el tema de las variables de entorno. NETLIFY_AUTH_TOKEN hay que dejarlo vacío para que Docker coja la variable del exterior, mientras que NETLIFY_SITE_ID lo podemos poner ya que es un dato público. Los dos comandos operan sobre la misma imagen y sobre los mismos volúmenes, que es la carpeta sobre la que está.</p>
<p>Podemos probar que funciona de forma sencilla:</p>
<pre><code>
docker-compose build
docker-compose run builder
NETLIFY_AUTH_TOKEN=XXXXXXX docker-compose run publisher
</code></pre>
<p>Y debería desplegar correctamente la página sin pedirnos nada</p>

<h2>GitHub Actions</h2>
<p>Para crear una acción en GitHub necesitamos crear un archivo en la carpeta .github/workflows, de tipo YAML. Las GitHub Actions definen unos parámetros para saber cuando se inician y uno o más jobs. Cada job puede tener su vez varios steps. Los jobs son paralelos, los steps son secuenciales. Cada job define además una imagen base, nosotros usaremos Ubuntu 18.04, pero nos da un poco igual mientras tenga Docker.</p>
<pre><code>
name: Deploy
on: [push]
jobs:
  build:
    runs-on: ubuntu-18.04
    steps:
    - name: Checkout
      uses: actions/checkout@v1
    - name: Build Docker images
      run: docker-compose build
    - name: Build website
      run: docker-compose run builder
    - name: Deploy website to Netlify
      run: docker-compose run publisher
      env:
        NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
</code></pre>
<p>El primer paso es obtener el código, después repetimos los mismos comandos que en local, debidamente separados en steps diferentes. Para definir variables de entorno usamos env y para acceder a los secretos que almacena GitHub accedemos al objeto global secrets.</p>
<p>Y ya estaría. Ahora cada vez que subamos un cambio, GitHub arrancará la acción, que desplegará en Netlify una nueva versión de la página</p>

<div style="text-align:center">
<img src="https://files.adrianistan.eu/GitHubAction.png" width="600">
</div>

<p>Lo bueno de este sistema es que es muy personalizable. Si no usas Lektor y usas Hugo, Jekyll o Gatsby, puedes cambiar simplemente la imagen Docker y los comandos en docker-compose y seguirá funcionando. Incluso puedes hacer virguerías mucho mayores. También hay gente que en vez de usar Docker usa Nix con el mismo propósito: tener todo definido en archivos comunes compartidos entre desarrollo y GitHub Actions. Sin embargo, Nix me daba muchos problemas para instalar el paquete npm de Netlify y en general Nix no viene instalado en las imágenes de GitHub Actions, así que sería un paso extra.</p>
<p>Hay gente que dirá que para qué usar Docker si puedes instalarlo con comandos similares directamente en el fichero de workflow de GitHub Actions. Y la diferencia es que tu sistema y GitHub Actions van a tener comportamientos más separados. Puede que nunca falle, pero puede que sí, ya que estás trabajando con dos entornos diferentes y que pueden evolucionar por caminos diferentes. Por eso recomiendo usar Docker o Nix o algo que nos permita tener entornos iguales tanto en el desarrollo como el despliegue.</p>
<p>Un paso extra que podríamos añadir sería subir la imagen de Docker a un registro, y usar esa imagen en nuestro docker-compose.yml, en vez de generarla cada vez. De este modo sí que sí, todas nuestras nuestras builds usasen el mismo exacto entorno, inmutable, y si quisiéramos cambiarlo deberíamos generar una nueva imagen y subirla al registro de nuevo</p>]]></description>
                <comments>https://blog.adrianistan.eu/desplegar-web-estatica-github-netlify</comments>
                <pubDate>Fri, 31 Jan 2020 20:15:40 +0000</pubDate>
            </item>
        
            <item>
                <title>Primeros pasos con Nix: un Linux más funcional</title>
                <link>https://blog.adrianistan.eu/primeros-pasos-nix-linux-funcional</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/primeros-pasos-nix-linux-funcional</guid>
                <description><![CDATA[<p>Como ya sabéis, me encanta experimentar cosas alternativas dentro de la informática. Cosas que quizá no sean populares ni vayan a serlo, pero aportan un punto de vista diferente. Es por eso que en este blog se habla de <a href="https://blog.adrianistan.eu/programacion-web-prolog">Prolog</a>, hablamos de <a href="https://blog.adrianistan.eu/que-paso-con-web-semantica">web semántica</a>, <a href="https://blog.adrianistan.eu/rust-101-tutorial-rust-espanol">de Rust</a> y <a href="https://blog.adrianistan.eu/programando-haiku-bapplication-bwindow-bbutton-parte-i">se habla de Haiku</a>. Porque para ver el típico framework hecho en Java ya tienes cientos de blogs por ahí. En este blog intento hablar de cosas de las que poca gente habla. Hoy nos toca hablar de <a href="https://nixos.org/">Nix</a>, un gestor de paquetes funcional, y base de NixOS, una distribución Linux muy interesante. Sin embargo aquí solo nos centraremos en Nix.</p>

<div style="text-align:center">
<img src="https://files.adrianistan.eu/Nix7.png" width="300">
</div>

<p>Nix es un gestor de paquetes, al igual que pacman en Arch Linux, apt en Debian o yum en RedHat. Si solo fuese un sistema más, no tendría interés, no solo hay estos tres, hay muchos más. Pero Nix ofrece algo diferente. Nix es funcional. Nix no mantiene un estado global del sistema y nos permite describir entornos de forma declarativa. Nix permite hacer rollbacks instantáneos (incluso varias versiones). Nix es además un lenguaje de programación, similar a Haskell (perezoso, puro, con currificación, pero con tipado dinámico). Nix permite tener instaladas a la vez varias versiones de un mismo paquete, sin provocar conflictos. En la gran mayoría de sistemas de paquetes, el funcionamiento es sencillo: se desempaqueta, reemplazando los ficheros si los hubiese, dentro del sistema de carpetas global. En Nix, se gestiona un PATH de forma dinámica que nos permite acceder a los paquetes residentes en /nix. Pero no nos adelantemos, vayamos poco a poco.</p>


<h2>Instalando Nix</h2>
<p>Hay dos formas de instalar Nix. Una es instalarse NixOS, la distro diseñada para trabajar con Nix de forma nativa. Pero también podemos instalar Nix en cualquier otra distro Linux y en macOS. Para ello deberemos ejecutar el siguiente comando:</p>
<pre><code>
curl https://nixos.org/nix/install | sh
</code></pre>
<p>Esto nos instalará Nix para nuestro usuario, aunque necesitará permisos de root. Por defecto crea un perfil. En Nix podemos tener infinitos perfiles. Cada perfil es un entorno, con paquetes diferentes que no interfieren entre sí. Podemos pensar en ello como si fuesen contenedores o máquinas virtuales, pero en realidad son simplemente colecciones de enlaces simbólicos al Nix Store, la carpeta donde se guardan todos los paquetes (por defecto /nix/store). Cada perfil carga ciertos programas en el PATH, así como sus librerías.</p>

<p>Para cargar un perfil y usarlo desde Bash/Zsh tendremos que ejecutar el script nix.sh correspondiente:</p>
<pre><code>
source ~/.nix-profile/etc/profile.d/nix.sh
</code></pre>
<p>También lo podemos añadir a nuestro fichero .bashrc o equivalente para que no tengamos que hacerlo manualmente</p>.
<h2>Instalar, borrar y actualizar paquetes</h2>
<p>El comando principal para manipular los paquetes dentro de los perfiles es <b>nix-env</b>. Para ver los paquetes que tenemos disponibles para instalar ejecutamos:
</p>
<pre><code>
nix-env -qa
</code></pre>
<p>Para acordarse mejor, qa significa "query all".
Si queremos ver los paquetes que tenemos instalados en el perfil, podemos ejecutar solamente</p>
<pre><code>
nix-env -q
</code></pre>
<p>Que es simplemente query. Para filtrar los paquetes podemos usar expresiones regulares estándar. Por ejemplo:</p>
<pre><code>
nix-env -qa "firefox.*"
</code></pre>
<p>Busca todos los paquetes disponibles que empiezen por <i>firefox</i>. Para instalar paquetes tenemos el flag i, de install. Vamos a instalar cowsay.</p>
<pre><code>
nix-env -i cowsay
</code></pre>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/NixCowsay.png">
</div>
<p>
Al instalar descarga el paquete y sus dependencias si no existiesen ya, las deja en el Nix Store y genera los enlaces simbólicos para que sean accesibles desde el perfil actual. Aquí pueden surgir varias dudas.</p>

<p><b>¿Si ya existe un paquete con otra versión que ocurre?</b> Nix es quizá el gestor de paquetes más inteligentes al respecto. Si tienes ya descargada la misma versión exacta debido a otro paquete, se reutiliza, aprovechando el espacio. Sin embargo, Nix también quiere garantizar el funcionamiento de los programas y el determinismo de cuando se construyó el paquete, así que cuando las versiones divergen, se instalan ambas en paralelo. ¡Y ambas versiones de la librería funcionan a la vez en el mismo perfil! Esto se consigue gracias a la forma en la que se construyen los paquetes, que veremos más adelante.
</p>
<p><b>¿Qué es el fichero drv?</b> Es el fichero que almacena una derivación. Cuando realizamos modificaciones de los paquetes en Nix, estamos haciendo derivaciones. Esto nos facilita poder revertir los cambios y hace muy difícil que Nix se corrompa.</p>
<p><b>¿De dónde se descargan los paquetes?</b> Los repositorios en Nix se llaman <b>channels</b>. Por defecto se usa nixpkg-unstable como repositorio, que es de tipo rolling release, disponiendo de los últimos paquetes siempre. Existen otros, no obstante, principalmente usados por NixOS. Los canales son en principio repositorios de código Nix, que dicen como compilar los paquetes. En la mayoría de los paquetes existen ya versiones compiladas, pero si no las hubiera la instalación procedería a compilar de cero el paquete.</p>
<p>Vamos a usar el programa</p>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/NixStegosaurus.png">
</div>
<p>Ahora vamos a quitar el programa. Podemos hacer dos cosas. Podemos hacer rollback y restaurar la versión anterior del perfil o podemos realizar una operación de borrado (que genera una nueva derivación).</p>
<p>Para borrar, generando una nueva derivación:</p>
<pre><code>
nix-env -e cowsay
</code></pre>
<p>Para hacer rollback, podemos ejecutar:</p>
<pre><code>
nix-env --rollback
</code></pre>
<p>Que nos sirve para ir a la versión inmediatamente anterior. También podemos listar las generaciones y saltar a una de ellas</p>
<pre><code>
nix-env --list-generations
nix-env -G NUMERO_GENERATION
</code></pre>
<p>Cuando borramos o actualizamos un paquete, no se elimina nada, todo se mantiene, de esta forma podemos hacer rollbacks instantáneos. Pero una vez ya estamos seguros de que no necesitamos ese paquete por si acaso, podemos limpiar las generaciones antiguas donde se usaba. Para ello debemos ejecutar periódicamente el siguiente comando:</p>
<pre><code>
nix-collect-garbage
nix-collect-garbage -d
</code></pre>
<p>La segunda opción es más agresiva y perderemos la habilidad absoluta de hacer rollback, dejándonos solo con la última generación.</p>
<p>Para actualizar los paquetes usamos este comando:</p>
<pre><code>
nix-env -u
</code></pre>
<h2>Gestionando canales</h2>
<p>Los repositorios en Nix se llaman canales. Todas las operaciones con respecto a los canales se realizan con el comando nix-channel. Podemos ver los canales que tenemos con:</p>
<pre><code>
nix-channel --list
</code></pre>
<p>El canal nixpkgs-unstable es el que podemos encontrar en GitHub: <a href="https://github.com/NixOS/nixpkgs">https://github.com/NixOS/nixpkgs/</a>.</p>
<p>Podemos actualizar los canales con update: </p>
<pre><code>
nix-channel --update
</code></pre>
<p>Para añadir un canal usamos add</p>
<pre><code>
nix-channel --add https://nixos.org/channels/channel-name nixos 
</code></pre>
<h2>El lenguaje Nix</h2>
<p>Nix es realmente un lenguaje de programación funcional, especialmente diseñado para manejar la paquetería del sistema, pero completo. Es similar a Haskell, aunque sin tipado estático. No voy a entrar en muchos detalles de este lenguaje, pero vamos a ver algunas cosillas que serán útiles para construir paquetes y manejar ficheros .nix</p>
<p>Podemos acceder a un REPL de Nix escribiendo</p>
<pre><code>
nix repl
</code></pre>
<p>En Nix todo son expresiones, que se evalúan de forma perezosa como en Haskell. La artimética es sencilla, con la única diferencia de que hay que hay que tener cuidado con la división. El símbolo / significa ruta de archivo, no división. Hay que usar la función builtins.div para dividir.</p>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/NixArithmetic.png">
</div>
<p>También existen listas, strings y attribute sets, que son como diccionarios. Para asignar variables usamos la sintaxis let/in, que también existe en Haskell. Básicamente, on let definimos las variables y en in la expresión que las usa.</p>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/NixList.png">
</div>
<p>Para acceder a los atributos de los attribute sets podemos usar la sintaxis punto o usar with para no tener que escribir. Así, estas dos líneas son equivalentes.</p>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/NixWith.png">
</div>
<p>En Nix también hay sentencias if, pero al ser expresiones, siempre deben retornar un valor, el else es obligatorio siempre</p>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/NixIf.png">
</div>
<p>Las funciones en Nix son anónimas, pero podemos almacenarlas en variables. Admiten varios argumentos, aunque si vienes de Haskell, que sepas que Nix también realiza currificación. Las funciones de Nix también admiten parámetros que sean un attribute set, y los puede descomponer directamente. Además esto nos permite definir argumentos opcionales.</p>
<div style="text-align:center">
<img src="https://files.adrianistan.eu/NixFunctions.png">
</div>
<p>Lo último que debemos saber del lenguaje es el uso de import, que sirve para cargar la expresión resultado de un archivo en otro</p>
<h2>Creando paquetes</h2>
<p>Una vez conocemos el lenguaje Nix, no es evidente como se crean paquetes. Existen dos formas básicas de crear paquetes: in-tree y out-tree. Los paquetes in-tree son los que componen nixpkgs, y básicamente cada paquete se define por una función que es llamada desde un punto de entrada principal, que es el índice de paquetes. Si queremos subir un paquete a nixpkgs deberemos hacerlos así. Sin embargo, si queremos empaquetar algo que no queremos que llegue a nixpkgs debemos hacerlo de forma que sea independiente, aunque usando algunas funciones de nixpkgs.</p>
<p>Lo primero que vamos a hacer es importar nixpkgs, para poder usar sus utilidades. La mayoría de paquetes se beneficiarán de las funciones prediseñadas. Stdenv tiene funcionalidad genérica, pero útil para programas en C/C++. Para otros lenguajes podemos usar este mismo módulo u otros más específicos (de Rust, de Haskell, ...). La expresión es una llamada a mkDerivation, con ciertos datos, entre ellos un builder, que será un script que construirá la aplicación, el origen de los datos (función fetchurl normalmente) y algunas dependencias, con buildInputs. Podemos pasar cualquier variable de Nix a Bash, simplemente añadiendo más variables a mkDerivation.</p>

<pre><code>
let 
    nixpkgs = import <nixpkgs> {} ;
in
nixpkgs.stdenv.mkDerivation {
    name = "pcc-1.0.0" ;
    builder = ./builder.sh ;
    src = nixpkgs.fetchurl {
        url = https://github.com/aarroyoc/pcc/archive/3f90d424494f4d1971ea34e66883fdee8a587b1f.zip;
        sha256 = "ec80f0c8af5dc9d6f0fbb691a4132ac8d44e42dd05865e23c80c2e0f0219d56f";
    };
    buildInputs = [ nixpkgs.unzip nixpkgs.bison nixpkgs.flex];
}
</code></pre>
<p>o si usamos with para simplificar</p>
<pre><code>
with import <nixpkgs> {};

stdenv.mkDerivation {
  name = "pcc-1.0.0";
  builder = ./builder.sh;
  src = fetchurl {
    url = https://github.com/aarroyoc/pcc/archive/3f90d424494f4d1971ea34e66883fdee8a587b1f.zip;
    sha256 = "ec80f0c8af5dc9d6f0fbb691a4132ac8d44e42dd05865e23c80c2e0f0219d56f";
  };
  buildInputs = [ unzip bison flex ];
}
</code></pre>
<p>Y el builder.sh contiene:</p>
<pre><code>
source $stdenv/setup

unzip $src
cd pcc-*
make
mkdir -p $out/bin
cp pcc $out/bin/
</code></pre>
<p>Dentro del script hay que cumplir varias normas. La primera línea, si usamos stdenv, debe ser llamar al setup. La variable src es la que hemos definido como resultado de fetchurl y la variable out representa la estructura que va a tener el paquete.</p>
<p>Con Nix podemos importar otros paquetes como base y personalizarlos obteniendo así paquetes personalizados de forma sencilla y reproducible.</p>
<p>Podemos compilar el paquete con nix-build</p>
<pre><code>
nix-build pcc.nix
</code></pre>
<p>E instalarlo</p>
<pre><code>
nix-env -f pcc.nix -i pcc
</code></pre>
<p>Lo que hacemos con nix-env es indicar que use otro archivo nix en vez de nixpkgs y de él, instale pcc.</p>
<h2>Construyendo entornos</h2>
<p>Con Nix también podemos construir entornos que tengan ciertos paquetes cargados, ideal para documentar el software exacto necesario para trabajar y desplegar programas. Para ello usaremos mkShell</p>
<pre><code>
let
  pkgs = import <nixpkgs> {};
in

pkgs.mkShell {
    name = "python-datascience";
    buildInputs = with pkgs; [
        python38
        python38Packages.numpy
        python38Packages.scikitlearn
        python38Packages.scipy
        python38Packages.matplotlib
    ];
}
</code></pre>
<p>Y lo cargamos</p>
<pre><code>
nix-shell datascience.nix
</code></pre>
<p>Y ya tendríamos un entorno para ejecutar nuestros scripts de NumPy, SciPy y Sklearn. Estos shells se construyen como si fuesen paquetes y también se pueden distribuir. Si faltase algún paquete de Python, podríamos añadirlo fácilmente a Nix. Por ejemplo, suponiendo que no tuviésemos paquete para <a href="http://python-requests.org/">requests</a>, usaríamos las funciones de Nix para Python</p>
<pre><code>
let
  pkgs = import <nixpkgs> {};
  requests = pkgs.python38.pkgs.buildPythonPackage rec {
      pname = "requests";
      version = "2.22.0";
      src = pkgs.python38.pkgs.fetchPypi {
          inherit pname version;
          sha256 = "1d5ybh11jr5sm7xp6mz8fyc7vrp4syifds91m7sj60xalal0gq0i";
      };
      doCheck = false;
      buildInputs = with pkgs; [
          python38
          python38Packages.chardet
          python38Packages.idna
          python38Packages.urllib3
      ];
  };
in

pkgs.mkShell {
    name = "python-datascience";
    buildInputs = with pkgs; [
        python38
        python38Packages.numpy
        python38Packages.scikitlearn
        python38Packages.scipy
        python38Packages.matplotlib
        requests
    ];
}
</code></pre>

<h2>Conclusión</h2>
<p>Nix es un gestor de paquetes muy potente, ofrece mejoras respecto a apt, yum, dnf, etc y se acerca mucho a Docker y otros sistemas modernos. Es puramente funcional, que se refleja en una curva de aprendizaje inicial más elevada. A priori no parece que ninguna gran distro que quiera pasarse a Nix, pero se puede instalar de forma paralela en tu Linux o Mac. Si quieres ir un paso más allá, NixOS utiliza el lenguaje Nix para todavía más partes de la administración del sistema y existen proyectos como <a href="https://github.com/rycee/home-manager">home-manager</a> que te permiten configurar entornos completos en Nix de una forma más extensa que solo decidir los paquetes que va a disponer.</p>
]]></description>
                <comments>https://blog.adrianistan.eu/primeros-pasos-nix-linux-funcional</comments>
                <pubDate>Sat, 25 Jan 2020 19:36:28 +0000</pubDate>
            </item>
        
            <item>
                <title>Teletexto #002</title>
                <link>https://blog.adrianistan.eu/teletexto-002</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/teletexto-002</guid>
                <description><![CDATA[<p>Bienaventurados todos aquellos lectores del&nbsp;<em>Teletexto</em>. En esta segunda edici&oacute;n voy a seguir repasando algunos proyectos, art&iacute;culos y noticias que me parecen de inter&eacute;s.</p>
<p>&nbsp;</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://files.adrianistan.eu/TeletextoUcrania.png" alt="" /></p>
<ul>
<li>Esta semana ha sido triste para el mundo Rust. La librer&iacute;a actix-web, una de las m&aacute;s usadas para construir aplicaciones web fue declarada muerta y retirada de forma repentina por su creador, cansado de cientos de cr&iacute;ticas hacia su librer&iacute;a que constru&iacute;a en sus ratos libres. Reflexiona sobre ello Steve Klabnik en <a href="https://words.steveklabnik.com/a-sad-day-for-rust">A sad day for Rust</a>.</li>
<li>&iquest;No sabes que es DevOps? &iquest;Quieres informarte un poco sobre el tema? En el <a href="https://www.ivoox.com/undefined-v8-devops-audios-mp3_rf_46590275_1.html">podcast n&uacute;mero 8 de Undefined</a> hablamos de DevOps (s&iacute;, yo incluido)</li>
<li>Las falacias son las trampas en el mundo de la l&oacute;gica. Parecen argumentos convincentes pero no lo son. En <a href="https://falacias.escepticos.es/">https://falacias.escepticos.es/</a> tienen un listado muy completo de estas falacias, explicadas de forma gr&aacute;fica, con ejemplos y como rebatirlas.</li>
</ul>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://files.adrianistan.eu/Falacias.gif" alt="" /></p>
<ul>
<li>&iquest;Son los SMS de confirmaci&oacute;n seguros? Aunque muchas webs (en especial bancos) hayan pasado a usar estos servicios para conseguir un 2FA, hay muchos motivos para pensar que no son seguros. En <a href="https://www.issms2fasecure.com/">https://www.issms2fasecure.com/</a> exponen los resultados de una investigaci&oacute;n que confirm&oacute; que los SMS apenas supon&iacute;an un problema para acceder a los sistemas.</li>
<li>&iquest;Os acord&aacute;is del <a href="../../agromapa-castilla-leon">Agromapa de Castilla y Le&oacute;n</a>? Pues hace ya un tiempo, datos.gob.es, el portal nacional, incluy&oacute; esta aplicaci&oacute;n en su cat&aacute;logo: <a href="https://datos.gob.es/es/aplicaciones/agromapa-de-castilla-y-leon">https://datos.gob.es/es/aplicaciones/agromapa-de-castilla-y-leon</a></li>
<li>&iquest;Deber&iacute;an llevar los blogs un apartado para comentarios? Aunque parece una caracter&iacute;stica imprescindible, hay gente que piensa que no, que los blogs son estrictamente personales y si alguien quiere responderte, pueden hacerlo tambi&eacute;n en su propio blog. <a href="https://chrisdone.com/posts/blog-comments/">Reflexi&oacute;n de Chris Done</a>. Estad tranquilos, aunque me ha hecho meditarlo, en este blog se quedar&aacute;n los comentarios.</li>
<li>Hubo un tiempo en el que el mundo open source no sab&iacute;a que sistema de control de versiones usar. Lleg&oacute; a haber tres alternativas muy decentes para intentar sustituir a <a href="https://subversion.apache.org/">Subversion</a>: <a href="https://www.mercurial-scm.org/">Mercurial</a>, <a href="https://git-scm.com/">Git</a> y <a href="http://bazaar.canonical.com/en/">Bazaar</a>. Finalmente Git&nbsp;<em>ha ganado</em> aunque Mercurial sigue vivo en sitios como Facebook o Mozilla. Bazaar fue un proyecto de Canonical, usado principalmente en entornos cercanos a Ubuntu, y desde 2016 no ha tenido ninguna nueva versi&oacute;n. Finalmente ha habido un fork, llamado <a href="https://www.breezy-vcs.org/">Breezy</a>, que combina lo mejor de Bazaar con el soporte a Git de forma transparente. Es adem&aacute;s la &uacute;nica forma de gestionar repositorios Bazaar con Python 3.</li>
<li>&iquest;C&oacute;mo salir de VIM? <a href="https://github.com/hakluke/how-to-exit-vim/blob/master/README.md">Solo respuestas incorrectas</a>.</li>
<li><a href="https://www.csc.gov.sg/articles/how-to-build-good-software">&iquest;C&oacute;mo construir buen software?</a> Un art&iacute;culo muy interesante sobre los errores que llevan al software a fracasar. Adoptar la mentalidad que propone sobre el software puede ser muy &uacute;til, que por otro lado, parece de perogrullo.</li>
<li>Ryan C Gordon es un desarrollador conocido entre otras cosas, por mantener la librer&iacute;a SDL y haber portado gran cantidad de juegos a Linux. Recibi&oacute; dinero de Google, pero no quiso aceptarlo ppr la pol&iacute;tica de esta empresa ante los sindicatos. Decidi&oacute; repartir su dinero (y el de gente que quisiera colaborar) en ayudar a proyectos peque&ntilde;os. Lo ha llamado <a href="https://icculus.org/microgrant2019/">Icculus Microgrant 2019</a>. Hay proyectos muy interesantes, aunque uno era muy conocido para m&iacute;, <a href="../../introduccion-a-prolog-tutorial-en-espanol">SWI Prolog</a>.</li>
<li>Si en el anterior Teletexto presentaba TerminusDB, en este presento <a href="https://dgraph.io/">Dgraph</a>, una base de datos de grafos (por tanto similar a Neo4J y tambi&eacute;n a TerminusDB). Usa un lenguaje de consulta similar a GraphQL.</li>
<li>Acabamos el Teletexto, con <a href="https://hakibenita.com/sql-dos-and-donts">12 consejos para escribir SQL libre de fallos y con m&aacute;s rendimiento</a>.</li>
<li>Finalmente la canci&oacute;n de este Teletexto es Canci&oacute;n Consumo de Luis Eduardo Aute:</li>
</ul>
<p style="text-align: center;"><iframe src="https://www.youtube-nocookie.com/embed/j_VnOTWTnZY" width="560" height="315" frameborder="0" allowfullscreen=""></iframe></p>
<p style="text-align: center;">&nbsp;</p>]]></description>
                <comments>https://blog.adrianistan.eu/teletexto-002</comments>
                <pubDate>Sun, 19 Jan 2020 20:12:38 +0000</pubDate>
            </item>
        
            <item>
                <title>Prolog DCG: gramáticas de clausula definida</title>
                <link>https://blog.adrianistan.eu/prolog-dcg-gramaticas-clausula-definida</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/prolog-dcg-gramaticas-clausula-definida</guid>
                <description><![CDATA[<blockquote>
<p><em>Hoy, 14 de enero, es el <a href="http://www.logica-universalis.org/wld2">WORLD LOGIC DAY</a>. Para celebrarlo, una entrada que ten&iacute;a ya escrita sobre nuestro lenguaje de programaci&oacute;n l&oacute;gico preferido: Prolog.</em></p>
</blockquote>
<p>Una de las caracterśiticas m&aacute;s potentes de <a href="../../introduccion-a-prolog-tutorial-en-espanol">Prolog</a> es su potencia para la manipulaci&oacute;n simb&oacute;lica. Uno de los primeros usos que se le dio fue la definici&oacute;n de gram&aacute;ticas a trav&eacute;s de cl&aacute;usulas. Estas definiciones constan de una sintaxis especial, aunque no es m&aacute;s que az&uacute;car sint&aacute;ctico sobre el lenguaje basado en predicados y t&eacute;rminos de siempre. Aqu&iacute; veremos la potencia de estas gram&aacute;ticas dentro de Prolog y los usos que se les puede llegar a dar, desde simplemente validar "listas" a construir compiladores de lenguajes de programaci&oacute;n.</p>
<p>Pero vamos a ir poco a poco desgranando todo esto.</p>
<h2>Gram&aacute;ticas</h2>
<p>En esencia un DCG es simple: es un conjunto de reglas que describen un tipo concreto de lista. Estas "reglas" en ciencias de la computaci&oacute;n tienen un nombre especial: gram&aacute;ticas. Las gram&aacute;ticas nos permiten generar lenguajes, entendiendo lenguajes como cualquier conjunto de s&iacute;mbolos, uno detr&aacute;s de otro (o sea, el espa&ntilde;ol, Python o una imagen digital). Las gram&aacute;ticas se dividen en varias categor&iacute;as, seg&uacute;n los lenguajes que son capaces de generar. Esta diferenciaci&oacute;n viene de <a href="https://es.wikipedia.org/wiki/Noam_Chomsky">Noam Chomsky</a>, que a parte de fil&oacute;sofo y activista, se le considera el padre de las gram&aacute;ticas.</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://files.adrianistan.eu/ChomskyHierarchy.png" alt="" width="300" />Las gram&aacute;ticas regulares son las que generan lenguajes regulares. Los lenguajes regulares son aquellos que podemos analizar mediante expresiones regulares. Las expresiones regulares o Regex no dejan de ser una manera de definir gram&aacute;ticas regulares. Los DCG de Prolog nos permiten llegar a las gram&aacute;ticas dependientes de contexto, el &uacute;ltimo paso antes de llegar a las recursivamente enumerables que son pr&aacute;cticamente intratables. Sin embargo, para no complicarlo mucho, vamos a hacer los ejemplos de este post con gram&aacute;ticas independientes de contexto. Otros programas capaces de llegar a este punto son el famoso programa de Unix Yacc (en GNU es <a href="https://www.gnu.org/software/bison/">Bison</a>) y el famoso <a href="https://www.antlr.org/">ANTLR</a>. Adem&aacute;s, pr&aacute;cticamente todos los lenguajes de programaci&oacute;n est&aacute;n definidos por gram&aacute;ticas independientes de contexto.</p>
<p>Una gram&aacute;tica muy simple ser&iacute;a por ejemplo, podr&iacute;a definir un lenguaje que es una cadena de x. Mientras el "c&oacute;digo" sea una sucesi&oacute;n de x, es parte del lenguaje, pero cualquier otra cosa es un error. Conceptualmente lo podr&iacute;amos definir as&iacute;, siendo S una variable y x un t&eacute;rmino:</p>
<pre><code>
S -&gt; .
S -&gt; xS
</code></pre>
<p>Donde . significa "nada" o "cadena vac&iacute;a". La gram&aacute;tica se define de forma recursiva. Dado una cadena de entrada S, se puede descomponer seg&uacute;n la primera regla en cadena vac&iacute;a o en un caracter x m&aacute;s S otra vez. Pongamos un ejemplo:</p>
<p>xx forma parte del lenguaje porque S = xx, podemos descomponerlo seg&uacute;n la segunda regla en x(S=x). Hemos aceptado de este modo el primer caracter y nos sigue quedando una parte por analizar. Como ha aparecido una S nueva, volvemos a aplicar la segunda regla y obtenemos xx(S=). No hemos acabado, nos sigue quedando una S, pero al aplicar la primera regla ya nos quedamos sin variables y la gram&aacute;tica ha aceptado la cadena. Por tanto xx forma parte del lenguaje.</p>
<p>Esto se puede complicar mucho, he aqu&iacute; una gram&aacute;tica algo m&aacute;s compleja:</p>
<pre><code>
Frase -&gt; SujetoVerbo
Sujeto -&gt; DeterminanteNombre
Determinante -&gt; el
Nombre -&gt; gato
Nombre -&gt; perro
Verbo -&gt; ladra
Verbo -&gt; maulla
</code></pre>
<p>No es dif&iacute;cil comprobar que frases como "elgatoladra" o "elperromaulla" son v&aacute;lidas, pero "lavacaladra" no.</p>
<h2>Vuelta a Prolog</h2>
<p>Volvamos a Prolog con nuestro lenguaje X. La gram&aacute;tica la podemos expresar con las DCG de esta forma:</p>
<pre><code>
program --&gt; [].
program --&gt; [x], program.
</code></pre>
<p>Podemos usar el predicado&nbsp;<strong>phrase</strong> para comprobar si una cadena pertenece al lenguaje X:</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://files.adrianistan.eu/PrologPhrase.png" alt="" /></p>
<p>As&iacute; xxxx forma parte del lenguaje y xxyx no forma parte. Prolog tiene la caracter&iacute;stica, de que si sobre algo no puede esclarecer si es cierto o falso, buscar&aacute; la forma de&nbsp;<strong>satisfacer</strong> las condiciones. Podemos usar esto para ir generando todos los lenguajes posibles.</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://files.adrianistan.eu/PrologXLang.png" alt="" /></p>
<h2>Parseando programas</h2>
<p>Podemos usar las capacidades de Prolog para a&ntilde;adir variables que nos permitan realizar un parseo y obtener as&iacute; una estructura de &aacute;rbol del documento.</p>
<p>Para este ejemplo, primero vamos a definir un lenguaje de calculadora, que permita de momento solamente sumar.</p>
<pre><code>
program --&gt; expr, oper, expr.
expr --&gt; ['('], expr, oper, expr, [')'].
expr --&gt; [N], {number(N)}.
oper --&gt; [+].
</code></pre>
<p>La sintaxis con llaves nos permite llamar a predicados auxiliares para construir una variable. Esta gram&aacute;tica admite expresiones de este tipo: 45 + ((70+45)+56).</p>
<p>Ahora para parsear, a&ntilde;adimos variables a las DCG para ir guardando un &aacute;rbol AST.</p>
<pre><code>
program(node(O, E1, E2)) --&gt; expr(E1), oper(O), expr(E2).
expr(node(O, E1, E2)) --&gt; ['('], expr(E1), oper(O), expr(E2), [')'].
expr(node(number, N)) --&gt; [N], {number(N)}.
oper(+) --&gt; [+].
</code></pre>
<p>Y vemos el resultado de parsear diferentes expresiones</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://files.adrianistan.eu/PrologAST.png" alt="" /></p>
<p>Y ahora realizar un compilador/int&eacute;rprete para este lenguaje es trivial.</p>
<pre><code>
program(node(O, E1, E2)) --&gt; expr(E1), oper(O), expr(E2).
expr(node(O, E1, E2)) --&gt; ['('], expr(E1), oper(O), expr(E2), [')'].
expr(node(number, N)) --&gt; [N], {number(N)}.
oper(+) --&gt; [+].

execute(node(number, Out), Out).
execute(node(+, E1, E2), Out) :-
    execute(E1, OutE1),
    execute(E2, OutE2),
    Out is OutE1 + OutE2.

execute(Program) :-
    phrase(program(Tree), Program),
    execute(Tree, Out),
    format(Out).
</code></pre>
<p>&iexcl;Y funciona correctamente!</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://files.adrianistan.eu/PrologSumLang.png" alt="" /></p>
<p>A&ntilde;adir el resto de operaciones (resta, multiplicaci&oacute;n, divisi&oacute;n) queda en manos del lector. Por supuesto, para ejecutar los programas todav&iacute;a tenemos que pasar una cadena de elementos, que en terminolog&iacute;a de lenguajes se llaman <strong>tokens</strong>, pero eso se podr&iacute;a hacer con otra gram&aacute;tica DCG o de otra forma dependiendo del lenguaje (en el mundo Unix cl&aacute;sico se suele hacer con Lex, cuya versi&oacute;n en Linux suele ser <a href="https://github.com/westes/flex">Flex</a>).</p>
<p>Como conclusi&oacute;n, hemos visto que la manipulaci&oacute;n de s&iacute;mbolos de Prolog es potente y nos permite describir de forma corta y elegante, as&iacute; como realizar un parseado de forma sencilla tambi&eacute;n.</p>]]></description>
                <comments>https://blog.adrianistan.eu/prolog-dcg-gramaticas-clausula-definida</comments>
                <pubDate>Tue, 14 Jan 2020 13:02:47 +0000</pubDate>
            </item>
        
            <item>
                <title>Teletexto #001</title>
                <link>https://blog.adrianistan.eu/teletexto-001</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/teletexto-001</guid>
                <description><![CDATA[<p>Bienvenidos a una nueva secci&oacute;n del a&ntilde;o 2020. Se trata de&nbsp;<strong>Teletexto</strong>, una secci&oacute;n donde recopilar&eacute; enlaces que considero interesantes junto con un peque&ntilde;o texto. La idea de esta secci&oacute;n viene de que tengo un Trello con ideas gigante. Muchas de ellas no las voy a tocar, pero me gustar&iacute;a mencionarlas en el blog, ya que me parecen cosas muy interesantes. As&iacute; pues, el contenido de esta secci&oacute;n ser&aacute; principalmente t&eacute;cnico, pero guiado por mi curiosidad y mi inter&eacute;s en las cosas que vaya viendo. Esta secci&oacute;n adem&aacute;s me servir&aacute; para poder publicar un poco m&aacute;s r&aacute;pido. Finalmente, se llama Teletexto, precisamente por ser una muy buena met&aacute;fora de lo que es: res&uacute;menes cortos de informaci&oacute;n y tecnolog&iacute;a algo ya viejuna, pero sumamente interesante. Y sin nada m&aacute;s que decir, empezamos la primera edici&oacute;n.</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://files.adrianistan.eu/Teletexto.jpg" alt="" /></p>
<ul>
<li><strong><a href="https://impurepics.com/">Impure Pics</a></strong>, esta curiosa p&aacute;gina es un conjunto de memes e im&aacute;genes explicativas sobre el mundo de la programaci&oacute;n funcional, Haskell, Scala, PureScript,... &iexcl;Incluso tenemos un quiz para saber que tipo de typeclass somos! Muy recomendable su visita aunque no sepas de programaci&oacute;n funcional.</li>
</ul>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://files.adrianistan.eu/HaskellImpurePics.jpeg" alt="" width="500" /></p>
<ul>
<li>&iquest;Alguna vez has pensado cu&aacute;ntas cosas pueden ir dentro de la etiqueta HEAD en HTML? <a href="https://htmlhead.dev/">https://htmlhead.dev/</a> tiene un listado muy completo de qu&eacute; puede ir. Por supuesto, eso no quiere decir que tu p&aacute;gina deba llevar todas ellas, pero siempre est&aacute; bien darle un repaso.</li>
<li>Hace poco descubr&iacute; que existe un deporte llamado&nbsp;<a href="http://combatemedieval.org/"><em>Combate Medieval</em></a>. Al parecer, trata de recrear de forma l&uacute;dica y competitiva, el combate de la edad Media (pero sin morir claro). Hay incluso una liga nacional. Para m&aacute;s informaci&oacute;n, la web recoge las normas y principales clubs de Espa&ntilde;a.</li>
<li><strong>scikit-learn</strong>, sklearn para los amigos, es una de las mejores librer&iacute;as de Machine Learning que existen en la actualidad, sobre todo si quieres probar cosas diferentes a redes neuronales (que tambi&eacute;n tiene, pero ah&iacute; no es tan buena) o quieres realizar operaciones de limpieza de datos, test, etc. Recientemente, han sacado la versi&oacute;n 0.22 que incluye, entre otras cosas:
<ul>
<li>Una nueva API de plotteado</li>
<li>Los algoritmos de ensemble: StackingClassifier y StackingRegressor</li>
<li>Permutation Importance</li>
<li>Soporte para valores desconocidos en el Gradient Boosting</li>
<li>Poda de &aacute;rboles m&aacute;s completa</li>
<li>Mejoras en el m&oacute;dulo de OpenML</li>
<li>Y muchas m&aacute;s cosas</li>
<li>Lista de cambios de <a href="https://scikit-learn.org/stable/auto_examples/release_highlights/plot_release_highlights_0_22_0.html">sklearn 0.22</a></li>
</ul>
</li>
<li>Siguiendo con actualizaciones,&nbsp;<strong>Alpine Linux</strong>, una de las distros m&aacute;s importantes en el mundo cloud, ha lanzado la versi&oacute;n 3.11.0, abriendo la rama 3.11. Al poco publicaron la 3.11.2 por bugs iniciales, pero los cambios importantes son:
<ul>
<li>Linux 5.4</li>
<li>Soporte a Raspberry Pi 4</li>
<li>Soporte inicial a GNOME y KDE</li>
<li>Soporte a Vulkan</li>
<li>MinGW-w64 y Rust disponibles</li>
<li>Actualizaci&oacute;n gen&eacute;rica de paquetes</li>
<li>Python 2 est&aacute; obsoleto. Se han borrado la mayor&iacute;a de paquetes relativos a Python 2 aunque algunos todav&iacute;a quedan en esta versi&oacute;n.</li>
<li>Lista de cambios de <a href="https://alpinelinux.org/posts/Alpine-3.11.0-released.html">Alpine Linux 3.11</a></li>
</ul>
</li>
<li>Otra actualizaci&oacute;n m&aacute;s, <a href="https://docs.djangoproject.com/en/3.0/releases/3.0/">Django 3.0</a>, ha salido con la principal novedad el soporte a <strong>ASGI</strong>, que es el equivalente a WSGI pero en entornos as&iacute;ncronos. Esto es un cambio interno principalmente.</li>
<li>UNIX empieza a contar su tiempo interno desde el 1 de enero de 1970, una convenci&oacute;n llamada UNIX Epoch, y que se usa en bastantes sitios. Pero en el sistema operativo <strong>OpenVMS</strong> no, se empieza a contar desde el 17 de noviembre de 1858. Los motivos son bastante interesantes y <a href="https://www.slac.stanford.edu/~rkj/crazytime.txt">se pueden leer aqu&iacute;</a>.</li>
<li>Bases de datos hay muchas, y de tipos diferentes (no solo de tablas relacionales va a vivir el mundo). En este sector hubo algo de movimiento con la&nbsp;<strong>web sem&aacute;ntica</strong>, y se dise&ntilde;aron bases de datos especiales para esto. Sin embargo, no acabaron de despegar y su uso es muy minoritario. <a href="https://terminusdb.com/"><strong>TerminusDB</strong></a> es una base de datos nueva, hecha en <a href="../../introduccion-a-prolog-tutorial-en-espanol">Prolog</a> y que intenta dar una vuelta a esto volviendo a abrazar RDF y todo lo que conlleva. Es uno de mis descubrimientos personales del a&ntilde;o pasado y les deseo lo mejor. En mi opini&oacute;n creo que el enfoque que adopta no es el mejor, aunque es pr&aacute;ctico. Por un lado los documentos (siempre en JSON-LD) se gestionan con una sencilla API REST y por otro lado las consultas se realizan con WOQL en vez de <a href="../../web-semantica-sparql">SPARQL</a>.</li>
<li>Por &uacute;ltimo, acabamos con un enlace de sumo inter&eacute;s para aquellos que usen o vayan a usar <strong>PostgreSQL</strong>, y tambi&eacute;n recomendable a cualquier persona que toque SQL: <a href="https://postgres-bits.herokuapp.com">Postgres Bits</a>. Se trata de las diapositivas de una conferencia de Heroku sobre Postgres y nos cuenta 12 cosas que tiene Postgres que quiz&aacute; no conozcamos. Empezamos por los WITH y los ARRAYs y acabando con UUIDs y HSTORE (el almacenamiento clave-valor de Postgres).</li>
</ul>
<p>Para despedirnos del primer Teletexto vamos a poner una canci&oacute;n y tambi&eacute;n os pido que si hab&eacute;is encontrado algo interesante, lo pong&aacute;is en los comentarios, y as&iacute; el Teletexto ser&aacute; m&aacute;s interesante para todos.</p>
<p style="text-align: center;"><iframe src="https://www.youtube-nocookie.com/embed/C_WVQOAgll8" width="560" height="315" frameborder="0" allowfullscreen=""></iframe></p>]]></description>
                <comments>https://blog.adrianistan.eu/teletexto-001</comments>
                <pubDate>Wed, 08 Jan 2020 14:08:41 +0000</pubDate>
            </item>
        
            <item>
                <title>Diesel, un ORM para Rust</title>
                <link>https://blog.adrianistan.eu/diesel-orm-rust</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/diesel-orm-rust</guid>
                <description><![CDATA[<p>Un tipo de librer&iacute;a muy popular en lenguajes din&aacute;micos son los ORM. Los ORM son librer&iacute;as que hacen de intermediario entre la base de datos y nuestra aplicaci&oacute;n, permiti&eacute;ndonos expresar en el lenguaje de programaci&oacute;n deseado las estructuras y datos y procedimientos. Rust, a pesar de ser un lenguaje de programaci&oacute;n est&aacute;tico, cuenta con un potente ORM gracias al sistema de macros. Se llama <a href="https://diesel.rs">Diesel&nbsp;</a> y es compatible con PostgreSQL, SQLite y MySQL. Un ejemplo uso de Diesel es este mismo blog, que lo usa para almacenar posts y comentarios en una base de datos PostgreSQL.</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://files.adrianistan.eu/Diesel.png" alt="" width="359" height="186" /></p>
<h2>Enfoque</h2>
<p>Dentro de los ORM existen varios enfoques, por un lado est&aacute;n aquellos que lo &uacute;nico que hacen es hacer accesibles las estructuras de datos desde el lenguaje, realizando las conversiones de tipos. Estos ORM m&aacute;s b&aacute;sicos se suelen llamar&nbsp;<strong>data mappers</strong>. Diesel soporta esto, pero adem&aacute;s soporta un enfoque donde las operaciones tambi&eacute;n se realizan en Rust y tambi&eacute;n podemos usar migraciones (a trav&eacute;s de una CLI).</p>
<p>Personalmente no uso la CLI, pero para proyectos nuevos puede ser m&aacute;s que interesante. En este tutorial, intentaremos ver todas las formas de usar Diesel.</p>
<h2>Creando el proyecto con migraciones</h2>
<p>Lo primero ser&aacute; crear un proyecto Rust, con cargo new y a&ntilde;adir Diesel como dependencia en el fichero Cargo.toml. <a href="https://github.com/aarroyoc/blog-ejemplos">Todo el c&oacute;digo lo ten&eacute;is aqu&iacute; por si en alg&uacute;n momento necesit&aacute;is referencia</a>. Ser&aacute; el momento de instalar diesel_cli y crear la base de datos.</p>
<pre><code>
cargo new diesel-sample
cargo install diesel_cli --no-default-features --features sqlite<br />diesel setup --database-url db.sqlite3
</code></pre>
<p>Se habr&aacute; generado un fichero diese.toml y una carpeta migrations vac&iacute;a. Vamos a crear una migraci&oacute;n nueva (init) en SQL.</p>
<pre><code>
diesel migration generate init
</code></pre>
<p>Habr&aacute; generado una carpeta dentro de migrations con dos ficheros up y down. Up sirve para hacer la migraci&oacute;n, down para revertirla. El contenido de up.sql ser&aacute; el siguiente:</p>
<pre><code class="language-sql">
CREATE TABLE post (
	id INTEGER,
	title TEXT NOT NULL,
	content TEXT NOT NULL,
	PRIMARY KEY(id));
</code></pre>
<p>Y el de down.sql</p>
<pre><code>
DROP TABLE post;
</code></pre>
<p>Podemos ejecutar la migraci&oacute;n con:</p>
<pre><code>
diesel migration run
</code></pre>
<p>Y deshacerla con</p>
<pre><code>
diesel migration redo
</code></pre>
<p>Adem&aacute;s de eso se nos habr&aacute; generado un fichero llamado schema.rs, en el lugar especificado por el fichero diesel.toml (normalmente ser&aacute; en src). Este fichero sigue una estructura muy interesante y es el verdadero pegamento entre nuestra aplicaci&oacute;n y la base de datos. Las migraciones lo &uacute;nico que hacen es ejecutar el SQL y tomar nota del resultado final para generar el fichero schema.rs, que contiene una macro.</p>
<p>Es decir, ahora podemos seguir, vayas a usar migraciones o no.</p>
<h2>Schema.rs</h2>
<p>Si usas migraciones no deber&iacute;as editar este archivo a mano pero si no vas a usar migraciones (sistemas heredados o simplemente tienes otra forma de manejar el SQL) puedes crearlo y editarlo a mano. La sintaxis es sencilla:</p>
<pre><code>
table! {
    post (id) {
        id -&gt; Integer,
        title -&gt; Text,
        content -&gt; Text,
    }
}
</code></pre>
<p>Por un lado hay un table! por cada tabla en la base de datos. Esta lleva el nombre de la tabla (Post) y su clave primaria (id), a continuaci&oacute;n se define un listado de campos y su tipo. Estos tipos no son ni de Rust ni SQL, son tipos que define Diesel.</p>
<p>Otras macros que pueden aparecer en el fichero schema.rs son joinable y <span class="pl-en">allow_tables_to_appear_in_same_query.</span></p>
<p><span class="pl-en">Joinable indica que las tablas est&aacute;n relacionadas entre s&iacute; y se puede hacer un JOIN. As&iacute; pues, en el caso de que hubiese comentarios en la base de datos, y estos tuvieran un campo post_id, lo podr&iacute;amos relacionar as&iacute;:</span></p>
<pre><code>
joinable!(comment -&gt; post (post_id));
</code></pre>
<p>Normalmente siempre se usa juno con <span class="pl-en">allow_tables_to_appear_in_same_query, que permite que en una misma query se pueda acceder a dos tablas:<br /></span></p>
<pre><code>
allow_tables_to_appear_in_same_query!(post, comment);
</code></pre>
<h2>Queryable</h2>
<p>Ahora podemos definir estructuras en Rust que representen el resultado de una consulta a la base de datos, esto lo podemos hacer con Queryable. La estructura no tiene por qu&eacute; ser id&eacute;ntica a la tabla, incluso puede tener menos campos o m&aacute;s resultado de un JOIN. Pero es muy importante que el orden de los campos estructura. Si queremos despreocuparnos podemos usar QueryableByName, pero tendremos que indicar la tabla. Pong&aacute;moslo todo junto. Adem&aacute;s, primero vamos a meter algunos datos en la base de datos:</p>
<pre><code>
sqlite3 db.sqlite3
&gt; INSERT INTO post VALUES (1, "El Guardi&aacute;n entre el Centeno", "El libro de los psic&oacute;patas");<br />&gt; INSERT INTO post VALUES (2, "On the road", "Esencia de la generaci&oacute;n beat");
</code></pre>
<p>El c&oacute;digo es el siguiente:</p>
<pre><code class="language-rust">
#[macro_use]
extern crate diesel;

use diesel::prelude::*;
use diesel::sqlite::SqliteConnection;
use self::schema::*;

mod schema;

#[derive(Queryable)]
struct Post{
    id: i32,
    title: String,
    content: String,
}

fn main() {
    let conn = SqliteConnection::establish("db.sqlite3").unwrap();

    let posts = post::table.load::&lt;Post&gt;(&amp;conn).unwrap();
    for post in posts {
        println!("Title: {}\tContent: {}", post.title, post.content);
    }
}
</code></pre>
<p>Lo primero que hay que hacer es importar diesel con macros. Luego cargamos el preludio de Diesel, la conexi&oacute;n con Sqlite y nuestro esquema. Hay unos helpers disponibles bajo el subm&oacute;dulo dsl. Sin embargo, en mi experiencia, la colisi&oacute;n de nombres se vuelve un problema real y es mejor ser m&aacute;s verboso.</p>
<p>Esta selecci&oacute;n es muy sencilla, veamos otra que tire de WHERE.</p>
<p>Para a&ntilde;adir condiciones usamos&nbsp;<strong>filter.</strong> Las condiciones tienen que escribirse con una API fluent, no se pueden usar condiciones igual que en Rust de momento. Por ejemplo, para pedir los posts con ID &gt; 1:</p>
<pre><code class="language-rust">
    let posts = post::table
        .filter(post::id.gt(1))
        .load::&lt;Post&gt;(&amp;conn)
        .unwrap();
</code></pre>
<p>Donde gt significa "greater than" y es el equivalente a &gt;.</p>
<h2>SELECT y JOIN</h2>
<p>La API de Diesel admite m&aacute;s opciones por supuesto. Podemos elegir los campos que queremos que se carguen de la base de datos. Evidentemente, la estructura a la que carguemos los datos deber&aacute; soportarlo. De hecho, si solo seleccionamos una columna podremos ahorrarnos este paso e ir directamente con tipos primitivos.</p>
<pre><code class="language-rust">
    let posts = post::table
        .select(post::title)
        .load::&lt;String&gt;(&amp;conn)
        .unwrap();
    for post_title in posts {
        println!("Title: {}", post_title);
    }
</code></pre>
<p>Para realizar JOIN, es necesario que las tablas est&eacute;n relacionadas por&nbsp;<strong>joinable</strong> y una vez hecho eso, podemos hacer el join directamente, sin especificar IDs ni claves for&aacute;neas, simplemente agregando&nbsp;<strong>inner_join</strong> a la petici&oacute;n.</p>
<h2>Insertable</h2>
<p>&iquest;Y si queremos insertar algo en la base de datos? Diesel nos ofrece Insertable, para poder volcar estructuras en las base de datos. Su uso es muy similar a Queryable pero a&ntilde;adiendo el nombre de la tabla. De hecho, en el ejemplo que voy a poner, PostInsert y Post podr&iacute;an ser la misma estructura pero lo voy a separar. Al igual que en SQL, no tenemos que proveer todos los campos si el esquema lo permite. A&ntilde;adimos los datos con&nbsp;<strong>insert_into</strong>.</p>
<pre><code class="language-rust">
#[derive(Insertable)]
#[table_name="post"]
struct PostInsert{
    id: i32,
    title: String,
    content: String,
}

...

let new_post = PostInsert {
        id: 5,
        title: "Test".to_string(),
        content: "Lorem Ipsum".to_string(),
    };

    diesel::insert_into(post::table)
        .values(&amp;new_post)
        .execute(&amp;conn);
</code></pre>
<h2>SQL</h2>
<p>Diesel nos permite escribir queries en SQL directamente tambi&eacute;n. Para ello tenemos&nbsp;<strong>sql_query</strong>. Las estructuras donde obtendremos la informaci&oacute;n deben tener QueryableByName, esto es debido a que Queryable hace la traducci&oacute;n simplemente por el orden de los elementos, QueryableByName la hace teniendo en cuenta el nombre de las columnas.</p>
<pre><code class="language-rust">
#[derive(QueryableByName)]
#[table_name="post"]
struct PostRaw {
    id: i32,
    title: String,
}
...
    let posts = diesel::sql_query("SELECT id,title FROM post WHERE id &gt; 0").load::&lt;Post&gt;(&amp;conn).unwrap();
    for post in posts {
        println!("Title: {}\tID: {}", post.title, post.id);
    }
</code></pre>
<p>De este modo, Diesel solo realiza las funciones de data mapper y podemos reusar nuestro c&oacute;digo SQL o si preferimos SQL, podemos usarlo.</p>
<h2>UPDATE y DELETE</h2>
<p>Para realizar UPDATE o DELETE, tenemos que seleccionar primero las filas (con find o filter) y despu&eacute;s ejecutar.</p>
<p>En el caso de UPDATE, usaremos set para ir seteando los nuevos valores. Para introducir el valor usamos eq (igual):</p>
<pre><code class="language-rust">
diesel::update(post::table.find(1))
        .set(post::title.eq("T&iacute;tulo actualizado".to_string()))
        .execute(&amp;conn);
</code></pre>
<p>Y el DELETE ser&iacute;a similar.</p>
<pre><code class="language-rust">
    diesel::delete(post::table.find(1))
        .execute(&amp;conn);
</code></pre>
<p>Y con esto ya tendr&iacute;amos las operaciones b&aacute;sicas de CRUD y alguna m&aacute;s implementadas con Diesel.</p>
<p>Con el paso del tiempo, Diesel ha resultado ser una de las librer&iacute;as m&aacute;s interesantes del mundo Rust, una librer&iacute;a estable y que nos ahorra mucho trabajo y nos ayuda a encontrar errores. <a href="https://github.com/aarroyoc/blog-ejemplos">Todo el c&oacute;digo del art&iacute;culo est&aacute; en GitHub.</a></p>]]></description>
                <comments>https://blog.adrianistan.eu/diesel-orm-rust</comments>
                <pubDate>Fri, 03 Jan 2020 15:43:12 +0000</pubDate>
            </item>
        
            <item>
                <title>¡Feliz año 2020!</title>
                <link>https://blog.adrianistan.eu/feliz-2020</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/feliz-2020</guid>
                <description><![CDATA[<p>Si tomas los cuatro n&uacute;meros primos consecutivos empezando con 17, los elevas al cuadrado y los sumas, &iquest;qu&eacute; te da?</p>
<p style="text-align: center;">17&sup2;+19&sup2;+23&sup2;+29&sup2; = 2020</p>
<p style="text-align: left;">&iexcl;Efectivamente! 2020. &iexcl;Felicidades a todos los lectores del blog que me acompa&ntilde;&aacute;is! S&eacute; que ya sois unos cuantos. Espero que el a&ntilde;o que viene podamos seguir hablando aqu&iacute; de programaci&oacute;n, inteligencia artificial, computaci&oacute;n y lo que surja.</p>
<p style="text-align: left;">Antes de dar paso a la muerte definitiva a Python 2.7 vamos a repasar las principales estad&iacute;sticas del blog.</p>
<p style="text-align: left;">Este a&ntilde;o hab&eacute;is visitado el blog la friolera de <strong>44.864 usuarios</strong> en <strong>58.116 sesiones</strong>, un claro aumento respecto a 2018 (39.328 usuarios y 51.731 sesiones).</p>
<h2 style="text-align: left;">Pa&iacute;ses</h2>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://files.adrianistan.eu/Paises2019.png" alt="" />Los tres primeros puestos son inamovibles, aunque Espa&ntilde;a cada vez representa menos porcentaje de visitas. En el cuarto puesto Chile ha adelantado a Argentina, aunque siguen muy cerca el uno del otro. Los pa&iacute;ses europeos Francia y Reino Unido salen de la lista, tienen muchas menos visitas este a&ntilde;o.&nbsp;</p>
<p>Las ciudades que m&aacute;s visitan mi blog son: Madrid, Santiago (de Chile), Bogot&aacute;, Ciudad de M&eacute;xico, Barcelona, Buenos Aires y Medell&iacute;n.</p>
<h2>Tecnolog&iacute;a</h2>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://files.adrianistan.eu/Navegador2019.png" alt="" /></p>
<p>Resultados muy similares al a&ntilde;o pasado</p>
<h2 style="text-align: left;">Art&iacute;culos</h2>
<p>Y el art&iacute;culo m&aacute;s visitado en 2019 es:</p>
<p style="text-align: center;"><a href="../../estadistica-python-media-mediana-varianza-percentiles-parte-iii"><span class="_GApu  _GAl ACTION-drilldown TARGET-0-0 ">Estad&iacute;stica en Python: media, mediana, varianza, percentiles (Parte III)</span></a></p>
<p style="text-align: left;"><span class="_GApu  _GAl ACTION-drilldown TARGET-0-0 ">No me voy a extender mucho en esto porque con la Ley de Zipf se vieron los art&iacute;culos m&aacute;s le&iacute;dos</span></p>
<p style="text-align: left;"><span class="_GApu  _GAl ACTION-drilldown TARGET-0-0 ">Sin extenderme mucho m&aacute;s, &iexcl;os deseo feliz a&ntilde;o a todos!</span></p>]]></description>
                <comments>https://blog.adrianistan.eu/feliz-2020</comments>
                <pubDate>Tue, 31 Dec 2019 19:48:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Yield, generadores y corrutinas en Python</title>
                <link>https://blog.adrianistan.eu/yield-generadores-python</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/yield-generadores-python</guid>
                <description><![CDATA[<p>Estaba realizando uno de los problemas del Advent of Code 2019, cuando tuve la oportunidad de usar generadores y yield en Python, y para mi sorpresa, mucha gente los desconoc&iacute;a. Os pongo en situaci&oacute;n. Si record&aacute;is, el a&ntilde;o pasado intent&eacute; publicar mis soluciones al Advent of Code comentadas aqu&iacute;, aunque no logr&eacute; acabar y sigo teniendo pendientes de hacer los &uacute;ltimos d&iacute;as de 2018. Este a&ntilde;o lo he vuelto a intentar pero no he publicado nada por aqu&iacute;, lo que considero que ha sido una buena decisi&oacute;n, ya que gastaba mucho tiempo y eran posts bastante densos de leer con poca utilidad. Sin embargo, he seguido comentando con compa&ntilde;eros, a trav&eacute;s de Telegram sobre todo, diferentes soluciones. Ese d&iacute;a hab&iacute;a que ejecutar 4 procesos de forma circular, uno detr&aacute;s de otro pero manteniendo el estado en cada uno de ellos. Aunque existen otras formas v&aacute;lidas de resolverlo, considero que merece la pena echar un vistazo a los generadores y corrutinas de Python.</p>
<p>Empecemos por lo m&aacute;s sencillo de definir, un generador.</p>
<h2>&iquest;Qu&eacute; es un generador?</h2>
<p>Un generador es una funci&oacute;n que permite ser pausada, devolviendo informaci&oacute;n, para posteriormente ser restaurada. En las paradas en el intercambio de datos solo se produce desde la corrutina, hacia la funci&oacute;n que la llam&oacute;. De ah&iacute; viene su nombre: funciones "generadoras" porque van generan informaci&oacute;n.</p>
<p>Visto as&iacute; puede incluso parecer parecido a la programaci&oacute;n con hilos pero no os enga&ntilde;&eacute;is, todo esto sucede en un &uacute;nico hilo. Cuando se llega al punto de parada en el generador, definido dentro de esta, se guarda el estado de la funci&oacute;n, variables locales dentro de ella y se devuelve alg&uacute;n dato a la funci&oacute;n original. y se vuelve a la funci&oacute;n original. Cuando se vuelve a restaurar la corrutina, se carga en memoria el estado de la funci&oacute;n y se contin&uacute;a hasta la siguiente parada.</p>
<p>Esto cambia un poco nuestra mentalidad, ya que normalmente se ense&ntilde;a que las funciones se llaman, con unos datos de entrada, y cuando acaban devuelven un valor.</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://files.adrianistan.eu/Coroutine.jpg" alt="" /></p>
<p>&nbsp;</p>
<h2>Sintaxis de los generadores en Python</h2>
<p>La palabra clave para implementar corrutinas es&nbsp;<strong>yield.</strong> yield hace una parada de la corrutina y devuelve un valor. Podemos pensar en yield como un return, pero sobre el que despu&eacute;s sigue la funci&oacute;n. Para controlar la corrutina disponemos de varios m&eacute;todos. El primero, es usar <strong>for</strong> para ir llamando const&aacute;ntemente al generador.</p>
<pre><code class="language-python">
def generator():
    yield "Hola"
    yield "Mundo"

def main():
    f = generator()
    for x in f:
        print(x)

main()
</code></pre>
<p>Obteniendo como resultado "Hola Mundo". Usar for es la mejor manera de controlar un generador cuando tenemos solamente uno. En general, los generadores tambi&eacute;n funcionar&aacute;n en cualquier funci&oacute;n que trabaje con iteradores, como <strong>itertools.takewhile</strong>.</p>
<pre><code class="language-python">
from itertools import takewhile

def fib():
    a = 0
    b = 1
    while True:
        c = a+b
        a = b
        b = c
        yield c

def main():
    f = fib()
    a = takewhile(lambda x: x&lt;100, f)
    print(list(a))

main()
</code></pre>
<p>Este ejemplo, almacena en una lista todos los n&uacute;meros de la secuencia de Fibonacci menores que 100.</p>
<p>Otra forma de controlar los generadores es a trav&eacute;s de la funci&oacute;n&nbsp;<strong>next</strong>. Una llamada a next ejecuta el generador hasta la siguiente parada y devuelve el contenido de yield.</p>
<pre><code class="language-python">
def gen():
    yield "Hola"
    yield "Mundo"

def main():
    f = gen()
    print(next(f))
    print(next(f))

main()
</code></pre>
<p>Es equivalente al Hola Mundo anterior hecho con for. Usar next es mucho m&aacute;s flexible pero es preferible usar for si podemos.</p>
<h2>&iquest;Qu&eacute; es una corrutina?</h2>
<p>Una corrutina es una&nbsp;<strong>funci&oacute;n</strong> que se puede suspender su ejecuci&oacute;n y posteriormente restaurarla. En estas paradas puede haber un intercambio de datos en <strong>ambos sentidos</strong>.</p>
<p>A diferencia de los generadores, aqu&iacute; hay transmisi&oacute;n de informaci&oacute;n tambi&eacute;n de la funci&oacute;n base a la corrutina. Esto se hace a trav&eacute;s de la funci&oacute;n&nbsp;<strong>send</strong> y el hecho de que <strong>yield</strong> devuelve valores.</p>
<p>Veamos este sencillo ejemplo.</p>
<pre><code class="language-python">
def gen():
    x = yield "Hola"
    yield x

def main():
    f = gen()
    print(next(f))
    y = f.send("Mundo")
    print(y)

main()
</code></pre>
<p>Aqu&iacute; podemos ver como el primer yield recibe un dato, que se env&iacute;a a trav&eacute;s de&nbsp;<strong>send</strong>. send es a su vez un next, as&iacute; que el valor de vuelta de send es el de la siguiente parada.</p>
<p>En Python no llegaron las corrutinas puras hasta Python 3.3, sin embargo, eran casos muy excepcionales los que necesitaban la funcionalidad extra, el <strong>yield from</strong>. El yield from es una sintaxis que permite que la corrutina llame a otraa funciones y sean estas funciones las que se encarguen de hacer el yield. Si no lo indic&aacute;semos, las nuevas funciones ser&iacute;an simplemente corrutinas de las corrutinas, con yield from logramos eliminar eso.</p>
<pre><code class="language-python">
def gen():
    yield from sub()

def sub():
    yield "Hola Mundo"

def main():
    f = gen()
    print(next(f))

main()
</code></pre>
<p>Los casos de uso de las corrutinas se solapan mucho con los de los objetos en el mundo OOP, ya que ambos mecanismos nos permiten mantener estado entre dos contextos. En el caso de los objetos, las llamadas a m&eacute;todos permiten "restaurar" la ejecuci&oacute;n, mientras que en las corrutinas, una llamada a next o send.</p>
<p>Los generadores y corrutinas existen en muchos lenguajes de corte imperativo como Python, Go o Lua y en otros de corte funcional como Scheme y Haskell.</p>
<p>&iquest;Conoc&iacute;as los generadores y las corrutinas? &iquest;Los has usado alguna vez? Cuenta tu experiencia en los comentarios</p>
<p>&nbsp;</p>]]></description>
                <comments>https://blog.adrianistan.eu/yield-generadores-python</comments>
                <pubDate>Fri, 27 Dec 2019 16:19:59 +0000</pubDate>
            </item>
        
            <item>
                <title>Ley de Zipf en el blog</title>
                <link>https://blog.adrianistan.eu/ley-zipf-blog</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/ley-zipf-blog</guid>
                <description><![CDATA[<p>Estaba yo leyendo uno de mis blogs favoritos,&nbsp;<a href="https://diasyfrases.blogspot.com"><em><strong>Los d&iacute;as y las frases</strong></em></a>, que pese a lo que se pueda pensar de m&iacute;, no trata, ni remotamente de tecnolog&iacute;a, programaci&oacute;n, etc sino de aforismos e historia. Muy entretenido, siempre leo sus art&iacute;culos nada m&aacute;s salir. Hace unos meses ya, el autor public&oacute; una entrada sobre la ley de Zipf. Como &eacute;l lo explica mejor que nadie, voy a copiar literalmente el texto:</p>
<blockquote>
<div style="text-align: justify;"><em> George Kingsley <strong>Zipf</strong> fue un <strong><u><a href="https://diasyfrases.blogspot.com/2019/07/es-la-vida-demasiado-corta-para.html">ling&uuml;ista</a></u></strong> norteamericano de mediados del siglo XX que se dedic&oacute; a aplicar el <strong>an&aacute;lisis estad&iacute;stico</strong> a las lenguas. <br /></em></div>
<div style="text-align: justify;">&nbsp;</div>
<div style="text-align: justify;"><em> Uno de los estudios que le report&oacute; fama fue el descubrimiento de la ley que lleva su nombre, la "<strong>Ley de Zipf</strong>", seg&uacute;n la cual la frecuencia con la que son utilizadas las palabras siguen una distribuci&oacute;n <strong>estad&iacute;stica </strong>concreta. No entraremos en detalles t&eacute;cnicos de su formulaci&oacute;n, pero b&aacute;sicamente nos dice que la palabra m&aacute;s usada en un idioma (the, en ingl&eacute;s) aparece el doble de veces que la segunda m&aacute;s usada (of), y el triple que la tercera, etc.&nbsp;</em></div>
<div style="text-align: justify;">&nbsp;</div>
<div style="text-align: justify;"><em> Pero esta ley de la frecuencia de las apariciones no ocurre solo con las palabras, su &aacute;mbito es mucho mayor. Por ejemplo, en el de las poblaciones de las ciudades de un pa&iacute;s: la ciudad m&aacute;s grande suele tener el doble de habitantes que la segunda poblaci&oacute;n de ese pa&iacute;s. Y en general es aplicable a la ordenaci&oacute;n de&nbsp; grandes conjuntos de datos... E <strong><u><a href="https://diasyfrases.blogspot.com/2016/07/cisne-negro.html">internet</a></u>,&nbsp;</strong>que no deja de ser una <strong>base de datos</strong> enorme,no podr&iacute;a ser menos, tambi&eacute;n se puede describir el n&uacute;mero de visitas a las p&aacute;ginas individuales de Internet en un intervalo de tiempo dado... (Art&iacute;culo <a href="https://diasyfrases.blogspot.com/2019/09/cumple-este-blog-con-la-ley-de-zipf.html">https://diasyfrases.blogspot.com/2019/09/cumple-este-blog-con-la-ley-de-zipf.html</a>)</em></div>
</blockquote>
<p style="text-align: center;"><img src="https://files.adrianistan.eu/Zipf.jpeg" alt="" /></p>
<p style="text-align: left;">A continuaci&oacute;n, prueba con los art&iacute;culos del blog, seg&uacute;n n&uacute;mero de visitas, a ver si la popularidad sigue esta curiosa ley, a priori, relacionada con la ling&uuml;&iacute;stica. &iexcl;Al parecer <em>Los d&iacute;as y las frases</em> sigue una distribuci&oacute;n similar a la ley de Zipf! &iquest;Y mi blog, Adrianist&aacute;n? &iquest;Seguir&aacute; tambi&eacute;n la ley de Zipf?</p>
<h2 style="text-align: left;">Experimento</h2>
<p>Voy a tomar los datos del mes de octubre, ya que es el m&aacute;s pr&oacute;ximo que ya ha acabado y considero que es un mes representativo, bastante normalillo. Adem&aacute;s, las entradas que publiqu&eacute; en octubre no parecen haber tenido demasiado impacto en general. Tambi&eacute;n he decidido quitar la p&aacute;gina de inicio, ya que no es un art&iacute;culo como tal.</p>
<p>El art&iacute;culo m&aacute;s visto del mes es Estad&iacute;stica en Python Parte 3 con 1327 visitas. A partir de aqu&iacute; podemos calcular las visitas estimadas seg&uacute;n la ley, dividiendo progresivamente.</p>
<table>
<tbody>
<tr>
<th>Art&iacute;culos</th>
<th>Visitas Reales</th>
<th>Visitas Zipf</th>
</tr>
<tr>
<td>/estadistica-python-media-mediana-varianza-percentiles-parte-iii</td>
<td>1327</td>
<td>1327</td>
</tr>
<tr>
<td>/estadistica-python-distribucion-binomial-normal-poisson-parte-vi</td>
<td>445</td>
<td>663.5</td>
</tr>
<tr>
<td>/estadistica-python-pandas-numpy-scipy-parte-i</td>
<td>434</td>
<td>442.333333333333</td>
</tr>
<tr>
<td>/rust-101-tutorial-rust-espanol</td>
<td>328</td>
<td>331.75</td>
</tr>
<tr>
<td>/introduccion-a-prolog-tutorial-en-espanol</td>
<td>233</td>
<td>265.4</td>
</tr>
<tr>
<td>/tutorial-de-cmake</td>
<td>199</td>
<td>221.166666666667</td>
</tr>
<tr>
<td>/estadistica-python-analisis-datos-multidimensionales-regresion-lineal-parte-iv</td>
<td>162</td>
<td>189.571428571429</td>
</tr>
<tr>
<td>/cosas-no-sabias-python</td>
<td>151</td>
<td>165.875</td>
</tr>
<tr>
<td>/estadistica-python-ajustar-datos-una-distribucion-parte-vii</td>
<td>134</td>
<td>147.444444444444</td>
</tr>
</tbody>
</table>
<p>(veo que os gusta mucho la estad&iacute;stica con Python)</p>
<p>Vemos que hay n&uacute;meros muy pr&oacute;ximos a la estimaci&oacute;n, pero mejor hagamos un gr&aacute;fico.</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://files.adrianistan.eu/LeyZipf.png" alt="" /></p>
<p>Vemos que la curva real se ajusta relativamente bien a la curva estimada por la ley de Zipf. El punto donde m&aacute;s se aleja (tanto absoluto como relativamente) es el segundo art&iacute;culo.</p>
<p>Podr&iacute;amos decir, que s&iacute;, en Adrianist&aacute;n tambi&eacute;n se aplica la ley de Zipf. &iquest;Ser&aacute;, quiz&aacute;, que esta ley se aplica en todos los sistemas de informaci&oacute;n? &iquest;Es parte intr&iacute;nseca de la realidad? Os dejo reflexionar</p>]]></description>
                <comments>https://blog.adrianistan.eu/ley-zipf-blog</comments>
                <pubDate>Mon, 25 Nov 2019 21:47:21 +0000</pubDate>
            </item>
        
            <item>
                <title>Historia de la privacidad</title>
                <link>https://blog.adrianistan.eu/historia-privacidad</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/historia-privacidad</guid>
                <description><![CDATA[<p>&Uacute;ltimamente se habla mucho de la privacidad. Si bien antes era algo anecd&oacute;tico, tras la llegada de las redes sociales y los dispositivos m&oacute;viles, la privacidad se ha convertido en un tema mucho m&aacute;s com&uacute;n. M&aacute;s a&uacute;n con los esc&aacute;ndalos como el de Cambridge Analytica que obligaron a Mark Zuckerberg a declarar. Pero entonces me surgi&oacute; una curiosidad. &iquest;De d&oacute;nde sale la privacidad? &iquest;Los romanos ten&iacute;an privacidad? &iquest;Por qu&eacute; en la historia de la humanidad es un concepto que parece inexistente?</p>
<p>Lo dej&eacute; pasar, pero entonces, viendo un episodio de House (s&iacute;, ya s&eacute;, la serie acab&oacute; hace mucho, qu&eacute; se le va a hacer), Taub dice en un momento: <em>la privacidad es un invento moderno, las tribus antiguas no la ten&iacute;an, ya que iba en contra de la propia supervivencia como grupo ocultarse informaci&oacute;n (</em>o algo as&iacute;). No sab&iacute;a si esa afirmaci&oacute;n era cierta y me puse a investigar.</p>
<h2>Or&iacute;genes</h2>
<p>Es dif&iacute;cil saber si en las primeras tribus de Homo Sapiens exist&iacute;an conceptos tales como privacidad o la intimidad. Es posible que en las tribus primigenias, no hubiese separaci&oacute;n p&uacute;blico-privada, ya que todo el grupo podr&iacute;a tratarse como una gran familia, donde todos los miembros se tratasen entre s&iacute; como iguales miembros de una tribu. Es plausible, pero no hay demasiadas evidencias, salvo el hecho de que posiblemenre viviesen en cuevas todos juntos. <br /><br />Las primeras referencias a la privacidad llegan de manos de <strong>Arist&oacute;teles</strong>, fil&oacute;sofo griego, quien hablaba de <em>esferas</em> de la vida. La primera hac&iacute;a referencia al &aacute;mbito p&uacute;blico, la <em>polis</em> y se relacionaba con la vida pol&iacute;tica. La segunda hac&iacute;a referencia al &aacute;mbito privado, la <em>oikos</em> y se relacionaba con la vida domeśtica. Estas son las primeras referencias claras que tenemos a una idea de privacidad.</p>
<p style="text-align: center;"><img src="https://files.adrianistan.eu/StuartMill.jpeg" alt="" width="200" /></p>
<p>Sin embargo, la idea de privacidad en el imaginario colectivo es difusa y muchas veces hace referencia a conceptos similares pero diferentes dependiendo del autor. <strong>John Stuart Mill</strong> hablaba en 1859 en su ensayo <em>On Liberty</em> de lo p&uacute;blico/privado en relaci&oacute;n al control gubernamental y a la auto regulaci&oacute;n. Seg&uacute;n &eacute;l, es necesaria la privacidad para poder tener libertad y no ser t&iacute;teres de quien gobernase.<br /><br />En el plano antropol&oacute;gico, <strong>Margaret Mead</strong> descubri&oacute; que la privacidad, aunque expresada de formas diferentes, era un rasgo com&uacute;n en multitud de culturas. <strong>Alan Westin</strong> analiz&oacute; comportamientos animales descubriendo que el deseo de privacidad no era &uacute;nico al Homo Sapiens, aunque tambi&eacute;n, se expresaba de formas diferentes.<br /><br />Hasta este punto el concepto de privacidad puede referirse a varias ideas: la separaci&oacute;n entre gobierno/individuo, conocimiento prohibido, confidencialidad o el mero hecho de la soledad.</p>
<h2>Concepto moderno</h2>
<p>Quiz&aacute; el primer estudio sistem&aacute;tico sobre la privacidad sea el ensayo de <strong>Samuel Warren</strong> y <strong>Louis Brandeis</strong> de 1890, <em>The Right to Privacy</em>. Este ensayo es muy interesante, ya que parte de un contexto muy similar al actual y es que "debido a los cambios tecnol&oacute;gicos como la fotograf&iacute;a o los peri&oacute;dicos, es necesario defender este derecho impl&iacute;cito en nuestro modo de vida". Se propone por primera vez que haya regulaciones respecto a la privacidad, que en definitiva, siguen los principios asumidos pero no escritos, de gran parte de la poblaci&oacute;n anglosajona en ese momento. La privacidad es invadida cuando se hace p&uacute;blica informaci&oacute;n relativa a la vida privada de una persona, por parte de un tercero. Se defiende tambi&eacute;n "el derecho a la soledad". La privacidad se justifica como un derivado del derecho a la personalidad, y que esta personalidad no pueda ser violada por otros individuos. Sin privacidad, argumentan, la personalidad no se puede mantener y esta es un elemento vital de cualquier ser humano.</p>
<p style="text-align: center;"><img src="https://files.adrianistan.eu/RightPrivacy.jpeg" alt="" width="200" /></p>
<p>Una vez sentada la base de la privacidad con Warren y Brandeis, poco a poco se ir&aacute; incorporando a c&oacute;digos y legislaciones, como un derecho m&aacute;s.</p>
<p>En 1948, aparecer&aacute; en la <em>Declaraci&oacute;n Universal de Derechos Humanos</em> un art&iacute;culo referente a la protecci&oacute;n de la vida privada (art&iacute;culo 12):</p>
<blockquote>
<p>Nadie ser&aacute; objeto de injerencias arbitrarias en su vida privada, su familia, su domicilio o su correspondencia, ni de ataques a su honra o a su reputaci&oacute;n. Toda persona tiene derecho a la protecci&oacute;n de la ley contra tales injerencias o ataques.</p>
</blockquote>
<p>En 1978, la Constituci&oacute;n Espa&ntilde;ola se convertir&aacute; en una de las primeras constituciones en recoger este derecho, a trav&eacute;s de su art&iacute;culo 18:</p>
<blockquote>
<p>1. Se garantiza el derecho al honor, a la intimidad personal y familiar y a la propia imagen.<br /><br />2. El domicilio es inviolable. Ninguna entrada o registro podr&aacute; hacerse en el sin consentimiento del titular o resoluci&oacute;n judicial, salvo en caso de flagrante delito.<br /><br />3. Se garantiza el secreto de las comunicaciones y, en especial, de las postales, telegr&aacute;ficas y telef&oacute;nicas, salvo resoluci&oacute;n judicial.<br /><br />4. La Ley limitar&aacute; el uso de la inform&aacute;tica para garantizar el honor y la intimidad personal y familiar de los ciudadanos y el pleno ejercicio de sus derechos.</p>
</blockquote>
<p>Sin embargo, estos reconocimientos no han estado exentos de cr&iacute;tica desde el punto de vista filos&oacute;fico.</p>
<h2>Cr&iacute;ticas a la privacidad</h2>
<p>Existen cuatro vertientes cr&iacute;ticas de la privacidad:</p>
<p><strong>Reduccionismo de Thomson</strong>: La privacidad no es un derecho porque en realidad, todas las "invasiones" de la privacidad no son m&aacute;s que violaciones de otros derechos ya reconocidos. Por tanto, la privacidad no es un derecho en s&iacute;, sino una mezcolanza de situaciones en las que aplican otros derechos diferentes. El concepto de privacidad es por tanto redundante.</p>
<p><strong>Cr&iacute;tica econ&oacute;mica de Posner</strong>: La informaci&oacute;n protegida bajo la privacidad no es diferente a cualquier otro tipo de informaci&oacute;n. Adem&aacute;s, al mantenerla privada, estamos en la mayor&iacute;a de casos cometiendo ineficiencias en el sistema econ&oacute;mico. Posner argumenta que el &uacute;nico sentido que tiene una informaci&oacute;n de ser privada es cuando, su valor econ&oacute;mico es mayor al mantenerse en secreto.</p>
<p><strong>Visi&oacute;n de Bork</strong>: para Bork la privacidad fue un derecho inventado, sin justificaci&oacute;n aparente, ya que no se deriva de ninguna ley natural o de un derecho anterior preexistente. Es por ello que Bork argumenta que la privacidad como derecho, se basa en la tradici&oacute;n de ciertas jurisdicciones que han podido otorgarle el papel de pseudoderecho, pero realmente no lo es.</p>
<p><strong>Cr&iacute;tica feminista</strong>: si bien existen muchos puntos de vista dentro de la filosof&iacute;a feminista, una posici&oacute;n es bastante cr&iacute;tica. Esta argumenta que la privacidad no es m&aacute;s que un mecanismo que permite ocultar la represi&oacute;n y el abuso que sufren las mujeres en el entorno familiar. En un mundo sin privacidad, este tipo de actos no quedar&iacute;an en el entorno meramente dom&eacute;stico, que dejar&iacute;a de existir, sino que ser&iacute;an p&uacute;blicos y ser&iacute;an m&aacute;s inusuales y m&aacute;s efectivamente penados cuando sucediesen. Otro enfoque apunta a que si bien esto es algo que sucede actualmente, un mundo sin privacidad completa tambi&eacute;n podr&iacute;a ser igual de perjudicial para las mujeres, aunque de otra forma, decidiendo por ejemplo en sus preferencias reproductivas.</p>
<h2>Conclusiones</h2>
<p>Despu&eacute;s de analizar todo esto, lo primero que se nos puede venir a la cabeza es que la privacidad es un concepto poco definido, y a la vez, asumido como algo "evidente" en muchas culturas (en particular, la occidental que ser&aacute; la m&aacute;s pr&oacute;xima a los lectores de este blog). Es muy curiosa la historia que ha habido hasta llegar aqu&iacute; y ser&aacute; interesante ver dentro de 50 a&ntilde;os c&oacute;mo evoluciona.</p>]]></description>
                <comments>https://blog.adrianistan.eu/historia-privacidad</comments>
                <pubDate>Sun, 24 Nov 2019 23:28:42 +0000</pubDate>
            </item>
        
            <item>
                <title>Ponencia Linux y Tapas 2019: &quot;Web 3 0: redes descentralizadas&quot;</title>
                <link>https://blog.adrianistan.eu/linux-tapas-web-30-redes-descentralizadas</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/linux-tapas-web-30-redes-descentralizadas</guid>
                <description><![CDATA[<p>El pasado 19 de octubre, tuvo lugar en la ciudad de Le&oacute;n el evento <a href="https://linuxytapas.wordpress.com/">Linux y Tapas 2019</a>. Era mi primera vez en este evento y encima, adem&aacute;s decid&iacute; dar una ponencia. El evento en s&iacute; me gust&oacute; mucho. Primero se queda en la catedral de Le&oacute;n y luego nos dirigimos todos juntos al barrio h&uacute;medo a comer a base de tapas. Nunca hab&iacute;a estado en el barrio h&uacute;medo y me sorprendi&oacute; muy gratamente. Durante este tiempo puedes hablar con el resto de asistentes. Conoc&iacute; a mucha gente interesante y pudimos hablar de gran cantidad de temas diferentes, eso s&iacute;, en su mayor&iacute;a temas que no podr&iacute;as sacar en conversaciones con tus amigos no t&eacute;cnicos.</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://files.adrianistan.eu/LyT.png" alt="" width="300" /></p>
<p>Posteriormente fueron las charlas en la<a href="https://www.sierrapambley.org/"> fundaci&oacute;n Sierra Pambley</a>, un sitio muy bien situado. All&iacute; tuvimos charlas muy interesantes sobre la educaci&oacute;n (hubo dos, aunque totalmente diferentes), sobre ciberseguridad y una sobre Raspberry Pi a cargo de <a href="https://www.atareao.es/">El Atareao</a>, a qui&eacute;n se le premi&oacute; por su acitividad en las redes y en el software libre durante este tiempo. Tambi&eacute;n estuvo mi charla, la cu&aacute;l fue grabada y os dejo aqu&iacute; para que la disfrut&eacute;is. Pod&eacute;is descargar el fichero ODP en <a href="http://ppt.adrianistan.eu/#Web%203%200:%20redes%20descentralizadas:%20Linux%20y%20Tapas%202019">mi repositorio de presentaciones</a>.</p>
<p>Una vez m&aacute;s, muchas gracias a todos los participantes y organizadores del evento. &iexcl;Espero que nos veamos el a&ntilde;o que viene!</p>
<p style="text-align: center;"><iframe src="https://www.youtube.com/embed/uxOhmSSV-Q0" width="560" height="315" frameborder="0" allowfullscreen=""></iframe></p>
<p><a href="http://ppt.adrianistan.eu/#Web%203%200:%20redes%20descentralizadas:%20Linux%20y%20Tapas%202019">Ver presentaci&oacute;n en mi repositorio</a></p>]]></description>
                <comments>https://blog.adrianistan.eu/linux-tapas-web-30-redes-descentralizadas</comments>
                <pubDate>Fri, 01 Nov 2019 16:54:42 +0000</pubDate>
            </item>
        
            <item>
                <title>Crónica Neuronal: matrices de expresión genética para leucemia</title>
                <link>https://blog.adrianistan.eu/cronica-neuornal-matrices-expresion-genetica-leucemia</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/cronica-neuornal-matrices-expresion-genetica-leucemia</guid>
                <description><![CDATA[<p>Bienvenidos a un nuevo episodio de la serie <strong>Cr&oacute;nica</strong> <strong>Neuronal</strong>. Hoy vamos a tocar un problema del campo de la bioinform&aacute;tica. En concreto, vamos a usar matrices de expresi&oacute;n gen&eacute;tica para identificar si un paciente de leucemia la tiene de tipo ALL o de tipo AML. <a href="https://www.webmd.com/cancer/lymphoma/leukemia-all-vs-aml#1">Ambas leucemias tienen s&iacute;ntomas muy parecidos</a> y es interesante poder encontrar un modelo de aprendizaje autom&aacute;tico que pueda distinguirlas.</p>
<h2>&iquest;Qu&eacute; es una matriz de expresi&oacute;n gen&eacute;tica?</h2>
<p>La expresi&oacute;n gen&eacute;tica es el grado en que un gen se manifiesta en la formaci&oacute;n de una prote&iacute;na, que luego tiene efectos en el organismo. Este grado se mide a trav&eacute;s de la presencia de mRNA, aunque no es exactamente proporcional, suele ser adecuado en muchas ocasiones.&nbsp;&nbsp;</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://files.adrianistan.eu/MatrizExpresionGenetica.png" alt="" /></p>
<p>Una matriz de expresi&oacute;n gen&eacute;tica no es m&aacute;s que un conjunto de muestras (samples), donde se analiza la expresi&oacute;n gen&eacute;tica de muchos genes. Cada casilla representa la expresi&oacute;n gen&eacute;tica de un gen en concreto en un individuo en particular. La idea es, con esta informaci&oacute;n, obtener ciertos patrones de genes que nos ayuden y nos den pistas en la investigaci&oacute;n de enfermedades. Tambi&eacute;n nos puede servir para diagnosticar a individuos nuevos enfermedades, as&iacute; como su estado y poder as&iacute; obtener mejores tratamientos.</p>
<p>En general, este tipo de datos son complejos de analizar por aprendizaje autom&aacute;tico, ya que por lo general existen muy pocos individuos y muchos genes a tener en cuenta. Hay una historia, no demostrada todav&iacute;a, relacionada con las farmac&eacute;uticas que son quienes generan estos ficheros, dice que estas no dan todos los datos que tienen para esforzar a los investigadores a trabajar y luego cuando publican resultados, estas farmac&eacute;uticas los pueden comprobar con mucha m&aacute;s facilidad. Un lugar de donde se pueden conseguir matrices de expresi&oacute;n gen&eacute;tico es el archivo<a href="https://www.ebi.ac.uk/arrayexpress/"> ArrayExpress</a> mantenido por el European Bioinformatics Institute.</p>
<h2>Vistazo a los datos</h2>
<p>Para esta cr&oacute;nica neuronal, voy a usar <a href="https://www.cs.waikato.ac.nz/ml/weka/">Weka</a>, el software de aprendizaje autom&aacute;tico de la Universidad de Waikato (Nueva Zelanda). Es un programa hecho en Java que funciona en Windows, MacOS y Linux.</p>
<p>En este caso, el fichero de datos del que parto ya est&aacute; en formato ARFF (el nativo de Weka, es similar a CSV pero hipervitaminado) y normalizados (escalado). Una inspecci&oacute;n r&aacute;pida nos hace ver que hay 7129 atributos diferentes, sin nombre, solo est&aacute;n numerados (son los genes). Hay una clase, de tipo binario: ALL y AML. Es decir, para cada paciente de leucemia se nos da su expresi&oacute;n gen&eacute;tica de 7129 genes y se nos da su tipo de leucemia real. Solo tenemos 72 pacientes para entrenar.</p>
<p><img src="https://files.adrianistan.eu/WekaPre.png" alt="" /></p>
<p>Vamos a usar validaci&oacute;n cruzada con 10 pliegues para el test de los modelos. Vamos a probar los siguientes algoritmos: J48, NaiveBayes, IBK1, Regresi&oacute;n Log&iacute;stica, Perceptr&oacute;n Multicapa con una capa oculta de 10 neuronas y SVM con kernel lineal.</p>
<p>Si no has usado Weka antes, los pasos para realizar esto son realmente simples. Dir&iacute;gete a la pesta&ntilde;a "Classify". Una vez all&iacute; clica sobre "Choose" y elige el algoritmo de entre las diferentes carpetas.</p>
<p><img src="https://files.adrianistan.eu/WekaSelect.png" alt="" /></p>
<p>Si el algoritmo tuviese par&aacute;metros de ajuste, se har&iacute;a clic sobre el nombre del algoritmo en negrita una vez seleccionado.</p>
<p><img src="https://files.adrianistan.eu/WekaOptions.png" alt="" /></p>
<p>Pero en la gran mayor&iacute;a de los algoritmos usaremos los ajustes por defecto. Los resultados son los siguientes:</p>
<table style="border-collapse: collapse; width: 100%;" border="1">
<tbody>
<tr>
<td style="width: 50%;">Algoritmo</td>
<td style="width: 50%;">Tasa de Acierto</td>
</tr>
<tr>
<td style="width: 50%;">J48</td>
<td style="width: 50%;">0.7916</td>
</tr>
<tr>
<td style="width: 50%;">NaiveBayes</td>
<td style="width: 50%;">0.9861</td>
</tr>
<tr>
<td style="width: 50%;">IBK1</td>
<td style="width: 50%;">0.8472</td>
</tr>
<tr>
<td style="width: 50%;">Regresi&oacute;n Log&iacute;stica</td>
<td style="width: 50%;">0.9027</td>
</tr>
<tr>
<td style="width: 50%;">MLP (H10)</td>
<td style="width: 50%;">0.9722</td>
</tr>
<tr>
<td style="width: 50%;">SVM (lineal)</td>
<td style="width: 50%;">0.9861</td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
<p>Los mejores resultados los obtienen SVM y NaiveBayes. Esto &uacute;ltimo nos puede decir que no hay muchos genes correlacionados, sino que son independientes entre s&iacute;. Regresi&oacute;n Log&iacute;stica y MLP han tardado considerablemente m&aacute;s que el resto.</p>
<p>Ahora vamos a ver qu&eacute; atributos son m&aacute;s <strong>importantes</strong>. Es l&oacute;gico pensar que muchos de estos atributos (genes) son superfluos, y no afectan en el diagn&oacute;stico del tipo de leucemia.&nbsp;</p>
<p>A continuaci&oacute;n vamos a analizar qu&eacute; genes son m&aacute;s relevantes para la tarea de construir un clasificador entre los tipos de leucemia.</p>
<p>Para ello primero usaremos m&eacute;todos de&nbsp;<strong>filtrado</strong>.</p>
<h2>Filtrado</h2>
<p>Los m&eacute;todos de filtrado se basan en diferentes heur&iacute;sticas que nos permiten seleccionar un conjunto de atributos relevantes. La mayor&iacute;a de estas heur&iacute;sticas elaboran un r&aacute;nking, dentro de las cu&aacute;les podemos elegir el n&uacute;mero de atributos que queramos. Los algoritmos para el filtrado que vamos a probar son: Incertidumbre sim&eacute;trica, ReliefF, SVM recursivo y CfsSubset (este &uacute;ltimo no genera ranking).</p>
<table width="100%" cellspacing="0" cellpadding="4">
<tbody>
<tr valign="top">
<td width="50%">
<p>Algoritmo</p>
</td>
<td width="50%">
<p>Atributos (4)</p>
</td>
</tr>
<tr valign="top">
<td width="50%">
<p>Incertidumbre sim&eacute;trica</p>
</td>
<td width="50%">
<p>0.74 1834 attribute1834</p>
<p>0.74 4847 attribute4847</p>
<p>0.737 1882 attribute1882</p>
<p>0.734 3252 attribute3252</p>
</td>
</tr>
<tr valign="top">
<td width="50%">
<p>ReliefF</p>
</td>
<td width="50%">
<p>0.26503581944 3252 attribute3252</p>
<p>0.25909453333 4196 attribute4196</p>
<p>0.21482608611 1779 attribute1779</p>
<p>0.19528160278 4847 attribute4847</p>
</td>
</tr>
<tr valign="top">
<td width="50%">
<p>SVM</p>
</td>
<td width="50%">
<p>7129 1882 attribute1882</p>
<p>7128 1834 attribute1834</p>
<p>7127 1779 attribute1779</p>
<p>7126 1796 attribute1796</p>
</td>
</tr>
<tr valign="top">
<td width="50%">
<p>CfsSubset</p>
</td>
<td width="50%">
<p>No se pudo calcular</p>
</td>
</tr>
</tbody>
</table>
<p>Si nos quedamos solo con cuatro atributos, los resultados son los siguientes. Estos resultados se obtienen en Weka en la pesta&ntilde;a de Selecci&oacute;n de Atributos. Eligiendo el algoritmo y un m&eacute;todo de b&uacute;squeda (Ranker, o en su defecto, GreedyStepwise).</p>
<p><img src="https://files.adrianistan.eu/WekaSelectAttributes.png" alt="" width="1365" height="767" /></p>
<p>Podemos repetir el procedimiento con 8, 16 y 32 atributos. No os voy a poner los atributos, pero s&iacute; vamos a ver como se comportan.</p>
<p>Vemos que en general hay genes que se repiten entre m&eacute;todos, como el 1779 o el 4847. Estos genes pueden ser determinantes para diagnosticar los diferentes tipos de leucemia.</p>
<table width="665" cellspacing="0" cellpadding="4">
<tbody>
<tr valign="top">
<td width="105">
<p>&nbsp;</p>
</td>
<td width="68">
<p>J48</p>
</td>
<td width="87">
<p>NaiveBayes</p>
</td>
<td width="87">
<p>IBK1</p>
</td>
<td width="87">
<p>Reg. Log</p>
</td>
<td width="87">
<p>MLP (H10)</p>
</td>
<td width="86">
<p>SVM (lineal)</p>
</td>
</tr>
<tr valign="top">
<td width="105">
<p>Incertidumbre sim&eacute;trica (4)</p>
</td>
<td width="68">
<p>0.9027</p>
</td>
<td width="87">
<p>0.9444</p>
</td>
<td width="87">
<p>0.9166</p>
</td>
<td width="87">
<p>0.9444</p>
</td>
<td width="87">
<p>0.9305</p>
</td>
<td width="86">
<p>0.9305</p>
</td>
</tr>
<tr valign="top">
<td width="105">
<p>Incertidumbre sim&eacute;trica (8)</p>
</td>
<td width="68">
<p>0.8472</p>
</td>
<td width="87">
<p>0.9444</p>
</td>
<td width="87">
<p>0.9305</p>
</td>
<td width="87">
<p>0.9444</p>
</td>
<td width="87">
<p>0.9583</p>
</td>
<td width="86">
<p>0.9305</p>
</td>
</tr>
<tr valign="top">
<td width="105">
<p>Incertidumbre sim&eacute;trica (16)</p>
</td>
<td width="68">
<p>0.8472</p>
</td>
<td width="87">
<p>0.9583</p>
</td>
<td width="87">
<p>0.9583</p>
</td>
<td width="87">
<p>0.9583</p>
</td>
<td width="87">
<p>0.9861</p>
</td>
<td width="86">
<p>0.9444</p>
</td>
</tr>
<tr valign="top">
<td width="105">
<p>Incertidumbre sim&eacute;trica (32)</p>
</td>
<td width="68">
<p>0.8611</p>
</td>
<td width="87">
<p>0.9583</p>
</td>
<td width="87">
<p>0.9583</p>
</td>
<td width="87">
<p>0.9583</p>
</td>
<td width="87">
<p>0.9722</p>
</td>
<td width="86">
<p>0.9722</p>
</td>
</tr>
</tbody>
</table>
<p><br /><br /></p>
<p>Con solo 4 atributos seleccionados por incertidumbre sim&eacute;trica, obtenemos muy buenos resultados con algunos m&eacute;todos: NaiveBayes y Regresi&oacute;n Log&iacute;stica.</p>
<table width="100%" cellspacing="0" cellpadding="4">
<tbody>
<tr valign="top">
<td width="14%">
<p>&nbsp;</p>
</td>
<td width="14%">
<p>J48</p>
</td>
<td width="14%">
<p>NaiveBayes</p>
</td>
<td width="14%">
<p>IBK1</p>
</td>
<td width="14%">
<p>Reg. Log</p>
</td>
<td width="14%">
<p>MLP (H10)</p>
</td>
<td width="14%">
<p>SVM (lineal)</p>
</td>
</tr>
<tr valign="top">
<td width="14%">
<p>ReliefF (4)</p>
</td>
<td width="14%">
<p>0.9166</p>
</td>
<td width="14%">
<p>0.9166</p>
</td>
<td width="14%">
<p>0.8888</p>
</td>
<td width="14%">
<p>0.9444</p>
</td>
<td width="14%">
<p>0.9444</p>
</td>
<td width="14%">
<p>0.9444</p>
</td>
</tr>
<tr valign="top">
<td width="14%">
<p>ReliefF (8)</p>
</td>
<td width="14%">
<p>0.8611</p>
</td>
<td width="14%">
<p>0.9722</p>
</td>
<td width="14%">
<p>0.9444</p>
</td>
<td width="14%">
<p>0.9027</p>
</td>
<td width="14%">
<p>0.9305</p>
</td>
<td width="14%">
<p>0.9444</p>
</td>
</tr>
<tr valign="top">
<td width="14%">
<p>ReliefF (16)</p>
</td>
<td width="14%">
<p>0.8472</p>
</td>
<td width="14%">
<p>0.9444</p>
</td>
<td width="14%">
<p>0.9305</p>
</td>
<td width="14%">
<p>0.9305</p>
</td>
<td width="14%">
<p>0.9305</p>
</td>
<td width="14%">
<p>0.9722</p>
</td>
</tr>
<tr valign="top">
<td width="14%">
<p>ReliefF (32)</p>
</td>
<td width="14%">
<p>0.8333</p>
</td>
<td width="14%">
<p>0.9583</p>
</td>
<td width="14%">
<p>0.9305</p>
</td>
<td width="14%">
<p>0.9583</p>
</td>
<td width="14%">
<p>0.9722</p>
</td>
<td width="14%">
<p>0.9722</p>
</td>
</tr>
</tbody>
</table>
<p><br /><br /></p>
<p>En este caso con 4 y 32 atributos se obtienen resultados similares al m&eacute;todo anterior. No obstante, los algoritmos con buen desempe&ntilde;o son diferentes.</p>
<table width="100%" cellspacing="0" cellpadding="4">
<tbody>
<tr valign="top">
<td width="14%">
<p>&nbsp;</p>
</td>
<td width="14%">
<p>J48</p>
</td>
<td width="14%">
<p>NaiveBayes</p>
</td>
<td width="14%">
<p>IBK1</p>
</td>
<td width="14%">
<p>Reg. Log.</p>
</td>
<td width="14%">
<p>MLP (H10)</p>
</td>
<td width="14%">
<p>SVM (lineal)</p>
</td>
</tr>
<tr valign="top">
<td width="14%">
<p>SVM (4)</p>
</td>
<td width="14%">
<p>0.9166</p>
</td>
<td width="14%">
<p>0.9722</p>
</td>
<td width="14%">
<p>1</p>
</td>
<td width="14%">
<p>1</p>
</td>
<td width="14%">
<p>1</p>
</td>
<td width="14%">
<p>1</p>
</td>
</tr>
<tr valign="top">
<td width="14%">
<p>SVM (8)</p>
</td>
<td width="14%">
<p>0.9166</p>
</td>
<td width="14%">
<p>0.9722</p>
</td>
<td width="14%">
<p>0.9861</p>
</td>
<td width="14%">
<p>0.9861</p>
</td>
<td width="14%">
<p>1</p>
</td>
<td width="14%">
<p>1</p>
</td>
</tr>
<tr valign="top">
<td width="14%">
<p>SVM (16)</p>
</td>
<td width="14%">
<p>0.875</p>
</td>
<td width="14%">
<p>1</p>
</td>
<td width="14%">
<p>1</p>
</td>
<td width="14%">
<p>1</p>
</td>
<td width="14%">
<p>1</p>
</td>
<td width="14%">
<p>1</p>
</td>
</tr>
<tr valign="top">
<td width="14%">
<p>SVM (32)</p>
</td>
<td width="14%">
<p>0.8472</p>
</td>
<td width="14%">
<p>0.9861</p>
</td>
<td width="14%">
<p>1</p>
</td>
<td width="14%">
<p>1</p>
</td>
<td width="14%">
<p>1</p>
</td>
<td width="14%">
<p>1</p>
</td>
</tr>
</tbody>
</table>
<p><br /><br /></p>
<p>La selecci&oacute;n por SVM es excelente, ya que con solo 4 genes, logra tasas de acierto del 100% en 4 m&eacute;todos diferentes. Seguramente estos 4 genes tengan la relaci&oacute;n m&aacute;s directa con la enfermedad.</p>
<h2>Envoltorio</h2>
<p>Otro m&eacute;todo de selecci&oacute;n de atributos es el m&eacute;todo del envoltorio. Aqu&iacute; se elige un algoritmo de clasificaci&oacute;n y seg&uacute;n su desempe&ntilde;o se van descartando atributos irrelevantes. Idealmente, el mismo algoritmo envuelto es el que luego se usa en clasificaci&oacute;n, pero aqu&iacute; probaremos todos con todos.</p>
<table width="100%" cellspacing="0" cellpadding="4">
<tbody>
<tr valign="top">
<td width="50%">
<p>Algoritmo</p>
</td>
<td width="50%">
<p>Atributos</p>
</td>
</tr>
<tr valign="top">
<td width="50%">
<p>J48</p>
</td>
<td width="50%">
<p>Selected attributes: 4847 : 1</p>
<p>attribute4847</p>
</td>
</tr>
<tr valign="top">
<td width="50%">
<p>NaiveBayes</p>
</td>
<td width="50%">
<p>Selected attributes: 6,461,760,6615 : 4</p>
<p>attribute6</p>
<p>attribute461</p>
<p>attribute760</p>
<p>attribute6615</p>
</td>
</tr>
<tr valign="top">
<td width="50%">
<p>IBK1</p>
</td>
<td width="50%">
<p>Selected attributes: 28,1834,3258,3549 : 4</p>
<p>attribute28</p>
<p>attribute1834</p>
<p>attribute3258</p>
<p>attribute3549</p>
</td>
</tr>
<tr valign="top">
<td width="50%">
<p>Reg. Log.</p>
</td>
<td width="50%">
<p>Selected attributes: 202,1882,6049 : 3</p>
<p>attribute202</p>
<p>attribute1882</p>
<p>attribute6049</p>
</td>
</tr>
<tr valign="top">
<td width="50%">
<p>MLP (H10)</p>
</td>
<td width="50%">
<p>Selected attributes: 1796,1834,2288 : 3</p>
<p>attribute1796</p>
<p>attribute1834</p>
<p>attribute2288</p>
</td>
</tr>
<tr valign="top">
<td width="50%">
<p>SVM (lineal)</p>
</td>
<td width="50%">
<p>Selected attributes: 162,1796,2111,3252 : 4</p>
<p>attribute162</p>
<p>attribute1796</p>
<p>attribute2111</p>
<p>attribute3252</p>
</td>
</tr>
</tbody>
</table>
<p>En general la mayor&iacute;a de envoltorios se decantan por 3 o 4 atributos, pero muchas veces diferentes. J48 se decanta por solo un &uacute;nico atributo.</p>
<table width="100%" cellspacing="0" cellpadding="4">
<tbody>
<tr valign="top">
<td width="14%">
<p>&nbsp;</p>
</td>
<td width="14%">
<p>J48</p>
</td>
<td width="14%">
<p>NaiveBayes</p>
</td>
<td width="14%">
<p>IBK1</p>
</td>
<td width="14%">
<p>Reg. Log.</p>
</td>
<td width="14%">
<p>MLP (H10)</p>
</td>
<td width="14%">
<p>SVM (lineal)</p>
</td>
</tr>
<tr valign="top">
<td width="14%">
<p>WrapJ48</p>
</td>
<td width="14%">
<p>0.9444</p>
</td>
<td width="14%">
<p>0.9305</p>
</td>
<td width="14%">
<p>0.9166</p>
</td>
<td width="14%">
<p>0.9305</p>
</td>
<td width="14%">
<p>0.9444</p>
</td>
<td width="14%">
<p>0.8611</p>
</td>
</tr>
<tr valign="top">
<td width="14%">
<p>WrapNB</p>
</td>
<td width="14%">
<p>0.9166</p>
</td>
<td width="14%">
<p>0.9861</p>
</td>
<td width="14%">
<p>0.8333</p>
</td>
<td width="14%">
<p>0.9583</p>
</td>
<td width="14%">
<p>0.9583</p>
</td>
<td width="14%">
<p>0.8194</p>
</td>
</tr>
<tr valign="top">
<td width="14%">
<p>WrapIBK1</p>
</td>
<td width="14%">
<p>0.8888</p>
</td>
<td width="14%">
<p>0.9861</p>
</td>
<td width="14%">
<p>1</p>
</td>
<td width="14%">
<p>0.9444</p>
</td>
<td width="14%">
<p>0.9444</p>
</td>
<td width="14%">
<p>0.8888</p>
</td>
</tr>
<tr valign="top">
<td width="14%">
<p>WrapRegLog</p>
</td>
<td width="14%">
<p>0.9583</p>
</td>
<td width="14%">
<p>0.9861</p>
</td>
<td width="14%">
<p>0.9444</p>
</td>
<td width="14%">
<p>1</p>
</td>
<td width="14%">
<p>0.9722</p>
</td>
<td width="14%">
<p>0.8333</p>
</td>
</tr>
<tr valign="top">
<td width="14%">
<p>WrapSVM</p>
</td>
<td width="14%">
<p>0.8888</p>
</td>
<td width="14%">
<p>0.9305</p>
</td>
<td width="14%">
<p>0.9166</p>
</td>
<td width="14%">
<p>0.9444</p>
</td>
<td width="14%">
<p>0.9861</p>
</td>
<td width="14%">
<p>0.9722</p>
</td>
</tr>
</tbody>
</table>
<p>En general el comportamiento es bueno con el m&eacute;todo que hizo de envoltorio, y peor en el resto. Sorprende MLP (H10) que logra resultados muy decentes con todas las selecciones y SVM, que da resultados muy mediocres salvo con su envoltorio.</p>
<p>Si comparamos m&eacute;todos de filtro con m&eacute;todos de envoltorio, nos encontramos con que hemos visto m&eacute;todos de filtro muy superiores como SVM, si bien el m&eacute;todo de envoltorio con el mismo algoritmo en ambas etapas es tambi&eacute;n una opci&oacute;n muy interesante.</p>
<p>Por norma general, los m&eacute;todos de envoltorio son mejores pero tambi&eacute;n mucho m&aacute;s costosos de ejecutar. Es por ello, que ante las selecciones de filtrado obtenidas antes, normalmente nos quedar&iacute;amos con esos atributos. En este caso, y por mera curiosidad, hemos continuado con los m&eacute;todos de envoltorio, mucho m&aacute;s lentos.</p>
<p>Si tuvi&eacute;semos que construir un sistema hoy para detectar entre los tipos de leucemia, la opci&oacute;n ser&iacute;a eliminar atributos mediante SVM recursivo y construir el clasificador bien con SVM o con otro de los algoritmos que aciertan siempre, ya que no tenemos otros criterios todav&iacute;a para discernir.</p>
<p>Espero que os haya gustado este episodio de Cr&oacute;nica Neuronal. En este caso, no he usado Python sino que he usado Weka, pero espero que aporte perspectiva y ayude a la gente a conocer por un lado los arrays de expresi&oacute;n gen&eacute;tica, y por otro, los algoritmos de selecci&oacute;n de atributos relevantes, sin entrar en el detalle de como funcionan.</p>
<p>&nbsp;</p>]]></description>
                <comments>https://blog.adrianistan.eu/cronica-neuornal-matrices-expresion-genetica-leucemia</comments>
                <pubDate>Mon, 21 Oct 2019 22:33:58 +0000</pubDate>
            </item>
        
            <item>
                <title>Tu código va a morir</title>
                <link>https://blog.adrianistan.eu/tu-codigo-va-a-morir</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/tu-codigo-va-a-morir</guid>
                <description><![CDATA[<p>Memento mori. En la Antigua Roma, cuando un general ven&iacute;a victorioso de una campa&ntilde;a, se le organizaba un desfile por las calles de Roma. Lanzaban v&iacute;tores y proclamas en su honor. No obstante, al lado de &eacute;l hab&iacute;a un siervo, que le iba repitiendo constantemente <em>las limitaciones de la naturaleza humana, con el fin de impedir que incurriese en la soberbia y pretendiese, a la manera de un dios omnipotente, usar su poder ignorando las limitaciones impuestas por la ley y la costumbre. (Wikipedia)</em>. Esto se resume en un aforismo, Memento Mori, "Recuerda que vas a morir". Este chorro de realidad nos devuelve a la humildad, y a la vez, si uno lo piensa, nos deja entrever que los problemas de ahora no son tan importantes como muchas veces creemos que son.</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://files.adrianistan.eu/QuoVadis.jpg" alt="" /></p>
<p style="text-align: center;"><em>Fotograma de la pel&iacute;cula Quo Vadis, el esclavo le repite al general "Memento Mori". Personalmente, fue al ver esta pel&iacute;cula cuando supe de la existencia de esta tradici&oacute;n.</em></p>
<p style="text-align: left;">Hace unas semanas estuve en la Tech Party de Madrid. All&iacute; escuch&eacute; algo que ha retumbado en mi mente, por ser f&aacute;cil de entender y a la vez, por las consecuencias que conlleva. <strong>Tu c&oacute;digo va a morir</strong>.</p>
<p style="text-align: left;">Es cierto, por muy bueno que sea tu c&oacute;digo, alg&uacute;n d&iacute;a morir&aacute;. Por muy malo que sea, tambi&eacute;n morir&aacute; (aunque quiz&aacute; no lo suficientemente r&aacute;pido dir&aacute;n algunos). No te creas que por haber escrito el mejor c&oacute;digo de la historia, mereces un reconocimiento especial. No creas que por usar ese framework de moda con ese nuevo lenguaje, vas autom&aacute;ticamente a ser mejor, alguien digno de aplauso y recuerdo colectivo. Al final el c&oacute;digo morir&aacute; y algo m&aacute;s moderno lo reemplazar&aacute; igualmente, quiz&aacute; incluso ya no exista la raz&oacute;n de ser de ese software.</p>
<p style="text-align: left;">Tampoco pienses que el c&oacute;digo debe ser perfecto, busca la calidad, evidentemente, pero al&eacute;jate del perfeccionismo. En ocasiones <a href="http://blog.koalite.com/2019/05/no-corrijas-ese-bug/">ciertos bugs es mejor que esten presentes de forma cr&oacute;nica en un software</a>, como si de una enfermedad, en ocasiones molesta pero leve, se tratase. Tu c&oacute;digo debe madurar a buen ritmo, como un beb&eacute;, que poco a poco asume m&aacute;s responsabilidades y tareas, sin que ello le ocasione un desequilibrio interno (algo t&iacute;pico de familias desestructuradas). "Buen ritmo", eso quiere decir que no podr&aacute;s trabajar en ello indefinidamente, si quieres que sea &uacute;til, alg&uacute;n d&iacute;a deber&aacute;s poder decir que est&aacute; listo. Y si no, piensa que t&uacute; tambi&eacute;n morir&aacute;s y no podr&aacute;s acabarlo.&nbsp;</p>
<p style="text-align: left;">En ocasiones, la gente dice: "yo morir&eacute;, pero es software libre, alguien lo tomar&aacute;", "la empresa lo seguir&aacute; usando". Y aunque supongo que podr&iacute;a darse el caso, la gran mayor&iacute;a de los sistemas, de las comunidades, acaban siendo reemplazadas por otras. Es el ciclo vital. Recuerda que no eres excepcional, lo m&aacute;s probable es que lo que hagas no tenga un gran impacto. Ser&aacute; olvidado. No le des m&aacute;s importancia al c&oacute;digo de la que tiene.</p>
<p style="text-align: left;">Y esto no lo digo para desanimarte o para hacerte creer que lo que haces no vale nada. Al contrario, te lo digo para que te liberes. Para romper las cadenas que te ata, muchas veces de forma subconsciente, ese deseo de perdurar en la eternidad. Ese deseo de destacar, de producir el c&oacute;digo perfecto. Pero recuerda, al final, somos m&aacute;s bien como esas l&aacute;grimas, que se perder&aacute;n en la lluvia. Y no habr&aacute; que atormentarse por ello.</p>]]></description>
                <comments>https://blog.adrianistan.eu/tu-codigo-va-a-morir</comments>
                <pubDate>Wed, 25 Sep 2019 15:41:29 +0000</pubDate>
            </item>
        
            <item>
                <title>Terraform, infraestructura como código declarativo</title>
                <link>https://blog.adrianistan.eu/terraform-cloud-declarativa</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/terraform-cloud-declarativa</guid>
                <description><![CDATA[<p>Como alguno de los lectores ya sabr&aacute;, he empezado a trabajar este verano en <a href="https://www.telefonica.com">Telef&oacute;nica</a> como becario. El proyecto donde estoy es 100% cloud y para ello usamos muchas herramientas. Hoy os vengo a hablar de <a href="https://www.terraform.io/">Terraform</a>, una herramienta que nos permite declarar la infraestructura como c&oacute;digo y de forma declarativa.</p>
<h2>&iquest;Por qu&eacute;?</h2>
<p>Antiguamente, cuando se desplegaban servicios, o una de dos: o se usaban servidores f&iacute;sicos, que se ten&iacute;an que comprar, instalar, mantener, ... o se usaba un hosting compartido, el cu&aacute;l para ciertas cosas puede estar bien, pero tiene muchas limitaciones. Hoy en d&iacute;a, gracias a los proveedores cloud, podemos alquilar infraestructura bajo demanda. Terraform es una herramienta para dejar por escrito toda la infraestructura en la nube que necesita nuestro servicio y crearla/modificarla seg&uacute;n modifiquemos los archivos. Adem&aacute;s es declarativo, lo que quiere decir que tenemos que expresarnos en c&oacute;digo seg&uacute;n lo que queremos obtener, no c&oacute;mo, por tanto tambi&eacute;n cumple el papel de documentaci&oacute;n. Piensa en Terraform como en los planos de un edificio. Nosotros definimos las vigas, paredes, tuber&iacute;as sobre el papel y los obreros se encargan de construirlo. Terraform es eso, nosotros definimos m&aacute;quinas, bases de datos y el programa se encarga de construir la infraestructura en la nube.</p>
<h2>Funcionamiento interno</h2>
<p>Terraform tiene varios componentes que vamos a definir primero:</p>
<ul>
<li>recurso: cualquier cosa que exista en la nube (o fuera) sobre la que Terraform tenga control de creaci&oacute;n, modificaci&oacute;n y destrucci&oacute;n</li>
<li>datos: puntos donde podemos obtener informaci&oacute;n para nuestro c&oacute;digo.</li>
<li>salidas: valores generados durante la ejecuci&oacute;n del plan y que pueden ser de inter&eacute;s (direcciones IP, contrase&ntilde;as, ...)</li>
<li>c&oacute;digo: aqu&iacute; es donde vamos a escribir la infraestructura que necesitamos (m&aacute;quinas virtuales, cl&uacute;steres de Kubernetes, balanceadores de carga, discos, ...). Representa el estado &oacute;ptimo del sistema.</li>
<li>estado: aqu&iacute; Terraform almacena la infraestructura real, con mucha m&aacute;s informaci&oacute;n que la que existe en el c&oacute;digo. Adem&aacute;s le sirve a Terraform para acordarse entre ejecuciones de la infraestructura que controla</li>
<li>proveedores: los recursos y los datos necesitan de un plugin que conecte la nube con Terraform. Terraform contiene multitud de proveedores: Azure, AWS, Google Cloud, Netlify, OpenStack, Kubernetes, Let's Encrypt, Helm, Digital Ocean, OVH, Alibabba Cloud, Oracle Cloud, PostgreSQL, Triton, VMware vSphere, Heroku, Linode, Packet, 1&amp;1 <a href="https://www.terraform.io/docs/providers/">y muchos m&aacute;s</a>. Adicionalmente, existen proveedores creados por terceros.</li>
<li>plan: se trata del paso en cual Terraform compara el estado con el c&oacute;digo y encuentra diferencias entre el estado real y el &oacute;ptimo. Opcionalmente el estado se puede actualizar antes (lo hace por defecto) para tener un estado lo m&aacute;s real posible. Cuando tengamos un plan lo podemos aplicar y entonces Terraform modificar&aacute; la infraestructura.</li>
</ul>
<h2>Una m&aacute;quina virtual sencilla</h2>
<p>Para este ejemplo voy a usar Microsoft Azure, pero se pueden usar otros proveedores similares haciendo los cambios adecuados. Cualquier estudiante puede pedir el <a href="https://education.github.com/pack">GitHub Education Pack</a> que regala 100$ para gastar en Azure.</p>
<pre><code>
provider "azurerm" {
  version = "~&gt; 1.28.0"
}

provider "random" {}

resource "random_string" "username" {
  length = 12
  special = false
}

resource "random_password" "password" {
  length = 12
  special = true
}

resource "azurerm_resource_group" "main" {
  name     = "blog"
  location = "France Central"
}

resource "azurerm_public_ip" "main" {
  name                = "blog-ip"
  location            = "${azurerm_resource_group.main.location}"
  resource_group_name = "${azurerm_resource_group.main.name}"
  allocation_method   = "Static"
}

resource "azurerm_virtual_network" "main" {
  name                = "blog-network"
  address_space       = ["10.0.0.0/16"]
  location            = "${azurerm_resource_group.main.location}"
  resource_group_name = "${azurerm_resource_group.main.name}"
}

resource "azurerm_subnet" "internal" {
  name                 = "blog-internal"
  resource_group_name  = "${azurerm_resource_group.main.name}"
  virtual_network_name = "${azurerm_virtual_network.main.name}"
  address_prefix       = "10.0.2.0/24"
}

resource "azurerm_network_interface" "main" {
  name                = "blog-nic"
  location            = "${azurerm_resource_group.main.location}"
  resource_group_name = "${azurerm_resource_group.main.name}"

  ip_configuration {
    name                          = "conf1"
    subnet_id                     = "${azurerm_subnet.internal.id}"
    private_ip_address_allocation = "Dynamic"
    public_ip_address_id = "${azurerm_public_ip.main.id}"
  }
}

resource "azurerm_virtual_machine" "main" {
  name                  = "blog-vm"
  location              = "${azurerm_resource_group.main.location}"
  resource_group_name   = "${azurerm_resource_group.main.name}"
  network_interface_ids = ["${azurerm_network_interface.main.id}"]
  vm_size               = "Standard_DS1_v2"

  storage_image_reference {
    publisher = "Canonical"
    offer     = "UbuntuServer"
    sku       = "18.04-LTS"
    version   = "latest"
  }
  storage_os_disk {
    name              = "disk-os-1"
    caching           = "ReadWrite"
    create_option     = "FromImage"
    managed_disk_type = "Standard_LRS"
  }
  os_profile {
    computer_name  = "blog"
    admin_username = "${random_string.username.result}"
    admin_password = "${random_password.password.result}"
  }
  os_profile_linux_config {
    disable_password_authentication = false
  }
}

output "login_information" {
  value = "${random_string.username.result}:${random_password.password.result}:${azurerm_public_ip.main.ip_address}"
}
</code></pre>
<p>&nbsp;</p>
<p>El c&oacute;digo es sencillo y declarativo. Definimos dos proveedores (azurerm y random) para los recursos que vamos a definir. Los recursos se identifican por el tipo de recurso y por un identificador &uacute;nico. Luego definimos usuario y contrase&ntilde;a aleatorios para la m&aacute;quina, un grupo de recursos de Azure, una direcci&oacute;n IP p&uacute;blica, la infraestructura de red y finalmente una m&aacute;quina virtual con Ubuntu. En output mostramos la IP p&uacute;blica de la m&aacute;quina, su usuario y contrase&ntilde;a, para poder conectarnos. Aqu&iacute; podr&iacute;amos pasar a usar otras herramientas como Ansible para instalar todo lo necesario en la m&aacute;quina virtual.</p>
<p>Aqu&iacute; hemos visto el uso de referencias, con el s&iacute;mbolo del d&oacute;lar. Estas referencias le permiten a Terraform construir/destruir la infraestructura en el orden correcto. Por defecto Terraform intenta realizar todas las operaciones en paralelo, salvo que necesite una informaci&oacute;n que venga de otro recurso. En ese caso, tiene que ser creado con &eacute;xito para poder proceder a la creaci&oacute;n del siguiente. Si no podemos usar referencias, podemos usar&nbsp;<strong>depends_on</strong>.</p>
<p>Hagamos un&nbsp;<strong>terraform apply</strong> para realizar un plan y aplicarlo. Adem&aacute;s veremos las salidas definidas.</p>
<p><img src="https://files.adrianistan.eu/TerraformOutput.png" alt="" /></p>
<p>Y vemos como ha sido creado en Azure con &eacute;xito.</p>
<p><img src="https://files.adrianistan.eu/PanelAzure.png" alt="" /></p>
<p>Ahora si modificamos algo desde la web y volvemos a ejecutar <strong>terraform apply</strong>, se detectar&aacute; que el estado real es diferente al &oacute;ptimo y tratar&aacute; de revertir el cambio. Tambi&eacute;n si modificamos los ficheros se intentar&aacute; modificar el entorno real.&nbsp;</p>
<h2>Variables y bucles</h2>
<p>Terraform admite datos externos y bucles. Vamos a verlo. Con variable podemos introducir datos desde variables de entorno, la CLI o un fichero .tfvars. Estas variables pueden tener un valor por defecto. Las variables se definen con -var="var_name=var_value", con las variables de entorno TF_VAR_var_name=var_value y el fichero terraform.tfvars.</p>
<p>Los bucles se pueden realizar con&nbsp;<strong>count</strong>, si cada elemento no tiene identidad (por ejemplo, el n&uacute;mero de r&eacute;plicas de una VM igual) o con&nbsp;<strong>for_each</strong> si cada elemento debe tener identidad (por ejemplo, una VM para cada pa&iacute;s).</p>
<pre><code>
provider "azurerm" {
  version = "~&gt; 1.28.0"
}

variable "name" {
  default = "adrianistan"
}

variable "country" {
  default = [
    "es",
    "ar"
  ]
}

resource "azurerm_resource_group" "main" {
  name     = "blog"
  location = "France Central"
}

resource "azurerm_app_service_plan" "main" {
  name                = "main-appserviceplan"
  location            = "${azurerm_resource_group.main.location}"
  resource_group_name = "${azurerm_resource_group.main.name}"
  kind                = "Linux"
  reserved            = true

  sku {
    tier = "Basic"
    size = "B1"
  }
}

resource "azurerm_app_service" "main" {
  for_each            = toset(var.country)
  name                = "${var.name}-${each.value}"
  location            = "${azurerm_resource_group.main.location}"
  resource_group_name = "${azurerm_resource_group.main.name}"
  app_service_plan_id = "${azurerm_app_service_plan.main.id}"

  site_config {
    linux_fx_version = "DOCKER|nginx:1.17.3"
  }
}

output "web" {<br />  value = "${formatlist("%s", [for o in azurerm_app_service.main : o.default_site_hostname])}"<br />}
</code></pre>
<p>En este ejemplo usamos un bucle for_each para generar dos Azure App Services (cargados con nginx), uno para Espa&ntilde;a y otro para Argentina.</p>
<p>Comprobamos como funciona:</p>
<p><img src="https://files.adrianistan.eu/TerraformPlan.png" alt="" /></p>
<p><img src="https://files.adrianistan.eu/TerraformApply.png" alt="" /></p>
<p><img src="https://files.adrianistan.eu/TerraformNginx.png" alt="" /></p>
<p>Espero que esta breve introducci&oacute;n a Terraform os haya resultado interesante. Se trata de un lenguaje sencillo, declarativo y donde la mayor parte de nuestros problemas vendr&aacute;n de conocer la documentaci&oacute;n de cada proveedor al dedillo.</p>
<p>&nbsp;</p>]]></description>
                <comments>https://blog.adrianistan.eu/terraform-cloud-declarativa</comments>
                <pubDate>Tue, 17 Sep 2019 20:38:10 +0000</pubDate>
            </item>
        
            <item>
                <title>Podcast sobre Docker</title>
                <link>https://blog.adrianistan.eu/podcast-docker</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/podcast-docker</guid>
                <description><![CDATA[<p>Acaba de salir el segundo episodio del podcast&nbsp;<strong>Undefined</strong>. Se trata de un podcast donde algunos conocidos hablamos de temas de inform&aacute;tica. En este episodio, dedicado a Docker, soy uno de los colaboradores as&iacute; que: &iexcl;a escucharlo todos!</p>
<p><iframe id="audio_40081198" style="border: 1px solid #EEE; box-sizing: border-box; width: 100%;" src="https://www.ivoox.com/player_ej_40081198_4_1.html?c1=ff6600" height="200" frameborder="0" scrolling="no" allowfullscreen=""></iframe></p>
<p>Todav&iacute;a estamos empezando este proyecto as&iacute; que puede haber ciertas cosas que no se sientan tan profesionales como otros podcasts, iremos mejorando, pero vuestros comentarios son importantes. &iexcl;A disfrutar!</p>]]></description>
                <comments>https://blog.adrianistan.eu/podcast-docker</comments>
                <pubDate>Mon, 19 Aug 2019 17:23:28 +0000</pubDate>
            </item>
        
            <item>
                <title>Re: Rust no es un buen reemplazo de C</title>
                <link>https://blog.adrianistan.eu/rust-no-es-buen-reemplazo-c</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/rust-no-es-buen-reemplazo-c</guid>
                <description><![CDATA[<p>Como muchos ya sab&eacute;is, pod&eacute;is contactar conmigo de muchas formas. De entre ellas,&nbsp;<em>Lector an&oacute;nimo<strong>&nbsp;</strong></em>eligi&oacute; el correo para enviarme esta pregunta:</p>
<blockquote>
<p><em>Hola.</em><br /><br /><em> Sigo tu blog desde hace alg&uacute;n tiempo y tienes un contenido interesante. </em><br /><em> Soy aficionado a la programaci&oacute;n y s&eacute; algo de Python. Estaba pensando en </em><br /><em> aprender Rust. El caso es que le&iacute; un art&iacute;culo y me gustar&iacute;a saber tu </em><br /><em> opini&oacute;n, o sugerencia para que escribas algo en tu blog.</em><br /><br /><em> <a href="https://drewdevault.com/2019/03/25/Rust-is-not-a-good-C-replacement.html" target="_blank" rel="noopener noreferrer" data-saferedirecturl="https://www.google.com/url?q=https://drewdevault.com/2019/03/25/Rust-is-not-a-good-C-replacement.html&amp;source=gmail&amp;ust=1566054755660000&amp;usg=AFQjCNEVLn7YxLcy3i89zFNRw7j7oDj0Nw">https://drewdevault.com/2019/0<wbr />3/25/Rust-is-not-a-good-C-repl<wbr />acement.html</a></em><br /><br /><em> Saludos.Lector an&oacute;nimo.</em></p>
</blockquote>
<p>Le respond&iacute; por correo pero os copio mi mensaje por si a alguno m&aacute;s le interesa:</p>
<blockquote>
<div><em>Hola Lector an&oacute;nimo,</em></div>
<div>&nbsp;</div>
<div><em>Muchas gracias por seguir el blog, aunque no lo creas, me anima mucho conocer gente nueva que lo haga. </em></div>
<div>&nbsp;</div>
<div><em>Respecto al art&iacute;culo, me sonaba de haberlo le&iacute;do antes (es de marzo as&iacute; que es posible). No estoy de acuerdo en la mayor&iacute;a de puntos aunque quiz&aacute; el que m&aacute;s me moleste es el que no hay una especificaci&oacute;n porque es una verdad a medias: no existe un est&aacute;ndar externo tipo ISO, ANSI o ECMA pero todos los cambios se someten a un proceso de RFC (<a href="https://github.com/rust-lang/rfcs/tree/master/text" target="_blank" rel="noopener" data-saferedirecturl="https://www.google.com/url?q=https://github.com/rust-lang/rfcs/tree/master/text&amp;source=gmail&amp;ust=1566071987532000&amp;usg=AFQjCNEwpXBAWNQutcXIuNVXtmG5ZzYW9A">https://github.com/rust-lang/<wbr />rfcs/tree/master/text</a>) que es el mismo que se usa en Internet y muy similar a las PEP de Python. Igualmente con lo de Cargo, por una vez que alguien hace algo decente en este campo con el tema de dependencias (que en C es un caos y realmente no se deber&iacute;a aspirar a eso).</em></div>
<div>&nbsp;</div>
<div><em>Por otro lado, el tono general del art&iacute;culo es que Rust es muy complejo y que C es m&aacute;s simple. Pero la cuesti&oacute;n de fondo, y que justifica en mi opini&oacute;n la complejidad de Rust (la cu&aacute;l no voy a negar) es que el problema que quiere resolver ES complejo y simplificarlo solo te puede llevar a dos opciones: no servir en todos los casos de uso o trasladar la complejidad a otro campo, que en el mundo de C, es la mente del programador que debe ser consciente de lo que hace. </em></div>
<div>&nbsp;</div>
<div><em>Lo mismo con el tema de la concurrencia, si es complejo de hacer en C como el admite es porque es dif&iacute;cil y porque cuando se dise&ntilde;&oacute; el lenguaje no era algo que se tuviese en cuenta (&iquest;cu&aacute;ntos multicores hab&iacute;a en los 70?), pero a diferencia de lo que &eacute;l dice, s&iacute; merece la pena muchas veces usar diferentes hilos, sobre todo ahora en que los procesadores no aumentan su velocidad (incluso disminuye, por eficiencia energ&eacute;tica) pero s&iacute; su n&uacute;mero de cores. </em></div>
<div>&nbsp;</div>
<div><em>Y luego finaliza con el tema de la seguridad que para &eacute;l no es importante, pero ES importante, sobre todo ahora que el software puede provocar:</em></div>
<ul>
<li><em>p&eacute;rdidas de vidas humanas (piensa en los aviones, los coches aut&oacute;nomos o las herramientas m&eacute;dicas avanzadas)</em></li>
<li><em>hackeos, robos de datos y otro tipo de ataques en pleno auge de los ciberataques</em></li>
</ul>
<div><em>En fin, yo he programado mucho en C y realmente me parece un buen lenguaje para su &eacute;poca, lo respeto mucho, pero creo que a d&iacute;a de hoy hemos avanzado algo y hemos encontrado cosas, que s&iacute;, pueden ser m&aacute;s complejas, pero resuelven de forma m&aacute;s correcta los problemas. Por eso si bien no soy partidario de iniciar proyectos nuevos en C, tampoco soy de esos que va pregonando que todo se reescriba en Rust jajaja.</em></div>
<div>&nbsp;</div>
<div><em>Respecto a si debes aprenderlo... Bueno, eso es algo personal, yo lo probar&iacute;a un poco y ver&iacute;a que tal. Laboralmente todav&iacute;a no tiene mucho tir&oacute;n (en mi trabajo por ejemplo no lo uso, aunque cada vez somos m&aacute;s los de la secta jejejeje) pero es muy interesante. Quiz&aacute; todav&iacute;a le falta algo de madurez pero con el tiempo llegar&aacute; (as&iacute; a lo tonto Python ya tiene 30 a&ntilde;os por ejemplo y Rust no llega a 10 todav&iacute;a).</em></div>
<div>&nbsp;</div>
<div><em>Si tienes alguna duda m&aacute;s, no te cortes y preg&uacute;ntame, ver&eacute; que puedo hacer jajaja</em></div>
<div>&nbsp;</div>
<div><em>Un saludo, Adri&aacute;n</em></div>
</blockquote>]]></description>
                <comments>https://blog.adrianistan.eu/rust-no-es-buen-reemplazo-c</comments>
                <pubDate>Sat, 17 Aug 2019 12:11:13 +0000</pubDate>
            </item>
        
            <item>
                <title>jq, el sed del siglo XXI</title>
                <link>https://blog.adrianistan.eu/jq-sed-siglo-xxi</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/jq-sed-siglo-xxi</guid>
                <description><![CDATA[<p>Que levante la mano qui&eacute;n no ha o&iacute;do hablar de <strong>sed</strong>. Una herramienta presente en cualquier sistema UNIX, se trata de un editor de texto en modo streaming. Aunque sed es una herramienta muy potente, con <a href="https://catonmat.net/proof-that-sed-is-turing-complete">un lenguaje de programaci&oacute;n propio que es Turing completo</a>, la mayor cantidad de usos son sustituir o extraer datos usando expresiones regulares y los comandos&nbsp;<strong>s</strong> y&nbsp;<strong>p</strong> de sed. jq es sed para el siglo XXI porque trabaja de forma nativa con JSON, es decir, trabaja con objetos, no con texto plano.</p>
<h2>El caso del texto plano</h2>
<p>Tradicionalmente en UNIX se han tomado varias decisiones de dise&ntilde;o, que a falta de otros sistemas m&aacute;s populares, se han tomado como acertadas. Una de ellas es el concepto de texto plano. En UNIX el formato por defecto de cualquier cosa ha sido texto plano. Texto plano entendido como que no se tiene en cuenta el formato o la estructura del fichero. Las herramientas del sistema han sido dise&ntilde;adas para trabajar con &eacute;l (grep, sed, cut, ...). Esto hace muy dif&iacute;cil acceder a partes concretas de un archivo. Ciertas herramientas como AWK soportan formatos estructurados como CSV, de forma bastante c&oacute;moda, pero CSV sigue siendo un formato con limitaciones.</p>
<p>Existen sistemas que han tomado esto de otra forma, por ejemplo PowerShell. La shell de Microsoft usa objetos como elemento b&aacute;sico de comunicaci&oacute;n, soportando atributos y arrays y desterrando las operaciones de texto plano a un segundo plano (ba dum tss).</p>
<p>El texto plano no es malo, y muchas veces es lo mejor. Consume pocos recursos, es simple de entender, pero muchas veces necesitamos una estructura por detr&aacute;s que nos ayude a manipular los datos. En mi opini&oacute;n deber&iacute;a haber m&aacute;s documentos estructurados (que luego pueden contener atributos con text plano claro) por defecto.</p>
<h3>jq, la esencia de sed para JSON</h3>
<p>Si hablamos de formatos para documentos, quiz&aacute; el m&aacute;s popular sea JSON. JSON es simple y efectivo, poco verboso, soporta booleanos, n&uacute;meros, cadenas de texto plano, objetos y arrays. JSON casualmente usa ficheros de texto plano para guardarse, pero es un error usar grep, sed y AWK para buscar en estos ficheros. Al hacerlo estamos ignorando por completo la estructura del documento y esto puede hacer todo m&aacute;s complejo, dar resultados err&oacute;neos, etc</p>
<p>jq, aplica las ideas de estas herramientas cl&aacute;sicas de UNIX al sistema de documentos de JSON. Lo puedes instalar en Debian/Ubuntu f&aacute;cilmente:</p>
<pre><code>
sudo apt install jq
</code></pre>
<p>El hola mundo de jq ser&iacute;a aplicar el selector ., para seleccionar todo el documento JSON.</p>
<p>Para todos los ejemplos voy a usar el siguiente fichero JSON, totalmente inventado:</p>
<pre><code class="language-javascript">
{
    "cars" : [
        {
            "name": "Renault 21",
            "company": "Renault",
            "year" : 1980
        },
        {
            "name": "Citr&ouml;en C-Zero",
            "company": "Citr&ouml;en",
            "year": 2009
        },
        {
            "name": "Opel Monza",
            "company": "Opel",
            "year": 1978
        },
        {
            "name": "Pegaso Z-102",
            "company": "Pegaso",
            "year": 1951
        }
    ],
    "drivers" : [
        {
            "name" : "James Hunt",
            "birthplace" : "United Kingdom"
        },
        {
            "name" : "Niki Lauda",
            "birthplace" : "Austria"
        },
        {
            "name" : "Jim Clark",
            "birthplace": "United Kingdom"
        },
        {
            "name" : "Juan Manuel Fangio",
            "birthplace": "Argentina"
        },
        {
            "name" : "Fernando Alonso",
            "birthplace": "Spain"
        }
    ]
}
<br /><img src="https://files.adrianistan.eu/JqDot.png" alt="" /></code></pre>
<h2>Acceso a datos</h2>
<p>Para acceder a los datos usamos una sintaxis similar a la de JavaScript. Accedemos mediante punto a los atributos y con corchetes a los arrays. Si queremos indicar que un campo es opcional, usamos ?. Por ejemplo, si queremos acceder solamente a los coches:</p>
<p><img src="https://files.adrianistan.eu/JqCars.png" alt="" /></p>
<p>Un concepto b&aacute;sico de jq son los&nbsp;<strong>pipes</strong>, de forma similar a Bash, podemos pasar la salida de un comando a otro. Sin embargo, los pipes de jq no transmiten texto plano, sino documentos JSON. Veamos un ejemplo, para acceder al campo year podemos realizarlo de forma compuesta usando pipes. En primer lugar seleccionar&iacute;amos el subdocumento del coche y en el siguiente paso acceder&iacute;amos al elemento year del subdocumento.</p>
<p><img src="https://files.adrianistan.eu/JqPipe.png" alt="" /></p>
<p>Otro concepto muy potente de jq es el poder realizar&nbsp;<strong>map</strong> sobre cada elemento del array. De esta forma, todo lo que indiquemos a continuaci&oacute;n se realizar&aacute; para todos los documentos del array. Estos arrays tambi&eacute;n soportan&nbsp;<strong>slicing</strong>, por lo que podemos decir que el map se realice solo desde el elemento X al elemento Y de la forma [X:Y].</p>
<p><img src="https://files.adrianistan.eu/JqArray.png" alt="" /></p>
<p>Si adem&aacute;s queremos que la salida sea un array v&aacute;lido de JSON, encerramos la expresi&oacute;n entre corchetes.</p>
<h2>B&uacute;squedas con regex</h2>
<p>Ya hemos visto como acceder a los elementos. Vamos ahora a ver como realizar b&uacute;squedas por regex. El comando&nbsp;<strong>test</strong> nos permite ejecutar una comprobaci&oacute;n dada una expresi&oacute;n regular y devuelve true o false.</p>
<p><img src="https://files.adrianistan.eu/JqTest.png" alt="" /></p>
<p>Sin embargo, esto no es muy &uacute;til, ya que la mayor&iacute;a de las veces no vamos a querer un listado de true y falses. <strong>select</strong> es un comando que filtra los subdocumentos que recibe dependiendo de su expresi&oacute;n en el interior es true o false. Combin&aacute;ndolos podemos filtrar la salida seg&uacute;n la b&uacute;squeda.</p>
<p><img src="https://files.adrianistan.eu/JqSelectTest.png" alt="" /></p>
<p>Aplicando las reglas de los pipes podemos realizar la b&uacute;squeda solo en un campo pero obtener el subdocumento completo. Por ejemplo, aqu&iacute; obtenemos un listado de pilotos que han nacido en Reino Unido.</p>
<p><img src="https://files.adrianistan.eu/JqAdvancedSelect.png" alt="" /></p>
<h2>Generando documentos</h2>
<p>Hasta ahora hemos visto como acceder, buscar y filtrar informaci&oacute;n del documento. Pero jq permite tambi&eacute;n generar documentos JSON nuevos de salida. Para ello, hay que usar las llaves y escribir la estructura de nuestro documento.</p>
<p><img src="https://files.adrianistan.eu/JqBuild.png" alt="" /></p>
<p>Aqu&iacute; en este ejemplo, hemos decidido quedarnos con los coches y eliminar el campo de company del JSON. Usamos map, pipes y todo entre corchetes, para obtener un array de salida. Evidentemente, como los campos de entrada y salida se llaman igual, esto se puede simplificar.</p>
<p><img src="https://files.adrianistan.eu/JqShortBuild.png" alt="" /></p>
<p>A jq tambi&eacute;n le podemos pasar variables externas desde fuera con el argumento --arg. Si usamos el argumento -n podemos generar documentos JSON desde cero.</p>
<p><img src="https://files.adrianistan.eu/JqExternVariables.png" alt="" /></p>
<p>Las <strong>variables</strong> se referencian por su nombre siempre precedidas del s&iacute;mbolo del d&oacute;lar (como en Bash). Se pueden crear variables usando&nbsp;<strong>as $variable</strong>.</p>
<h2>Funciones y operadores aritm&eacute;ticos</h2>
<p>jq contiene un buen pu&ntilde;ado de funciones y operadores aritm&eacute;ticos. Por ejemplo, podemos usar la resta para calcular en vez del a&ntilde;o de fabricaci&oacute;n del coche, su edad actual (suponiendo que vivimos en 2019).</p>
<p><img src="https://files.adrianistan.eu/JqAge.png" alt="" /></p>
<p>Existen funciones muy interesantes, algunas de ellas son&nbsp;<strong>length</strong> (longitud de una cadena de texto o de un array), <strong>has </strong>(comprobar si existe una propiedad),&nbsp;<strong>in</strong> (la funci&oacute;n inversa de has),&nbsp;<strong>map</strong> (aplicar una funci&oacute;n a todos los elementos y devolver el nuevo array),&nbsp;<strong>del</strong> (elimina un subdocumento),&nbsp;<strong>add</strong> (a&ntilde;ade todos los elementos entre s&iacute;),&nbsp;<strong>any</strong>,&nbsp;<strong>all</strong> (comprueban si una condici&oacute;n se cumple en alg&uacute;n elemento o en todos),&nbsp;<strong>flatten</strong> (simplifica los arrays, aplan&aacute;ndolos),&nbsp;<strong>sort</strong>,&nbsp;<strong>sort_by</strong> (para ordenar),&nbsp;<strong>group_by</strong> (para agrupar en base a un campo),&nbsp;<strong>unique</strong> (elimina los elementos duplicados),&nbsp;<strong>while</strong> (aplica una operaci&oacute;n hasta que se deje de cumplir la condici&oacute;n),&nbsp;<strong>join</strong> (al estilo SQL) y muchos m&aacute;s.&nbsp;</p>
<p>En jq adem&aacute;s existen operadores condicionales (<strong>if-then-else, and</strong>, <strong>or</strong> y&nbsp;<strong>not</strong>) y&nbsp;<strong>try-catch</strong> para detectar errores. No obstante, en la mayor&iacute;a de las ocasiones no los vas a usar y es mucho m&aacute;s legible usar los elementos presentados anteriormente.</p>
<p>En general, si tienes un buen dominio de la<strong> programaci&oacute;n funcional</strong>, jq te parecer&aacute; bastante intuitivo, ya que las similaridades son evidentes.</p>
<h2>Asignaciones</h2>
<p>Una cosa muy interesante que tiene jq es poder editar los archivos directamente, sin tener que generar uno nuevo, a trav&eacute;s de las asignaciones. La asignaci&oacute;n b&aacute;sica es&nbsp;<strong>|=</strong> que permite modificar un documento con una versi&oacute;n nueva. Por ejemplo, si queremos editar la compa&ntilde;&iacute;a del coche, &uacute;nicamente cuando es Opel, podemos recurrir a una combinaci&oacute;n de if-then-else con |=.</p>
<p><img src="https://files.adrianistan.eu/JqAssign.png" alt="" /></p>
<p>Como vemos, el fichero de salida es id&eacute;ntico al original pero modificando Opel por Vauxhall.&nbsp;</p>
<h2>Conclusiones</h2>
<p>jq es una herramienta muy potente, pensada para trabajar con documentos JSON en un flujo de trabajo similar al de Bash y Sed pero con nociones de la estructura del archivo. jq se puede usar en cualquier sistema pr&aacute;cticamente y es muy potente, como pod&eacute;is haber visto. Espero que os haya picado el gusanillo y a partir de ahora lo empec&eacute;is a utilizar. Muy interesante es su combinaci&oacute;n con&nbsp;<strong>curl</strong> para poder trabajar con APIs web desde la terminal.</p>]]></description>
                <comments>https://blog.adrianistan.eu/jq-sed-siglo-xxi</comments>
                <pubDate>Mon, 12 Aug 2019 17:38:16 +0000</pubDate>
            </item>
        
            <item>
                <title>Videojuegos de ordenador para el verano</title>
                <link>https://blog.adrianistan.eu/videojuegos-verano-ordenador</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/videojuegos-verano-ordenador</guid>
                <description><![CDATA[<p>En este blog no he hablado mucho sobre videojuegos. Me gustan mucho, pero &uacute;ltimamente no juego tanto como anta&ntilde;o. Eso s&iacute;, cuando juego, suelo estar bastante tiempo. En la &eacute;poca de verano sin embargo, encuentro m&aacute;s tiempo para jugar. Hasta hace bastante tiempo no ten&iacute;a un <a href="https://e-sport.tech/requisitos-counter-strike-go/">tipo de PC</a> acorde a las exigencias de los juegos actuales. Hace unos a&ntilde;os me constru&iacute; un PC que ya me permit&iacute;a jugar a bastantes triple A.</p>
<p>Tiene un procesador AMD FX-4350 y una gr&aacute;fica AMD R7 250. De RAM pusimos 8 GB y de disco ten&iacute;a 1 TB de tipo HDD. No era lo mejor del momento, pero entraba dentro de nuestro presupuesto. El ordenador ha cumplido mis expectativas y me ha permitido jugar a los juegos que quer&iacute;a, cuando y como quer&iacute;a. En este art&iacute;culo os voy a comentar algunos de los juegos que juego en verano. Por supuesto, m&aacute;s pronto que tarde necesitar&aacute; una renovaci&oacute;n, con los &uacute;ltimos procesadores de AMD y las gr&aacute;ficas de NVIDIA, que rompen r&eacute;cords cada vez que se presentan. Tambi&eacute;n he podido comprobar la potencia de los SSD, que dentro de poco se convertir&aacute;n en algo obligatorio.</p>
<p>La mayor&iacute;a de los juegos de la lista son compatibles con ordenadores bastante asequibles, no necesitar&aacute;s un ordenador gaming de &uacute;ltima generaci&oacute;n para poder jugar satisfactoriamente a ellos.</p>
<h3>Starcraft II</h3>
<p><img src="https://files.adrianistan.eu/Starcraft.jpg" alt="" /></p>
<p>Leyendo las noticias de inteligencias artificiales como DeepMind que juegan al juego me entr&oacute; curiosidad. Hab&iacute;a jugado al primero unas pocas horas, as&iacute; que sab&iacute;a un poco por donde iba. No obstante, aprovechando que ahora es gratuito, convertido en free to play, me atrev&iacute; a ponerme m&aacute;s en serio con este juego. Se trata de uno de los juegos m&aacute;s importantes de la esfera de los e-sports. No obstante, yo empec&eacute; con la campa&ntilde;a gratuita, que es un buen tutorial. Luego pas&eacute; a partidas multijugador 1vs1. El ritmo es diferente, es mucho m&aacute;s fren&eacute;tico, si no eres r&aacute;pido, te comen. Siendo yo una persona m&aacute;s acostumbrada a ir m&aacute;s lentamente, construyendo las bases m&aacute;s detenidamente, me ha costado un poco entender la mentalidad. Todav&iacute;a soy muy malo y fuera de la campa&ntilde;a es dif&iacute;cil de aprender mec&aacute;nicas avanzadas.</p>
<h3>Factorio</h3>
<p><img src="https://files.adrianistan.eu/Factorio.jpg" alt="" /></p>
<p>Factorio es un juego de automatizaci&oacute;n industrial. La idea es muy simple, pero tremendamente potente. Tienes que ir construyendo cosas, como si fuese una gran cadena de montaje. El objetivo es construir un cohete bastante complejo pero eso es lo de menos. Lo adictivo es intentar hacer tu f&aacute;brica lo m&aacute;s eficiente y productiva posible. Un juego muy recomendable para programadores. Existe modo de juego pac&iacute;fico y con enemigos. Recomiendo empezar con el pac&iacute;fico para saber de que va la cosa, y luego pasarse a enemigos, que es m&aacute;s interesante.</p>
<h2>Cities Skylines</h2>
<p><img src="https://files.adrianistan.eu/CitiesSkylines.jpg" alt="" /></p>
<p>Cities Skylines es el city builder por excelencia de esta generaci&oacute;n. Con una cantidad ingente de mods y DLCs, el juego completo es impresionante y me gusta bastante. Respecto a la simulaci&oacute;n... A nivel econ&oacute;mico y de satisfacci&oacute;n de la gente es muy f&aacute;cil y lo &uacute;nico interesante es a nivel tr&aacute;fico (no es casualidad que est&eacute; hecho por una empresa que se dedicaba a hacer juegos de transporte). No obstante, por defecto la gesti&oacute;n del transporte es justamente un poco mediocre. Por otro lado la calidad visual es muy buena.</p>
<h3>Enter the Gungeon</h3>
<p><img src="https://files.adrianistan.eu/EnterTheGungeon.jpg" alt="" /></p>
<p>Enter the Gungeon es un juego roguelike muy entretenido. Con contenido a rabiar (armas, salas, enemigos, objetos,...), la mec&aacute;nica del juego es simple. Dispara y que no te den (sobre todo m&aacute;s lo segundo). Tiene un toque de humor que me encanta y el juego internamente es complejo, aunque no lo parezca. Debo decir que todav&iacute;a no lo he acabado pero me sigue entreteniendo aunque nunca pase del cuarto piso.</p>
<h3>Batman: Arkham Asylum</h3>
<p><img src="https://files.adrianistan.eu/Batman.jpeg" alt="" /></p>
<p>Batman: Arkham Asylum es un triple A con unos a&ntilde;itos ya. Batman es mi superh&eacute;roe favorito (por no decir, el &uacute;nico que tolero) y ten&iacute;a ganas de probar este juego. Se trata de un juego de acci&oacute;n, sigilo y combate cuerpo a cuerpo en tercera persona. Me sorprendi&oacute; gratamente, una ambientaci&oacute;n perfecta, una historia interesante y una jugabilidad muy potente.</p>
<h3>Dirt 3</h3>
<p><img src="https://files.adrianistan.eu/Dirt3.jpg" alt="" /></p>
<p>Dirt 3 es uno de los mejores juegos de carreras que he podido jugar. Principalmente son carreras de rally, las cu&aacute;les son ligeramente diferentes. En primer lugar, suelen ser punto a punto, es decir, nunca repites la misma curva y adem&aacute;s se intenta que no te la puedas llegar a aprender. En segundo lugar, son cronometradas, pocas veces tendr&aacute;s que adelantar. El juego propone una conducci&oacute;n bastante realista (sin llegar a ser una simulaci&oacute;n), con buenas f&iacute;sicas. Bastante contenido, con montones de coches de las marcas cl&aacute;sicas: Renault, Lancia, Mitsubishi, Mini, Peugeout, BMW, Subaru, ... Yo me pas&eacute; la campa&ntilde;a con marchas manuales y se lo recomiendo a todo el mundo, ya que es muy satisfactorio (en F1 puede ser m&aacute;s deseperante). El juego tambi&eacute;n tiene pruebas de circuito y de drifting, las cu&aacute;les no son tan buenas pero son minoritarias. En definitiva, un buen juego para quemar adrenalina, a velocidades tan bajas que te sorprender&aacute;s.</p>
<h3>Theme Hospital</h3>
<p><img src="https://files.adrianistan.eu/ThemeHospital.jpg" alt="" /></p>
<p>Este juego es quiz&aacute; el m&aacute;s antiguo de esta lista, de hecho si lo compr&aacute;is en GOG, lo recibir&eacute;is empaquetado junto con DOSBox para que funcione correctamente. Theme Hospital es un juego de tipo tycoon (gesti&oacute;n empresarial). En este caso estamos ante uno de los mejores t&iacute;tulos del g&eacute;nero, donde gestionamos un hospital. El juego no pretende ser serio, de hecho todas las enfermedades son inventadas y est&uacute;pidas pero es entretenido y llegado a un punto, puede llegar a suponer un reto lograr que no te despidan. Juego muy creativo, en jugabilidad ha envejecido bien, pero hay algunos detalles un poco arcaicos.</p>
<h3>Tr&oacute;pico 5</h3>
<p><img src="https://files.adrianistan.eu/Tropico.jpg" alt="" /></p>
<p>Tr&oacute;pico es una saga de city builders, mismo g&eacute;nero que Cities Skylines, pero con otro enfoque, radicalmente diferente. En Tr&oacute;pico gestionamos una isla tropical siendo presidentes de esta. Aqu&iacute; la pol&iacute;tica es m&aacute;s importante, ya que inspir&aacute;ndose en ciertas dictaduras, deber&aacute;s mantenerte en el poder. Dentro de la isla habr&aacute; facciones, que tendr&aacute;s que contentar y podr&aacute; haber rebeldes, que podr&aacute;n iniciar la revoluci&oacute;n. Para ello, t&uacute; que eres precavido, habr&aacute;s construido un ej&eacute;rcito lo suficientemente fuerte, con un salario aceptable. Adem&aacute;s organizar&aacute;s elecciones falsas para dar apariencia de democracia. El juego tiene un fant&aacute;stico sentido del humor y Tr&oacute;pico 5 mejor&oacute; mucho la jugabilidad respecto al 3 (el anterior que he jugado). Por otro lado, cabe destacar la escala del juego. Si en Cities Skylines las ciudades son de cientos de miles de habitantes, en Tr&oacute;pico con una isla de 150 habitantes ya tendr&aacute;s bastantes problemas. Es un juego mucho m&aacute;s individualizado, donde cada persona importa. Adem&aacute;s en la versi&oacute;n 5, la historia del mundo influye.</p>
<h3>Mini Metro</h3>
<p><img src="https://files.adrianistan.eu/MiniMetro.jpg" alt="" /></p>
<p>Mini Metro es un juego sencillo donde tenemos que ir construyendo l&iacute;neas de metro. El juego tiene una est&eacute;tica totalmente minimalista, que junto a su mec&aacute;nica simple hace que sea muy f&aacute;cil aprender a jugar. Sin embargo, la dificultad de este juego seg&uacute;n avanza el tiempo no es balad&iacute; y en alg&uacute;n momento perder&aacute;s (se pierde cuando una estaci&oacute;n est&aacute; colapsada). Juego muy simple pero entretenido al que le he echado muchas horas. Como punto negativo, el ritmo. Al empezar el juego es demasiado simple y aburrido, para luego ya ponerse interesante. La intensidad no es nada regular.</p>
<p>&nbsp;</p>
<h3>El resto</h3>
<p>Hay juegos que he jugado este verano que se quedan fuera de la lista, principalmente porque no me han gustado tanto. Algunos de ellos, sin orden en particular: F&oacute;rmula 1 2015, The Sexy Brutale, Super Meat Boy, Counter-Strike: GO, Fortnite, Maldita Castilla, Civilization V, Borderlands, ...</p>
<p>Como siempre, cualquier aportaci&oacute;n es bienvenida en los comentarios. Un saludo y hasta otro d&iacute;a.</p>]]></description>
                <comments>https://blog.adrianistan.eu/videojuegos-verano-ordenador</comments>
                <pubDate>Sat, 10 Aug 2019 17:24:35 +0000</pubDate>
            </item>
        
            <item>
                <title>Crónica Neuronal: Indian Liver Patients Record</title>
                <link>https://blog.adrianistan.eu/cronica-neuronal-indian-liver-patients-record</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/cronica-neuronal-indian-liver-patients-record</guid>
                <description><![CDATA[<p>Otro d&iacute;a de verano y otro d&iacute;a de <strong>Cr&oacute;nica Neuronal</strong>. Hoy he elegido un dataset m&eacute;dico, se trata de<a href="https://www.kaggle.com/uciml/indian-liver-patient-records"> Indian Liver Patients Record</a>, o lo que es lo mismo, <em>Registro de pacientes de h&iacute;gado en la India</em>. Las enfermedades relacionadas con el h&iacute;gado han ido en aumento en los &uacute;ltimos a&ntilde;os: el alcohol, la poluci&oacute;n, la comida en mal estado, las drogas y los pepinillos son algunas de las causas de este aumento. En el dataset, originalmente de UCI y bajo licencia Creative Commons Zero, tenemos datos m&eacute;dicos de varias personas y si deben recibir tratamiento para el h&iacute;gado o no. El objetivo es identificar, dado un paciente nuevo, si deber&iacute;a iniciar un tratamiento del h&iacute;gado o si por el contrario, est&aacute; sano.</p>
<p><img src="https://files.adrianistan.eu/Liver.jpg" alt="" /></p>
<h2>An&aacute;lisis de datos</h2>
<p>Las variables o features del dataset son las siguientes:</p>
<ul>
<li><strong>Age</strong>: edad de la persona</li>
<li><strong>Gender</strong>: sexo de la persona</li>
<li><strong>Total Bilirubin</strong>: <a href="https://es.wikipedia.org/wiki/Bilirrubina">bilirrubina</a> total en el h&iacute;gado en mg/dL. Es la suma de la bilirrubina directa y la indirecta</li>
<li><strong>Direct Bilirubin</strong>: bilirrubina directa en mg/dL.</li>
<li><strong>Alkaline Phosphotas</strong>: fosfatasa alcalina en IU/L</li>
<li><strong>Alanina aminotransferasa</strong>: enzima conocida tambi&eacute;n como transaminasa glut&aacute;mico pir&uacute;vica, en IU/L</li>
<li><strong>Aspartate Aminotransferas</strong>: enzima conocida tambi&eacute;n como aspartato transaminasa, en IU/L</li>
<li><strong>Total Protiens</strong>: prote&iacute;nas totales, en g/dL</li>
<li><strong>Albumin</strong>: <a href="https://es.wikipedia.org/wiki/Alb%C3%BAmina">alb&uacute;mina</a>, una prote&iacute;na que se genera en la sangre, en g/dL</li>
<li><strong>Albumin and Globulin Ratio</strong>: ratio de alb&uacute;mina por gl&oacute;bulos en sangre</li>
<li><strong>Dataset</strong>: si necesitan tratamiento o no</li>
</ul>
<p>Vamos a entrar m&aacute;s en detalle de las variables usando&nbsp;<strong>Seaborn</strong>.</p>
<pre><code class="language-python">
import pandas as pd
import numpy as np
import seaborn as sns

data = pd.read_csv("indian_liver_patient.csv")

data.describe()

sns.countplot(data["Gender"])

sns.countplot(data["Dataset"])

sns.distplot(data["Total_Bilirubin"])
</code></pre>
<p>Hay solo dos variables categ&oacute;rica en este dataset: la clase y Gender. La clase no hace falta transformarla, ya que ya son dos n&uacute;meros. Vamos a analizar Gender, con <strong>countplot</strong>, que tendremos que transformar con <strong>OneHotEncoder</strong>:</p>
<p><img src="https://files.adrianistan.eu/IndianLiverSex.png" alt="" /></p>
<p>Tenemos much&iacute;simos m&aacute;s datos de hombres que de mujeres. En una predicc&oacute;n real har&iacute;a falta tener m&aacute;s datos de mujeres. Puede ser que esto no influya o puede que s&iacute; (como en muchas enfermedades, un caso muy evidente, el c&aacute;ncer de mama) o incluso puede que se deba factores culturales. Pero de cara a un diagn&oacute;stico deber&iacute;amos poder tener datos equilibrados.&nbsp;</p>
<p><img src="https://files.adrianistan.eu/IndianLiverDataset.png" alt="" /></p>
<p>En el caso de la clase, tambi&eacute;n vemos que est&aacute; desequilibrada, con muchos m&aacute;s datos de gente que deber&iacute;a recibir tratamiento (1) de los que no (2). A la hora de realizar el test del algoritmo deberemos <strong>estratificar</strong>, para asegurarnos que se incluye esta misma proporci&oacute;n en los conjuntos de entrenamiento reducidos.</p>
<p>Podemos analizar la distribuci&oacute;n de alguna variable continua, con <strong>distplot</strong>, pero no he encontrado nada relevante aqu&iacute; (parece obvio que hay un &uacute;nico valor mayoritario).</p>
<p><img src="https://files.adrianistan.eu/IndianLiverBilirubin.png" alt="" /></p>
<p>Usando <strong>jointplot</strong> en Seaborn podemos ver correlaciones variable a variable. Usando <strong>pairplot</strong>, podemos ver todas de golpe.</p>
<p><img src="https://files.adrianistan.eu/IndianLiverPairplot.png" alt="" /></p>
<p>As&iacute; a priori no observo valores demasiado extra&ntilde;os, as&iacute; que de momento, mantendremos todos los valores.</p>
<p>Analizando los datos compruebo que existen 4 valores no existentes en la columna Albumin_and_Globulin_Ratio. Procedo a rellenarlos con una media de valores. Hacemos el OneHotEncoding:</p>
<pre><code class="language-python">
data.isna().sum()

data["Albumin_and_Globulin_Ratio"] = data["Albumin_and_Globulin_Ratio"].fillna(data["Albumin_and_Globulin_Ratio"].mean())

data = pd.get_dummies(data)

X = data.drop(columns=["Dataset"])
Y = data["Dataset"]
</code></pre>
<p>A continuaci&oacute;n, probamos diferentes algoritmos de la familia de &aacute;rboles: DecisionTree y RandomForest. Prob&eacute; el criterio de entrop&iacute;a (ID3, C4.5 y similares) y el de Gini. Y Gini era m&aacute;s estable, as&iacute; que es mi elecci&oacute;n.</p>
<pre><code class="language-python">
from sklearn.model_selection import train_test_split


train_x, test_x, train_y, test_y = train_test_split(X,Y,test_size=1/3,stratify=Y,random_state=0)

from sklearn.tree import DecisionTreeClassifier

tree = DecisionTreeClassifier(criterion="gini", max_depth=3, min_samples_split=4, max_features=4)
tree.fit(train_x, train_y)
predict_y = tree.predict(test_x)

np.sum(predict_y == test_y)/test_y.shape[0]

from sklearn.ensemble import RandomForestClassifier

tree = RandomForestClassifier(criterion="gini", max_depth=None, min_samples_split=4, max_features=4, n_estimators=100)
tree.fit(train_x, train_y)
predict_y = tree.predict(test_x)

np.sum(predict_y == test_y)/test_y.shape[0]
</code></pre>
<p>Con DecisionTreeClassifier y el criterio de Gini, lo tune&eacute; con max_depth=3, min_samples_split=4 y max_features=4. Todo ello para que no haga sobreajuste, t&iacute;pico en &aacute;rboles. Este max_features es interesante. Podr&iacute;amos haberlo hecho antes, pero con los &aacute;rboles da igual. B&aacute;sicamente si dos variables est&aacute;n muy correlacionadas, aportan pr&aacute;cticamente la misma informaci&oacute;n, ser&aacute;n ignoradas en la construcci&oacute;n del modelo, reduciendo el sobreajuste. El resultado es un 70% de acierto.</p>
<p>Con RandomForest he conservado los ajustes de max_features y de min_samples_split pero he desactivado la profundidad m&aacute;xima. Consistentemente se obtiene un 73-74% de acierto.</p>
<p>Por &uacute;ltimo, para XGBoost, que era la primera vez que lo usaba, le met&iacute; en un bucle que va modificando valores, probando muchas configuraciones posibles. Esto llev&oacute; un rato, pero consigui&oacute; una configuraci&oacute;</p>
<pre><code class="language-python">
import xgboost as xgb

res = dict()

for d in range(1,10):
    for l in range(1,9):
        for m in range(1,5):
            for g in range(0,10):
                tree = xgb.XGBClassifier(max_depth=d, learning_rate=l/100, n_estimators=100, objective="binary:logistic", booster="gbtree", n_jobs=4, min_child_weight=m, gamma=g/10)
                tree.fit(train_x, train_y)
                predict_y = tree.predict(test_x)

                res[(d,l,m,g)] = np.sum(predict_y == test_y)/test_y.shape[0]
                print(res[(d,l,m,g)])
max(res, key=res.get)
</code></pre>
<p>El valor &oacute;ptimo fue profundidad m&aacute;xima de 7, learning_rate de 0.08, min_child_weight de 3 y gamma de 0.3. Estos valores fueron lo mejor que pude encontrar para este holdout espec&iacute;fico. Pero al cambiar el random_state del Holdout, estos aciertos se desmoronaron hasta llegar al 64%. Los par&aacute;metros no son buenos, simplemente hab&iacute;a coincidido que iban muy bien. Con esta tragedia, es un buen momento para recordar uno de los m&eacute;todos alternativos a Holdout: <strong>validaci&oacute;n cruzada</strong>, que sin duda usar&eacute; en otro dataset de otra Cr&oacute;nica Neuronal.</p>
<h2>Conclusi&oacute;n</h2>
<p>Hemos obtenido f&aacute;cilmente modelos con m&aacute;s del 70% de acierto de forma consistente, con RandomForest y xgboost. Prob&eacute; tambi&eacute;n K-Vecinos, con resultados parecidos pero m&aacute;s lento y el perceptr&oacute;n multicapa, sin poder igualar resultados. Espero que este dataset os haya gustado, espero vuestros aportes en los comentarios.</p>
<p>&nbsp;</p>]]></description>
                <comments>https://blog.adrianistan.eu/cronica-neuronal-indian-liver-patients-record</comments>
                <pubDate>Tue, 06 Aug 2019 21:34:07 +0000</pubDate>
            </item>
        
            <item>
                <title>Crónica Neuronal: House Prices</title>
                <link>https://blog.adrianistan.eu/cronica-neuronal-house-prices</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/cronica-neuronal-house-prices</guid>
                <description><![CDATA[<p>Bienvenidos a una secci&oacute;n del blog titulada&nbsp;<strong>Cr&oacute;nica Neuronal</strong>. En esta secci&oacute;n resolveremos problemas reales de Inteligencia Artificial de forma pr&aacute;ctica. La estructura es la siguiente. Yo presento un problema, muchas veces sacado de <a href="https://www.kaggle.com">Kaggle</a>, <a href="https://spainml.com/">SpainML</a> u otro sitio y voy contando como lo voy resolviendo, escribiendo paso a paso mis pensamientos en cada momento. De hecho, yo no he resuelto estos problemas, sino que los voy resolviendo sobre la marcha mientras escribo la cr&oacute;nica. A los problemas les dedico un tiempo limitado y puede ser posible (como en este caso) que no llegue a resultados satisfactorios.</p>
<h2>House Prices</h2>
<p>Este primer problema ha sido creado por <a href="https://www.kaggle.com/c/house-prices-advanced-regression-techniques/">Kaggle</a>, forma parte de la categor&iacute;a de problemas para principiantes. El problema consiste en predecir el valor de venta real (es decir, por el que se ejecuta una compra-venta) de casas de Ames, Iowa. Para ello disponemos de un historial de compra-ventas en la zona de Ames, con gran cantidad de detalles:</p>
<ul>
<li><strong>MSSubClass</strong>: Tipo de edificio</li>
<li><strong>MSZoning</strong>: Clasificaci&oacute;n de la zona</li>
<li><strong>LotFrontage</strong>: Linear feet of street connected to property</li>
<li><strong>LotArea</strong>: Lot size in square feet</li>
<li><strong>Street</strong>: Tipo de acceso por carretera</li>
<li><strong>Alley</strong>: Tipo de acceso por callej&oacute;n</li>
<li><strong>LotShape</strong>: Forma de la propiedad</li>
<li><strong>LandContour</strong>: Rugosidad de la propiedad</li>
<li><strong>Utilities</strong>: Tipo de utilidades disponibles</li>
<li><strong>LotConfig</strong>: Configuraci&oacute;n de la parcela</li>
<li><strong>LandSlope</strong>: Inclinaci&oacute;n de la parcela</li>
<li><strong>Neighborhood</strong>: Barroi</li>
<li><strong>Condition1</strong>: Proximidad a carretera principal o ferrocarril</li>
<li><strong>Condition2</strong>: Proximidad a carretera principal o ferrocarril (si hubiese un segundo)</li>
<li><strong>BldgType</strong>: Tipo de vivienda</li>
<li><strong>HouseStyle</strong>: Estilo de la vivienda</li>
<li><strong>OverallQual</strong>: Calidad de materiales y construcci&oacute;n</li>
<li><strong>OverallCond</strong>: Calidad de la casa en general</li>
<li><strong>YearBuilt</strong>: Fecha original de construcci&oacute;n</li>
<li><strong>YearRemodAdd</strong>: Fecha de remodelaci&oacute;n</li>
<li><strong>RoofStyle</strong>: Tipo de techo</li>
<li><strong>RoofMatl</strong>: Material del techo</li>
<li><strong>Exterior1st</strong>: Material del exterior de la casa</li>
<li><strong>Exterior2nd</strong>: Material del exterior de la casa</li>
<li><strong>MasVnrType</strong>: Recubrimiento exterior decorativo</li>
<li><strong>MasVnrArea</strong>: &Aacute;rea del recubrimiento exterior decorativo</li>
<li><strong>ExterQual</strong>: Calidad del material exterior</li>
<li><strong>ExterCond</strong>: Condiciones actuales del material exterior</li>
<li><strong>Foundation</strong>: Tipo de pilares</li>
<li><strong>BsmtQual</strong>: Altura del s&oacute;tano</li>
<li><strong>BsmtCond</strong>: Condiciones del s&oacute;tano</li>
<li><strong>BsmtExposure</strong>: Exposici&oacute;n del s&oacute;tano al exterior</li>
<li><strong>BsmtFinType1</strong>: Calidad del primer &aacute;rea acabada del s&oacute;tano</li>
<li><strong>BsmtFinSF1</strong>: Tipo del primer &aacute;rea acabada</li>
<li><strong>BsmtFinType2</strong>: Calidad del segundo &aacute;rea acabada del s&oacute;tano</li>
<li><strong>BsmtFinSF2</strong>: Tipo del segundo &aacute;rea acabada</li>
<li><strong>BsmtUnfSF</strong>: &Aacute;rea sin acabar del s&oacute;tano</li>
<li><strong>TotalBsmtSF</strong>: &Aacute;rea del s&oacute;tano</li>
<li><strong>Heating</strong>: Tipo de calefacci&oacute;n</li>
<li><strong>HeatingQC</strong>: Calidad de la calefacci&oacute;n</li>
<li><strong>CentralAir</strong>: Sistema de aire acondicionado centralizado</li>
<li><strong>Electrical</strong>: Sistema el&eacute;ctrico</li>
<li><strong>1stFlrSF</strong>: &Aacute;rea primera planta</li>
<li><strong>2ndFlrSF</strong>: &Aacute;rea segunda planta</li>
<li><strong>LowQualFinSF</strong>: &Aacute;rea finalizada de baja calidad</li>
<li><strong>GrLivArea</strong>: &Aacute;rea habitable sobre el suelo</li>
<li><strong>BsmtFullBath</strong>: S&oacute;tano con ba&ntilde;o completo</li>
<li><strong>BsmtHalfBath</strong>: S&oacute;tano con ba&ntilde;o incompleto</li>
<li><strong>FullBath</strong>: Ba&ntilde;os completos sobre el suelo</li>
<li><strong>HalfBath</strong>: Ba&ntilde;os incompletos sobre el suelo</li>
<li><strong>Bedroom</strong>: Dormitorios sobre el suelo</li>
<li><strong>Kitchen</strong>: Cocinas</li>
<li><strong>KitchenQual</strong>: Calidad cocinas</li>
<li><strong>TotRmsAbvGrd</strong>: Habitaciones sobre el suelo (sin ba&ntilde;os)</li>
<li><strong>Functional</strong>: Rating de funcionalidad</li>
<li><strong>Fireplaces</strong>: N&uacute;mero de chimeneas</li>
<li><strong>FireplaceQu</strong>: Calidad chimeneas</li>
<li><strong>GarageType</strong>: Tipo garaje</li>
<li><strong>GarageYrBlt</strong>: A&ntilde;o del garaje</li>
<li><strong>GarageFinish</strong>: Calidad interior del garaje</li>
<li><strong>GarageCars</strong>: N&uacute;mero de coches en el garaje</li>
<li><strong>GarageArea</strong>: &Aacute;rea del garaje</li>
<li><strong>GarageQual</strong>: Calidad del garaje</li>
<li><strong>GarageCond</strong>: Condiciones actuales del garaje</li>
<li><strong>PavedDrive</strong>: Entrada al garaje asfaltada</li>
<li><strong>WoodDeckSF</strong>: &Aacute;rea exterior recubierta de madera</li>
<li><strong>OpenPorchSF</strong>: &Aacute;rea de porche abierto</li>
<li><strong>EnclosedPorch</strong>: &Aacute;rea de porche cerrado</li>
<li><strong>3SsnPorch</strong>: &Aacute;rea de porche tres-estaciones</li>
<li><strong>ScreenPorch</strong>: &Aacute;rea de porche pantalla</li>
<li><strong>PoolArea</strong>: &Aacute;rea de la piscina</li>
<li><strong>PoolQC</strong>: Calidad de la piscina</li>
<li><strong>Fence</strong>: Calidad de la valla</li>
<li><strong>MiscFeature</strong>: Miscel&aacute;nea</li>
<li><strong>MiscVal</strong>: Valor de la miscel&aacute;nea</li>
<li><strong>MoSold</strong>: Mes de venta</li>
<li><strong>YrSold</strong>: A&ntilde;o de venta</li>
<li><strong>SaleType</strong>: Tipo de venta</li>
<li><strong>SaleCondition</strong>: Condici&oacute;n de la venta</li>
</ul>
<p>Como vemos, el n&uacute;mero de variables es enorme. Hay que tener en cuenta que muchas propiedades pueden no influir o influir de forma poco significativa. En caso de detectarlo puede ser convenientes eliminarlas, ya que pueden a&ntilde;adir ruido innecesario.</p>
<p>Se trata de un problema de&nbsp;<strong>regresi&oacute;n</strong>, ya que se nos pide predecir un valor num&eacute;rico dentro de un continuo (los precios, a efectos pr&aacute;cticos son continuos) y un infinito. Esto se opone a los problemas de&nbsp;<strong>clasificaci&oacute;n</strong>, donde intentamos predecir algo dentro de una lista de posibles valores.</p>
<h2>Cargando datos y Holdout</h2>
<p>Vamos a empezar abriendo Jupyter y cargando los datos en formato CSV con Pandas. Si leemos el CSV vemos que tiene una columna extra llamada, Id. Vamos a eliminarla, ya que esta columna &uacute;nica para cada instancia nos provocar&iacute;a <strong>sobreajuste</strong>.</p>
<p>Adem&aacute;s, vamos a preparar un sistema de prueba. Me decanto por el m&eacute;todo&nbsp;<strong>Holdout</strong> con 2/3-1/3. Esto es que nuestros datos de entrenamiento y de test son 2/3 y 1/3 del total respectivamente. Cuando tengamos un buen algoritmo, podremos usar&nbsp;<strong>todos</strong> los datos para la clasificaci&oacute;n en Kaggle. De momento vamos a eliminar adem&aacute;s las variables para las que existan valores desconocidos con&nbsp;<strong>dropna</strong>.</p>
<pre><code class="language-python">
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split

data_csv = pd.read_csv("train.csv")

data = data_csv.drop(columns=["Id"])

data.dropna(axis="columns", inplace=True)
data = pd.get_dummies(data)

x = data.drop(columns=["SalePrice"])
y = data["SalePrice"]

train_x, test_x, train_y, test_y = train_test_split(x, y, test_size=1/3)
</code></pre>
<h2>&Aacute;rboles de decisi&oacute;n</h2>
<p>Siempre es recomendable probar los conjuntos de datos con algoritmos sencillos de aprendizaje autom&aacute;tico. Los &aacute;rboles de decisi&oacute;n son r&aacute;pidos y pueden ofrecernos informaci&oacute;n sobre el conjunto de datos muy interesante. En mi caso voy a usar <a href="https://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeRegressor.html">DecisionTreeRegressor</a> perteneciente a sklearn. No obstante, este m&oacute;dulo no admite variables categ&oacute;ricas. Para ello podemos usar <strong>LabelEncoder&nbsp;</strong>o&nbsp;<strong>OneHotEncoder</strong> tambi&eacute;n de sklearn y poder realizar una transformaci&oacute;n. &iquest;Cu&aacute;l usar? La teor&iacute;a dicta que LabelEncoder cuando las categor&iacute;as tienen un orden y OneHotEncoder cuando no tiene sentido. Como en muchas propiedades no sabemos si es intr&iacute;nsecamente mejor Ladrillo o Piedra, usaremos OneHotEncoder por defecto. En este caso no voy a usar sklearn, sino el m&eacute;todo de Pandas&nbsp;<strong>get_dummies</strong>. Adem&aacute;s creo que no va a hacer falta <strong>normalizar</strong> los datos, as&iacute; que nos saltamos este paso.</p>
<pre><code class="language-python">
from sklearn.tree import DecisionTreeRegressor

tree = DecisionTreeRegressor(criterion="mse", splitter="best", max_depth=None, min_samples_split=10, min_samples_leaf=5)
tree.fit(train_x, train_y)
predict_y = tree.predict(test_x)

1 - np.sum(abs(predict_y - test_y) &lt; 0.1*test_y)/test_y.shape[0]
</code></pre>
<p>Por &uacute;ltimo, para evaluar los datos elegimos que una diferencia del 10% entre el valor real y el valor predicho nos vale. Este &aacute;rbol de decisi&oacute;n, tiene un criterio MSE, no tiene altura m&aacute;xima pero a cambio le he puesto que el tama&ntilde;o de las hojas sea m&iacute;nimo de 5 (es decir, una ramificaci&oacute;n final solo lo puede ser si al menos 5 instancias de entrenamiento caen en ella) y un n&uacute;mero de split de 10 (para crear una rama hace falta por lo menos 10 ejemplos). Los resultados son bastante mediocres: una tasa de error del&nbsp; 50%. Pero est&aacute; suficientemente bien, como para hacer una entrega de prueba en Kaggle.</p>
<h2>Realizando las predicciones</h2>
<p>Aqu&iacute; me encontr&eacute; con un problema. Los datos de test ten&iacute;an iferentes datos que los de entrenamiento y el OneHotEncoding con get_dummies fallaba. La soluci&oacute;n que utilic&eacute; es cargar los datos de entrenamiento y test en un mismo DataFrame de Pandas, hacer el get_dummies y despu&eacute;s volverlos a separar. Despu&eacute;s de realizar esto tenemos que volver a entrenar el modelo con todos los datos posibles y realizar la predicci&oacute;n y guardarla en el CSV. El c&oacute;digo completo es el siguiente:</p>
<pre><code class="language-python">
import pandas as pd
import numpy as np

data_csv = pd.read_csv("train.csv")
test_csv = pd.read_csv("test.csv")
size = test_csv.shape
all_data = pd.concat((test_csv,data_csv),sort=False)

all_data.dropna(axis="columns",inplace=True)
all_data = pd.get_dummies(all_data,drop_first=True)

test = all_data[0:size[0]]
data = all_data[size[0]:]

x = data
y = data_csv["SalePrice"]


from sklearn.model_selection import train_test_split


train_x, test_x, train_y, test_y = train_test_split(x,y,test_size=1/3)


# ARBOLES DE DECISION

from sklearn.tree import DecisionTreeRegressor

tree = DecisionTreeRegressor(criterion="mse",splitter="best",max_depth=None,min_samples_split=10,min_samples_leaf=5)
tree.fit(train_x,train_y)
predict_y = tree.predict(test_x)


1 - np.sum(abs(predict_y - test_y) &lt; 0.1*test_y)/test_y.shape[0]


# PREDECIR

tree.fit(x,y)

test_out = tree.predict(test)
out = pd.DataFrame({"Id" : test_id, "SalePrice" : test_out})

out.to_csv("out.csv",index=False)
</code></pre>
<p>El fichero out.csv lo podemos subir a Kaggle.</p>
<p><img src="https://files.adrianistan.eu/KaggleScore.png" alt="" /></p>
<p>El resultado es mejor de lo esperado, quiz&aacute; Kaggle usa un interavalo de admisi&oacute;n m&aacute;s alto que el m&iacute;o. Igualmente, lo voy a dejar en el 10%</p>
<h2>K-Vecinos</h2>
<p>Voy a probar el algoritmo de <strong>K-Vecinos</strong>. No tengo muchas esperanzas en &eacute;l, pero la interfaz de programaci&oacute;n en sklearn es muy parecida a la los &aacute;rboles de decisi&oacute;n.</p>
<pre><code class="language-python">
from sklearn.neighbors import KNeighborsRegressor

knr = KNeighborsRegressor(n_neighbors=5,p=2,metric="minkowski")
knr.fit(train_x,train_y)
predict_y = knr.predict(test_x)

1 - np.sum(abs(predict_y - test_y) &lt; 0.1*test_y)/test_y.shape[0]
</code></pre>
<p>Y efectivamente, el resultado es horrible. K-Vecinos tiene una tasa de error del 60%. No creo que merezca la pena insistir mucho en este algoritmo. Lo he configurado con distancia de Minkowski y P=2 (lo que equivale a la distancia eucl&iacute;dea).</p>
<h2>Regresi&oacute;n Lineal</h2>
<p>La <strong>regresi&oacute;n lineal</strong> en cambio s&iacute; que creo que puede ser interesante probarla.&nbsp;</p>
<pre><code class="language-python">
from sklearn.linear_model import LinearRegression

reg = LinearRegression()
reg.fit(train_x,train_y)
predict_y = reg.predict(test_x)

1 - np.sum(abs(predict_y - test_y) &lt; 0.1*test_y)/test_y.shape[0]
</code></pre>
<p>Los resultados son los mejores hasta ahora: 43%. Pero podr&iacute;a ser mejor. Decido probar con <strong>m&aacute;quinas de vector soporte</strong>.</p>
<h2>SVM</h2>
<p>Si la regresi&oacute;n lineal ha obtenido los mejores resultados, con una SVM ser&iacute;amos capaces de llegar a ese mismo valor y posiblemente mejorarlo. Sin embargo, con un SVR lineal no pude llegar a replicarlo</p>
<pre><code class="language-python">
from sklearn.svm import SVR

svr = SVR(kernel="linear", max_iter=-1)
svr.fit(train_x,train_y)
predict_y = svr.predict(test_x)

1 - np.sum(abs(predict_y - test_y) &lt; 0.1*test_y)/test_y.shape[0]
</code></pre>
<p>Y consegu&iacute; un m&iacute;sero 51% de error. Y este era el kernel que mejor funcionaba, el lineal. Prob&eacute; poly con diferentes grados, rbf y sigmoid y todos ellos daban un resultado peor. Es hora de sacar la maquinaria pesada y entrar a tope con <strong>redes neuronales</strong>.</p>
<h2>Perceptr&oacute;n multicapa</h2>
<p>He decidido saltarme directamente el Adaline y otros modelos m&aacute;s simples ya que no creo que mejoren la regresi&oacute;n lineal. Al perceptr&oacute;n multicapa al principio le quise dar un gran n&uacute;mero de neuronas (150 al principio) en una capa oculta, con una funci&oacute;n de activaci&oacute;n RELU y un solver de tipo ADAM. Como el resultado era muy parecido al de la SVM. Empec&eacute; a hacer cambios. LBFGS es bueno en datasets peque&ntilde;os y efectivamente, los resultados mejoraron. Modifiqu&eacute; a geometr&iacute;a de la red. Con 3 capas de 100 y RELU empec&eacute; a obtener resultados mucho mejores que con cualquier m&eacute;todo, pero muy sensibles a variaciones aleatorias.&nbsp;</p>
<p>Aqu&iacute; apliqu&eacute; normalizaci&oacute;n con&nbsp;<strong>StandardScaler</strong>, pero no cambi&oacute; demasiado el resultado.</p>
<pre><code class="language-python">
# Perceptron Multicapa
from sklearn.neural_network import MLPRegressor
from sklearn.preprocessing import StandardScaler

ss = StandardScaler()
ss.fit(train_x)
train_x_ss = ss.transform(train_x)
test_x_ss = ss.transform(test_x)

mlp = MLPRegressor(hidden_layer_sizes=(100,100,100),activation="relu",solver="lbfgs",max_iter=50000)
mlp.fit(train_x_ss, train_y)
predict_y = mlp.predict(test_x_ss)

1 - np.sum(abs(predict_y - test_y) &lt; 0.1*test_y)/test_y.shape[0]
</code></pre>
<h2>Segundo test</h2>
<p>Vuelvo a subir a Kaggle. Esta vez obtengo una tasa de error de 0.17975. Ha mejorado, pero bastante poco.</p>
<h2>Random Forest</h2>
<p>Los &aacute;rboles parecieron ir bien, pruebo los&nbsp;<strong>Random Forest</strong>, con buenos resultados. Tasa de error en local del 39% y 0.17223 en Kaggle. No obstante, en Kaggle parezco haberme quedado estancado.</p>
<pre><code class="language-python">
from sklearn.ensemble import RandomForestRegressor

rand = RandomForestRegressor()
rand.fit(train_x,train_y)
predict_y = rand.predict(test_x)

1 - np.sum(abs(predict_y - test_y) &lt; 0.1*test_y)/test_y.shape[0]
</code></pre>
<h2>Limpiando datos</h2>
<p>Aqu&iacute; empec&eacute; a buscar ayuda en Internet. Algo que no hab&iacute;a realizado y que es muy interesante, es limpiar los datos. Eliminar algunas columnas espec&iacute;ficamente y rellenar otras con valores faltantes con otros valores. Volv&iacute; a ejecutar el Random Forest obteniendo un 38% en local pero un 0.14754 en Kaggle.</p>
<h2>Conclusi&oacute;n</h2>
<p>No he conseguido mi objetivo de llegar al top 25% de Kaggle. Todav&iacute;a me quedan muchas cosas por aprender. Para pr&oacute;ximos problemas debo realizar un an&aacute;lisis exploratorio de los datos m&aacute;s avanzado (aqu&iacute; casi no lo he realizado) y debo entender otras t&eacute;cnicas de regresi&oacute;n (he de decir que prefiero problemas de <strong>clasificaci&oacute;n</strong> a d&iacute;a de hoy). &iquest;Vosotros ten&eacute;is alguna idea de como mejorar los resultados, os escucho en los comentarios? Adem&aacute;s, os dejo el c&oacute;digo final.</p>
<pre><code class="language-python">
import pandas as pd
import numpy as np

data_csv = pd.read_csv("train.csv")
test_csv = pd.read_csv("test.csv")
test_id = test_csv["Id"]
size = test_csv.shape
all_data = pd.concat((test_csv,data_csv),sort=False)

all_data.drop(['SalePrice','Utilities', 'RoofMatl', 'MasVnrArea', 'BsmtFinSF1', 'BsmtFinSF2', 'BsmtUnfSF', 'Heating', 'LowQualFinSF',
               'BsmtFullBath', 'BsmtHalfBath', 'Functional', 'GarageYrBlt', 'GarageArea', 'GarageCond', 'WoodDeckSF',
               'OpenPorchSF', 'EnclosedPorch', '3SsnPorch', 'ScreenPorch', 'PoolArea', 'PoolQC', 'Fence', 'MiscFeature', 'MiscVal'],
              axis=1, inplace=True)

all_data['MSSubClass'] = all_data['MSSubClass'].astype(str)


all_data['MSZoning'] = all_data['MSZoning'].fillna(all_data['MSZoning'].mode()[0])


all_data['LotFrontage'] = all_data['LotFrontage'].fillna(all_data['LotFrontage'].mean())

all_data['Alley'] = all_data['Alley'].fillna('NOACCESS')

all_data.OverallCond = all_data.OverallCond.astype(str)


all_data['MasVnrType'] = all_data['MasVnrType'].fillna(all_data['MasVnrType'].mode()[0])

for col in ('BsmtQual', 'BsmtCond', 'BsmtExposure', 'BsmtFinType1', 'BsmtFinType2'):
    all_data[col] = all_data[col].fillna('NoBSMT')

all_data['TotalBsmtSF'] = all_data['TotalBsmtSF'].fillna(0)


all_data['Electrical'] = all_data['Electrical'].fillna(all_data['Electrical'].mode()[0])


all_data['KitchenAbvGr'] = all_data['KitchenAbvGr'].astype(str)


all_data['KitchenQual'] = all_data['KitchenQual'].fillna(all_data['KitchenQual'].mode()[0])


all_data['FireplaceQu'] = all_data['FireplaceQu'].fillna('NoFP')


for col in ('GarageType', 'GarageFinish', 'GarageQual'):
    all_data[col] = all_data[col].fillna('NoGRG')

means 0
all_data['GarageCars'] = all_data['GarageCars'].fillna(0.0)

popular values
all_data['SaleType'] = all_data['SaleType'].fillna(all_data['SaleType'].mode()[0])

all_data['YrSold'] = all_data['YrSold'].astype(str)
all_data['MoSold'] = all_data['MoSold'].astype(str)

all_data
all_data['TotalSF'] = all_data['TotalBsmtSF'] + all_data['1stFlrSF'] + all_data['2ndFlrSF']
all_data.drop(['TotalBsmtSF', '1stFlrSF', '2ndFlrSF'], axis=1, inplace=True)

all_data = pd.get_dummies(all_data,drop_first=True)

test = all_data[0:size[0]]
data = all_data[size[0]:]

x = data
y = data_csv["SalePrice"]


from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder, OneHotEncoder, LabelBinarizer


train_x, test_x, train_y, test_y = train_test_split(x,y,test_size=1/3)


# ARBOLES DE DECISION

from sklearn.tree import DecisionTreeRegressor

tree = DecisionTreeRegressor(criterion="mse",splitter="best",max_depth=None,min_samples_split=10,min_samples_leaf=5)
tree.fit(train_x,train_y)
predict_y = tree.predict(test_x)


1-np.sum(abs(predict_y - test_y) &lt; 0.1*test_y)/test_y.shape[0]


# K-MEDIAS

from sklearn.neighbors import KNeighborsRegressor

knr = KNeighborsRegressor(n_neighbors=7,p=1,metric="minkowski")
knr.fit(train_x,train_y)
predict_y = knr.predict(test_x)

1- np.sum(abs(predict_y - test_y) &lt; 0.1*test_y)/test_y.shape[0]


# Regresion Lineal

from sklearn.linear_model import LinearRegression

reg = LinearRegression()
reg.fit(train_x,train_y)
predict_y = reg.predict(test_x)

1 - np.sum(abs(predict_y - test_y) &lt; 0.1*test_y)/test_y.shape[0]


# SVM
from sklearn.svm import SVR
from sklearn.preprocessing import MinMaxScaler

svr = SVR(kernel="linear", degree=3, gamma="scale", max_iter=-1)
svr.fit(train_x,train_y)
predict_y = svr.predict(test_x)

1 - np.sum(abs(predict_y - test_y) &lt; 0.1*test_y)/test_y.shape[0]



# Perceptron Multicapa
from sklearn.neural_network import MLPRegressor
from sklearn.preprocessing import StandardScaler

ss = StandardScaler()
ss.fit(train_x)
train_x_ss = ss.transform(train_x)
test_x_ss = ss.transform(test_x)

mlp = MLPRegressor(hidden_layer_sizes=(100,100,100),activation="relu",solver="lbfgs",max_iter=50000)
mlp.fit(train_x_ss, train_y)
predict_y = mlp.predict(test_x_ss)

1 - np.sum(abs(predict_y - test_y) &lt; 0.1*test_y)/test_y.shape[0]



# Random Forest
from sklearn.ensemble import RandomForestRegressor

rand = RandomForestRegressor(n_estimators=100)
rand.fit(train_x,train_y)
predict_y = rand.predict(test_x)

1 - np.sum(abs(predict_y - test_y) &lt; 0.1*test_y)/test_y.shape[0]


# PREDECIR

rand.fit(x,y)

test_out = rand.predict(test)
out = pd.DataFrame({"Id" : test_id, "SalePrice" : test_out})
out.to_csv("out.csv",index=False)
</code></pre>]]></description>
                <comments>https://blog.adrianistan.eu/cronica-neuronal-house-prices</comments>
                <pubDate>Sun, 04 Aug 2019 16:10:41 +0000</pubDate>
            </item>
        
            <item>
                <title>Web Semántica desde cero: Linked Data y SOLID</title>
                <link>https://blog.adrianistan.eu/web-semantica-linked-data-solid</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/web-semantica-linked-data-solid</guid>
                <description><![CDATA[<p>Ya hemos visto tres componentes de la Web Sem&aacute;ntica: RDF, RDF Schema y SPARQL. Sin embargo, todav&iacute;a no hemos sido capaces de hiperconectar la informaci&oacute;n presente en diferentes servidores. La especificaci&oacute;n <a href="https://www.w3.org/TR/ldp/">Linked Data</a>, nos da unas pautas a seguir sobre como usar HTTP para conseguir que todo est&eacute; hiperconectado. Adem&aacute;s veremos el concepto de SOLID, que propuso Tim Berners-Lee para que la ciudadan&iacute;a recuperase el poder sobre sus datos en la web.</p>
<p><img src="https://files.adrianistan.eu/LinkedData.png" alt="" /></p>
<h2>Linked Data</h2>
<p>Lo primero que hay que tener en cuenta es que Linked Data nos define ciertas restricciones , entre otras, las IRI deber&aacute;n ser Web IRI, eso quiere decir que tienen que usar HTTP o HTTPS obligatoriamente como protocolo. Esto es as&iacute;, porque un concepto clave es la&nbsp;<strong>dereferenciaci&oacute;n</strong>. Es decir, dado un identificador, poder acceder al recurso en bruto. Esto es sencillo en recursos digitales, simplemente usa la direcci&oacute;n web donde est&aacute; el recurso, pero en recursos f&iacute;sicos o conceptos abstractos es complicado. Por eso se distingue entre la direcci&oacute;n web del recurso y las de la informaci&oacute;n. Pongamos un ejemplo sencillo, la receta de la tarta de Santiago.</p>
<p>Podemos definir una IRI para el recurso tal que as&iacute;: https://adrianistan.eu/recipe/tarta-santiago. En las tripletas que usen la tarta de Santiago usaremos ese identificador de recurso. &iquest;Pero qu&eacute; debe de hacer el servidor cuando accedamos a ese recurso con un GET de HTTP? En primer lugar, nuestra petici&oacute;n vendr&aacute; acompa&ntilde;ada de un campo Accept que deber&aacute; indicar el formato que queremos leer. Si es una m&aacute;quina podemos pedir XML, JSON-LD o Turtle. Si es un humano podemos indicar HTML. Entonces, el servidor manda una respuesta <strong>HTTP 303 See other</strong>, indicando la URL a la que tenemos que acceder ahora, en el formato correcto. As&iacute;, desde una aplicaci&oacute;n que pieda JSON-LD tendr&iacute;amos una redirecci&oacute;n:</p>
<p>http://adrianistan.eu/recipe/tarta-santiago -&gt; http://adrianistan.eu/recipe/tarta-santiago.json</p>
<p>Y ya podr&iacute;amos acceder a la informaci&oacute;n. Si en cambio, queremos leer la receta en formato humano la redirecci&oacute;n ser&iacute;a as&iacute;:</p>
<p>http://adrianistan.eu/recipe/tarta-santiago -&gt; http://adrianistan.eu/recipe/tarta-santiago.html</p>
<p>Seg&uacute;n los formatos que queramos leer tendremos una URL diferente, pero el IRI ser&aacute; el mismo siempre.</p>
<p>Una plataforma LinkedData completa debe ofrecer una serie de servicios para todos los verbos HTTP: GET, PUT, PATCH, HEAD, OPTIONS, DELETE, POST. De este modo se pueden dise&ntilde;ar APIs basadas en LinkedData. Sin embargo, este sistema no ha tenido mucho &eacute;xito y no voy a explicarlo en detalle.</p>
<h2>SOLID</h2>
<p><a href="https://www.genbeta.com/actualidad/tim-berners-lee-anuncia-solid-proyecto-open-source-internet-descentralizado-buenas-intenciones-algo-utopia">En 2018</a>, Tim Berners-Lee, padre de la web, propuso SOLID, un proyecto para restablecer el poder de los usuarios en la web. SOLID significia Social Linked Data y es una aplicaci&oacute;n de las t&eacute;cnolog&iacute;as de Linked Data para tratar de promover un sistema descentralizado. Podemos pensar en Solid como una especie de pincho USB. Esto son los&nbsp;<strong>pod</strong>. La gente tiene sus propios pods, ya sea autogestionados, o en sitios como los&nbsp; de correo electr&oacute;nico. Un pod no es m&aacute;s que un espacio de almacenamiento, pero fundamentado sobre las ideas de Linked Data. As&iacute;, nuestro pod contiene todos nuestros datos (a trav&eacute;s de RDF) de cada aplicaci&oacute;n y datos personales. Cuando una aplicaci&oacute;n quiere acceder a nuestros datos, le podemos dar permiso desde nuestro pod para que acceda la informaci&oacute;n que consideremos oportuna. Al usar Linked Data y RDF, diversas aplicaciones son capaces de usar los mismos datos sin cambiar de formato.</p>
<p><img src="https://files.adrianistan.eu/SOLID.png" alt="" /></p>
<p>Le deseo mucha suerte al proyecto SOLID, aunque no ha tenido mucho apoyo por parte de los desarrolladores. En parte el proyecto es bastante ut&oacute;pico, no es tan f&aacute;cil que todas las empresas dejen sus datos y hagan las transici&oacute;n a SOLID tan f&aacute;cilmente, solo ofrece ventajas para los usuarios, no para las empresas. Por otro lado, Linked Data nunca ha sido popular fuera de ambientes acad&eacute;micos o cient&iacute;ficos (al parecer estas tecnolog&iacute;as se usan bastante en biomedicina), la mayor&iacute;a de desarrolladores no conoce bien la tecnolog&iacute;a y apenas existen librer&iacute;as. Proyectos de software libre populares como Mastodon o PeerTube ni siquieran soportan SOLID.</p>
<p>Por otro lado, la transici&oacute;n a PODs por parte de los usuarios no es trivial y requiere de ense&ntilde;ar a los usuarios y a que sean conscientes de administrar sus propios pods.</p>
<p>He estado pensado sobre esto &uacute;ltimamente y creo que aunque la idea es buena tiene problemas pr&aacute;cticos. Pero, &iquest;y si combinamos los pods de SOLID con IPFS? &iquest;Y si SOLID se integrase en los navegadores? Yo mientras tanto voy a ir experimentando con esta tecnolog&iacute;a en futuros proyectos.</p>]]></description>
                <comments>https://blog.adrianistan.eu/web-semantica-linked-data-solid</comments>
                <pubDate>Mon, 22 Jul 2019 16:24:47 +0000</pubDate>
            </item>
        
            <item>
                <title>Web Semántica desde cero: SPARQL</title>
                <link>https://blog.adrianistan.eu/web-semantica-sparql</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/web-semantica-sparql</guid>
                <description><![CDATA[<p>Hasta ahora hemos visto de forma bastante te&oacute;rica el funcionamiento de RDF y de RDF Schema. Hemos dicho que RDF es un modelo de datos, interoperable y sem&aacute;ntico. No obstante, hasta ahora no hemos accedido a la informaci&oacute;n all&iacute; expuesta. Por eso en este art&iacute;culo veremos <strong>SPARQL</strong>, el lenguaje de consultas de RDF; del mismo modo que el modelo relacional tiene SQL o los documentos XML tienen XQuery.</p>
<p>SPARQL es un lenguaje de consultas frente a una base de datos RDF. Esta base de datos puede ser local o ser un servidor. Los servidores m&aacute;s conocidos son <a href="https://virtuoso.openlinksw.com/">Virtuoso</a>, <a href="https://jena.apache.org/">Apache Jena,</a> <a href="https://www.marklogic.com/">MarkLogic</a> y <a href="https://aws.amazon.com/es/neptune/">Amazon Neptune</a>. La sintaxis de SPARQL est&aacute; inspirada en <strong>Turtle</strong> y en SQL. En un origen, SPARQL solo era un lenguaje de consulta, es decir, no pod&iacute;a a&ntilde;adir, editar o borrar informaci&oacute;n. Existe una extensi&oacute;n, relativamente aceptado llamado SPARQL Update que a&ntilde;ade capacidades de edici&oacute;n a las consultas.</p>
<p><img src="https://files.adrianistan.eu/SPARQLSurname.png" alt="" /></p>
<p><em>Los apellidos m&aacute;s comunes en la Wikipedia, un ejemplo de consulta SPARQL</em></p>
<h2>SELECT</h2>
<p>La sentencia m&aacute;s importante es SELECT. La salida de SPARQL siempre es en formato tabla, pudiendo dar para una misma consulta varias filas de resultados. Cada columna es una propiedad de las que pedimos en SELECT. Para describir qu&eacute; valores de salida deben aparecer usamos variables. Las variables en SPARQL empiezan por el s&iacute;mbolo de interrogaci&oacute;n. Estas variables las usaremos m&aacute;s adelante para filtrar.</p>
<h2>FROM</h2>
<p>La sentencia FROM en SPARQL es opcional. Indica el o los grafos sobre los que se debe buscar. Por defecto existe un grafo activo, si no lo indicamos, se usar&aacute; este.</p>
<h2>WHERE</h2>
<p>La sentencia que nos permite filtrar las tripletas RDF. Su sintaxis es muy simple, simplemente escribimos tripletas con variables entre llaves. El motor SPARQL realizar&aacute; <strong>unificaci&oacute;n</strong> buscando las variables para las que todas las tripletas existen en la base de datos. Cada tripleta se separa por un punto. Podemos usar namespaces al igual que en otras partes de la web sem&aacute;ntica</p>
<p>Con esto podemos escribir ya una consulta sencilla.</p>
<pre><code>
PREFIX wd: &lt;http://www.wikidata.org/entity/&gt;
PREFIX wdt: &lt;http://www.wikidata.org/prop/direct/&gt;
PREFIX rdfs: &lt;http://www.w3.org/2000/01/rdf-schema#&gt;

SELECT ?city ?city_name
WHERE {
  ?city wdt:P31 wd:Q515 .
  ?city wdt:P17 wd:Q29 .
  ?city rdfs:label ?city_name .
}
</code></pre>
<p>Esta consulta, <a href="https://query.wikidata.org/#PREFIX%20wd%3A%20%3Chttp%3A%2F%2Fwww.wikidata.org%2Fentity%2F%3E%0APREFIX%20wdt%3A%20%3Chttp%3A%2F%2Fwww.wikidata.org%2Fprop%2Fdirect%2F%3E%0APREFIX%20rdfs%3A%20%3Chttp%3A%2F%2Fwww.w3.org%2F2000%2F01%2Frdf-schema%23%3E%0A%0ASELECT%20%3Fcity%20%3Fcity_name%0AWHERE%20%7B%0A%20%20%3Fcity%20wdt%3AP31%20wd%3AQ515%20.%0A%20%20%3Fcity%20wdt%3AP17%20wd%3AQ29%20.%0A%20%20%3Fcity%20rdfs%3Alabel%20%3Fcity_name%20.%0A%7D">que pod&eacute;is ejecutar en Wikidata vosotros mismos</a>, nos devuelve una tabla de recursos y nombres de ciudades de Espa&ntilde;a. Las tripletas, un pel&iacute;n cr&iacute;pticas debido a la nomenclatura de Wikidata, son las siguientes:</p>
<ol>
<li>(CITY,esInstanciaDe,wikidata:Ciudad)</li>
<li>(CITY,pais,wikidata:Espa&ntilde;a)</li>
<li>(CITY,rdfs:label,CITY_NAME)</li>
</ol>
<p>Al ejecutar esta sentencia, el motor SPARQL busca valores para CITY y CITY_NAME para los que las tres tripletas est&eacute;n definidas.</p>
<p><img src="https://files.adrianistan.eu/SPARQLTable.png" alt="" /></p>
<p>Aqu&iacute; vemos el resultado de la ejecuci&oacute;n. Como vemos, todas las salidad de la foto corresponden a Madrid, el mismo recurso pero en diferentes idiomas. Eso es porque rdfs:label soporta diferentes idiomas para el valor. Afortunadamente podemos filtrar por lenguaje gracias a una carecter&iacute;stica de RDF que permite especificar el lenguaje de las tripletas (xml:lang).</p>
<h2>FILTER</h2>
<p>La sentencia que debemos usar es <strong>FILTER</strong>, para filtrar los resultados en base a otros:</p>
<pre><code>
PREFIX wd: &lt;http://www.wikidata.org/entity/&gt;
PREFIX wdt: &lt;http://www.wikidata.org/prop/direct/&gt;
PREFIX rdfs: &lt;http://www.w3.org/2000/01/rdf-schema#&gt;

SELECT ?city ?city_name
WHERE {
  ?city wdt:P31 wd:Q515 .
  ?city wdt:P17 wd:Q29 .
  ?city rdfs:label ?city_name .
  FILTER (lang(?city_name) = 'es')
}
</code></pre>
<p>Los resultados ahora son &uacute;nicos ya que nos muestra solo los nombres en Espa&ntilde;ol. FILTER es muy potente, tambi&eacute;n admite<strong> expresiones booleanas</strong> (como ?edad &gt; 30) y <strong>regex</strong> para realizar una comprobaci&oacute;n con expresiones regulares de la cadena de texto.</p>
<h2>DISTINCT</h2>
<p>Sin embargo, puede haber valores repetidos. En ocasiones simplemente queremos los elementos diferentes, no nos importa si se repiten o no. Podemos usar <strong>DISTINCT.</strong></p>
<p><img src="https://files.adrianistan.eu/SPARQLDistinct.png" alt="" /></p>
<h2>COUNT, SUM, MAX, MIN, ...</h2>
<p>Al igual que en SQL, en SPARQL podemos usar agregadores en las consultas. Esto requiere SPARQL 1.1:</p>
<pre><code>
PREFIX wd: &lt;http://www.wikidata.org/entity/&gt;
PREFIX wdt: &lt;http://www.wikidata.org/prop/direct/&gt;
PREFIX rdfs: &lt;http://www.w3.org/2000/01/rdf-schema#&gt;

SELECT (COUNT(?city_name) AS ?n_city)
WHERE {
  ?city wdt:P31 wd:Q515 .
  ?city wdt:P17 wd:Q29 .
  ?city rdfs:label ?city_name .
  FILTER (lang(?city_name) = 'es')
}
</code></pre>
<p>&nbsp;</p>
<h2>OPTIONAL</h2>
<p>En SPARQL podemos definir tripletas en un WHERE que queremos que se cumplan, pero que no queremos que rechace los datos si no lo hace. Esto es normalmente usado para obtener datos que pueden existir o no. Se usa un bloque <strong>OPTIONAL</strong> dentro del WHERE.</p>
<pre><code>
PREFIX wd: &lt;http://www.wikidata.org/entity/&gt;
PREFIX wdt: &lt;http://www.wikidata.org/prop/direct/&gt;
PREFIX rdfs: &lt;http://www.w3.org/2000/01/rdf-schema#&gt;

SELECT ?city_name ?brother_name
WHERE {
  ?city wdt:P31 wd:Q515 .
  ?city wdt:P17 wd:Q29 .
  ?city rdfs:label ?city_name .
  OPTIONAL {
    ?city wdt:P190 ?brother .
    ?brother rdfs:label ?brother_name .
    FILTER ( lang(?brother_name) = 'es') 
  }
  FILTER (lang(?city_name) = 'es')
}
</code></pre>
<p>En este caso obtenemos tambi&eacute;n las <a href="https://es.wikipedia.org/wiki/Hermanamiento_de_ciudades">ciudades hermanas</a> de una ciudad. Sin embargo, es algo que no todas las ciudades tienen por qu&eacute; tener.</p>
<h2>UNION</h2>
<p>Al igual que en SQL, podemos disponer de varias condiciones diferentes, las cuales ambas son v&aacute;lidas. En SPARQL se expresa con la palabra&nbsp;<strong>UNION</strong> y dos bloques de llaves. Cada bloque es independiente del otro. Podemos decir que se ejecutan dos consultas y se juntan los resultados. Esto puede ser muy &uacute;til si tenemos datos con ontolog&iacute;as parecidas pero no iguales y queremos obtener datos de ambas de ellas a la vez.</p>
<h2>Property Paths</h2>
<p>Se trata de una caracter&iacute;stica que nos ahorra trabajo. Permiten describir caminos sobre el grafo, de longitud variable, de forma mucho m&aacute;s c&oacute;moda y breve. Sin entrar mucho en detalle, podemos usar la barra (/) para concatenar propiedades. Si tenemos una estructura tal que X p1 Y y Y p2 Z y solo nos interesan X y Z, podemos escribir X p1/p2 Z.</p>
<pre><code>
PREFIX wd: &lt;http://www.wikidata.org/entity/&gt;
PREFIX wdt: &lt;http://www.wikidata.org/prop/direct/&gt;
PREFIX rdfs: &lt;http://www.w3.org/2000/01/rdf-schema#&gt;

SELECT ?city_name ?brother_name
WHERE {
  ?city wdt:P31 wd:Q515 .
  ?city wdt:P17 wd:Q29 .
  ?city rdfs:label ?city_name .
  OPTIONAL {
    ?city wdt:P190/rdfs:label ?brother_name .
    FILTER (lang(?brother_name) = 'es') .
  }
  FILTER (lang(?city_name) = 'es')
}
</code></pre>
<h2>ORDER BY</h2>
<p>Al igual que en SQL, se puede ordenar la tabla saliente por un campo de los presentes en SELECT.</p>
<h2>LIMIT</h2>
<p>Podemos limitar el n&uacute;mero de resultados con LIMIT al final de la consulta.</p>
<h2>CONSTRUCT</h2>
<p>Si con SELECT generamos tablas, con&nbsp;<strong>CONSTRUCT</strong> podemos construir documentos RDF como salida de la consulta. Para ello usamos el lenguaje de tripletas que usamos en la cl&aacute;usula WHERE, salvo que aqu&iacute; las variables ser&aacute;n los valores de salida.</p>
<pre><code>
PREFIX wd: &lt;http://www.wikidata.org/entity/&gt;
PREFIX wdt: &lt;http://www.wikidata.org/prop/direct/&gt;
PREFIX rdfs: &lt;http://www.w3.org/2000/01/rdf-schema#&gt;

CONSTRUCT {
  ?city rdfs:label ?city_name .
}
WHERE {
  ?city wdt:P31 wd:Q515 .
  ?city wdt:P17 wd:Q29 .
  ?city rdfs:label ?city_name .
  OPTIONAL {
    ?city wdt:P190/rdfs:label ?brother_name .
    FILTER (lang(?brother_name) = 'es') .
  }
  FILTER (lang(?city_name) = 'es')
}
</code></pre>
<h2>ASK</h2>
<p>Otra opci&oacute;n adem&aacute;s de&nbsp;<strong>SELECT</strong> y&nbsp;<strong>CONSTRUCT</strong> es&nbsp;<strong>ASK</strong>. Con ASK preguntamos si un conjunto de tripletas en cuesti&oacute;n existen en la base de datos. Esto nos devuelve true o false.</p>
<pre><code>
PREFIX wd: &lt;http://www.wikidata.org/entity/&gt;
PREFIX wdt: &lt;http://www.wikidata.org/prop/direct/&gt;
PREFIX rdfs: &lt;http://www.w3.org/2000/01/rdf-schema#&gt;

ASK {
  ?city rdfs:label "Valladolid"@es .
  ?city wdt:P31 wd:Q515 .
  ?city wdt:P17 wd:Q29 .
}
</code></pre>
<p>ASK no admite WHERE, porque en realidad ya es un WHERE. Aqu&iacute; hay un detalle sobre el soporte a idiomas de RDF. En la consulta la string es "Valladolid"@es y no "Valladolid". Esto es as&iacute; para indicar que la string es Valladolid y est&aacute; en espa&ntilde;ol. No es lo mismo "Valladolid"@fr que "Valladolid"@es, para RDF son dos valores distintos.</p>
<h2>INSERT / DELETE</h2>
<p>Originalmente, SPARQL no permit&iacute;a modificar los datos. Sin embargo, HP y otras empresas desarrollaron una extensi&oacute;n llamada SPARQL Update para poder hacerlo. Finalmente, fue incorporado a SPARQL 1.1. La sintaxis es&nbsp;<strong>Turtle</strong>. La primera orden es&nbsp;<strong>INSERT DATA</strong>.</p>
<pre><code>
PREFIX dc: &lt;http://purl.org/dc/elements/1.1/&gt;

INSERT DATA{
    &lt;http://libros.com/2001&gt; dc:title "2001: una odisea en el espacio" ;
                                           dc:creator "Arthur C. Clarke" .
}
</code></pre>
<p><strong>DELETE DATA</strong> es exactamente igual. Si queremos usar variables, tenemos con una sintaxis ligeramente diferente para&nbsp;<strong>INSERT </strong>y&nbsp;<strong>DELETE</strong>.</p>
<pre><code>
# DATOS ANTES
@prefix foaf: &lt;http://xmlns.com/foaf/0.1/&gt; . 
&lt;http://example/president25&gt; foaf:givenName "Bill" . 
&lt;http://example/president25&gt; foaf:familyName "McKinley" . 
&lt;http://example/president27&gt; foaf:givenName "Bill" . 
&lt;http://example/president27&gt; foaf:familyName "Taft" . 
&lt;http://example/president42&gt; foaf:givenName "Bill" . 
&lt;http://example/president42&gt; foaf:familyName "Clinton" .

# SPARQL
PREFIX foaf: &lt;http://xmlns.com/foaf/0.1/&gt;
DELETE { ?person foaf:givenName 'Bill' } 
INSERT{ ?person foaf:givenName 'William' }
WHERE { ?person foaf:givenName 'Bill' }

# DATOS DESPU&Eacute;S
@prefix foaf: &lt;http://xmlns.com/foaf/0.1/&gt; .
&lt;http://example/president25&gt; foaf:givenName "William" .
&lt;http://example/president25&gt; foaf:familyName "McKinley" .
&lt;http://example/president27&gt; foaf:givenName "William" .
&lt;http://example/president27&gt; foaf:familyName "Taft" .
&lt;http://example/president42&gt; foaf:givenName "William" .
&lt;http://example/president42&gt; foaf:familyName "Clinton" .
</code></pre>
<h2>Conclusi&oacute;n</h2>
<p>Con esto queda explicado el 90% del uso de SPARQL, el lenguaje de consulta propuesto por W3C para manipular grafos RDF. Es un lenguaje potente y con bastante soporte. Idealmente, los clientes podr&iacute;an hacer las consultas directamente al servidor, supliendo en bastantes casos de uso a otras tecnolog&iacute;as como REST o GraphQL.</p>]]></description>
                <comments>https://blog.adrianistan.eu/web-semantica-sparql</comments>
                <pubDate>Fri, 19 Jul 2019 21:06:08 +0000</pubDate>
            </item>
        
            <item>
                <title>Web Semántica desde cero: RDF Schema</title>
                <link>https://blog.adrianistan.eu/web-semantica-rdf-schema</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/web-semantica-rdf-schema</guid>
                <description><![CDATA[<p>En el art&iacute;culo anterior comentamos la piedra angular de la web sem&aacute;ntica, el modelo de datos RDF. RDF es muy flexible y potente, pero &iquest;c&oacute;mo conseguimos dar significado a las relaciones? Al fin y al cabo, las tripletas no dejan de ser tuplas de strings de cara al ordenador. Tiene que haber un componente, que permita hacer de intermediario entre el significado humano y la m&aacute;quina. Esto son las <strong>ontolog&iacute;as</strong>. Las ontolog&iacute;as definen el sentido sem&aacute;ntico de los recursos de RDF. RDF Schema nos permite dise&ntilde;ar ontolog&iacute;as de forma sencilla. Como veremos, a&ntilde;ade operadores que podemos usar en RDF para definir las ontolog&iacute;as. Por tanto, RDF Schema no es comparable a XML Schema o JSON Schema, que sirven para validar que un documento sigue una estructura, sino que sirve para dotar de significado a la informaci&oacute;n.</p>
<p><img src="https://files.adrianistan.eu/RDFSchemaWiki.png" alt="" width="500" /></p>
<h2>Clases</h2>
<p>El primer concepto clave de RDF Schema son las clases. Los recursos pueden pertenecer a una clase. La clase de un recurso se especifica con la propiedad ya vista anteriormente de <strong>rdf:type</strong>. Las clases se relacionan entre s&iacute; mediante propiedades. Aqu&iacute; se pueden definir dos conceptos: <strong>rango</strong> y&nbsp;<strong>dominio</strong>. Su significado es similar al de las funciones matem&aacute;ticas. El rango representa los conjuntos de valores que puede tomar un atributo. Y el dominio, los recursos sobre los que tiene sentido aplicar ese atributo. Por tanto en una tripleta que use una propiedad, el recurso deber&aacute; estar dentro del dominio y el valor dentro del rango. Mencionar que las propiedades son tambi&eacute;n recursos, en esencia.</p>
<p>Adem&aacute;s se definen relaciones de herencia entre clases, de tal modo que existen subclases y superclases, como en programaci&oacute;n orientada a objetos. Las subclases heredan los atributos de su superclase y adem&aacute;s son tambi&eacute;n superclase. La herencia que soporta RDF Schema es <strong>herencia m&uacute;ltiple</strong>, donde una clase puede tener varias superclases. Tambi&eacute;n existe herencia de atributos, habiendo subatributos y superatributos. Si tenemos un atributo A, y un subatributo B se entiende que usar el atributo B implica el atributo A para el mismo objeto y valor.</p>
<p>Sin embargo, a diferencia de la POO, aqu&iacute; las clases son conceptos abiertos y podemos integrar nuevas propiedades sobre la marcha, modificando clases ya existentes. Esta flexibilidad es necesaria, para que funcione en un mundo tan heterog&eacute;neo como es la web.</p>
<h3>Clases predefinidas por RDF Schema</h3>
<ul>
<li><strong>rdfs:Class</strong>: La superclase absoluta. Toda clase es hija de esta</li>
<li><strong>rdfs:Resource</strong>: La clase que de la que heredan todos los recursos (las propiedades tambi&eacute;n son recursos)</li>
<li><strong>rdfs:Literal</strong>: La clase de la que heredan todos los valores at&oacute;micos o literales</li>
<li><strong>rdf:Property</strong>: La clase de la que heredan todas las propiedades</li>
</ul>
<h3>Propiedades predefindas por RDF Schema</h3>
<ul>
<li><strong>rdfs:range</strong>: Para indicar el rango de una propiedad</li>
<li><strong>rdfs:domain</strong>: Para indicar el dominio de una propiedad</li>
<li><strong>rdfs:subPropertyOf</strong>: Para indicar que una propiedad hereda de otra</li>
<li><strong>rdfs:subClassOf</strong>: Para indicar que una clase hereda de otra</li>
<li><strong>rdfs:comment</strong>: Descripci&oacute;n en formato humano de un recurso</li>
<li><strong>rdfs:label</strong>: El nombre en formato humano de un recurso</li>
<li><strong>rdfs:isDefinedBy</strong>: Para indicar que recurso define a otro</li>
<li><strong>rdfs:seeAlso</strong>: Informaci&oacute;n adicional sobre el recurso</li>
<li><strong>rdfs:member</strong>: Miembro del recurso</li>
<li><strong>rdf:type</strong>: La clase de un recurso</li>
</ul>
<p>Ahora veamos un ejemplo de ontolog&iacute;a en formato XML.</p>
<p><img src="https://files.adrianistan.eu/RDFClass.png" alt="" /></p>
<p>Aqu&iacute; se definen dos clases: animal y horse. Y horse se define como subclase de animal. Por tanto un recurso de la clase horse es a la vez de la clase animal.</p>
<p><img src="https://files.adrianistan.eu/RDFSchema.png" alt="" /></p>
<p>Esta ontolog&iacute;a es m&aacute;s completa, porque define propiedades tambi&eacute;n</p>
<h2>Inferencia</h2>
<p>Una caracter&iacute;stica de las ontolog&iacute;as es que podemos realizar inferencias a trav&eacute;s de ellas. Inferir algo es generar informaci&oacute;n nueva aplicando reglas l&oacute;gicas. Veamos un ejemplo. Bas&aacute;ndonos en la ontolog&iacute;a de la &uacute;ltima foto vamos a tener dos tripletas:</p>
<ol>
<li>("Matem&aacute;tica Discreta","isTaughtBy","Grigoris Antoniu")</li>
<li>("isTaughtBy","rdfs:range","Miembro Acad&eacute;mico")</li>
</ol>
<p>Entonces podemos deducir que:</p>
<ul>
<li>("Grigoris Antoniu","rdf:type","Miembro Acad&eacute;mico")</li>
</ul>
<p>Ya que Grigoris est&aacute; admitido en el rango de isTaughtBy y el rango de isTaughtBy solo admite "Miembro Acad&eacute;mico".</p>
<p>Con esto ya tenemos lo b&aacute;sico para dise&ntilde;ar ontolog&iacute;as. Actualmente existen muchas ontolog&iacute;as: Schema.org (colaboraci&oacute;n entre Google, Bing, Yandex, recursos web principalmente), Dublin Core (recursos digitales), DOAP (describir proyectos de software), FOAF (personas y actividades), SIOC (comunidades online), SKOS (para describir diccionarios, tesauros, conceptos).</p>
<p>RDF Schema es simple y f&aacute;cil de entender, pero poco potente. Para solventar algunos de los problemas de RDF Schema se dise&ntilde;&oacute; OWL. En el siguiente art&iacute;culo veremos algo ya mucho m&aacute;s pr&aacute;ctico, realizar consultas en SPARQL sobre recursos RDF.</p>]]></description>
                <comments>https://blog.adrianistan.eu/web-semantica-rdf-schema</comments>
                <pubDate>Thu, 18 Jul 2019 12:43:45 +0000</pubDate>
            </item>
        
            <item>
                <title>Web Semántica desde cero: RDF</title>
                <link>https://blog.adrianistan.eu/web-semantica-rdf</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/web-semantica-rdf</guid>
                <description><![CDATA[<p>Este art&iacute;culo es el primero de una serie de tutoriales que van a explorar la <strong>web sem&aacute;ntica</strong> a d&iacute;a de hoy, fuera de todo hype inicial y todo el universo de los&nbsp;<strong>datos enlazados</strong>. Lo primero ser&aacute; entender el concepto fundamental y lo segundo, RDF, piedra angular de la web sem&aacute;ntica tal y como la describe W3C.</p>
<p>La idea fundamental de la web sem&aacute;ntica, es tener una web con significado, interpretable tanto por humanos como por m&aacute;quinas. En la web tradicional, las webs no tienen significado, para un ordenador no deja de ser texto que no es capaz de interpretar con facilidad. La web sem&aacute;ntica es una red, interconectada de datos con valor sem&aacute;ntico. El valor sem&aacute;ntico se expresa a trav&eacute;s de ontolog&iacute;as. RDF es el modelo de datos propuesto por W3C para modelar esta red. No es la &uacute;nica propuesta, tambi&eacute;n existen microformats, microdata y otros formatos. Sin embargo, RDF es lo suficientemente maduro como para basarnos en &eacute;l directamente, adem&aacute;s de ser muy flexible.</p>
<p><img src="https://files.adrianistan.eu/RDFLisa.jpg" alt="" width="850" height="579" /></p>
<p>En esta imagen, cortes&iacute;a de W3C, podemos ver el concepto de datos enlazados, en un grafo, donde la informaci&oacute;n se relaciona a trav&eacute;s de propiedades comunes que todos entienden, pero la informaci&oacute;n reside en servidores diferentes. As&iacute;, aunque el perfil de usuario de Bob exista en http://example.org, sabemos que le interesa la Mona Lisa y que su autor fue Leonardo da Vinci. Simplemente siguiendo el grafo, podemos conocer esa informaci&oacute;n, aunque Bob no especificase ni guardase en su servidor en ning&uacute;n momento que la Mona Lisa fue obra de Leonardo da Vinci.</p>
<h2>&iquest;Qu&eacute; es RDF?</h2>
<p>RDF son las iniciales de&nbsp;<em>Resource Description Framework</em> y es un modelo de datos. Define una forma en la que se representa toda la informaci&oacute;n. RDF se basa en el concepto, ya usado con anterioridad, de Objeto-Atributo-Valor (tambi&eacute;n llamado sujeto-predicado-objeto). Esto es muy sencillo de entender y a la vez, muy flexible. B&aacute;sicamente, toda la informaci&oacute;n se almacena en tripletas. Una tripleta son tres valores: recurso, propiedad y valor. El recurso es el individuo sobre el que decimos algo, la propiedad es la caracter&iacute;stica sobre la que decimos algo y el valor es el contenido. Por ejemplo, podemos modelar una persona f&aacute;cilmente usando tripletas.</p>
<pre><code>
("Alonzo Church","fecha-nacimiento","1903-06-14")
("Alonzo Church","lugar-nacimiento","Washington D.C.")
("Alonzo Church","profesion","matematico")
</code></pre>
<p>Los valores pueden ser a su vez recursos o pueden ser valores at&oacute;micos. De este modo se logra un grafo de relaciones. Las tripletas RDF se pueden almacenar en muchos formatos diferentes. Al principio, se hizo mucho &eacute;nfasis en la sintaxis XML. Hoy d&iacute;a tambi&eacute;n son muy comunes las sintaxis Turtle, N3 o JSON-LD. RDF presenta un modelo de datos semitipado. Sobre esto hablaremos m&aacute;s adelante. En esto que hemos visto surge un problema, &iquest;c&oacute;mo identificamos cada objeto dentro de un sistema interoperable? Si estuvi&eacute;semos en Microsoft alguien habr&iacute;a dicho <a href="https://es.wikipedia.org/wiki/Identificador_%C3%BAnico_global">GUID</a>, pero en la W3C la respuesta fue IRI.</p>
<h2>&iquest;Qu&eacute; es IRI?</h2>
<p>IRI son las iniciales de <em>Internationalized Resource Identifier</em>, se trata de la versi&oacute;n m&aacute;s extensa de las que las URL son un caso particular. Los IRI se definen como los identificadores en toda la web, seg&uacute;n W3C. Una IRI puede ser http://www.google.com/ o tel:+34000111222. Ambas cadenas permiten identificar un recurso. Ese es el concepto de IRI. Si el identificador es a la vez un "puntero" ya que nos permite a su vez localizar la informaci&oacute;n, hablamos de IRL. En RDF tenemos que usar IRI, es decir, no tienen por qu&eacute; permitir la localizaci&oacute;n de un recurso, pero s&iacute; lo deben identificar de forma inequ&iacute;cova y exclusiva.</p>
<h2>Namespaces</h2>
<p>En RDF hemos dicho que necesitamos usar IRI en las tripletas. Los IRI pueden ser muy largos, y con muchas partes repetidas. Para ello, se usan los espacios de nombres, que al igual que en XML, nos permiten acortar lo que escribimos. Un beneficio secundario es que podemos usar espacios de nombres ya definidos y tener as&iacute; la interoperabilidad que es la base de la web sem&aacute;ntica. Esto son las denominadas ontolog&iacute;as. Algunas de las m&aacute;s conocidas son Schema.org, Dublin Core y FOAF.</p>
<h2>Sintaxis de RDF</h2>
<p>Vamos a crear un ejemplo real de uso de RDF y comprobaremos las diferencias entre las distintas sintaxis. Para ello, vamos a elegir un conjunto de canciones.</p>
<h3>Tripletas Objeto-Atributo-Valor</h3>
<pre><code>
("https://blog.adrianistan.eu/song/Vagabundear","https://blog.adrianistan.eu/web-semantica-rdf/autor","https://blog.adrianistan.eu/group/NuestroPeque&ntilde;oMundo")
("https://blog.adrianistan.eu/song/Vagabundear","https://blog.adrianistan.eu/web-semantica-rdf/album","https://blog.adrianistan.eu/web-semantica-rdf/CantarTierraMia")
("https://blog.adrianistan.eu/song/CantigaApocrifa","https://blog.adrianistan.eu/web-semantica-rdf/autor","https://blog.adrianistan.eu/group/NuestroPeque&ntilde;oMundo")
("https://blog.adrianistan.eu/song/CantigaApocrifa","https://blog.adrianistan.eu/web-semantica-rdf/album","https://blog.adrianistan.eu/web-semantica-rdf/CantarTierraMia")
("https://blog.adrianistan.eu/group/NuestroPeque&ntilde;oMundo","https://blog.adrianistan.eu/web-semantica-rdf/pais","https://blog.adrianistan.eu/country/Espa&ntilde;a")
("https://blog.adrianistan.eu/group/NuestroPeque&ntilde;oMundo","https://blog.adrianistan.eu/web-semantica-rdf/genero","https://blog.adrianistan.eu/gender/Folk")
("https://blog.adrianistan.eu/web-semantica-rdf/CantarTierraMia","https://blog.adrianistan.eu/web-semantica-rdf/fecha","1975")
</code></pre>
<p>Ahora veamos como estas tripletas, que ya usan IRI, se pasan a las diferentes sintaxis.</p>
<h3>Grafo</h3>
<p><img src="https://files.adrianistan.eu/RDFGraph2.png" alt="" /></p>
<p>La representaci&oacute;n de grafo es &uacute;til para ver las interrelaciones, aunque r&aacute;pidamente se puede volver bastante dif&iacute;cil de leer. En cualquier caso, siempre tenemos que tener en mente que el modelo RDF genera un grafo.</p>
<h3>XML</h3>
<p><img src="https://files.adrianistan.eu/RDFXML.png" alt="" /></p>
<p>La sintaxis XML es verbosa, pero admite muchas opciones. En primer lugar definimos una etiqueta global RDF, que en este caso est&aacute; dentro del namespace rdf, que usamos para referirnos al est&aacute;ndar RDF de 1999 (la &uacute;ltima versi&oacute;n es de 2014, pero en este ejemplo no hay cambios). Cada recurso tiene un <strong>rdf:Description</strong>, pero si queremos que este objeto tenga un tipo o clase (que se indica con <strong>rdf:type</strong>) podemos simplemente usar ese nombre como etiqueta (en el ejemplo lo hacemos con&nbsp;<strong>ex:cancion</strong>). Esto forma parte de RDF Schema y lo veremos en el siguiente art&iacute;culo. Cada objeto tiene un IRI, expresado con el atributo <strong>rdf:about</strong>. rdf:about admite IRIs absolutas y relativas. Tambi&eacute;n existe,&nbsp;<strong>rdf:ID</strong>, que solo admite rutas relativas, pero algo mejor. Cuando el valor de una tripleta es at&oacute;mico, lo podemos incluir directamente, si es otro recurso, usamos <strong>rdf:resource</strong>.&nbsp;</p>
<p>Los ficheros XML <a href="https://www.w3.org/RDF/Validator/">se pueden validar en el servicio de W3C</a>.</p>
<h3>JSON-LD</h3>
<pre><code>
{
  "@context": {
    "ex": "https://blog.adrianistan.eu/web-semantica-rdf/",
    "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
    "rdfs": "http://www.w3.org/2000/01/rdf-schema#",
    "xsd": "http://www.w3.org/2001/XMLSchema#"
  },
  "@graph": [
    {
      "@id": "https://blog.adrianistan.eu/group/NuestroPeque&ntilde;oMundo",
      "ex:genero": {
        "@id": "https://blog.adrianistan.eu/gender/Folk"
      },
      "ex:pais": {
        "@id": "https://blog.adrianistan.eu/country/Espa&ntilde;a"
      }
    },
    {
      "@id": "https://blog.adrianistan.eu/song/Vagabundear",
      "@type": "ex:cancion",
      "ex:album": {
        "@id": "#CantarTierraMia"
      },
      "ex:autor": {
        "@id": "https://blog.adrianistan.eu/group/NuestroPeque&ntilde;oMundo"
      }
    },
    {
      "@id": "#CantarTierraMia",
      "ex:fecha": "1975"
    },
    {
      "@id": "https://blog.adrianistan.eu/song/CantigaApocrifa",
      "@type": "ex:cancion",
      "ex:album": {
        "@id": "#CantarTierraMia"
      },
      "ex:autor": {
        "@id": "https://blog.adrianistan.eu/group/NuestroPeque&ntilde;oMundo"
      }
    }
  ]
}
</code></pre>
<p>JSON-LD fue creado mucho m&aacute;s recientemente, debido a la p&eacute;rdida de popularidad de XML. Una parte de la comunidad quer&iacute;a seguir usando RDF con JSON, as&iacute; surgi&oacute; JSON-LD (JSON for Linking Data).</p>
<p>Como vemos, es menos verboso. Se distinguen dos partes fundamentales: <strong>@context</strong> y @<strong>graph</strong>. En @context van los namespaces que vamos a usar. En @graph tenemos un array con los recursos. Cada recurso tiene una propiedad @id, su IRI (tanto relativos como absolutos) y sus propiedades. Las propiedades pueden ser valores directamente u objetos con propiedad @id, que hacen referencia a otro objeto. JSON-LD es muy sencillo de usar, pero tiene poco soporte.</p>
<h3>N3</h3>
<pre><code>
@prefix ex: &lt;https://blog.adrianistan.eu/web-semantica-rdf/&gt; .
@prefix rdf: &lt;http://www.w3.org/1999/02/22-rdf-syntax-ns#&gt; .
@prefix rdfs: &lt;http://www.w3.org/2000/01/rdf-schema#&gt; .
@prefix xml: &lt;http://www.w3.org/XML/1998/namespace&gt; .
@prefix xsd: &lt;http://www.w3.org/2001/XMLSchema#&gt; .

&lt;https://blog.adrianistan.eu/song/CantigaApocrifa&gt; a ex:cancion ;
    ex:album &lt;#CantarTierraMia&gt; ;
    ex:autor &lt;https://blog.adrianistan.eu/group/NuestroPeque&ntilde;oMundo&gt; .

&lt;https://blog.adrianistan.eu/song/Vagabundear&gt; a ex:cancion ;
    ex:album &lt;#CantarTierraMia&gt; ;
    ex:autor &lt;https://blog.adrianistan.eu/group/NuestroPeque&ntilde;oMundo&gt; .

&lt;#CantarTierraMia&gt; ex:fecha "1975" .

&lt;https://blog.adrianistan.eu/group/NuestroPeque&ntilde;oMundo&gt; ex:genero &lt;https://blog.adrianistan.eu/gender/Folk&gt; ;
    ex:pais &lt;https://blog.adrianistan.eu/country/Espa&ntilde;a&gt; .
</code></pre>
<p>N3 fue un formato dise&ntilde;ado espec&iacute;ficamente para RDF por Tim Berners-Lee y W3C. Es posiblemente el lenguaje menos verboso. Al principio se definen los namespaces. A continuaci&oacute;n se definen los recursos. El IRI del recurso y a continuaci&oacute;n separado por punto y coma, propiedad y valor, finalizando con un punto. Siempre que queramos usar un IRI usamos comillas latinas. Los valores at&oacute;micos entre comillas inglesas. N3 dispone de muchas cosas, como soporte para reglas, un subconjunto con todo lo necesario para RDF es <strong>Turtle</strong>. Este c&oacute;digo por ejemplo, tambi&eacute;n es v&aacute;lido en Turtle.</p>
<h3>N-Triples</h3>
<pre><code>
&lt;https://blog.adrianistan.eu/song/Vagabundear&gt; &lt;https://blog.adrianistan.eu/web-semantica-rdf/album&gt; &lt;#CantarTierraMia&gt; .
&lt;https://blog.adrianistan.eu/group/NuestroPeque&ntilde;oMundo&gt; &lt;https://blog.adrianistan.eu/web-semantica-rdf/genero&gt; &lt;https://blog.adrianistan.eu/gender/Folk&gt; .
&lt;https://blog.adrianistan.eu/song/CantigaApocrifa&gt; &lt;http://www.w3.org/1999/02/22-rdf-syntax-ns#type&gt; &lt;https://blog.adrianistan.eu/web-semantica-rdf/cancion&gt; .
&lt;https://blog.adrianistan.eu/song/CantigaApocrifa&gt; &lt;https://blog.adrianistan.eu/web-semantica-rdf/autor&gt; &lt;https://blog.adrianistan.eu/group/NuestroPeque\u00F1oMundo&gt; .
&lt;https://blog.adrianistan.eu/group/NuestroPeque&ntilde;oMundo&gt; &lt;https://blog.adrianistan.eu/web-semantica-rdf/pais&gt; &lt;https://blog.adrianistan.eu/country/Espa\u00F1a&gt; .
&lt;https://blog.adrianistan.eu/song/CantigaApocrifa&gt; &lt;https://blog.adrianistan.eu/web-semantica-rdf/album&gt; &lt;#CantarTierraMia&gt; .
&lt;#CantarTierraMia&gt; &lt;https://blog.adrianistan.eu/web-semantica-rdf/fecha&gt; "1975" .
&lt;https://blog.adrianistan.eu/song/Vagabundear&gt; &lt;http://www.w3.org/1999/02/22-rdf-syntax-ns#type&gt; &lt;https://blog.adrianistan.eu/web-semantica-rdf/cancion&gt; .
&lt;https://blog.adrianistan.eu/song/Vagabundear&gt; &lt;https://blog.adrianistan.eu/web-semantica-rdf/autor&gt; &lt;https://blog.adrianistan.eu/group/NuestroPeque\u00F1oMundo&gt; .
</code></pre>
<p>El &uacute;ltimo formato es b&aacute;sicamente lo mismo que he puesto al principio, pero de forma estandarizada. N-Triples es muy simple e incluso menos verboso que XML en ocasiones, pero hay que repetir los IRI de objeto cada vez. Tampoco soporta namespaces.</p>
<h3>RDFa, microdata, ...</h3>
<p>Existen otros formatos para representar RDF, quiz&aacute; el m&aacute;s popular de los que faltan sea RDFa. Este formato se acopla a HTML y permite introducir RDF en HTML. Este formato es muy usado en SEO, aunque Google tambi&eacute;n reconoce JSON-LD.</p>
<h2>Un paso m&aacute;s</h2>
<p>A continuaci&oacute;n lo m&aacute;s interesante ser&iacute;a definir ontolog&iacute;as o schemas, para informar de un determinado valor sem&aacute;ntico. Gracias a RDF Schema podremos definir relaciones de significado b&aacute;sicas.</p>
<p>Con esto acabamos el primer apartado dedicado a la web sem&aacute;ntica. Este art&iacute;culo ha sido el maś te&oacute;rico pero es clave para comprender lo que haremos m&aacute;s adelante</p>
<p>&nbsp;</p>]]></description>
                <comments>https://blog.adrianistan.eu/web-semantica-rdf</comments>
                <pubDate>Tue, 16 Jul 2019 20:11:15 +0000</pubDate>
            </item>
        
            <item>
                <title>miniKanren, programación lógica diminuta</title>
                <link>https://blog.adrianistan.eu/minikanren-programacion-logica</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/minikanren-programacion-logica</guid>
                <description><![CDATA[<p>Cuando hablamos de programaci&oacute;n l&oacute;gica, lo primero que se nos viene a la mente es Prolog, un lenguaje del que ya he hablado varias veces, y que fue uno de los primeros lenguajes l&oacute;gicos as&iacute; como uno de los m&aacute;s populares. Hoy vengo a hablaros de una familia de lenguajes, llamada <a href="http://minikanren.org/">miniKanren</a>, que demuestra que cualquier lenguaje de programaci&oacute;n puede adoptar el paradigma l&oacute;gico con solo 3 instrucciones. miniKanren est&aacute; disponible como DSL en muchos lenguajes: Scheme, Haskell, Clojure (llamado core.logic), Python, C#, Elixir, Go, JavaScript, Rust, ... y pr&aacute;cticamente cualquier lenguaje.</p>
<h2>Historia</h2>
<p>miniKanren surgi&oacute; originalmente en el libro <a href="https://amzn.to/2Rg9LKM">The Reasoned Schemer</a>, escrito por <a href="http://www.cs.indiana.edu/~dfried/">Daniel P. Friedman</a>, <a href="http://webyrd.net">William E. Byrd</a>, <a href="http://okmij.org/ftp/">Oleg Kiselyov</a>, and <a href="http://hemann.pl/">Jason Hemann</a> y publicado por MIT Press siendo la &uacute;ltima edici&oacute;n la de 2018. miniKanren se implementa originalmente en Scheme (algo bastante habitual para el MIT) pero sus principios b&aacute;sicos se pueden implementar en cualquier lenguaje de programaci&oacute;n de tipo general.</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://files.adrianistan.eu/Schemer.jpeg" alt="" width="460" height="460" /></p>
<p>As&iacute;, es posible a&ntilde;adir el paradigma l&oacute;gico a cualquier lenguaje a&ntilde;adiendo &uacute;nicamente 3 funciones. Este conjunto de 3 funciones se denomina&nbsp;<strong>microKanren</strong> y normalmente las implementaciones de miniKanren implementan microKanren en el lenguaje de destino y el resto de miniKanren se implementa usando las funciones definidas en microKanren. As&iacute; miniKanren es una familia de lenguajes que no comparten sintaxis (la sintaxis depende del lenguaje de destino) aunque generalmente es muy parecida. Yo usar&eacute; <a href="https://github.com/logpy/logpy">miniKanren para Python</a>, ya que es una implementaci&oacute;n completa en un lenguaje bastante popular.</p>
<p>Las funciones de microKanren son las siguientes:</p>
<ul>
<li>==
<ul>
<li>Realiza la operaci&oacute;n de unificaci&oacute;n, que ya coment&eacute; en la <a href="https://blog.adrianistan.eu/introduccion-a-prolog-tutorial-en-espanol">ihttps://blog.adrianistan.eu/introduccion-a-prolog-tutorial-en-espanolntroducci&oacute;n a Prolog</a> y b&aacute;sica en programaci&oacute;n l&oacute;gica</li>
</ul>
</li>
<li>fresh
<ul>
<li>Introduce un nuevo scope, definiendo variables l&oacute;gicas. A su vez, realiza la conjunci&oacute;n de las relaciones que se definen.</li>
</ul>
</li>
<li>conde
<ul>
<li>Introduce la posibilidad de m&uacute;ltiples respuestas, definiendo varios conjuntos independientes donde todas los predicados tienen que ser ciertos a la vez. Esto es una mezcla de AND y OR a la vez. Veremos un ejemplo m&aacute;s abajo.</li>
</ul>
</li>
</ul>
<h2>Tutorial b&aacute;sico de miniKanren</h2>
<p>Para el peque&ntilde;o tutorial voy a usar <a href="https://github.com/logpy/logpy">LogPy</a>, un dialecto de miniKanren para Python. Se instala f&aacute;cilmente a trav&eacute;s de PyPI.</p>
<p>Una vez lo tengamos instalado, vamos a ejecutar nuestro primer programa en miniKanren.&nbsp;</p>
<pre><code class="language-python">
from kanren import run, eq, var

x = var()
sol = run(1,x,eq(x,42))
print(sol[0])
</code></pre>
<p>En la primera l&iacute;nea importamos las funciones que vamos a usar, run, eq y var.&nbsp;<strong>var</strong> nos permite crear variables l&oacute;gicas, que podemos usar en nuestros programas miniKanren. Cumple la misma funci&oacute;n que <strong>fresh</strong>. <strong>run</strong> ejecuta el programa miniKanren. Tiene varios argumentos. El primero es el n&uacute;mero de soluciones a buscar, el segundo es sobre qu&eacute; variables vamos a buscar soluciones. El resto de argumentos, son predicados del programa miniKanren. En este caso usamos&nbsp;<strong>eq</strong> que es == pero como Python no admite la sintaxis, se modifica el nombre. B&aacute;sicamente estamos pidiendo que X unifique con 42. La soluci&oacute;n, que es un array de soluciones, nos indica claramente que para que X unifique con 42, X tiene que ser 42.</p>
<p>Si te acuerdas de Prolog, probablemente te est&eacute;s preguntando como hacer relaciones, por supuesto miniKanren las soporta:</p>
<pre><code class="language-python">
from kanren import run, var, Relation, facts

x = var()
parent = Relation()
facts(parent,("Homer","Bart"),
        ("Homer","Lisa"),
        ("Abe","Homer"))

sol = run(2,x,parent("Homer",x))
print(sol[0])
print(sol[1])
</code></pre>
<p>Para definir relaciones, primero creamos una relaci&oacute;n. Despu&eacute;s insertamos hechos correspondientes a esa relaci&oacute;n. Esa relaci&oacute;n la podemos usar en el c&oacute;digo miniKanren de forma muy similar a Prolog. En este caso pedimos dos soluciones, ya que sabemos que existen dos posibles valores de X: Bart y Lisa.</p>
<p>Como hemos dicho, miniKanren no es un lenguaje independiente, es m&aacute;s bien un lenguaje ad-hoc al lenguaje que estamos usando. Por eso podemos combinar funciones de Python con miniKanren.</p>
<pre><code class="language-python">
from kanren import run, eq, conde, var, Relation, facts

x = var()

parent = Relation()
facts(parent,("Homer","Bart"),
        ("Homer","Lisa"),
        ("Abe","Homer"))

def grandparent(x,z):
    y = var()
    return conde((parent(x,y), parent(y,z)))

sol = run(1,x,grandparent(x,"Bart"))
print(sol[0])
</code></pre>
<p>Obteniendo como respuesta "Abe". Aqu&iacute; usamos&nbsp;<strong>conde</strong>. Conde hemos dicho antes que es un OR y AND a la vez. En un primer nivel es un OR, es decir, conde(A,B) es verdadero si A o B son verdaderos. Sin embargo si A y B son predicados compuestos, entre ellos se aplica la relaci&oacute;n AND. Por ejemplo, conde((A,B,C),(D,E)) significa que o bien A, B y C son ciertos o D y E son ciertos para que el predicado sea cierto.</p>
<p>Con esto ya estamos listos para recrear el Hola Mundo del paradigma l&oacute;gico, la mortalidad de S&oacute;crates.</p>
<pre><code class="language-python">
from kanren import run, var, fact, Relation

x = var() 
human = Relation()

fact(human,"Socrates")

def mortal(x):
    return human(x)

sol = run(1,x,mortal(x))
print(sol)
</code></pre>
<p>As&iacute; de simple y sencillo y sin tener que cambiar de lenguaje de programaci&oacute;n.</p>
<p>Un predicado que suelen llevar las implementaciones de miniKanren que no es estrictamente necesario es <strong>membero</strong> que es cierto cuando X pertenece a la lista.</p>
<pre><code class="language-python">
from kanren import run, membero, var

x = var()
sol = run(1,x,
    membero(x,[1,2,3]),
    membero(x,[2,3,4])
    )
print(sol)
</code></pre>
<p>En este caso habr&iacute;a dos posibles respuestas (2 o 3) ya que ambos n&uacute;meros cumplen la condici&oacute;n de estar en ambas listas a la vez. La respuesta final vendr&aacute; dada por la implementaci&oacute;n de miniKanren (en LogPy da 2).</p>
<p>&nbsp;</p>
<h2>Diferencias con Prolog</h2>
<p>Una vez visto miniKanren, quiz&aacute; haga falta saber las<a href="https://stackoverflow.com/questions/28467011/what-are-the-main-technical-differences-between-prolog-and-minikanren-with-resp"> diferencias que tiene respecto a Prolog</a>. En primer lugar, Prolog es un lenguaje independiente, con el que podemos dise&ntilde;ar aplicaciones desde cero. Esto sin embargo suele ser un problema, ya que aunque para ciertas tareas la programaci&oacute;n l&oacute;gica es muy buena, para otras es un verdadero infierno. Adem&aacute;s, esto ha hecho que el n&uacute;mero de librer&iacute;as que existen para Prolog sean muy limitadas. Con miniKanren no tenemos ese problema. B&aacute;sicamente estamos usando el lenguaje base y cuando queramos realizar programaci&oacute;n l&oacute;gica usamos las funciones de miniKanren.</p>
<p>Prolog tambi&eacute;n es un lenguaje que se deriva del sistema l&oacute;gico en varios puntos, como los&nbsp;<strong>cortes</strong>, o la base de datos modificable de hechos (<strong>assert</strong> y&nbsp;<strong>retract</strong>). Esto otorga flexibilidad, pero pierde en limpieza. Adem&aacute;s, Prolog no realiza el chequeo de ocurrencias en la unificaci&oacute;n, paso matem&aacute;ticamente necesario pero computacionalmente costoso y que no suele suponer grandes diferencias de resultados en la pr&aacute;ctica. Adem&aacute;s Prolog soporta operaciones aritm&eacute;ticas. Esto no significa que en miniKanren no se puedan usar, pero dependen del lenguaje base, son por tanto indiferentes de cara a este DSL. Por otro lado en miniKanren no existe mutabilidad y todo c&oacute;digo escrito es <em>thread-safe</em>, aunque no existen muchas implementaciones que aprovechen el paralelismo. En las b&uacute;squedas, Prolog suele usar el m&eacute;todo primero en profundidad mientras que miniKanren suele ser primero en anchura.</p>
<h2>Conclusi&oacute;n</h2>
<p>Si bien Prolog es un lenguaje muy interesante, el hecho de ser algo separado lo ha hecho perder mucho inter&eacute;s durante los a&ntilde;os y aunque se puede empotrar, esto no es f&aacute;cil. miniKanren es un lenguaje l&oacute;gico puro, que si bien es algo m&aacute;s limitado, la experiencia de uso es mejor. Quiz&aacute; la mejor opci&oacute;n a d&iacute;a de hoy para realizar programaci&oacute;n l&oacute;gica en sistemas en producci&oacute;n. No obstante a d&iacute;a de hoy sigue faltando mucha documentaci&oacute;n sobre miniKanren, aunque si tienes experiencia con Prolog, lo entender&aacute;s r&aacute;pidamente.</p>]]></description>
                <comments>https://blog.adrianistan.eu/minikanren-programacion-logica</comments>
                <pubDate>Fri, 14 Jun 2019 16:58:44 +0000</pubDate>
            </item>
        
            <item>
                <title>Cosas que (probablemente) no sabías de Python</title>
                <link>https://blog.adrianistan.eu/cosas-no-sabias-python</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/cosas-no-sabias-python</guid>
                <description><![CDATA[<p>Python es un lenguaje muy popular hoy en d&iacute;a. Aunque pueda no ser el mejor, su desempe&ntilde;o es bueno, con mucha documentaci&oacute;n, librer&iacute;as, es c&oacute;modo y f&aacute;cil de aprender. Python adem&aacute;s sigue la filosof&iacute;a de bater&iacute;as incluidas, es decir, de intentar llevar de serie casi todo lo que vayas a poder necesitar. En este art&iacute;culo vamos a ver algunas partes de Python no tan conocidas pero igualmente interesantes y &uacute;tiles.</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://files.adrianistan.eu/python-batteries-included.jpg" alt="" width="450" height="192" /></p>
<h2>Frozensets y sets</h2>
<p>Los sets y frozenset son estructuras de datos que replican el funcionamiento de los conjuntos de matem&aacute;ticas. Esto quiere decir que es un contenedor de elementos, donde cada elemento solo puede aparecer una vez y no hay orden establecido entre los elementos. Los sets/frozensets tienen varias operaciones: uni&oacute;n, intersecci&oacute;n, diferencia, diferencia sim&eacute;trica y se puede comprobar si un conjunto es disjunto, subconjunto o superconjunto respecto a otro. La diferencia fundamental entre entre set y frozenset es que frozenset es inmutable, lo cu&aacute;l puede ser mejor seg&uacute;n nuestro problema (mejor estilo de programaci&oacute;n y m&aacute;s rendimiento) pero es que adem&aacute;s es hasheable, lo que significa que podemos usarlo en sitios donde se requiera que exista un hash, como por ejemplo, las claves de un diccionario.</p>
<pre><code class="language-python">
a = set([27,53])
b = frozenset([27,42])

c = a | b # Uni&oacute;n
d = a &amp; b # Intersecci&oacute;n
e = a ^ b # Diferencia sim&eacute;trica
f = a - b # Diferencia

print(c)
print(d)
print(e)
print(f)

if 42 in b:
    print("42 dentro de B")

if a &gt;= b:
    print("A es superconjunto y B es subconjunto")

x = dict()
x[b] = 43
# x[a] = 43 dar&iacute;a error
</code></pre>
<p>Los sets adem&aacute;s soportan <em>set comprehensions:</em></p>
<pre><code class="language-python">
a = { x for x in range(1,10)}
</code></pre>
<p>&nbsp;</p>
<h2>Statistics</h2>
<p>Desde Python 3.4 existe un m&oacute;dulo llamado&nbsp;<a href="https://docs.python.org/3/library/statistics.html"><strong>statistics</strong></a> que nos trae operaciones b&aacute;sicas de estad&iacute;stica ya implementadas. Evidentemente si vamos a hacer un uso intensivo, es mejor recurrir a las funciones de NumPy/SciPy pero en muchas ocasiones no necesitamos tanta potencia y este m&oacute;dulo nos viene de perlas. El m&oacute;dulo implementa funciones de media aritm&eacute;tica, media arm&oacute;nica, mediana, moda, varianza poblacional y varianza muestral (con sus respectivas desviaciones). Todas las funciones admiten los tipos int, float, Decimal y Fraction.</p>
<pre><code class="language-python">
import statistics

data = [1,2,2,2,3,3,4,5,8]

a = statistics.mean(data)
b = statistics.median(data)
c = statistics.mode(data)
d = statistics.pvariance(data)
e = statistics.variance(data)

print(a)
print(b)
print(c)
print(d)
print(e)
</code></pre>
<h2>Decimal y Fraction</h2>
<p>Estos dos tipos sirven para representar n&uacute;meros, pero son diferentes entre s&iacute; y tambi&eacute;n a int y float. En primer lugar veamos por qu&eacute; son necesarios. En Python existe int para n&uacute;meros enteros y float para n&uacute;meros con parte decimal. Sin embargo, los float no son precisos. Los floats en Python son similares a los de C y usan el est&aacute;ndar IEEE 754. Esto est&aacute; bien porque el hardware funciona as&iacute; y es r&aacute;pido, pero es imposible hacer cuentas de forma precisa e incluso representar algunos n&uacute;meros es imposible. Para ello existen Decimal y Fraction que son dos maneras diferentes de conseguir precisi&oacute;n.</p>
<p>Decimal usa una precisi&oacute;n arbitraria para representar el n&uacute;mero decimal como un n&uacute;mero entero (por defecto con 28 posiciones). Decimal est&aacute; especialmente recomendado para operaciones financieras, donde la precisi&oacute;n se conoce a priori. Fraction por otra parte usa el principio de que cualquier n&uacute;mero racional se puede representar con una divisi&oacute;n de n&uacute;meros enteros. Por tanto, almacena dos n&uacute;meros, un numerador y un denominador, y los mantiene separados. Al hacer operaciones, como en cualquier otra fracci&oacute;n, se operan los n&uacute;meros por separado, siempre manteniendo que tanto numerador como denominador sean enteros. De esta forma, y teniendo en cuenta que int tiene precisi&oacute;n arbitraria en Python, podemos representar con gran precisi&oacute;n todos los n&uacute;meros racionales. Ambos m&eacute;todos tienen el inconveniente de ser m&aacute;s lentos que float y gastar m&aacute;s memoria, pero son m&aacute;s precisos.</p>
<p>En este ejemplo que pongo abajo, se hace tres veces la misma operaci&oacute;n: 0.1+0.2 que tiene que dar 0.3. Como ver&aacute;s si ejecutas el c&oacute;digo, solo las versiones hechas con Decimal y Fraction hacen la operaci&oacute;n bien, mientras que float falla.</p>
<pre><code class="language-python">
a = 0.1
b = 0.2
c = 0.3

if a+b == c:
    print("Float: operaci&oacute;n correcta")

from decimal import Decimal

pi = Decimal('3.14159')
a = Decimal('0.1')
b = Decimal('0.2')
c = Decimal('0.3')

if a+b == c:
    print("Decimal: operaci&oacute;n correcta")

from fractions import Fraction
a = Fraction(1,10)
b = Fraction(2,10)
c = Fraction(3,10)

if a+b == c:
    print("Fracion: operaci&oacute;n correcta")
</code></pre>
<h2>F-Strings</h2>
<p>Las <a href="https://www.python.org/dev/peps/pep-0498/">f-strings</a> fueron a&ntilde;adidas en Python 3.6 y son cadenas de texto que admiten formato de forma muy sencilla y flexible. Es la opci&oacute;n recomendada, sustituyendo al resto de otras formas de hacerlo (aunque siguen funcionando).</p>
<pre><code class="language-python">
usuario = "Godofredo"

mensaje = f"El usuario {usuario} ha entrado al sistema"
print(mensaje)
</code></pre>
<h2>Pathlib</h2>
<p>En nuestro d&iacute;a a d&iacute;a es frecuenta trabajar con archivos en diferentes directorios. Pathlib (disponible desde Python 3.4) nos ayuda a manejar de forma sencilla rutas de forma multiplataforma. Pero aunque no necesitemos multiplataforma, las abtracciones de Pathlib son muy interesantes.</p>
<pre><code class="language-python">
from pathlib import Path

root = Path('dev')
print(root)
# dev

path = root / 'pcc'

print(path.resolve())
# /home/aarroyoc/dev/pcc
</code></pre>
<h2>Functools</h2>
<p>El m&oacute;dulo <a href="https://docs.python.org/3.7/library/functools.html">functools</a> es de los m&aacute;s interesantes de Python, sobre todo si vienes de la programaci&oacute;n funcional, ya que nos permite manipular funciones. Quiero destacar tres funciones de este m&oacute;dulo: partial, lru_cache y reduce.</p>
<p><strong>reduce </strong>nos permite reducir un iterable a un valor usando una funci&oacute;n. Es una operaci&oacute;n muy com&uacute;n en programaci&oacute;n funcional y a partir de Python 3 hay que usarla a trav&eacute;s de este modulo. Por ejemplo, podemos programar una funci&oacute;n <a href="https://es.wikipedia.org/wiki/Factorial">factorial</a> de la siguiente forma:</p>
<pre><code class="language-python">
from functools import reduce

def factorial(n):
    def multiply(a,b):
        return a*b

    return reduce(multiply,range(1,n+1))
</code></pre>
<p>Otra funci&oacute;n interesante es&nbsp;<strong>lru_cache</strong>, disponible a partir de Python 3.2, la cu&aacute;l es una cache LRU (Last Recently Used) ya construida para nuestro uso y disfrute. Si no lo sab&eacute;is, las cach&eacute;s LRU son un tipo de cach&eacute; con un tama&ntilde;o fijo donde el elemento que se elimina cuando falta espacio es el &uacute;ltimo en ser usado (es decir, le&iacute;do o a&ntilde;adido a la cach&eacute;). Las cach&eacute;s LRU se usan en muchos sitios (como en las propias CPUs. Adem&aacute;s, esta versi&oacute;n de Python dispone de tama&ntilde;o infinito si se lo configuramos, lo cu&aacute;l es muy interesante. La funci&oacute;n se usa como decorador y compara los argumentos de llamada para ver si la funci&oacute;n ya fue llamada con esos argumentos, y en ese caso devolver el valor calcualdo con anterioridad. Si no existe, lo calcula y lo almacena. Esto es lo que se llama <strong>memoizaci&oacute;n</strong>, una t&eacute;cnica muy usada en <a href="https://blog.adrianistan.eu/programacion-dinamica-el-problema-de-knapsack/">programaci&oacute;n din&aacute;mica</a>.</p>
<pre><code class="language-python">
@lru_cache(maxsize=None)
def fib(n):
    if n &lt; 2:
        return n
    return fib(n-1) + fib(n-2)
</code></pre>
<p>Usar lru_cache en funciones recursiva aumentar&aacute; el consumo de memoria, pero puede mejorar la velocidad significativamente si se llama muchas veces a una funci&oacute;n con los mismos argumentos.</p>
<p>Por &uacute;ltimo,&nbsp;<strong>partial</strong> nos permite definir funciones parciales. &iquest;Asombroso eh? Si no has tocado programaci&oacute;n funcional seguramente te parezca que no tiene sentido. Para que nos entendamos, una funci&oacute;n parcial es una funci&oacute;n que ya tiene algunos argumentos rellenados, pero otros no. No estamos hablando de argumentos por defecto, porque eso se define sobre la propia funci&oacute;n, sino de una funci&oacute;n que es una especializaci&oacute;n de otra m&aacute;s gen&eacute;rica. Veamos un ejemplo, vamos a definir un nuevo print que no a&ntilde;ada un salto de l&iacute;nea al final:</p>
<pre><code class="language-python">
from functools import partial

printx = partial(print,end='')

printx("Hola amigos")
print(" de Adrianist&aacute;n")
</code></pre>
<p>De este modo,&nbsp;<strong>printx</strong> se define como la funci&oacute;n parcial de print con el argumento end ya configurado a caracter vac&iacute;o en vez de '\n' que es que usa print por defecto.</p>
<h2>Depuraci&oacute;n</h2>
<p>Si quieres depurar c&oacute;digo Python, lo normal es usar <strong>pdb</strong>, el m&oacute;dulo con funciones de depuraci&oacute;n. Sin embargo, su uso se hac&iacute;a un poco farragoso, as&iacute; que desde Python 3.7 se puede usar&nbsp;<strong>breakpoint</strong>. Simplemente llama a breakpoint (no hay que importar nada) y el programa entrar&aacute; en modo depuraci&oacute;n.</p>
<pre><code class="language-python">
import statistics

data = [1,2,2,2,3,3,4,5,8]

a = statistics.mean(data)
b = statistics.median(data)
c = statistics.mode(data)
breakpoint()
d = statistics.pvariance(data)
e = statistics.variance(data)

print(a)
print(b)
print(c)
print(d)
print(e)
</code></pre>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://files.adrianistan.eu/BreakpointPython.png" alt="" width="724" height="363" /></p>
<p>Otras funciones &uacute;tiles de depuraci&oacute;n son&nbsp;<strong>dir, vars, type </strong>y <strong>help</strong>. Dir muestra los atributos de un objeto o las variables locales, vars es similar pero m&aacute;s completa y dif&iacute;cil de leer. type devuelve el tipo de una variable y help muestra la documentaci&oacute;n asociada a una clase o funci&oacute;n.</p>
<h2>Enums</h2>
<p>En algunos lenguajes de programaci&oacute;n existe un tipo llamado enum o enumeration, que suele ser un tipo que admite &uacute;nicamente un conjunto finito de valores (normalmente peque&ntilde;o y mapeadas a un n&uacute;mero entero, pero con la seguridad del tipado extra). Python no dispon&iacute;a de esta funcionalidad (como muchos lenguajes de script) hasta Python 3.4, donde podemos crear clases que sean enumeraciones.</p>
<pre><code class="language-python">
from enum import Enum, auto

class OperatingSystem(Enum):
    LINUX = auto()
    MACOSX = auto()
    WINDOWSNT = auto()
    HAIKU = auto()
    SOLARIS = auto()

print(OperatingSystem.HAIKU)
</code></pre>
<h2>Data classes</h2>
<p>En la programaci&oacute;n orientada a objetos es habitual a veces encontrarnos con clases donde la mayor parte de las l&iacute;neas las perdemos en definir el constructor con datos, getters y setters. Las data classes, disponibles a partir de Python 3.7 nos permiten ahorrarnos todo este trabajo.</p>
<pre><code class="language-python">
from dataclasses import dataclass

@dataclass
class PC:
    cpu: str
    freq: int
    price: int = 1000

    def discount(self):
        return self.price*0.8

pc = PC("Intel Core i7",4000,2000)
print(pc.discount())
print(pc)
</code></pre>
<h2>Tipado</h2>
<p>Al usar data classes te habr&aacute;s dado cuenta que hay que poner tipos. Esto se llama&nbsp;<strong>type hints</strong> y no son tipos como en otros lenguajes. Me explico. En Python existen tipos, pero no existe forma de forzar a que un argumento de una funci&oacute;n sea de un tipo o de otro. Los type hints no son m&aacute;s que indicaciones que Python sabe como ignorar, el int&eacute;rprete de Python no les hace caso. Son los IDEs, los linters como mypy y algunas librer&iacute;as como pydantic los que en todo caso realizan la verificaci&oacute;n de tipos. Los tipados tal y como se conocen ahora se a&ntilde;adieron en Python 3.5 y en el m&oacute;dulo <a href="https://docs.python.org/3/library/typing.html">typing</a> hay muchos tipos avanzados.</p>
<pre><code class="language-python">
from typing import Dict

Options = Dict[str,str]

def add_option(options: Options, key: str, content: str) -&gt; None:
    options[key] = content

a = dict()
add_option(a,"b","c")
</code></pre>
<h2>Collections</h2>
<p>El m&oacute;dulo <a href="https://docs.python.org/3.7/library/collections.html#collections.Counter">collections</a> tiene numerosas estructuras de datos m&aacute;s avanzadas para mayor comodidad o mejor rendimiento. Voy a comentar tres: defaultdict, Counter y deque.</p>
<p><strong>defaultdict</strong> es una estructura de datos que particularmente encuentro muy interesante. Se trata de diccionarios con valor por defecto. Muy simple de entender y m&aacute;s &uacute;til de lo que parece. Por ejemplo, en una cuadr&iacute;cula bidimensional de todo ceros salvo unos pocos elementos que queremos que sean uno. Luego al analizar si se nos pregunta por una coordenada siempre podremos devolver el valor del diccionario (sin realizar ninguna comprobaci&oacute;n).</p>
<pre><code class="language-python">
from collections import defaultdict

mapa = defaultdict(lambda: 0)
mapa[(0,0)] += 1
mapa[(15,67)] += 1
</code></pre>
<p><strong>Counter</strong> es un contador, simple y llanamente, pero extremadamente c&oacute;modo y sorprendentemente &uacute;til. Existen dos formas b&aacute;sicas de crear un contador, una es pasando un iterable y Counter se encargar&aacute; de ir contando todos los elementos y otra es pasar ya un diccionario de las cuentas hechas. Los contadores tienen operaciones especiales como la suma, la resta, la uni&oacute;n y la intersecci&oacute;n de contadores.</p>
<pre><code class="language-python">
from collections import Counter

c = Counter("Rodrigo Diaz de Vivar")
print(c)
d = Counter({"d": 6, "i": 8})

e = c + d

print(e)
</code></pre>
<p>Por &uacute;ltimo,&nbsp;<strong>deque</strong> es una lista doblemente enlazada. Esto puede ser mucho m&aacute;s interesante que&nbsp;<strong>list</strong> para ciertas operaciones. List es un simple array, con acceso aleatorio muy r&aacute;pido, pero las modificaciones pueden ser lentas. En deque las modificaciones son r&aacute;pidas pero el acceso aleatorio es muy lento. Las operaciones son pr&aacute;cticamente las mismas que en list, as&iacute; que no voy a poner c&oacute;digo.</p>
<h2>Itertools</h2>
<p>El m&oacute;dulo itertools de Python es otro gran m&oacute;dulo lleno de funcionalidad interesante, en este caso para trabajar con iteradores, basado en los lenguajes APL, Haskell y SML.</p>
<p>Lo primero a mencionar son los iteradores infinitos: <strong>count</strong>,&nbsp;<strong>cycle</strong> y&nbsp;<strong>repeat</strong>. Por ejemplo, cycle es un iterador que toma un iterador y cuando este se acaba lo vuelve a repetir, as&iacute; hasta el infinito.</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://files.adrianistan.eu/CyclePython.png" alt="" width="667" height="479" /></p>
<p>Luego tenemos otras funciones como <strong>takewhile&nbsp;</strong>(genera un iterador a partir de otro iterador mientras se cumpla una condici&oacute;n), <strong>chain</strong> (que une iteradores), <strong>groupby</strong> (genera subiteradores seg&uacute;n un atributo de agrupamiento) y&nbsp;<strong>tee</strong> (que genera N subiteradores).</p>
<pre><code class="language-python">
from itertools import *

a = range(1,100)
b = takewhile(lambda x: x&lt;50,a)
c = dropwhile(lambda x: x&lt;50,a)
d = chain(b,c)
e = sorted(d,key=lambda x: x%2 == 0)
for k, g in groupby(e,key=lambda x: x%2==0):
    print(k)
    for x in g:
        print(x,end=' ')
    print("")
</code></pre>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://files.adrianistan.eu/IterToolPython.png" alt="" width="727" height="126" /></p>
<p>Por &uacute;ltimo, el m&oacute;dulo tiene funciones muy &uacute;tiles de combinatoria como el producto cartesiano, las permutaciones, las combinaciones (con y sin repetici&oacute;n).</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="http://files.adrianistan.eu/CombPython.png" alt="" /></p>
<h2>Zip, reversed y enumerate</h2>
<p>La funci&oacute;n zip es un builtin, es decir, no hay que importar nada. La funci&oacute;n zip junta dos iteradores en uno y un iterador de tuplas. Zip tiene la longitud del iterador m&aacute;s corto, si necesitas que tome la longitud del m&aacute;s largo, la funci&oacute;n zip_longest de itertools hace justamente eso.</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://files.adrianistan.eu/ZipPython.png" alt="" width="360" height="329" /></p>
<p>La funci&oacute;n reversed recorre el iterador en orden inverso y enumerate proporciona una tupla con el elemento y su posici&oacute;n en el iterador.</p>
<h2>Any y all</h2>
<p>Any y all son otros dos builtins de Python. Devuelven true cuando alg&uacute;n elemento del iterador cumple la condici&oacute;n (any) o todos la cumplen (all). Usando estas expresiones podemos comprobar que una palabra es palindr&oacute;mico de la siguiente forma:</p>
<pre><code class="language-python">
def palindromic(sequence):
    return all(
        n == m
        for n, m in zip(sequence, reversed(sequence))
    )
print(palindromic("abba"))
</code></pre>
<h2>property</h2>
<p>Cuando trabajamos con c&oacute;digo orientado a objeto en Python, es recomendable usar propiedades, en vez de funciones manuales de getters y setters.</p>
<pre><code class="language-python">
class P:

    def __init__(self,x):
        self.x = x

    @property
    def x(self):
        return self.__x

    @x.setter
    def x(self, x):
        if x &lt; 0:
            self.__x = 0
        elif x &gt; 1000:
            self.__x = 1000
        else:
            self.__x = x
</code></pre>
<h2>staticmethod y classmethod</h2>
<p>Estas funciones tambi&eacute;n son decoradores de funciones dentro de una clase. Su uso es similar es parecido. classmethod es para m&eacute;todos que pueden llamarse tanto de forma est&aacute;tica como con un objeto instanciado, un ejemplo t&iacute;pico son los m&eacute;todos factor&iacute;a. staticmethod sin embargo se refiere a m&eacute;todos est&aacute;ticos dentro de una clase, aunque en Python tampoco se usan demasiado.</p>
<h2>isinstance e issubclass</h2>
<p>Para acabar con las funciones propias de la orientaci&oacute;n a objetos, estas dos funciones nos permiten comprobar si un objeto es instancia de una clase y si una clase es subclase de otra.</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://files.adrianistan.eu/InstancePython.png" alt="" width="661" height="257" /></p>
<h2>Conclusiones</h2>
<p>Como veis, Python tiene muchas cosas interesantes. Espero que en esta lista haya al menos alguna cosa que no conoci&eacute;seis. Si adem&aacute;s conoces alguna otra cosa no tan conocida pero que consideras &uacute;til dentro de Python, puedes ponerla en los comentarios.</p>]]></description>
                <comments>https://blog.adrianistan.eu/cosas-no-sabias-python</comments>
                <pubDate>Sun, 09 Jun 2019 15:13:38 +0000</pubDate>
            </item>
        
            <item>
                <title>Programación web en Prolog</title>
                <link>https://blog.adrianistan.eu/programacion-web-prolog</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/programacion-web-prolog</guid>
                <description><![CDATA[<p>Como ya hemos visto con anterioridad, <a href="../../introduccion-a-prolog-tutorial-en-espanol">Prolog</a> es un muy buen lenguaje para ciertos problemas. Hoy en d&iacute;a, el lenguaje que se cre&iacute;a que ser&iacute;a el futuro est&aacute; pr&aacute;cticamente olvidado. No obstante, existen algunas personas que se han esforzado para que Prolog siga siendo un lenguaje &uacute;til. &iquest;Y qu&eacute; requisito es b&aacute;sico para un lenguaje &uacute;til? &iexcl;Que pueda usarse para hacer un servidor web claro! Es por eso que decid&iacute; emprender una aventura quijotesca y ver hasta donde era capaz de llegar Prolog en el desarrollo web. Para ello voy a usar <a href="http://www.swi-prolog.org/">SWI Prolog.</a>&iquest;Suena alocado? Pues ya lo est&aacute;s usando, el servidor de gesti&oacute;n de im&aacute;genes de Adrianist&aacute;n (https://files.adrianistan.eu) est&aacute; hecho en Prolog.</p>
<h2>Handlers y configuraci&oacute;n</h2>
<p>Como todo buen web framework, en este debe poder configurarse unas rutas (handlers) y algunos par&aacute;metros tales como el puerto, n&uacute;mero de hilos (Prolog por defecto usar&aacute; varios hilos para manejar las peticiones web en paralelo) y m&aacute;s cosas. Este c&oacute;digo es el m&aacute;s simple que nos permite acceder a una p&aacute;gina y mostrar un hola mundo.</p>
<pre><code class="language-prolog">
:- use_module(library(http/thread_httpd)).
:- use_module(library(http/http_dispatch)).
:- use_module(library(http/html_write)).
:- use_module(library(http/http_unix_daemon)).


:- http_handler(/,index,[]).

index(_Request) :-
    format('Content-Type: text/plain~n~n'),
    format('Hola Mundo').

:- http_daemon([port(4777),fork(false)]).
</code></pre>
<p>Si ejecutamos con SWI Prolog, veremos que el servidor arranca:</p>
<p style="text-align: center;"><img src="https://files.adrianistan.eu/PrologWebHelloWorld.png" alt="" width="1366" height="768" /></p>
<p style="text-align: left;">Como vemos, el c&oacute;digo es super simple. Centr&eacute;monos m&aacute;s en la parte de los handlers. Es tan simple que parece que faltan cosas, &iquest;c&oacute;mo definir rutas con par&aacute;metros? &iquest;y la distinci&oacute;n entre GET y POST? Todo es muy sencillo como veremos.</p>
<p style="text-align: left;">El predicado <strong>http_handler</strong> tiene aridad 3. En primer lugar la ruta, en segundo lugar el predicado que responder&aacute; a la petici&oacute;n y en tercer lugar una lista con las opciones.</p>
<p style="text-align: left;">En primer lugar las rutas se definen con algo denominado <strong>FileSpec</strong>, que es una construcci&oacute;n de Prolog que permite expresar rutas relativas a partir de un alias absoluto. No solo se usa aqu&iacute;, sino en todas partes. F&iacute;jate en como usamos <strong>use_module</strong>. Tenemos library y dentro una ruta. library representa un path absoluto con ese alias y dentro se representa el resto de la ruta. Otro alias predefinido y que podemos usar en el contexto de una aplicaci&oacute;n web es <strong>root</strong>. root(.) es equivalente a /. Si queremos soportar paths variables, &iexcl;usamos variables de Prolog! Luego nos aseguramos que que esas variables tambi&eacute;n esten presentes en el predicado de la respuesta.</p>
<p style="text-align: left;">Aqu&iacute; alguien se puede extra&ntilde;ar ya que este predicado no tiene la misma aridad arriba que abajo, <em>aparentemente</em>. En realidad como http_handler siempre a&ntilde;ade la variable Request, Prolog no tiene ning&uacute;n problema.</p>
<pre><code class="language-prolog">
:- use_module(library(http/thread_httpd)).
:- use_module(library(http/http_dispatch)).
:- use_module(library(http/html_write)).
:- use_module(library(http/http_unix_daemon)).


:- http_handler(/,index,[]).
:- http_handler(root(user/User),user(User),[method(get)]).

index(_Request) :-
    format('Content-Type: text/plain~n~n'),
    format('Hola Mundo').

user(User,_Request) :-
    format('Content-Type: text/plain~n~n'),
    format('Usuario: '),
    format(User).

:- http_daemon([port(4777),fork(false)]).
</code></pre>
<p style="text-align: left;">Por otro lado en las opciones podemos definir gran variedad de ajustes, entre ellos el m&eacute;todo de la petici&oacute;n. Como bien es sabido en Prolog, Prolog unifica, &iexcl;incluso en una aplicaci&oacute;n web! As&iacute; que la clave es cuanto m&aacute;s expl&iacute;citos seamos, en menos ocasiones unificar&aacute; el handler.</p>
<p style="text-align: center;"><img src="https://files.adrianistan.eu/PrologWebParameter.png" alt="" width="1366" height="768" /></p>
<h2 style="text-align: left;">Ficheros, HTML y JSON</h2>
<p>Ya hemos visto como funcionan los handlers pero hemos estado generando las respuestas con unas llamadas a format un poco cutres. Por fortuna, Prolog nos hace la vida m&aacute;s f&aacute;cil si vamos a devolver un fichero, un HTML din&aacute;mico o un archivo JSON.</p>
<p>Para ficheros, es trivial, simplemente respondemos con el predicado <strong>http_reply_file</strong>.</p>
<p>Para HTML hay dos maneras de hacerlo. Una es usando el DSL, otra es usando plantillas. Aqu&iacute; vamos a ver el DSL ya que es m&aacute;s estilo Prolog. Para responder usando este m&eacute;todo usamos el predicado <strong>reply_html_page</strong>. Este toma un head y un body. Este DSL es muy intuitivo y permite escribir estructuras anidadas as&iacute; como atributos. Este DSL adem&aacute;s permite ser usado con DCG, as&iacute; que se puede escribir en un predicado aparte y se pueden hacer includes de forma c&oacute;moda. Adem&aacute;s se pueden pasar variables, aunque en este ejemplo no lo hago.&nbsp;</p>
<p>Por &uacute;ltimo, para responder un JSON usamos el predicado <strong>reply_json</strong>. SWI Prolog admite varias sintaxis para representar JSON. Podemos usar dentro un t&eacute;rmino json la sintaxis Clave-Valor, Clave=Valor o Clave(Valor). Esta &uacute;ltima es la que he usado. Adem&aacute;s a partir de SWI Prolog 7 se pueden usar los diccionarios de Prolog tambi&eacute;n.</p>
<p>Finalmente aqu&iacute; un ejemplo con los tres predicados en uso:</p>
<pre><code class="language-prolog">
:- use_module(library(http/thread_httpd)).
:- use_module(library(http/http_dispatch)).
:- use_module(library(http/html_write)).
:- use_module(library(http/http_json)).
:- use_module(library(http/http_unix_daemon)).


:- http_handler(/,index,[]).
:- http_handler(root(user/User),user(User),[method(get)]).
:- http_handler('/foto.jpg',static,[]).
:- http_handler('/api',api,[]).

index(_Request) :-
    reply_html_page(
        title('Prolog WebApp'),
        [\index_body]).

index_body --&gt;
    html([
        h1('Prolog WebApp'),
        p([style('color: red'),id('parrafo')],'Hola Mundo'),
        ul([
            li('Alonso Quijana'),
            li('Sancho Panza')   
        ])
    ]).

api(_Request) :-
    reply_json(json([
       name('Alonso Quijana'),
       email('quijana@mail.xp') 
    ])).

user(User,_Request) :-
    format('Content-Type: text/plain~n~n'),
    format('Usuario: '),
    format(User).

static(Request) :-
    http_reply_file('burgos.jpeg',[],Request).

:- http_daemon([port(4777),fork(false)]).
</code></pre>
<h2>Peticiones POST</h2>
<p style="text-align: left;">Vamos a ver como leer esos formularios desde Prolog. En primer lugar para obtener un listado de todas las variables POST usamos el predicado <strong>http_read_data</strong>. Despu&eacute;s simplemente buscamos en la lista con el archiconocido predicado&nbsp;<strong>member</strong>, unificando con una variable libre para obtener ya el dato directamente.</p>
<pre><code class="language-prolog">
form(Request) :-
    http_read_data(Request,Data,[]),
    member(email=Email,Data),
    format('Content-Type: text/plain~n~n'),
    format('El correo indicado es: '),
    format(Email).
</code></pre>
<h2 style="text-align: left;">Autenticaci&oacute;n Basic</h2>
<p>Un ejemplo m&aacute;s elaborado es la autenticaci&oacute;n HTTP. Aqu&iacute; defino un predicado auth que va a contener la validaci&oacute;n.</p>
<pre><code class="language-prolog">
auth(Request) :-
    Realm = 'Prolog WebApp',
    (
        string_codes("123456789",Password),
        member(authorization(Header),Request),http_authorization_data(Header,basic(admin,Password)) -&gt; true
        ;
        throw(http_reply(authorise(basic, Realm)))
    ).
</code></pre>
<p>Para usarla, simplemente hay que poner el predicado auth en la primera l&iacute;nea de cualquier predicado de handler.</p>
<h2>Conclusiones</h2>
<p>El c&oacute;digo final est&aacute; en el repositorio de <a href="https://github.com/aarroyoc/blog-ejemplos">ejemplos del blog</a>. Respecto a la base de datos. No lo he comentado porque ser&iacute;a otro asunto, adem&aacute;s Prolog ya es una base de datos gracias a assert y retract. SWI Prolog incluye adem&aacute;s una muy buena implementaci&oacute;n de RDF. Espero que con este post os haya abierto la mente a un tipo de programaci&oacute;n diferente, la l&oacute;gica, que no solo sirve para resolver problemas "raros" sino para aplicaciones web de forma extremadamente sencilla (&iexcl;una vez comprendamos Prolog claro!). Se trata de un enfoque muy interesante y con el que se puede llegar a ser muy productivo. Yo me esperaba algo mucho peor, pero la gente detr&aacute;s de SWI Prolog se ha esforzado para que todo sea simple y claro.</p>
<p style="text-align: left;">&nbsp;</p>
<p style="text-align: left;">&nbsp;</p>]]></description>
                <comments>https://blog.adrianistan.eu/programacion-web-prolog</comments>
                <pubDate>Thu, 25 Apr 2019 17:22:18 +0000</pubDate>
            </item>
        
            <item>
                <title>Bosque, el nuevo lenguaje de programación de Microsoft</title>
                <link>https://blog.adrianistan.eu/bosque-lenguaje-programacion-microsoft</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/bosque-lenguaje-programacion-microsoft</guid>
                <description><![CDATA[<p>Hace unos pocos d&iacute;as Microsoft Research, la parte de investigaci&oacute;n de Microsoft, public&oacute; <a href="https://github.com/Microsoft/BosqueLanguage">Bosque</a>, un nuevo lenguaje de programaci&oacute;n. El lenguaje sigue en fase experimental y el compilador es un poco patatero (est&aacute; hecho en TypeScript y necesita Node.js para funcionar). Pero eso es lo de menos, porque son detalles. Lo importante son las ideas que aporta.</p>
<h2>La filosof&iacute;a de Bosque</h2>
<p>Como muchos otros lenguajes, Bosque nace de unas ideas que pretende seguir. La filosof&iacute;a detr&aacute;s de Bosque es la siguiente:</p>
<blockquote>
<blockquote>
<p><span class="x-hidden-focus" style="color: #24292e; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; font-style: normal; font-weight: 400; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; background-color: #ffffff; float: none;">El lenguaje de programaci&oacute;n Bosque ha sido dise&ntilde;ado para escribir c&oacute;digo que es simple, obvio y f&aacute;cil de razonar con &eacute;l tanto para humanos como para m&aacute;quinas. Las caracter&iacute;sticas claves del dise&ntilde;o proporcionan formas de evitar la complejidad accidental en el proceso de desarrollo. El objetivo es mejorar la productividad de los desarrolladores, mejorar la calidad del software y habilitar una nueva clase de compiladores y herramientas de desarrollo.</span></p>
</blockquote>
</blockquote>
<p>En su descripci&oacute;n adem&aacute;s nos informa que toma conceptos de ML (el padre de Haskell) y JavaScript, adem&aacute;s el autor, Mark Marron, tambi&eacute;n ha admitido que hay algunas cosas de P que le gustan.</p>
<h2>Hola Mundo en Bosque</h2>
<p>Actualmente el compilador est&aacute; en fase experimental, si quieres probarlo estas son los comandos necesarios para hacerlo funcionar en Linux (Node.js tiene que estar instalado)</p>
<pre><code>
git clone https://github.com/Microsoft/BosqueLanguage
cd BosqueLanguage/ref_impl
npm install
npm run-script build
npm test
node bin/test/app_runner.js FICHERO_BOSQUE
</code></pre>
<p>Y un hola mundo tiene la siguiente pinta.</p>
<pre><code>
namespace NSMain;

entrypoint function main(): String {
	return "Hola Mundo";
}</code></pre>
<p>Y el resultado es el siguiente:</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="http://files.adrianistan.eu/BosqueHolaMundo.png" alt="" /></p>
<p>A primera vista ya parece un poco raro ya que hemos devuelto directamente un string en la funci&oacute;n main. En Bosque no hay efectos colaterales, lo que tambi&eacute;n implica que no puede haber (de momento) entrada y salida m&aacute;s all&aacute; de las propias funciones de punto de entrada. En Bosque el punto de entrada es arbitrario, pero de momento la mayor&iacute;a de ejemplos usan la funci&oacute;n main dentro de NSMain.</p>
<h2>Variables inmutables</h2>
<p>En Bosque todas las variables son inmutables. Eso quiere decir que no es posible modificar su contenido una vez han sido asignadas:</p>
<pre><code>
namespace NSMain;

function add(x: Int, y: Int): Int {
	return x+y;
}

entrypoint function main(): Int {
	var result = add(7,12);
	return result;
}
</code></pre>
<p>No obstante, es posible usar az&uacute;car sint&aacute;ctico y tener variables "mutables" con var!, la gracia es que debido a otros dise&ntilde;os de Bosque que veremos m&aacute;s adelante, no altera el resultado global.</p>
<h2>Par&aacute;metros por referencia</h2>
<p>Tambi&eacute;n es posible modificar ciertos par&aacute;metros por referencia directamente de forma segura, aseguranda la ausencia de efectos colaterales. Esto permite una programaci&oacute;n m&aacute;s c&oacute;moda.</p>
<h2>Tipado de Strings</h2>
<p>Los dise&ntilde;adores de Bosque saben que el tipo String es muy vers&aacute;til y se usa para muchas cosas, pero para el compilador sigue siendo String. &iquest;Por qu&eacute; no tipar estos strings? En un ejemplo sacado de la documentaci&oacute;n, se usa Zipcode como un tipo de String. Los zipcodes solo pueden pasar a string y un string no puede pasar directamente a Zipcode si no es verificado antes. As&iacute; evitamos mezclar Zipcodes y Emails (por ejemplo), que en otros lenguajes ser&iacute;an ambos Strings.</p>
<pre><code>
function foo(zip: String[Zipcode], name: String) {...}

var zc: String[Zipcode] = ...;
var user: String = ...;

foo(user, zc) //Type error String not convertible to String[Zipcode]
foo(zc, user) //ok
</code></pre>
<h2>Invocaciones flexibles</h2>
<p>Aqu&iacute; de forma parecida a Python, Bosque permite gran flexibilidad a la hora de pasar argumentos.</p>
<pre><code>
function nsum(d: Int, ...args: List[Int]): Int {
    return args.sum(default=d);
}

function np(p1: Int, p2: Int): {x: Int, y: Int} {
    return @{x=p1, y=p2};
}

//calls with explicit arguments
var x = nsum(0, 1, 2, 3); //returns 6

var a = np(1, 2);         //returns @{x=1, y=2}
var b = np(p2=2, 1);      //also returns @{x=1, y=2}

//calls with spread arguments
var t = @[1, 2, 3];
var p = nsum(0, ...t);    //returns 6 -- same as explicit call

var r = @{p1=1, p2=2};
var q = np(...r);         //returns @{x=1, y=2} -- same as explicit call
</code></pre>
<h2>Operaciones de golpe</h2>
<p>Bosque permite hacer modificaciones sobre una estructura o tupla de golpe, en una sola l&iacute;nea, indicando todos los campos que se modifican y su operaci&oacute;n. Esto puede ser muy interesante si permite generar instrucciones SIMD autom&aacute;ticamente y aprovechar de una vez toda esa potencia subyacente que tienen los procesadores actuales.</p>
<h2>Manejo de nones</h2>
<p>En Bosque existe el valor especial none similar al NULL o undefined de otros lenguajes. Bosque tiene un tratamiento especial para este tipo de valores que permite que sea m&aacute;s c&oacute;modo trabajar con ellos. La verdad es que esto me ha sorprendido ya que la tendencia actual es a reducir los NULL. Aunque claro, si se permiten solamente en situaciones controladas por el compilador podr&iacute;a ser muy interesante, ya que ser&iacute;a m&aacute;s simple y sencillo que las alternativas que existen en Haskell y Rust.</p>
<h2>Procesamiento iterativo</h2>
<p>Bosque no tiene bucles. No al menos nada comparable al WHILE y al FOR. Otro trabajo de investigaci&oacute;n de Microsoft (<a href="https://www.microsoft.com/en-us/research/uploads/prod/2018/10/LoopIdioms.pdf">Mining Semantic Loop Idioms</a>) afirm&oacute; que en la gran mayor&iacute;a de casos todos los bucles pertenecen a unos casos concretos de bucle. Es por ello, y con el objetivo de hacer la programaci&oacute;n m&aacute;s predecible, que no existen los bucles como tal. Otra vez, un ejemplo desde la documentaci&oacute;n</p>
<pre><code>
var v: List[Int?] = List@{1, 2, none, 4};

//Chained - List@{1, 4, 16}
v-&gt;filter(fn(x) =&gt; x != none)-&gt;map[Int](fn(x) =&gt; x*x)

//Piped none filter - List@{1, 4, 16}
v |&gt; filter(fn(x) =&gt; x != none) |&gt; map[Int](fn(x) =&gt; x*x)

//Piped with noneable filter - List@{1, 4, 16}
v |??&gt; map[Int](fn(x) =&gt; x*x)

//Piped with none to result - List@{1, 4, none, 16}
v |?&gt; map[Int](fn(x) =&gt; x*x)
</code></pre>
<h2>Recursi&oacute;n</h2>
<p>En la mayor&iacute;a de lenguajes funcionales se suelen suplir algunas de las carencias habituales (bucles, variables inmutables, etc) con complejas jerarqu&iacute;as de recursi&oacute;n. Estos sistemas funcionan pero no son muy claros. Bosque quiere romper con este esquema permitiendo solo llamadas recursivas a funciones que expl&iacute;citamente lo marquen.</p>
<h2>Igualdad</h2>
<p>Las comprobaciones de igualdad siempre est&aacute;n definidas por el tipo. Nunca se comprueba la referencia, evitando as&iacute; uno de los tipos de fallos m&aacute;s comunes en lenguajes OOP.</p>
<p>Y muchas otras cosas. El objetivo final es que cada programa Bosque tenga un &uacute;nico y bien definido resultado, y cada funci&oacute;n lo mismo. De este modo ser&aacute; m&aacute;s f&aacute;cil encontrar y razonar sobre los fallos, obteniendo software de mejor calidad en menos tiempo.</p>
<p>Habr&aacute; que ver como evoluciona Bosque, que ahora mismo est&aacute; muy verde y sujeto a muchos cambios. Puede ser una interesante alternativa en el futuro o un simple experimento de Microsoft Research que no vaya a m&aacute;s (o cuyos resultados se apliquen a C# por ejemplo). Vosotros, &iquest;qu&eacute; opin&aacute;is?</p>
<p>&nbsp;</p>]]></description>
                <comments>https://blog.adrianistan.eu/bosque-lenguaje-programacion-microsoft</comments>
                <pubDate>Wed, 24 Apr 2019 16:12:49 +0000</pubDate>
            </item>
        
            <item>
                <title>Miri: una máquina virtual para Rust</title>
                <link>https://blog.adrianistan.eu/miri-maquina-virtual-rust</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/miri-maquina-virtual-rust</guid>
                <description><![CDATA[<p>Hace poco sal&iacute;a la noticia de que Miri ya estaba disponible para instalar en las versiones nightly de Rust. Pero, &iquest;qu&eacute; es Miri? y &iquest;por qu&eacute; es importante una m&aacute;quina virtual para Rust?</p>
<p>Rust es un lenguaje compilado a c&oacute;digo m&aacute;quina. No necesita int&eacute;rpretes ni m&aacute;quinas virtuales para funcionar, sin embargo, hay situaciones donde Miri es &uacute;til. Hay dos casos en particular donde es interesante y se habilita la posibilidad de un tercero:</p>
<ul>
<li>Evaluar las <strong>expresiones const</strong> de forma m&aacute;s eficiente</li>
<li>Detectar <strong>fallos</strong> que solo pueden ser detectados en <strong>runtime</strong></li>
<li>Permitir&aacute; tener un REPL de Rust en el futuro</li>
</ul>
<p>&iquest;C&oacute;mo? Las expresiones const son c&oacute;digo Rust que se ejecuta en tiempo de compilaci&oacute;n, para obtener una constante que finalmente ser&aacute; la que vaya en el ejecutable. Cuando rustc se encuentra con una de ellas, compila el c&oacute;digo necesario para obtener el valor de vuelta de esa funci&oacute;n. Este proceso es largo, sobre todo teniendo en cuenta que ese c&oacute;digo solo va a ser ejecutado durante la compilaci&oacute;n del programa entero, por lo que no es necesaria una compilaci&oacute;n completa a c&oacute;digo m&aacute;quina. De este modo se mejora la velocidad en la mayor&iacute;a de expresiones const.</p>
<p>Por otro lado, Rust no es capaz de prevenir todos los fallos posibles dada su naturaleza y en c&oacute;digo unsafe todav&iacute;a menos. Miri comprueba los siguientes fallos:</p>
<ul>
<li>Accesos fuera de memoria y use-after-free</li>
<li>Uso incorrecto de variables no inicializadas</li>
<li>Violaci&oacute;n de precondiciones intr&iacute;nsecas (llegar a <a href="https://doc.rust-lang.org/stable/std/hint/fn.unreachable_unchecked.html" rel="nofollow"><code>unreachable_unchecked</code></a> , llamar <a href="https://doc.rust-lang.org/stable/std/ptr/fn.copy_nonoverlapping.html" rel="nofollow"><code>copy_nonoverlapping</code></a> con rangos que se superponen, ...)</li>
<li>Accesos a memoria no lo suficientemente bien alineados</li>
<li>Violaciones de invariantes de tipos b&aacute;sicos (por ejemplo un bool que no sea ni 0 ni 1 internamente o un valor de enum fuera de rango)</li>
</ul>
<p>En este sentido Miri aporta algo similar a Valgrind de C pero mejorado.</p>
<p>Actualmente Miri tiene algunas limitaciones, por ejemplo, no soporta concurrencia, acceso al sistema de archivos o acceso a la red.</p>
<h2>&iquest;C&oacute;mo usar Miri?</h2>
<p>Miri est&aacute; disponible solo en algunas versiones nightly. Al tiempo de escribir este post, los siguientes comandos permiten obtener la copia m&aacute;s reciente de Miri:</p>
<pre><code>
rustup toolchain install nightly-2019-04-17
rustup component add miri --toolchain=nightly-2019-04-17
cargo +nightly-2019-04-17 miri test
cargo +nightly-2019-04-17 miri run
</code></pre>
<p>El uso es muy sencillo, test ejecuta los test y run ejecuta la aplicaci&oacute;n si es un binario.</p>
<p>Pongamos un ejemplo de un test que funciona normalmente pero falla en Miri.</p>
<pre><code class="language-rust">
#[test]
fn does_not_work_on_miri() {
    let x = 0u8;
    assert!(&amp;x as *const _ as usize % 4 &lt; 4);
}
</code></pre>
<p>Ejecutando cargo test todo funciona bien pero con miri test falla.</p>
<p style="text-align: center;"><img src="../../static/img/MiriTest.png" alt="" width="1096" height="508" /></p>
<p style="text-align: left;">Y con esto ya tenemos otra herramienta m&aacute;s para nuestra programaci&oacute;n en Rust</p>]]></description>
                <comments>https://blog.adrianistan.eu/miri-maquina-virtual-rust</comments>
                <pubDate>Thu, 18 Apr 2019 12:15:18 +0000</pubDate>
            </item>
        
            <item>
                <title>Programación lineal: alcanzando la perfección</title>
                <link>https://blog.adrianistan.eu/programacion-lineal-alcanzar-perfeccion</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/programacion-lineal-alcanzar-perfeccion</guid>
                <description><![CDATA[<p>La <strong>programación lineal</strong> es una disciplina dentro de las matemáticas, más concretamente, del campo de la <strong>investigación operativa</strong> muy interesante. La problemática que trata de resolver es la de asignar recursos limitados entre actividades competitivas de la mejor manera posible, es decir, optimizar este reparto. Programación significa en este contexto <strong>planificación</strong>.</p>
<p>La historia de la programación lineal está muy ligada a la investigación operativa. Esta disciplina tuvo su auge durante la Segunda Guerra Mundial, cuando los ejércitos financiaron a los matemáticos para encontrar formas óptimas de asignar los recursos limitados de los que se disponía durante la guerra (comida, medicamentos, soldados, aviones, ...). Durante estos años se sentaron las bases pero no fue hasta el año 1947 cuando George Dantzig desarrolló el <strong>algoritmo símplex</strong>. Este algoritmo, ha sido calificado como uno de los 10 algoritmos más importantes del siglo XX, y no es para menos, ya que abre la puerta a resolver un montón de problemas en un tiempo relativamente eficiente.</p>
<p>El algoritmo símplex tiene una complejidad de [latex]O(2^{n})[/latex], muy costoso computacionalmente, pero afortunadamente la mayoría de problemas se resolverán en tiempo polinómico.</p>
<p style="text-align: center;"><img src="https://files.adrianistan.eu/Simplex.png" alt="" /></p>
<p>En primer lugar vamos a describir las características de la programación lineal, ya que existen otros modelos, programación entera, programación binaria, ...</p>
<ul>
<li>Modelo lineal: es decir, todo lo que aparecen son funciones lineales. Esto significa que vamos a trabajar SIEMPRE con números reales (esta es una de las grandes limitaciones)</li>
<li>Variables de decisión: variables del modelo que deben determinarse, la solución suele formar parte de ellas</li>
<li>Función objetivo: Una función donde intervienen una o más variables de decisión y que tenemos que optimizar: ya sea maximizar o minimizar.</li>
<li>Restricciones: desigualdades e igualdades que tienen que cumplir las variables de decisión en una solución válida, también llamada, factible</li>
</ul>
<p>Imagina que tenemos 30 piezas de hierro. Nuestro objetivo es tener el mayor número de piezas de hierro al cabo de 100 segundos. Para ello podemos comprar un extractor básico o un extractor eléctrico. El extractor básico tiene un coste de 10 piezas de hierro y el eléctrico 23 piezas. La velocidad de minado del primero es 0.25 piezas por segundo y el segundo es 0.5 piezas por segundo. ¿Cuál sería la forma óptima de hacerlo?</p>
<p style="text-align: center;"><img src="https://files.adrianistan.eu/FactorioExtractor.png" alt="" /></p>
<p>Bien, el modelo de programación lineal es muy simple, hay dos variables de decisión (cantidad de extractores básico y eléctricos que compramos), una función objetivo a maximizar que representa la cantidad final de hierro que vamos a tener. Esto se calcula teniendo en cuenta el hierro que va a producir en 100 segundos y el coste que tiene comprar uno. Y una restricción que sirve para indicar que tenemos una cantidad de hierro limitado y cada extractor tiene un precio en hierro diferente. Esto se expresa de la siguiente forma:</p>
<p> </p>
<p>$$X_1: \text{cantidad de extractores básicos a fabricar} \\ X_2: \text{cantidad de extractores eléctricos a fabricar} \\ \text{maximizar} z= (100*0.25-10)X_1 + (100*0.5-23)X_2 \\ 10X_1 + 23X_2 \le 30 \\ X_i \ge 0, i=1,2$$</p>
<p>Y ya está. Ahora, si usamos un programa que soporte el algoritmo símplex obtendremos la solución. Existen muchos programas, y por lo general, bastante caros: IBM CPLEX, Xpress de FICO, Gurobi e incluso el GNU GLPK. No obstante, estos programas son bastante avanzados y admiten muchas opciones. Para resolverlo voy a usar SWI Prolog, pero no nos centremos mucho en los detalles:</p>
<pre><code class="language-prolog">
:- use_module(library(simplex)).

extractor(S) :-
    gen_state(S0),
    extractor_constraints(S0,S1),
    maximize([15*x1,27*x2],S1,S).

extractor_constraints -->
    constraint([10*x1,27*x2] =< 30).
</code></pre>
<p style="text-align: center;"><img src="https://files.adrianistan.eu/PrologSimplex.png" alt="" /></p>
<p>El resultado es que X1=3 y X2=0, lo que quiere decir, que lo óptimo es gastarse el hierro en comprar 3 extractores básicos y ninguno avanzado. Una cosa que podemos plantearnos es si modificando el coste de compra del extractor avanzado, la solución pasa a ser otra. Simplemente modificamos la restricción y la función objetivo:</p>
<p>$$X_1: \text{cantidad de extractores básicos a fabricar} \\ X_2: \text{cantidad de extractores eléctricos a fabricar} \\ \text{maximizar} z= (100*0.25-10)X_1 + (100*0.5-20)X_2 \\ 10X_1 + 20X_2 \le 30 \\ X_i \ge 0, i=1,2$$</p>
<p>Y en este caso la solución sí es diferente, pero es una fracción, lo cuál no tiene mucho sentido. Este es uno de los inconvenientes principales de la PL, que las variables son infinitamente divisibles.</p>
<h2>Mezclas</h2>
<p>Vamos a poner un ejemplo un poco más complejo, en este caso con mezclas.</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://files.adrianistan.eu/Refineria.jpg" alt="" /></p>
<p>Una refinería mezcla 4 tipos de crudos para producir 3 tipos de gasolina los datosde interés son los siguientes:</p>
<table style="border-collapse: collapse; width: 100%;" border="1">
<tbody>
<tr>
<td style="width: 25%;">Crudos</td>
<td style="width: 25%;">Octanaje</td>
<td style="width: 25%;">Barriles diarios</td>
<td style="width: 25%;">€/barril</td>
</tr>
<tr>
<td style="width: 25%;">1</td>
<td style="width: 25%;">68</td>
<td style="width: 25%;">4000</td>
<td style="width: 25%;">31</td>
</tr>
<tr>
<td style="width: 25%;">2</td>
<td style="width: 25%;">86</td>
<td style="width: 25%;">5050</td>
<td style="width: 25%;">33</td>
</tr>
<tr>
<td style="width: 25%;">3</td>
<td style="width: 25%;">91</td>
<td style="width: 25%;">7100</td>
<td style="width: 25%;">36</td>
</tr>
<tr>
<td style="width: 25%;">4</td>
<td style="width: 25%;">99</td>
<td style="width: 25%;">4300</td>
<td style="width: 25%;">39</td>
</tr>
</tbody>
</table>
<p> </p>
<table style="border-collapse: collapse; width: 100%;" border="1">
<tbody>
<tr>
<td style="width: 25%;">Gasolina</td>
<td style="width: 25%;">Octanaje mínimo</td>
<td style="width: 25%;">PVP</td>
<td style="width: 25%;">Demanda diaria</td>
</tr>
<tr>
<td style="width: 25%;">1</td>
<td style="width: 25%;">95</td>
<td style="width: 25%;">45</td>
<td style="width: 25%;">Como máximo 10000 barriles</td>
</tr>
<tr>
<td style="width: 25%;">2</td>
<td style="width: 25%;">90</td>
<td style="width: 25%;">43</td>
<td style="width: 25%;">Cualquier cantidad</td>
</tr>
<tr>
<td style="width: 25%;">3</td>
<td style="width: 25%;">85</td>
<td style="width: 25%;">41</td>
<td style="width: 25%;">Al menos 15000 barriles</td>
</tr>
</tbody>
</table>
<p>La compañía vende el crudo que no utiliza en la producción de gasolinas a 39€/barril si su índice de octanaje está por encima de 90 y a 37€/barril si está pordebajo de 90.</p>
<p>¿Cuál es el plan de producción óptimo?</p>
<p>$$X_{ij}: \text{barriles de crudo i para producir gasolina de tipo j} \\ V_i: \text{barriles de crudo tipo i que se venden} \\ C_i: \text{barriles de crudo de tipo i que se compran} \\ G_J: \text{barriles de gasolina de tipo j que se producen y se venden} \\ Max z = 45G_1+43G_2+41G_3+37V_1+37V_2+39V_3+39V_4-31C_1-33C_2-36C_3-39C_4 \\ C_1 \le 4000 \\ C_2 \le 5050 \\ C_3 \le 7100 \\ C_4 \le 4300 \\ G_1 \le 10000 \\ G_3 \ge 15000 \\ 68X_{11} + 86X_{21} + 91X_{31} + 99X_{41} \ge 95G_1 \\ 68X_{12} + 86X_{22} + 91X_{32} + 99X_{42} \ge 90G_2 \\ 68X_{13} + 86X_{23} + 91X_{33} + 99X_{43} \ge 85G_3 \\ X_{11} + X_{21} + X_{31} + X_{41} = G_1 \\ X_{12} + X_{22} + X_{32} + X_{42} = G_2 \\ X_{13} + X_{23} + X_{33} + X_{43} = G_3 \\ X_{11} + X_{12} + X_{13} + V_1 = C_1 \\ X_{21} + X_{22} + X_{23} + V_2 = C_2 \\ X_{31} + X_{32} + X_{33} + V_3 = C_3 \\ X_{41} + X_{42} + X_{43} + V_4 = C_4 $$</p>
<p>Cuya solución es la siguiente: X13=3457.41, X21=1509.97, X23=3540.03, X33=7100, X41=3397.44, X43=902.564, G1=4907.41, G3=15000, V1=542.593, C1=4000, C2=5050, C3=7100 y C4=4300.</p>
<h2>Transporte</h2>
<p>Un tipo especial de PL es el problema del transporte. Tenemos N orígenes y M destinos, cada ruta entre origen y destino tiene un coste. ¿Cómo transportamos de forma óptima?</p>
<p style="text-align: center;"><img src="https://files.adrianistan.eu/AmazonLogistics.jpg" alt="" /></p>
<p>Por ejemplo, tenemos dos fábricas de chips verdes, A y B, y queremos transportarlo a 3 sitios, X, Y y Z. A-X cuesta 50, A-Y cuesta 100, A-Z cuesta 70. B-X cuesta 60, B-Y cuesta 30 y B-Z cuesta 200. En A hay 200 chips, en B hay 300, y queremos en 100 chips en X, 200 en Y y 200 en Z.</p>
<p>$$X_{ij}: \text{chips que se mueven de i a j} \\ Min z = 50X_{AX} + 100X_{AY} + 70X_{AZ} + 60X_{BX} + 30X_{BY} + 200X_{BZ} \\ X_{AX}+X_{AY}+X_{AZ} = 200 \\ X_{BX}+X_{BY}+X_{BZ}=300 \\ X_{AX}+X_{BX}=100 \\ X_{AY} + X_{BY} = 200 \\ X_{AZ} + X_{BZ} = 200 \\ X_i \ge 0, i=A,B j=X,Y,Z$$</p>
<pre><code class="language-prolog">transporte(S) :-
    gen_state(S0),
    transporte_constraints(S0,S1),
    minimize([50*ax,100*ay,70*az,60*bx,30*by,200*bz],S1,S).

transporte_constraints -->
    constraint([ax,ay,az] = 200),
    constraint([bx,by,bz] = 300),
    constraint([ax,bx] = 100),
    constraint([ay,by] = 200),
    constraint([az,bz] = 200).</code></pre>
<p>La solución es Xaz = 200 (todo lo que se produce en A va a Z) y Xbx = 100 y Xby = 200 (desde B se mandan 100 a X y 200 a Y).</p>
<p>El problema del transporte es aplicable no solo a transporte propiamente dicho, sino que se puede aplicar a muchos otros problemas.</p>
<p>Espero que esta corta introducción a la programación lineal os haya picado la curiosidad por si no lo conocíais. Esto es una disciplina muy amplia y se podría hacer un blog solo de esto.</p>]]></description>
                <comments>https://blog.adrianistan.eu/programacion-lineal-alcanzar-perfeccion</comments>
                <pubDate>Sun, 14 Apr 2019 23:29:06 +0000</pubDate>
            </item>
        
            <item>
                <title>Agromapa de Castilla y León</title>
                <link>https://blog.adrianistan.eu/agromapa-castilla-leon</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/agromapa-castilla-leon</guid>
                <description><![CDATA[<p>Hace unos meses salió la convocatoria del Concurso de Datos Abiertos de Castilla y León 2018. Esta era la tercera vez que se organizaba y la primera en la que me sentía suficientemente preparado como para presentar algo.</p>
<p>El objetivo del concurso es muy simple, hay que elaborar un proyecto usando, entre otras cosas, los datos abiertos que ofrece la Junta de Castilla y León. El resultado fue Agromapa, una web interactiva donde podemos ver los cultivos más populares de los municipios de forma muy vistosa, así como saber en qué zonas se cultivan unos y otros. Una forma didáctica de saber más sobre la comunidad. Para el proyecto usé la librería D3, usando un fichero GeoJSON que obtuve de <a href="https://wambachers-osm.website/boundaries/">OSM Boundaries</a> (los datos son de OpenStreetMap, pero desde OpenStreeMap es difícil sacar cierta información). Además he usado datos de <a href="https://www.wikidata.org/wiki/Wikidata:Main_Page">Wikidata</a>, como las fotos de las especies naturales y de los municipios. Todo ello usando además los CSV de la propia Junta de Castilla y León, los cuáles tenían algunos problemas (por ejemplo había palabras mal escritas en los datos de columna, un "bug" que me llevó un buen rato encontrar).</p>
<p>Además usé Python con Pandas para generar el GeoJSON con todos los datos que se muestran en pantalla. Este fichero ocupa la friolera de 17MB, y aunque soy consciente que podía haber sido más pequeño, no tuve tiempo para optimizar mucho el fichero (implicaba entre otras cosas rehacer algunas cosas)</p>
<p>Aquí tenéis una imagen y un enlace al proyecto.</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://files.adrianistan.eu/AgroMapa.png" alt="" width="1366" height="741" /></p>
<p style="text-align: center;"><a href="http://adrianistan.eu/agromapa">http://adrianistan.eu/agromapa</a></p>
<p style="text-align: left;">Además, todo el código lo tenéis disponible en GitHub, para que podáis ver como se ha hecho cada cosa.</p>
<p style="text-align: center;"><a href="https://github.com/aarroyoc/agromapa">https://github.com/aarroyoc/agromapa</a></p>
<p style="text-align: left;">El caso es que presente el proyecto y no salió ganador, pero salió con una mención especial. Esto significa que no hay dotación económica pero si reconocimiento por su parte de ser un buen proyecto. Hoy era el día de la entrega de premios, a la que había sido invitado. Fue en el Monasterio de Nuestra Señora del Prado, actual sede de la Consejería de Educación y acudieron el Vicepresidente de la Junta y la Consejera de Economía y Hacienda.</p>
<p style="text-align: left;"><img style="display: block; margin-left: auto; margin-right: auto;" src="https://files.adrianistan.eu/DatosAbiertos.jpg" alt="" width="644" height="389" />En general me ha gustado mucho la experiencia, he mejorado mis capacidades con D3, SVG y SPARQL y el resultado ha gustado a mucha gente, entre familiares, amigos y miembros del jurado. ¡Os invito a que le echéis un vistazo!</p>]]></description>
                <comments>https://blog.adrianistan.eu/agromapa-castilla-leon</comments>
                <pubDate>Mon, 08 Apr 2019 16:43:13 +0000</pubDate>
            </item>
        
            <item>
                <title>¿Qué pasó con la Web Semántica?</title>
                <link>https://blog.adrianistan.eu/que-paso-con-web-semantica</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/que-paso-con-web-semantica</guid>
                <description><![CDATA[<p>La web sem&aacute;ntica ha sido un concepto estudiado y desarrollado por W3C pr&aacute;cticamente desde el comienzo de la web misma por el propio Tim Berners-Lee, aunque su desarrollo se aceler&oacute; con el art&iacute;culo de 2001 en Scientific American, <em>The Semantic Web</em>. La idea es muy sencilla, si la web ha servido para enlazar documentos, de distintos or&iacute;genes, la web sem&aacute;ntica es lo mismo, pero reduci&eacute;ndonos a lo m&aacute;s fundamental, los datos en s&iacute; y adem&aacute;s, de forma comprensible para ser procesado por una m&aacute;quina.<br /><br />El concepto es muy prometedor, pero con el paso de los a&ntilde;os cada vez se oye menos sobre el tema. En este art&iacute;culo analizaremos las tecnolog&iacute;as que componen la web sem&aacute;ntica y los problemas que tiene en el mundo actual.</p>
<h2><br />Demasiados est&aacute;ndares</h2>
<p>La web sem&aacute;ntica dispone de varios est&aacute;ndares con la misma finalidad, o con objetivos superpuestos. Esto es una redundancia que impide a los desarrolladores saber exactamente a qu&eacute; adherirse. Por ejemplo, es habitual pensar que RDF es un tipo de XML. Pero esto es incorrecto. RDF es un est&aacute;ndar abstracto que no especifica como se debe almacenar su informaci&oacute;n. Aparte de XML existen muchas otras implementaciones, v&aacute;lidas, para representar datos RDF: Turtle, N3, N-Quads, RDFa, JSON-LD,...<br /><br />No solo RDF sufre este problema. En los lenguajes de representaci&oacute;n de reglas de inferencia tambi&eacute;n ocurre. Existe de hecho un est&aacute;ndar dise&ntilde;ado para la interoperabilidad entre estos lenguajes, RIF: <em>Although originally envisioned by many as a "rules layer" for the semantic web, in reality </em><em>the design of RIF is based on the observation that there are many "rules languages" in existence, and </em><em>what is needed is to exchange rules between them.</em>. Mientras tanto W3C mantiene otro est&aacute;ndar para las reglas, SWRL (Semantic Web Rule Language). Existen adem&aacute;s otros est&aacute;ndares inpendientes: R2ML y RuleML.<br /><br />Estos est&aacute;ndares disponen muchas veces de varios perfiles o dialectos, dise&ntilde;ados para cargas de trabajo diferentes. RIF por ejemplo, tiene varios dialectos, algunos estandarizados (DTB, Core, FLD, BLD y PRD) y otros sin estandarizar (CASPD, URD y SILK principalmente).</p>
<p>&nbsp;</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://blog.adrianistan.eu/static/img/standards.png" alt="" width="500" height="283" /><br /><br />Esta fragmentaci&oacute;n tambi&eacute;n se ha visto en el propio formato RDF. Al ser demasiado abstracto, algunos programadores dise&ntilde;aron de forma m&aacute;s pragm&aacute;tica los formatos microformats y microdata, que aunque pueden ser sustituidos por RDFa, estos primeros son m&aacute;s sencillos de comprender. Estos est&aacute;ndares tienen unas ontolog&iacute;as fijas, &uacute;tiles, adaptadas y sin posibilidad de ser extendidas o reemplazadas (aunque microformats2 si es m&aacute;s potente es este aspecto). La idea es <em>evolucionar la web</em> en vez <br />de <em>revolucionar la web</em>, proponiendo cambios que causen poca fricci&oacute;n en el desarrollo web actual. Esto forma parte del movimiento de la web sem&aacute;ntica en letras min&uacute;sculas.</p>
<h2><br />Demasiada abstracci&oacute;n y falta de formaci&oacute;n</h2>
<p>Muchos est&aacute;ndares de la W3C se definen de forma muy abstracta, priorizando la correcci&oacute;n de los est&aacute;ndares desde un punto de vista te&oacute;rico. Los desarrolladores no est&aacute;n acostumbrados a tales niveles de abstracci&oacute;n y esperan tecnolog&iacute;a mucho m&aacute;s concreta.<br /><br />Si a esto se le suman pocos ejemplos de uso pr&aacute;ctico, poca documentaci&oacute;n de entrada y una falta de marketing respecto a otras tecnolog&iacute;as, muy poca gente va a poder entender el modelo mental de RDF y sacar toda su potencia.</p>
<h2><br />Burocracia</h2>
<p>Algunas partes de la web sem&aacute;ntica sufren de un problema burocr&aacute;tico. No solo los est&aacute;ndares llegan tarde y sin implementaci&oacute;n de referencia, sino que muchas veces el proceso de realizar modificaciones o a&ntilde;adir un elemento es lento y laborioso.<br /><br />El proceso de comit&eacute; de W3C ha demostrado ser lento e ineficaz en otras &aacute;reas. Por ejemplo, los navegadores han dejado de seguir el est&aacute;ndar HTML de W3C para centrarse en el est&aacute;ndar HTML de WHATWG, una organizaci&oacute;n creada por ellos mismos, mucho m&aacute;s r&aacute;pida en definir nuevas caracter&iacute;sticas. Gracias a WHATWG se pudo lograr HTML5, que luego fue estandarizado por W3C cuando todos los navegadores ya hab&iacute;an implementado las caracter&iacute;sticas del est&aacute;ndar. Otro caso similar existe con SVG, el est&aacute;ndar de gr&aacute;ficos vectoriales, que ha sido criticado por nunca lanzar la revisi&oacute;n SVG 2.0, debido a problemas internos.<br /><br />Otros componentes de la web sem&aacute;ntica como las URI ni siquiera son asignadas por la W3C sino que la asignaci&oacute;n de esquemas depende de IANA.</p>
<h2><br />Protecci&oacute;n de datos</h2>
<p>A nivel legal, muchos empresas y organizaciones prefieren mantener los datos de car&aacute;cter personal de forma privada. No existe un sistema integrado que permita acceder solamente a ciertos agentes acceder a determinada informaci&oacute;n de una base de datos. La web sem&aacute;ntica parece preparada &uacute;nicamente para datos de car&aacute;cter totalmente abierto, no parcialmente abiertos.</p>
<h2>Valor de los datos</h2>
<p>Seg&uacute;n diversos analistas los datos se han convertido en el nuevo petr&oacute;leo. Una empresa vale m&aacute;s por los datos que tiene que por los servicios que presta. Si asumimos esto, &iquest;por qu&eacute; regalar los datos? Ser&iacute;a tan tonto como regalar dinero. Las empresas han aprendido a mantener los datos de forma cerrada, para consumo propio o previo pago de grandes cantidades de dinero. <br /><br />Existen empresas, no obstante, que facilitan APIs para acceder y manipular la informaci&oacute;n. La gracia de estos sistemas es que est&aacute;n limitados en cuanto a peticiones y a la informaci&oacute;n a la que podemos acceder. Estas APIs, aunque suelen seguir un modelo REST com&uacute;n, no son interoperables entre s&iacute;. De este modo se intenta mantener la filosof&iacute;a del jard&iacute;n cerrado. Que los desarrolladores puedan hacer software que complemente a mi plataforma y no al rev&eacute;s.<br /><br />A grandes empresas como Google o Facebook no les interesa que toda su informaci&oacute;n sea accesible por otros desarrolladores, ya que les restar&iacute;a valor a su empresa. Es evidente que estas empresas no van a invertir en estas tecnolog&iacute;as, al menos para su desarrollo p&uacute;blico. En el caso de Google est&aacute; actitud es muy evidente. En 2010 compr&oacute; <a href="https://en.wikipedia.org/wiki/Freebase">Freebase</a>, una base de datos sem&aacute;ntica abierta bastante importante, para despu&eacute;s cerrarla e integrar su tecnolog&iacute;a en Google Knowledge Graph.<br /><br />S&oacute;lamente organizaciones como Wikipedia, MusicBrainz, DBpedia y los distintos organismos p&uacute;blicos mantienen bases de datos accesibles de forma totalmente abierta y transparente de los datos. Muchas de estas acciones englobadas dentro del movimiento Open Data pero la adopci&oacute;n por parte de las empresas ha ido decreciendo.</p>
<h2><br />Escalabilidad</h2>
<p>Se ha criticado que los modelos basados en RDF no son escalables. En efecto la mayor&iacute;a de implementaciones no han sido tan trabajadas como lo han podido ser bases de datos relacionales, o software de procesamiento Big Data. Es cierto que servidores como Virtuoso son capaces de manejar miles de millones de tripletas, pero sigue habiendo problemas subyacentes relacionado con la dificultad del indexado de RDF.</p>
<h2><br />Laboriosidad</h2>
<p>Formalizar el conocimiento lleva un esfuerzo considerable. Lleva esfuerzo tanto en la parte de creaci&oacute;n de los esquemas como en la parte de generaci&oacute;n de los datos. Con la aparici&oacute;n de sistemas Big Data, la tendencia es a almacenar los datos tal y como es generada, para luego ser analizada. Aunque se podr&iacute;an usar estos sistemas, para intentar formalizar los datos, no parece ser una pr&aacute;ctica com&uacute;n. Existen pocas herramientas enfocadas a hacer menos laborioso el trabajo, con la &uacute;nica excepci&oacute;n de Prot&eacute;g&eacute; para la elaboraci&oacute;n de ontolog&iacute;as.<br /><br />Han surgido ontolog&iacute;as m&aacute;s simples, con enfoque pr&aacute;ctico en organizaciones como <a href="http://schema.org">Schema.org</a>, donde los buscadores (Google, Bing, Yandex, ...) definen ontolog&iacute;as v&aacute;lidas para RDFa, con el fin de dotar a sus buscadores de mayor poder sem&aacute;ntico. Otros ejemplos son la iniciativa <a href="http://dublincore.org/">Dublin Core</a>.</p>
<h2><br />Falta de implementaciones</h2>
<p>La web sem&aacute;ntica dispone de muchas programas, de tipo acad&eacute;mico, muchos de ellos abandonados al poco tiempo. No obstante no hay apenas sistemas listos para producci&oacute;n. Muchos sistemas de gesti&oacute;n de bases de datos con soporte a tecnolog&iacute;as de la web sem&aacute;ntica no lo hacen de forma exclusiva. Existen varias bases de datos con soporte a SPARQL, <a href="https://virtuoso.openlinksw.com/">Virtuoso</a>, <a href="https://www.marklogic.com/">MarkLogic</a>, <a href="https://jena.apache.org/">Apache Jena</a>, <a href="https://aws.amazon.com/es/neptune/">Amazon Neptune</a>, Neo4j, aunque no en todos estos sistemas SPARQL es la opci&oacute;n recomendada por sus autores. Algunos est&aacute;ndares no est&aacute;n implementados (OWL2 solo es implementado por MarkLogic de forma completa).<br /><br />Fuera de las bases de datos existen todav&iacute;a menos librer&iacute;as que manejen RDF de forma sencilla para el programador, siendo el &uacute;nico ejemplo destacable la librer&iacute;a <a href="https://github.com/RDFLib/rdflib">RDFLib</a> para Python, que incluye un int&eacute;rprete de SPARQL para consultas locales, as&iacute; como un adaptador para usar PostgreSQL como almac&eacute;n de tuplas RDF.<br /><br />W3C no se ha encargado de mantener implementaciones de referencia de sus est&aacute;ndares.</p>
<h2><br />Conclusiones</h2>
<p>Creo firmemente que estamos ante una situaci&oacute;n de <em>worse is better</em>. Concepto ingenieril donde los haya que viene a decir se <br />resume en estas m&aacute;ximas:</p>
<ul>
<li>El dise&ntilde;o debe ser simple, tanto en implementaci&oacute;n como en interfaz. La simplicidad es el elemento m&aacute;s importante del dise&ntilde;o</li>
<li>El dise&ntilde;o no debe ser inconsistente. La consistencia puede ser sacrificada por la simplicidad, o pueden eliminarse del dise&ntilde;o las partes menos comunes o que introducen complejidad innecesaria</li>
<li>El dise&ntilde;o cubrir todas las situaciones cu&aacute;l probables sea que ocurran. Todos los casos razonablemente probables deben ser cubiertos. En todo caso, la completitud es la &uacute;ltima propiedad a tener en cuenta.</li>
</ul>
<p>Este estilo, denominado <em>escuela de Nueva Jersey</em> tiene su m&aacute;ximo exponente en UNIX y C, piezas tecnol&oacute;gicas de gran &eacute;xito cuya fama se basa en el uso de estas reglas. Un enfoque ingenieril basado en compromisos. Todo en contraposici&oacute;n a la <em>escuela de MIT</em>, de origen acad&eacute;mico, identificado popularmente con Lisp. El enfoque acad&eacute;mico no admite incorreciones, inconsistencias y completitud. La simplicidad es deseable, pero sin sacrificar en ning&uacute;n momento las caracter&iacute;sticas anteriores.<br /><br />Las tecnolog&iacute;as de la web sem&aacute;ntica parecen cumplir el enfoque de la escuela del MIT. Un producto acad&eacute;mico, que engloba a todo, a costa de reducir simplicidad.<br /><br />Afortunadamente muchos de los problemas mencionados aqu&iacute; est&aacute;n siendo tratados actualmente en una lluvia de ideas, denominada <a href="https://github.com/w3c/EasierRDF/"><strong>EasierRDF</strong></a> y W3C tiene intenci&oacute;n de ser m&aacute;s din&aacute;mica, sobre todo despu&eacute;s de haber perdido el control estandarizador de HTML y otras tecnolog&iacute;as clave, en favor de organizaciones m&aacute;s din&aacute;micas.<br /><br />Adem&aacute;s, se est&aacute;n desarrollando nuevos est&aacute;ndares de gran inter&eacute;s, como <a href="http://ipld.io">IPLD</a> (InterPlanetaryLinkedData) que permite extender la web sem&aacute;ntica a IPFS, Git y plataformas basadas en blockchain como Ethereum y Bitcoin.</p>]]></description>
                <comments>https://blog.adrianistan.eu/que-paso-con-web-semantica</comments>
                <pubDate>Sun, 10 Mar 2019 10:56:10 +0000</pubDate>
            </item>
        
            <item>
                <title>API de Adrianistán</title>
                <link>https://blog.adrianistan.eu/api-adrianistan</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/api-adrianistan</guid>
                <description><![CDATA[<p>Con unos d&iacute;as de diferencia respecto a la<a href="https://blog.adrianistan.eu/ano-nuevo-blog-nuevo"> publicaci&oacute;n del nuevo blog</a>, ha llegado una caracter&iacute;stica muy interesante, la API del blog.</p>
<p>Se trata de una API sem&aacute;ntica, usando el lenguaje de consulta <a href="https://www.w3.org/TR/sparql11-query/">SPARQL</a>, el cual es una recomendaci&oacute;n de W3C. La API solo permite hacer consultas. Aunque te&oacute;ricamente en SPARQL tambi&eacute;n se pueden modificar datos, no est&aacute; activado por motivos evidentes. La API es de acceso p&uacute;blico y cualquiera puede usarla, adem&aacute;s al ser SPARQL pod&eacute;is combinar las consultas con bases de datos de otros servidores. La API expone una ontolog&iacute;a basada en <a href="https://schema.org/BlogPosting">Schema.org</a>. En esa p&aacute;gina encontrar&eacute;is los nombres de los campos, su formato, etc</p>
<p>En este corto post voy a demostrar como hacer uso de esta funcionalidad. En este art&iacute;culo no pretendo explicar SPARQL, aunque si veo inter&eacute;s puede ser un futuro tutorial.</p>
<h2>Una petici&oacute;n simple</h2>
<p>Para hacer una petici&oacute;n, podemos usar cURL o cualquier programa similar para lanzar una petici&oacute;n a https://blog.adrianistan.eu/api. Hay tres requisitos:</p>
<ul>
<li>la petici&oacute;n debe ser POST</li>
<li>la petici&oacute;n debe declarar su Content-Type como application/sparql-query (tal y como dicta el est&aacute;ndar W3C)</li>
<li>en el cuerpo debe estar la consulta SPARQL en texto plano</li>
</ul>
<p>Veamos un ejemplo sencillo usando cURL</p>
<pre><code>
curl -X POST https://blog.adrianistan.eu/api -d "SELECT ?id WHERE { ?id ?b ?c.} LIMIT 5" --header "Content-Type: application/sparql-query"
</code></pre>
<p>El resultado es el siguiente:</p>
<p><img src="https://blog.adrianistan.eu/static/img/SPARQLDiario.png" alt="" width="1354" height="132" /></p>
<p>El resultado est&aacute; en formato JSON. Actualmente no es configurable el formato de salida.</p>
<h2>Una petici&oacute;n m&aacute;s compleja</h2>
<p>Esta primera petici&oacute;n no aportaba gran cosa, simplemente devolv&iacute;a resultados. Ahora vamos a hacer una query seria.</p>
<p>La query devolver&aacute; las URL de la gente que comenta en el blog:</p>
<pre><code>
PREFIX schema: &lt;http://schema.org/&gt;
PREFIX rdf: &lt;http://www.w3.org/1999/02/22-rdf-syntax-ns#&gt;

SELECT DISTINCT ?url 
WHERE {
    ?comment rdf:type schema:Comment.
    ?comment schema:author ?author.
    ?author schema:url ?url.
    FILTER(!regex(?url,"adrianistan.eu","i")).
}
</code></pre>
<p>Y si enviamos la query a la API, nos responde con las URL de vuestros comentarios:</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://blog.adrianistan.eu/static/img/SPARQLComentarios.png" alt="" width="729" height="289" /></p>
<p>Como v&eacute;is, la API es bastante potente y aunque su uso vaya a ser anecd&oacute;tico, quer&iacute;a tener una API de consulta en condiciones.</p>
<p>&nbsp;</p>]]></description>
                <comments>https://blog.adrianistan.eu/api-adrianistan</comments>
                <pubDate>Wed, 13 Feb 2019 19:06:42 +0000</pubDate>
            </item>
        
            <item>
                <title>Año nuevo, blog nuevo</title>
                <link>https://blog.adrianistan.eu/ano-nuevo-blog-nuevo</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/ano-nuevo-blog-nuevo</guid>
                <description><![CDATA[<p>Llevaba un mes entero sin escribir, &iquest;cre&iacute;ais que estaba descansando en una playa paradis&iacute;aca?</p>
<p>Evidentemente no, primero porque las playas no son mis lugares favoritos, y segundo porque realmente estaba haciendo algo gordo. En realidad eran varias cosas, pero la primera que puede ver la luz ahora mismo es esta. Este propio blog. &iquest;Lo v&eacute;is? Es nuevo. Est&aacute; hecho a mano en Rust. Y aunque todav&iacute;a le faltan algunas cosas, ya es lo suficientemente capaz como para sustituir a WordPress.</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://blog.adrianistan.eu/static/img/NewDesign.png" alt="" width="500" height="249" /></p>
<p>WordPress me gusta, funciona bien, es un producto intuitivo, con buen marketing y con una factura t&eacute;cnica aceptable. Pero hab&iacute;a varios "problemas":</p>
<ul>
<li>WordPress usa MariaDB (o MySQL), ahora mismo quiero aprovechar la potencia de SQL est&aacute;ndar y en eso solo PostgreSQL me ofrece lo que busco. No es que en este blog lo vaya a usar, pero as&iacute; evito tener que mantener dos bases de datos paralelamente en la misma Raspberry</li>
<li>soy un programador que no ha programado su blog (y aunque sea una tonter&iacute;a, admiro mucho a la gente que se programa sus propios blogs)</li>
<li>quer&iacute;a probar Rust en producci&oacute;n</li>
<li>quer&iacute;a probar tecnolog&iacute;a relacionada con la web sem&aacute;ntica</li>
</ul>
<p>Como veis, ning&uacute;n motivo real, podr&iacute;amos decir que lo he hecho por que s&iacute;.</p>
<h2>Tecnolog&iacute;as usadas</h2>
<p>Este sistema de blog, que he llamado Diario y que es software libre, aunque hay que modificar unas cuantas cosas, es un mejunje de varias tecnolog&iacute;as. Por un lado quer&iacute;a usar Rust en producci&oacute;n y por otro quer&iacute;a probar tecnolog&iacute;a relacionada con la web sem&aacute;ntica, as&iacute; que hay varios lenguajes de programaci&oacute;n involucrados.</p>
<ul>
<li>Rust es el lenguaje de programaci&oacute;n principal
<ul>
<li><a href="../../tutorial-rocket-echa-volar-tus-webapps-rust/">Rocket</a> es el servidor web, esto me obliga a usar nightly. Sin embargo Rocket me parece un framework web muy bien dise&ntilde;ado y funciona muy bien</li>
<li><a href="http://diesel.rs/">Diesel</a> como ORM. Pero no estoy usando migraciones, as&iacute; que al principio me cost&oacute; pillar la documentaci&oacute;n. Es un ORM muy bien dise&ntilde;ado pero cuando hay un error de compilaci&oacute;n es bastante cr&iacute;ptico</li>
<li><a href="https://tera.netlify.com/">Tera</a> como motor de plantillas</li>
<li>y alguna librer&iacute;a m&aacute;s como <a href="https://crates.io/crates/chrono">Chrono</a>, Regex, Ring o Serde</li>
</ul>
</li>
<li><a href="https://www.postgresql.org/">PostgreSQL</a> como base de datos</li>
<li><a href="https://www.python.org/">Python</a> para la funcionalidad del formulario de contacto. La raz&oacute;n es que no consegu&iacute; hacer funcionar lettre en un tiempo razonable para poder enviar emails. As&iacute; que uso el m&oacute;dulo smtp de Python.</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/XSLT">XSLT</a>, para generar los feeds RSS, el sitemap y para alguna cosa m&aacute;s
<ul>
<li>XSLT no lo hab&iacute;a usado nunca, pero no es complicado, eso s&iacute;, no hay librer&iacute;as en Rust para usarlo, as&iacute; que he usado xsltproc de l&iacute;nea de comandos</li>
</ul>
</li>
</ul>
<p>Quiz&aacute; todo te parezca normal, hasta lo de XSLT. &iquest;Por qu&eacute; he usado XSLT cuando ya tengo un sistema completo en Rust, que solo es cuesti&oacute;n de programar los feeds RSS y ya? Pues porque quiero que este blog tenga una API, una API de la web sem&aacute;ntica.</p>
<p>Para ello, tiene que aceptar peticiones <a href="https://en.wikipedia.org/wiki/SPARQL">SPARQL</a>. Todav&iacute;a no lo he conseguido, pero viendo la falta de herramientas y librer&iacute;as para trabajar con SPARQL, mi idea es generar un fichero con informaci&oacute;n RDF donde se almacena TODA la parte p&uacute;blica del blog. All&iacute; se ejecutar&aacute;n las consultas y de paso, me sirve para generar ficheros RSS y similares en XML de forma f&aacute;cil y sencilla, transformando el fichero RDF original.</p>
<h2>C&oacute;digo fuente</h2>
<p>El c&oacute;digo fuente del sistema, por si quer&eacute;is echarle un ojo, est&aacute; aqu&iacute;: <a href="https://github.com/aarroyoc/diario">https://github.com/aarroyoc/diario</a></p>
<h2>Feedback</h2>
<p>Todav&iacute;a quedan muchas cosas, como por ejemplo la API SPARQL (aunque eso casi est&aacute;). He intentado adem&aacute;s que todas las URLs antiguas sean compatibles con el nuevo sistema, aunque puede haber fallos, lo mismo con el formato de algunas p&aacute;ginas. Me gustar&iacute;a que coment&aacute;seis que cosas &eacute;chais en falta, hay que a&ntilde;adir, qu&eacute; tal el dise&ntilde;o del blog y ese tipo de cosas. Al final sois vosotros los que vais a leerlo m&aacute;s veces que yo.</p>]]></description>
                <comments>https://blog.adrianistan.eu/ano-nuevo-blog-nuevo</comments>
                <pubDate>Mon, 11 Feb 2019 21:44:53 +0000</pubDate>
            </item>
        
            <item>
                <title>Feliz Navidad 2018</title>
                <link>https://blog.adrianistan.eu/feliz-navidad-2018</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/feliz-navidad-2018</guid>
                <description><![CDATA[<p><iframe src="https://www.youtube.com/embed/eh2fpnwAPC0" width="560" height="315" frameborder="0" allowfullscreen=""></iframe></p><p>&nbsp;</p><!-- wp:paragraph --><p>&iexcl;Feliz Navidad a todos! Que los problemas de vuestra rutina no os amarguen el carb&oacute;n de az&uacute;car que os merec&eacute;is.</p><!-- /wp:paragraph --><p>&nbsp;</p><!-- wp:paragraph --><p>Estas fechas adem&aacute;s son apropiadas para hacer un repaso de lo que ha sido el blog durante 2018. As&iacute; que voy a abrir Google Analytics y veamos que ha ocurrido.</p><!-- /wp:paragraph --><p>&nbsp;</p><!-- wp:heading --><h2>Usuarios</h2><!-- /wp:heading --><p>&nbsp;</p><!-- wp:paragraph --><p>Este a&ntilde;o hemos batido r&eacute;cord, con <strong>39.328 usuarios &uacute;nicos</strong> contabilizados, en <strong>51.731 sesiones</strong>. Esto es casi el doble que el a&ntilde;o pasado en ambas m&eacute;tricas.</p><!-- /wp:paragraph --><p>&nbsp;</p><!-- wp:image {"id":1694} --><figure class="wp-block-image"><img class="wp-image-1694" src="https://files.adrianistan.eu/Analytics2018.png" alt="" /></figure><!-- /wp:image --><p>&nbsp;</p><!-- wp:paragraph --><p>Respecto a vuestra edad, cada vez sois m&aacute;s j&oacute;venes. Ha habido un crecimiento relativo importante en la categor&iacute;a 18-24 y un retroceso en los otros grupos de edad, pero en general todos los grupos tienen m&aacute;s lectores. Respecto a vuestro sexo, este a&ntilde;o ha habido un 18,65% de mujeres, respecto al a&ntilde;o anterior de 12,3%. Recordad que todos estos datos demogr&aacute;ficos no son nada precisos y es una estimaci&oacute;n que hace Google Analytics.</p><!-- /wp:paragraph --><p>&nbsp;</p><!-- wp:paragraph --><p>En cuanto a vuestros intereses, bastante similar a otros a&ntilde;os, usuarios tecnol&oacute;gicos, bastante m&aacute;s software. Las categor&iacute;as que m&aacute;s han crecido este a&ntilde;o son <strong>Enterprise Software</strong> y <strong>Science/Mathematics</strong>, curioso.</p><!-- /wp:paragraph --><p>&nbsp;</p><!-- wp:paragraph --><p>La mayor&iacute;a ten&eacute;is configurado como idioma el ingl&eacute;s o el castellano, con el tercer idioma siendo el franc&eacute;s (pero a mucha distancia).</p><!-- /wp:paragraph --><p>&nbsp;</p><!-- wp:paragraph --><p>En cuanto a vuestra ubicaci&oacute;n, la mayor&iacute;a segu&iacute;s siendo de Espa&ntilde;a, aunque latinoam&eacute;rica ha subido este a&ntilde;o bastante, algo que me ha alegrado mucho. El pa&iacute;s que m&aacute;s ha aumentado su porcentaje de visitas respecto al a&ntilde;o pasado a sido <del>Nueva Espa&ntilde;a</del> M&eacute;xico. Destacar que aunque en Venezuela y Estados Unidos ha aumentado el n&uacute;mero de lectores, lo ha hecho a un ritmo tan bajo, que su porcentaje de lectores ha bajado, &iquest;tendr&aacute; que ver la situaci&oacute;n pol&iacute;tica? Del Top 15 tambi&eacute;n se&ntilde;alar que Reino Unido es el &uacute;nico pa&iacute;s donde ha bajado el n&uacute;mero de lectores. Y este a&ntilde;o se han recibido visitas desde 114 territorios diferentes del mundo.</p><!-- /wp:paragraph --><p>&nbsp;</p><!-- wp:image {"id":1695} --><figure class="wp-block-image"><img class="wp-image-1695" src="https://files.adrianistan.eu/Paises2018.png" alt="" /></figure><!-- /wp:image --><p>&nbsp;</p><!-- wp:paragraph --><p>En cuanto a ciudades, la mayor&iacute;a ven&iacute;s de la ciudad con el mejor agua del mundo, los mejores atascos, las mejores cervezas y las mejores catedrales: Madrid. Le sigue Barcelona y en tercer lugar, Santiago de Chile. El top 10 lo completan: Bogot&aacute;, Ciudad de M&eacute;xico, Buenos Aires, Valencia, La Victoria, Medell&iacute;n y Sevilla. Esta vez, mi propia ciudad, Valladolid, no est&aacute; en el top 10.</p><!-- /wp:paragraph --><p>&nbsp;</p><!-- wp:image {"id":1697} --><figure class="wp-block-image"><img class="wp-image-1697" src="https://files.adrianistan.eu/Navegadores2018-1024x361.png" alt="" /></figure><!-- /wp:image --><p>&nbsp;</p><!-- wp:paragraph --><p>Se confirma la tendencia de que Chrome es el navegador m&aacute;s usado. El segundo es Firefox y el tercero es Safari. Como cosas curiosas, Edge ha superado a Internet Explorer y hay m&aacute;s usuarios de Opera que de Edge.</p><!-- /wp:paragraph --><p>&nbsp;</p><!-- wp:image {"id":1698} --><figure class="wp-block-image"><img class="wp-image-1698" src="https://files.adrianistan.eu/SistemaOperativo2018-1024x539.png" alt="" /></figure><!-- /wp:image --><p>&nbsp;</p><!-- wp:paragraph --><p>En cuanto a sistemas operativos se vuelve a ver una dominancia de Windows y Android. Por otro lado me gustar&iacute;a conocer a los que han entrado desde BeOS y desde Tizen.</p><!-- /wp:paragraph --><p>&nbsp;</p><!-- wp:heading --><h2>&iquest;C&oacute;mo lleg&aacute;is a este blog?</h2><!-- /wp:heading --><p>&nbsp;</p><!-- wp:paragraph --><p>Ahora vamos a ver como acced&eacute;is a este blog. La gran mayor&iacute;a lleg&aacute;is a trav&eacute;s de un buscador, principalmente Google aunque tambi&eacute;n Yahoo, Ecosia, Bing, Ask, DuckDuckGo y Yandex.</p><!-- /wp:paragraph --><p>&nbsp;</p><!-- wp:paragraph --><p>De los que lleg&aacute;is a trav&eacute;s de otras p&aacute;ginas, la mayor&iacute;a ven&iacute;s de Men&eacute;ame, que este a&ntilde;o se ha portado muy bien con el blog. La siguiente web es GitHub, ya que enlaza al blog en varios sitios (tutoriales de Rust, repos muy curiosos, &iexcl;e incluso desde issues!). Menci&oacute;n especial a las tres instancias de Moodle de las que tengo constancia que enlazan a mi blog (una de un instituto de la Rep&uacute;blica Checa, muy curioso). Bastantes visitas tambien desde Reddit.</p><!-- /wp:paragraph --><p>&nbsp;</p><!-- wp:paragraph --><p>Tambi&eacute;n hay qui&eacute;n nos sigue desde el <a href="https://t.me/adrianistan">canal de Telegram,</a> un <a href="https://blog.adrianistan.eu/feed/">lector RSS</a>, redes sociales y el correo (pod&eacute;is encontrar m&aacute;s informaci&oacute;n en la barra lateral).</p><!-- /wp:paragraph --><p>&nbsp;</p><!-- wp:heading --><h2>Lo m&aacute;s visto</h2><!-- /wp:heading --><p>&nbsp;</p><!-- wp:paragraph --><p>Finalmente, lo mejor para el final, &iquest;qu&eacute; contenido es el m&aacute;s popular del blog?</p><!-- /wp:paragraph --><p>&nbsp;</p><!-- wp:paragraph --><p>He de decir que esto ha sido una total sorpresa, se trata de un art&iacute;culo que no tuvo apenas repercusi&oacute;n cuando lo publiqu&eacute;, pero parece que much&iacute;sima gente ha llegado a &eacute;l a trav&eacute;s de Google. Se trata de...</p><!-- /wp:paragraph --><p>&nbsp;</p><!-- wp:paragraph {"align":"center"} --><p style="text-align: center;"><a href="https://blog.adrianistan.eu/2017/11/04/estadistica-python-media-mediana-varianza-percentiles-parte-iii/">Estad&iacute;stica en Python: media, mediana, varianza, percentiles (Parte III)<br /></a></p><!-- /wp:paragraph --><p>&nbsp;</p><!-- wp:paragraph --><p>La verdad es que me ha sorprendido mucho, pero al parecer mucha gente desea hacer estad&iacute;stica en Python, pero curiosamente no es el primer art&iacute;culo de la serie que hice.</p><!-- /wp:paragraph --><p>&nbsp;</p><!-- wp:paragraph --><p>El segundo art&iacute;culo ha sido: <a href="https://blog.adrianistan.eu/2018/09/19/ipfs-el-futuro-de-la-web-descentralizada/">IPFS, el futuro de la web descentralizada,</a> que tuvo bastante repercusi&oacute;n e inter&eacute;s por vuestra parte desde el principio.</p><!-- /wp:paragraph --><p>&nbsp;</p><!-- wp:paragraph --><p>El tercer art&iacute;culo con m&aacute;s visitas ha sido: <a href="https://blog.adrianistan.eu/2017/10/31/estadistica-python-pandas-numpy-scipy-parte-i/">Estad&iacute;stica en Python: Parte 1</a> Como v&eacute;is, la estad&iacute;stica ha tenido bastante &eacute;xito en el blog.</p><!-- /wp:paragraph --><p>&nbsp;</p><!-- wp:paragraph --><p>Sin nada m&aacute;s, me despido. &iexcl;Muchas gracias a todos los que segu&iacute;s el blog! Este a&ntilde;o cada vez sois m&aacute;s, y est&aacute;is m&aacute;s presentes. Yo me voy a ver <em>&iexcl;Qu&eacute; bello es vivir!</em> otra vez, que es una pel&iacute;cula que me gusta mucho.</p><!-- /wp:paragraph -->]]></description>
                <comments>https://blog.adrianistan.eu/feliz-navidad-2018</comments>
                <pubDate>Tue, 25 Dec 2018 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Algoritmos genéticos: un caso práctico</title>
                <link>https://blog.adrianistan.eu/algoritmos-geneticos-un-caso-practico</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/algoritmos-geneticos-un-caso-practico</guid>
                <description><![CDATA[<p>Existen muchos tipos de algoritmos. Hoy vamos a hablar de un tipo de algoritmo no demasiado conocido, los <strong>algoritmos gen&eacute;ticos</strong>, que son de la familia de los <strong>algoritmos evolutivos</strong>. Adem&aacute;s veremos su aplicaci&oacute;n pr&aacute;ctica en el <strong>vectorizado de im&aacute;genes</strong> con un programa que he realizado en <a href="https://blog.adrianistan.eu/rust-101-tutorial-rust-espanol/">Rust</a>.</p>
<h2>&iquest;Qu&eacute; es un algoritmo gen&eacute;tico?</h2>
<p>Un <a href="https://es.wikipedia.org/wiki/Algoritmo_gen%C3%A9tico">algoritmo gen&eacute;tico</a> es un algoritmo inspirado en el mecanismo de la naturaleza de la evoluci&oacute;n biol&oacute;gica, descrita por Darwin all&aacute; por 1859 en el libro <em>El Origen de las Especies</em>. La idea es tener un conjunto de individuos (posibles soluciones). Estos individuos son evaluados para ver qu&eacute; tan buenos son. Quedarnos con los mejores y proceder a la creaci&oacute;n de nuevos individuos como resultados de la recombinaci&oacute;n gen&eacute;tica de dos individuos (como en la reproducci&oacute;n sexual). Posteriormente a&ntilde;adir alguna mutaci&oacute;n gen&eacute;tica para explorar nuevas posibilidades ligeramente diferentes. Proceder de nuevo a la selecci&oacute;n natural hasta que tengamos individuos lo suficientemente buenos para nosotros.</p>
<p>Normalmente no son los algoritmos m&aacute;s eficientes ni tienen por qu&eacute; dar un resultado &oacute;ptimo, pero pueden servir para dar aproximaciones bastante buenas al resultado &oacute;ptimo. Existen estrategias para mejorar los algoritmos gen&eacute;ticos como la gesti&oacute;n de la antig&uuml;edad de los individuos, para evitar m&aacute;ximos locales; la conservaci&oacute;n de individuos con mal desempe&ntilde;o, para conservar mayor variedad gen&eacute;tica; ...</p>
<p>Como veremos m&aacute;s adelante, uno de los elementos m&aacute;s importante de estos algoritmos es la funci&oacute;n de evaluaci&oacute;n, que muchas veces es m&aacute;s complicada de programar de lo que parece.</p>
<figure class="wp-block-image"><img class="wp-image-1675" src="https://files.adrianistan.eu/AlgGenetico.png" alt="" /></figure>
<h2>Un caso pr&aacute;ctico: vectorizado de im&aacute;genes</h2>
<p>Para ver como estos conceptos te&oacute;ricos funcionan en la pr&aacute;ctica, vamos a hacer un programa que vectorice im&aacute;genes. &iquest;Esto qu&eacute; es? Digamos que hay dos tipos de im&aacute;genes en inform&aacute;tica. Por un lado tenemos las im&aacute;genes que almacenan los colores en p&iacute;xeles (<strong>rasterizadas</strong>) y por otro lado aquellas que almacenan la imagen como f&oacute;rmulas matem&aacute;ticas, que se aplican cuando se quiere ver la imagen (<strong>vectoriales</strong>). Los formatos m&aacute;s comunes de im&aacute;genes rasterizadas son JPEG, PNG, GIF y WEBP. Los formatos m&aacute;s comunes de im&aacute;genes vectoriales son SVG y AI.</p>
<figure class="wp-block-image"><img class="wp-image-1682" src="https://files.adrianistan.eu/Rasterizadas.png" alt="" />
<figcaption>A las im&aacute;genes rasterizadas no se les puede hacer zoom hasta el infinito, se ven borrosas</figcaption>
</figure>
<figure class="wp-block-image"><img class="wp-image-1683" src="https://files.adrianistan.eu/Vectorizadas.png" alt="" />
<figcaption>A las im&aacute;genes vectoriales se les puede hacer zoom infinito, no pierden calidad</figcaption>
</figure>
<p>Pasar de una imagen vectorial a una rasterizada es trivial, pero el proceso inverso no lo es, y es justo donde vamos a aplicar el algoritmo gen&eacute;tico.</p>
<p>En nuestro caso, vamos a tomar siluetas, negras sobre fondo blanco y tratar de vectorizarlas con<strong> curvas de B&eacute;zier</strong>.</p>
<p>&nbsp;</p>
<figure class="wp-block-image"><img class="wp-image-1676" src="https://files.adrianistan.eu/MendelVectorizer.png" alt="" />
<figcaption>Ejemplo de ejecuci&oacute;n en la versi&oacute;n final de Mendel Vectorizer. La curva azul es la imagen vectorial generada.</figcaption>
</figure>
<h2>Curvas de B&eacute;zier</h2>
<p>En los a&ntilde;os 60, <a href="https://es.wikipedia.org/wiki/Pierre_B%C3%A9zier">Pierre B&eacute;zier</a>, que trabajaba para Renault, dise&ntilde;&oacute; un tipo de curva para el dise&ntilde;o asistido por ordenador (CAD). Estas son las conocidas como curvas de B&eacute;zier. Nuestro algoritmo tratar&aacute; de encontrar la curva de B&eacute;zier m&aacute;s similar a la curva de la imagen rasterizada. Para ello necesitamos un punto de inicio, un punto final y dos puntos de control.</p>
<figure class="wp-block-image"><img class="wp-image-1681" src="https://files.adrianistan.eu/RealCubicBezier.gif" alt="" />
<figcaption>Curva c&uacute;bica de B&eacute;zier</figcaption>
</figure>
<p>En nuestro algoritmo, las curvas ser&aacute;n los individuos, y las coordenadas de los puntos de control ser&aacute;n los genes (habr&aacute; 4 genes por tanto).</p>
<p>Este es el c&oacute;digo que he usado para definir las curvas de B&eacute;zier en Rust.</p>
<pre class="wp-block-code"><code class="language-rust">#[derive(Copy, Clone)]
pub struct Point {
    pub x: f64,
    pub y: f64,
}
impl Point {
    pub fn distance(&amp;self, other: &amp;Point) -&gt; f64 {
        ((self.x - other.x).powf(2.0) + (self.y - other.y).powf(2.0)).sqrt()
    }
    pub fn middle(&amp;self, other: &amp;Point) -&gt; Point {
        Point {
            x: (self.x + other.x) / 2.0,
            y: (self.y + other.y) / 2.0,
        }
    }
}
#[derive(Clone)]
pub struct Bezier {
    pub start: Point,
    pub control1: Point,
    pub control2: Point,
    pub end: Point,
}
impl&lt;'a&gt; Bezier {
    pub fn iter(&amp;self) -&gt; BezierIter {
        BezierIter {
            bezier: self,
            position: 0.0,
        }
    }
}
pub struct BezierIter&lt;'a&gt; {
    bezier: &amp;'a Bezier,
    position: f64,
}
impl&lt;'a&gt; Iterator for BezierIter&lt;'a&gt; {
    type Item = Point;
    fn next(&amp;mut self) -&gt; Option {
        if self.position &gt; 1.0 {
            return None;
        }
        let x = self.bezier.start.x * (1.0 - self.position).powf(3.0)
            + 3.0 * self.bezier.control1.x * self.position * (1.0 - self.position).powf(2.0)
            + 3.0 * self.bezier.control2.x * self.position.powf(2.0) * (1.0 - self.position)
            + self.bezier.end.x * self.position.powf(3.0);
        let y = self.bezier.start.y * (1.0 - self.position).powf(3.0)
            + 3.0 * self.bezier.control1.y * self.position * (1.0 - self.position).powf(2.0)
            + 3.0 * self.bezier.control2.y * self.position.powf(2.0) * (1.0 - self.position)
            + self.bezier.end.y * self.position.powf(3.0);
        self.position += 0.01;
        Some(Point { x, y })
    }}
</code></pre>
<h2>Encontrar puntos iniciales</h2>
<p>El primer paso de nuestro algoritmo es buscar los puntos iniciales (y finales) de las curvas. En definitiva esto es un problema de b&uacute;squeda de esquinas.</p>
<figure class="wp-block-image"><img class="wp-image-1677" src="https://files.adrianistan.eu/fast9.png" alt="" />
<figcaption>Ejemplo de aplicaci&oacute;n de FAST9 a una imagen</figcaption>
</figure>
<p>Existen varios algoritmos de b&uacute;squeda de esquinas: Harris, FAST9, FAST12, ... No obstante, no tuve muy buenos resultados en las im&aacute;genes con las que trabajaba. As&iacute; que esta parte del algoritmo se la dejo al humano. El humano se encargar&aacute; de poner los puntos, en orden, que tiene que tratar de unir el algoritmo con curvas de B&eacute;zier.</p>
<h2>Funci&oacute;n de evaluaci&oacute;n</h2>
<p>Muchas veces la funci&oacute;n de evaluaci&oacute;n es el punto m&aacute;s delicado de estos algoritmos. En este caso la idea fundamental es identificar si la curva de B&eacute;zier est&aacute; encima de la curva original. Para ello tomamos 100 puntos equidistantes de la curva de B&eacute;zier (con la ecuaci&oacute;n param&eacute;trica de la curva es muy sencillo).</p>
<p>[latex]\mathbf{B}(t)=\mathbf{P}_0(1-t)^3+3\mathbf{P}_1t(1-t)^2+3\mathbf{P}_2t^2(1-t)+\mathbf{P}_3t^3 \mbox{ , } t \in [0,1].[/latex]</p>
<p>Estos puntos se comparan con la imagen real, si ah&iacute; en la imagen original hab&iacute;a una l&iacute;nea no pasa nada, si no, se resta 100. De este modo se penaliza gravemente salirse de la curva. Esto se hace as&iacute; ya que la otra opci&oacute;n evidente (bonificar el estar sobre en la l&iacute;nea) podr&iacute;a dar lugar a resultados inesperados.</p>
<figure class="wp-block-image"><img class="wp-image-1678" src="https://files.adrianistan.eu/Evaluacion.png" alt="" />
<figcaption>Ejemplificaci&oacute;n de la funci&oacute;n de evaluaci&oacute;n. Los puntos amarillos est&aacute;n dentro de la l&iacute;nea. Los puntos verdes est&aacute;n fuera de la l&iacute;nea, penalizando a la curva en su "adaptaci&oacute;n al medio".</figcaption>
</figure>
<p>Pongamos como ejemplo una funci&oacute;n de evaluaci&oacute;n que bonifique por estar sobre la l&iacute;nea y no penalice por salirse de esta. Una l&iacute;nea bien adaptada a estas condiciones podr&iacute;a recorrerse toda la imagen, cruzando todas las l&iacute;neas posibles, generando un garabato totalmente in&uacute;til pero muy bueno seg&uacute;n esta funci&oacute;n. Por ello, nuestra funci&oacute;n de evaluaci&oacute;n se basa en penalizar las salidas de la l&iacute;nea.</p>
<p>La funci&oacute;n de evaluaci&oacute;n presentada no es perfecta, ya que puede pasar de largo el punto final y dar la vuelta. Esta curva es m&aacute;s larga que la &oacute;ptima, pero al estar completamente dentro de la l&iacute;nea negra original, la funci&oacute;n de evaluaci&oacute;n no la puede clasificar como peor que otras alternativas. Para solventar este problema una idea es que la longitud de la curva tenga una implicaci&oacute;n en la funci&oacute;n. No obstante, el c&aacute;lculo de la longitud de una curva de Bezier es demasiado complejo y no lo he codificado. Tambi&eacute;n podr&iacute;a aproximarse a trav&eacute;s de segmentos rectos entre los 100 puntos calculados anteriormente.</p>
<figure class="wp-block-image"><img class="wp-image-1679" src="https://files.adrianistan.eu/NoDeseable.png" alt="" />
<figcaption>Ejemplo de curva con puntuaci&oacute;n m&aacute;xima pero no &oacute;ptima desde el punto de vista humano</figcaption>
</figure>
<pre class="wp-block-code"><code class="language-rust">pub fn evaluate(image: &amp;GrayImage, line: &amp;Bezier) -&gt; f64 {
    let mut eval = 0.0;
    for point in line.iter() {
        let x = point.x as u32;
        let y = point.y as u32;
        if image.in_bounds(x, y) {
            let pixel = image.get_pixel(x, y);
            if pixel.data[0] &lt; 200 {
                eval += 1.0;
            } else {
                eval -= 100.0;
            }
        } else {
            eval -= 100.0;
        }
    }
    eval
}</code></pre>
<h2>Selecci&oacute;n natural</h2>
<p>La funci&oacute;n de selecci&oacute;n natural deja &uacute;nicamente las 500 mejores curvas, de acuerdo a la funci&oacute;n de evaluaci&oacute;n, es decir, las mejor adaptadas de momento. Para la ordenaci&oacute;n, Rust usa un algoritmo denominado Timsort, con coste O(nlogn). Sin embargo, en todo el algoritmo trabajamos con poblciones finitas, por lo que puede asimilarse a una constante, con coste O(1).&nbsp;</p>
<pre class="wp-block-code"><code class="language-rust">pub fn natural_selection(image: &amp;GrayImage, mut population: Vec) -&gt; Vec {
    population.sort_by(|a, b| {
        let a = evaluate(&amp;image, &amp;a);
        let b = evaluate(&amp;image, &amp;b);
        b.partial_cmp(&amp;a).unwrap()
    });

    population.into_iter().take(GOOD_ONES).collect()
}</code></pre>
<h2>Poblaci&oacute;n inicial</h2>
<p>La poblaci&oacute;n inicial se forma con 1000 curvas generadas con par&aacute;metros totalmente aleatorios. Los valores de cada coordenada, eso s&iacute;, est&aacute;n comprendidos entre -d y d siendo d la distancia en l&iacute;nea recta entre los puntos de inicio y final.</p>
<pre class="wp-block-code"><code class="language-rust">let mut population = Vec::new();
        let mut rng = thread_rng();
        let distancia = start.distance(&amp;end);
        for _ in 0..1000 {
            let xrand: f64 = rng.gen_range(-distancia, distancia);
            let yrand: f64 = rng.gen_range(-distancia, distancia);
            let mut control1 = start.middle(&amp;end);
            control1.x += xrand;
            control1.y += yrand;
            let mut control2 = start.middle(&amp;end);
            control2.x += xrand;
            control2.y += yrand;
            population.push(Bezier {
                start,
                end,
                control1,
                control2,
            });
        }</code></pre>
<h2>Recombinado</h2>
<p>El proceso de recombinaci&oacute;n permite mezclar los mejores espec&iacute;menes tratando de conseguir uno mejor. Este algoritmo gen&eacute;tico es de tipo RCGA (<a href="https://engineering.purdue.edu/~sudhoff/ee630/Lecture04.pdf">Real Coded Genetic Algorithm</a>) ya que los genes son n&uacute;meros reales, en contraposici&oacute;n a los t&iacute;picos genes binarios.Para estos algoritmos existen distintas variantes, aqu&iacute; se usa el sistema de blend. El sistema de blend implica que de entre los dos padres se toman los valores m&iacute;nimos y m&aacute;ximos para cada coordenada. Posteriormente se elige un nuevo valor de forma aleatoria con la condici&oacute;n de que est&eacute; dentro del rango de m&iacute;nimo y m&aacute;ximo definido anteriormente.</p>
<pre class="wp-block-code"><code class="language-rust">            // CROSSOVER
            // Blend o Linear (Blend) https://engineering.purdue.edu/~sudhoff/ee630/Lecture04.pdf
            let mut i: usize = 0;
            let mut babies = Vec::new();
            while i &lt; GOOD_ONES {
                let line1 = &amp;population[i];
                let line2 = &amp;population[i + 1];

                let min_x = line1.control1.x.min(line2.control1.x);
                let max_x = line1.control1.x.max(line2.control1.x);
                let min_y = line1.control1.y.min(line2.control1.y);
                let max_y = line1.control1.y.max(line2.control1.y);
                let control1 = Point {
                    x: rng.gen_range(min_x, max_x),
                    y: rng.gen_range(min_y, max_y),
                };

                let min_x = line1.control2.x.min(line2.control2.x);
                let max_x = line1.control2.x.max(line2.control2.x);
                let min_y = line1.control2.y.min(line2.control2.y);
                let max_y = line1.control2.y.max(line2.control2.y);
                let control2 = Point {
                    x: rng.gen_range(min_x, max_x),
                    y: rng.gen_range(min_y, max_y),
                };

                babies.push(Bezier {
                    start,
                    end,
                    control1,
                    control2,
                });

                i += 2;
            }
            population.append(&amp;mut babies);</code></pre>
<h2>Mutaci&oacute;n</h2>
<p>La fase de mutaci&oacute;n permite generar peque&ntilde;as variaciones aleatorias respecto a la poblaci&oacute;n. Afecta al 10% de la poblaci&oacute;n aunque solo afecta a una coordenada a la vez.</p>
<p>Al ser un algoritmo RCGA, no vale simplemente con cambiar el valor de un bit. El enfoque utilizado en este algoritmo es el de una distribuci&oacute;n normal de cambios de media 0. La distribuci&oacute;n tiene la forma N(0,d/2). Esto implica que en la mayor&iacute;a de las ocasiones la variaci&oacute;n (tanto positiva como negativa) en la coordenada ser&aacute; bastante peque&ntilde;a.</p>
<figure class="wp-block-image"><img class="wp-image-1195" src="https://files.adrianistan.eu/Normal.png" alt="" />
<figcaption>Distribuci&oacute;n normal, aunque esta no es de media 0</figcaption>
</figure>
<pre class="wp-block-code"><code class="language-rust"> population = population
                .into_iter()
                .map(|mut line| {
                    if rng.gen::() &lt; 0.10 {
                        let normal = Normal::new(0.0, distancia / 2.0);
                        let mutation_where: u32 = rng.gen_range(1, 5);
                        // Solo muta un gen, respecto a una Normal
                        match mutation_where {
                            1 =&gt; line.control1.x += rng.sample(normal),
                            2 =&gt; line.control1.y += rng.sample(normal),
                            3 =&gt; line.control2.x += rng.sample(normal),
                            4 =&gt; line.control2.y += rng.sample(normal),
                            _ =&gt; (),
                        }
                    }
                    line
                })
                .collect();</code></pre>
<h2>El programa: Mendel Vectorizer</h2>
<p>El programa definitivo, <strong><a href="https://github.com/aarroyoc/mendel-vectorizer">Mendel Vectorizer</a></strong>, disponible en GitHub, tiene m&aacute;s detalles. La interfaz gr&aacute;fica est&aacute; hecha usando <a href="https://gtk-rs.org/">GTK</a>, la interfaz de l&iacute;nea de comandos usa <a href="https://crates.io/crates/clap">Clap</a> y el algoritmo se ejecuta en paralelo usando paso de mensajes entre los hilos. El programa genera un fichero SVG resultado que puede ser abierto en programas como <a href="https://inkscape.org/es/">Inkscape</a> o Adobe Illustrator.</p>
<figure class="wp-block-image"><img class="wp-image-1684" src="https://files.adrianistan.eu/InkscapeGA-1024x555.png" alt="" />
<figcaption>El fichero generado por Mendel Vectorizer en Inkscape</figcaption>
</figure>
<p>Para usar Mendel Vectorizer en tu ordenador, sigue los siguientes pasos.</p>
<p>Desc&aacute;rgate una copia del c&oacute;digo con Git. Compila con Rust (una edici&oacute;n que soporte la edici&oacute;n 2018, m&iacute;nimo la 1.31) y ejec&uacute;talo.</p>
<pre class="wp-block-code"><code>git clone https://github.com/aarroyoc/mendel-vectorizercd mendel-vectorizercargo buildcargo run</code></pre>
<p>Tambi&eacute;n pod&eacute;is descargaros la memoria en PDF del programa.</p>
<div class="wp-block-file"><a href="https://files.adrianistan.eu/memoria.pdf">Descargar memoria de Mendel Vectorizer</a><a class="wp-block-file__button" href="https://files.adrianistan.eu/memoria.pdf" download="">Descargar</a></div>]]></description>
                <comments>https://blog.adrianistan.eu/algoritmos-geneticos-un-caso-practico</comments>
                <pubDate>Tue, 18 Dec 2018 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Advent of Code 2018: segunda semana</title>
                <link>https://blog.adrianistan.eu/advent-of-code-2018-segunda-semana</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/advent-of-code-2018-segunda-semana</guid>
                <description><![CDATA[<p>Segunda semana del Advent of Code. En esta semana ya hemos tenido algunos problemas muy interesantes. Intentar&eacute; comentarlos de la mejor manera posible. <a href="https://github.com/aarroyoc/advent-of-code-2018">Todo el c&oacute;digo est&aacute; aqu&iacute;</a> y en<a href="https://blog.adrianistan.eu/2018/12/07/advent-of-code-2018-primera-semana/"> este otro post coment&eacute; mis soluciones de la primera semana</a>.</p>
<h2>D&iacute;a 8</h2>
<p>El d&iacute;a 8 se nos propone un problema de grafos tambi&eacute;n. B&aacute;sicamente se nos define un &aacute;rbol, donde cada nodo puede tener hijos y metadatos. En la primera parte nos piden sumar todos los metadatos.</p>
<p>Aqu&iacute; al contrario que en el d&iacute;a 7, no voy a usar networkx. Era m&aacute;s dif&iacute;cil adaptar networkx al problema que hacer el &aacute;rbol a mano. Uno de los puntos complicados de este problema es el parseo de la entrada, que hice de forma recursiva. Cada nodo es un diccionario con las propiedades tama&ntilde;o, una lista de metadatos y una lista de nodos hijo.</p>
<p>En la segunda parte se define el concepto de <strong>valor de nodo</strong> y como calcularlo. Tambi&eacute;n es bastante sencillo de implementar. Finalmente hacemos un recorrido por el &aacute;rbol de tipo primero en anchura (BFS) con una <strong>deque</strong> de Python.</p>
<pre class="wp-block-code"><code class="language-python">from collections import deque
def read_node(start,numbers):
    length = 2
    child_nodes = numbers[start]
    metadata_entries = numbers[start+1]
    children = list()
    while child_nodes &gt; 0:
        child_node = read_node(start+length,numbers)
        children.append(child_node)
        length += child_node["length"]
        child_nodes -= 1
    metadata = list()
    while metadata_entries &gt; 0:
        metadata.append(numbers[start+length])
        length += 1
        metadata_entries -= 1
    node = dict([("length",length),("metadata",metadata),("children",children)])
    return node

def read_file(file):
    with open(file) as f:
        line = f.readline()
    numbers = [int(x) for x in line.split()]
    G = read_node(0,numbers)
    return G
def node_value(N):
    if len(N["children"]) == 0:
        return sum(N["metadata"])
    else:
        s = 0
        for i in N["metadata"]:
            if i-1 &lt; len(N["children"]):
                s += node_value(N["children"][i-1])
        return s
        
def day8(file):
    G = read_file(file)
    to_visit = deque()
    to_visit.append(G)
    metadata_sum = 0
    while len(to_visit) &gt; 0:
        N = to_visit.popleft()
        metadata_sum += sum(N["metadata"])
        to_visit.extend(N["children"])
    print("METADATA SUM: %d" % metadata_sum)
    print("NODE VALUE: %d" % node_value(G))</code></pre>
<h2>D&iacute;a 9</h2>
<p>Este d&iacute;a fue muy interesante. Se nos explica un juego, que consiste en ir a&ntilde;adiendo canicas en una circunferencia y cuando el n&uacute;mero de canica que a&ntilde;adimos es m&uacute;ltiplo de 23, obtenemos puntos y quitamos una canica 7 puestos por detr&aacute;s.</p>
<p>Aqu&iacute; tuve una mala decisi&oacute;n de dise&ntilde;o ya que al principio quise hacer esto con una lista de Python (el equivalente a vector en otros lenguajes de programaci&oacute;n). La idea era sencilla y funcionaba hasta que lleg&oacute; la parte 2. La parte 2 te pedr&iacute;a calcular los puntos teniendo en cuenta 100 veces m&aacute;s canicas. Esto fue un grave problema para mi c&oacute;digo. Calculo que tardar&iacute;a 6 horas en calcularlo, pero antes optimic&eacute;. La optimizaci&oacute;n consist&iacute;a en usar una lista circular doblemente enlazada. &iquest;Esto qu&eacute; es? Se trata de una <a href="https://es.wikipedia.org/wiki/Lista_enlazada">lista enlazada</a>, doblemente, porque cada nodo tiene referencia al elemento siguiente y al anterior. Y es circular porque ambos extremos est&aacute;n unidos. Esto permite las inserciones y borrados en O(1). Adem&aacute;s los movimientos relativos (en este problema todos son as&iacute;) son extremadamente sencillos. La implementaci&oacute;n de esta estructura de datos en Python es muy sencilla (en otros lenguajes es m&aacute;s complicado). No me molest&eacute; en hacer funciones que me hiciesen sencilla la vida y las conexiones/desconexiones las hago a mano directamente en el c&oacute;digo del problema.</p>
<pre class="wp-block-code"><code class="language-python">from collections import defaultdict
class Marble:
    def __init__(self,value,left=None,right=None):
        self.value = value
        self.left = left
        self.right = right
        if self.left == None:
            self.left = self
        if self.right == None:
            self.right = self

def day9(PLAYERS,LAST_MARBLE):
    SCORE = defaultdict(lambda: 0)
    player = 0
    marble = 0
    current_marble_pos = 0
    current_marble = None
    while marble &lt;= LAST_MARBLE:
        if marble &gt; 0 and marble % 23 == 0:
            SCORE[player] += marble
            pivote = current_marble.left.left.left.left.left.left.left
            SCORE[player] += pivote.value
            pivote.left.right = pivote.right
            pivote.right.left = pivote.left
            current_marble = pivote.right
        else:
            if current_marble == None:
                current_marble = Marble(marble)
            else:
                current_marble = Marble(marble,current_marble.right,current_marble.right.right)
                current_marble.left.right = current_marble
                current_marble.right.left = current_marble
        player += 1
        player = player % PLAYERS
        marble += 1
    return max(SCORE.values())</code></pre>
<p>Curiosamente, en la propia librer&iacute;a de Python <strong>deque</strong> tiene una operaci&oacute;n llamada <strong>rotate</strong> que permite hacer este problema en poqu&iacute;simas l&iacute;neas y de forma muy eficiente. Pero desconoc&iacute;a la existencia de esa funci&oacute;n (que lo que hace es mover la "cabeza" de la lista enlazada que es deque).</p>
<h2>D&iacute;a 10</h2>
<p>Este problema es muy interesante. Se nos da una serie de puntos que van movi&eacute;ndose por la pantalla. En un determinado momento estos puntos se juntan y forman un mensaje en pantalla.</p>
<p>Aqu&iacute; lo interesante no es mover los puntos, eso es trivial, simplemente es sumar la velocidad cada vez las coordenadas. Lo interesante es saber cuando parar. Existen varias ideas:</p>
<ul>
<li>Revisi&oacute;n humana de cada iteraci&oacute;n</li>
<li>Comprobar que no haya puntos separados del resto (con grafos)</li>
<li>Comprobar que el &aacute;rea de concentraci&oacute;n de puntos es m&iacute;nima</li>
</ul>
<p>Y alguna m&aacute;s. Para el ejemplo la primera idea serv&iacute;a. Pero en la prueba real, era m&aacute;s complicado. A m&iacute; se me ocurri&oacute; la tercera opci&oacute;n, la cu&aacute;l es bastante eficiente. En cada iteraci&oacute;n calculamos el &aacute;rea que contiene a todos los puntos, cuando ese &aacute;rea ya no se reduce m&aacute;s, hemos llegado al mensaje.</p>
<pre class="wp-block-code"><code class="language-python">import re
def read_file(file):
    stars = list()
    p = re.compile("position=&lt;([ -][0-9]+), ([ -][0-9]+)&gt; velocity=&lt;([ -][0-9]+), ([ -][0-9]+)&gt;")
    with open(file) as f:
        lines = f.readlines()
    for line in lines:
        m = p.match(line.strip())
        try:
            pos_x = int(m.group(1))
        except:
            print(line)
        pos_y = int(m.group(2))
        vel_x = int(m.group(3))
        vel_y = int(m.group(4))
        stars.append([pos_x,pos_y,vel_x,vel_y])
    return stars
def print_stars(stars):
    stars = sorted(stars,key=lambda x: x[0],reverse=True)
    min_width = stars[-1][0]
    max_width = stars[0][0]
    min_height = min(stars,key=lambda x: x[1])[1]
    max_height = max(stars,key=lambda x: x[1])[1]
    s = str()
    for j in range(min_height,max_height+1):
        p = [star for star in stars if star[1] == j]
        for i in range(min_width,max_width+1):
            if len(p) == 0:
                s += "."
            else:
                if any(map(lambda star: star[0] == i and star[1] == j,p)):
                    s += "#"
                else:
                    s += "." 
       s += "\n"
    return s
def step(stars):
    a = map(lambda x: [x[0]+x[2],x[1]+x[3],x[2],x[3]],stars)
    return list(a)
# LA RESPUESTA CORRECTA TIENE AREA MINIMA

def area(stars):
    stars = sorted(stars,key=lambda x: x[0], reverse=True)
    min_width = stars[-1][0]
    max_width = stars[0][0]
    min_height = min(stars,key=lambda x: x[1])[1]
    max_height = max(stars,key=lambda x: x[1])[1]
    area = (max_width-min_width)*(max_height-min_height)
    return area
def day10(file):
    stars = read_file(file)
    a = area(stars)
    steps = 0
    while area(step(stars)) &lt; a:
        stars = step(stars)
        steps += 1
        a = area(stars)
    print_stars(stars)
    print(steps)</code></pre>
<p>La parte de dibujado me cost&oacute; y ah&iacute; tuve un fallo que me cost&oacute; media hora aproximadamente en resolver. Una mejor opci&oacute;n, pero que no se me ocurri&oacute;, hubiese sido usar <a href="https://python-pillow.org/">Pillow</a> y crear una imagen. Es mucho m&aacute;s f&aacute;cil que dibujar sobre una terminal (y posiblemente m&aacute;s r&aacute;pido).</p>
<figure class="wp-block-image"><img class="wp-image-1658" src="https://files.adrianistan.eu/Day10AOC.png" alt="" /></figure>
<h2>D&iacute;a 11</h2>
<p>Para este problema hay 3 posibles algoritmos. En la primera parte nos piden que de una matriz extraigamos el cuadrado de 3x3 con mayor valor. La matriz hay que construirla pero es trivial. Yo decido usar un diccionario, con clave la tupla de coordenadas. Vamos recorriendo todas las posiciones y calculamos el valor. Ahora para buscar el cuadrado, simplemente vamos probando todos los posibles cuadrados.</p>
<p>En la segunda parte nos dicen que bsuquemos el cuadrado m&aacute;ximo pero el tama&ntilde;o puede ser cualquiera. Aqu&iacute; con la fuerza bruta ya tarda demasiado. Mi soluci&oacute;n fue usar <a href="https://blog.adrianistan.eu/2018/03/11/programacion-dinamica-el-problema-de-knapsack/">programaci&oacute;n din&aacute;mica</a>, para ello la clave pasa a tener un valor m&aacute;s, el tama&ntilde;o del cuadrado. Cuando creamos la tabla estamos asignando valor al cuadrado 1x1 de posici&oacute;n X,Y. Representado es la tupla (x,y,1). Seg&uacute;n vamos avanzando hasta 300x300 vamos guardando los resultados intermedios, de modo que podamos reutilizarlos. Por ejemplo, el valor de (x,y,4) solamente es la suma de (x,y,2), (x+2,y,2), (x,y+2,2) y (x+2,y+2,2). Evidentemente esto solo funciona en los tama&ntilde;os pares. En los tama&ntilde;os impares decid&iacute; coger el cuadrado de dimensi&oacute;n inmediatamente menor y calcular los laterales con los cuadrados de tama&ntilde;o 1. Este sistema funciona mucho mejor que la fuerza bruta pero es lento. <a href="https://www.youtube.com/watch?v=TJuYCBAwtEU">Los profesionales</a> usaron el algoritmo <a href="https://en.wikipedia.org/wiki/Summed-area_table">Summed Area Table</a> (del que desconoc&iacute;a su existencia). Este algoritmo es el &oacute;ptimo para este problema.</p>
<p>&nbsp;</p>
<pre class="wp-block-code"><code class="language-python">def generate_fuel(x,y,idg):
    fuel = (((x+10)*y)+idg)*(x+10)
    fuel %= 1000 
    fuel = (fuel // 100) - 5
    return fuel
def generate_table(idg):
    fuel = {(x,y,size):0 for x in range(1,301) for y in range(1,301) for size in range(1,301)}
    for x in range(1,301):
        for y in range(1,301):
            fuel[(x,y,1)] = generate_fuel(x,y,idg)
    return fuel
def find_best(fuel):
    max_point = [-1,-1]
    max_score = -1
    for x in range(1,301):
        for y in range(1,301):
            if x+3 &gt; 301 or y+3 &gt; 301:
                continue
            score = fuel[(x,y,1)]+fuel[(x+1,y,1)]+fuel[(x+2,y,1)]+fuel[(x,y+1,1)]+fuel[(x+1,y+1,1)]+fuel[(x+2,y+1,1)]+fuel[(x,y+2,1)]+fuel[(x+1,y+2,1)]+fuel[(x+2,y+2,1)]
            if score &gt; max_score:
                max_score = score
                max_point = [x,y]
    return max_point[0],max_point[1]
def find_best_any_size(fuel):
    max_score = -1
    max_point = [-1,-1,-1]
    for size in range(2,300+1):
        for x in range(1,301):
            for y in range(1,301):
                if x+size &gt; 301 or y+size &gt; 301:
                    continue
                if size % 2 == 0:
                    mid = size // 2
                    fuel[(x,y,size)] = fuel[(x+mid,y,mid)]+fuel[(x,y+mid,mid)]+fuel[(x+mid,y+mid,mid)]+fuel[(x,y,mid)]
                else:
                    fuel[(x,y,size)] = fuel[(x,y,size-1)]
                    for i in range(x,x+size-1):
                        fuel[(x,y,size)] += fuel[(i,y+size-1,1)]
                    for j in range(y,y+size-1):
                        fuel[(x,y,size)] += fuel[(x+size-1,j,1)]
                    fuel[(x,y,size)] += fuel[(x+size-1,y+size-1,1)]
                score = fuel[(x,y,size)]
                if score &gt; max_score:
                    max_score = score
                    max_point = [x,y,size]
    return max_point[0],max_point[1],max_point[2]
def day11():
    fuel = generate_table(1133)
    x,y = find_best(fuel)
    print("BEST POINT: %d,%d" % (x,y))
    x,y,size = find_best_any_size(fuel)
    print("BEST POINT ANY SIZE: %d,%d,%d" % (x,y,size))
if __name__ == "__main__":
    day11()</code></pre>
<h2>D&iacute;a 12</h2>
<p>El d&iacute;a 12 me trajo recuerdos de un algoritmo con el que me pele&eacute; mucho, el denominado <a href="https://blog.adrianistan.eu/2018/01/20/juego-la-vida-conway-c-interfaz-grafica/">HashLife</a>. El problema es un <a href="https://blog.adrianistan.eu/2017/08/28/automatas-celulares-unidimensionales-python/">aut&oacute;mata celular unidimensional</a>. Las reglas vienen dadas como patrones. La &uacute;nica diferencia es que hay que guardar su posici&oacute;n para luego calcular un n&uacute;mero. La primera parte es bastante sencilla.</p>
<pre class="wp-block-code"><code class="language-python">import re
from collections import defaultdict

def read_file(file):
    rules = defaultdict(lambda: ".")
    rule_prog = re.compile("([.#]+) =&gt; ([.#])")
    with open(file) as f:
        lines = f.readlines()
    state = lines[0].split(": ")[1].strip()
    for line in lines[2:]:
        m = rule_prog.match(line.strip())
        rules[m.group(1)] = m.group(2)
    return state,rules

def parse_state(pots):
    state = dict()
    for i,p in enumerate(pots):
        state[i] = p
    return state

def find(rules,current):
    if current in rules:
        return rules[current]
    else:
        size = len(current)
        mid = size // 2
        left = find(rules,current[0:mid])
        right = find(rules,current[mid:])
        rules[current] = left + right
        return rules[current]


def iter(state,rules):
    new_state = dict()
    xmin = min(state.keys())
    xmax = max(state.keys())
    for x in range(xmin-2,xmax+3):
        current = ("%c%c%c%c%c" % (
                    state.get(x-2,"."),
                    state.get(x-1,"."),
                    state.get(x,"."),
                    state.get(x+1,"."),
                    state.get(x+2,".")
                    ))
        new = rules[current]
        if new == "#" or xmin &lt;= x &lt;= xmax:
            new_state[x] = new
    return new_state

def sum_pots(state):
    n = 0
    for pot in state:
        if state[pot] == "#":
            n += pot
    return n

def print_state(state):
    xmin = min(state.keys())
    xmax = max(state.keys())
    s = str("XMIN %d : " % xmin)
    for x in range(xmin-2,xmax+3):
        s += state.get(x,".")
    print(s)


def day12(file):
    state,rules = read_file(file)
    state = parse_state(state)
    for i in range(20):
        #print_state(state)
        state = iter(state,rules)
    #print_state(state)
    n = sum_pots(state)
    return n

if __name__ == "__main__":
    day12("input.txt")
</code></pre>
<figure class="wp-block-image"><img class="wp-image-1659" src="https://files.adrianistan.eu/Day12AOC-1024x392.png" alt="" /></figure>
<p>La segunda parte nos ped&iacute;a lo mismo pero para el n&uacute;mero &iexcl;50000000000! Inmediatamente pens&eacute; en optimizarlo de forma similar a HashLife. La idea consiste en almacenar patrones mayores a los de las reglas (que son todos de tama&ntilde;o 5), para poder evitar c&aacute;lculos innecesarios.Adem&aacute;s a&ntilde;ad&iacute; un recolector de basura para ir eliminando por la izquierda las celdas in&uacute;tiles.</p>
<p>No obstante, y aunque es much&iacute;simo m&aacute;s eficiente, sigue sin ser capaz de procesar tal bestialidad de n&uacute;mero en un tiempo razonable.</p>
<p>Y he aqu&iacute; lo que me ha cabreado, porque no he podido sacarlo. A partir de cierto momento, el dibujo siempre es el mismo pero desplaz&aacute;ndose a la derecha. De modo que el valor del siguiente paso siempre es la suma de una constante. Finalmente modifiqu&eacute; el c&oacute;digo para que buscase una situaci&oacute;n en la que el n&uacute;mero fuese resultado de una suma de una constante. Una vez hecho eso, calcula con una multiplicaci&oacute;n lo que valdr&iacute;a cuando llegase a 50000000000.</p>
<pre class="wp-block-code"><code class="language-python">import re
from collections import defaultdict

XMIN = -2

def find(rules,current):
    if len(current) &lt; 5:
        return ""
    if current in rules:
        return rules[current]
    elif len(current) == 5:
        return "."
    else:
        size = len(current)
        left=find(rules,current[0:size-1])
        right=find(rules,current[size-5:])
        rules[current] = left+right
        return rules[current]

def read_file(file):
    rules = defaultdict(lambda: ".")
    rule_prog = re.compile("([.#]+) =&gt; ([.#])")
    with open(file) as f:
        lines = f.readlines()
    state = lines[0].split(": ")[1].strip()
    for line in lines[2:]:
        m = rule_prog.match(line.strip())
        rules[m.group(1)] = m.group(2)
    return state,rules


def print_state(state):
    print(state)

def sum_pots(state):
    n = 0
    for i,c in enumerate(state):
        if c == "#":
            n += i + XMIN
    return n

def day12(file):
    global XMIN
    state,rules = read_file(file)
    XMAX = len(state)+1
    state = "..%s.." % state
    sums = list()
    i = 0
    while len(sums) &lt; 3 or sums[-1]-sums[-2] != sums[-2]-sums[-3]:
        state = find(rules,"..%s.." % state)
        if state[0] == "." and state[1] == "." and state[2] == "." and state[3] == ".":
            state = state[2:]
            XMIN += 2
        if state[0] == "#" or state[1] == "#":
            state = "..%s" % state
            XMIN -= 2
        if state[-1] == "#" or state[-2] == "#":
            state = "%s.." % state
        sums.append(sum_pots(state))
        i += 1
    diff = sums[-1]-sums[-2]
    missing = 50000000000 - i
    n = missing*diff + sums[-1]

    return n
</code></pre>
<p>Y con esto pude finalmente calcular el resultado.</p>
<h2>D&iacute;a 13</h2>
<p>El d&iacute;a 13 ten&iacute;amos unas v&iacute;as de tren. En estas v&iacute;as hab&iacute;a unos trenecitos que se desplazaban siguiendo unas normas. El objetivo en la primera parte era conocer el donde se produc&iacute;a el primer choque.</p>
<pre class="wp-block-code"><code class="language-python">from PIL import Image

XMAX = 0
YMAX = 0
STEP = 0

nextDirection = dict()
nextDirection["start"] = "left"
nextDirection["left"] = "center"
nextDirection["center"] = "right"
nextDirection["right"] = "left"

def relative_direction(original,movement):
    print("CROSS")
    if movement == "center":
        return original
    if original == "v":
        if movement == "left":
            return "&gt;"
        else:
            return "&lt;"
    elif original == "&gt;":
        if movement == "left":
            return "^"
        else:
            return "v"
    elif original == "^":
        if movement == "left":
            return "&lt;"
        else:
            return "&gt;"
    else:
        if movement == "left":
            return "v"
        else:
            return "^"

def day13(file):
    global XMAX
    global YMAX
    global STEP
    plano = dict()
    carts = list()
    with open(file) as f:
        lines = f.readlines()
    YMAX = len(lines)
    XMAX = len(lines[0])
    for y,line in enumerate(lines):
        for x,char in enumerate(line):
            # SI HAY UN CARRITO, DEDUCIR TIPO DE VIA
            if char == "^" or char == "v" or char == "&lt;" or char == "&gt;":
                if (x,y-1) in plano:
                    plano[(x,y)] = "|"
                else:
                    plano[(x,y)] = "-"
                carts.append([x,y,char,"left"])
            else:
                plano[(x,y)] = char
    
    end = False
    while not end:
        carts.sort(key=lambda x: x[1])
        carts.sort(key=lambda x: x[0])

        for cart in carts:
            # CHECK CRASH
            for crt in carts:
                if cart[0] == crt[0] and cart[1] == crt[1] and id(cart) != id(crt):
                    print("CRASH AT %d-%d" % (cart[0],cart[1]))
                    end = True
            try:
                x = cart[0]
                y = cart[1]
                print(cart)
                if cart[2] == "&gt;":
                    if plano[(x+1,y)] == "/":
                        cart[2] = "^"
                    elif plano[(x+1,y)] == "\\":
                        cart[2] = "v"
                    elif plano[(x+1,y)] == "+":
                        cart[2] = relative_direction(cart[2],cart[3])
                        cart[3] = nextDirection[cart[3]]
                    cart[0] += 1
                elif cart[2] == "&lt;":
                    if plano[(x-1,y)] == "/":
                        cart[2] = "v"
                    elif plano[(x-1,y)] == "\\":
                        cart[2] = "^"
                    elif plano[(x-1,y)] == "+":
                        cart[2] = relative_direction(cart[2],cart[3])
                        cart[3] = nextDirection[cart[3]]
                    cart[0] -= 1
                elif cart[2] == "^":
                    if plano[(x,y-1)] == "/":
                        cart[2] = "&gt;"
                    elif plano[(x,y-1)] == "\\":
                        cart[2] = "&lt;"
                    elif plano[(x,y-1)] == "+":
                        cart[2] = relative_direction(cart[2],cart[3])
                        cart[3] = nextDirection[cart[3]]
                    cart[1] -= 1
                elif cart[2] == "v":
                    print()
                    if plano[(x,y+1)] == "/":
                        cart[2] = "&lt;"
                    elif plano[(x,y+1)] == "\\":
                        cart[2] = "&gt;"
                    elif plano[(x,y+1)] == "+":
                        cart[2] = relative_direction(cart[2],cart[3])
                        cart[3] = nextDirection[cart[3]]
                    cart[1] += 1
            except:
                breakpoint()
        STEP += 1
        print_train(plano,carts)

def print_train(plano,carts):
    im = Image.new("RGB",(XMAX,YMAX))
    for x,y in plano:
        if plano[(x,y)] != " ":
            im.putpixel((x,y),(255,255,255))
        if plano[(x,y)] == "+":
            im.putpixel((x,y),(120,120,120))
    for cart in carts:
        if cart[2] == "&gt;":
            im.putpixel((cart[0],cart[1]),(255,0,0))
        elif cart[2] == "&lt;":
            im.putpixel((cart[0],cart[1]),(0,255,0))
        elif cart[2] == "^":
            im.putpixel((cart[0],cart[1]),(0,0,255))
        else:
            im.putpixel((cart[0],cart[1]),(0,120,120))

    im.save("train-%d.png" % STEP,"PNG")

if __name__ == "__main__":]]></description>
                <comments>https://blog.adrianistan.eu/advent-of-code-2018-segunda-semana</comments>
                <pubDate>Sun, 16 Dec 2018 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Las mejores librerías gratuitas para gráficas en PHP</title>
                <link>https://blog.adrianistan.eu/las-mejores-librerias-gratuitas-para-graficas-en-php</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/las-mejores-librerias-gratuitas-para-graficas-en-php</guid>
                <description><![CDATA[<!-- wp:paragraph --><p><em>"Los datos son el nuevo petróleo"</em> es algo que dicen muchas personas y que parece confirmarse si se leen <a href="https://www.noticiastecnologicas.com">noticias tecnológicas</a>. Y en efecto, actualmente podemos generar una cantidad de datos inmensa de los que podemos extraer una información muy valiosa. Y ahí esta la clave, en "extraer". Los datos como tal no sirven para nada. Al igual que el petróleo, no lo queremos en bruto, sino refinado, tratado. Con los datos pasa lo mismo. Para representar esta información desde hace muchos años estadística ha ido diseñando métodos para representar datos y extraer información de ellos.</p><!-- /wp:paragraph --><!-- wp:paragraph --><p>Antiguamente estas gráficas se realizaban a mano y su creación era muy costosa. Hoy en día, como seguro que sabrás si sigues las <a href="https://www.noticiastecnologicas.com">noticias tecnológicas actuales</a>, podemos usar herramientas para generar estas gráficas de forma sencilla, directamente en un servidor. En este artículo vamos a ver las mejores librerías para generar gráficas en PHP.</p><!-- /wp:paragraph --><!-- wp:heading --><h2>phpChart</h2><!-- /wp:heading --><!-- wp:paragraph --><p>La primera librería de la lista es <a href="https://phpchart.com/">phpChart</a>. Se trata de una librería de pago, pero con muchas opciones para las gráficas. Desafortunadamente, solo las versiones más caras soportan gráficos que no sean de barras, de líneas y de sectores. La librería funciona generando código JavaScript para el cliente que le permite dibujar la gráfica usando Canvas. Esta gráfica es interactiva, pudiendo el usuario hacer zoom, cambiar de modo de gráfico y permitiendonos hacer animaciones. </p><!-- /wp:paragraph --><!-- wp:image {"id":1664} --><figure class="wp-block-image"><img src="https://files.adrianistan.eu/phpChart-1024x453.png" alt="" class="wp-image-1664"/></figure><!-- /wp:image --><!-- wp:heading --><h2>pChart</h2><!-- /wp:heading --><!-- wp:paragraph --><p>Otra opción es generar imágenes para las gráficas en el servidor. Esto es precisamente lo que hace<a href="http://www.pchart.net/"> pChart.</a> Originalmente era un mero wrapper sobre la librería estándar de gráficos de PHP (GD) para añadir anti-alising, pero con el tiempo se enfocó a las gráficas estadísticas. Sus principales cualidades son una gran calidad en las visualizaciones y un rendimiento óptimo. pChart es gratuita si nuestra aplicación es software libre y de pago si no lo es. Requiere las extensiones GD y FreeType para funcionar correctamente. pChart soporta gran cantidad de gráficas y es usado por empresas como Intel o Airbus e instituciones como la NASA y el CERN. La librería tiene capacidades interactivas, aunque algo más reducidas que otras soluciones.</p><!-- /wp:paragraph --><!-- wp:image {"id":1663} --><figure class="wp-block-image"><img src="https://files.adrianistan.eu/pChart-1024x486.png" alt="" class="wp-image-1663"/></figure><!-- /wp:image --><!-- wp:heading --><h2>JpGraph</h2><!-- /wp:heading --><!-- wp:paragraph --><p>Otra librería que permite renderizar en el servidor es <a href="https://jpgraph.net/">JpGraph</a>. Esta librería, gratuita para usos no-comerciales, está diseñada con orientación a objetos. Tiene una gran variedad de gráficas disponibles, de buena calidad y como ellos promocionan: ligeros. Existe una versión PRO con todavía más tipos de gráficos, aunque son bastante específicos y normalmente no harán falta. La librería necesita que GD esté instalado y es compatible con PHP7.</p><!-- /wp:paragraph --><!-- wp:image {"id":1665} --><figure class="wp-block-image"><img src="https://files.adrianistan.eu/jpGraph.png" alt="" class="wp-image-1665"/></figure><!-- /wp:image --><!-- wp:heading --><h2>D3.js</h2><!-- /wp:heading --><!-- wp:paragraph --><p>Para acabar, una de mis personalmente favoritas, <a href="https://d3js.org/">D3.js</a>. D3 no es una librería para generar gráficas y tampoco es PHP. Se trata de una librería JavaScript con utilidades para los documentos <em>basados en datos</em>, la idea es incluir esta librería y usarla donde quieras mostrar las gráficas. La ventaja de D3 es que te permite hacer lo que quieras y tiene una gran comunidad. Es open-source, y aunque es algo más difícil de usar, genera unos resultados excelentes, usando SVG de por medio. De hecho, D3 no generará código visual, sino que nos ayudará a hacerlo nosotros mismos. He ahí la dificultad y a su vez, su gran flexibilidad. </p><!-- /wp:paragraph --><!-- wp:image {"id":1666} --><figure class="wp-block-image"><img src="https://files.adrianistan.eu/D3-1024x482.png" alt="" class="wp-image-1666"/></figure><!-- /wp:image -->]]></description>
                <comments>https://blog.adrianistan.eu/las-mejores-librerias-gratuitas-para-graficas-en-php</comments>
                <pubDate>Sat, 15 Dec 2018 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Advent of Code 2018: primera semana</title>
                <link>https://blog.adrianistan.eu/advent-of-code-2018-primera-semana</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/advent-of-code-2018-primera-semana</guid>
                <description><![CDATA[Este año, como ya viene siendo habitual, tiene lugar la competición de programación <a href="https://adventofcode.com">Advent of Code</a>. El objetivo es resolver un problema de programación al día, hasta el día de navidad, a modo de un particular calendario de adviento. Este año, como ya no viene siendo tan habitual, me he propuesto hacerlos todos y explicar mis soluciones. Esta semana todos han sido resueltos en Python.<br><br><a href="https://files.adrianistan.eu/AdventCode2018.png"><img class="aligncenter size-full wp-image-1643" src="https://files.adrianistan.eu/AdventCode2018.png" alt="" width="741" height="239" /></a><br><br>Los programas que he usado, así como los enunciados y los datos de entrada que me han tocado (a cada uno le tocan datos diferentes) están en <a href="https://github.com/aarroyoc/advent-of-code-2018">este repositorio de GitHub</a>.<br><h2>Día 1</h2><br>El primer reto que nos proponen es muy sencillo. Se nos pasa un archivo con sumas y restas y tenemos que calcular el resultado final. Pongamos un ejemplo trivial:<br><br><code>+1, +1, -2</code> = 0<br><br>El procesdimiento es sencillo, leer cada operación, extraer el signo, convertir a número y aplicar la operación detectada por el signo.<br><pre class="lang:python decode:true ">def apply_freq(freq):<br>    with open("input.txt") as f:<br>        lines = f.readlines()<br>    for line in lines:<br>        signo = line[0:1]<br>        numero = int(line[1:])<br>        if signo == "+":<br>            freq += numero<br>        elif signo == "-":<br>            freq -= numero<br>    return freq<br><br>if __name__ == "__main__":<br>    freq = 0<br>    freq = apply_freq(freq)<br>    print("FREQ FINAL: %d" % freq)</pre><br>La segunda parte es más interesante, ya que nos dice que tenemos que aplicar la misma cadena de operaciones de forma indefinida hasta encontrar una resultado repetido.<br><br>Aquí la clave es conocer el funcionamiento de un <a href="https://docs.python.org/3/tutorial/datastructures.html"><strong>Set</strong></a> o conjunto. Se trata de una estructura de datos que no permite elementos repetidos. La idea está en ir almacenando los resultados que van saliendo hasta encontrar uno que ya esté dentro. Al encontrarlo salir e indicarlo.<br><pre class="lang:python decode:true ">FREQS = set()<br><br>def apply_freq(freq):<br>    with open("input.txt") as f:<br>        lines = f.readlines()<br>    for line in lines:<br>        signo = line[0:1]<br>        numero = int(line[1:])<br>        if signo == "+":<br>            freq += numero<br>        elif signo == "-":<br>            freq -= numero<br>        if freq in FREQS:<br>            return (True,freq)<br>        else:<br>            FREQS.add(freq)<br>    return (False,freq)<br><br>if __name__ == "__main__":<br>    freq = 0<br>    while True:<br>        end,freq = apply_freq(freq)<br>        if end:<br>            break<br>    print("FREQ FINAL: %d" % freq)</pre><br>Aquí viene una anécdota interesante. Estoy haciendo estos retos con unos amigos en un grupo de Telegram y al poco de yo haberlo terminado empezaron a preguntar cuánto tardaba en ejecutarse la segunda parte. Al parecer les iba muy lento, yo sorprendido les dije que había sido instantáneo y que estaba usando Python. Ellos se preguntaban como podía ser, ya que lo habían hecho en C y les estaba tardando varios minutos.<br><br><a href="https://files.adrianistan.eu/Example_of_a_set.svg_.png"><img class="size-full wp-image-1644" src="https://files.adrianistan.eu/Example_of_a_set.svg_.png" alt="" width="562" height="480" /></a> Una representación de un set o conjunto. No puede haber elementos repetidos. No existe orden definido<br><br>La respuesta tiene que ver con el <strong>set</strong>. Yo sinceramente fui a él directamente pero otros compañeros no, y usaron listas y arrays. La búsqueda que realizaban para saber si el elemento estaba dentro era lineal. Comparada con la búsqueda en un set implementado con tabla hash que es constante, el rendimiento es muy inferior. He aquí un ejemplo de como lo más importante de cara a la eficiencia es el algoritmo y las estructuras de datos que usamos para resolver un problema.<br><h2>Día 2</h2><br>El segundo día se nos propone otro problema de dificultad similar. Sobre una lista de palabras tenemos que contar cuantas palabras tienen 2 letras repetidas y 3 letras repetidas, para finalmente multiplicar ambos números.<br><br>En este caso fui bastante pragmático y opté por usar la clase <a href="https://docs.python.org/3/library/collections.html#collections.Counter">Counter</a> de Python. Counter cuenta cuantas veces aparece una letra y lo deja en un diccionario. Podemos ignorar las claves, ya que lo único que tenemos que hacer es buscar en los valores si está el 3 y el 2, y si es así, sumar uno a nuestro contador.<br><pre class="lang:python decode:true">from collections import Counter<br><br>twice = 0<br>triple = 0<br><br>with open("input.txt") as f:<br>    lines = f.readlines()<br>for line in lines:<br>    c = Counter(line)<br>    if 3 in c.values():<br>        triple += 1<br>    if 2 in c.values():<br>        twice += 1<br>total = twice*triple<br>print("CHECKSUM: %d" % total)</pre><br>La segunda parte ya es más interesante. Se nos pide que la misma lista de palabras, encontremos las dos palabras que solo se diferencian en una letra (se tiene en cuenta el orden). Aquí el algoritmo es un poco más lento ya que para cada palabra la comparamos con el resto de palabras, hasta encontrar aquella que efectivamente tiene un cambio de únicamente una palabra.<br><br>Para ello hace falta una función que nos diga cuantas palabras hay de diferencia entre dos palabras, que es bastante sencillita.<br><pre class="lang:python decode:true ">def changes(base,word):<br>    changes = 0<br>    for i,letter in enumerate(base.strip()):<br>        if not letter == word[i]:<br>            changes += 1<br>    return changes<br><br>def find_similar(lines):<br>    for base in lines:<br>        for word in lines:<br>            if changes(base,word) == 1:<br>                return (base,word)<br><br>with open("input.txt") as f:<br>    lines = f.readlines()<br>base,word = find_similar(lines)<br>final = str()<br>for i,letter in enumerate(base.strip()):<br>    if letter == word[i]:<br>        final += letter<br>print("FINAL %s"%final)</pre><br>Una vez tengamos las dos palabras que solo se diferencian en una letra, hacemos un bucle similar al usado para encontrarlas, solo que esta vez para formar la palabra con todas las letras que tienen en común, ignorando la diferente.<br><h2>Día 3</h2><br>En el día 3 nos proponen un problema muy interesante. Tenemos una lista de parcelas rectangulares, definidas por la posición de su esquina superior-izquierda y su ancho y alto, todo en metros. Para la primera parte tenemos que encontrar cuantos metros cuadrados del universo ficticio están en dos o más parcelas a la vez.<br><br>Lo primero que hay que hacer es leer la entrada, que esta vez ya no es tan trivial. <a href="https://docs.python.org/3/howto/regex.html">Un simple RegEx</a> nos permite obtener toda la información de forma sencilla.<br><pre class="lang:python decode:true ">from dataclasses import dataclass<br>import re<br><br>@dataclass<br>class Claim():<br>    id: int<br>    x: int<br>    y: int<br>    width: int<br>    height: int<br><br>def read_file():<br>    claims = []<br>    with open("input.txt") as f:<br>        lines = f.readlines()<br>    prog = re.compile(r"#([0-9]+) @ ([0-9]+),([0-9]+): ([0-9]+)x([0-9]+)");<br>    for line in lines:<br>        result = prog.match(line.strip())<br>        claim = Claim(<br>            id=int(result.group(1)),<br>            x=int(result.group(2)),<br>            y=int(result.group(3)),<br>            width=int(result.group(4)),<br>            height=int(result.group(5))<br>        )<br>        claims.append(claim)<br>    return claims<br></pre><br>Para el algoritmo en sí, vamos a usar <strong>defaultdict</strong>. La idea es tener una t<a href="https://es.wikipedia.org/wiki/Tabla_hash">abla hash</a>, cuya clave sean las coordenadas del mundo y su valor, el número de parcelas que estan sobre ella. Usamos defaultdict para que por defecto cualquier coordenada que no existiese con anterioridad tenga valor 0.<br><br>Así pues vamos a recorrer todas las parcelas y para cada parcela vamos a visitar todos los metros cuadrados que contiene en la tabla hash, sumando 1 para indicar que ese metro cuadrado pertenece a una parcela (más).<br><pre class="lang:python decode:true ">if __name__ == "__main__":<br>    claims = read_file()<br>    area = defaultdict(lambda: 0)<br>    for claim in claims:<br>        for i in range(claim.x,claim.x+claim.width):<br>            for j in range(claim.y,claim.y+claim.height):<br>                area[(i,j)] += 1<br>    overlaps = count_overlaps(area)<br>    print("Overlaps: %d" % overlaps)</pre><br>La segunda parte nos indica que solo existe una parcela que no esté invadida por otra parcela. Hay que buscar cuál es e informar del ID que tiene. Para esto he simplemente vuelto a recorrer cada parcela comprobando si tienen algún metro cuadrado con uso por más de 1 parcela. Si para una parcela no se encuentran metros compartidos, automáticamente se devuelve su ID (ya que solo hay una parcela de estas características).<br><br>El código completo es el siguiente:<br><pre class="lang:python decode:true ">from dataclasses import dataclass<br>from collections import defaultdict<br>import re<br><br>@dataclass<br>class Claim():<br>    id: int<br>    x: int<br>    y: int<br>    width: int<br>    height: int<br><br>def read_file():<br>    claims = []<br>    with open("input.txt") as f:<br>        lines = f.readlines()<br>    prog = re.compile(r"#([0-9]+) @ ([0-9]+),([0-9]+): ([0-9]+)x([0-9]+)");<br>    for line in lines:<br>        result = prog.match(line.strip())<br>        claim = Claim(<br>            id=int(result.group(1)),<br>            x=int(result.group(2)),<br>            y=int(result.group(3)),<br>            width=int(result.group(4)),<br>            height=int(result.group(5))<br>        )<br>        claims.append(claim)<br>    return claims<br><br>def count_overlaps(area):<br>    overlaps = 0<br>    for overlap in area.values():<br>        if overlap &gt; 1:<br>            overlaps += 1<br>    return overlaps<br><br>def find_nonoverlaping(claims,area):<br>    for claim in claims:<br>        overlaps = False<br>        for i in range(claim.x,claim.x+claim.width):<br>            for j in range(claim.y,claim.y+claim.height):<br>                if area[(i,j)] &gt; 1:<br>                    overlaps = True<br>        if not overlaps:<br>            return claim.id <br><br>if __name__ == "__main__":<br>    claims = read_file()<br>    area = defaultdict(lambda: 0)<br>    for claim in claims:<br>        for i in range(claim.x,claim.x+claim.width):<br>            for j in range(claim.y,claim.y+claim.height):<br>                area[(i,j)] += 1<br>    overlaps = count_overlaps(area)<br>    print("Overlaps: %d" % overlaps)<br>    non_overlaping = find_nonoverlaping(claims,area)<br>    print("ID: %d" % non_overlaping)</pre><br>En esta segunda parte, tengo la intuición de que existe una manera más eficiente de hacerlo, pero todavía no he encontrado esa solución más eficiente.<br><h2>Día 4</h2><br>El problema del día 4, aunque aparentemente distinto al anterior, tiene muchas cosas en común y mi forma de resolverlo fue bastante parecida.<br><br>Se nos pide que sobre un registro de todas las noches identifiquemos el guardia que duerme más horas, y posteriormente su minuto preferido para quedarse dormido.<br><br>En primer lugar, la entrada de texto ya es bastante más compleja, pero con unos simples regex se puede analizar rápidamente, al menos para extraer la información que necesitamos. Aquí conviene prestar atención a que los registros de dormirse y despertarse ya que todos ocurren a la misma hora, luego lo único importante son los minutos. Otro detalle respecto a la entrada es que nos indican que está desordenada, sin embargo el formato de representación de la fecha que nos dan (parecido al ISO), se ordena cronológicamente simplemente con una ordenación alfabética.<br><br>La idea es muy similar a la del algoritmo anterior, primero tenemos una tabla hash con todos los guardias. Allí almacenamos otra tabla hash con los minutos de la hora y ponemos un cero si nunca se han dormido en ese minuto. Si usamos <strong>defaultdict</strong>, como en el código anterior, el código se simplifica bastante. En definitiva estamos usando dos tablas hash en vez de una y aplicar la misma idea de sumar 1, salvo que esta vez con el tiempo en vez del espacio (aunque Einstein vino a decir que eran cosas muy parecidas).<br><pre class="lang:python decode:true ">import re<br>from collections import defaultdict<br><br>def read_file():<br>    with open("input.txt") as f:<br>        lines = f.readlines()<br>    lines.sort()<br>    return lines<br><br><br>if __name__ == "__main__":<br>    lines = read_file()<br>    guard_prog = re.compile(r"[* ]+Guard #([0-9]+)")<br>    time_prog = re.compile(r"\[([0-9]+)-([0-9]+)-([0-9]+) ([0-9]+):([0-9]+)")<br>    current_guard = 0<br>    start_time = 0<br>    end_time = 0<br>    timetable = defaultdict(lambda: defaultdict(lambda: 0))<br>    for line in lines:<br>        # Hay tres tipos de líneas<br>        # Guardia, Sleep, Wake<br>        a = guard_prog.match(line.split("]")[1])<br>        if a != None:<br>            current_guard = a.group(1)<br>        elif "falls" in line:<br>            t = time_prog.match(line.split("]")[0])<br>            start_time = int(t.group(5))<br>        elif "wakes" in line:<br>            t = time_prog.match(line.split("]")[0])<br>            end_time = int(t.group(5))<br>            for i in range(start_time,end_time):<br>                timetable[current_guard][i] += 1<br><br>    # Calcular horas dormido<br>    max_guard = ""<br>    max_guard_sleeptime = 0<br>    for guard in timetable:<br>        s = sum(timetable[guard].values())<br>        if s &gt; max_guard_sleeptime:<br>            max_guard_sleeptime = s<br>            max_guard = guard<br><br>    print("El guardia que más duerme es el %s con %d minutos" % (max_guard,max_guard_sleeptime))<br><br>    #Calcular minuto ideal<br>    max_minute = 0<br>    max_minute_times = 0<br>    for minute in timetable[max_guard]:<br>        if timetable[max_guard][minute] &gt; max_minute_times:<br>            max_minute = minute<br>            max_minute_times = timetable[max_guard][minute]<br><br>    print("El guardia duerme más en el minuto %d (%d veces)" % (max_minute,max_minute_times))<br><br>    print("CHECKSUM %d" % (max_minute*int(max_guard)))<br></pre><br>Posteriormente se recorren estas tablas hash para calcular lo pedido.<br><br>La segunda parte nos pide algo similar pero no idéntico, el guardia que se ha quedado dormido más veces en el mismo minuto (e indicar que minuto es).<br><br>La estructura de datos es exactamente la misma y solamente añadimos otro bucle para que busque este otro dato:<br><pre class="lang:python decode:true ">import re<br>from collections import defaultdict<br><br>def read_file():<br>    with open("input.txt") as f:<br>        lines = f.readlines()<br>    lines.sort()<br>    return lines<br><br><br>if __name__ == "__main__":<br>    lines = read_file()<br>    guard_prog = re.compile(r"[* ]+Guard #([0-9]+)")<br>    time_prog = re.compile(r"\[([0-9]+)-([0-9]+)-([0-9]+) ([0-9]+):([0-9]+)")<br>    current_guard = 0<br>    start_time = 0<br>    end_time = 0<br>    timetable = defaultdict(lambda: defaultdict(lambda: 0))<br>    for line in lines:<br>        # Hay tres tipos de líneas<br>        # Guardia, Sleep, Wake<br>        a = guard_prog.match(line.split("]")[1])<br>        if a != None:<br>            current_guard = a.group(1)<br>        elif "falls" in line:<br>            t = time_prog.match(line.split("]")[0])<br>            start_time = int(t.group(5))<br>        elif "wakes" in line:<br>            t = time_prog.match(line.split("]")[0])<br>            end_time = int(t.group(5))<br>            for i in range(start_time,end_time):<br>                timetable[current_guard][i] += 1<br><br>    # Calcular horas dormido<br>    max_guard = ""<br>    max_guard_sleeptime = 0<br>    for guard in timetable:<br>        s = sum(timetable[guard].values())<br>        if s &gt; max_guard_sleeptime:<br>            max_guard_sleeptime = s<br>            max_guard = guard<br><br>    print("El guardia que más duerme es el %s con %d minutos" % (max_guard,max_guard_sleeptime))<br><br>    #Calcular minuto ideal<br>    max_minute = 0<br>    max_minute_times = 0<br>    for minute in timetable[max_guard]:<br>        if timetable[max_guard][minute] &gt; max_minute_times:<br>            max_minute = minute<br>            max_minute_times = timetable[max_guard][minute]<br><br>    print("El guardia duerme más en el minuto %d (%d veces)" % (max_minute,max_minute_times))<br><br>    print("CHECKSUM %d" % (max_minute*int(max_guard)))<br><br>    # El guardia que ha estado un minuto concreto mas veces dormido<br>    max_guard = ""<br>    guard_minute = 0<br>    guard_minutes = 0<br>    for guard in timetable:<br>        for minute in timetable[guard]:<br>            if timetable[guard][minute] &gt; guard_minutes:<br>                max_guard = guard<br>                guard_minute = minute<br>                guard_minutes = timetable[guard][minute]<br>    print("El guardia %s se ha dormido en el minuto %d (%d veces)" % (max_guard,guard_minute,guard_minutes))<br>    print("CHECKSUM %d" % (guard_minute*int(max_guard)))</pre><br>En este caso, la segunda parte apenas ha implicado modificaciones, siendo la estructura de datos subyacente intacta.<br><h2>Día 5</h2><br>Este día se nos propone un reto aparentemente sencillo, pero cuya resolución puede ser muy lenta o rápida dependiendo de como lo hagamos. He de decir, que mi solución era muy lenta, extremadamente y tuve que mirar como lo habían hecho otras personas para entender como se podía optimizar.<br><br>La primera tarea consiste en reducir unas cadenas de reactivos. La norma es que si hay una letra minúscula y una mayúscula al lado, se pueden quitar. Se nos pide la longitud de la cadena de reactivos después de reducirlo al máximo.<br><pre class="lang:python decode:true ">if __name__ == "__main__":<br><br>    with open("input.txt") as f:<br>        line = f.readline()<br>    <br>    line = list(line.strip())<br>    end = False<br>    while not end:<br>        end = True<br>        for i in range(1,len(line)):<br>            if line[i-1] != line[i] and line[i-1].lower() == line[i].lower():<br>                end = False<br>                del line[i-1]<br>                del line[i-1]<br>                break<br>    print("Units: %d" % (len(line)))</pre><br>La versión original consistía en ir realizando elimaciones sobre la propia lista. Todo ello en un bucle que para cuando en una iteración no se modifica la cadena. Esto es extremadamente ineficiente. Tomando el código de <a href="https://github.com/petertseng/adventofcode-rb-2018/blob/master/05_alchemical_reduction.rb">Peter Tseng</a>, existe una opción mejor. Se puede ir haciendo un string nuevo poco a poco comprobando si la nueva letra reacciona con la última del string nuevo. Esto tiene la ventaja de que solo hace falta una iteración para cubrir todas las reacciones. La versión mejorada es la siguiente:<br><pre class="lang:python decode:true">def react(line):<br>    new = list()<br>    for c in line.strip():<br>        if len(new) &gt; 0 and c != new[-1] and c.lower() == new[-1].lower():<br>            del new[-1]<br>        else:<br>            new += c<br>    return new<br><br>if __name__ == "__main__":<br><br>    with open("input.txt") as f:<br>        line = f.readline()<br>    line = react(line)<br>    print("Units: %d" % (len(line)))</pre><br>Para la segunda parte se nos pida que encontremos la letra, que si eliminamos del compuesto antes de empezar la reacción, genera la cadena más pequeña. Hay que probar con todas las letras, mención especial a <strong>string.ascii_lowercase</strong> que tiene un iterador con todas las letras minúsculas del alfabeto inglés. Y posteriormente, encontrar la que de resultado inferior. Como no nos pide la letra, solamente la longitud que tendría esa cadena, no tenemos que pasar información extra.<br><pre class="lang:python decode:true">import string<br><br>def react(line):<br>    new = list()<br>    for c in line:<br>        if len(new) &gt; 0 and c != new[-1] and c.lower() == new[-1].lower():<br>            del new[-1]<br>        else:<br>            new += c<br>    return new<br><br>def min_react(line,letter):<br>    line = [c for c in line if c.lower() != letter]<br>    return len(react(line))<br><br>if __name__ == "__main__":<br><br>    with open("input.txt") as f:<br>        line = f.readline()<br>    l = react(line)<br><br>    print("Units: %d" % (len(l)))<br>    <br>    m = min([min_react(line,char) for char in string.ascii_lowercase])<br>    print("Minimum length: %d" % (m))</pre><br><h2>Día 6</h2><br>Esto se empieza a complicar. El día 6 nos pide que sobre una cuadrícula encontremos qué punto de control está más cercano a esa posición. De los puntos de control que un número de cuadrículas cercanas finitas, encontrar cuántas cuadrículas tiene el punto de control con más cuadrículas asociadas. Para saber la distancia de una cuadrícula al punto de control se usa la<a href="https://es.wikipedia.org/wiki/Geometr%C3%ADa_del_taxista"> distancia de Manhattan</a>.<br><br>Lo primero es reconocer que puntos de control tienen áreas infinitas, para no tenerlos en cuenta.<br><br>Para ello, voy a calcular dos puntos extremos (esquina superior izquierda y esquina inferior derecha), dentro del rectángulo que forman estos puntos están contenidos todos los puntos de control. El objetivo es calcular las distancias de las cuadrículas justo por fuera de este rectángulo. Las cuadrículas que estén más lejos de eso no van a cambiar de punto de control, ya que el más cercano en el borde seguirá siendo el más cercano en el borde + N, ya que no hay más puntos de control fuera.<br><br>Posteriormente, empezamos a calcular las distancias de todos los puntos de la cuadrícula. Para almacenar los datos vuelvo a usar una tabla hash (defaultdict de Python), donde la clave es la coordenada X,Y y el valor es el punto de control más cercano a esa cuadrícula. Si dos puntos de control están a la misma distancia o no se ha calculado, se usa -1.<br><br>Cuando se ha calculado el punto de control más cercano, se revisa si ese punto estaba fuera del rectángulo que contiene a los puntos de control. Si está fuera, el punto de control pasa a un conjunto de puntos con infinitas cuadrículas cercanas.<br><br>Para el conteo de cuántas cuadrículas tiene un punto de control que sabemos que es finito, uso otra tabla hash, inicializada por defecto a 0, cuya clave es el identificador de punto de control y su valor, el número de cuadrículas. Después, de los valores almacenados se calcula el máximo.<br><pre class="lang:python decode:true ">from dataclasses import dataclass<br>from collections import defaultdict<br>import math<br><br>@dataclass<br>class Punto:<br>    x: int<br>    y: int<br>    owner: int<br><br>def distancia(p1,p2):<br>    return abs(p1.x-p2.x)+abs(p1.y-p2.y)<br><br>if __name__ == "__main__":<br>    with open("input.txt") as f:<br>        lines = f.readlines()<br>    puntosControl = list()<br>    xlist = list()<br>    ylist = list()<br>    for i,line in enumerate(lines):<br>        l = line.split(",")<br>        xlist.append(int(l[0]))<br>        ylist.append(int(l[1]))<br>        puntosControl.append(Punto(x=int(l[0]),y=int(l[1]),owner=i))<br>    esquinaSuperiorIzquierda = Punto(x=min(xlist),y=min(ylist),owner=-1)<br>    esquinaInferiorDerecha = Punto(x=max(xlist),y=max(ylist),owner=-1)<br><br>    # Los que están fuera del rango esquinaSuperiorIzquierdaxesquinaInferiorDerecha se excluyen automáticamente<br>    excluidos = set()<br>    world = defaultdict(lambda: -1)<br>    for i in range(esquinaSuperiorIzquierda.x-1,esquinaInferiorDerecha.x+2):<br>        for j in range(esquinaSuperiorIzquierda.y-1,esquinaInferiorDerecha.y+2):<br>            punto = Punto(x=i,y=j,owner=-1)<br>            distanciaMin = math.inf<br>            total = 0<br>            for p in puntosControl:<br>                if distancia(punto,p) == distanciaMin:<br>                    punto.owner = -1<br>                if distancia(punto,p) &lt; distanciaMin:<br>                    distanciaMin = distancia(punto,p)<br>                    punto.owner = p.owner<br>                <br>            if i == esquinaSuperiorIzquierda.x-1 or i == esquinaInferiorDerecha.x+1 or j == esquinaSuperiorIzquierda.y-1 or j == esquinaInferiorDerecha.y+1:<br>                excluidos.add(punto.owner)<br>            if punto.owner &gt; -1:<br>                world[(i,j)] = punto.owner<br>    conteo = defaultdict(lambda: 0)<br>    for p in world:<br>        if not world[p] in excluidos:<br>            conteo[world[p]] += 1<br>    print("Maximum finite area: %d" % max(conteo.values()))</pre><br>En la segunda parte nos dicen que hay una región de puntos cuya suma de distancias a todos los puntos de control es menor a 10000. ¿Cuántos puntos forman esta región? Aquí creo que el enunciado no fue demasiado claro, ya que en un principio pensé que podría haber varias áreas, o que podría haber puntos sueltos, no conectados a la región. Sin embargo eso no pasa. Yo diseñé un algoritmo que iba visitando las celdas adyacentes, pero en realidad no hacía falta, simplemente se puede contar cuantos puntos cumplen la condición. Y se puede hacer en el mismo bucle que la primera parte.<br><pre class="lang:python decode:true ">from dataclasses import dataclass<br>from collections import defaultdict<br>import math<br><br>@dataclass<br>class Punto:<br>    x: int<br>    y: int<br>    owner: int<br><br>def distancia(p1,p2):<br>    return abs(p1.x-p2.x)+abs(p1.y-p2.y)<br><br>if __name__ == "__main__":<br>    with open("input.txt") as f:<br>        lines = f.readlines()<br>    puntosControl = list()<br>    xlist = list()<br>    ylist = list()<br>    for i,line in enumerate(lines):<br>        l = line.split(",")<br>        xlist.append(int(l[0]))<br>        ylist.append(int(l[1]))<br>        puntosControl.append(Punto(x=int(l[0]),y=int(l[1]),owner=i))<br>    esquinaSuperiorIzquierda = Punto(x=min(xlist),y=min(ylist),owner=-1)<br>    esquinaInferiorDerecha = Punto(x=max(xlist),y=max(ylist),owner=-1)<br><br>    # Los que están fuera del rango esquinaSuperiorIzquierdaxesquinaInferiorDerecha se excluyen automáticamente<br>    excluidos = set()<br>    world = defaultdict(lambda: -1)<br>    world_total = 0<br>    for i in range(esquinaSuperiorIzquierda.x-1,esquinaInferiorDerecha.x+2):<br>        for j in range(esquinaSuperiorIzquierda.y-1,esquinaInferiorDerecha.y+2):<br>            punto = Punto(x=i,y=j,owner=-1)<br>            distanciaMin = math.inf<br>            total = 0<br>            for p in puntosControl:<br>                if distancia(punto,p) == distanciaMin:<br>                    punto.owner = -1<br>                if distancia(punto,p) &lt; distanciaMin:<br>                    distanciaMin = distancia(punto,p)<br>                    punto.owner = p.owner<br>                total += distancia(punto,p)<br>            if total &lt; 10000:<br>                world_total += 1<br>                <br>            if i == esquinaSuperiorIzquierda.x-1 or i == esquinaInferiorDerecha.x+1 or j == esquinaSuperiorIzquierda.y-1 or j == esquinaInferiorDerecha.y+1:<br>                excluidos.add(punto.owner)<br>            if punto.owner &gt; -1:<br>                world[(i,j)] = punto.owner<br>    conteo = defaultdict(lambda: 0)<br>    for p in world:<br>        if not world[p] in excluidos:<br>            conteo[world[p]] += 1<br>    print("Maximum finite area: %d" % max(conteo.values()))<br>    print("Region size: %d" % world_total)</pre><br><h2>Día 7</h2><br>El día 7 se nos propone un reto muy interesante. En primer lugar, tenemos una lista de tareas que hacer en orden. Cada tarea depende de que otras hayan finalizado. Se nos pide el orden en el que se deberán hacer. Para resolver esto vamos a usar una estructura de datos nueva, el grafo dirigido. No voy a implementarlo yo, sino que voy a usar la magnífica librería <a href="https://networkx.github.io/">networkx</a>.<br><br>La idea es construir un grafo dirigido, con las tareas. Un nodo dirigido de C a A significa que antes de hacer la tarea A, C tiene que estar completado. Por supuesto puede darse el caso de que antes de hacer A haya que hacer C y otras tareas.<br><br>Vamos a realizar una búsqueda primero en anchura (DFS en inglés). Para ello mantenemos una lista con las tareas completadas y otra lista con las tareas que podríamos empezar a hacer. Cuando completamos una tarea vemos si las tareas a las que llevan necesitan todavía más tareas por realizar o en cambio ya pueden ser añadidas a la lista de "listas para empezar". El enunciado nos indica que ante la situación de dos tareas listas para empezar, se usa el orden alfabético para determinar cuál va antes.<br><br>Hace falta además una lista de tareas iniciales, que pueden empezar sin esperar. Esto se hace con dos conjuntos según leemos el archivo. Se hace la diferencia entre ellos y esas tareas no tienen prerrequisitos.<br><pre class="lang:python decode:true">import networkx as nx<br>import re<br><br>def read_file():<br>    first = set()<br>    second = set()<br>    G = nx.DiGraph()<br>    prog = re.compile("Step ([A-Z]) must be finished before step ([A-Z]) can begin.")<br>    with open("input.txt") as f:<br>        lines = f.readlines()<br>    for line in lines:<br>        r = prog.match(line.strip())<br>        if not r.group(1) in G:<br>            G.add_node(r.group(1))<br>        if not r.group(2) in G:<br>            G.add_node(r.group(2))<br>        if not G.has_edge(r.group(1),r.group(2)):<br>            G.add_edge(r.group(1),r.group(2))<br>        first.add(r.group(1))<br>        second.add(r.group(2))<br>    return (G,first- second)<br><br>if __name__ == "__main__":<br>    G,starter = read_file()<br>    path = list()<br>    to_visit = sorted(starter,reverse=True)<br><br>    while len(to_visit) &gt; 0:<br>        node = to_visit.pop()<br>        path.append(node)<br>        neighbours = G[node]<br>        for n in neighbours:<br>            if not n in to_visit and not n in path:<br>                allCompleted = True<br>                for u,v in G.in_edges(nbunch=n):<br>                    if not u in path:<br>                        allCompleted = False<br>                if allCompleted:<br>                    to_visit.append(n)<br>        to_visit = sorted(to_visit,reverse=True)<br>    print("".join(path))</pre><br>La segunda parte también es muy interesante. Se nos indica que las tareas tienen una duración de N segundos, dependiendo del valor alfabético de la letra. Además, ahora existen 5 trabajadores que pueden ir haciendo tareas en paralelo. ¿En cuánto tiempo podemos acabar todas las tareas?<br><pre class="lang:python decode:true">import networkx as nx<br>import re<br><br>def read_file():<br>    first = set()<br>    second = set()<br>    G = nx.DiGraph()<br>    prog = re.compile("Step ([A-Z]) must be finished before step ([A-Z]) can begin.")<br>    with open("input.txt") as f:<br>        lines = f.readlines()<br>    for line in lines:<br>        r = prog.match(line.strip())<br>        if not r.group(1) in G:<br>            G.add_node(r.group(1))<br>        if not r.group(2) in G:<br>            G.add_node(r.group(2))<br>        if not G.has_edge(r.group(1),r.group(2)):<br>            G.add_edge(r.group(1),r.group(2))<br>        first.add(r.group(1))<br>        second.add(r.group(2))<br>    return (G,first- second)<br><br>def duration(step):<br>    return 60+ord(step)-64<br><br>if __name__ == "__main__":<br>    G,starter = read_file()<br>    path = list()<br>    to_visit = sorted(starter,reverse=True)<br><br>    while len(to_visit) &gt; 0:<br>        node = to_visit.pop()<br>        path.append(node)<br>        neighbours = G[node]<br>        for n in neighbours:<br>            if not n in to_visit and not n in path:<br>                allCompleted = True<br>                for u,v in G.in_edges(nbunch=n):<br>                    if not u in path:<br>                        allCompleted = False<br>                if allCompleted:<br>                    to_visit.append(n)<br>        to_visit = sorted(to_visit,reverse=True)<br>    print("".join(path))<br><br>    end_letter = path[-1]<br>    path = list()<br>    to_visit = sorted(starter,reverse=True)<br>    <br>    second = 0<br>    workers = list()<br>    # Trabajo Actual, segundo que termina<br>    workers.append(['.',0])<br>    workers.append(['.',0])<br>    workers.append(['.',0])<br>    workers.append(['.',0])<br>    workers.append(['.',0])<br>    def full_workers(workers):<br>        full = True<br>        for w in workers:<br>            if w[0] == ".":<br>                full = False<br>        return full<br>    end = False<br>    while not end:<br>        if len(to_visit) == 0 or full_workers(workers):<br>            second += 1<br>        for i in range(0,len(workers)):<br>            if workers[i][1] &lt;= second:<br>                if workers[i][0] != ".":<br>                    path.append(workers[i][0])<br>                    neighbours = G[workers[i][0]]<br>                    for n in neighbours:<br>                        if not n in to_visit and not n in path:<br>                            allCompleted = True<br>                            for u,v in G.in_edges(nbunch=n):<br>                                if not u in path:<br>                                    allCompleted = False<br>                            if allCompleted:<br>                                to_visit.append(n)<br>                    to_visit = sorted(to_visit,reverse=True)<br>                if workers[i][0] == end_letter:<br>                    print("Finish point")<br>                    print("Seconds: %d" % second)<br>                    end = True<br>                if len(to_visit) &gt; 0:<br>                    node = to_visit.pop()<br>                    workers[i][1] = second+duration(node)<br>                    workers[i][0] = node<br>                else:<br>                    workers[i][0] = "."</pre><br>Bien, partiendo del mismo grafo dirigido ahora vamos a hacer otro tipo de recorrido, también DFS, pero no vamos a añadir nuevos elementos a la lista de forma inmediata, sino cuando hayan sido acabados de procesar. Almacenamos los trabajadores como listas dentro de una lista de trabajadores. Cada trabajador guarda la tarea que estaba haciendo y el segundo en el que acabará. Defino una función para saber si los trabajadores están todos ocupados.<br><br>Lo primero a tener en cuenta es que el tiempo no avanza hasta que la lista de tareas que se puede realizar está vacía o los trabajadores están llenos. Luego en cada iteración del bucle, analizamos a los trabajadores. Si no han acabado, no se hace nada. Si ya han acabado y estaban con una tarea, se añade la tarea a la lista de tareas finalizadas, y se analiza si se han desbloqueado nuevas tareas disponibles para realizar. Si la tarea que ha realizado es la última tarea, se acaba el programa.<br><br>Por último si hay una tareas disponible para hacer, se la añadimos al trabajador y si no, pues le indicamos que no haga nada (así no añadimos por duplicado la tarea en el futuro).<br><br><a href="https://files.adrianistan.eu/Day7.png"><img class="size-full wp-image-1645" src="https://files.adrianistan.eu/Day7.png" alt="" width="663" height="565" /></a> Salida de debug que saqué en el día 7<br><h2>Conclusión</h2><br>Esta ha sido la primera semana del Advent of Code 2018. Como vemos, el nivel de los problemas ha ido aumentado de forma progresiva. La próxima semana comentaré las soluciones correspondientes. <a href="https://github.com/aarroyoc/advent-of-code-2018">Tenéis todo el código hasta ahora aquí.</a><br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/advent-of-code-2018-primera-semana</comments>
                <pubDate>Fri, 07 Dec 2018 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Conclusiones de la visita de Richard Stallman a Valladolid</title>
                <link>https://blog.adrianistan.eu/conclusiones-de-la-visita-de-richard-stallman-a-valladolid</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/conclusiones-de-la-visita-de-richard-stallman-a-valladolid</guid>
                <description><![CDATA[Richard Stallman, el padre del software libre, vino a visitarnos a la ciudad de <a href="https://es.wikipedia.org/wiki/Valladolid">Valladolid</a>. La oportunidad de conocer a tal personaje en primera persona era única, así que no dudé en asistir, con la suerte que tuve de poder estar en primera fila durante la conferencia.<br><br>La conferencia no contaba nada nuevo, nada que no supiese cualquier persona que haya leído un poco sobre la idea del software libre, pero se hizo amena. Stallman explica muy bien y las diapositivas están muy bien hechas. Tiene bastantes chistes precocinados pero que causan buen impacto en la audiencia.<br><br><a href="https://files.adrianistan.eu/IMG_20181204_200118.jpg"><img class="aligncenter size-large wp-image-1637" src="https://files.adrianistan.eu/IMG_20181204_200118-576x1024.jpg" alt="" width="576" height="1024" /></a>Pero Stallman es un personaje. Hablando con la gente que cenó con él el día anterior, algunos me contaban que había sido bastante irrespetuoso, sacando el portátil durante la cena para hacer sus cosas y gritar de vez en cuando que no escuchaba bien.<br><br>Durante la charla ha estado bebiendo de su té, descalzo y hasta el momento de empezar ha seguido mandando correos. Le noté bastante envejecido, caminaba medio cojo y se le notaba la marca de la operación en el brazo. Para empezar a puesto su famosa versión de <a href="https://stallman.org/guantanamero.ogg">Guantanamera</a>.<br><br>La presentación ha seguido explicando las <strong>cuatro libertades</strong> del software libre, de forma bastante extensa, explicando para todos los públicos por qué son importantes.<br><br>Durante la charla también ha hablado del <strong>software malévolo</strong>, no confundir con privativo. Según él, son dos cosas distintas, pero relacionado. Puede haber software honesto privativo y software malévolo libre, pero son minorías en la práctica. También ha hablado del <strong>software privado</strong>, que es perfectamente ético, y que hasta él programa software privado. La diferencia es que el software privado nunca se distribuye fuera del propio autor u organización. Bastante parte de la charla se ha centrado en esta parte, tocando el tema de la privacidad a fondo. Para Stallman la recolección de datos personales debería estar prohibida.<br><br>Para ilustrar este punto, ha puesto como ejemplo algo que le ha horrorizado, el sistema de <strong>parquímetros</strong> de Valladolid. Según él son horribles, no porque haya que pagar, que es algo a lo que está dispuesto, sino porque hay que poner la matrícula del vehículo. Poner la matrícula en el parquímetro lo que sirve es para rastrear a la gente. Este tipo de acciones nos acercan cada vez más a la dictadura y a la destrucción de los derechos humanos.<br><br>Personalmente el ejemplo me parece bastante exagerado y aunque veo su punto, creo que la recolección de datos personales puede ser necesario en ciertas situaciones, siempre que se traten de forma adecuada.<br><br>También ha habido tiempo para hablar de historia. Habló de la historia de GNU, como <strong>Linux</strong> al principio tenía una licencia no libre (se impedía su redistribución comercial), pero que al poco cambió a GPL 2, siendo el candidato perfecto para el proyecto GNU. Ha comentado que <strong>Hurd</strong> tiene un diseño muy elegante, moderno pero quizá fue demasiado complicado y que en perspectiva fue un error diseñarlo de esa forma. Ha dicho que Hurd no es usable para nada práctico ahora mismo.<br><br>Ha insistido mucho en que el sistema operativo se llama <strong>GNU con Linux</strong>.<br><br>También ha hablado del <strong>open source</strong>. Y cuando le proclaman padre del open source afirma: "si soy el padre es porque han hecho la reproducción invitro con semen mío robado". Afirma que la gente del open source tiene otra ideología, mucho más pragmática, pero incompleta, ya que no preserva las libertades.<br><br>Ha hablado de licencias libres: débiles y la GPL. De entre las débiles recomienda la Apache, aunque por supuesto la mejor es la GPL, en sus distintas versiones. Ha confirmado que nadie está trabajando en la <strong>GPL4</strong>. Aquí ha aprovechado para criticar a <strong>GitHub</strong> que ha seguido una muy mala costumbre de no prestar atención a las licencias del software que aleja. Además indica que es necesario poner un comentario en cada archivo con la licencia que sigue. Eso de poner un archivo con la licencia en la carpeta raíz no es suficiente según Stallman.<br><br>Esto lo ha enlazado con <a href="https://www.gnu.org/software/librejs/">LibreJS</a>, el complemento que detecta si el código JavaScript de una página es libre o no y lo bloquea o no según esto. Stallman no ha criticado en ningún momento JavaScript, simplemente que tiene que respetar los mismos criterios de los programas nativos para ser software libre.<br><br>También ha hablado de distros libres, metiéndose con Ubuntu bastante. Reconoce que estos usuarios están más cerca de la libertad que si usasen Windows o macOS pero que todavía les falta un poco. Y lo peor para Stallman es que mucha gente cree que sistemas como Ubuntu son libres y la gente se queda en ellos.<br><br>Por último ha hablado del software libre en la educación, también ha recomendado a la universidad tener una <strong>asignatura de ingeniería inversa</strong> (o retroingeniería como él lo llama).<br><br>Después de esto ha proseguido con el momento más cómico de la charla, se puso su túnica y su aureola y empezó a hablar de la<strong> religión de Emacs. </strong><br><br><a href="https://files.adrianistan.eu/IMG_20181204_203251.jpg"><img class="aligncenter size-large wp-image-1639" src="https://files.adrianistan.eu/IMG_20181204_203251-576x1024.jpg" alt="" width="576" height="1024" /></a><br><br>Nos bendijo nuestros ordenadores, y habló de como formar parte de la Iglesia del tan importante Emacs. No es necesario guardar celibato para ser santo pero hay varias cosas, como el <strong>peregrinaje de Emacs</strong> (saberse todas las combinaciones de teclado de memoria) o los cismas actuales (¿cuál es la tecla más importante en Emacs?). También ha dedicado palabras a los Vimeros como yo. Usar Vi no es pecado, es una penitencia. Y eso a pesar de que VIVIVI es el número de la bestia. También contó como en China le intentaron atacar unos seguidores de Vi, pero tenía sentido porque la violencia empieza por vi.<br><br>Después ha subastado un ñu, poniendo caras para que no dejásemos al ñu solo. Además de incidir en que no puede haber pingüinos solos, tiene que haber ñus acompañándolos.<br><br>Por último la ronda de preguntas. Mi pregunta ha sido <strong><em>¿Las redes neuronales pueden ser software libre? </em></strong>Su respuesta ha sido que existen herramientas para alterar los valores numéricos de estas redes, mucho más fácil que la ingeniería inversa. Por tanto no sería técnicamente lo mismo. Creo que lo ha puesto al nivel de una fotografía o un vídeo, donde la edición es más sencilla y no tiene sentido hablar de fotografía libre.<br><br>También se ha preguntado por <strong>Microsoft</strong> y su deriva open source. Stallman celebra que publique cosas con licencias de software libre, pero eso no quita que siga teniendo software privativo que espía como Windows.<br><br>Le han preguntado por un <strong>teléfono que respete la privacidad</strong>. Según él es imposible, aunque se está intentando con un interruptor que permita desconectar la conexión móvil de forma física. El problema es de la tecnología móvil (no importa que sean smartphones o no), que para enrutar los paquetes guarda la ubicación de los dispositivos. El propósito era muy inocente pero se puede usar para espiar. Eso sí, él ha usado teléfonos móviles, siempre cuando necesita llamar pregunata a alguien que haya alrededor si le pueden dejar el teléfono.<br><br>Por último, sobre el <strong>hardware libre</strong> ha dicho que el concepto es erróneo. No puede existir hardware libre. El software libre existe, porque se puede copiar de forma exacta, en el mundo físico eso no pasa. Habría que hablar de hardware con diseños libres, pero ningún objeto podrá ser hardware libre o hardware privativo.<br><br><a href="https://files.adrianistan.eu/IMG_20181204_211011.jpg"><img class="aligncenter size-large wp-image-1638" src="https://files.adrianistan.eu/IMG_20181204_211011-1024x576.jpg" alt="" width="840" height="473" /></a>]]></description>
                <comments>https://blog.adrianistan.eu/conclusiones-de-la-visita-de-richard-stallman-a-valladolid</comments>
                <pubDate>Tue, 04 Dec 2018 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Alojando una web en IPFS</title>
                <link>https://blog.adrianistan.eu/alojando-una-web-en-ipfs</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/alojando-una-web-en-ipfs</guid>
                <description><![CDATA[En el <a href="https://blog.adrianistan.eu/2018/09/19/ipfs-el-futuro-de-la-web-descentralizada/">primer post</a> vimos como interactuar con IPFS de forma sencilla. Ahora vamos a dar un paso más y vamos a alojar una web en IPFS, aprovechando todas las ventajas de escalabilidad y disponibilidad que nos ofrece la red. Para ello usaremos además otro protocolo llamado IPNS, que sería similar a DNS pero en la red IPFS.<br><br>Las páginas ideales para IPFS actualmente son las que siguen el <a href="https://jamstack.org/">JAMstack</a>, es decir, uso extensivo de JavaScript en el cliente, que podrá conectarse a APIs para obtener/actualizar información. Las APIs no tienen por qué ser centralizadas, ya que JavaScript con WebRTC puede buscar peers, posibilitando APIs descentralizadas.<br><h2>Generando el contenido estático</h2><br>El primer paso será generar el contenido estático de la web. Para ello existen herramientas muy conocidas como <a href="https://blog.adrianistan.eu/2014/08/05/probando-jekyll/">Jekyll</a>, <a href="https://blog.adrianistan.eu/2016/05/26/tutorial-hugo-espanol-generador-sitios-estaticos/">Hugo</a> o <a href="https://blog.getpelican.com/">Pelican</a>.<br><br>No nos vamos a alargar más en esto, ya que cada herramienta tiene sus pasos. El resultado final será una carpeta con ficheros HTML, CSS, fuentes, imágenes y JavaScript.<br><h2>Subir a IPFS</h2><br>Teniendo el nodo IPFS en funcionamiento, subimos la carpeta del modo habitual, en mi caso, voy a subir la página que está en <a href="http://adrianistan.eu">http://adrianistan.eu</a> .<br><pre class="lang:default decode:true ">ipfs add -r adrianistan.eu/</pre><br><a href="https://files.adrianistan.eu/IPFS-AddWebsite.png"><img class="aligncenter size-large wp-image-1597" src="https://files.adrianistan.eu/IPFS-AddWebsite-1024x195.png" alt="" width="840" height="160" /></a>Y anotamos el último hash.<br><br>Comprobamos que la web es accesible, tanto desde la gateway del nodo, como una externa:<br><br><a href="https://files.adrianistan.eu/IPFS-CheckGateway.png"><img class="wp-image-1599 size-large" src="https://files.adrianistan.eu/IPFS-CheckGateway-1024x508.png" alt="" width="840" height="417" /></a> Tanto en el nodo local como en uno externo, la web carga perfectamente con el hash<br><h2><a href="https://files.adrianistan.eu/IPFS-GatewayExternal.png"><img class="aligncenter size-large wp-image-1598" src="https://files.adrianistan.eu/IPFS-GatewayExternal-1024x508.png" alt="" width="840" height="417" /></a> IPFS tiene direccionamiento por contenido</h2><br>En el post anterior mencionamos que IPFS direcciona por contenido gracias a los hashes. Esto tiene unas consecuencias interesantes. Por ejemplo, si se añade un archivo duplicado a IPFS, este tiene exactamente la misma dirección, ya que comparten hash. Por otro lado, los documentos no se pueden actualizar, porque entonces su hash cambia. Sin embargo en una web queremos cambiar contenido, ahí entra en acción IPNS.<br><h2>IPNS, gestión de nombres para IPFS</h2><br>IPNS es un protocolo parecido en intenciones a DNS que redirige un ID única al hash correspondiente en ese momento. Registramos el hash de la web actual en IPNS.<br><pre class="lang:default decode:true ">ipfs name publish HASH</pre><br><a href="https://files.adrianistan.eu/IPNS.png"><img class="aligncenter size-large wp-image-1600" src="https://files.adrianistan.eu/IPNS-1024x58.png" alt="" width="840" height="48" /></a>Ahora sí, el hash IPNS puede publicarse por la red, ya que siempre apuntará a la última versión de la web.<br><br>Para acceder a recursos a través de IPNS tenemos que cambiar de protocolo, en vez de /ipfs/HASH, tenemos que poner <strong>/ipns/HASH</strong>. Vale tanto para comandos como para las gateways HTTP.<br><br><a href="https://cloudflare-ipfs.com/ipns/QmYDVeoadAzk9ZW6zwJK3E3KHrA1LWLveEdqUv4XAcCjKa/">https://cloudflare-ipfs.com/ipns/QmYDVeoadAzk9ZW6zwJK3E3KHrA1LWLveEdqUv4XAcCjKa/</a><br><br>En cualquier momento podemos comprobar a que dirección IPFS apunta el hash IPNS:<br><pre class="lang:default decode:true ">ipfs name resolve QmYDVeoadAzk9ZW6zwJK3E3KHrA1LWLveEdqUv4XAcCjKa</pre><br><a href="https://files.adrianistan.eu/IPNS-Resolve.png"><img class="aligncenter size-full wp-image-1601" src="https://files.adrianistan.eu/IPNS-Resolve.png" alt="" width="837" height="62" /></a>Para actualizar el contenido simplemente se vuelve a repetir el paso de <em>ipfs name publish</em>. IPFS automáticamente modificará la redirección de IPNS.<br><br>Los contenidos antiguos no desaparecen, pero pueden no ser accesibles ya que ningún nodo tenga copia de ellos.<br><h2>DNSLink</h2><br>Aún así, ir dándole a la gente un hash IPNS es demasiado complicado. Afortunadamente, podemos usar el DNS tradicional para indicar una ruta IPNS y así, como veremos, facilitar bastante las cosas.<br><br>Para ello añadimos un campo TXT en los DNS de nuestro dominio. El contenido es el siguiente:<br><pre class="lang:default decode:true ">dnslink=/ipns/HASH</pre><br>Con esto podremos usar <strong>/ipns/dominio.com</strong> en la red IPFS. Pero todavía hace falta un cliente IPFS.<br><br>Afortunadamente, podemos redirigir mediante CNAME a una gateway HTTP de confianza y ¡la web funcionará correctamente! Para ello hay que crear un campo TXT en el subdominio <strong>_dnslink </strong>con el mismo contenido que el anterior.<br><br>Todas las gateways de IPFS soportan DNSLink para que la transición a IPFS sea lo más transparente posible.<br><br><a href="https://files.adrianistan.eu/DNSLink.png"><img class="aligncenter size-full wp-image-1602" src="https://files.adrianistan.eu/DNSLink.png" alt="" width="778" height="166" /></a>Así, finalmente la página carga con un dominio normal y corriente en un navegador normal y corriente.<br><br><a href="https://files.adrianistan.eu/IPNS-DNSLink.png"><img class="size-large wp-image-1603" src="https://files.adrianistan.eu/IPNS-DNSLink-1024x507.png" alt="" width="840" height="416" /></a> Fijaos en la URL<br><br>De hecho, vosotros mismos podéis acceder:<br><br><a href="http://adrianistan.yayeyo.ga">http://adrianistan.yayeyo.ga</a><br><h2>IPFS Companion</h2><br>Si usamos mucho IPFS, puede que nos interese una extensión que maneje el protocolo <strong>ipfs://</strong> . Tanto en <a href="https://addons.mozilla.org/en-US/firefox/addon/ipfs-companion/">Firefox</a> como en <a href="https://chrome.google.com/webstore/detail/ipfs-companion/nibjojkomfdiaoajekhjakgkdhaomnch">Chrome</a> existe IPFS Companion, una extensión que nos permite acceder a contenido IPFS de forma sencilla.<br><h2><a href="https://files.adrianistan.eu/IPFS-Companion.png"><img class="aligncenter size-large wp-image-1604" src="https://files.adrianistan.eu/IPFS-Companion-1024x640.png" alt="" width="840" height="525" /></a>Servicio systemd</h2><br>Por último, quiero dejar el servicio de systemd necesario para tener el nodo IPFS funcionando constantemente en nuestro ordenador. En este caso, IPFS lo instalé vía Snap.<br><pre class="lang:default decode:true ">[Unit]<br>Description=IPFS daemon<br><br>[Service]<br>ExecStart=/snap/bin/ipfs daemon<br>Restart=on-failure<br><br>[Install]<br>WantedBy=default.target</pre><br><pre class="lang:default decode:true ">sudo cp ipfs.service /etc/systemd/user/</pre><br><pre class="lang:default decode:true ">sudo systemctl --user start ipfs.service<br>sudo systemctl --user enable ipfs.service</pre><br>Y con esto ya tendríamos suficiente como para jugar con IPFS un buen rato.]]></description>
                <comments>https://blog.adrianistan.eu/alojando-una-web-en-ipfs</comments>
                <pubDate>Mon, 24 Sep 2018 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>IPFS, el futuro de la web descentralizada</title>
                <link>https://blog.adrianistan.eu/ipfs-el-futuro-de-la-web-descentralizada</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/ipfs-el-futuro-de-la-web-descentralizada</guid>
                <description><![CDATA[Imagina la web sin servicios centralizados. Donde puedas acceder a un vídeo o a una galería de fotos y que no haya una única manera de acceder a contenido. Eso y mucho más es <strong>IPFS</strong>. <a href="https://ipfs.io/">IPFS</a> son las siglas de <strong>InterPlanetary File System</strong> y se trata de una red descentralizada de intercambio de archivos. Nos puede recordar a BitTorrent y en efecto, una buena descripción para IPFS es Torrent 2.0. No obstante IPFS implementa bastantes mejoras sobre BitTorrent, mejoras que lo hacen más útil.<br><br><a href="https://files.adrianistan.eu/IPFS.png"><img class="aligncenter size-full wp-image-1587" src="https://files.adrianistan.eu/IPFS.png" alt="" width="600" height="200" /></a><br><br>En un post próximo explicaré como podemos sacar partido a IPFS ahora mismo, pero antes vamos a ver algunos conceptos fundamentales de IPFS.<br><br>Siendo más técnicos, IPFS es una red P2P inspirada en BitTorrent, Git y Kademlia. El protocolo es una red de distribución de contenido (CDN) y define un sistema de archivos donde el identificador es el propio contenido (a través de su hash). En IPFS nada desaparece sino que se versiona.<br><br>Vamos a ver como usar IPFS desde la terminal. Existen varios clientes, en diferentes lenguajes, como js-ipfs (que tendrá mucha utilidad en aplicaciones web descentralizadas usando JavaScript), pero de momento el cliente más maduro es go-ipfs. <a href="https://dist.ipfs.io/#go-ipfs">Desde la web puedes descargarlo</a>.<br><h2>Iniciar IPFS</h2><br>Una vez IPFS esté instalado hay que ejecutar lo siguiente:<br><pre class="lang:default decode:true ">ipfs init<br>o<br>ipfs init --profile server</pre><br>La segunda opción es para centros de datos, ya que reduce el tráfico interno.<br><br><a href="https://files.adrianistan.eu/IPFS-Init.png"><img class="aligncenter size-full wp-image-1588" src="https://files.adrianistan.eu/IPFS-Init.png" alt="" width="735" height="164" /></a>Este comando genera las claves RSA del nodo, necesarias para la comunicación con la red IPFS. Además se nos informa de nuestro ID de nodo. Para acceder a este ID en cualquier momento podemos escribir:<br><pre class="lang:default decode:true">ipfs id</pre><br><h2>Iniciar el nodo IPFS</h2><br>Para acceder al contenido IPFS necesitamos ejecutar un nodo. Este nodo se inicia con <strong>ipfs daemon</strong>. Una cosa importante es que por ejecutar el nodo no se va a descargar nada que no pidamos. IPFS es explícito, pero necesita estar activo.<br><pre class="lang:default decode:true ">ipfs daemon</pre><br><h2><a href="https://files.adrianistan.eu/IPFS-Daemon.png"><img class="aligncenter size-full wp-image-1590" src="https://files.adrianistan.eu/IPFS-Daemon.png" alt="" width="829" height="288" /></a>Obtener archivos</h2><br>Ahora vamos a descargar nuestros primeros archivos de la red IPFS.<br><br>Para obtener un archivo existen varias formas:<br><pre class="lang:default decode:true">ipfs cat /ipfs/QmS4ustL54uo8FzR9455qaxZwuMiUhyvMcX9Ba8nUH4uVv/readme<br>o<br>ipfs cat QmS4ustL54uo8FzR9455qaxZwuMiUhyvMcX9Ba8nUH4uVv/readme</pre><br>Podemos usar el comando <strong>cat</strong> de IPFS, que funciona de forma similar a cat de Unix.<br><br>Las URL de IPFS todavía no han sido definidas de forma definitiva pero de momento siguen el siguiente formato: <em>/protocolo (omitible)/hash/fichero (si es una carpeta)</em>. Es el hash el que identifica el <em>bloque de contenido</em> y es lo que sirve para encontrar el contenido en la red IPFS. Internamente usa una DHT (<em>Distributed Hash Table).</em> IPFS no usa trackers como las primeras versiones de BitTorrent. En cambio usa una DHT basada en Kademlia (bastante rápida).<br><br>Para obtener un fichero binario usamos el operador de redirección de Linux.<br><pre class="lang:default decode:true">ipfs cat QmWYhH2Nac5vSgTmc6NULncakvhCXgAci33M5NXHjjBuU8 &gt; Comuneros.jpg</pre><br>También se puede acceder a IPFS por dos métodos más:<br><h5>FUSE</h5><br>Podemos montar un sistema de ficheros en Linux gracias a FUSE que es una puerta a IPFS.<br><pre class="lang:default decode:true ">ipfs mount</pre><br>Y tendrás acceso a carpetas <em>/ipfs/hash/</em> integradas en tu sistema. Muy útil si lo queremos integrar en aplicaciones.<br><h5>GATEWAY HTTP</h5><br>Podemos usar IPFS sin tener instalado nada, usando la <em>puerta de acceso</em> que alguien generosamente proporcione a la red. La puerta se encargará de llevar el contenido IPFS al mundo cliente-servidor de HTTP. Existen varias, de hecho cada nodo tiene un servidor HTTP en localhost para esto mismamente, pero aquí voy a mencionar dos: la de IPFS.io y la de CloudFare.<br><ul><br> 	<li>IPFS.io: <a href="https://ipfs.io/ipfs/QmWYhH2Nac5vSgTmc6NULncakvhCXgAci33M5NXHjjBuU8">https://ipfs.io/ipfs/QmWYhH2Nac5vSgTmc6NULncakvhCXgAci33M5NXHjjBuU8</a> (se prepone https://ipfs.io a la URL de IPFS).</li><br> 	<li>CloudFare: <a href="https://cloudflare-ipfs.com/ipfs/QmWYhH2Nac5vSgTmc6NULncakvhCXgAci33M5NXHjjBuU8">https://cloudflare-ipfs.com/ipfs/QmWYhH2Nac5vSgTmc6NULncakvhCXgAci33M5NXHjjBuU8</a> (se prepone https://cloudfare-ipfs.com a la URL de IPFS).</li><br></ul><br>Esto sirve para llevar IPFS a cualquier ordenador a través de un navegador web tradicional como Chrome, Firefox o Safari. También facilita la tarea de integrar IPFS en aplicaciones que no tengan desarrollada una librería específica.<br><h2>Añadir archivos</h2><br>Ahora vamos a poner en la red IPFS nuevos archivos. Para ello usamos <strong>ipfs add</strong>.<br><br><a href="https://files.adrianistan.eu/IPFS-Add.png"><img class="aligncenter size-large wp-image-1589" src="https://files.adrianistan.eu/IPFS-Add-1024x56.png" alt="" width="840" height="46" /></a>Esta operación genera el hash que identifica al contenido en la red y que tendremos que enviar a las personas que queramos que accedan al contenido.<br><br>La red IPFS de este modo es semi-privada, ya que sin saber el hash no podemos acceder al contenido en cuestión.<br><br>Y ya estaría, así de simple. También podemos añadir carpetas.<br><br><a href="https://files.adrianistan.eu/IPFS-AddFolder.png"><img class="aligncenter size-large wp-image-1593" src="https://files.adrianistan.eu/IPFS-AddFolder-1024x401.png" alt="" width="840" height="329" /></a>El último hash que se ve en la pantalla hace referencia a la carpeta entera.<br><h2>Pining</h2><br>Por defecto, IPFS almacena los archivos propios del nodo (los que hemos añadido con ipfs add). Pero entonces IPFS no supone ninguna ventaja respecto a HTTP. No es distribuido, solo hay una copia de los ficheros en el universo.<br><br>Cuando descargamos un fichero, IPFS guarda en caché una copia para ofrecer a la red, haciendo que sea un sistema distribuido de verdad. No obstante si estamos realmente interesados en conservar el fichero en nuestro nodo a disposición de la red IPFS tenemos que hacer pin <strong>(ipfs add).</strong> Cuando nos deje de interesar conservar el fichero a la red, podemos quitar el pin <strong>(ipfs rm</strong>).<br><pre class="lang:default decode:true ">ipfs pin add QmWYhH2Nac5vSgTmc6NULncakvhCXgAci33M5NXHjjBuU8<br>ipfs pin rm -r QmWYhH2Nac5vSgTmc6NULncakvhCXgAci33M5NXHjjBuU8</pre><br><h2>Más cosas</h2><br>Todavía nos quedan muchas cosas de IPFS que ver, como el versionado, el sistema de nombres IPNS, el sistema de enlazado semántico IPLD y como podemos usar IPFS para páginas y aplicaciones web.<br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/ipfs-el-futuro-de-la-web-descentralizada</comments>
                <pubDate>Wed, 19 Sep 2018 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Computación cuántica para torpes: introducción para programadores</title>
                <link>https://blog.adrianistan.eu/computacion-cuantica-para-torpes-introduccion-para-programadores</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/computacion-cuantica-para-torpes-introduccion-para-programadores</guid>
                <description><![CDATA[Hace ya bastante tiempo quese viene hablando de ordenadores cuánticos. ¿Qué son? ¿En qué se diferencian de los ordenadores clásicos? ¿Son de verdad tan potente como dicen?<br><br>En este largo artículo, ideal para el verano, vamos a ver los principios fundamentales de los ordenadores cuánticos más allá de lo que la típica revista o web te podría contar. Veremos qué es un qubit y algunas puertas lógicas interesantes, así como su aplicación.<br><br><a href="https://files.adrianistan.eu/IBMQComputer.jpg"><img class="size-full wp-image-1552" src="https://files.adrianistan.eu/IBMQComputer.jpg" alt="" width="720" height="480" /></a> Los ordenadores cuánticos actuales requieren temperaturas de funcionamiento cercanas al cero absoluto<br><h2>Notación de Dirac</h2><br>Lo primero antes de entrar en materia cuántica, será adaptar nuestra notación clásica, a otra más potente. Esta notación que vamos a ver, llamada <a href="https://la-mecanica-cuantica.blogspot.com/2009/08/la-notacion-bra-ket-de-dirac.html">notación de Dirac o Bra-ket</a>, nos será muy útil ya que los bits clásicos no son más que un caso concreto de qubit en esta notación.<br><br>En esta notación tenemos que representar los bits como matrices. Un conjunto de N bits se representa con una matriz de 1 columna y [latex]2^N[/latex] filas. En todas las posiciones existen ceros salvo para la posición que representa la combinación que representa. Veamos algunos ejemplos sencillos:<br><br>Un bit con valor 0 se representa así<br><p style="text-align: center;">[latex]<br>| 0 \rangle = \begin{pmatrix}<br>1\\<br>0<br>\end{pmatrix}[/latex]</p><br>Un bit con valor 1 se representa así:<br><p style="text-align: center;">[latex]<br>| 1 \rangle = \begin{pmatrix}<br>0\\<br>1<br>\end{pmatrix}[/latex]</p><br>Aquí contamos como en informática, empezando desde cero. Como ves la posición 0 del vector solo es 1 cuando representa el bit 0. Si la posición que tiene el 1 es la segunda, representa el bit 1.<br><br>La parte que va a la izquierda del igual se llama ket. En este caso hemos representado ket 0 y ket 1.<br><br>Si tenemos más bits se puede hacer de la misma forma. Vamos a representtar ket 10. 10 en binario es 2, así que estará en la posición tercera.<br><p style="text-align: center;">[latex]<br>| 10 \rangle = \begin{pmatrix}<br>0\\<br>0\\<br>1\\<br>0<br>\end{pmatrix}[/latex]</p><br><br><h2>Puertas lógicas como producto de matrices</h2><br>¿Recuerdas el producto de matrices de tus clases de álgebra? Resulta que todas las puertas lógicas clásicas pueden representarse como producto de matrices. Por ejemplo, la puerta lógica NOT se puede implementar con esta matriz:<br><p style="text-align: center;">[latex]<br>\begin{pmatrix}<br>0 &amp; 1 \\<br>1 &amp; 0 \\<br>\end{pmatrix}<br>[/latex]</p><br>Y aquí la vemos en acción<br><p style="text-align: center;">[latex]<br>\begin{pmatrix}<br>0 &amp; 1 \\<br>1 &amp; 0 \\<br>\end{pmatrix}\begin{pmatrix}<br>1 \\<br>0<br>\end{pmatrix}<br>=<br>\begin{pmatrix}<br>0 \\<br>1<br>\end{pmatrix}<br>[/latex]</p><br>También podría hacerse con la puerta AND que toma como entrada dos bits.<br><p style="text-align: center;">[latex]<br>\begin{pmatrix}<br>1 &amp; 1 &amp; 1 &amp; 0 \\<br>0 &amp; 0 &amp; 0 &amp; 1<br>\end{pmatrix}<br>\begin{pmatrix}<br>0 \\<br>0 \\<br>0 \\<br>1<br>\end{pmatrix}<br>=<br>\begin{pmatrix}<br>0 \\<br>1<br>\end{pmatrix}<br>[/latex]</p><br><br><br><a href="https://files.adrianistan.eu/IBMQKeyboard.jpeg"><img class="size-large wp-image-1553" src="https://files.adrianistan.eu/IBMQKeyboard-747x1024.jpeg" alt="" width="747" height="1024" /></a> Un teclado con puertas cuánticas<br><h2>Juntando bits</h2><br>Para formar bits más grandes ya sabemos que tenemos que tener una matriz tan grande como combinaciones haya ([latex]2^N[/latex] posiciones, N bits). Existe una forma de calcular automáticamente la posición que hay que marcar y es hacer el <strong>producto tensorial</strong>. Si no sabes calcularlo no importa mucho, porque apenas lo vamos a usar, pero puedes ver un ejemplo de como funciona. En este ejemplo, queremos juntar los bits 1 y 1 (3 en decimal).<br><p style="text-align: center;">[latex]<br>\begin{pmatrix}<br>0 \\<br>1<br>\end{pmatrix}<br>\otimes<br>\begin{pmatrix}<br>0 \\<br>1<br>\end{pmatrix}<br>=<br>\begin{pmatrix}<br>0 \\<br>0 \\<br>0 \\<br>1<br>\end{pmatrix}<br>[/latex]</p><br><br><h2>Qubits</h2><br>Hasta ahora no hemos visto nada realmente nuevo, solo hemos preparado el terreno para dejar paso a los qubits. Personalmente desconocía que podían usarse matrices para operar con bits y aunque no es tan práctico como otros sistemas, lo cierto es que es muy explícito y elegante.<br><br><a href="https://files.adrianistan.eu/Schrodinger.jpg"><img class="aligncenter size-full wp-image-1555" src="https://files.adrianistan.eu/Schrodinger.jpg" alt="" width="670" height="502" /></a>Los qubits son bits como los que hemos visto antes pero tienen un estado indeterminado. No es que tengan un valor de forma secreta y no sepamos cuál es. Podríamos decir que son los dos valores clásicos a la vez, como el <a href="https://www.youtube.com/watch?v=WMnjRIx3_XE">gato de Schrodinger.</a> Cuando realizamos una observación sobre el qubit, este <strong>colapsa</strong> y podemos ver uno de los dos estados. ¿Cuál vemos? No podemos saberlo a priori, pero hay <strong>probabilidades</strong>. Los bits clásicos no son más que qubits cuyas probabilidades de colapsar a 0 o 1 es del 100%, por tanto no hay duda y su valor sí que está determinado.<br><br>¿Cómo representamos los qubits y sus estados cuánticos? Con <strong>números complejos</strong>. Si recuerdas, estos eran los que tenían una parte real y una imaginaria. No obstante, voy a tomar solo los números <strong>reales</strong> para simplificar. Los números reales son números complejos válidos, pero los complejos son mucho más extensos.<br><br><a href="https://files.adrianistan.eu/EsferaBloch.jpg"><img class="size-large wp-image-1551" src="https://files.adrianistan.eu/EsferaBloch-1024x482.jpg" alt="" width="840" height="395" /></a> La esfera de Bloch permite representar todos los estados cuánticos (números complejos)<br><br>Para calcular la probabilidad que tiene un estado cuántico de colapsar a un valor, hacemos el módulo y elevamos al cuadrado: [latex]|a|^2[/latex].<br><br>Todo qubit además debe cumplir una propiedad fundamental:<br><p style="text-align: center;">[latex]<br>\begin{pmatrix}<br>a \\<br>b<br>\end{pmatrix}<br>\text{ es un qubit}<br>\Leftrightarrow<br>|a|^2 + |b|^2 = 1<br>[/latex]</p><br>Y es que la probabilidad de ser 0 y de ser 1 sumadas deben equivaler al suceso seguro, es decir, 1. 100% de probabilidades de que de 0 o 1.<br><br>Con esto ya podemos definir algunos qubits.<br><p style="text-align: center;">[latex]<br>\begin{pmatrix}<br>\frac{1}{\sqrt{2}} \\<br>\frac{1}{\sqrt{2}}<br>\end{pmatrix}<br>[/latex]</p><br>Este es mi qubit preferido. Representa el estado de superposición cuántica. Cada valor tiene el 50% de probabilidades de salir. [latex]|\frac{1}{\sqrt{2}}|^2 = \frac{1}{2}[/latex]. Cuando colapsemos el qubit al observarlo será como lanzar una moneda al aire.<br><br>Otro detalle que a veces se nos pasa por alto es que los qubits pueden contener valores negativos. Estos qubits son físicamente diferentes a los positivos, pero tienen las mismas probabilidades de colapsar en los mismos valores que los positivos.<br><p style="text-align: center;">[latex]<br>\begin{pmatrix}<br>-1 \\<br>0<br>\end{pmatrix}<br>[/latex]</p><br>Es un qubit válido, que colapsa con 100% de probilidad a 0.<br><br>¿Cómo se representan los qubits en notación de Dirac? Representando la probabilidad que tiene cada combinación de bits de aparecer. Para un qubit sería algo así:<br><p style="text-align: center;">[latex]<br>\alpha | 0 \rangle + \beta | 1 \rangle<br>[/latex]</p><br>Siendo [latex]\alpha[/latex] y [latex]\beta[/latex] las probabilidades de colapsar a cada estado.<br><h2>Puertas cuánticas</h2><br>Ahora vamos a ver cuáles son las puertas lógicas más importantes del mundo cuántico.<br><br><a href="https://files.adrianistan.eu/IBMQ.png"><img class="aligncenter size-large wp-image-1537" src="https://files.adrianistan.eu/IBMQ-1024x608.png" alt="" width="840" height="499" /></a><br><h4>Negación (Pauli X)</h4><br>Esta es exactamente igual que en el mundo clásico, con la misma matriz que hemos visto antes. Su símbolo es el cuadrado con una X.<br><br><a href="https://files.adrianistan.eu/IBMQNegation.png"><img class="aligncenter size-full wp-image-1535" src="https://files.adrianistan.eu/IBMQNegation.png" alt="" width="283" height="57" /></a>Aquí vemos una imagen del <a href="https://quantumexperience.ng.bluemix.net/qx">simulador IBM Q</a> usando la puerta X cuántica. IBM nos deja ejecutarlo en ordenadores cuánticos <strong>reales</strong>. Veamos los resultados.<br><br><a href="https://files.adrianistan.eu/IBMQResults.png"><img class="aligncenter size-full wp-image-1536" src="https://files.adrianistan.eu/IBMQResults.png" alt="" width="292" height="238" /></a>¡Terrible! La mayoría de casos, el ordenador cuántico responde 1, el valor correcto, pero un 13% de los casos no. Teóricamente había una probabilidad del 100% y en la práctica solo es del 86.3%. ¡Y solo es una puerta X! Es por ello que los procesadores cuánticos todavía necesitan mejorar mucho. Google, Microsoft e IBM están investigando de forma independiente en ordenadores cuánticos. Veremos quién consigue tener antes ordenadores cuánticos precisos (aunque hay expertos que piensan que nunca se podrá lograr).<br><h4>CNOT</h4><br>Esta puerta es muy interesante. Toma dos qubits, uno de control, que permanece invariable al traspasar la puerta y otro de datos. Al qubit de datos se le aplica la puerta X si el qubit de control está activo. Su matriz es la siguiente:<br><p style="text-align: center;">[latex]<br>CNOT = \begin{pmatrix}<br>1 &amp; 0 &amp; 0 &amp; 0 \\<br>0 &amp; 1 &amp; 0 &amp; 0 \\<br>0 &amp; 0 &amp; 0 &amp; 1 \\<br>0 &amp; 0 &amp; 1 &amp; 0<br>\end{pmatrix}<br>[/latex]</p><br>Se reprensenta de esta forma:<br><br><a href="https://files.adrianistan.eu/CNOT.png"><img class="aligncenter size-full wp-image-1539" src="https://files.adrianistan.eu/CNOT.png" alt="" width="60" height="90" /></a>O similar, porque los símbolos de computación cuántica no están todavía muy estandarizados. El punto grande es el qubit de control y el punto pequeño está sobre el qubit de datos.<br><h4>HADAMARD</h4><br>Esta puerta es quizá la más famosa del mundo cuántico. Veamos su matriz:<br><p style="text-align: center;">[latex]<br>\begin{pmatrix}<br>\frac{1}{\sqrt{2}} &amp; \frac{1}{\sqrt{2}} \\<br>\frac{1}{\sqrt{2}} &amp; -\frac{1}{\sqrt{2}}<br>\end{pmatrix}<br>[/latex]</p><br>Esta puerta permite poner un qubit clásico en estado de superposición cuántica. Y también deshacerlo. Es muy usada en algoritmos cuánticos. Se representa con un cuadrado y una H.<br><br><a href="https://files.adrianistan.eu/IBMH.png"><img class="aligncenter size-full wp-image-1540" src="https://files.adrianistan.eu/IBMH.png" alt="" width="243" height="92" /></a>Los resultados en el ordenador cuántico real de IBM Q son:<br><br><a href="https://files.adrianistan.eu/IBMHStats.png"><img class="aligncenter size-full wp-image-1541" src="https://files.adrianistan.eu/IBMHStats.png" alt="" width="191" height="242" /></a>Cuando debería de ser bastante más cercano a 50% en los dos valores.<br><br>Con esto ya tenemos las puertas más usadas. Básicamente con Hadamard, X y CNOT se pueden implementar casi todos los circuitos cuánticos. Solo nos faltarían las puertas que operan entran en números complejos para poder implementar todos los circuitos.<br><h2>Algoritmo de Deutsch-Jozsa</h2><br>El<a href="https://es.wikipedia.org/wiki/Algoritmo_de_Deutsch-Jozsa"> algoritmo de Deutsch-Jozsa</a> es uno de los algoritmos cuánticos más sencillos de entender y que mejoran drásticamente el rendimiento respecto a un algoritmo clásico.<br><br>El planteamiento básico es que tenemos una caja negra que aplica una función sobre un bit. Estas funciones pueden ser: set-to-0, set-to-1 (ambas constantes), identidad (no cambiar nada) y X (ambas dinámicas) . Si queremos saber que función contiene la caja negra, ¿Cuántas veces tenemos que pasar valores? En una CPU clásica tendríamos que hacerlo dos veces para poder determinar que función contiene la caja negra. En una CPU cuántica... también. No hay diferencia. Pero, si cambiamos la pregunta a ¿de qué categoría es la función de la caja negra?, la cosa cambia. En una CPU clásica seguiríamos necesitando 2 pruebas, pero en una CPU cuántica y con un circuito por el exterior, podemos aprovechar la superposición cuántica para realizar una sola prueba y determinar si en el interior hay una función constante o dinámica.<br><br>Vamos a ver estas 4 funciones de la caja negra como son:<br><br><a href="https://files.adrianistan.eu/QuantumGates.png"><img class="aligncenter size-full wp-image-1544" src="https://files.adrianistan.eu/QuantumGates.png" alt="" width="900" height="200" /></a>¿Se te ocurre como puedes crear un circuito fuera de la caja negra que con una sola prueba, ya sepa si estamos ante las funciones Set-0, Set-1 o Identidad, Negación?<br><br>El circuito es el siguiente:<br><br><a href="https://files.adrianistan.eu/IBMBlackBox.png"><img class="aligncenter size-full wp-image-1545" src="https://files.adrianistan.eu/IBMBlackBox.png" alt="" width="390" height="89" /></a>Tal y como está diseñado si en q[1] medimos 0, la función es de tipo constante y si medimos 1, es de tipo dinámica. Un desarrollo matemático de los productos de matrices, <a href="https://es.wikipedia.org/wiki/Algoritmo_de_Deutsch-Jozsa">como el que hay en Wikipedia</a>, te mostrará como siempre es cierto. Este también es un ejemplo de como los ordenadores cuánticos pueden dar resultados totalmente deterministas.<br><br>Esta idea, se puede generalizar y extrapolar a otros problemas, generando una colección muy interesante de algoritmos que se ejecutan en tiempo exponencialmente menor que en una CPU clásica.<br><h2>Algoritmos de Shor y de Grover</h2><br>Estos dos algoritmos han sido llamados los <em>killer apps</em> de la computación cuántica, ya que son algoritmos que mejoran sustancialmente (uno de forma exponencial, otro de forma cuadrática) los tiempos de problemas reales.<br><br>El <a href="https://es.wikipedia.org/wiki/Algoritmo_de_Shor">algoritmo de Shor</a> fue el primero en ser descubierto, en 1994 por <a href="http://www-math.mit.edu/~shor/">Peter Shor</a>. Sirve para factorizar números (es decir, sacar los números primos que multiplicados generan el número original). Lo puede hacer en [latex]O((\log{n})^3)[/latex]. De este modo, los algoritmos tipo RSA que se basan en la factorización de números podrían romperse en tiempo polinómico, por lo cuál RSA ya no serviría como protección. El algoritmo de Shor no da siempre los resultados correctos, pero tiene una probabilidad de éxito superior a la de fracaso, por lo que con repetir múltiples veces la ejecución podríamos estar casi seguros del resultado.<br><br>El <a href="https://es.wikipedia.org/wiki/Algoritmo_de_Grover">algoritmo de Grover</a> fue descubierto en 1996 por Lov Grover y permite buscar en una lista no ordenada de datos en [latex]O(\sqrt{n})[/latex] mientras que en una computadora clásica sería [latex]O(n)[/latex].<br><br>Estos dos algoritmos sin duda marcan lo que se ha llamada la <strong>supremacía cuántica</strong> y que ocurrirá cuando los ordenadores cuánticos puedan ejecutar con suficiente precisión estos algoritmos y su utilidad en el mundo real supere a la de los algoritmos clásicos.<br><h2>Entrelazamiento cuántico</h2><br>Ya hemos visto las bases de los circuitos cuánticos. Ahora veamos algunas consecuencias de todo lo anterior. Cosas cuánticas que parecen hasta cierto punto fuera de la lógica. ¿Qué ocurre si tenemos varios qubits en un estado como este?<br><p style="text-align: center;">[latex]<br>\begin{pmatrix}<br>\frac{1}{\sqrt{2}} \\<br>0 \\<br>0 \\<br>\frac{1}{\sqrt{2}}<br>\end{pmatrix}<br>[/latex]</p><br>En este estado solo puede colapsar a 00 o a 11. ¿Esto qué significa? Significa que los qubits están entrelazados entre sí, uno depende de otro, si los separamos y uno colapsa a 0, el otro colapsa a 0. Si uno colapsa a 1, el otro colapsa a 1.<br><br>Es importante destacar que los qubits pueden separarse en este estados. Los qubits alejados a millones de kilómetros siguen entrelazados y el valor al que deciden colapsar se decide de <strong>forma instantánea</strong>. Esto quiere decir que se <em>sincronizan</em> a una velocidad <strong>superior a la de la luz</strong>. El truco es que no se transmite información, por eso el universo lo permite, pero esto permite realizar la teletransportación cuántica.<br><br>La forma de entrelazar qubits es muy sencilla, con una puerta Hadamard y una CNOT.<br><br><a href="https://files.adrianistan.eu/QuantumEntanglement.png"><img class="aligncenter size-full wp-image-1546" src="https://files.adrianistan.eu/QuantumEntanglement.png" alt="" width="373" height="113" /></a><a href="https://files.adrianistan.eu/QuantumEntanglementStats.png"><img class="aligncenter size-full wp-image-1547" src="https://files.adrianistan.eu/QuantumEntanglementStats.png" alt="" width="526" height="297" /></a><a href="https://quantumexperience.ng.bluemix.net/qx">IBM Q</a> todavía tiene que mejorar, pero se aprecia claramente el entrelazamiento cuántico.<br><h2>Teletransportación cuántica</h2><br>La teletransportación existe, al menos entre qubits. Y es <strong>instantánea</strong> (más o menos). La teletransportación cuántica la podemos provocar usando varios qubits entrelazados. Necesitamos 3 qubits. El qubit que va a ser teletransportado, un qubit del emisor y un qubit del receptor. La idea es entrelazar el emisor con el receptor (qubit de destino) y posteriormente el qubit del emisor con el qubit que va a ser transportado.<br><br><a href="https://files.adrianistan.eu/QuantumTeletransportation2.png"><img class="aligncenter size-full wp-image-1549" src="https://files.adrianistan.eu/QuantumTeletransportation2.png" alt="" width="724" height="185" /></a>No he sido capaz de hacer que IBM Q haga una teletransportación, así que aquí viene un esquema distinto. T es el qubit a transportar, A es el emisor y B el receptor. En este ejemplo se usa la puerta Pauli Z, cuya matriz es la indicada.<br><br>El truco de la teletransportación instantánea tiene que ver con que A y B tienen que estar entrelazados, por tanto, han tenido que ser transportados a sus respectivos lugares anteriormente a velocidad inferior a la luz.<br><br>Esto teletransporta qubits pero <strong>no hace copias</strong>. Esto es debido al <a href="https://es.wikipedia.org/wiki/Teorema_de_no_clonaci%C3%B3n">Teorema de No Clonación</a>.<br><h2>Lenguajes de programación</h2><br>Mientras esperamos a que los ordenadores cuánticos sean lo suficientemente estables, ya existen lenguajes de programación que podemos usar en simuladores. Quizá el más conocido sea Q# de Microsoft (funciona en Linux, tranquilos), que guarda similitudes con C#. Otro bastante usado es OpenQasm de IBM, algo más parecido a ensamblador.<br><pre class="lang:c# decode:true">namespace Quantum.Bell<br>{<br>    open Microsoft.Quantum.Primitive;<br>    open Microsoft.Quantum.Canon;<br><br>    operation Set (desired: Result, q1: Qubit) : ()<br>    {<br>        body<br>        {<br>            let current = M(q1);<br>            if (desired != current)<br>            {<br>                X(q1);<br>            }<br>        }<br>    }<br>    operation BellTest (count : Int, initial: Result) : (Int,Int,Int)<br>    {<br>        body<br>        {<br>            mutable numOnes = 0;<br>            mutable agree = 0;<br>            using (qubits = Qubit[0])<br>            {<br>                for (test in 1..count)<br>                {<br>                    Set (initial, qubits[0]);<br>                    Set (Zero, qubits[1]);<br><br>                    H(qubits[0]);<br>                    CNOT(qubits[0],qubits[1]);<br>                    let res = M (qubits[0]);<br><br>                    if (M (qubits[1]) == res) <br>                    {<br>                        set agree = agree + 1;<br>                    }<br><br>                    // Count the number of ones we saw:<br>                    if (res == One)<br>                    {<br>                        set numOnes = numOnes + 1;<br>                    }<br>                }<br><br>                Set(Zero, qubits[0]);<br>            }<br>            // Return number of times we saw a |0&gt; and number of times we saw a |1&gt;<br>            return (count-numOnes, numOnes, agree);<br>        }<br>    }<br>}</pre><br>Este es un ejemplo de <em>lanzar la moneda</em> con entrelazamiento cuántico en Q#, el lenguaje cuántico de Microsoft.<br><h2>Referencias</h2><br><a href="https://www.youtube.com/watch?v=F_Riqjdh2oM">Quantum Computing for Computer Scientists</a><br><a href="https://www.infoq.com/articles/quantum-computing-algoritms-two">Cats, Qubits, and Teleportation: The Spooky World of Quantum Algorithms</a><br><a href="https://www.youtube.com/watch?v=v7b4J2INq9c">Microsoft Quantum Development Kit: Introduction and step-by-step demo</a><br><a href="https://en.wikipedia.org/wiki/Qubit">Qubit</a>]]></description>
                <comments>https://blog.adrianistan.eu/computacion-cuantica-para-torpes-introduccion-para-programadores</comments>
                <pubDate>Tue, 17 Jul 2018 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Usar AVA para tests en una API hecha en Node.js y Express</title>
                <link>https://blog.adrianistan.eu/usar-ava-para-tests-en-una-api-hecha-en-node-js-y-express</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/usar-ava-para-tests-en-una-api-hecha-en-node-js-y-express</guid>
                <description><![CDATA[Testear nuestras aplicaciones es algo fundamental si queremos garantizar un mínimo de calidad. En este breve post, explicaré como he usado <a href="https://github.com/avajs/ava">AVA</a> para crear tests para mis APIs: tanto unitarios como de integración con AVA.<br><br><a href="https://files.adrianistan.eu/ava.png"><img class="aligncenter size-large wp-image-1500" src="https://files.adrianistan.eu/ava-1024x640.png" alt="" width="840" height="525" /></a><br><br>AVA es una herramienta de testing, que nos permite describir los tests de forma muy sencilla. De todas las herramientas que he probado, AVA es muy preferida. Es muy sencilla, ejecuta los tests en paralelo y permite escribir código en ES6 (usa Babel por debajo). Además tiene bastante soporte siendo el framework de testing usando por muchos proyectos ya.<br><br>Instalamos AVA de la forma típica:<br><pre class="lang:default decode:true ">npm install ava --save-dev</pre><br>A continuación creamos un fichero cuya terminación sea .test.js, por ejemplo, <em>suma.test.js</em>. El lugar da igual.<br><br>Una opción de diseño es poner los test unitarios al lado de las funciones en sí, otra opción es crear una carpeta para todos los tests, ya que los de integración van a ir casi siempre ahí. Para ejecutar los tests, simplemente:<br><pre class="lang:default decode:true">ava</pre><br><a href="https://files.adrianistan.eu/AvaTest.png"><img class="aligncenter size-full wp-image-1501" src="https://files.adrianistan.eu/AvaTest.png" alt="" width="571" height="151" /></a>El interior de <em>suma.test.js</em> debe importar la función <strong>test</strong> de AVA y las funciones que quiera probar.<br><br>Los tests se definen como una llamada a la función test con la descripción del test y a continuación un callback (asíncrono si queremos) con el objeto que controla los tests (llamado t normalmente). Veamos un ejemplo simple:<br><pre class="lang:js decode:true">import test from "ava";<br>import {suma} from "./operaciones";<br><br>test("Suma",t =&gt; {<br>    t.is(suma(1,2),3);<br>});</pre><br>El objeto t soporta múltiples operaciones, siendo <strong>is</strong> la más básica. Is pide que sus dos argumentos sean iguales entre sí, como Assert.Equal de xUnit.<br><br>Veamos que más soporta Ava.<br><ul><br> 	<li><em>t.pass()</em>: Continúa el test (sigue)</li><br> 	<li><em>t.fail()</em>: Falla el test (no sigue)</li><br> 	<li><em>t.truthy(val)</em>: Continúa el test si val es <em>verdaderoso</em> (usando la lógica de JavaScript) o falla el test</li><br> 	<li><em>t.true(val)</em>: Continúa el test si val es <em>verdadero</em> (más estricto que el anterior) o falla.</li><br> 	<li><em>t.is(val1,val2)</em>: Continúa el test si val1 y val2 son iguales (superficialmente) o falla.</li><br> 	<li><em>t.deepEqual(val1,val2)</em>: Continúa el test si val1 y val2 son iguales (profundamente) o falla.</li><br> 	<li><em>t.throws(funcion)</em>: Ejecuta la función especificada esperando que lance una excepción. Si no lo hace, falla el test. Se puede especificar el tipo de excepción que esperamos en el segundo argumento.</li><br> 	<li><em>t.notThrows(funcion)</em>: Exactamente lo contrario que la anterior.</li><br></ul><br>Y algunas más, pero estas son las esenciales.<br><pre class="lang:js decode:true ">import test from "ava";<br><br>function sum(a,b){<br>    return a+b;<br>}<br><br>function login(username,password){<br>    if(username === null || password === null){<br>        throw new Error("Missing username or password");<br>    }<br>}<br><br>test("Test example: Sum",t =&gt; {<br>    t.is(sum(1,2),3);<br>});<br><br>test("Login fail username null", t =&gt; {<br>    t.throws(()=&gt;{<br>        login(null,"123456");<br>    });<br>});<br>test("Login fail password null", t =&gt; {<br>    t.throws(()=&gt;{<br>        login("username",null);<br>    });<br>});</pre><br>También podemos definir funciones que se ejecuten antes y después de nuestros tests, y una sola vez o con cada test. Podemos usar <strong>test.before, test.beforeEach, test.after</strong> y <strong>test.afterEach</strong> en vez de <strong>test</strong>. Por ejemplo, si tienes una base de datos que necesita inicialización, puedes definir ese código en <strong>test.before</strong> y la limpieza en <strong>test.after</strong>.<br><pre class="lang:js decode:true ">import test from "ava";<br>import db from "../db";<br><br>test.before(async () =&gt; {<br>    // Iniciar el ORM Sequelize<br>    await db.sync();<br>});<br></pre><br>&nbsp;<br><br>Con esto ya podemos hacer tests unitarios, pero no podemos probar la aplicación web al 100%. Entra en acción <strong>supertest</strong> que nos permitirá tener un servidor Express simulado para que los tests puedan probar la aplicación al completo.<br><h2>Supertest</h2><br>Instalamos supertest<br><pre class="lang:default decode:true ">npm install supertest --save-dev</pre><br>En el fichero de test necesitamos crear un objeto de tipo aplicación de Express. Este objeto puede ser el mismo que usas en tu aplicación real o ser una versión simplificada con lo que quieras probar.<br><pre class="lang:js decode:true">import test from "ava";<br>import request from "supertest";<br>import auth from "http-auth";<br>import express from "express";<br><br>function setup(){<br>    const app = express();<br><br>    let basic = auth.basic({<br>        realm: "Upload Password"<br>    }, function (username, password, callback) {<br>        callback(username === "admin" &amp;&amp; password === "123456");<br>    });<br><br>    app.get("/upload",auth.connect(basic),function(req,res){<br>        res.sendFile("upload.html");       <br>    });<br>    return app;<br>}<br><br>test("Página upload requiere autenticación HTTP Basic", async t =&gt; {<br>    let res = await request(setup())<br>        .get("/upload")<br>        .send();<br>    t.is(res.status,401);<br>    t.true(res.header["www-authenticate"] !== undefined);<br>});</pre><br>Aquí la función de test es asíncrona, AVA es compatible con ambos tipos. Supertest es muy completo y permite probar APIs enteras con su sencilla interfaz que junto con AVA, se convierte en algo casi obligatorio para una aplicación que vaya a producción.]]></description>
                <comments>https://blog.adrianistan.eu/usar-ava-para-tests-en-una-api-hecha-en-node-js-y-express</comments>
                <pubDate>Sun, 15 Jul 2018 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>La perlificación de Python</title>
                <link>https://blog.adrianistan.eu/la-perlificacion-de-python</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/la-perlificacion-de-python</guid>
                <description><![CDATA[<blockquote><br><div class="col-xs-12"><br><br>You know, FOX turned into a hardcore sex channel so gradually I didn't even notice<br><br></div></blockquote><br><div class="col-xs-12"><br><p style="text-align: right;"><strong>Marge Simpson, <em>Los Simpson</em></strong></p><br><br></div><br><a href="https://files.adrianistan.eu/Simpsons.jpg"><img class="aligncenter size-full wp-image-1493" src="https://files.adrianistan.eu/Simpsons.jpg" alt="" width="640" height="480" /></a>Recientemente <a href="https://blog.python.org/2018/06/python-3.html">ha salido Python 3.7</a>, con interesantes novedades. También han salido los primeros artículos hablando de <a href="https://hackernoon.com/7-features-proposed-so-far-in-python-3-8-acb0d97c83c8">las novedades que podrá traer Python 3.8</a>. Como muchos ya conoceréis, y si no es así explico, Python funciona a base de PEPs (Python Enhancement Proposal). Cualquier persona puede abrir un PEP, que es un documento que describe la funcionalidad que se quiere añadir/modificar/eliminar. Estas PEPs se discuten y finalmente Guido, creador de Python, las aprueba y se codifican.<br><br>Dentro de las PEP relacionadas con Python 3.8 hay algunas bastante controvertidas que han hecho saltar la voz de alarma. No ha faltado gente que ha opinado que cada vez Python se parece más a Perl. Este proceso habría empezado con Python 3 pero se habría ido haciendo más evidente hasta llegar a hoy. Cada vez con más sintaxis poco utilizada, con más elementos, más cómodo de escribir para el experimentado aunque menos legible si no dominas el lenguaje.<br><br>Y resulta curioso, porque Python es en parte una respuesta a la excesiva complejidad que podían tener los programas hechos en Perl. Su popularidad se debe a que es fácil de aprender y eso parece que ya no está tan claro.<br><br><a href="https://files.adrianistan.eu/Perl.gif"><img class="aligncenter size-full wp-image-1494" src="https://files.adrianistan.eu/Perl.gif" alt="" width="643" height="326" /></a><br><br>Con la introducción de operadores como := o ?? o anotaciones como @dataclass se va, en cierta medida, perdiendo el espíritu original de Python. Y es cierto que otros lenguajes tienen algo similar, pero precisamente Python había sido muy selecto en incluir un conjunto bastante reducido de características, que todo el mundo pudiese dominar. Al final se sacrifica legibilidad y facilidad de aprendizaje por una ergonomía que beneficia a unos pocos en unos casos concretos.<br><h2>Lenguajes de programación barrocos</h2><br><a href="https://files.adrianistan.eu/PlazaUniversidad.jpg"><img class="size-large wp-image-1495" src="https://files.adrianistan.eu/PlazaUniversidad-1024x685.jpg" alt="" width="840" height="562" /></a> Universidad de Valladolid, ejemplo de barroco civil. Foto: https://artevalladolid.blogspot.com<br><br>Python lleva un tiempo entrando en un proceso de perlificación pero en mi opinión no es el único lenguaje que ha entrado en una espiral parecida. Cada vez más lenguajes han pasado del renacimiento, donde se buscaba la consistencia, la simetría, la sencillez sin perder la funcionalidad, hacia el barroco, donde los lenguajes son más recargados, con más elementos sintácticos, que cubren mejor casos concretos, pero que de por sí no son tan esenciales, no cambian aspectos fundamentales del lenguaje y normalmente introducen diversas formas de hacer algo en un mismo lenguaje.<br><br>Veamos más ejemplos: en C++20 se propuso añadir funcionalidad de dibujado 2D a la librería estándar (propuesta que fue rechazada en una historia bastante larga para este post) y se han propuesto conceptos, módulos, comparación de tres vías, reflexión, metaclases,... En C# 8.0 se han propuesto también bastantes cosas como records, tipos non-nullable, interfaces con métodos ya implementados (traits) y rangos. Y eso sin contar con las características que estos dos lenguajes ya tienen, que son bastante más extensos que Python.<br><br><a href="https://files.adrianistan.eu/RetabloSanMiguelSanJulian.jpg"><img class="wp-image-1496 size-large" src="https://files.adrianistan.eu/RetabloSanMiguelSanJulian-790x1024.jpg" alt="" width="790" height="1024" /></a> Retablo lateral de la Iglesia de San Miguel y San Julián (Valladolid). Barroco puro. Foto: https://commons.wikimedia.org/wiki/File:San_Miguel_-_retablo_relicario.jpg<br><br>Hay un <em>horror vacui</em>, horror al vacío, a funcionalidades. Siempre se añade y casi nunca se elimina. ¿Pero es realmente necesario? Es evidente que durante mucho tiempo, los lenguajes evolucionaban de forma muy lenta y muchos de los cambios que han hecho eran necesarios. Desde hace unos años, se ha aumentado la velocidad de los cambios, pero esto no puede seguir así eternamente, porque el ritmo que llevan muchas veces es superior al de los avances en lenguajes de programación y la retrocompatibilidad impide ir quitando cosas al mismo ritmo que se añaden. De este modo, todos los lenguajes que entran en esta espiral crecen y crecen. No se llegan a pulir, simplemente crecen.<br><blockquote><span class="st">La perfección no se alcanza cuando no hay nada más que añadir, sino cuando no hay nada más que quitar</span></blockquote><br><p style="text-align: right;"><strong><span class="st"><em>Antoine de Saint</em>-<em>Exupéry</em></span></strong></p><br>Uno podría comparar esto con lenguajes naturales, como el español o el inglés. En estos idiomas, pese a existir reglas, existen numerosas excepciones. Es posible que lenguajes como Python se estén viendo influenciados por las mismas mecánicas humanas que rigen los lenguajes naturales y que han hecho que existan excepciones. Tendría bastante sentido que ocurriese así. Pero personalmente, me gustaría que aprender Python no costase tanto como aprender alemán o francés.<br><h2>Los procesos comunitarios</h2><br>Para mí, gran parte de esta sobrecarga viene dada por los procesos comunitarios. En un proceso comunitario como PEP, comité de C++ o similares es mucho más fácil añadir que eliminar. En C++ la situación ha llegado a un punto en el que Bjarne Stroustrup, creador de C++, ha pedido que se relajen con las propuestas en <em><a href="http://open-std.org/JTC1/SC22/WG21/docs/papers/2018/p0977r0.pdf">Remember the Vasa!</a>, </em>en honor a un bonito barco sueco que se hundió por exceso de carga. No tanto por su peso, sino por su disposición y las reformas que hubo que hacer para que todo encajase.<br><br><a href="https://files.adrianistan.eu/Vasa.jpg"><img class="size-full wp-image-1497" src="https://files.adrianistan.eu/Vasa.jpg" alt="" width="810" height="634" /></a> El Vasa fue recuperado después de su naufragio y se expone en Estocolmo. Foto: https://rachelannsblog.wordpress.com/2016/08/03/set-sail-set-at-the-bottom-of-the-sea/<br><br>Es por ello que las personas encargadas de elegir que se añade al lenguaje o no deben de ser muy conscientes de lo que supone, ya que una vez se introduzca, va a ser casi imposible eliminarlo.<br><br>No estoy en contra de añadir nuevas características (¡al contrario!) pero se debe respetar la visión de conjunto del lenguaje, que todo cuadre y esté cohesionado. No siempre tener más es mejor.<br><h2>¿Te ha gustado el artículo?</h2><br>Si es así, puedes ayudar al blog. Dentro de unos días es el Amazon Prime Day. Como muchos de vosotros seguro que os compraréis algo, no quiero dejar la oportunidad de deciros que este blog tiene enlace de referidos y que por cada cosa que compréis con el enlace, me llevo una pequeña parte (a vosotros no os va a costar más caro).<br><br><a href="https://www.amazon.es//ref=as_li_ss_tl?ie=UTF8&amp;linkCode=ll2&amp;tag=adrarrcal-21&amp;linkId=31c59cde3f5f4b573208e29a0a776f7d">Enlace Amazon.es</a><br><br>Será muy bien recibido<br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/la-perlificacion-de-python</comments>
                <pubDate>Tue, 10 Jul 2018 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>¿Cómo funcionan los sistemas basados en inodos?</title>
                <link>https://blog.adrianistan.eu/como-funcionan-los-sistemas-basados-en-inodos</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/como-funcionan-los-sistemas-basados-en-inodos</guid>
                <description><![CDATA[Después de ver como funcionan de forma genérica los <a href="https://blog.adrianistan.eu/2018/06/11/como-funcionan-los-sistemas-de-archivos-basados-en-fat/">sistemas FAT</a>, saltamos a los sistemas de inodos. Estos se han usado tradicionalmente en sistemas UNIX (UFS, ext2), así que tradicionalmente ha existido una cierta rivalidad  en las redes entre FAT e inodos similar a la de Windows/Linux. Lo cierto es que a nivel técnico cada uno tiene fortalezas y debilidades.<br><h2>Partición</h2><br>Tomando la base de FAT, una partición de un sistema basado en inodos también contiene un sector de arranque y un superbloque con metadatos. También es necesario un bloque dedicado al directorio raíz presente en el disco. Además es necesario espacio para almacenar todos los inodos y un mapa de bits de espacio libre que en FAT no hacía falta, ya que la propia tabla nos indicaba que bloques del disco estaban libres.<br><h2>Los inodos</h2><br>¿Qué es inodo te podrás preguntar? Es una estructura de datos, su nombre proviene de <em>index node</em> y es que los inodos no son más que índices, que almacenan los números de bloque de las diferentes partes del archivo. Además, contienen metadatos como permisos, propietario, tamaño, fecha de modificación, referencias, tipo de fichero (directorio, archivo, enlace duro, enlace blando,...) <strong>salvo</strong> el nombre del propio archivo, que en ningún sitio del inodo aparece.<br><br><a href="https://files.adrianistan.eu/Dibujo-sin-título8.png"><img class="aligncenter size-full wp-image-1472" src="https://files.adrianistan.eu/Dibujo-sin-título8.png" alt="" width="835" height="459" /></a><br><br>Este sistema tiene una ventaja de rendimiento respecto a FAT en cuanto al acceso aleatorio a los archivos, ya que es mucho más rápido de esta forma que con FAT. En FAT para hacer lo mismo tenemos que ir recorriendo la tabla arriba y abajo siguiendo los números de bloque hasta encontrar el bloque deseado.<br><br>Normalmente un inodo tiene un tamaño fijo, lo que implica que el índice no se puede alargar hasta el infinito. Esto hace que haya un número limitado de bloques que puede usar un archivo y por ende, que haya un tamaño máximo de archivo que no es muy elevado. Para solventar este problema hay varias soluciones. El enfoque de UFS y de ext2/3/4 consiste en múltiples niveles de indexado progresivo.<br><br>Esto significa que los primeros bloques son números de bloque directos pero los siguientes son números de bloque que llevan a tablas de inodo secundarias que ya sí, hacen referencia al archivo real. Más adelante los números de bloque hacen referencias a tablas de inodo secundarias que a su vez llaman a tablas de inodos terciarias.<br><br><a href="https://files.adrianistan.eu/Dibujo-sin-título9.png"><img class="aligncenter size-large wp-image-1473" src="https://files.adrianistan.eu/Dibujo-sin-título9-1024x391.png" alt="" width="840" height="321" /></a>Esto provoca algo que en principio puede parecer paradójico y es que es más lento leer una zona final de un archivo que una zona del principio, aunque en una lectura secuencial no se nota demasiado.<br><br>Otra solución a esto es enlazar los inodos a modo de lista enlazada añadiendo al final de cada inodo un número de inodo al que continuar la lectura de índices.<br><h2>Localización de los inodos</h2><br>Los inodos se crean cuando se formatea el disco y permanecen en un estado libre. No se pueden añadir más inodos ni quitar inodos y no puede haber más archivos y directorios que inodos por esta misma razón. Esto es una desventaja respecto a FAT, ya que en FAT puede haber tantos archivos como bloques haya/entradas tenga la tabla. En sistemas de inodos como ext2/3/4 puede ocurrir que no queden inodos libres pero haya todavía bloques libres, dejando todo ese espacio inutilizado (aunque en realidad lo podrían usar los archivos existentes si creciesen).<br><br>Los inodos se pueden ubicar de dos formas distintas. Un enfoque consiste en ponerlos al principio del disco todos juntos. Otro enfoque, el que sigue ext3, consiste en dividir el disco en 4 zonas y ubicar inodos en cada inicio de zona. La idea es que los inodos de esa zona usen bloques de esa zona y de esta forma reducir los desplazamientos de las cabezas lectoras del disco (en unidades SSD esto da completamente igual como podréis suponer).<br><h2>Gestión del espacio libre</h2><br>Una de las grandes ventajas de FAT era que la tabla podía mantener a la vez un listado de bloques libres, listos para ser usados. Con inodos no tenemos esa facilidad y tenemos que recurrir a otros tipos de estructura. Aquí hay muchos planteamientos siendo el más común el mapa de bits. El mapa de bits es una estructura que se compone de un listado de bits. Cada bit corresponde a un bloque y dependiendo de si el bit es 1 o 0 podemos saber si el bloque está libre u ocupado.<br><br><a href="https://files.adrianistan.eu/Dibujo-sin-título10.png"><img class="aligncenter size-full wp-image-1474" src="https://files.adrianistan.eu/Dibujo-sin-título10.png" alt="" width="936" height="338" /></a>Como veis, los sistemas basados en inodos tienen que mantener varias estructuras de datos de forma paralela. Esto aumenta las probabilidades de inconsistencias en el sistema. Por este motivo, muchos sistemas más avanzados como ext4 mantienen un <strong>journal</strong> o diario.<br><h2>Fragmentación</h2><br>Veamos como se comportan los sistemas de inodos ante los tres tipos de fragmentación. Respecto a la <strong>fragmentación interna</strong>, que es aquella que sucede cuando asignamos espacio de más a archivos que no necesitan tanto, tiene los mismos valores que FAT, ya que los bloques siguen pudiendo pertenecer únicamente a un archivo. Realmente, para mejorar la fragmentación interna tenemos que saltar a sistemas como XFS o JFS.<br><br>La fragmentación externa de los sistemas basados en inodos es la misma que la de FAT, 0, ya que todo se asigna mediante bloques del mismo tamaño.<br><br>Respecto a la fragmentación de datos, históricamente las implementaciones como UFS o ext2/3 se han comportado relativamente bien, aunque a nivel teórico nada impide que se comporten igual de mal que FAT, no existen mecanismos preventivos. Ext4 por contra, sí que tiene mecanismos de prevención (<em>delayed allocation</em>).<br><h2>Directorios y enlaces duros</h2><br>En los sistemas basados en inodos los directorios son más sencillos que en FAT, ya que no tienen que almacenar los metadatos del archivo en cuestión. En un fichero de directorio simplemente se almacena el nombre y el número de inodo correspondiente.<br><br><a href="https://files.adrianistan.eu/Dibujo-sin-título11.png"><img class="aligncenter size-full wp-image-1476" src="https://files.adrianistan.eu/Dibujo-sin-título11.png" alt="" width="700" height="333" /></a>Aquí surge un concepto muy interesante, el de <strong>enlaces duros</strong>. Un enlace duro no es más que otra entrada de directorio que apunta a un mismo inodo. Realmente desde el punto de vista del sistema de archivos no existe un fichero original y su enlace. Simplemente dos entradas diferentes apuntando al mismo inodo. ¿Y cómo reacciona esto a los borrados? Imagina la siguiente situación: tengo un fichero y creo un enlace duro. Borro el fichero original y su inodo se libera. Ahora creo otro archivo y reutiliza ese mismo inodo que estaba libre. ¡Ahora el enlace duro apunta a un contenido totalmente distinto sin darse cuenta! Para prevenir esto, los inodos <strong>no se liberan</strong> hasta que no quede ninguna entrada de directorio apuntando a ellos. Para eso sirve el campo referencias dentro del inodo, para llevar la cuenta de cuántas veces se hace referencia al inodo en el sistema de archivos. Cuando el valor de referencias llega a cero, se puede liberar sin problema.<br><h2>Conclusiones</h2><br>Los sistemas basados en inodos son conceptualmente algo más complejos que los basados en FAT. Comparten además limitaciones comunes, usan más espacio de disco, aunque suelen ser más rápidos.<br><br>Actualmente existen sistemas basados en inodos mucho más avanzados y complejos que UFS y ext2/3/4 y que mejoran este sistema de forma sustancial. Por ejemplo en XFS los inodos tienen una estructura totalmente distinta donde se indica un bloque de inicio y una longitud en número de bloques contiguos que pertenecen al archivo. Muchos sistemas además usan estructuras de árbol B+ como pueden ser ZFS o Btrfs.]]></description>
                <comments>https://blog.adrianistan.eu/como-funcionan-los-sistemas-basados-en-inodos</comments>
                <pubDate>Thu, 14 Jun 2018 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>¿Cómo funcionan los sistemas de archivos basados en FAT?</title>
                <link>https://blog.adrianistan.eu/como-funcionan-los-sistemas-de-archivos-basados-en-fat</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/como-funcionan-los-sistemas-de-archivos-basados-en-fat</guid>
                <description><![CDATA[Voy a dedicar unas entradas en el blog a hablar del funcionamiento de los sistemas de archivos, un componente fundamental en la gran mayoría de sistemas informáticos. Voy a empezar con los basados en FAT sin centrarme en ninguno en concreto (FAT16, FAT32, exFAT,...). Intentaré escribir también sobre inodos y mecanismos más raros y avanzados.<br><br>En esencia un sistema de archivos es un método ordenado que permite guardar datos sobre un soporte físico para luego poder acceder a ellos. Históricamente ha habido muchos enfoques a este problema: los sistemas más usados usan archivos, directorios y enlaces.<br><h2>Bloques y sectores: la división del disco</h2><br>Esta parte es común a muchos sistemas de archivos, tanto FAT como inodos, como otros. A nivel físico los dipositivos están divididos. En el caso del disco duro, el dispositivo de almacenamiento más común, los discos se dividen en sectores de N bytes, según parámetros de la fabricación. Estos sectores cuentan con código de control de errores incorporado y todo ello es manejado por la controladora de disco que opera ajena al sistema operativo. Los sistemas de archivos dividen a su vez el disco en bloques. Es importante dejar claro que bloque no es igual a sector. El bloque es una división que hace el sistema de archivos y los sectores los hace la controladora de disco. Usualmente un bloque equivale a varios sectores, aunque la equivalencia real depende del sistema de archivos en concreto.<br><h2>Algunos bloques especiales: boot y superbloque y raíz</h2><br>Antes de entrar en el mecanismo específico de FAT es necesario comentar que en un disco existirán dos bloques de vital importancia ajenos a FAT. El primero es el bloque de boot o arranque (también llamado MBR). Normalmente situado en el sector 0 del disco duro, contiene código para iniciar el sistema operativo. El superbloque suele ser el bloque siguiente, contiene metadatos del sistema de archivos (por ejemplo, puede decir que usamos FAT con bloques de 32KiB, etc). Además en FAT es necesario que exista un fichero <strong>siempre</strong>, el fichero del directorio raíz.<br><h2>Directorios</h2><br>Los directorios o carpetas son archivos como otros cualquiera, solamente que en sus metadatos se indica que es un directorio y no un fichero corriente. Los ficheros de directorios son los encargados de almacenar los metadatos de los ficheros (paras saber si son ficheros, otros directorios, su fecha de modificación, propietario y tamaño entre otros) que se encuentran en el directorio así como una referencia al bloque.<br><h2><a href="https://files.adrianistan.eu/Dibujo-sin-título1.png"><img class="aligncenter size-full wp-image-1456" src="https://files.adrianistan.eu/Dibujo-sin-título1.png" alt="" width="807" height="337" /></a>Un fichero en FAT</h2><br>Vamos al asunto. Supongamos que en un determinado bloque N tenemos un fichero. Este bloque es de 64 KiB. Si un fichero ocupa menos de 64 KiB, perfecto, todos los datos entran en el bloque. Simplemente ajustamos los metadatos de tamaño con el valor correcto y el resto del espacio que queda en el bloque queda inutilizado.<br><br>Este espacio perdido se denomina <strong>fragmentación interna</strong> y dependiendo de los datos que se almacenen en un disco duro, el porcentaje de pérdida puede ser mayor o menor. Evidentemente si tenemos bloques muy grandes y ficheros muy pequeños perderemos mucho espacio debido a la fragmentación interna. Tener bloques muy pequeños y ficheros muy grandes también es problemático pero por otros motivos.<br><blockquote>Tipos de fragmentación<br><br>En un sistema de archivos existen 3 tipos de fragmentación: interna, externa y de datos. La interna se refiere al espacio perdido en bloques asignados a ficheros que no están llenos por completo. La externa se refiere al espacio que perdemos por no tener un espacio libre contiguo lo suficientemente grande como para guardar el fichero allí. Ningún sistema FAT o de inodos tiene fragmentación externa al usar todos bloques de tamaño predefinido. Por último la fragmentación de datos, o fragmentación a secas, se refiere a que los bloques asignados estén contiguos o no. Esto tiene implicaciones a nivel de rendimiento pero no al número de bytes que se vuelven inútiles como los otros dos tipos de fragmentación.</blockquote><br>¿Pero qué pasa si el contenido de nuestro fichero no puede entrar en el tamaño de bloque? Aquí viene la gracia de FAT, la File-Allocation-Table. La FAT es una tabla compuesta por entradas que indican el siguiente bloque del archivo. La tabla está indexada por bloque y además de indicar cuál es el siguiente bloque del archivo también indica si el archivo acaba ahí o si ese bloque está libre y puede usarse para un archivo nuevo.<br><br><a href="https://files.adrianistan.eu/Dibujo-sin-título4.png"><img class="aligncenter size-full wp-image-1460" src="https://files.adrianistan.eu/Dibujo-sin-título4.png" alt="" width="709" height="394" /></a>En la foto el archivo /home/user/hola.txt tiene una longitud menor al tamaño de bloque. Así que miramos en la FAT la entrada 150 y efectivamente comprobamos que no hay bloque siguiente ya que es un <em>End-of-File</em>.<br><br><a href="https://files.adrianistan.eu/Dibujo-sin-título5.png"><img class="aligncenter size-full wp-image-1461" src="https://files.adrianistan.eu/Dibujo-sin-título5.png" alt="" width="764" height="451" /></a><br><br>Pongamos un ejemplo con un archivo largo. Cada celda de la tabla correspondiente al índice del bloque actual indica el siguiente bloque a leer. Estos bloques pueden estar en cualquier parte. Si en un disco duro muchos ficheros tienen los bloques muy dispersos, se dice que necesita ser <strong>desfragmentado</strong>.<br><h2>Conclusiones</h2><br>FAT es muy rápido si la tabla FAT consigue ser cargada en memoria, ya que obtener los datos necesarios de ella será muy rápido. Desgraciadamente, dependiendo del tamaño del disco duro y del tamaño de los bloques, esta tabla puede ocupar mucho y ser impráctico.<br><br>El mecanismo FAT como vemos es simple pero efectivo. Dispone de una fragmentación interna limitada y carece de fragmentación externa. Un inconveniente es que FAT cuando busca un archivo necesita empezar siempre desde el directorio raíz. Esto implica accesos cada vez más lentos a medida que vayamos haciendo carpetas, aunque con sistemas de caché se puede reducir.<br><br>El esquema FAT tampoco impone restricciones propiamente dichas al número archivos de un sistema ni a su tamaño. Sin embargo en la práctica suelen existir límites.<br><br><em>Edito: He explicado mejor que significa la fragmentación y sus tipos</em>]]></description>
                <comments>https://blog.adrianistan.eu/como-funcionan-los-sistemas-de-archivos-basados-en-fat</comments>
                <pubDate>Mon, 11 Jun 2018 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Bindings entre Rust y C&#x2F;C++ con bindgen</title>
                <link>https://blog.adrianistan.eu/bindings-rust-c-bindgen</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/bindings-rust-c-bindgen</guid>
                <description><![CDATA[Rust es un lenguaje con muchas posibilidades pero existe mucho código ya escrito en librerías de C y C++. Código que ha llevado mucho tiempo, que ha sido probado en miles de escenarios, software maduro y que no compensa reescribir. Afortunadamente podemos reutilizar ese código en nuestras aplicaciones Rust a través de bindings. Los bindings no son más que trozos de código que sirven de pegamento entre lenguajes. Esto es algo que se ha podido hacer <em>desde siempre</em> pero dependiendo de la librería podía llegar a ser muy tedioso. Afortunadamente tenemos <a href="https://github.com/rust-lang-nursery/rust-bindgen"><strong>bindgen</strong></a>, un programa que permite generar estos bindings de forma automática analizando el código de la librería de C o C++.<br><br><a href="https://files.adrianistan.eu/glue-304256_1280.png"><img class="aligncenter wp-image-1452 size-medium" src="https://files.adrianistan.eu/glue-304256_1280-279x300.png" alt="" width="279" height="300" /></a><br><br>En este post veremos como usar <strong>SQLite</strong> desde Rust usando bindgen.<br><h2>Instalando bindgen</h2><br>En primer lugar necesitamos tener instalado <strong>Clang 3.9</strong> o superior. En Ubuntu o Debian necesitamos estos paquetes:<br><pre class="lang:default decode:true ">sudo apt install llvm-3.9-dev libclang-3.9-dev clang-3.9</pre><br>Para el resto de plataformas puedes descargar el binario desde la <a href="https://releases.llvm.org/download.html">página de descargas de LLVM</a>.<br><br>Bindgen permite dos modos de uso: línea de comandos o desde el código Rust. El más habitual es desde código Rust pero antes veremos el modo en línea de comandos.<br><h2>Modo línea de comandos</h2><br>Para bindings sencillos podemos usar el modo línea de comandos. Instalamos binden con Cargo:<br><pre class="lang:default decode:true">cargo install bindgen</pre><br>Su uso es muy sencillo:<br><pre class="lang:default decode:true ">bindgen /usr/include/sqlite3.h -o sqlite.rs</pre><br>Simplemente indicamos el fichero de cabecera que queremos traducir y su correspondiente fichero de salida en Rust. Este fichero será el pegamento. Vamos a crear un programa que use este pegamento:<br><pre class="lang:rust decode:true">mod sqlite;<br><br>use sqlite::{sqlite3_open, sqlite3_exec, sqlite3_close, SQLITE_OK};<br>use std::ffi::CString;<br>use std::ptr::{null_mut,null};<br><br>fn main(){<br>    let mut db = null_mut();<br>    let database_name = CString::new("test.db").unwrap().into_raw();<br>    let sql = CString::new("<br>    CREATE TABLE contacts (name TEXT, tel TEXT);<br>    INSERT INTO contacts VALUES ('Adrian','555-555-555');").unwrap().into_raw(); <br>    let mut error_msg = null_mut();<br>    unsafe{<br>        sqlite3_open(database_name,&amp;mut db);<br>        let rc = sqlite3_exec(db,sql,None,null_mut(),&amp;mut error_msg);<br>        if rc != SQLITE_OK as i32 {<br>            let error = CString::from_raw(error_msg);<br>            println!("ERROR: {}",error.into_string().unwrap());<br>        }<br>        sqlite3_close(db);<br>    }<br>}</pre><br>Como se puede apreciar, las llamadas al módulo de pegamento de hacen desde un bloque <strong>unsafe</strong> ya que se van a usar punteros al estilo C, de forma insegura. <a href="https://blog.adrianistan.eu/2017/10/12/diversion-punteros-rust-bloques-unsafe/">Hace tiempo escribí sobre ello</a> así que voy a saltarme esa parte.<br><br>Compilamos enlazando de forma manual <strong>libsqlite3</strong> de la siguiente forma:<br><pre class="lang:default decode:true ">rustc main.rs -lsqlite3</pre><br>Si todo va bien, compilará aunque con numerosos warnings. En principio no son importantes.<br><br>Ahora si ejecutamos el programa resultante debería crear una base de datos nueva con una tabla contacts y los datos insertados.<br><br><a href="https://files.adrianistan.eu/SQliteBindgen.png"><img class="aligncenter size-full wp-image-1451" src="https://files.adrianistan.eu/SQliteBindgen.png" alt="" width="725" height="266" /></a>¡Hemos conseguido llamar a una librería de C desde Rust y no hemos escrito ningún binding!<br><h2>Build.rs</h2><br>El sistema anterior funciona, pero no es lo más práctico, además no usa Cargo que es el sistema estándar de construcción de programas y crates un Rust. Lo habitual es dejar este proceso automatizado en el fichero <strong>build.rs</strong> que se ejecuta con Cargo.<br><br>Lo primero es añadir la siguiente línea al fichero Cargo.toml:<br><pre class="lang:default decode:true ">[build-requires]<br>bindgen = "0.26.3"</pre><br>El siguiente paso consiste en crear un archivo cabecera de C que a su vez haga referencia a todos los archivos de cabecera que necesitamos. En el caso de SQLite es bastante simple.<br><pre class="lang:c decode:true ">#include &lt;sqlite3.h&gt;</pre><br>Y lo llamamos wrapper.h<br><br>Ahora viene lo interesante. Dentro de build.rs creamos un programa que gracias a la API de bindgen haga lo mismo que la línea de comandos.<br><pre class="lang:rust decode:true " title="build.rs">extern crate bindgen;<br><br>use std::env;<br>use std::path::PathBuf;<br><br>fn main() {<br>    // indicamos al linker que necesitamos sqlite3<br>    println!("cargo:rustc-link-lib=sqlite3");<br><br><br>    let bindings = bindgen::Builder::default()<br>        .header("wrapper.h")<br>        .generate()<br>        .expect("Unable to generate bindings");<br><br>    // escribir los bindings en $OUT_DIR/bindings.rs<br>    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());<br>    bindings<br>        .write_to_file(out_path.join("bindings.rs"))<br>        .expect("Couldn't write bindings!");<br>}</pre><br>El archivo build.rs debe estar en la misma carpeta que Cargo.toml para que funcione.<br><br>Finalmente para hacer accesible nuestros bindings creamos un módulo llamado sqlite.rs con el siguiente contenido.<br><pre class="lang:rust decode:true " title="sqlite.rs">#![allow(non_upper_case_globals)]<br>#![allow(non_camel_case_types)]<br>#![allow(non_snake_case)]<br><br>include!(concat!(env!("OUT_DIR"), "/bindings.rs"));</pre><br>Lo único que hace es desactivar unos warnings molestos e incluir el texto de bindings.rs al completo.<br><br>Una vez hecho esto podemos usar desde el programa principal la librería de la misma forma que hemos visto antes.<br><br>Ahora podríamos usar estos bindings directamente en nuestro programa o <em>rustizarlos</em> (darles una capa segura alrededor e idiomática) y subirlo a Crates.io.<br><br><a href="https://github.com/aarroyoc/blog-ejemplos/tree/master/sqlite-bindgen"><em>El código del post está en GitHub</em></a>]]></description>
                <comments>https://blog.adrianistan.eu/bindings-rust-c-bindgen</comments>
                <pubDate>Thu, 07 Jun 2018 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Ayuda a la ciencia desde tu casa con BOINC</title>
                <link>https://blog.adrianistan.eu/ayuda-a-la-ciencia-desde-tu-casa-con-boinc</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/ayuda-a-la-ciencia-desde-tu-casa-con-boinc</guid>
                <description><![CDATA[El invento de los ordenadores se lo debemos a los científicos, que buscando maneras de acelerar sus cálculos decidieron crear máquinas que los automatizaban. A partir de esos desarrollos la informática se ha diversificado en muchas más ramas, sin embargo la necesidad de procesar grandes cantidades de datos sigue siendo de vital importancia para la investigación en física, química, medicina y demás ramas de la ciencia.<br><br><a href="https://files.adrianistan.eu/Two_women_operating_ENIAC.gif"><img class="size-full wp-image-1448" src="https://files.adrianistan.eu/Two_women_operating_ENIAC.gif" alt="" width="640" height="422" /></a> El ENIAC, uno de los primeros ordenadores construidos<br><br>Este tipo de cálculos se realizan normalmente en supercomputadoras en clúster que trabajan en paralelo. Sin embargo en muchas ocasiones, nada impide que cada nodo esté separado kilómetros de distancia. Ahí nace el proyecto BOINC (siglas de Berkeley Open Infrastructure for Network Computing). BOINC es un sistema de computación distribuida en Grid. Esto quiere decir que el cómputo necesario para un proyecto se divide en paquetes. Cada paquete se manda a un ordenador que forma parte de la red. Cada ordenador se encarga de procesarlo. Cuando los cálculos han finalizado, los envía al servidor central que le entrega a su vez al ordenador un paquete con cómputos nuevos por realizar.<br><br><a href="https://files.adrianistan.eu/BOINC.png"><img class="aligncenter size-large wp-image-1447" src="https://files.adrianistan.eu/BOINC-1024x428.png" alt="" width="840" height="351" /></a>De todos los sistemas de computación distribuida en Grid, BOINC es sin duda el más popular. La potencia de cálculo de BOINC es de media 23 petaflops, algo lejos del mayor supercomputador (Sunway TaihuLight, de China, con 93 petaflops) pero superior a muchos supercomptadores como MareNostrum, el mayor supercomputador de España y que tiene 11 petaflops.<br><br><a href="https://files.adrianistan.eu/MareNostrum4.jpg"><img class="size-full wp-image-1445" src="https://files.adrianistan.eu/MareNostrum4.jpg" alt="" width="990" height="558" /></a> Supercomputador Mare Nostrum 4<br><br>Cualquier ordenador del mundo puede colaborar con la red BOINC aportando su potencia a la red y calculando datos muy importantes para las investigaciones científicas.<br><h2>¿Cómo puedo usar BOINC y ayudar?</h2><br>Es muy sencillo. BOINC dispone de versiones para Windows, Mac, Linux y Android, así como versiones optimizadas con CUDA (Nvidia), OpenCL y existen versiones de terceros para Solaris, HP-UX, FreeBSD y otros sistemas.<br><br>El primer paso es descargar BOINC desde <a href="https://boinc.berkeley.edu/">https://boinc.berkeley.edu/</a> . Si estás en Android puedes ir directamente a Google Play y descargar BOINC desde allí.<br><br>El cliente BOINC se divide en dos partes: una que se dedica a calcular y otra que es una interfaz de usuario que nos permite controlar la parte que calcula (BOINC Manager). La ventaja de esto es que son programas independientes y no hace falta tener la interfaz de usuario abierta para seguir calculando.<br><h2>Proyectos</h2><br>Lo primero que tenemos que hacer nada más abrir el BOINC Manager es registrarnos en un proyecto para que nos empiece a mandar tareas.<br><br><a href="https://files.adrianistan.eu/ProyectosBOINC.png"><img class="aligncenter size-full wp-image-1442" src="https://files.adrianistan.eu/ProyectosBOINC.png" alt="" width="722" height="620" /></a>Algunos de los más populares son:<br><ul><br> 	<li><a href="https://setiathome.berkeley.edu/">SETI@Home</a>. Es el proyecto que busca vida extreterrestre por el universo analizando las señales de radiotelescopios como el de <a href="https://es.wikipedia.org/wiki/Radiotelescopio_de_Arecibo">Arecibo</a>. Fue uno de los primeros proyectos de BOINC y uno de los más populares.</li><br> 	<li><a href="https://boinc.bakerlab.org/rosetta/">Rosetta@Home</a>: Este proyecto busca nuevos tipos de proteínas así como su aplicación en enfermedades como la malaria y el alzheimer,.</li><br> 	<li><a href="https://einsteinathome.org/">Einstein@Home</a>: Este proyecto busca detectar ondas gravitacionales desde los datos recogidos de observatorios como <a href="https://www.ligo.caltech.edu/">LIGO</a>.</li><br> 	<li><a href="https://www.primegrid.com/">PrimeGrid</a>: Busca nuevos números primos, esenciales en campos como la de la criptografía.</li><br> 	<li><a href="https://www.climateprediction.net/">ClimatePrediction</a>: Busca modelos climáticos mejorados que permitan mejorar nuestra capacidad de predicción del clima</li><br> 	<li><a href="http://milkyway.cs.rpi.edu/milkyway/">MilkyWay@Home</a>: Investiga modelos tridimensionales sobre nuestra galaxia, la vía láctea</li><br> 	<li><a href="https://lhcathome.cern.ch/lhcathome/">LHC@Home</a>: Tareas relacionadas con la investigación en física de partículas con datos generados por el <a href="https://home.cern/topics/large-hadron-collider">LHC</a>.</li><br> 	<li><a href="http://asteroidsathome.net/boinc/">Asteroids@Home</a>: Amplía nuestro conocimiento sobre los billones de asteroides que hay en el universo, analizando los que podemos ver</li><br></ul><br>Para registrarnos indicamos nuestro correo y nuestra contraseña y automáticamente todo empezará a funcionar. ¡No hay que hacer nada más! BOINC se encargará de todo automáticamente.<br><br><a href="https://files.adrianistan.eu/LoginBOINC.png"><img class="aligncenter size-full wp-image-1443" src="https://files.adrianistan.eu/LoginBOINC.png" alt="" width="733" height="633" /></a>Desde la interfaz podemos parar los cálculos en cualquier momento pulsando <em>Suspend</em> o <em>Pausar</em>.<br><h2><a href="https://files.adrianistan.eu/MainBOINC.png"><img class="aligncenter size-full wp-image-1444" src="https://files.adrianistan.eu/MainBOINC.png" alt="" width="451" height="717" /></a>BOINC Credits</h2><br>En recompensa a nuestro trabajo se nos dan BOINC Credits. La cantidad de BOINC Credits es proporcional a los gigaflops que hayamos aportado. Estos créditos no sirven realmente para nada. Únicamente para fardar ante tus amigos. Y es que hay gente realmente picada y existen equipos de gente que se unen para superar a otros equipos. Los BOINC Credits también sirven para realizar benchmarks de los ordenadores. Puedes consultar tus BOINC Credits desde BOINC Manager o desde webs externas como BOINC Stats.<br><br><img class="size-large" src="https://boincstats.com/signature/-1/user/37365905606/sig.png" width="355" height="162" /> Mis estadísticas en BOINC]]></description>
                <comments>https://blog.adrianistan.eu/ayuda-a-la-ciencia-desde-tu-casa-con-boinc</comments>
                <pubDate>Sat, 26 May 2018 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Fabric, automatiza tareas en remoto</title>
                <link>https://blog.adrianistan.eu/fabric-automatiza-tareas-en-remoto</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/fabric-automatiza-tareas-en-remoto</guid>
                <description><![CDATA[En la medida de lo posible tenemos que automatizar todo lo posible. No solo reducimos tiempo sino que reducimos errores, y a la vez, estamos documentando un proceso. Muchas de estas ideas ya las dejé claras en <a href="https://blog.adrianistan.eu/2015/08/04/haz-scripts/">¡Haz scripts!</a> Sin embargo, hay situaciones donde es más complicada esta situación, como por ejemplo, trabajar con máquinas remotas. Aquí entra <a href="http://docs.fabfile.org/en/2.0/index.html">Fabric</a> en acción, una librería para Python que permite automatizar procesos en remoto a través de SSH.<br><br>Originalmente Fabric, compatible únicamente con Python 2, funcionaba a través de unos archivos llamados <em>Fabfile</em> que se ejecutaban con el comando <em>fab</em>. Este mecanismo ha sido transformado por completo en Fabric 2, que ha salido hace nada. Yo ya he hecho la transición y puedo decir que el nuevo sistema, más enfocado a ser una librería sin más, es mucho más práctico y útil que el anterior.<br><br><a href="https://files.adrianistan.eu/Fabric.png"><img class="aligncenter size-full wp-image-1435" src="https://files.adrianistan.eu/Fabric.png" alt="" width="150" height="117" /></a><br><h2>Instalando Fabric</h2><br>Voy a usar <a href="https://docs.pipenv.org/">Pipenv,</a> que es la manera recomendada de gestionar proyectos en Python actualmente.<br><pre class="lang:default decode:true">pipenv install fabric==2.0.1</pre><br>Y accedemos al virtualenv con:<br><pre class="lang:default decode:true">pipenv shell</pre><br><h2>Conexiones</h2><br>El elemento fundamental de Fabric a partir de la versión 2 son las conexiones. Estos objetos representan la conexión con otra máquina y podemos:<br><ul><br> 	<li>ejecutar comandos en el shell de la otra máquina, <strong>run, sudo.</strong></li><br> 	<li>descargar archivos desde la máquina remota a local, <strong>get</strong></li><br> 	<li>subir archivos de local a remoto, <strong>put</strong></li><br> 	<li>hacer forwarding, <strong>forward_local</strong>, <strong>forward_remote</strong>.</li><br></ul><br>Para iniciar una conexión necesitamos la dirección de la máquina y alguna manera de identificarnos. En todo el tema de la autenticación Fabric delega el trabajo en Paramiko, que soporta gran variedad de opciones, incluida la opción de usar gateways.<br><br>Vamos a mostrar un ejemplo, en este caso, de autenticación con contraseña:<br><pre class="lang:python decode:true ">from fabric import Connection<br>from getpass import getpass<br><br>password = getpass(prompt="Password for Numancia: ")<br>numancia = Connection(host="192.168.0.158",user="roma",connect_kwargs={"password" : password})</pre><br>El objeto creado con Connection ya lo podemos usar. Habitualmente no es necesario cerrar la conexión aunque en casos extremos puede ser necesario, una manera es llamando a <strong>close</strong>, otra es con un bloque <strong>with.</strong><br><h2>Tareas</h2><br>Una vez que ya tenemos la conexión podemos pasárselo a diferentes funciones, cada una con una tarea distinta. Veamos algunos ejemplos.<br><h4>Actualizar con apt</h4><br><pre class="lang:python decode:true">def update(cxn):<br>    cxn.run("sudo apt update")<br>    cxn.sudo("apt upgrade -y")</pre><br>Los comandos que necesiten sudo pueden necesitar una pseudo-terminal para preguntar la contraseña (si es que la piden, no todos los servidores SSH piden contraseña al hacer sudo). En ese caso añadimos, pty=True.<br><pre class="lang:python decode:true ">def update(cxn):<br>    cxn.run("sudo apt update",pty=True)<br>    cxn.run("sudo apt upgrade -y",pty=True)</pre><br>&nbsp;<br><h4>Backup de Mysql/Mariadb y cifrado con gpg</h4><br><pre class="lang:default decode:true">def backup_sql(cxn):<br>    date = time.strftime("%Y%m%d%H%M%S")<br>    filename = "/tmp/blog-backup-"+date+".sql.gz"<br><br>    cxn.run("sudo mysqldump blog | gzip &gt; "+filename)<br>    local_filename = "backups/"+os.path.basename(filename)<br>    cxn.get(filename,local_filename)<br>    cxn.run("sudo rm "+filename)<br>    os.system("gpg --cipher-algo AES256 -c "+local_filename)<br>    os.remove(local_filename)</pre><br><em>Requiere que MySQL/MariaDB esté configurado con la opción de autenticación POSIX (modo por defecto en el último Ubuntu)</em><br><br>Como véis, usar Fabric es muy sencillo, ya que apenas se diferencia de un script local.<br><br><a href="https://files.adrianistan.eu/FabricLogin.png"><img class="aligncenter size-full wp-image-1436" src="https://files.adrianistan.eu/FabricLogin.png" alt="" width="716" height="254" /></a><br><br>Si queremos obtener el resultado del comando, podemos simplemente asignar a una variable la evaluación de los comandos run/sudo.<br><pre class="lang:python decode:true">def isLinux(cxn):<br>    result = cxn.run("uname -s")<br>    return result.stdout.strip() == "Linux"<br></pre><br><h2>Grupos</h2><br>Fabric es muy potente, pero en cuanto tengamos muchas máquinas vamos a hacer muchas veces las mismas tareas. Podemos usar un simple bucle <strong>for</strong>, pero Fabric nos trae una abstracción llamada <strong>Group</strong>. Básicamente podemos juntar conexiones en un único grupo y este ejecutas las acciones que pidamos. Existen dos tipos de grupo, <strong>SerialGroup</strong>, que ejecuta las operaciones secuencialmente y <strong>ThreadGroup</strong> que las ejecuta en paralelo.<br><pre class="lang:python decode:true">from fabric import ThreadingGroup<br><br>def update(cxn):<br>    cxn.run("sudo apt update")<br><br><br>pool = ThreadingGroup("roma@192.168.0.158","madrid@192.168.0.155")<br>update(pool)</pre><br>O si tienes ya objetos de conexión creados:<br><pre class="lang:python decode:true">from fabric import ThreadingGroup<br><br>def update(cxn):<br>    cxn.run("sudo apt update")<br><br>pool = ThreadingGroup.from_connections([roma,madrid])<br>update(pool)</pre><br>Con esto ya deberías saber lo básico para manejar Fabric, una fantástica librería para la automatización en remoto. Yo la uso para este blog y otras webs. Existen alternativas como <strong>Ansible</strong>, aunque a mí nunca me ha terminado de gustar su manera de hacer las cosas, que requiere mucho más aprendizaje.]]></description>
                <comments>https://blog.adrianistan.eu/fabric-automatiza-tareas-en-remoto</comments>
                <pubDate>Sat, 19 May 2018 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Natural Language Understanding con Snips NLU en Python</title>
                <link>https://blog.adrianistan.eu/natural-language-understanding-con-snips-nlu-en-python</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/natural-language-understanding-con-snips-nlu-en-python</guid>
                <description><![CDATA[Uno de los campos más importantes de la inteligencia artificial es el del tratamiento del lenguaje natural. Ya en 1955, en el primer evento sobre Inteligencia Artificial, y promovido entre otros por John McCarthy (creador de Lisp) y Claude Shannon (padre de la teoría de la información y promotor del uso del álgebra de boole para la electrónica), estas cuestiones entraron en el <a href="http://www-formal.stanford.edu/jmc/history/dartmouth/dartmouth.html">listado de temas</a>.<br><br>En aquella época se era bastante optimista con las posibilidades de describir el lenguaje natural (inglés, español, ...) de forma precisa con un conjunto de reglas de forma similar a las matemáticas. Luego se comprobó que esto no era una tarea tan sencilla.<br><br>Hoy día, es un campo donde se avanza mucho todos los días, aprovechando las técnicas de machine learning combinadas con heurísticas propias de cada lenguaje.<br><br><strong>Natural Language Understanding</strong> nos permite saber qué quiere decir una frase que pronuncia o escribe un usuario. Existen diversos servicios que provee de esta funcionalidad: <a href="https://www.ibm.com/watson/services/natural-language-understanding/">IBM Watson</a>, <a href="https://www.luis.ai/">Microsoft LUIS</a> y también existe software libre, como <a href="https://snips-nlu.readthedocs.io/en/latest/index.html">Snips NLU</a>.<br><br><a href="https://files.adrianistan.eu/Snips.png"><img class="aligncenter size-large wp-image-1432" src="https://files.adrianistan.eu/Snips-1024x538.png" alt="" width="840" height="441" /></a>Snips NLU es una librería hecha en <a href="https://blog.adrianistan.eu/rust-101-tutorial-rust-espanol/">Rust</a> y con interfaz en Python que funciona analizando el texto con datos entrenados gracias a machine learning y da como resultado un <strong>intent</strong>, o significado de la frase y el valor de los <strong>slots</strong>, que son <em>variables</em> dentro de la frase.<br><blockquote>¿Qué tiempo hará mañana en Molina de Aragón?</blockquote><br>Y Snips NLU nos devuelve:<br><ul><br> 	<li>intent: obtenerTiempo</li><br> 	<li>slots:<br><ul><br> 	<li>cuando: mañana</li><br> 	<li>donde: Molina de Aragón</li><br></ul><br></li><br></ul><br>Pero para esto, antes hay que hacer un par de cosas.<br><h2>Instalar Snips NLU</h2><br>Instala Snips NLU con <a href="https://docs.pipenv.org/">Pipenv</a> (recomendado) o Pip:<br><pre class="lang:default decode:true">pipenv install snips-nlu</pre><br><pre class="lang:default decode:true">pip install snips-nlu</pre><br>&nbsp;<br><h2>Datos de entrenamiento</h2><br>En primer lugar vamos a crear un listado de frases que todas expresen la intención de obtener el tiempo y lo guardamos en un fichero llamado <strong>obtenerTiempo.txt</strong>. Así definimos un intent:<br><pre class="lang:default decode:true">¿Qué tiempo hará [cuando:snips/time](mañana) en [donde:localidad](Molina de Aragón)?<br>¿Qué tal hará [cuando:snips/time](pasado mañana) en [donde:localidad](Ponferrada)?<br>¿Qué temperatura habrá [cuando:snips/time](mañana) en [donde:localidad](Frías)?<br></pre><br>La sintaxis es muy sencilla. Cada frase en una línea. Cuando una palabra forme parte de un slot, se usa la sintaxis [NOMBRE SLOT:TIPO](texto). En el caso de [donde:localidad](Frías). Donde es el nombre del slot, localidad es el tipo de dato que va y Frías es el texto original de la frase. En el caso del slot cuando, hemos configurado el tipo como <em>snips/time</em> que es uno de los predefinidos por Snips NLU.<br><br>Creamos también un fichero llamado <strong>localidad.txt</strong>, con los posibles valores que puede tener localidad. Esto no quiere decir que no capture valores fuera de esta lista, como veremos después, pero tienen prioridad si hubiese más tipos. También se puede configurar a que no admita otros valores, pero no lo vamos a ver aquí.<br><pre class="lang:default decode:true">Burgos<br>Valladolid<br>Peñaranda de Bracamonte<br>Soria<br>Almazán<br>Íscar<br>Portillo<br>Toro<br>Fermoselle<br>Sahagún<br>Hervás<br>Oña<br>Saldaña<br>Sabiñánigo<br>Jaca<br></pre><br>Ahora generamos un fichero JSON listo para ser entrenado con el comando <strong>generate-dataset</strong>.<br><pre class="lang:default decode:true">generate-dataset --language es --intent-files obtenerTiempo.txt --entity-files localidad.txt &gt; dataset.json</pre><br><h2>Entrenamiento</h2><br>Ya estamos listos para el entrenamiento. Creamos un fichero Python como este y lo ejecutamos:<br><pre class="lang:python decode:true">import io<br>import json<br>from snips_nlu import load_resources, SnipsNLUEngine<br><br>load_resources("es")<br><br>with io.open("dataset.json") as f:<br>    dataset = json.load(f)<br><br>engine = SnipsNLUEngine()<br><br>engine.fit(dataset)<br><br>engine_json = json.dumps(engine.to_dict())<br>with io.open("trained.json",mode="w") as f:<br>    f.write(engine_json)</pre><br>El entrenamiento se produce en <strong>fit</strong>, y esta tarea puede tardar dependiendo del número de datos que metamos. Una vez finalizado, generama un fichero trained.json con el entrenamiento ya realizado.<br><h2>Hacer preguntas</h2><br>Ha llegado el momento de hacer preguntas, cargando el fichero de los datos entrenados.<br><pre class="lang:python decode:true">import io<br>import json<br>from snips_nlu import SnipsNLUEngine, load_resources<br><br>load_resources("es")<br><br>with io.open("trained.json") as f:<br>    engine_dict = json.load(f)<br><br>engine = SnipsNLUEngine.from_dict(engine_dict)<br><br>phrase = input("Pregunta: ")<br><br>r = engine.parse(phrase)<br>print(json.dumps(r, indent=2))</pre><br><a href="https://files.adrianistan.eu/PythonSNIPS.png"><img class="aligncenter size-full wp-image-1428" src="https://files.adrianistan.eu/PythonSNIPS.png" alt="" width="787" height="633" /></a>Ahora sería tarea del programador usar el valor del intent y de los slots para dar una respuesta inteligente.<br><br>Te animo a que te descargues el proyecto o lo hagas en casa e intentes hacerle preguntas con datos retorcidos a ver qué pasa y si guarda en los slots el valor correcto.]]></description>
                <comments>https://blog.adrianistan.eu/natural-language-understanding-con-snips-nlu-en-python</comments>
                <pubDate>Tue, 17 Apr 2018 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Introducción a Prolog, tutorial en español</title>
                <link>https://blog.adrianistan.eu/introduccion-a-prolog-tutorial-en-espanol</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/introduccion-a-prolog-tutorial-en-espanol</guid>
                <description><![CDATA[<strong>Prolog</strong> es un lenguaje de programación lógico, quizá uno de los más populares de este paradigma ya que fue el primero en implementarlo, en el año 1972 en Francia.<br><br>Durante un tiempo, se creyó que PROLOG supondría la revolución de los lenguajes de programación,<a href="http://hemeroteca.abc.es/nav/Navigate.exe/hemeroteca/madrid/abc/1986/10/12/171.html"> siendo uno de los estandartes de los lenguajes de programación de quinta generación</a>. Tanto se creía que Borland, la famosa empresa de compiladores para MS-DOS, tenía <em>Turbo Prolog</em>, junto a <em>Turbo C++, Turbo Pascal, Turbo Assembler y Turbo Basic</em>.<br><br>Desafortunadamente, Prolog no triunfó como se esperaba y solo fue usado dentro del mundo de la Inteligencia Artificial. Existe un estándar ISO sobre Prolog (al igual que Ada, C++ y otros lenguajes estandarizados) pero no es demasiado influyente. Tampoco ha habido ningún éxito en alguna de las versiones propuestas para dotarle de orientación a objetos al lenguaje.<br><br>Prolog sin embargo ha influenciado a algunos lenguajes como <a href="https://www.erlang.org/">Erlang</a>, que toman algunos aspectos de él.<br><br>No obstante, Prolog sigue siendo un lenguaje muy interesante, diferente al resto de lenguajes (tanto imperativos, como funcionales), así que pongámonos a ello.<br><br>Actualmente existen varios compiladores de Prolog: SWI Prolog, GNU Prolog, Visual Prolog (al mucha gente no lo considera Prolog de verdad), ...<br><br>La mejor versión bajo mi punto de vista es <a href="http://www.swi-prolog.org/">SWI Prolog</a>, que tiene una librería de <em>predicados</em> bastante extensa. Es rápido y permite generar ejecutables <em>nativos</em> (realmente no lo son, pero la máquina virtual de Prolog ocupa muy poco y apenas se nota en el tamaño del ejecutable).<br><br>Lo podéis descargar gratuitamente desde <a href="http://www.swi-prolog.org/">su página oficial</a> que como curiosiad, está hecha en SWI Prolog. También está en la paquetería de las distribuciones GNU/Linux habituales.<br><h2>Nuestro primer programa</h2><br>Para empezar con Prolog voy a tomar un camino distinto de muchos tutoriales y voy a empezar haciendo un programa con Entrada/Salida y que se ejecute como un binario independiente. La potencia de Prolog no está ahí especialmente, pero es un buen punto de partida<br><br>Para ello definimos un <strong>predicado</strong> main de la siguiente forma y lo guardamos en un fichero <strong>main.pl</strong>.<br><pre class="lang:default decode:true">main :- <br>	write("Hola Mundo"),<br>	nl,<br>	write("¿Cuál es tu nombre? "),<br>	read_string(user_input,['\n'],[],_,Nombre),<br>	write("Hola "),write(Nombre),nl,<br>	halt.<br></pre><br>¿Qué hace el programa? Imprime en pantalla Hola Mundo (<strong>write</strong>), una nueva línea (<strong>nl</strong>), lee un string de teclado con el separador \n y lo <em>unifica </em>con la variable Nombre. Esto ya veremos que significa, pero de momento puedes pensar que Nombre es una variable de salida y que a partir de ahí Nombre tiene un valor establecido.<br><br>Compilamos con el siguiente comando:<br><pre class="lang:default decode:true">swipl --goal=main --stand_alone=true -o main -c main.pl</pre><br><a href="https://files.adrianistan.eu/PrologCompile.png"><img class="aligncenter size-full wp-image-1418" src="https://files.adrianistan.eu/PrologCompile.png" alt="" width="726" height="383" /></a>Con esto le decimos a SWI Prolog que el objetivo a demostrar es <strong>main</strong> y que nos genere un fichero <strong>stand_alone</strong> (independiente).<br><br>Y ejecutamos el fichero ejecutable como uno más.<br><br><a href="https://files.adrianistan.eu/PrologExecute.png"><img class="aligncenter size-full wp-image-1417" src="https://files.adrianistan.eu/PrologExecute.png" alt="" width="724" height="89" /></a>Ahora que ya sabemos como se generan programas compilados, vamos a introducirnos más en lo que hace especial a Prolog.<br><h2>La terminal de Prolog</h2><br>Prolog fue diseñado con una terminal interactiva en mente. La idea era que el usuario fuese introduciendo preguntas y el programa en Prolog fuese contestando. Este enfoque, similar a usar un programa desde el REPL de tu lenguaje, no ha acabado cuajando, pero es necesario pasar por él. Más adelante veremos como con SWI Prolog no hace falta usar la terminal. La terminal se abre escribiendo <strong>swipl</strong> en la línea de comandos:<br><br><a href="https://files.adrianistan.eu/SWIProlog.png"><img class="aligncenter size-full wp-image-1404" src="https://files.adrianistan.eu/SWIProlog.png" alt="" width="726" height="237" /></a>Vemos un símbolo de interrogación. Eso nos permite saber que estamos en una terminal de Prolog. Podemos escribir <em>write("Hola").</em> y tener nuestro hola mundo tradicional, aunque no es muy interesante, pero sí es muy interesante que después escribe <strong>true</strong>. Más adelante veremos por qué.<br><h2>Los programas Prolog</h2><br>En Prolog los programas son algo diferentes a lo que estamos acostumbrados. Realmente en Prolog no hay <em>programas</em> sino una <em>base de datos</em>. Un programa se compone de <em>predicados</em>, muy parecidos a los del <a href="https://es.wikipedia.org/wiki/L%C3%B3gica_de_primer_orden"><em>Cálculo de Predicados</em></a>. Los predicados aportan información sobre las relaciones entre elementos. Todos los predicados tienen que acabar en punto.<br><br>Siguiendo las mismas normas que el cálculo de predicados:<br><ul><br> 	<li>Las constantes empiezan por minúscula</li><br> 	<li>Las variables empiezan por mayúscula</li><br> 	<li>Las funciones son constantes seguidas de N teŕminos. Son funciones estrictamente matemáticas.</li><br> 	<li>Los predicados pueden ser atómicos o compuestos, con operadores lógicos (and, or, implica, etc)</li><br></ul><br>Prolog durante la ejecución, va a intentar <strong>demostrar</strong> que el predicado es cierto, usando el <a href="https://en.wikipedia.org/wiki/Backtracking">algoritmo de backtracking</a>. Y ahí está la verdadera potencia de Prolog. Veamos unos ejemplos:<br><pre class="lang:default decode:true" title="comida.pl">fruta(manzana).<br>fruta(naranja).<br>fruta(platano).</pre><br>Guarda ese archivo con extensión .pl y ejecuta <strong>swipl comida.pl<em>.</em></strong><br><br><a href="https://files.adrianistan.eu/PrologArchivo.png"><img class="aligncenter size-full wp-image-1405" src="https://files.adrianistan.eu/PrologArchivo.png" alt="" width="729" height="346" /></a>Ahora en la terminal podemos hacer preguntas. ¿Es la manzana una fruta? Prolog responde verdadero. ¿Es la pera una fruta? Prolog responde que falso, porque según el archivo comida.pl, no lo es. Prolog no es inteligente, no sabe que significan las palabras, simplemente actúa siguiendo un conjunto de normas formales.<br><br>Hemos dicho que Prolog tiene variables. Una variable en Prolog es un marcador de <em>hueco</em>, es algo que no existe, porque no es ninguna constante en específico. Veamos la potencia de las variables con este otro predicado.<br><br><a href="https://files.adrianistan.eu/PrologMultiple.png"><img class="aligncenter size-full wp-image-1406" src="https://files.adrianistan.eu/PrologMultiple.png" alt="" width="728" height="130" /></a>En este caso pedimos demostrar <em>fruta(X).</em>. Prolog buscará la primera solución que demuestra el predicado, que es que X valga manzana. Aquí podemos pulsar ENTER y Prolog se para o pulsar N y Prolog busca otra solución. ¿Potente, verdad? Prolog realiza un proceso interno que se llama <strong>unificación</strong>, es importante saber como funciona para ver que hace Prolog en realidad.<br><h2>Unificación</h2><br>La unificación es un proceso que combina dos predicados en uno que encaja. Para ello buscamos las sustituciones de valores con los que dos predicados son compatibles (realmente solo se pueden modificar las variables). No obstante, Prolog busca siempre el unificador más general, que es aquel que unifica dejando los predicados de forma más genérica posible, es decir, que pueden usarse con más valores.<br><br><a href="https://files.adrianistan.eu/Unificacion-2.png"><img class="aligncenter size-large wp-image-1414" src="https://files.adrianistan.eu/Unificacion-2-724x1024.png" alt="" width="724" height="1024" /></a><br><br>Espero que esta imagen aclare el concepto de unificación. Básicamente Prolog para intentar demostrar un predicado intentará unificar con otros predicados del programa. Así cuando ponemos por ejemplo <em>comida(manzana)</em> , unifica con <em>comida(X)</em> así que Prolog toma ese predicado para continuar.<br><h2>Backtracking</h2><br>Cuando Prolog intenta demostrar un predicado aplica el algoritmo de backtracking. Este algoritmo recorre todas las soluciones posibles pero de forma más inteligente que la fuerza bruta. Backtracking intenta conseguir una solución hasta que un predicado falla, en ese momento, Prolog <strong>va hacia atrás</strong> y continúa por otra vía que pueda seguir.<br><br><a href="https://files.adrianistan.eu/Backtracking.png"><img class="aligncenter size-full wp-image-1420" src="https://files.adrianistan.eu/Backtracking.png" alt="" width="500" height="447" /></a>Cada predicado es un nodo. Si un predicado falla se vuelve atrás. Esto es muy interesate ya que Prolog técnicamente puede <strong>ejecutar código hacia atrás</strong>.<br><br>Un predicado Prolog sigue esta estructura:<br><br><a href="https://files.adrianistan.eu/PrologFlow.png"><img class="aligncenter size-full wp-image-1421" src="https://files.adrianistan.eu/PrologFlow.png" alt="" width="256" height="256" /></a><br><h2>Predicados avanzados</h2><br>Pero los predicados de Prolog no tienen por qué ser así de simples. Normalmente se usa el operador :- para indicar algo que para que se cumpla la parte de la izquierda, tiene que cumplirse la parte de la derecha antes (en cálculo de predicados es ←).<br><br>Por ejemplo, todas las frutas son comidas, así que podemos añadir esto a nuestro archivo.<br><pre class="lang:default decode:true ">fruta(manzana).<br>fruta(naranja).<br>fruta(platano).<br><br>comida(X) :- fruta(X).</pre><br>Y los predicados en Prolog se pueden repetir y repetir y repetir. Prolog siempre intenta demostrar de arriba a abajo, si falla un predicado, prueba con el siguiente más para abajo y termina cuando no hay más predicados que probar. ¡Así que podemos definir comida en base a más predicados!<br><pre class="lang:default decode:true">fruta(manzana).<br>fruta(naranja).<br>fruta(platano).<br><br>carne(pollo).<br>carne(vaca).<br>carne(cerdo).<br>carne(caballo).<br><br>comida(paella).<br>comida(pulpo).<br><br>comida(X) :- fruta(X).<br>comida(X) :- carne(X).<br></pre><br><h2><a href="https://files.adrianistan.eu/TodoComidas.png"><img class="aligncenter size-full wp-image-1407" src="https://files.adrianistan.eu/TodoComidas.png" alt="" width="722" height="229" /></a>Operaciones</h2><br>En Prolog existen varios operadores importantes:<br><ul><br> 	<li>, (coma) AND</li><br> 	<li>; (punto y coma) OR</li><br> 	<li>A = B, se intenta unificar A y B. Devuelve true si funciona</li><br> 	<li>A \= B es falso si A y B unifican</li><br> 	<li>A is B, se evalúa B (es decir, se calcula lo que representa) y se unifica con A</li><br> 	<li>A =:= B , evalúa A, evalúa B y los compara. Verdadero si son iguales</li><br> 	<li>A =\= B, evalúa A, evalúa B y los compara. Falso si son iguales</li><br> 	<li>Y muchos otros como =&lt;, &gt;=, &gt;, &lt; que tienen el comportamiento esperado.</li><br> 	<li>Las operaciones matemáticas solo se pueden introducir en expresiones que vayan a ser evaluadas.</li><br></ul><br>Quiero prestar especial atención en símbolo de igual que <strong>no es asignación</strong> sino unificación, pero puede parecerse. Estas dos líneas son equivalentes:<br><pre class="lang:default decode:true ">X = 5<br>% y <br>5 = X</pre><br>Ya que en ambos casos se unifica X con 5.<br><br>Veamos un ejemplo de todo esto, ya mucho más realista.<br><pre class="lang:default decode:true " title="restaurante.pl">%%%%%%%%%%%%%%%%%%%%%%%%%%%<br>%   Programa restaurante  %<br>%%%%%%%%%%%%%%%%%%%%%%%%%%%	<br><br>% menu<br><br>entrada(paella).<br>entrada(gazpacho).<br>entrada(pasta).<br><br>carne(filete_de_cerdo).<br>carne(pollo_asado).<br><br>pescado(trucha).<br>pescado(bacalao).<br><br>postre(flan).<br>postre(nueces_con_miel).<br>postre(naranja).<br><br>% Valor calorico de una racion<br><br>calorias(paella, 200).<br>calorias(gazpacho, 150).<br>calorias(pasta, 300).<br>calorias(filete_de_cerdo, 400).<br>calorias(pollo_asado, 280).<br>calorias(trucha, 160).<br>calorias(bacalao, 300).<br>calorias(flan, 200).<br>calorias(nueces_con_miel, 500).<br>calorias(naranja, 50).<br><br>% plato_principal(P) P es un plato principal si es carne o pescado<br><br>plato_principal(P):- carne(P).<br>plato_principal(P):- pescado(P).<br><br>% comida(Entrada, Principal, Postre)<br><br>comida(Entrada, Principal, Postre):-<br>        entrada(Entrada),<br>        plato_principal(Principal),<br>        postre(Postre).<br>    <br>% Valor calorico de una comida<br><br>valor(Entrada, Principal, Postre, Valor):-<br>        calorias(Entrada, X),<br>        calorias(Principal, Y),<br>        calorias(Postre, Z),<br>        sumar(X, Y, Z, Valor).<br><br>% comida_equilibrada(Entrada, Principal, Postre)<br><br>comida_equilibrada(Entrada, Principal, Postre):-<br>        comida(Entrada, Principal, Postre),<br>        valor(Entrada, Principal, Postre, Valor),<br>        menor(Valor, 600).<br><br><br>% Conceptos auxiliares<br><br>sumar(X, Y, Z, Res):-<br>        Res is X + Y + Z.             % El predicado "is" se satisface si Res se puede unificar<br>                                      % con el resultado de evaluar la expresion X + Y + Z <br>menor(X, Y):- <br>        X &lt; Y.                        % "menor" numerico<br><br>dif(X, Y):-<br>        X =\= Y.                      % desigualdad numerica <br><br><br>% cuantas comidas llevan naranja de postre<br>%<br><br>comidas_con_naranja :-<br>	write("Comidas con naranja: "),<br>	aggregate_all(count,comida(_,_,naranja),X),<br>	write(X),<br>	nl.<br></pre><br>Lo cargamos con <strong>swipl restaurante.pl </strong>y podemos contestar a las siguientes preguntas de forma sencilla:<br><br><em>¿Qué valor calórico tiene la comida de paella, trucha y flan?</em><br><pre class="lang:default decode:true ">valor(paella,trucha,flan,N).</pre><br><em>Dime una comidas equilibrada que lleve naranja de postre</em><br><pre class="lang:default decode:true ">comida_equilibrada(X,Y,Z),Z=naranja.</pre><br><a href="https://files.adrianistan.eu/PrologRestaurante.png"><img class="aligncenter size-full wp-image-1415" src="https://files.adrianistan.eu/PrologRestaurante.png" alt="" width="727" height="342" /></a><em>¿Cuántas comidas con naranja hay?</em><br><pre class="lang:default decode:true">comidas_con_naranja.</pre><br><h2>Variables anónimas y predicados predefinidos</h2><br>Si te fijas en el predicado <em>comidas_con_naranja</em>, es diferente al resto. Esta programado de otra forma. La salida por pantalla se hace con write como ya habíamos visto al principio, write es un predicado que siempre es cierto y en caso de backtracking simplemente pasa. <strong>aggregate_all</strong> es también otro predicado incluido en SWI Prolog, para en este caso, contar cuantas soluciones tiene el predicado <em>comida(_,_,naranja)</em>. X unifica en aggregate_all con el valor que toca (que es una constante, los números son constantes) y en las siguientes sentencias (write), X ha sido sustituido por el valor con el que unificó en aggregate_all.<br><br>Por otro lado, ¿qué significa la barra baja? Es una <strong>variable anónima</strong>. Cuando no usamos una variable en más lugares y no nos interesan sus valores podemos simplemente poner barra baja.<br><h2>Listas en Prolog</h2><br>Prolog tiene un tipo de dato más avanzado, las listas. Su tamaño es dinámico y es conveniente distinguir la cabeza de la cola. La cabeza es el primer elemento de una lista, la cola el resto de la lista.<br><br>Las listas se crean con corchetes:<br><pre class="lang:default decode:true">X = [1,2,3,4,5],<br>sumlist(X,N).</pre><br><strong>sumlist</strong> es un predicado de SWI que hace la suma de todos los elementos de una lista.<br><br>Con la barra podemos separar cabeza y cola de una lista:<br><pre class="lang:default decode:true">[1,2,3,4] = X,<br>[H|T] = X.</pre><br><h3><a href="https://files.adrianistan.eu/PrologLista.png"><img class="aligncenter size-full wp-image-1422" src="https://files.adrianistan.eu/PrologLista.png" alt="" width="726" height="140" /></a>Implementando sumlist</h3><br>Vamos a ver como se puede implementar sumlist con Prolog de forma sencilla.<br><pre class="lang:default decode:true">sumar([],0).<br>sumar([H|T],N) :-<br>	sumar(T,X),<br>	N is X+H.</pre><br>El predicado es sencillo, para un caso base de lista vacía, la suma es 0. Para otros casos más complejos separamos la lista en cabeza H y cola T y la suma es N. Esta suma se define como el resultado de sumar T (queda en X) y la cabeza H.<br><h2><strong>Assert y retract<br></strong></h2><br>¿Podemos crear predicados en tiempo real? Claro. Prolog provee de los predicados especiales <strong>assert</strong> para añadir y <strong>retract</strong> para eliminar un predicado.<br><h2><a href="https://files.adrianistan.eu/PrologAssert.png"><img class="aligncenter size-full wp-image-1423" src="https://files.adrianistan.eu/PrologAssert.png" alt="" width="721" height="365" /></a>Operador de corte</h2><br>Prolog tiene un operador muy controvertido, el operador de corte, !. Se trata de un operador que permite no volver hacia atrás. Hay que intentar no usarlo, pero muchas veces mejora el rendimiento.<br><h2>Metaprogramación</h2><br>Prolog tiene un gran soporte para la metaprogramación. Assert y Retract son ya formas de metaprogramación, no obstante Prolog tiene muchas más.<br><br><strong>call</strong> permite ejecutar código Prolog desde Prolog.<br><br><strong>setarg</strong> permite modificar un término de un predicado y <strong>arg </strong>unifica una variable con el término de un predicado que queramos. <strong>nb_setarg</strong> es la versión de setarg que en caso de backtracking no deshace la operación. Un ejemplo de esto lo podemos encontrar en la definición de <strong>aggregate_all</strong> en la librería de SWI Prolog:<br><pre class="lang:default decode:true ">aggregate_all(count, Goal, Count) :-<br>    !,<br>    aggregate_all(sum(1), Goal, Count).<br>aggregate_all(sum(X), Goal, Sum) :-<br>    !,<br>    State = state(0),<br>    (  call(Goal),<br>           arg(1, State, S0),<br>           S is S0 + X,<br>           nb_setarg(1, State, S),<br>           fail<br>    ;  arg(1, State, Sum)<br>).</pre><br>&nbsp;<br><h2>Debug con trace</h2><br>Prolog posee un predicado muy útil para hacer debugging. Es <strong>trace</strong> y con el predicado que vaya a continuación podremos inspeccionar todas las operaciones del motor de backtracking.<br><br><a href="https://files.adrianistan.eu/PrologTrace.png"><img class="aligncenter size-full wp-image-1424" src="https://files.adrianistan.eu/PrologTrace.png" alt="" width="720" height="428" /></a>]]></description>
                <comments>https://blog.adrianistan.eu/introduccion-a-prolog-tutorial-en-espanol</comments>
                <pubDate>Thu, 12 Apr 2018 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Mónada ST en Haskell: STRef, STArray</title>
                <link>https://blog.adrianistan.eu/monada-st-en-haskell-stref-starray</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/monada-st-en-haskell-stref-starray</guid>
                <description><![CDATA[Haskell tiene una librería muy completa, con muchas estructuras de datos y, en este caso, mónadas muy interesantes. En este caso vamos a hablar de la mónada <strong>ST</strong>. <strong>ST</strong> son las siglas de <em>State Threads</em>. Fueron definidas por Simon Peyton-Jones en <a href="https://www.microsoft.com/en-us/research/publication/lazy-functional-state-threads/">un paper de 1994</a> y son una forma muy curiosa que tiene Haskell de permitirnos escribir código con estructuras de datos mutables. La gracia está en que se garantiza que es imposible que la mutabilidad interna que hay dentro de la mónada ST salga al exterior, de modo que de cara al exterior son funciones puras igualmente. Esta mónada no sirve para comunicarse con el exterior como sí sirve <strong>IO</strong> pero si podemos usar ST antes que IO, mejor.<br><h2>STRef</h2><br>La estructura más fundamental es STRef, que permite tener una variable mutable dentro de Haskell. Las funciones que permiten su manejo son <strong>newSTRef</strong>, <strong>readSTRef</strong>, <strong>writeSTRef</strong> y <strong>modifySTRef</strong>. Creo que es bastante evidente para qué sirve cada una, pero veamos un ejemplo práctico.<br><br>Vamos a hacer una función que sume todos los elementos de una lista. Existen varias formas de hacerlo de forma pura en Haskell:<br><pre class="lang:haskell decode:true ">sumar :: Num a =&gt; [a] -&gt; a<br>sumar [] = 0<br>sumar (x:xs) = x+sumar xs</pre><br><em>Una versión recursiva</em><br><pre class="lang:haskell decode:true ">sumar :: Num a =&gt; [a] -&gt; a<br>sumar = foldr1 (+)</pre><br><em>Una versión más compacta</em><br><br>En un lenguaje de programación imperativo sin embargo posiblemente haríamos algo parecido a esto:<br><pre class="lang:java decode:true ">int sumar(int[] nums){<br>    int suma = 0;<br>    for(int i=0;i&lt;nums.length;i++){<br>        suma += nums[i];<br>    }<br>    return suma;<br>}</pre><br>Con ST podemos hacer código que se parezca a la versión imperativa, lo cuál es útil en determinados algoritmos y también si queremos ganar eficiencia.<br><pre class="lang:haskell decode:true">import Control.Monad.ST<br>import Control.Monad<br>import Data.STRef<br><br>sumar :: Num a =&gt; [a] -&gt; a<br>sumar xs = runST $ do<br>	x &lt;- newSTRef 0<br>	<br>	forM_ xs $ \n -&gt; <br>		modifySTRef x $ \x -&gt; x+n<br><br>	readSTRef x<br></pre><br>Lo primero que tenemos que hacemos es ejecutar la mónada ST con <strong>runST</strong> y a continuación la función. Allí creamos una <em>variable mutable</em> con valor inicial 0. Después lanzamos <strong>forM_</strong> que itera sobre cada elemento de la lista xs ejecutando para cada elemento la función que llama a modifySTRef que ejecuta a su vez la función que suma el valor del acumulador con el valor del elemento actual de la lista.<br><br>Por último, la función finaliza devolviendo el valor de x.<br><br>Como veis, el código no tiene nada que ver a las otras versiones de Haskell y tiene un gran parecido con la versión de Java. No obstante, el código sigue siendo puro, sin efectos colaterales.<br><h2>STArray</h2><br>Pero no solo tenemos STRef, también tenemos <strong>STArray</strong> que es un array de tamaño fijo con estas mismas propiedades. Las funciones básicas son <strong>newListArray, readArray, writeArray </strong>y <strong>getElems</strong>.<br><br>Por ejemplo, podemos implementar <a href="https://es.wikipedia.org/wiki/Ordenamiento_de_burbuja">el algoritmo de Bubble Sort</a> de forma imperativa con STArray:<br><br><a href="https://files.adrianistan.eu/Sorting_bubblesort_anim.gif"><img class="aligncenter size-full wp-image-1401" src="https://files.adrianistan.eu/Sorting_bubblesort_anim.gif" alt="" width="277" height="257" /></a><br><pre class="lang:haskell decode:true ">import Control.Monad<br>import Control.Monad.ST<br>import qualified Data.Array.ST as ST<br>import Data.STRef<br><br>bubblesort :: [Int] -&gt; [Int]<br>bubblesort xs = runST $ do<br>	let l = length xs<br>	temp &lt;- newSTRef $ head xs<br>	array &lt;- ST.newListArray (0,l-1) xs :: ST s (ST.STArray s Int Int)<br><br>	forM_ [0..l] $ \i -&gt; do<br>		forM_ [1..(l-1)] $ \j -&gt; do<br>			prev &lt;- ST.readArray array (j-1)<br>			actual &lt;- ST.readArray array j	<br>			if prev &gt; actual then do<br>				writeSTRef temp prev<br>				ST.writeArray array (j-1) actual<br>				t &lt;- readSTRef temp<br>				ST.writeArray array j t<br>			else do<br>				return ()<br><br>	ST.getElems array<br>			<br>main :: IO ()<br>main = do<br>	let orden = bubblesort [3,4,1,2]			<br>	putStrLn ("Orden: "++(show orden))</pre><br><a href="https://files.adrianistan.eu/BubblesortHaskell.png"><img class="aligncenter size-full wp-image-1400" src="https://files.adrianistan.eu/BubblesortHaskell.png" alt="" width="727" height="109" /></a>La mónada ST junto con las funciones de Control.Monad nos permiten escribir código que parece imperativo y con mutabilidad, sin perder la pureza y la seguridad de Haskell.]]></description>
                <comments>https://blog.adrianistan.eu/monada-st-en-haskell-stref-starray</comments>
                <pubDate>Mon, 09 Apr 2018 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Un muro en Cuzco</title>
                <link>https://blog.adrianistan.eu/un-muro-en-cuzco</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/un-muro-en-cuzco</guid>
                <description><![CDATA[En Cuzco (Perú), capital del antiguo imperio Inca, hay un muro que está compuesto de dos zonas claramente diferenciadas.<br><br><a href="https://files.adrianistan.eu/MuroInca.jpg"><img class="wp-image-1394 size-full" src="https://files.adrianistan.eu/MuroInca.jpg" alt="" width="700" height="465" /></a> Foto de https://livinmiami.wordpress.com/2012/04/27/cuzco-8/<br><br>Este muro, célebre por la película <em>Diarios de Motocicleta</em>, ha sido llamado el muro de los incas y de los incapaces. Realmente el contraste es evidente. Quizá incluso nos pueda parecer que era imposible que los incas construyesen sus muros con esas piedras tan grandes, tan pesadas. Tenemos la misma mentalidad que la del muro construido por los españoles.<br><br>Y es que es todo cuestión de mentalidad, de prioridades. Las prioridades de los españoles eran otras. Son otras, porque esas prioridades siguen siendo lo mismo y forman parte de lo más profundo de la cultura. La mentalidad del muro español refleja varias cosas.<br><br><strong>Rapidez</strong>, es importante construir algo rápidamente. El muro inca posiblemente tardó más en construirse que el español, hecho de bloques más pequeños. Más rápida su extracción, más rápido su transporte, más rápida su colocación. Si hacemos rápidamente una tarea, podemos empezar a hacer otra tarea antes y al final, hemos hecho más tareas.<br><br><strong>Flexibilidad</strong>, es importante poder reaprovechar el trabajo ya realizado. Si producimos bloques más pequeños, estos nos pueden servir para otros muros, podemos tapar otros huecos, no estamos limitados al muro en concreto que estamos construyendo.<br><br><strong>Pragmatismo</strong>, es importante centrarse en el objetivo final de nuestras tareas. Un muro cumple la función de separar la calle de la casa, aislarla del frío, los ladrones y las miradas furtivas. Si cumple esas cualidades, ya tenemos un buen muro y cuánto más simple sea el mecanismo por el cuál ese muro acaba construido, mejor. No estamos para complicarnos con mundanidades.<br><br><a href="https://files.adrianistan.eu/stones-cusco-2608832_1920.jpg"><img class="aligncenter size-large wp-image-1395" src="https://files.adrianistan.eu/stones-cusco-2608832_1920-1024x680.jpg" alt="" width="840" height="558" /></a><br><br>Los incas tenían otra mentalidad cuando construyeron este muro.<br><br><strong>Durabilidad</strong>, es importante que las cosas que hagamos duren el mayor tiempo posible. Los bloques grandes aguantan más, son más difíciles de romper. Los muros con muchos bloques tienes más lugares de posibles grietas, donde puede entrar el agua. Al final con el tiempo, hemos perdido menos el tiempo con este muro que si lo hubiésemos hecho rápido, pero costó más de entrada.<br><br><strong>Seguridad</strong>, es importante que las cosas que construyamos generen el menor número de situaciones peligrosas. En la zona de Cuzco los terremotos son algo relativamente habitual. Los bloques grandes soportan mejor los terremotos y reducen las vibraciones que se transmiten.<br><br><strong>Naturaleza</strong>, es importante que nuestras obras sean similares a la naturaleza, de quien venimos, y fuente inagotable de sabiduría puesta en práctica. En la naturaleza encontramos grandes rocas por dóquier, son el día a día en el mundo fuera de la ciudad.<br><br><strong>Superación</strong>, el hombre siempre se pone metas para ver si es capaz de superarse. Cada momento en la vida es una oportunidad para hacerlo, da igual que se esté construyendo solo un muro, al final la magnificiencia se entrena. Construir con estas enormes piedras no es fácil, pero ahí estamos para superar esa dificultad y que la gente al verlo quede estupefacta.<br><br>Pero ante todo, ambos imperios cuando construyeron este muro lo hicieron de la forma <strong>correcta</strong>, la que ellos consideraban <strong>natural</strong>, posiblemente nunca se cuestionaron a sí mismos porque hacían los muros así. Era algo evidente para ellos.<br><br>Quizá nunca llegamos a entender la mentalidad de los incas y por qué construían sus muros así, quizá las razones que he dado aquí no tengan nada que ver con sus verdaderos motivos, su mentalidad y sus prioridades. De todos modos, ¿eres un inca o un incapaz?]]></description>
                <comments>https://blog.adrianistan.eu/un-muro-en-cuzco</comments>
                <pubDate>Wed, 04 Apr 2018 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Programación dinámica: el problema de knapsack</title>
                <link>https://blog.adrianistan.eu/programacion-dinamica-el-problema-de-knapsack</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/programacion-dinamica-el-problema-de-knapsack</guid>
                <description><![CDATA[Los algoritmos nos ayudan a resolver problemas. Hay muchas maneras de diseñarlos, un método que no conocía hasta hace poco y que me ha resultado muy interesante conocer es la <strong>programación dinámica</strong>. La idea fundamental de la programación dinámica es dividir el problema en subproblemas y posteriormente calcular una única vez cada subproblema. Existen dos acercamientos: <strong>top-down</strong> y <strong>bottom-up</strong>. La programación dinámica nos permite resolver problemas más rápidamente que con fuerza bruta, gastando a cambio mucha más memoria del ordenador.<br><br>Como explicado así, es un poco abstracto, vamos a intentar resolver un problemas clásico de programación dinámica, el problema de <strong>knapsack</strong> (o de la mochila).<br><h2>Problema de Knapsack</h2><br>Imagina que tenemos una mochila con capacidad para 10 kg. Tenemos 400 elementos para meter en la bolsa, cada uno de ellos con un peso y un valor. ¿Cuál es el valor máximo que podemos llevar en la bolsa?<br><br><a href="https://files.adrianistan.eu/KnapSack.jpeg"><img class="aligncenter wp-image-1352 size-medium" src="https://files.adrianistan.eu/KnapSack-228x300.jpeg" alt="" width="228" height="300" /></a><br><br>Este problema parece muy complicado de resolver. Quizá lo único que se nos ocurra es probar todas las combinaciones posibles de elementos, descartar las combinaciones que exceden el peso que puede llevar la mochila y de las restantes quedarnos con el máximo. Funcionaría, pero sería extremadamente lento.<br><br>Como cada elemento puede estar o no estar en la mochila, y son 400, las combinaciones son 2⁴⁰⁰=2582249878086908589655919172003011874329705792829223512830659356540647622016841194629645353280137831435903171972747493376. Excesivo.<br><br>Otra idea que podría surgir es <strong>backtracking</strong>. Básicamente recorreríamos todos los elementos posibles en forma de árbol, pero en cuanto veamos que ya no entran más elementos cortamos la búsqueda por esa rama y tomamos otra. Mejoraría el rendimiento, pero no es tampoco lo mejor que se podría hacer.<br><br>La solución es usar <strong>programación dinámica</strong>, vamos a aplicar el enfoque <strong>top-down</strong>. Lo primero es expresar la solución de forma recursiva.<br><br>La función <strong>mochila</strong> se define como el valor máximo que puede llevar una mochila, con N elementos a elegir y con una capacidad C.<br><pre class="lang:python decode:true ">def mochila(n,c):<br>    if n == 0 or c == 0:<br>        # solucion optima para cuando no quedan elementos o la capacidad disponible es 0<br>        return 0<br>    elif datos[n].peso &gt; c:<br>        # no metemos el elemento<br>        return mochila(n-1,c)<br>    else:<br>        #sin meter el elemento<br>        a = mochila(n-1,c)<br>        # metiendo el elemento<br>        b = datos[n].valor + mochila(n-1,c-datos[n].peso)<br>        return max(a,b)<br></pre><br>El primer if es la solución óptima, que es trivial si sabemos que ya no quedan elementos por revisar o si la capacidad que queda en la mochila es cero. El elif y else siguientes nos dan las soluciones óptimas también, pero de forma recursiva. En el primer caso, si el elemento es demasiado grande, solo se puede continuar probando con otro elemento, en el otro caso, hacemos los dos cálculos. Miramos si obtenemos más valor metiendo el elemento o sin meterlo y devolvemos el máximo. Este es el primer paso, básicamente es backtracking lo que hemos hecho.<br><h2>Memoizar</h2><br>Si bien la solución funcionaba, podemos darnos cuenta de que el ordenador iba a calcular muchas veces lo mismo. En programación dinámica no se calcula nunca nada más de una vez. Una solución sencilla para lograrlo es aplicar <strong>memoización</strong>, es decir, mantenemos un historial de llamadas a la función con argumentos. Piensa en ello como una caché.<br><br>En Python existe desde 3.2 en el módulo <strong>functools</strong> una caché que cumple lo necesario para memoizar.<br><pre class="lang:python decode:true ">import functools<br><br>@functools.lru_cache(maxsize=None)<br>def mochila(n,c):<br>    if n == 0 or c == 0:<br>        # solucion optima para cuando no quedan elementos o la capacidad disponible es 0<br>        return 0<br>    elif datos[n].peso &gt; c:<br>        # no metemos el elemento<br>        return mochila(n-1,c)<br>    else:<br>        #sin meter el elemento<br>        a = mochila(n-1,c)<br>        # metiendo el elemento<br>        b = datos[n].valor + mochila(n-1,c-datos[n].peso)<br>        return max(a,b)</pre><br>Este ligero cambio mejora drásticamente el rendimiento, haciendo que se calcule de forma casi instantánea (mientras que la otra versión tarda minutos).<br><h2>Recursividad</h2><br>Python no es un lenguaje que optimice las llamadas recursivas. Tampoco lo es Java. Ciertamente, en muchos lenguajes hacer demasiadas llamadas recursivas provoca un Stack Overflow. Ante esto se puede reescribir el algoritmo para que sea un bucle (el enfoque <strong>bottom-up</strong>) o se puede reescribir en un lenguaje que optimice las llamadas recursivas como Haskell o Lisp.<br><br>La solución bottom-up de este problema (usando una matriz) sería la siguiente:<br><pre class="lang:python decode:true ">V = list()<br>def mochila_two(n,c):<br>    for x in range(n):<br>        V.append([])<br>        for y in range(c+1):<br>            V[x].append(0)<br>    for i in range(n):<br>        for w in range(c+1):<br>            if datos[i].peso &lt;= w:<br>                V[i][w] = max(V[i-1][w],datos[i].valor+V[i-1][w-datos[i].peso])<br>            else:<br>                V[i][w] = V[i-1][w]<br>    <br>    return V[n-1][c]</pre><br>Esta versión es más rápida y no tiene el problema del límite de recursividad, pero es menos legible.<br><br><a href="https://files.adrianistan.eu/BottumUp.png"><img class="aligncenter size-full wp-image-1351" src="https://files.adrianistan.eu/BottumUp.png" alt="" width="805" height="378" /></a>(en la esquina encontraremos la solución óptima)]]></description>
                <comments>https://blog.adrianistan.eu/programacion-dinamica-el-problema-de-knapsack</comments>
                <pubDate>Sun, 11 Mar 2018 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Futures y Tokio: programar de forma asíncrona en Rust</title>
                <link>https://blog.adrianistan.eu/futures-tokio-programar-forma-asincrona-rust</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/futures-tokio-programar-forma-asincrona-rust</guid>
                <description><![CDATA[Rust es un lenguaje muy potente y uno de sus principales focos de diseño es la <strong>concurrencia</strong>. Sin embargo, si damos un repaso por la librería estándar veremos un par de cosas nada más, algo que puede resultar <strong>decepcionante</strong>. Esto se debe a que Rust no impone ningún modelo específico de concurrencia como sí lo hacen Go o Erlang. Rust nos provee de los ladrillos de construcción necesarios.<br><br>De forma estándar, Rust nos provee de <strong>Mutex</strong>, <strong>atómicos</strong>, <strong>threads</strong>, <strong>variables de condición</strong>, <strong>RwLock</strong> y un modelo algo más avanzado de mensajería mediante canales de múltiples productores y un único consumidor (mpsc). En el exterior tenemos crates como <strong>Actix</strong> que nos proveen del modelo de actores en Rust (dicen que es similar a <strong>Akka</strong>, yo lo desconozco), modelos de mensajería por canales más flexibles (mpmc) y una cosa muy interesante llamado <strong>Futures</strong>. Los futures o futuros no son esos contratos que se realizan en bolsa sobre el valor futuro de una acción, sino que son una manera cómoda de manejar valores que existirán en el futuro. Si has usado JavaScript o C# igual te suenan las <strong>Promises</strong> o Promesas y los <strong>Task</strong> respectivamente. Los futuros de Rust son exactamente lo mismo.<br><h2>¿Qué es un futuro?</h2><br>Un futuro es una variable que representa un dato que todavía no existe. Tenemos la promesa de que ese valor existirá en el futuro. La ventaja es que podemos usarlo aun cuando todavía no tenga un valor. Esto además permite escribir código asíncrono con facilidad.<br><br>Una diferencia con respecto a los <strong>Promises</strong> de JavaScript es que los futuros se basan en <strong>poll</strong> en vez de en <strong>push</strong>. Es decir, los futuros van a ir preguntando si el valor ya está disponible. Ante esta pregunta se puede responder con un error, con <em>todavía no está disponible</em> y con <em>ya está disponible, aquí tienes</em>.<br><br>Veamos un ejemplo muy tonto pero que puede servirnos para entender algunas cosas.<br><pre class="lang:rust decode:true ">extern crate futures;<br><br>use futures::*;<br>use std::time;<br>use std::thread;<br><br>fn suma(a: i32, b: i32) -&gt; SumFuture {<br>    SumFuture{<br>        a: a,<br>        b: b<br>    }<br>}<br><br>struct SumFuture{<br>    a: i32,<br>    b: i32<br>}<br><br>impl Future for SumFuture {<br>    type Item = i32;<br>    type Error = String;<br><br>    fn poll(&amp;mut self) -&gt; Result&lt;Async&lt;i32&gt;,String&gt; {<br>        thread::sleep(time::Duration::from_secs(1));<br>        Ok(Async::Ready(self.a+self.b))<br>    }<br>}<br><br>fn main() {<br>    let c = suma(4,5);<br>    println!("Suma: {}",c.wait().unwrap());<br>}<br></pre><br>Definimos un nuevo futuro, <em>SumFuture</em>, que devuelve el resultado de una suma. El futuro en su función <strong>poll</strong> duerme el hilo 1 segundo y después devuelve el resultado correcto. En la función main llamamos a suma que devuelve un futuro en vez del resultado. Con el futuro, esperamos con <strong>wait</strong> a que se resuelva y lo mostramos. Cuando un futuro se ejecuta se convierte en una <strong>tarea</strong>. Las <strong>tareas</strong> necesitan ejecutores. Wait ejecuta los futuros en el mismo hilo desde el que se hace la llamada, pero existen otras opciones. El programa, tarda un segundo en imprimir el número.<br><br><em>Pero esto no sirve para nada</em><br><br>Bueno, quizá ahora parezca una tontería, pero en cuanto introduzcamos más elementos, todo tendrá más sentido.<br><br>Una característica de los futuros es que se pueden encadenar, tal que así:<br><pre class="lang:rust decode:true ">fn main() {<br>    let c = suma(4,5)<br>    .and_then(|v|{<br>        suma(v,40)<br>    }).and_then(|v|{<br>        suma(v,40)<br>    }).wait().unwrap();<br>    println!("Suma: {}",c);<br>}</pre><br><h2>Ejecutores</h2><br>Los futuros nos ayudan con la concurrencia, esto es porque se puede esperar a varios futuros a la vez sin problema. Una manera de hacerlo es con <strong>CpuPool</strong>, un ejecutor que tiene un pool de hilos ya creados. Su uso es muy sencillo, en este ejemplo vemos como hago dos operaciones en paralelo:<br><pre class="lang:rust decode:true ">fn main() {<br>    let c = suma(4,5)<br>    .and_then(|v|{<br>        suma(v,40)<br>    }).and_then(|v|{<br>        suma(v,40)<br>    });<br>    let d = suma(15,14);<br>    <br>    let pool = CpuPool::new_num_cpus();<br>    let c = pool.spawn(c);<br>    let d = pool.spawn(d);<br>    let (c,d) = c.join(d).wait().unwrap();<br>    println!("SUMAS: {},{}",c,d);<br>}<br></pre><br>Con <strong>spawn</strong> añadimos un nuevo futuro a la ejecución y nos devuelve otro futuro. Con <strong>join</strong> podemos unir dos futuros en uno solo que se resuelva cuando ambos hayan resuelto. Finalmente hacemos <strong>wait</strong> en el hilo principal para esperar a que las sumas se hagan e imprimir el resultado. Existen otros ejecutores. Con Tokio usaremos otro.<br><h2>Tokio</h2><br><a href="https://files.adrianistan.eu/Screenshot-from-2018-02-23-17-16-38.png"><img class="aligncenter size-large wp-image-1345" src="https://files.adrianistan.eu/Screenshot-from-2018-02-23-17-16-38-1024x576.png" alt="" width="840" height="473" /></a><br><br>Tokio es una librería que nos permite ejecutar código de entrada/salida de forma asíncrona, usando futuros por debajo. Son especialmente útiles, la parte de red de Tokio y sus ejecutores correspondientes.<br><pre class="lang:rust decode:true">extern crate tokio;<br>extern crate tokio_io;<br>extern crate futures;<br><br>use futures::prelude::*;<br>use tokio_io::AsyncRead;<br>use futures::Stream;<br>use tokio_io::codec::*;<br>use std::net::*;<br>use tokio::prelude::*;<br><br>fn main(){<br>    let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);<br>    let listener = tokio::net::TcpListener::bind(&amp;socket).unwrap();<br>    let server = listener.incoming().for_each(|socket|{<br>       println!("Cliente recibido");<br>       Ok(())<br>    }).map_err(|err|{<br>        println!("error = {:?}",err);<br>    });<br>    tokio::run(server);<br>}</pre><br>En este ejemplo tenemos un pequeño servidor TCP que se mantiene a la espera de clientes, imprime un mensaje y cierra la conexión. Este servidor solo utiliza un hilo, no obstante, ya usa futuros. <strong>server</strong> es un futuro infinito, que nunca acaba, que ejecutamos en el hilo actual con run-&gt;spawn. El <strong>run</strong> solo es necesario para ejecutar el primer futuro, el que mantendrá viva la aplicación. <strong>spawn</strong> empieza a ejecutar el futuro, que entonces se pasa a denominar <strong>tarea</strong>. Aquí es Tokio en vez de CpuPool, el planificador de tareas.<br><h2>Servidor de Echo en Tokio</h2><br>Ahora vamos a ir con un ejemplo más interesante, ahora el cliente y el servidor estarán conectados durante un tiempo, mandando el cliente un mensaje y el servidor respondiendo. Pero queremos que haya varios clientes simultáneamente. Usando futuros y Tokio podemos hacerlo en un mismo hilo (al estilo Node.js).<br><pre class="lang:rust decode:true">extern crate tokio;<br>extern crate tokio_io;<br>extern crate futures;<br><br>use futures::prelude::*;<br>use tokio_io::AsyncRead;<br>use futures::Stream;<br>use tokio_io::codec::*;<br>use std::net::*;<br>use tokio::prelude::*;<br><br>fn main(){<br>    let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);<br>    let listener = tokio::net::TcpListener::bind(&amp;socket).unwrap();<br>    let server = listener.incoming().for_each(|socket|{<br>       let (writer,reader) = socket.framed(LinesCodec::new()).split();<br>       let action = reader<br>       .map(move |line|{<br>           println!("ECHO: {}",line);<br>           line<br>       })<br>       .forward(writer)<br>       .map(|_|{<br>        })<br>       .map_err(|err|{<br>           println!("error");<br>       });<br>       tokio::spawn(action);<br>       Ok(())<br>    }).map_err(|err|{<br>        println!("error = {:?}",err);<br>    });<br>    tokio::run(server);<br>}</pre><br>¿Qué hacemos ahora? Ahora a cada socket de cliente asignamos una tarea, que se encarga, de forma independiente, de ir leyendo línea a línea (gracias a <strong>LineCodec</strong>).<br><br>A partir de aquí se programa en forma de stream, un patrón muy común en la programación con futuros.<br><br>En el primer <strong>map</strong> imprimimos la línea en el servidor y la seguimos pasando por el stream. El siguiente paso es escribir en writer, con <strong>forward</strong> imprimimos y mandamos esa línea al cliente. Forward a su vez devuelve una tupla con datos (útil para seguir haciendo cosas). Como no los necesitamos, hacemos un <strong>map</strong> cuya única finalidad sea descartar los valores y finalmente un <strong>map_err</strong> para capturar posibles errores. Una vez hecho esto tenemos un futuro listo para ser ejecutado. Iniciamos la tarea con <strong>spawn</strong> y nos olvidamos, pasando a esperar a por un nuevo cliente.<br><br>Ahora, en el servidor podemos manejar varios clientes a la vez, cada uno en una tarea distinta, dentro de un mismo hilo.<br><h2>Cancelando la tarea</h2><br>Esta tarea que maneja el cliente es infinita, es decir, no se para. La razón es que <strong>reader</strong> es un lector que genera un futuro infinito esperando a nuevas líneas. Pero, ¿existe algún método para parar la tarea desde el servidor?<br><br>Esto no es algo demasiado trivial, pero se puede hacer de manera sencilla usando <strong>canales</strong> MPSC de Rust y futuros.<br><pre class="lang:rust decode:true">extern crate tokio;<br>extern crate tokio_io;<br>extern crate futures;<br><br>use futures::prelude::*;<br>use tokio_io::AsyncRead;<br>use futures::Stream;<br>use tokio_io::codec::*;<br>use tokio::prelude::*;<br><br>struct Cancellable{<br>    rx: std::sync::mpsc::Receiver&lt;()&gt;,<br>}<br><br>impl Future for Cancellable {<br>    type Item = ();<br>    type Error = std::sync::mpsc::RecvError;<br><br>    fn poll(&amp;mut self) -&gt; Result&lt;Async&lt;Self::Item&gt;,Self::Error&gt; {<br>        match self.rx.try_recv() {<br>            Ok(_) =&gt; Ok(Async::Ready(())),<br>            Err(_) =&gt; Ok(Async::NotReady)<br>        }<br>    }<br>}<br><br>fn main() {<br>    let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);<br>    let listener = tokio::net::TcpListener::bind(&amp;socket).unwrap();<br>    let server = listener.incoming().for_each(|socket|{<br>        let (writer,reader) = socket.framed(LinesCodec::new()).split();<br>        let (tx,rx) = std::sync::mpsc::channel();<br>        let cancel = Cancellable {<br>            rx: rx,<br>        };<br>        let action = reader<br>        .map(move |line|{<br>            println!("ECHO: {}",line);<br>            if line == "bye"{<br>                println!("BYE");<br>                tx.send(()).unwrap();<br>            }<br>            line<br>        })<br>        .forward(writer)<br>        .select2(cancel)<br>        .map(|_|{<br><br>        })<br>        .map_err(|err|{<br>            println!("error");<br>        });<br>        tokio::spawn(action);<br>        <br>        Ok(())<br>    }).map_err(|err|{<br>        println!("error = {:?}",err);<br>    });<br>    tokio::run(server);<br>}</pre><br>La idea aquí es hacer que de alguna manera, el futuro pueda resolverse y así finalizar la tarea. Una función interesante es <strong>select2</strong> que devuelve un futuro fusión de dos futuros. Este futuro se resuelve (devuelve valor y acaba la tarea) cuando alguno de ellos lo haga. Como el futuro de <strong>reader</strong> nunca acabará, entonces basta con poner un futuro que cuando queramos cerrar la conexión resolveremos.<br><br>Este futuro es <strong>cancel</strong> de tipo <strong>Cancellable</strong>. No viene en la librería, lo he creado arriba y lo que hace es esperar a que el extremo del canal reciba una comunicación. El valor nos da igual, simplemente que se haya producido un ping. Una vez hecho eso, el futuro resuelve y la conexión se cierra.<br><br>En el ejemplo, cuando el cliente manda <em>bye</em> la conexión se cierra.<br><br><a href="https://files.adrianistan.eu/Screenshot-from-2018-02-23-17-15-42.png"><img class="aligncenter size-large wp-image-1344" src="https://files.adrianistan.eu/Screenshot-from-2018-02-23-17-15-42-1024x576.png" alt="" width="840" height="473" /></a><br><br>Y esto es una breve introducción a los futuros y a Tokio en Rust. Todavía queda ver como podemos usar <strong>async/await</strong> para no tener que escribir todo esto en forma de stream.]]></description>
                <comments>https://blog.adrianistan.eu/futures-tokio-programar-forma-asincrona-rust</comments>
                <pubDate>Fri, 23 Feb 2018 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Te espero en la feria Espacios de Ingenio</title>
                <link>https://blog.adrianistan.eu/te-espero-la-feria-espacios-ingenio</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/te-espero-la-feria-espacios-ingenio</guid>
                <description><![CDATA[Los días próximos 15 y 16 de marzo tendrá lugar la feria Espacios de Ingenio. Tendrá lugar en la Escuela Técnica Superior de Arquitectura, en la ciudad de Valladolid. El objetivo es interesar a los alumnos de bachillerato en las carreras técnicas.<br><br><a href="https://files.adrianistan.eu/Arquitectura.jpg"><img class="aligncenter size-large wp-image-1337" src="https://files.adrianistan.eu/Arquitectura-1024x672.jpg" alt="" width="840" height="551" /></a><br><br>El viernes 16 por la mañana daré el taller (dos sesiones), <em><strong>Programar es fácil: ponte a ello</strong></em><em>. </em>En el taller se iniciará a los alumnos en la programación con Python.<br><br>Si tienes algún familiar que le pueda interesar, házselo saber al instituto/colegio para que le lleven a la actividad.<br><p style="text-align: center;"><a href="http://espaciosdeingenio.uva.es/17399/section/10693/espacios-de-ingenio.-creatividad-tecnologia-sostenibilidad.-2018.html">Inscripciones y más información</a></p>]]></description>
                <comments>https://blog.adrianistan.eu/te-espero-la-feria-espacios-ingenio</comments>
                <pubDate>Wed, 21 Feb 2018 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Lenguajes de programación que todo buen programador debe conocer</title>
                <link>https://blog.adrianistan.eu/lenguajes-programacion-buen-programador-conocer</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/lenguajes-programacion-buen-programador-conocer</guid>
                <description><![CDATA[Dice Bjarne Stroustrup (creador de C++) que nadie <a href="https://www.youtube.com/watch?v=NvWTnIoQZj4">debería llamarse un profesional si no conoce al menos 5 lenguajes suficientemente diferentes entre sí.</a> Comparto con él esa afirmación, así que he decidido hacer una lista con esos 5 lenguajes suficientemente diferentes entre sí. La razón de que sean diferentes entre sí es que implementan paradigmas distintos.<br><h2>Paradigmas de programación</h2><br>Cada lenguaje está moldeado en base a uno o varios paradigmas de programación. Aunque no hay una teoría con la que todos los autores esten de acuerdo, bajo mi punto de vista existen dos grandes grupos de paradigmas de programación. <strong>Imperativos</strong> y <strong>Declarativos</strong>. Los imperativos responden a la pregunta de <em>¿Cómo se calcula esto?</em> y los declarativos <em>¿Cuál es el resultado de esto?</em>. Otra manera de verlo es ver al paradigma imperativo como un intento de simplificar la electrónica subyacente. El paradigma declarativo por contra muchas veces se origina de la teoría matemática y posteriormente se aplica al ordenador.<br><br>Cada uno de estos paradigmas a su vez tienen más sub-paradigmas y luego existen paradigmas <em>transversales</em>, es decir, que se pueden aplicar (o no) tanto en lenguajes imperativos como en lenguajes declarativos.<br><br>Un buen programador necesita conocer estos paradigmas.<br><h2>Prolog</h2><br>Prolog es un claro ejemplo de <strong>programación lógica</strong>. Se trata de un lenguaje declarativo. Diseñado en los años 70 en Francia, Prolog tuvo mucha popularidad en el desarrollo de Inteligencia Artificial debido a sus características lógicas. En esencia, Prolog se basa en la demostración de predicados, similares a los del álgebra de predicados.<br><br><a href="https://files.adrianistan.eu/LogicaPredicados.png"><img class="size-full wp-image-1326" src="https://files.adrianistan.eu/LogicaPredicados.png" alt="" width="554" height="154" /></a> Ejemplos de lógica de predicados<br><br>Un programa Prolog es en realidad un conjunto de afirmaciones o <strong>predicados</strong>. En tiempo de ejecución se realizan preguntas sobre predicados. Prolog intenta entonces demostrar la veracidad del predicado, para ello usa el mecanismo de <em>backtracing</em>. Una característica muy interesante de Prolog es el <em>pattern matching</em>, que básicamente permite preguntar para qué valor de una variable se cumple un predicado. Esto permite realizar cosas muy interesantes:<br><pre class="lang:lisp decode:true">% PROGRAMA EN PROLOG<br>% PREDICADOS QUE SON VERDADEROS<br><br>hijo(bernardo,sonia).<br>hijo(veronica,sonia).<br>hijo(bernardo,luis).<br>hijo(veronica,luis).<br><br>varon(bernardo).<br>varon(luis).<br>mujer(veronica).<br>mujer(sonia).<br><br>% REGLAS<br><br>madre(X,Y) :- hijo(X,Y), mujer(X).<br></pre><br>Ahora, para saber quién es la madre de Sonia intentamos demostrar:<br><pre class="lang:default decode:true">?- madre(X,sonia).</pre><br>Y responderá X = veronica.<br><br>En predicados sin variables, Prolog solo devuelve true o false.<br><br>Existen varios compiladores/intérpretes de Prolog, siendo el más conocido SWI-Prolog, multiplataforma y con una extensa librería que incluye GUI multiplataforma y framework web. También existe GNU Prolog y Visual Prolog (antiguamente conocido como Turbo Prolog), aunque este último no se le considera Prolog auténtico por ser demasiado diferente al resto.<br><h2>Haskell</h2><br><a href="https://www.haskell.org/">Haskell</a> es un lenguaje declarativo que implementa el <strong>paradigma funcional</strong>. Es uno de los pocos lenguajes funcionales que son 100% puros. Se entiende por puros como la capacidad de no generar efectos colaterales. Haskell es un lenguaje fuertemente tipado y deriva de la <strong>teoría de categorías</strong>. Haskell ha sido objeto (hasta cierto punto merecido) de muchas bromas sobre este asunto, ya que para la mayoría de programadores, conocer teoría de categorías no es demasiado práctico.<br><br><a href="https://files.adrianistan.eu/MemeHaskell.jpg"><img class="aligncenter size-full wp-image-1328" src="https://files.adrianistan.eu/MemeHaskell.jpg" alt="" width="625" height="412" /></a>Otra característica de Haskell es que es <strong>perezoso</strong>, lo que significa que no calculará nada que no sea estrictamente necesario (esto puede parecer muy raro hasta que lo entiendes en la práctica).<br><pre class="lang:haskell decode:true ">module Main where<br><br>import Data.Matrix<br>import qualified Data.Vector as Vector<br>import qualified Reader<br>import qualified Data.Maybe as Maybe<br><br>main :: IO ()<br>main = do<br>    matrix &lt;- Reader.readFile "gosperglidergun.txt"<br>    putStrLn "Iterar cuantas veces?"<br>    n &lt;- getLine<br>    let times = read n :: Int<br>    let finalMatrix = Prelude.iterate Main.iterate matrix<br>    putStrLn $ prettyMatrix $ finalMatrix !! times<br><br><br>iterate :: Matrix Int -&gt; Matrix Int<br>iterate m = <br>    if hasToGrow then<br>        matrix (nrows m +2) (ncols m +2) (\(i,j) -&gt; builder (i-1,j-1))<br>    else<br>        matrix (nrows m) (ncols m) builder<br>    where<br>        builder (i,j) = <br>            if get (i,j) == 0 then<br>                if hasToBorn (i,j) then<br>                    1<br>                else<br>                    0<br>            else<br>                if hasToDie (i,j) then<br>                    0<br>                else<br>                    1<br>        hasToGrow = <br>            Vector.sum (getCol (ncols m) m) &gt; 0 || <br>            Vector.sum (getRow (nrows m) m) &gt; 0 ||<br>            Vector.sum (getCol 1 m) &gt; 0 ||<br>            Vector.sum (getRow 1 m) &gt; 0<br>        get (i,j) = Maybe.fromMaybe 0 (safeGet i j m)<br>        hasToBorn (i,j) = sumNeighbors (i,j) == 3<br>        hasToDie (i,j) = sumNeighbors (i,j) /= 2 &amp;&amp; sumNeighbors (i,j) /= 3<br>        sumNeighbors (i,j) = <br>            get (i-1,j-1) + get (i,j-1) + get (i+1,j-1) <br>            + get (i-1,j) + get (i+1,j)<br>            + get (i-1,j+1) + get (i,j+1) + get (i+1,j+1)<br></pre><br>Este código pertenece al <a href="https://blog.adrianistan.eu/2018/01/20/juego-la-vida-conway-c-interfaz-grafica/">juego de la vida de Conway</a> que (también) implementé en Haskell, simplemente por curiosidad, ya que no está optimizado. Faltaría el módulo Reader, así que no intentéis compilarlo directamente.<br><br>Haskell como tal no tiene variables ni bucles y sus <em>condicionales</em> no son exactamente iguales a los de los lenguajes imperativos (aunque se use if, en el caso de Haskell son bloques que siempre deben devolver un valor).<br><br><a href="https://files.adrianistan.eu/Screenshot-2018-2-18-Haskell-Language.png"><img class="aligncenter size-large wp-image-1329" src="https://files.adrianistan.eu/Screenshot-2018-2-18-Haskell-Language-1024x481.png" alt="" width="840" height="395" /></a>Aunque siempre ha sido un lenguaje académico (nació en 1994, un año antes que Java), ahora ha alcanzado bastante popularidad y algunas empresas como <a href="https://code.facebook.com/posts/745068642270222/fighting-spam-with-haskell/">Facebook lo usan en producción</a>.<br><br>El compilador más conocido, capaz de generar código nativo, es GHC. Este dispone de un REPL llamado GHCi y también compila a JavaScript y se está trabajando en WebAssembly. Otro intérprete es <strong><a href="https://www.haskell.org/hugs/">Hugs</a></strong>, pero solo es compatible con Haskell98.<br><br>La forma recomendada de instalar GHC es con <strong>Stack</strong>. <a href="http://haskellstack.org">Usa Stack</a> y ahórrate quebraderos de cabeza.<br><br>Otros lenguajes similares a Haskell son <a href="https://blog.adrianistan.eu/2017/07/15/formulario-registro-elm/"><strong>Elm</strong></a>, <strong>PureScript</strong>, <strong>Eta</strong> e <strong>Idris</strong>. Este último compila a JavaScript y pone énfasis en su librería, que es capaz de competir con Angular y React.<br><h2>Racket (Lisp)</h2><br><strong>Lisp</strong> no puede faltar nunca. Se trata de uno de los primeros lenguajes de programación y sigue siendo tan actual como el primer día, gracias a su simple pero efectivo diseño, inspirado en el <strong>cálculo lambda</strong> de Alonzo Church. No obstante, Lisp ha evolucionado mucho, si me preguntan que dialecto/lenguaje de Lisp merece la pena aprender ahora diría <a href="https://racket-lang.org/"><strong>Racket</strong></a>. Racket es un lenguaje de <strong>programación funcional</strong> y <strong>programación orientada a objetos</strong>. Racket no es 100% puro como Haskell (la mayoría de dialectos de Lisp no lo son) pero sigue siendo muy interesante. También, tiene <strong>tipado débil</strong> en contraposición al tipado fuerte de Haskell.<br><br>Racket desciende a su vez de <strong>Scheme</strong>, que es una de los ramas principales de Lisp, siendo la otra <strong>Common Lisp</strong>. ¿Por qué estas diferencias? La gente de Scheme prefiere un lenguaje elegante, lo más funcional posible mientras que la gente de Common Lisp prefirieron sacrificar eso a cambio de un lenguaje más práctico en el mundo real.<br><br>Racket cuenta con una extensísima librería estándar capaz de realizar todo lo que te imagines sin gran problema. Racket también soporta las famosas y potentes macros.<br><pre class="lang:scheme decode:true ">#lang racket<br><br>(require 2htdp/image) ; draw a picture<br>(let sierpinski ([n 8])<br>  (cond<br>    [(zero? n) (triangle 2 'solid 'red)]<br>    [else (define t (sierpinski (- n 1)))<br>          (freeze (above t (beside t t)))]))</pre><br><a href="https://files.adrianistan.eu/Screenshot-from-2018-02-18-16-23-34.png"><img class="aligncenter size-large wp-image-1330" src="https://files.adrianistan.eu/Screenshot-from-2018-02-18-16-23-34-1024x576.png" alt="" width="840" height="473" /></a><a href="https://blog.adrianistan.eu/2017/05/24/triangulo-sierpinski-javascript/">Triángulo de Sierpinski</a> en el IDE DrRacket.<br><h2>JavaScript</h2><br>JavaScript está en todas partes. Es uno de los lenguajes con mayor aplicación práctica. Web, servidor, bases de datos, scripting, plugins e incluso IoT. Por eso JavaScript me parece un lenguaje que deba estar en esta lista. Y sin embargo no es fácil categorizarlo correctamente. Ante todo, estamos ante un lenguaje de programación <strong>imperativo</strong>, con <strong>orientación a objetos</strong> y buen soporte a la <strong>orientación a eventos</strong>.<br><br>Y antes de que se me venga alguien a comerme, sí, JavaScript está orientado a objetos, aunque no siguen el patrón de clase/herencia que C++ y Java tienen tan acostumbrados a la gente. La orientación a objetos por <strong>prototipos</strong> no la inventó JavaScript, sino que ya estaba en otros lenguajes como <strong>Self</strong> y más actualmente <strong>Io</strong>. Y realmente lenguajes como Python o Ruby no se alejan mucho de esto internamente.<br><br>Actualmente, con la versión ES7, tenemos muchas cosas interesantes en <strong>programación asíncrona</strong> y clases al estilo Java que no son más que azúcar sintáctico sobre el verdadero modelo de JS.<br><br>Definitivamente, JavaScript es un lenguaje muy interesante y aunque a algunas personas les pueda parecer un <em>caos</em>, ciertamente es muy productivo y útil. Aprovechar al máximo JavaScript requiere pensar de forma distinta a como lo harías en otros lenguajes imperativos.<br><pre class="lang:js decode:true ">async function lastGist(username){<br>    let f = await fetch(`https://api.github.com/users/${username}/gists`);<br>    let data = await f.json();<br>    try{<br>        return data[0].description;<br>    }catch(e){<br>        return undefined;<br>    }<br>}<br><br>let users = ["aarroyoc","DanielBV","hecsaen"];<br>Promise.all(users.map(lastGist)).then((gists)=&gt;{<br>    gists.forEach((gist)=&gt;{<br>        console.log(`Last gist: ${gist}`);<br>    });<br>});<br></pre><br><h2>C# (o Java)</h2><br>C# es otro lenguaje bastante complejo, <strong>imperativo</strong>, <strong>orientado a objetos por clases</strong>, con partes de <strong>programación funcional</strong> y <strong>programación asíncrona</strong>. Pero el sistema de la orientación a objetos aquí es distinto. Aunque Alan Kay, creador de <strong>Smalltalk</strong> y casi casi de la orientación a objetos, opine que el estilo de C++ y de Java son <em>putas mierdas</em>, lo cierto es que es lo más usado actualmente. Herencia, clases, interfaces, etc<br><br>Personalmente prefiero C#, ya que como lenguaje es más potente que Java, pero ambos al final tienen bastantes características comunes entre sí.<br><pre class="lang:c# decode:true">using System;<br><br>class MainClass {<br>  public static void Main (string[] args) {<br>    var names = new List&lt;String&gt;<br>    {<br>        "Ana",<br>        "Felipe",<br>        "Emilia"<br>    };<br><br>    foreach (var name in names)<br>    {<br>        Console.WriteLine($"Hello {name}");<br>    }<br>  }<br>}</pre><br>Para C# el compilador más usado es Roslyn, actualmente disponible en .NET Framework, .NET Core y Mono.<br><br>Otros lenguajes parecidos son <strong>Java</strong> o si preferimos una sintaxis tipo Pascal: <strong>Delphi/Object-Pascal</strong> tiene conceptos muy similares.<br><h2>Conclusión</h2><br>Con esto ya tendríamos 5 lenguajes suficientemente diferentes. Ahora bien, nos hemos dejado lenguajes muy interesantes, tanto desde el punto de vista práctico como teórico. No puedo dejar de mencionar a <strong>Smalltalk</strong> por su implementación de los objetos, a <strong>C</strong> por su ligera capa de abstracción sobre ensamblador, al propio <strong>Ensamblador</strong> porque es lo que ejecuta la CPU realmente, a <strong>Python</strong> por su diseño así como su gran uso en ciencia de datos y scripts o a <strong>Rust</strong>, un lenguaje imperativo con sistema de <strong>traits,</strong> <strong>semántica de movimiento</strong>, <strong>pattern matching</strong>.<br><br>Merece también la pena mirar <strong>FORTH</strong>, <strong>SQL</strong> (quizá el lenguaje declarativo más usado del mundo) y lenguajes que se adapten bien a la <strong>programación reactiva</strong> (hay sobre todo librerías, aunque algún lenguaje como <strong>Simulink</strong> lo implementa). <strong>Mathemathica</strong> implementa también el paradigma de <strong>programación simbólica</strong>, muy poco explotado en otros lenguajes.<br><br>Estos han sido mis 5 lenguajes, ¿cuáles serían los tuyos?]]></description>
                <comments>https://blog.adrianistan.eu/lenguajes-programacion-buen-programador-conocer</comments>
                <pubDate>Sun, 18 Feb 2018 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>¿Cómo recuperar archivos borrados?</title>
                <link>https://blog.adrianistan.eu/recuperar-archivos-borrados</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/recuperar-archivos-borrados</guid>
                <description><![CDATA[Inevitablemente te ha tenido que pasar. Estabas manipulando ese fichero tan importante que contenía tu trabajo de clase. Y lo borraste. Todo el mundo puede tener esos despistes y por eso los sistemas operativos modernos suelen llevar una <em>Papelera de Reciclaje</em>, una forma de recuperar archivos que hemos borrado sin querer, dándonos otra oportunidad para pensar si realmente queremos borrar el archivo. No obstante, ¿qué pasa si has vaciado la papelera sin darte cuenta de lo que había dentro? O peor, hay pinchos USB que carecen de la opción de papelera. ¿Está todo perdido? No, todavía podemos probar un programa de recuperación. Existen varios programas como <strong>Recuva</strong> o Disk Drill. Yo voy a usar Recuva que es más popular, <a href="http://descargarrecuva.com"> puedes descargar aquí Recuva</a>. Se trata además de un programa gratuito, sin coste para el usuario (aunque existe una versión pro).<br><br><a href="https://files.adrianistan.eu/RecuvaInstaller.png"><img class="aligncenter size-large wp-image-1314" src="https://files.adrianistan.eu/RecuvaInstaller-1024x576.png" alt="" width="840" height="473" /></a>Una vez la instalación haya sido correcta (el procedimiento es trivial), ejecutamos el programa con <em>Run Recuva</em>.<br><h2>Asistente de Recuva</h2><br>El modo por defecto, es un asistente de que nos va haciendo preguntas sobre el tipo de archivo que vayamos a recuperar.<br><br><a href="https://files.adrianistan.eu/RecuvaWizard.png"><img class="aligncenter size-full wp-image-1315" src="https://files.adrianistan.eu/RecuvaWizard.png" alt="" width="540" height="513" /></a>Si el archivo que queremos recuperar no forma parte de ninguno de estos tipos de archivos, seleccionamos la primera opción. Luego nos pregunta sobre la antigua localización física del fichero. Es decir, en qué disco duro estaba. Si nos acordamos, el tiempo de recuperación puede reducirse mucho, si no lo sabemos la efectividad es la misma, pero tarda más.<br><br>Una vez le hayamos dado los datos, empezará la búsqueda de archivos que han sido recientemente borrados.<br><br><a href="https://files.adrianistan.eu/RecuvaProgress.png"><img class="aligncenter size-full wp-image-1316" src="https://files.adrianistan.eu/RecuvaProgress.png" alt="" width="442" height="237" /></a><br><h2>Lista de elementos</h2><br>Una vez haya finalizado la búsqueda, tendremos una lista de elementos que se pueden recuperar. Si son imágenes, podremos ver una miniatura de ellas.<br><br><a href="https://files.adrianistan.eu/RecuvaImagen.png"><img class="aligncenter size-large wp-image-1317" src="https://files.adrianistan.eu/RecuvaImagen-1024x576.png" alt="" width="840" height="473" /></a>Para recuperar el archivo lo seleccionamos y pulsamos <em>Recuperar</em>. Especificamos la carpeta donde van a ponerse los archivos extraídos de la recuperación y comenzamos. Si todo va bien, tendremos los archivos listos en la carpeta especificada. Tendremos que renombrar el archivo, ya que muchas veces suelen salir nombres que no tienen nada que ver con el original.<br><br><a href="https://files.adrianistan.eu/AntiguoRioEsguevaValladolid.png"><img class="wp-image-1321 size-large" src="https://files.adrianistan.eu/AntiguoRioEsguevaValladolid-1024x570.png" alt="" width="840" height="468" /></a> La foto son unos puentes antiguos bajo tierra del río Esgueva que se encuentran en Valladolid y que han salido al hacer obras en un solar<br><br>Recuva recomienda restaurar los archivos en unidades físicas diferentes para mejorar las probabilidades de éxito.<br><br>Existe también un modo avanzado, que tiene más opciones, permite hacer búsquedas más selectivas, etc... Podemos acceder fácilmente a él, haciendo click en <em>Cambiar al modo avanzado</em>.<br><h2>¿Cómo funcionan estos programas?</h2><br>¿Por qué funcionan los programas como Recuva? Realmente funcionan porque los borrados en los discos duros y memorias USB son <em>perezosos</em>. Es decir, borrar algo simplemente es decir que esa zona del disco se puede usar sin temor para nuevos archivos, pero no modifica nada más. Esto hace que los borrados sean muchos más rápidos y programas como Recuva lo aprovechan para recupera archivos si no ha llegado un fichero nuevo a esa zona. Es por eso importante tener en cuenta, que a más archivos que hayamos creado después del borrado, menos probabilidad hay de encontrarlo.]]></description>
                <comments>https://blog.adrianistan.eu/recuperar-archivos-borrados</comments>
                <pubDate>Fri, 02 Feb 2018 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Tutorial de introducción a Godot 3.0. Juego de Snake en C#</title>
                <link>https://blog.adrianistan.eu/tutorial-introduccion-godot-3-0-juego-snake-c</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/tutorial-introduccion-godot-3-0-juego-snake-c</guid>
                <description><![CDATA[Llegó el día. <a href="https://godotengine.org/article/godot-3-0-released">Godot 3.0 salió a la luz</a>. Se trata de una versión con muchas mejoras respecto a Godot 2.x. Se trata de un motor de videojuegos software libre, compatible con la mayoría de sistemas operativos (y consolas a través de una compañía privada). Aprovechando la ocasión voy a explicar como hacer un juego simple, usando C# y el motor 2D de Godot. Este tutorial sirve para familiarizarse con el motor.<br><h2>Instalando Godot</h2><br>Lo primero que tenemos que hacer es instalar Godot. Para ello vamos a la <a href="https://godotengine.org/download">página de descarga</a> y descargamos la versión que corresponda a nuestro sistema con Mono.<br><br><a href="https://files.adrianistan.eu/GodotDownload.png"><img class="aligncenter size-large wp-image-1300" src="https://files.adrianistan.eu/GodotDownload-1024x567.png" alt="" width="840" height="465" /></a>Es requisito indispensable tener instalado <strong>Mono SDK</strong>, tanto en Linux como en Windows. El Mono SDK se descarga desde <a href="http://www.mono-project.com/download/">http://www.mono-project.com/download/</a>.<br><br>Una vez descargado tendremos un fichero con tres archivos. Descomprímelos en una carpeta. Ahora simplemente puedes ejecutar el fichero ejecutable.<br><h2>Ventana de proyectos</h2><br>Nada más arrancar tendremos una ventana de proyectos. Desde ahí podemos abrir proyectos que hayamos creado, descargar demos y plantillas o simplemente crear un proyecto nuevo.<br><br><a href="https://files.adrianistan.eu/GodotProyectos.png"><img class="aligncenter size-large wp-image-1301" src="https://files.adrianistan.eu/GodotProyectos-1024x576.png" alt="" width="840" height="473" /></a>Si le damos a <em>Proyecto nuevo</em> procederemos a la creación de un nuevo proyecto (¡¿qué complicado, verdad?!).<br><br>Una vez hecho esto ya estaríamos en el editor propiamente dicho.<br><br><a href="https://files.adrianistan.eu/GodotEditor.png"><img class="aligncenter size-large wp-image-1302" src="https://files.adrianistan.eu/GodotEditor-1024x576.png" alt="" width="840" height="473" /></a><br><h2>Editor</h2><br>Rápidamente cambiamos al modo 2D y hacemos zoom hacia atrás. Vamos a ajustar la pantalla. Para ello vamos a <em>Proyecto-&gt;Ajustes de proyecto</em>. Buscamos el apartado <em>Display-&gt;Window</em> y ajustamos la resolución base. Esta será la que usaremos para poner los elementos en pantalla. Después vamos a <em>Stretch </em>y configuramos el modo 2D y mantener el aspect ratio.<br><br>Esta configuración sirve para empezar con juegos simples en 2D puedan escalar fácilmente a distintas pantallas sin tener problemas de descuadres.<br><br><a href="https://files.adrianistan.eu/GodotWindowSettings.png"><img class="aligncenter size-large wp-image-1303" src="https://files.adrianistan.eu/GodotWindowSettings-1024x576.png" alt="" width="840" height="473" /></a>Ahora hacemos click en el más (+) para añadir un nodo a la escena.<br><br>Para nuestro juego, voy a añadir un <strong>Node2D</strong>, que hereda de <strong>CanvasItem</strong> y tiene APIs para dibujar rectángulos y círculos..<br><br><a href="https://files.adrianistan.eu/GodotNode2D.png"><img class="aligncenter size-large wp-image-1304" src="https://files.adrianistan.eu/GodotNode2D-1024x575.png" alt="" width="840" height="472" /></a>No lo tocamos y hacemos click derecho en el Nodo. Damos click a Attach Script:<br><br><a href="https://files.adrianistan.eu/GodotAttachScript.png"><img class="aligncenter size-large wp-image-1305" src="https://files.adrianistan.eu/GodotAttachScript-1024x576.png" alt="" width="840" height="473" /></a>Es importante tener un nombre de script <strong>distinto</strong> a Node2D. Esto puede dar problemas. Una vez hecho esto, se nos mostrará esta pantalla, que nos permite escribir el código en C#. A partir de ahora voy a usar Visual Studio Code, porque lo considero mejor para escribir código en C# que el editor de Godot. Creamos también dos Sprite hijos del Node2D. Apple y SnakeBody van a llamarse y les creamos scripts de C# también.<br><br><a href="https://files.adrianistan.eu/GodotNodes.png"><img class="aligncenter size-large wp-image-1306" src="https://files.adrianistan.eu/GodotNodes-1024x576.png" alt="" width="840" height="473" /></a><br><h2>Dibujar en pantalla</h2><br>Abrimos con el VS Code el fichero .cs que hemos creado, en mi caso, Snake.cs.<br><br>Dentro veremos una clase con dos funciones, <strong>_Ready</strong> y <strong>_Process</strong>. Estas son solo un ejemplo de las funciones que podemos sobreescribir en las clases asociadas a nodos. Otras funciones serían: <strong>_Draw, _Input</strong>. ¿Cuándo tenemos que usar cada una? Ready es una especie de constructor, ahí inicializamos todo lo que haga falta. Process es llamada constantemente, ahí hacemos las actualizaciones y comprobaciones pertinentes. Draw permite dibujar (si no trabajamos con imágenes es muy útil). Draw no se llama útil. ¿Qué tenemos que hacer en Snake.cs? Bueno, hay que tener un timer para ir generando manzanas, así como comprobar que la serpiente se ha comido la manzana.<br><pre class="lang:c# decode:true" title="Snake.cs">using Godot;<br>using System;<br>using System.Timers;<br><br>public class Snake : Node2D<br>{<br>    // 640x360<br>    // casillas de 40x40 (mcd 640,360)<br>    private System.Timers.Timer timer; // usamos los timers de .NET<br>    private static readonly Random rnd = new Random(); // random de .NET<br>    private Apple apple; // manzana activa<br>    private SnakeBody body; // serpiente<br>    public override void _Ready()<br>    {<br>        body = GetNode("SnakeBody") as SnakeBody; // obtenemos el nodo SnakeBody<br>        body.Position = new Vector2(0,0); // arriba a la izquierda<br>        timer = new System.Timers.Timer(10000); // cada 10 s<br>        timer.Elapsed += NewApple; // se llama a NewApple<br>        timer.AutoReset = true; // de forma infinita<br>        timer.Start(); // y empezamos a contar ya!<br>        apple = GetNode("Apple") as Apple; // obtenemos el nodo Apple<br>        apple.Position = new Vector2(rnd.Next(15)*40,rnd.Next(8)*40); // posicion aleatoria<br>    }<br><br>    public void NewApple(object src ,ElapsedEventArgs e)<br>    {<br>        if(apple != null){<br>            RemoveChild(apple); // quita el nodo de la escena si estaba<br>        }<br>        apple = new Apple();<br>        apple.Position = new Vector2(rnd.Next(0,15)*40,rnd.Next(0,8)*40);<br>        AddChild(apple); // anade motor a la escena<br>    }<br><br>    public override void _Process(float delta)<br>    {<br>        // siempre se comprueba si la serpiente se come la manzana<br>        if(apple != null){<br>            if(body.TryEat(apple)){<br>                RemoveChild(apple);<br>                apple = null;<br>            }<br>        }<br>        <br>    }<br>}</pre><br>En este código ya vemos cosas interesantes. La primera es que podemos usar cualquier librería de .NET, en este caso he usado clases que forman parte de .NET Standard pero cualquier librería que funcione con Mono debería funcionar. También se puede usar NuGet, al final, Godot tiene un fichero SLN y un CSPROJ, por lo que no es más que un proyecto normal de C#.<br><br>La segunda cosa es que hay varias funciones para interactuar con Godot en sí: <strong>GetNode</strong> (para obtener un nodo, hay que hacer casting), <strong>AddChild</strong> (para añadir un nodo a una escena) y <strong>RemoveChild</strong> (para quitar un nodo a una escena).<br><pre class="lang:c# decode:true" title="SnakeBody.cs">using Godot;<br>using System;<br>using System.Collections.Generic;<br>using System.Linq;<br><br>public class SnakeBody : Sprite<br>{<br>    private float time = 0;<br>    private enum Direction{<br>        LEFT,<br>        RIGHT,<br>        UP,<br>        DOWN<br>    };<br>    private Direction direction;<br>    private List&lt;Rect2&gt; body;<br>    private bool eat = false;<br>    public override void _Ready()<br>    {<br>        // Called every time the node is added to the scene.<br>        // Initialization here<br>        direction = Direction.RIGHT;<br>        body = new List&lt;Rect2&gt;();<br>        body.Add(new Rect2(0,0,40,40));<br>        body.Add(new Rect2(40,0,40,40));<br>        SetZIndex(1);<br>    }<br><br>    public override void _Draw()<br>    {<br>        var color = new Color(1,0,0);<br>        foreach(var rect in body){<br>            this.DrawRect(new Rect2(rect.Position.x+2,rect.Position.y+2,36,36),color);<br>        }<br>    }<br><br>    public bool TryEat(Apple apple)<br>    {<br>        if(body[0].Position.x == apple.Position.x &amp;&amp; body[0].Position.y == apple.Position.y){<br>            Console.WriteLine("EAT!");<br>            eat = true;<br>        }<br><br>        return eat;<br>    }<br><br>    public bool Crash()<br>    {<br>        return body.Skip(1).Any(t=&gt;{<br>            return t.Position.x == body[0].Position.x &amp;&amp; t.Position.y == body[0].Position.y;<br>        });<br>    }<br><br>    public override void _Process(float delta)<br>    {<br>        // Called every frame. Delta is time since last frame.<br>        // Update game logic here.<br>        time += delta;<br>        if(time &gt; 0.5){<br>            Vector2 translation;<br>            switch(direction){<br>                case Direction.RIGHT: translation=new Vector2(40,0);break;<br>                case Direction.LEFT: translation=new Vector2(-40,0);break;<br>                case Direction.UP: translation = new Vector2(0,-40);break;<br>                default: translation = new Vector2(0,40);break;<br>            }<br>            if(body.Count &gt; 0){<br>                var newRect = new Rect2(body[0].Position,body[0].Size);<br>                newRect.Position += translation;<br>                if(newRect.Position.x &lt; 0){<br>                    newRect.Position = new Vector2(600,newRect.Position.y);<br>                }<br>                if(newRect.Position.x &gt; 600){<br>                    newRect.Position = new Vector2(0,newRect.Position.y);<br>                }<br>                if(newRect.Position.y &lt; 0){<br>                    newRect.Position = new Vector2(newRect.Position.x,320);<br>                }    <br>                if(newRect.Position.y &gt; 320){<br>                    newRect.Position = new Vector2(newRect.Position.x,0);<br>                }<br><br>                body.Insert(0,newRect);<br>                if(!eat){<br>                    body.RemoveAt(body.Count-1);<br>                }<br>                if(Crash()){<br>                    Console.WriteLine("CRASH! Game Over");<br>                }<br>            }<br>            this.Update();<br>            time = 0;<br>            eat = false;<br>        }<br>    }<br><br>    public override void _Input(InputEvent @event)<br>    {<br>        if(@event.IsAction("move_left") &amp;&amp; direction != Direction.RIGHT)<br>        {<br>            direction = Direction.LEFT;<br>            return;<br>        }<br>        if(@event.IsAction("move_right") &amp;&amp; direction != Direction.LEFT)<br>        {<br>            direction = Direction.RIGHT;<br>            return;<br>        }<br>        if(@event.IsAction("move_up") &amp;&amp; direction != Direction.DOWN)<br>        {<br>            direction = Direction.UP;<br>            return;<br>        }<br>        if(@event.IsAction("move_down") &amp;&amp; direction != Direction.UP)<br>        {<br>            direction = Direction.DOWN;<br>            return;<br>        }<br>    }<br>}</pre><br>El código de <strong>SnakeBody.cs</strong> es más largo, pero no más complejo. Como vemos la serpiente la represento como <strong>List&lt;Rect2&gt;</strong>. Además hay una variable <strong>time</strong> puesta para que la serpiente vaya a trompicones (como el snake auténtico).<br><br>Se puede ver como la función <strong>DrawRect</strong> nos sirve para dibujar un rectángulo. La API no tiene pérdida y es parecida a la API de Cairo o la Canvas de HTML5.<br><br>También se puede ver como se puede usar LINQ para comprobar las intersecciones de la serpiente consigo misma (en realidad se comprueba la cabeza con el resto de trozos, ya que la cabeza es la parte que va a estar presente en todos los golpes).<br><br>Con <strong>Update</strong> se fuerza una nueva llamada a _Draw.<br><br>Por último tenemos <strong>_Input</strong>. En Godot, la entrada se maneja por <strong>acciones</strong>, una capa de abstracción. Esto quiere decir que no es recomendable comprobar si una tecla ha sido pulsada, simplemente se asignan teclas a acciones desde Godot (o desde el juego en un panel de configuración) y en nuestro código comprobar las acciones.<br><h2>Crear acciones</h2><br>Para crear acciones vamos a <em>Proyecto-&gt;Ajustes de Proyecto-&gt;Mapa de entrada</em> y creamos las acciones que creamos convenientes. Yo las he llamado <strong>move_left, move_right, move_down</strong> y<strong> move_up</strong>. Luego las asignamos teclas de forma muy intuitiva.<br><br><a href="https://files.adrianistan.eu/GodotKeymap.png"><img class="aligncenter size-large wp-image-1307" src="https://files.adrianistan.eu/GodotKeymap-1024x576.png" alt="" width="840" height="473" /></a>Con esto ya tendríamos todo para un snake completito. Si le damos a ejecutar, podemos ver el juego en acción.<br><br><a href="https://files.adrianistan.eu/GodotSnake.png"><img class="aligncenter size-large wp-image-1308" src="https://files.adrianistan.eu/GodotSnake-1024x576.png" alt="" width="840" height="473" /></a><a href="https://files.adrianistan.eu/GodotSnake-2.png"><img class="aligncenter size-large wp-image-1309" src="https://files.adrianistan.eu/GodotSnake-2-1024x576.png" alt="" width="840" height="473" /></a> <a href="https://files.adrianistan.eu/GodotSnake-3.png"><img class="aligncenter size-large wp-image-1310" src="https://files.adrianistan.eu/GodotSnake-3-1024x576.png" alt="" width="840" height="473" /></a>Todo el código del juego lo tenéis en el <a href="https://github.com/aarroyoc/blog-ejemplos/tree/master/snake">GitHub de ejemplos del blog</a> y se puede importar a vuestro Godot.<br><br>&nbsp;<br><br>Al juego le faltan muchas cosas, como una pantalla de game over cuando se produce un choque. Y puntuación. Y más detalles. Pero eso ya os lo dejo a vosotros.<br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/tutorial-introduccion-godot-3-0-juego-snake-c</comments>
                <pubDate>Tue, 30 Jan 2018 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Generar frases con cadenas de Markov. Machine Learning en Python</title>
                <link>https://blog.adrianistan.eu/generar-frases-cadenas-markov-machine-learning-python</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/generar-frases-cadenas-markov-machine-learning-python</guid>
                <description><![CDATA[Hoy vamos a hacer un ejercicio muy sencillo de machine learning. Para ello usaremos cadenas de Markov. Trataremos de generar frases totalmente nuevas basadas en otras frases que le demos como entrada.<br><br>En mi caso voy a usar las frases del presentador argentino afincado en España, <em>Héctor del Mar</em>, porque siempre me han parecido bastante graciosas y tiene unas cuantas.<br><br><a href="https://files.adrianistan.eu/hectordelmar.jpg"><img class="wp-image-1293 size-full" src="https://files.adrianistan.eu/hectordelmar.jpg" alt="" width="640" height="480" /></a> Héctor del Mar es el de la derecha. Para quien no le conozca, suele comentar los shows de la WWE<br><h2>¿Qué son las cadenas de Markov?</h2><br>Las <a href="https://es.wikipedia.org/wiki/Cadena_de_M%C3%A1rkov">cadenas de Markov</a> es un modelo probabilístico que impone que la probabilidad de que suceda algo solo depende del estado anterior. Aplicado a nuestro caso con palabras, la probabilidad de que una palabra sea la siguiente de la frase solo depende de la palabra anterior. Observemos este grafo:<br><br><a href="https://files.adrianistan.eu/Markov.png"><img class="aligncenter size-large wp-image-1294" src="https://files.adrianistan.eu/Markov-1024x768.png" alt="" width="840" height="630" /></a>En él se han introducido dos frases: <em>El coche rojo </em>y <em>El coche verde</em>. La probabilidad de que <em>coche </em>sea la palabra que va después de <em>El</em> es del 100%, pero de que <em>rojo</em> sea la siguiente palabra después de <em>coche</em> es del 50%. Con este ejemplo, parece bastante limitado, pero la cosa cambia cuando metemos muchas frases y muchas palabras se repiten.<br><br>Para este ejemplo no obstante, usaré las dos últimas palabras como estado anterior, ya que suele dar resultados mucho más legibles (aunque pueden darse con más probabilidad frases que ya existen).<br><h2>Obteniendo los datos</h2><br>El primer paso será tener las frases en un formato óptimo. Para ello usaré <strong>requests</strong> y <strong>BeautifulSoup</strong><strong>4</strong>. Las frases las voy a sacar de <a href="https://es.wikiquote.org/wiki/H%C3%A9ctor_del_Mar">Wikiquote</a>.<br><pre class="lang:python decode:true ">from bs4 import BeautifulSoup<br>import requests<br><br>r = requests.get("https://es.wikiquote.org/wiki/H%C3%A9ctor_del_Mar")<br>soup = BeautifulSoup(r.text,"lxml")<br>frases = map(lambda x: x.text.replace("\"",""),soup.select(".mw-parser-output li"))<br>palabras = map(lambda x: str(x).split(" "),frases)</pre><br><h2>Generando el grafo de Markov</h2><br>Ahora hay que generar el grafo de Markov. Para ello vamos a usar un diccionario, donde en la clave tendremos el estado anterior, es decir, las dos últimas palabras (en forma de tupla). El contenido será una lista con todas las palabras a las que puede saltar. Al ser una lista, puede haber palabras repetidas, lo que efectivamente hará aumentar su probabilidad.<br><br>Aprovechando Python, voy a usar un <strong>defaultdict</strong> para simplificar el código, ya que con él me voy a asegurar de que todos los accesos al diccionario me van a devolver una lista.<br><pre class="lang:python decode:true ">from collections import defaultdict<br><br>almacen = defaultdict(lambda: list())<br><br>def add_word(prev,next):<br>    global almacen<br>    almacen[prev].append(next)<br><br>for frase in palabras:<br>    frase = list(frase)<br>    for i,palabra in enumerate(frase):<br>        if i == 0:<br>            add_word(("START","START"),palabra)<br>            continue<br>        if i == 1:<br>            add_word(("START",frase[0]),palabra)<br>            continue<br>        add_word((frase[i-2],frase[i-1]),palabra)</pre><br><h2>Generando una frase nueva</h2><br>Ahora viene el último paso, generrar una frase nueva. Para ello, empezamos con el estado <em>START,START</em> y seguimos el grafo hasta que acabemos. Para elegir la siguiente palabra de la lista usamos <strong>random.choice</strong>. La frase que se va generando se queda en una lista hasta que finalmente devolvemos un string completo.<br><pre class="lang:python decode:true">import random<br><br>def gen_word():<br>    word = list()<br>    state = "START","START"<br>    while True:<br>        w = random.choice(almacen[state])<br>        word.append(w)<br>        state = state[1],w<br>        if w.endswith(".") or w.endswith("!"):<br>            return " ".join(word)<br></pre><br><h2>Resultados</h2><br>Veamos los resultados:<br><br><a href="https://files.adrianistan.eu/MarkovHectorDelMar.png"><img class="aligncenter size-large wp-image-1295" src="https://files.adrianistan.eu/MarkovHectorDelMar-1024x768.png" alt="" width="840" height="630" /></a>Las frases en rojo son frases que dijo de verdad. Las frases en verde son frases generadas por este machine learning. La tasa de frases nuevas no es muy elevada, pero son más del 50%. Y todas son bastante divertidas.<br><br>El código fuente completo de <em>Markov-HectorDelMar</em> está en el repositorio Git del blog: <a href="https://github.com/aarroyoc/blog-ejemplos/tree/master/markov-hector-del-mar">https://github.com/aarroyoc/blog-ejemplos/tree/master/markov-hector-del-mar</a><br><br>Ahora que ya sabes usar cadenas de Markov, ¿por qué no meter como dato de entrada <em>El Quijote?</em>, ¿o los tuits de algún político famoso?, ¿o las entradas de este blog? Las posibilidades son infinitas.<br><br>Para despedirme, como diría el Héctor del Mar de verdad:<br><blockquote>Aquí estoy porque he venido, porque he venido aquí estoy, si no le gusta mi canto, como he venido, me voy. ¡Nos vamos, don Fernando!</blockquote>]]></description>
                <comments>https://blog.adrianistan.eu/generar-frases-cadenas-markov-machine-learning-python</comments>
                <pubDate>Thu, 25 Jan 2018 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Juego de la Vida de Conway en C# con interfaz gráfica</title>
                <link>https://blog.adrianistan.eu/juego-la-vida-conway-c-interfaz-grafica</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/juego-la-vida-conway-c-interfaz-grafica</guid>
                <description><![CDATA[Hoy os traigo un proyecto que realizamos <a href="https://github.com/DanielBV">Daniel Bazaco</a> y yo. Se trata del clásico de juego de la vida, esta vez hecho en C# con<a href="http://dot.net/core"> .NET Core</a> y <a href="https://github.com/AvaloniaUI/Avalonia">Avalonia</a> como librería gráfica. Funciona tanto en Windows como en GNU/Linux. El programa tiene la peculiaridad de que tiene implementados dos algoritmos totalmente distintos para el juego de la vida:<br><ul><br> 	<li>El clásico algoritmo de la matriz infinita.</li><br> 	<li>Un algoritmo usando Quadtrees y tablas de dispersión optimizadas, que permite tener patrones precalculados.</li><br></ul><br>La velocidad de este segundo algoritmo es muy superior a la del primero, aunque he de confesar que este segundo algoritmo no resulta evidente y tiene una desventaja en el modo gráfico. Este segundo algoritmo avanza <em>a trompicones</em>, por lo que no es posible realizar una animación gráfica idónea, a no ser que lo modifiquemos ligeramente. Este tercer algoritmo que es una modificación del segundo, es más lento, pero permite ser mostrado por la pantalla.<br><br>El programa admite ficheros tanto en formato estándar RLE como un formato propio, que hemos llamado Vaca. Puedes pasarte por <a href="http://www.conwaylife.com/wiki/Main_Page">la wiki del juego de la vida</a> y probar los ficheros RLE que encuentres. No obstante, hay que tener cuidado, pues algunos ficheros RLE no son del juego de la vida, sino de otros juegos con normas ligeramente modificadas.<br><h2>¿En qué consiste el Juego de la Vida?</h2><br>El juego de la vida es un autómata celular de dos dimensiones. También se le ha categorizado como juego para cero jugadores.<br><br>El juego tiene unas normas sencillas. Cada celda puede estar viva o muerta. En la siguiente evolución, las celdas pueden pasar a vivas o muertas siguiendo este esquema:<br><ul><br> 	<li>Una célula muerta con exactamente 3 células vecinas vivas "nace" (es decir, al turno siguiente estará viva).</li><br> 	<li>Una célula viva con 2 o 3 células vecinas vivas sigue viva, en otro caso muere o permanece muerta (por "soledad" o "superpoblación").</li><br></ul><br>Unas condiciones de partida determinadas podrán desencaminar comportamientos complejos y emergentes muy interesantes como las pistolas de gliders.<br><br>[video width="1364" height="704" mp4="https://files.adrianistan.eu/GosperGliderGun.mp4" webm="https://files.adrianistan.eu/GosperGliderGun.webm"][/video]<br><h2>Uso</h2><br><a href="https://files.adrianistan.eu/ConwayStartScreen.png"><img class="wp-image-1283 size-large" src="https://files.adrianistan.eu/ConwayStartScreen-1024x576.png" alt="" width="840" height="473" /></a> Pantalla de inicio de Conway<br><br>Desde aquí podemos dar a <em>Nuevo patrón</em> o <em>Cargar patrón</em>. Si le damos a Nuevo Patrón tendremos una matriz vacía y limpia. Podemos hacer click con el ratón para ir activando/desactivando las casillas. Puedes usar las teclas W, A, S y D o las flechas en pantalla para moverte por el universo infinito de Conway.<br><br><a href="https://files.adrianistan.eu/ConwayFreePattern.png"><img class="aligncenter size-large wp-image-1284" src="https://files.adrianistan.eu/ConwayFreePattern-1024x576.png" alt="" width="840" height="473" /></a>Una vez lo tengamos podemos guardarlo para no tener que volver a dibujarlo. Otra opción es cargar un patrón de la lista. Este programa admite formato RLE y Vaca, pero solo guarda archivos en formato Vaca.<br><br>Para ejecutar el juego de la vida hay tres botones importantes. El primero es <em>Ejecutar</em>, que ejecuta el juego de la vida indefinidamente. Se para cuando pulsamos <em>Parar</em> (el mismo botón).<br><br>El otro es <em>Siguiente</em>, que nos permite avanzar de iteración en iteración manualmente, muy interesante para observar al detalle ciertos patrones. Por otro lado tenemos <em>Iterar N veces</em>, que permite iterar N veces y que sirve para pruebas de rendimiento. Hay que tener en cuenta que tanto <em>Siguiente</em> como <em>Iterar N veces</em> funcionan un poco distinto con el algoritmo Quadtree (el activado por defecto), ya que este algoritmo hace varias evoluciones de golpe, para ser todavía más rápido. La parte mala es que no es posible ver en detalle cada algoritmo.<br><h5>Algoritmo Matriz</h5><br>[video width="1008" height="486" mp4="https://files.adrianistan.eu/AlgoritmoMatriz.mp4" webm="https://files.adrianistan.eu/AlgoritmoMatriz.webm"][/video]<br><h5>Algoritmo Quadtree</h5><br>[video width="1008" height="486" mp4="https://files.adrianistan.eu/AlgoritmoQuadtree.mp4" webm="https://files.adrianistan.eu/AlgoritmoQuadtree.webm"][/video]<br><h2>Línea de comandos</h2><br>Es posible ejecutar el juego de la vida en línea de comandos. Este modo permite cargar un archivo Vaca o RLE y ejecutarlo N iteraciones. Al finalizar se muestran estadísticas y se permite guardar el resultado o mostrarlo por pantalla con caracteres ASCII.<br><br>Hay dos parámetros, <strong>-i</strong> para indicar el fichero de entrada y <strong>-n</strong> para indicar las iteraciones a calcular.<br><h2><a href="https://files.adrianistan.eu/ConwayCli.png"><img class="aligncenter size-large wp-image-1290" src="https://files.adrianistan.eu/ConwayCli-1024x576.png" alt="" width="840" height="473" /></a>Algoritmo Quadtree</h2><br>¿Cómo funciona el algoritmo quadtree que tanto mejora el rendimiento del juego de la vida? Siendo sinceros, no es algoritmo sencillo o evidente. Su nombre más correcto es algoritmo<strong> Hashlife</strong> y fue descrito por Bill Gosper en los laboratorios de investigación de Xerox Palo Alto.<br><br>La idea básica es que muchas veces en el juego de la vida nos encontramos con patrones que se van repitiendo periódicamente y grandes zonas vacías.<br><br>Para ello recurre a un almacén de cuadrantes. Y es que ahora el universo ya no es una matriz infinita, sino un cuadrante. Y cada cuadrante tiene cuatro cuadrantes hijos (noroeste, noreste, suroeste y sureste), así hasta llegar al cuadrante mínimo ya no tiene hijos sino que es una celda viva o muerta. Esto evidentemente pone limitaciones al tamaño del universo, que será siempre potencia de dos.<br><br>El almacén es una tabla hash, pero no una corriente tipo HashMap de Java o Dictionary de C# sino que toma 4 elementos como índice, cuatro subcuadrantes. Si existe un elemento cuyos cuatro subcuadrantes son <em>iguales</em> (no se comprueba la igualdad exactamente, sería muy lento), se devuelve la siguiente iteración del cuadrante del almacén que cumple esos requisitos. De este modo no hace falta calcular los cuadrantes nada más que la primera vez que el programa se encontró con ellos.<br><br>Este sistema consume más memoria, pero mejora de forma sustancial la velocidad de ejecución. El algoritmo luego tiene bastantes más detalles (<a href="http://diasyfrases.blogspot.com.es/2015/11/dios-esta-en-los-detalles.html">el diablo está en los detalles</a>), pero esa es la idea principal, no calcular los cuadrantes más que una sola vez.<br><h2>Descargar Conway</h2><br>Podéis descargar Conway desde GitHub y compilarlo en Windows y GNU/Linux (Mac no está probado pero en principio funcionaría), con .NET Core 2.0 instalado.<br><p style="text-align: center;"><a href="https://github.com/aarroyoc/Conway">Conway en GitHub</a></p><br>&nbsp;<br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/juego-la-vida-conway-c-interfaz-grafica</comments>
                <pubDate>Sat, 20 Jan 2018 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Cheatsheet de Pandas</title>
                <link>https://blog.adrianistan.eu/cheatsheet-de-pandas</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/cheatsheet-de-pandas</guid>
                <description><![CDATA[He encontrado esta cheatsheet en formato PDF de cómo manejar Pandas, la cuál es muy interesante y que os dejo aquí.<a href="https://files.adrianistan.eu/Pandas_Cheat_Sheet.pdf"><img class="aligncenter size-large wp-image-1278" src="https://files.adrianistan.eu/Screenshot-2018-1-19-pandas-dev-pandas-657x1024.png" alt="" width="657" height="1024" /></a><a href="https://files.adrianistan.eu/Pandas_Cheat_Sheet.pdf">Pandas_Cheat_Sheet</a><br><br>Agradecimientos a Irv Lusting que se ha tomado la molestia de hacerla]]></description>
                <comments>https://blog.adrianistan.eu/cheatsheet-de-pandas</comments>
                <pubDate>Fri, 19 Jan 2018 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Estadística en Python: ajustar datos a una distribución (parte VII)</title>
                <link>https://blog.adrianistan.eu/estadistica-python-ajustar-datos-una-distribucion-parte-vii</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/estadistica-python-ajustar-datos-una-distribucion-parte-vii</guid>
                <description><![CDATA[Ya hemos visto con anterioridad que existen modelos que nos hacen la vida más sencilla. Sin embargo, en esos modelos conocíamos ciertos datos de antemano. ¿Qué pasa si tenemos datos y queremos ver si podemos estar ante un modelo de los ya definidos?<br><br>Este tipo de ajuste es muy interesante, ya que nos permite saber si los datos en bruto pueden parecerse a los modelos de <strong>Normal</strong> u otros y aprovecharlo.<br><h2>Ajuste de datos a una distribución</h2><br>Para ajustar datos a una distribución, todas las distribuciones continuas de SciPy cuentan con la función <strong>fit</strong>. Fit nos devuelve los parámetros con los que ajusta nuestros datos al modelo. ¡Ojo, no nos avisa si es un buen ajuste o no!<br><div><em>Ejemplo: Queremos saber si la altura de los hombres adultos del pueblo de Garray sigue una distribución normal. Para ello tomamos una muestra de 80 alturas de hombres adultos en Garray.</em></div><br><div></div><br><div>Los datos los tenemos en este CSV, cada altura en una línea:</div><br><div><br><pre class="height-set:true height:200 lang:default decode:true" title="alturas.csv">Altura<br>180.55743416791302<br>159.4830930711535<br>175.54566032406794<br>149.06378740901533<br>140.35494067172635<br>146.65963134242543<br>171.34024710764376<br>140.11601629465872<br>175.6026441151595<br>158.00860559393507<br>122.53612034588375<br>116.10055909040616<br>152.89225061770068<br>148.31372767763455<br>111.17487190927599<br>160.18952563680827<br>151.8729737480722<br>141.50350042949614<br>165.2379297612276<br>150.75979657877465<br>171.5257501059296<br>157.97922034080895<br>159.60144363114716<br>152.52036681430164<br>172.0678524550487<br>163.65457704485283<br>134.9562174388093<br>189.70206097599245<br>153.78203142905076<br>176.1787894042539<br>190.83025195589502<br>199.04182673196726<br>146.97803776211907<br>174.22118528139467<br>170.95045320552694<br>161.2797407784266<br>190.61061242859464<br>168.79257731811308<br>159.87099716863165<br>136.22823975268153<br>166.87622973701335<br>179.58044852016417<br>172.49583957582817<br>165.2662334997042<br>136.6663345224381<br>161.9352364324168<br>174.56164027542448<br>161.62817356012405<br>167.65579546297906<br>170.88930983697742<br>147.22062198310996<br>151.85737964663497<br>158.03323614736198<br>135.77570282853696<br>161.25435141827515<br>193.33084953437478<br>155.43189514766172<br>155.89204074847055<br>179.23931091736836<br>146.485962651657<br>166.61617663518228<br>161.70927578953211<br>164.89798613982495<br>139.18195138901498<br>180.30341647946335<br>162.4811239647979<br>171.1035005376699<br>147.01137545913147<br>187.03282087175134<br>172.2476631392949<br>152.9814634955974<br>174.43159049461713<br>174.83877117002814<br>132.66857703218636<br>173.98029972846837<br>133.5435543737402<br>169.62941676289472<br>166.4887567852903<br>138.1150540623029<br>170.52532661450618<br></pre><br>Vamos a ajustarlo.<br><pre class="lang:python decode:true ">import pandas as pd<br>import scipy.stats as ss<br><br>df = pd.read_csv("alturas.csv")<br><br>media, desviacion = ss.norm.fit(df["Altura"])<br><br>print(media) # media = 160,37<br>print(desviacion) # desviacion = 17,41<br><br><br></pre><br>En este caso nos informa de que estos datos parecen encajar con los de una distribución normal de media 160,37 y desviación típica 17,41.<br><h2>¿Cómo de bueno es el ajuste? Kolmogorov</h2><br>Hemos hecho un ajuste, pero no sabemos qué tan bueno es. Existen varios métodos para poner a prueba los ajustes. Existen varios métodos, siendo los más populares Chi-Cuadrado y Kolmogorov-Smirnov.<br><br>Chi-Cuadrado no se puede aplicar directamente sobre distribuciones continuas, aún así voy a explicar como se haría. Sin embargo, primero vamos a probar con Kolmogorov-Smirnov, que en SciPy es <strong>ktest</strong>.<br><pre class="lang:python decode:true">import pandas as pd<br>import scipy.stats as ss<br><br>df = pd.read_csv("altura.csv")<br>media, desviacion = ss.norm.fit(df["Altura"])<br>d, pvalor = ss.ktest(df["Altura"],"norm",args=(media,desviacion))<br># o alternativamente<br>d, pvalor = ss.ktest(df["Altura"],lambda x: ss.norm.cdf(x,media,desviacion))<br></pre><br>Este test nos devuelve dos valores: D y el p-valor. Yo voy a fijarme en el p-valor. El p-valor es el nivel mínimo de significación para el cual rechazaremos el ajuste. Cuanto más cerca del 1 esté, más confianza hay en el ajuste, cuanto más cerca de cero esté, menos confianza hay en el ajuste. ¿Pero cómo se interpreta exactamente?<br><br><a href="https://files.adrianistan.eu/8ff3.jpeg"><img class="aligncenter size-large wp-image-1274" src="https://files.adrianistan.eu/8ff3-1024x844.jpeg" alt="" width="840" height="692" /></a><br><br>Una forma sencillo de entenderlo es hacer obtener el nivel de significación, que es 1 - NivelConfianza/100. Así para una confianza del 95%, este será de 0.05, para una confianza del 99%, el valor se reduce a 0.01. Si el p-valor es inferior a este nivel de significación, el ajuste probablemente no es correcto.<br><pre class="lang:python decode:true ">import pandas as pd<br>import scipy.stats as ss<br><br>df = pd.read_csv("altura.csv")<br>media, desviacion = ss.norm.fit(df["Altura"])<br>d, pvalor = ss.ktest(df["Altura"],"norm",args=(media,desviacion))<br><br># queremos confianza al 99%<br>if pvalor &lt; 0.01:<br>    print("No se ajusta a una normal")<br>else:<br>    print("Se puede ajustar a una normal")<br></pre><br>Aquí algún estadístico más serio se tiraría de los pelos, porque en realidad lo único que se puede demostrar con seguridad es en el caso de que no se ajuste. Si salta que se puede ajustar a una normal, simplemente querría decir que no se puede rechazar la hipótesis de partida (que sea normal), pero no confirma que sea normal.<br><br>Esto es algo común en el contraste de hipótesis y con los p-valores en particular y ha suscitado crítica, hasta tal punto que en 2016, la <a title="American Statistical Association" href="https://es.wikipedia.org/wiki/American_Statistical_Association">American Statistical Association</a> publicó una serie de consejos para tratar con p-valores. El p-valor en este caso solo puede demostrar que no se ajusta a una normal. No obstante, para ir poco a poco, podemos simplificar esto un poco.<br><h2>Chi-Cuadrado</h2><br>Para hacer el test de Chi-Cuadrado primero hay que dividir los datos continuos. Para uso podemos usar la función de NumPy, <strong>histogram</strong> o la de SciPy <strong>binned_statistic</strong>. Tenemos que construir una lista con las frecuencias esperadas si siguiéramos al 100% la distribución a la que queremos ajustarnos.<br><pre class="lang:python decode:true">def test_chicuadrado(data,N):<br>    n = data.count()<br>    freqs, edges, _ = ss.binned_statistic(data,data,statistic="count")<br>    def ei(i):<br>        return n*(N.cdf(edges[i])- N.cdf(edges[i-1]))<br>    expected = [ei(i) for i in range(1,len(edges))]<br>    return ss.chisquare(freqs,expected)</pre><br>Una vez tengamos una lista con los valores esperados, simplemente podemos comparar las frecuencias reales con las esperadas con <strong>chisquare</strong>. Esto nos devolverá C y el p-valor. El significado del p-valor es exactamente igual.<br><br>Con esto ya hemos visto como empezar con estadística en Python. No sé si haré más artículos de este estilo, si no es así, espero que os hayan gustado esta serie de artículos. El mundo del data science es inmenso y os invito a seguirlo explorando.<br><br></div>]]></description>
                <comments>https://blog.adrianistan.eu/estadistica-python-ajustar-datos-una-distribucion-parte-vii</comments>
                <pubDate>Wed, 17 Jan 2018 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>¿Cómo funcionan Meltdown y Spectre? Explicado con InstaStories</title>
                <link>https://blog.adrianistan.eu/funcionan-meltdown-spectre-explicado-instastories</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/funcionan-meltdown-spectre-explicado-instastories</guid>
                <description><![CDATA[<a href="https://files.adrianistan.eu/IMG_20180105_121857_309.jpg"><img class="aligncenter wp-image-1250 size-large" src="https://files.adrianistan.eu/IMG_20180105_121857_309-623x1024.jpg" alt="" width="623" height="1024" /></a><a href="https://files.adrianistan.eu/IMG_20180105_121900_381.jpg"><img class="aligncenter size-large wp-image-1251" src="https://files.adrianistan.eu/IMG_20180105_121900_381-623x1024.jpg" alt="" width="623" height="1024" /></a> <a href="https://files.adrianistan.eu/IMG_20180105_121902_844.jpg"><img class="aligncenter size-large wp-image-1252" src="https://files.adrianistan.eu/IMG_20180105_121902_844-623x1024.jpg" alt="" width="623" height="1024" /></a> <a href="https://files.adrianistan.eu/IMG_20180105_121905_545.jpg"><img class="aligncenter size-large wp-image-1253" src="https://files.adrianistan.eu/IMG_20180105_121905_545-623x1024.jpg" alt="" width="623" height="1024" /></a> <a href="https://files.adrianistan.eu/IMG_20180105_121907_927.jpg"><img class="aligncenter size-large wp-image-1254" src="https://files.adrianistan.eu/IMG_20180105_121907_927-623x1024.jpg" alt="" width="623" height="1024" /></a> <a href="https://files.adrianistan.eu/IMG_20180105_121910_189.jpg"><img class="aligncenter size-large wp-image-1255" src="https://files.adrianistan.eu/IMG_20180105_121910_189-623x1024.jpg" alt="" width="623" height="1024" /></a> <a href="https://files.adrianistan.eu/IMG_20180105_121912_620.jpg"><img class="aligncenter size-large wp-image-1256" src="https://files.adrianistan.eu/IMG_20180105_121912_620-623x1024.jpg" alt="" width="623" height="1024" /></a> <a href="https://files.adrianistan.eu/IMG_20180105_121915_303.jpg"><img class="aligncenter size-large wp-image-1257" src="https://files.adrianistan.eu/IMG_20180105_121915_303-623x1024.jpg" alt="" width="623" height="1024" /></a> <a href="https://files.adrianistan.eu/IMG_20180105_121917_987.jpg"><img class="aligncenter size-large wp-image-1258" src="https://files.adrianistan.eu/IMG_20180105_121917_987-623x1024.jpg" alt="" width="623" height="1024" /></a> <a href="https://files.adrianistan.eu/IMG_20180105_121920_306.jpg"><img class="aligncenter size-large wp-image-1259" src="https://files.adrianistan.eu/IMG_20180105_121920_306-623x1024.jpg" alt="" width="623" height="1024" /></a> <a href="https://files.adrianistan.eu/IMG_20180105_121922_566.jpg"><img class="aligncenter size-large wp-image-1260" src="https://files.adrianistan.eu/IMG_20180105_121922_566-623x1024.jpg" alt="" width="623" height="1024" /></a> <a href="https://files.adrianistan.eu/IMG_20180105_121924_957.jpg"><img class="aligncenter size-large wp-image-1261" src="https://files.adrianistan.eu/IMG_20180105_121924_957-623x1024.jpg" alt="" width="623" height="1024" /></a> <a href="https://files.adrianistan.eu/IMG_20180105_121927_432.jpg"><img class="aligncenter size-large wp-image-1262" src="https://files.adrianistan.eu/IMG_20180105_121927_432-623x1024.jpg" alt="" width="623" height="1024" /></a> <a href="https://files.adrianistan.eu/IMG_20180105_121930_018.jpg"><img class="aligncenter size-large wp-image-1263" src="https://files.adrianistan.eu/IMG_20180105_121930_018-623x1024.jpg" alt="" width="623" height="1024" /></a>]]></description>
                <comments>https://blog.adrianistan.eu/funcionan-meltdown-spectre-explicado-instastories</comments>
                <pubDate>Fri, 05 Jan 2018 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Yew, crea webapps al estilo Angular&#x2F;React en Rust</title>
                <link>https://blog.adrianistan.eu/yew-frontend-rust-angular-react</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/yew-frontend-rust-angular-react</guid>
                <description><![CDATA[Hoy os traigo una librería muy potente y muy útil, ahora que Rust compila a <strong>WebAssembly</strong> de forma nativa. Se trata de <a href="https://github.com/DenisKolodin/yew"><strong>Yew</strong></a>, una librería para diseñar frontends, single-page-applications y en general, una librería que es una alternativa a <strong>Angular, React, Vue</strong> y <strong>Elm</strong>.<br><br>En particular, <strong>Yew</strong> se basa en The Elm Architecture, por lo que los usuarios de Elm serán los que encuentren más familiar a Yew.<br><br><a href="https://files.adrianistan.eu/Tejo.jpg"><img class="size-full wp-image-1246" src="https://files.adrianistan.eu/Tejo.jpg" alt="" width="1000" height="526" /></a> Yew significa tejo. He aquí uno de los tejos más famosos de Valladolid, en la plaza del Viejo Coso<br><h2>Instalar cargo-web y el soporte a WebAssembly</h2><br>Antes de empezar a programar necesitaremos instalar un plugin para Cargo, llamado <strong>cargo-web</strong>, que nos ayudará en el desarrollo web con Rust. Por otro lado, hace falta instalar el soporte de Rust a WebAssembly. Existen tres opciones actualmente: asmjs-unknown-emscripten, wasm32-unknown-emscripten y wasm32-unknown-unknown. Para los primeras opciones hace falta tener instalado <a href="https://github.com/kripken/emscripten"><strong>Emscripten</strong></a>. Para la última, no hace falta nada, por lo que es mi opción recomendada. Por otro lado, wasm32 significa WebAssembly y asmjs es asm.js, que es simplemente JavaScript y será compatible con más navegadores.<br><pre class="lang:default decode:true">cargo install cargo-web<br>rustup target add wasm32-unknown-unknown</pre><br><h2>The Elm Architecture</h2><br>Los principios de <a href="https://guide.elm-lang.org/architecture/"><em>The Elm Architecture</em></a> se basan en: modelo, mensajes, actualización y vista.<br><br>Para este tutorial vamos a hacer la típica aplicación de una lista donde guardamos notas. Nuestro modelo se va a componer de una lista de tareas, para simplificar, pongamos que una tarea es simplemente un String y un ID. Entonces también nos hará falta almacenar un contador para ir poniendo IDs. También, al tener un campo de texto, nos hará falta una variable para ir almacenando temporalmente lo que escribe el usuario.<br><pre class="lang:rust decode:true">struct Model {<br>    id: u32,<br>    tasks: Vec&lt;Task&gt;,<br>    input: String,<br>}<br><br>struct Task{<br>    content: String,<br>    id: u32,<br>}</pre><br>Lo siguiente es diseñar los <strong>mensajes</strong>. Los mensajes interactúan con el modelo y desencadenan una actualización de la vista. En esta aplicación solo nos hacen falta dos mensajes: añadir mensaje y borrar mensaje. Pero como tenemos un campo de texto, tenemos que introducir un mensaje Change y siempre viene bien un mensaje que no haga nada.<br><pre class="lang:rust decode:true">enum Msg {<br>    Add,<br>    Remove(u32),<br>    Change(String),<br>    None,<br>}</pre><br>Una vez hecho esto pasamos a crear la función <strong>update</strong>, en la que hacemos distintas cosas según el mensaje que recibamos.<br><pre class="lang:rust decode:true ">fn update(context: &amp;mut Context&lt;Msg&gt;, model: &amp;mut Model, msg: Msg) {<br>    match msg {<br>        Msg::Add =&gt; {<br>            let task = Task{<br>                content: model.input.clone(),<br>                id: model.id,<br>            };<br>            model.tasks.push(task);<br>            model.id += 1;<br>        }<br>        Msg::Change(content) =&gt; {<br>            model.input = content;<br>        }<br>        Msg::Remove(id) =&gt; {<br>            let mut i = 0;<br>            for task in model.tasks.iter(){<br>                if task.id == id{<br>                    break;<br>                }<br>                i+=1;<br>            }<br>            model.tasks.remove(i);<br>        }<br>        _ =&gt; {<br><br>        }<br>    }<br>}</pre><br>Así pues, si se lanza un mensaje <strong>Msg::Add</strong> lo que hacemos es copiar el valor de la variable temporal input, crear una nueva tarea con su ID y añadirla a la lista de tareas. Ya está. Yew mágicamente actualizará la página para reflejar que la lista de tareas ha sido modificada. Lo mismo pasa con Remove.<br><br>Ahora vamos a las vistas. Una vista es una función que devuelve <strong>Html&lt;Msg&gt;</strong> y se pueden componer varias funciones así. En nuestro caso, tenemos una vista principal donde se ve un campo de texto y un sitio donde se ejecuta un bucle for con las tareas del modelo. Y a cada tarea se le aplica la vista <strong>view_task</strong>.<br><pre class="lang:rust decode:true">fn view(model: &amp;Model) -&gt; Html&lt;Msg&gt; {<br>    html! {<br>        &lt;div&gt;<br>            &lt;ul&gt;<br>            { for model.tasks.iter().map(view_task) }<br>            &lt;/ul&gt;<br>            &lt;input type="text", value=&amp;model.input, oninput=|e: InputData| Msg::Change(e.value), onkeypress=|e: KeyData|{<br>                if e.key == "Enter" {<br>                    Msg::Add<br>                }else{<br>                    Msg::None<br>                }<br>            }, /&gt; <br>        &lt;/div&gt;<br>    }<br>}<br><br>fn view_task(task: &amp;Task) -&gt; Html&lt;Msg&gt;{<br>    let id = task.id;<br>    html!{<br>        &lt;li&gt;&lt;span&gt;{&amp;task.content}&lt;/span&gt;&lt;button onclick=move |_| Msg::Remove(id),&gt;{format!("X")}&lt;/button&gt;&lt;/li&gt;<br>    }<br>}</pre><br>La macro <strong>html!</strong> nos permite escribir HTML directamente en Rust, con algunas diferencias (¡prestad atención a las comas!). También nos permite introducir código Rust (entre llaves) y <strong>closures</strong> (observad onclick, oninput y onkeypress).<br><br>Finalmente en el método main, inicializamos el modelo y llamamos a <strong>program</strong>, que empieza a ejecutar Yew.<br><br>El código final queda así.<br><pre class="lang:rust decode:true">#[macro_use]<br>extern crate yew;<br><br>use yew::html::*;<br><br>struct Model {<br>    id: u32,<br>    tasks: Vec&lt;Task&gt;,<br>    input: String,<br>}<br><br>struct Task{<br>    content: String,<br>    id: u32,<br>}<br><br>enum Msg {<br>    Add,<br>    Remove(u32),<br>    Change(String),<br>    None,<br>}<br><br>fn update(context: &amp;mut Context&lt;Msg&gt;, model: &amp;mut Model, msg: Msg) {<br>    match msg {<br>        Msg::Add =&gt; {<br>            let task = Task{<br>                content: model.input.clone(),<br>                id: model.id,<br>            };<br>            model.tasks.push(task);<br>            model.id += 1;<br>        }<br>        Msg::Change(content) =&gt; {<br>            model.input = content;<br>        }<br>        Msg::Remove(id) =&gt; {<br>            let mut i = 0;<br>            for task in model.tasks.iter(){<br>                if task.id == id{<br>                    break;<br>                }<br>                i+=1;<br>            }<br>            model.tasks.remove(i);<br>        }<br>        _ =&gt; {<br><br>        }<br>    }<br>}<br><br>fn view(model: &amp;Model) -&gt; Html&lt;Msg&gt; {<br>    html! {<br>        &lt;div&gt;<br>            &lt;ul&gt;<br>            { for model.tasks.iter().map(view_task) }<br>            &lt;/ul&gt;<br>            &lt;input type="text", value=&amp;model.input, oninput=|e: InputData| Msg::Change(e.value), onkeypress=|e: KeyData|{<br>                if e.key == "Enter" {<br>                    Msg::Add<br>                }else{<br>                    Msg::None<br>                }<br>            }, /&gt; <br>        &lt;/div&gt;<br>    }<br>}<br><br>fn view_task(task: &amp;Task) -&gt; Html&lt;Msg&gt;{<br>    let id = task.id;<br>    html!{<br>        &lt;li&gt;&lt;span&gt;{&amp;task.content}&lt;/span&gt;&lt;button onclick=move |_| Msg::Remove(id),&gt;{format!("X")}&lt;/button&gt;&lt;/li&gt;<br>    }<br>}<br><br>fn main() {<br>    let model = Model {<br>        id: 0,<br>        tasks: vec![],<br>        input: String::from("")<br>    };<br>    program(model, update, view);<br>}<br></pre><br><h2><a href="https://files.adrianistan.eu/Screenshot-from-2018-01-02-00-18-14.png"><img class="aligncenter size-large wp-image-1245" src="https://files.adrianistan.eu/Screenshot-from-2018-01-02-00-18-14-1024x576.png" alt="" width="840" height="473" /></a>Ejecutando la aplicación web</h2><br>Usando cargo web, es muy sencillo generar la aplicación web. Simplemente ejecuta:<br><pre class="lang:default decode:true ">cargo web start --target-webasm</pre><br>El resultado, se montará en un mini servidor web. Si accedes a la URL que indica Cargo con tu navegador web, verás algo similar a esto:<br><br><a href="https://files.adrianistan.eu/YewBlog.png"><img class="aligncenter size-full wp-image-1243" src="https://files.adrianistan.eu/YewBlog.png" alt="" width="845" height="440" /></a>Añade items y bórralos. Observa como la aplicación funciona perfectamente.<br><h2>Distribuyendo la aplicación</h2><br>Realmente cargo web ha hecho muchas cosas por nosotros. Si nosotros queremos usar Yew en la vida real, no usaremos cargo web. Para ello, compilamos la aplicación web:<br><pre class="lang:default decode:true ">cargo web build --target-webasm</pre><br>Y accedemos a la carpeta <strong>target/wasm32-unknown-unknown/release</strong>. Allí encontraremos dos archivos que vamos a necesitar. Uno acabado en .<strong>js</strong> y otro acabado en <strong>.wasm</strong>. Ambos ficheros deberemos copiarlos donde queramos usarlos. Por último, será necesario un fichero HTML. En el fichero HTML solo hace falta cargar el fichero JS. Yew hará el resto.<br><br><a href="https://github.com/aarroyoc/blog-ejemplos/tree/master/yew_todo_list">Si quieres saber más, puedes descargarte el código de ejemplo que se encuentra en GitHub.</a>]]></description>
                <comments>https://blog.adrianistan.eu/yew-frontend-rust-angular-react</comments>
                <pubDate>Mon, 01 Jan 2018 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Leer y escribir JSON en Rust con Serde</title>
                <link>https://blog.adrianistan.eu/leer-escribir-json-rust-serde</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/leer-escribir-json-rust-serde</guid>
                <description><![CDATA[<strong>JSON</strong> es un formato muy popular para guardar y transmitir información. En Rust podemos leer y escribir JSON de forma transparente gracias a <a href="https://serde.rs"><strong>Serde</strong></a>. Serde es una librería para Rust, que permite transformar las structs nativas de Rust en ficheros JSON, YAML, BSON, MsgPack, XML y viceversa. Serde está compuesto de varios plugins, uno para cada formato de archivo, así que vamos a necesitar el plugin de JSON. Es lo que se dice serializar (guardar) y deserializar (leer).<br><h2>Deserializando JSON con Serde</h2><br>Para deserializar necesitamos primero definir el struct en Rust que se corresponde al JSON que vamos a cargar. Además, hay que incluir una cláusula <strong>derive</strong> e indicar que derivamos <strong>Deserializable</strong>.<br><pre class="lang:rust decode:true ">#[derive(Deserialize)]<br>struct Landmark {<br>    x: i32,<br>    y: i32,<br>    name: String<br>}<br></pre><br>Una vez hecho eso podemos realizar la transformación con Serde. Serde admite varios métodos de entrada. En mi caso voy a usar <strong>from_reader</strong>, que permite transformar desde cualquier <strong>std::io::Read</strong>. También se podría usar <strong>from_str</strong>, que permite leer desde un <strong>String</strong>.<br><br>Así pues un programa que lea Landmarks de un fichero JSON como este:<br><pre class="lang:js decode:true">[{<br>    "x": 42,<br>    "y" : -1,<br>    "name" : "Presa de Tibi"<br>},<br>{<br>    "x" : 50,<br>    "y" : 23,<br>    "name" : "Rollo de Villalón de Campos"<br>}]</pre><br>Quedaría así:<br><pre class="lang:rust decode:true">#[macro_use]<br>extern crate serde_derive;<br><br>extern crate serde;<br>extern crate serde_json;<br><br>use std::io::BufReader;<br>use std::fs::File;<br><br>#[derive(Deserialize)]<br>struct Landmark {<br>    x: i32,<br>    y: i32,<br>    name: String<br>}<br><br>fn main() {<br>    let file = File::open("landmarks.json").unwrap();<br>    let reader = BufReader::new(file);<br>    let landmarks: Vec&lt;Landmark&gt; = serde_json::from_reader(reader).unwrap();<br>    for landmark in landmarks{<br>        println!("Landmark name: {}\tPosition: ({},{})",landmark.name,landmark.x,landmark.y);<br>    }<br>}<br></pre><br>Nótese como usamos <strong>Vec&lt;Landmark&gt;</strong> para indicar que vamos a cargar un JSON que es un array de Landmarks.<br><h2>Serializar JSON</h2><br>Ahora si queremos generar un archivo JSON es muy sencillo. Simplemente marcamos <strong>Serialize</strong> en la misma estructura que queramos serializar. Posteriormente en Serde podemos utilizar diferentes métodos, como <strong>to_writer </strong>o <strong>to_string</strong>.<br><pre class="lang:rust decode:true ">#[macro_use]<br>extern crate serde_derive;<br><br>extern crate serde;<br>extern crate serde_json;<br><br>use std::io::{BufReader,BufWriter};<br>use std::fs::File;<br><br>#[derive(Serialize, Deserialize)]<br>struct Landmark {<br>    x: i32,<br>    y: i32,<br>    name: String<br>}<br><br>fn main() {<br>    let landmark = Landmark {<br>        x: 67,<br>        y: 23,<br>        name: String::from("Academia de Caballería")<br>    };<br>    let output = File::create("caballeria.json").unwrap();<br>    let writer = BufWriter::new(output);<br>    serde_json::to_writer(writer,&amp;landmark).unwrap();<br>}</pre><br><h2>Personalizando la serialización y la deserialización</h2><br>Serde permite a través de atributos, definir de forma precisa que ocurre con los elementos. En caso de no poner nada, por ejemplo, Serde usará los nombres del struct de Rust en los ficheros y si hay elementos de más, en el fichero al leer, los ignorará.<br><br>Por ejemplo, si queremos que en el fichero JSON, en vez de tener <strong>name</strong> sea <strong>nombre</strong>, podemos usar <strong>rename</strong>.<br><pre class="lang:rust decode:true ">#[derive(Serialize, Deserialize)]<br>struct Landmark {<br>    x: i32,<br>    y: i32,<br>    #[serde(rename = "nombre")]<br>    name: String<br>}</pre><br>Podemos también no querer guardar algún elemento del struct, usamos <strong>skip</strong>.<br><div><br><div><br><pre class="lang:rust decode:true">#[derive(Serialize, Deserialize)]<br>struct Landmark {<br>    x: i32,<br>    y: i32,<br>    name: String,<br>    #[serde(skip)]<br>    id: u32<br>}</pre><br>Si queremos que Serde falle en caso de que en el JSON haya más campos de los que hemos definido, usamos <strong>deny_unkown_fields</strong>.<br><pre class="lang:default decode:true ">#[derive(Serialize, Deserialize, Debug)]<br>#[serde(deny_unknown_fields)]<br>struct Landmark {<br>    x: i32,<br>    y: i32,<br>    name: String,<br>}</pre><br>Y si queremos que el nombre del struct sea distinto al que crea oportuno Serde, podemos redefinirlo también:<br><pre class="lang:rust decode:true ">#[derive(Serialize, Deserialize)]<br>#[serde(rename = "landmark")]<br>struct Landmark {<br>    x: i32,<br>    y: i32,<br>    name: String,<br>}</pre><br>Por último, mencionar que Serde permite serializar/deserializar cosas más complejas, con otros structs, con vectores, con <strong>HashMap</strong>, entre sus elementos,... Lo único que tendrá que pasar es que esas estructuras a su vez sean serializables/deserializables con Serde (es decir, hayan puesto <strong>derive(Serialize,Deserialize)</strong>).<br><br><a href="https://files.adrianistan.eu/SerdeJson.png"><img class="aligncenter size-large wp-image-1240" src="https://files.adrianistan.eu/SerdeJson-1024x546.png" alt="" width="840" height="448" /></a><br><br></div><br></div>]]></description>
                <comments>https://blog.adrianistan.eu/leer-escribir-json-rust-serde</comments>
                <pubDate>Sat, 30 Dec 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Gana dos entradas dobles para &quot;Bugs&quot; en el Museo Reina Sofía</title>
                <link>https://blog.adrianistan.eu/gana-dos-entradas-dobles-bugs-museo-reina-sofia</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/gana-dos-entradas-dobles-bugs-museo-reina-sofia</guid>
                <description><![CDATA[En enero de 2018, el Museo Reina Sofía de Madrid acogerá la exposición <strong>Bugs</strong>, una exposición dedicada a esas obras de arte que muchas veces pasan inadvertidas, los bugs.<br><br><a href="https://files.adrianistan.eu/Edificio_Sabatini._Museo_Nacional_Centro_de_Arte_Reina_Sofía.jpg"><img class="aligncenter size-large wp-image-1221" src="https://files.adrianistan.eu/Edificio_Sabatini._Museo_Nacional_Centro_de_Arte_Reina_Sofía-1024x736.jpg" alt="" width="840" height="604" /></a>En esta exposición, que se expondrá hasta el 1 de abril de 2018, podremos ver algunos de los máximos exponentes de este estilo artístico surgido a finales del siglo XX y muy popular en el siglo XXI. Como yo mismo he formado parte del comité de selección de obras, tengo el honor de regalar dos entradas dobles para la exposición.<br><br><em>«Queremos reunir en un mismo edificio todas las sensaciones que han provocado a los usuarios. Rabia, indignación, ira y soledad.»</em> dice Sara Echeverría, directora de exposiciones temporales del Reina Sofía y buena amiga.<br><br>Algunas de las obras, más destacadas son:<br><br><a href="https://files.adrianistan.eu/RWT3K.jpg"><img class="wp-image-1223 size-large" src="https://files.adrianistan.eu/RWT3K-1024x768.jpg" alt="" width="840" height="630" /></a> Pantalla azul de Windows sobre monitor Samsung<br><br><a href="https://files.adrianistan.eu/BlueScreenWiinXP.jpg"><img class="wp-image-1224 size-large" src="https://files.adrianistan.eu/BlueScreenWiinXP-1024x768.jpg" alt="" width="840" height="630" /></a> Pantalla azul de Windows sobre monitor LG y cables desordenados<br><br>&nbsp;<br><br><a href="https://files.adrianistan.eu/gatwick_airport_bsod.jpg"><img class="wp-image-1225 size-full" src="https://files.adrianistan.eu/gatwick_airport_bsod.jpg" alt="" width="933" height="803" /></a> Pantalla azul de Windows rotada PI/2 radianes en aeropuerto<br><br><em>«Las pantallas azules nos transmiten finalidad. Nos hacen ver que todo es efímero. Todo nuestro trabajo, nuestra huella en la humanidad, puede desvanecerse con un simple error de driver. Al final, no somos nada. Una gota más, del océano inmenso.»</em> dice Bill Gates, que fue invitado para preparar la exposición. «Creemos que Bill Gates es uno de los maestros en el arte del bug, su ayuda nos ha sido indispensable para encontrar el enfoque filosófico que buscábamos».<br><br><a href="https://files.adrianistan.eu/Scheme.png"><img class="size-full wp-image-1226" src="https://files.adrianistan.eu/Scheme.png" alt="" width="554" height="652" /></a> Final de un programa en LISP<br><br><em>«A mí siempre me han atraído los paréntesis de LISP. Era como algo sexual. Sentía orgasmos al descubrir que podía meter más paréntesis todavía.»</em> Y es que en la exposición también se verán obras proclives a bugs como los paréntesis de LISP o la sagrada trinidad de JavaScript.<br><br><a href="https://files.adrianistan.eu/DM1czpAXkAAH8qF.jpg"><img class="aligncenter size-full wp-image-1227" src="https://files.adrianistan.eu/DM1czpAXkAAH8qF.jpg" alt="" width="659" height="317" /></a><em>«JavaScript iba a llenar bastantes salas, así que solamente hemos elegido lo mejor. Ha sido difícil, porque cada día salen nuevos e impresionantes bugs, en cada nueva librería o framework. Habrá que estar atentos a ellos, porque son el futuro de los bugs»</em><br><br>También se ha dejado una sala especial a los ciberataques, que este año han sabido aprovechar con mucha intelegencia bugs de los sistemas operativos más conocidos. ¿Cómo olvidar el WannaCry de Edvard Munch?<br><br><a href="https://files.adrianistan.eu/munch-scream-bitcoin-tall-300x375.jpg"><img class="wp-image-1228 size-full" src="https://files.adrianistan.eu/munch-scream-bitcoin-tall-300x375.jpg" alt="" width="300" height="375" /></a> WannaCry, de Edvard Munch<br><br>Pero no sería  una exposición completa sin contar con bugs como <strong>el efecto 2000</strong> o el lanzamiento del cohete <strong>Ariane 5</strong> (esas cosas pasan por usar ints de 16 bits en un cohete).<br><br><iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/Y0y2Bnv1djA" frameborder="0" gesture="media" allow="encrypted-media" allowfullscreen="allowfullscreen"></iframe><br><br><iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/gp_D8r-2hwk" frameborder="0" gesture="media" allow="encrypted-media" allowfullscreen="allowfullscreen"></iframe><br><br>Si queréis participar para ganar una de las dos entradas dobles para ver la exposición, solo tenéis que <a href="https://es.wikipedia.org/wiki/D%C3%ADa_de_los_Santos_Inocentes">hacer click aquí</a>.]]></description>
                <comments>https://blog.adrianistan.eu/gana-dos-entradas-dobles-bugs-museo-reina-sofia</comments>
                <pubDate>Thu, 28 Dec 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>¡Feliz navidad y próspero 2018! (con fractales)</title>
                <link>https://blog.adrianistan.eu/feliz-navidad-prospero-2018-fractales</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/feliz-navidad-prospero-2018-fractales</guid>
                <description><![CDATA[Feliz Navidad! Si estás leyendo esto, darte las gracias por seguir leyendo este blog. Me alegro mucho de tener cada vez más lectores y es algo que me anima a escribir más y más. En este especial navideño disfrutaremos de éxitos musicales de la navidad, veremos como hacer un fractal muy navideño en Rust y os pediré vuestra opinión.<br><br>Comenzamos con <strong>In Dulci Jubilo</strong> y este cover de <a class="yt-simple-endpoint style-scope yt-formatted-string" href="https://www.youtube.com/channel/UCaeIy5lvabL7naWxfRUn5RA">Giuliano Ferrace Leanza</a><br><br><iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/qP4SoR5w7xQ" frameborder="0" gesture="media" allow="encrypted-media" allowfullscreen="allowfullscreen"></iframe><br><h2>El fractal de Koch</h2><br><a href="https://es.wikipedia.org/wiki/Helge_von_Koch">Helge von Koch</a> fue un matemático sueco que en 1904 publica un artículo sobre la que desde entonces se llamaría curva de Koch. Se trata de una curva, generada con unas reglas muy simples. La idea fundamental es que se parte de una recta, que se divide en tres trozos iguales. El trozo del medio se sustituye por un triángulo equilátero. Entonces la curva recorre el primer trozo, sube el triángulo, baja el triángulo y continúa recto.<br><br><a href="https://files.adrianistan.eu/621px-Von_koch_1_etape.svg_.png"><img class="aligncenter size-full wp-image-1211" src="https://files.adrianistan.eu/621px-Von_koch_1_etape.svg_.png" alt="" width="621" height="180" /></a>Si ahora en cada uno de los cuatro trozos rectos aplicamos la misma curva de Koch obtenemos esto:<br><br><a href="https://files.adrianistan.eu/621px-Von_koch_2_etapes.svg_.png"><img class="aligncenter size-full wp-image-1212" src="https://files.adrianistan.eu/621px-Von_koch_2_etapes.svg_.png" alt="" width="621" height="180" /></a>Según la terminología de Mandelbrot, este fractal es un teragón. Si ahora en vez de aplicar esto sobre una recta, lo hacemos sobre un triángulo equilátero con sus tres trozos rectos iguales, <strong>¿qué obtenemos?</strong><br><h2>Turtle, una librería de Rust para gráficos tortuga</h2><br>Quizá entre mis lectores haya alguno que aprendió a programar con <strong>LOGO</strong>. Una de las características de LOGO era que disponía de una tortuga con la que íbamos dibujando según nos movíamos. Esta manera de dibujar, no es óptima en rendimiento puro, pero es ideal para fractales y para <strong>enseñar a programar</strong>. Python incorpora en su librería estándar el módulo <strong>turtle</strong> y en Rust está a punto de salir una librería que soporta una API similar llamada <strong><a href="http://turtle.rs/">turtle</a>.</strong><br><br>Lo primero que tenemos que hacer es definir la curva de Koch con <strong>turtle</strong>.<br><pre class="lang:rust decode:true ">    fn koch(&amp;mut self, length: f64){<br>        self.forward(length/3.0);<br>        self.left(60.0);<br>        self.forward(length/3.0);<br>        self.right(120.0);<br>        self.forward(length/3.0);<br>        self.left(60.0);<br>        self.forward(length/3.0);<br>    }</pre><br>Con esto nos valdría, pero <em>no es recursivo</em>. Necesitamos poder aplicar la curva de Koch en nuestras rectas (donde hacemos <em>forward</em>) de forma recursiva. Pero si lo hacemos recursivo de forma infinita no acabará nunca, es por ello que tenemos que indicar cuantos niveles de recursividad queremos.<br><pre class="lang:rust decode:true ">    fn op(&amp;mut self, length: f64, level: u32) {<br>        if level == 0{<br>            self.forward(length);<br>        }else{<br>            self.koch(length,level);<br>        }<br>    }<br>    fn koch(&amp;mut self, length: f64, level: u32){<br>        self.op(length/3.0,level-1);<br>        self.left(60.0);<br>        self.op(length/3.0,level-1);<br>        self.right(120.0);<br>        self.op(length/3.0,level-1);<br>        self.left(60.0);<br>        self.op(length/3.0,level-1);<br>    }</pre><br>Esto ya tiene más sentido. Ahora juntemos todo lo necesario:<br><pre class="lang:rust decode:true " title="main.rs">extern crate turtle;<br><br>use turtle::Turtle;<br>use turtle::Distance;<br><br>trait Fractal{<br>    fn koch(&amp;mut self, length: f64, level: u32);<br>    fn op(&amp;mut self, length: f64, level: u32) {<br>        if level == 0{<br>            self.forward(length);<br>        }else{<br>            self.koch(length,level);<br>        }<br>    }<br>    fn forward(&amp;mut self, distance: Distance);<br>}<br><br>impl Fractal for Turtle{<br>    fn koch(&amp;mut self, length: f64, level: u32){<br>        self.op(length/3.0,level-1);<br>        self.left(60.0);<br>        self.op(length/3.0,level-1);<br>        self.right(120.0);<br>        self.op(length/3.0,level-1);<br>        self.left(60.0);<br>        self.op(length/3.0,level-1);<br>    }<br>    fn forward(&amp;mut self, distance: Distance){<br>        self.forward(distance);<br>    }<br>}<br><br>fn main() {<br>    let mut turtle = Turtle::new ();<br>    turtle.set_pen_size(2.0);<br>    turtle.pen_up();<br>    turtle.go_to([-200.0,100.0]);<br>    turtle.pen_down();<br>    turtle.set_speed(0);<br>    turtle.hide();<br>    turtle.right(90.0);<br>    turtle.koch(400.0,5);<br>    turtle.right(120.0);<br>    turtle.koch(400.0,5);<br>    turtle.right(120.0);<br>    turtle.koch(400.0,5);<br>}</pre><br>Si compilamos y ejecutamos esto con Cargo:<br><br><a href="https://files.adrianistan.eu/CopoFractalKoch.png"><img class="aligncenter size-large wp-image-1213" src="https://files.adrianistan.eu/CopoFractalKoch-1024x576.png" alt="" width="840" height="473" /></a>Y aquí tenemos al <strong>copo de nieve de Koch</strong>.<br><br>Tenéis el proyecto completo en GitHub: <a href="https://github.com/aarroyoc/fractal_koch_rust">https://github.com/aarroyoc/fractal_koch_rust</a><br><h2>Fractal (bis)</h2><br>Otro fractal muy interesante que programé mientras estaba haciendo el copo de nieve fue este otro. Desconozco si tiene nombre. Es algo más complejo de implementar y lo de los colores me dio bastantes dolores de cabeza hasta que encontré una progresión bella.<br><br><a href="https://files.adrianistan.eu/Fractal2-Mejor.png"><img class="aligncenter size-full wp-image-1216" src="https://files.adrianistan.eu/Fractal2-Mejor.png" alt="" width="978" height="680" /></a>Su código es (en este caso Python):<br><pre class="lang:python decode:true " title="fractal.py">from turtle import *<br>from math import cos, pi<br><br>a = 0<br><br>def fractal(length=100,level=1,flip=False):<br>    # 3906 llamadas a fractal = 5^5 + 5^4 + 5^3 + 5^2 + 5^1 + 5^0<br>    ((r,g,b),_) = color()<br>    global a<br>    a += 1<br>    if a % 7 == 0 and a &lt; 1953:<br>        b += 1<br>    if a % 7 == 0 and a &gt; 1953:<br>        r += 1<br>        b -= 1<br><br>    r = min(r,255)<br>    g = max(g,0)<br>    b = min(b,255)<br>    b = max(b,0)<br><br>    color(int(r),int(g),int(b))<br><br>    if not flip:<br>        left(90)<br>    else:<br>        right(90)<br>    if level == 1:<br>        forward(length)<br>    else:<br>        fractal(length*length/(2*cos(45)*length/2+length),level-1,flip)<br>    if not flip:<br>        right(45)<br>    else:<br>        left(45)<br>    if level == 1:<br>        forward(length/2)<br>    else:<br>        fractal(length*length/(4*cos(45)*length/2+length),level-1,not flip)<br>    if not flip:<br>        right(45)<br>    else:<br>        left(45)<br>    if level == 1:<br>        forward(length)<br>    else:<br>        fractal(length*length/(2*cos(45)*length/2+length),level-1,flip)<br>    if not flip:<br>        right(45)<br>    else:<br>        left(45)<br>    if level == 1:<br>        forward(length/2)<br>    else:<br>        fractal(length*length/(4*cos(45)*length/2+length),level-1,not flip)<br>    if not flip:<br>        right(45)<br>    else:<br>        left(45)<br>    if level == 1:<br>        forward(length)<br>    else:<br>        fractal(length*length/(2*cos(45)*length/2+length),level-1,flip)<br>    if not flip:<br>        left(90)<br>    else:<br>        right(90)<br><br>def main():<br>    colormode(255)<br>    penup()<br>    pensize(2.0)<br>    setx(-100)<br>    sety(-200)<br>    pendown()<br>    color(0,255,0)<br>    speed(0)<br>    tracer(False)<br>    fractal(50,6)<br>    done()<br><br><br>main()</pre><br>&nbsp;<br><br><iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/8CCf7gvmDEU" frameborder="0" gesture="media" allow="encrypted-media" allowfullscreen="allowfullscreen"></iframe><br><h2>Vuestra opinión</h2><br>Ahora os pido que me déis vuestra opinión sobre el blog. Usad los comentarios de debajo y contadme: ¿Cuál es el mayor problema del blog?, ¿crees que los contenidos salen con frecuencia suficiente y necesaria?, ¿los temas son interesantes?, ¿me voy mucho por las ramas?, ... Todo esto lo tendré en cuenta de cara al año que viene.<br><br>Aprovecho para recordar que podéis <a href="http://eepurl.com/b6NLlL">suscribiros a la lista de correo</a> para que os llegue un nuevo correo con cada post, podeís <a href="https://blog.adrianistan.eu/feed/">suscribiros al blog por RSS</a>, hay un <a href="https://t.me/adrianistan">canal de Telegram</a> y una <a href="https://www.facebook.com/Blog-Adrianist%C3%A1n-211912669250354/">página en Facebook</a>. En <a href="http://twitter.com/aarroyoca">Twitter</a>, <a href="http://gnusocial.cc/aarroyoc">GNU Social</a> e <a href="http://instagram.com/aarroyoca">Instagram</a> solo tengo perfiles personales, pero si os interesa, allí estoy. Por último en <a href="https://plus.google.com/+Adri%C3%A1nArroyoCalle">Google+</a> todavía sigo mandando los artículos.<br><br>Si os parece que me merezco una caña, siempre puedes donar usando PayPal, criptodivisas, tu apartamento en Comillas, ...<br><br>Sin más, feliz próspero año 2018<br><br><iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/1c6sWcsG4BE" frameborder="0" gesture="media" allow="encrypted-media" allowfullscreen="allowfullscreen"></iframe><br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/feliz-navidad-prospero-2018-fractales</comments>
                <pubDate>Fri, 22 Dec 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Estadísticas del blog, versión 2017</title>
                <link>https://blog.adrianistan.eu/estadisticas-blog-2017</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/estadisticas-blog-2017</guid>
                <description><![CDATA[Antes de acabar el año, es momento de hacer un repaso a las estadísticas del blog. Sé que el año no ha acabado, pero no creo que vaya a haber muchos cambios hasta que sean las campanadas.<br><h2>Usuarios</h2><br>En general 2017 ha sido un muy buen año. A fecha del 10 de diciembre he recibido <strong>24.677 sesiones</strong>, de <strong>19.618 usuarios</strong> y <strong>29.925 visitas a páginas</strong> (al acabar diciembre serán más de 30.000 fácilmente). Todo esto sin contar a la gente que entra con un bloqueador de anuncios, que puede bloquear Google Analytics también. <a href="https://pagefair.com/intel/">Según <strong>PageFair</strong></a>, en España el 19% de los usuarios de escritorio usan un AdBlocker. La gráfica que se dibuja es un poco rara sin embargo.<br><br><a href="https://files.adrianistan.eu/Analytics2017.png"><img class="aligncenter size-large wp-image-1201" src="https://files.adrianistan.eu/Analytics2017-1024x189.png" alt="" width="840" height="155" /></a>Prácticamente se ve plana debido al gran pico que hubo a finales de julio. De esta gráfica solo se puede apreciar otro pico el 20 de abril. Estos picos se relacionan con dos de los artículso más leídos este año en el blog.<br><br><a href="https://files.adrianistan.eu/EdadSexo2017.png"><img class="aligncenter size-large wp-image-1202" src="https://files.adrianistan.eu/EdadSexo2017-1024x391.png" alt="" width="840" height="321" /></a>El público no ha evolucionado mucho. Ha aumentado ligeramente el procentaje de mujeres y han aumentado los grupos de edad más alta, mientras que la franja 18-24 ha sufrido un descenso.<br><br><a href="https://files.adrianistan.eu/Idioma2017.png"><img class="aligncenter size-large wp-image-1203" src="https://files.adrianistan.eu/Idioma2017-1024x493.png" alt="" width="840" height="404" /></a>En el idioma tampoco hay grandes sorpresas. La mayoría entráis con <strong>español</strong>, <strong>catalán</strong> o <strong>en-us</strong> (que no deja de ser una configuración por defecto muy común). Las visitas en <strong>alemán</strong>, <strong>francés</strong> las achaco principalmente a emigrantes y <strong>portugués</strong> de Brasil a que mucha gente de allí sabe algo de español.<br><br><a href="https://files.adrianistan.eu/Mapa2017.png"><img class="aligncenter size-full wp-image-1204" src="https://files.adrianistan.eu/Mapa2017.png" alt="" width="611" height="387" /></a>Si bien otros años estaba más compensada la cosa, este año <strong>España</strong> acapara la mayor parte de las visitas (15.227). Le sigue muy de lejos, pero de forma nada despreciable <strong>Méxio</strong> y <strong>Argentina</strong> (1.588 y 1.003 respectivamente). Para completar los 10 países que más visitan mi blogn tenemos a Colombia, Estados Unidos, Chile, Venezuela, Perú, Alemania y Reino Unido.<br><br>Estados Unidos ya lleva siendo durante bastantes años un país hispano más, relativamente potente, en lo que es este blog y como siempre, me sorprende la falta de presencia de algunos países latinoamericanos como Uruguay o Ecuador, que podrían estar perfectamente por encima de Alemania o Reino Unido.<br><h2>Tecnología</h2><br><a href="https://files.adrianistan.eu/Navegadores2017.png"><img class="aligncenter size-large wp-image-1205" src="https://files.adrianistan.eu/Navegadores2017-1024x462.png" alt="" width="840" height="379" /></a><br><br>Respecto a los navegadores hay una tendencia clara en el uso de <strong>Chrome</strong>. <strong>Firefox</strong> se mantiene en segunda posición y <strong>Safari</strong> consolida su tercera posición. Vemos que <strong>Edge</strong> todavía no ha superado a <strong>Internet Explorer</strong> en este blog. El navegador más extraño con el que se ha accedido a mi blog es <strong>Puffin</strong>.<br><br><a href="https://files.adrianistan.eu/SistemaOperativo2017.png"><img class="aligncenter size-large wp-image-1206" src="https://files.adrianistan.eu/SistemaOperativo2017-1024x476.png" alt="" width="840" height="390" /></a>En sistemas operativos no hay apenas cambios. <strong>Windows</strong> y <strong>Android</strong> siguen siendo los reyes. Mención especial a aquellos que usan Firefox OS y BlackBerry.<br><h2>Adquisición</h2><br><a href="https://files.adrianistan.eu/Referral2017.png"><img class="aligncenter size-large wp-image-1207" src="https://files.adrianistan.eu/Referral2017-1024x517.png" alt="" width="840" height="424" /></a>Esta es una de mis partes preferidas, saber de dónde venís. <strong>Menéame</strong> repite como una web desde las que entra más tráfico al blog (este año este blog llegó a portada). <strong>Google</strong> aporta muchas visitas, como el gran buscador. <strong>RSS</strong> en realidad es una categoría peligrosa, puesto que hay varios sitios que toman la fuente <strong>RSS</strong> (planetas, bots, ...). Finalmente, <strong>Facebook</strong> parece despegar, aunque <strong>Twitter</strong> sigue arriba. Mención especial a <strong>Instagram</strong> (sorprendente para mí, la verdad) y... sigo sin saber como se contabilizan tantas visitas desde <strong>ads.adpv.com</strong>, pero creo que es un error en algún sitio.<br><br>Otras webs que me han enlazado: <a href="http://foroantiusura.org">foroantiusura.org</a>, <a href="http://hispachan.org">hispachan.org</a>, Pocket, <a href="http://iessanvicente.com">iessanvicente.com</a>, <a href="http://campusvirtual.uva.es">campusvirtual.uva.es</a>, <a href="http://aulas.inf.uva.es">aulas.inf.uva.es</a>, Mastodon, <a href="http://rust.libhunt.com">rust.libhunt.com</a>, GNU Social, <a href="http://www3.gobiernodecanarias.org">www3.gobiernodecanarias.org</a>, ... Desde el infame DesdeLinux todavía llega gente a mi blog...<br><h2>Contenido</h2><br><a href="https://files.adrianistan.eu/MasVistas2017.png"><img class="aligncenter size-large wp-image-1208" src="https://files.adrianistan.eu/MasVistas2017-1024x553.png" alt="" width="840" height="454" /></a>Y si, llegó la hora de saber cuáles han sido las páginas más vistas. La ganadora indiscutible es <a href="https://blog.adrianistan.eu/2017/07/27/lo-debes-las-tarjetas-credito-debito-prepago/"><em>Todo lo que debes de saber sobre las tarjetas de crédito, débito y prepago</em></a>, un artículo algo diferente en el blog, sobre uno de los muchos temas secundarios que me interesan y que gustó mucho. Este artículo causó el pico de finales julio. A continuación un clásico, <a href="https://blog.adrianistan.eu/2016/07/13/tutorial-gnu-cash/"><em>Tutorial de GnuCash</em></a>, que fue publicada el año anterior y sigue atrayendo a miles de personas. ¿Os acordáis del pico de abril? Fue provocado por <a href="https://blog.adrianistan.eu/2017/04/19/novedades-de-c17/"><em>Novedades de C++17</em></a>. Y es que, a la gente le interesa mucho C++. Y solo después de estos tres artículos tenemos a la portada del blog. El tutorial de Rust, de CMake y de WiX también han recibido bastantes visitas. Y el artículo de la <em>Calculadora de 4 bits</em> sigue recibiendo muchas visitas desde el año 2013 (es de los que estaban en el primer Blogger del principio).]]></description>
                <comments>https://blog.adrianistan.eu/estadisticas-blog-2017</comments>
                <pubDate>Sun, 10 Dec 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Estadística en Python: distribución binomial, normal y de Poisson (Parte VI)</title>
                <link>https://blog.adrianistan.eu/estadistica-python-distribucion-binomial-normal-poisson-parte-vi</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/estadistica-python-distribucion-binomial-normal-poisson-parte-vi</guid>
                <description><![CDATA[Después del rollo del capítulo anterior, vamos a entrar en algo más práctico, modelos de probabilidad. Hemos hablado de las funciones de probabilidad y de densidad, sin embargo, nos falta algo muy importante. ¿Cuáles son esas funciones exactamente? Es donde entran los modelos de probabilidad, modelos estadísticos que se pueden ajustar a una variable aleatoria con mejor o peor precisión y que nos dan los valores de la probabilidad. Empecemos, pero antes hagamos un apunte sobre las equivalencias en SciPy:<br><ul><br> 	<li><strong>cdf(x)</strong> - Función de distribución F(X)</li><br> 	<li><strong>sf(x)</strong> = 1 - cdf(x)</li><br> 	<li><strong>pmf(x)</strong> - Función de probabilidad f(x) (distribuciones discretas)</li><br> 	<li><strong>pdf(x)</strong> - Función de densidad f(x) (distribuciones continuas)</li><br> 	<li><strong>ppf(x)</strong> - Función inversa a cdf(x). Nos permite obtener el valor correspondiente a una probabilidad.</li><br></ul><br><h2>Distribución Binomial</h2><br>Un ensayo de Bernouilli se define como un experimento donde puede darse un éxito o fracaso y donde cada ensayo es independiente del anterior. Por ejemplo, un ensayo de Bernoulli de parámetro 0.5 sería lanzar una moneda a cara o cruz (mitad de posibilidades de cara, mitad de posibilidades de cruz).<br><br>Si repetimos N veces los ensayos de Bernouilli tenemos una distribución binomial.<br><p style="text-align: center;">[latex]<br>X \rightarrow B(N,P)<br>[/latex]</p><br><br><div>SciPy nos permite usar <strong>binom</strong> para trabajar con distribuciones binomiales.</div><br><div></div><br><div><em>Ejemplo: Un proveedor de DVDs regrabables afirma que solamente el 4 % de los</em></div><br><div><em>artículos suministrados son defectuosos. Si un cliente compra un lote de 25</em></div><br><div><em>DVDs, ¿cuál es el número esperado de DVDs defectuosos en el lote? Si el cliente</em></div><br><div><em>encuentra que 4 de los DVDs comprados son defectuosos, ¿debe dudar de la</em></div><br><div><em>afirmación del vendedor?</em></div><br><div></div><br><div>El número de DVDs defectuosos esperados es el equivalente a decir el número medio de DVDs defectuosos.</div><br><div><br><pre class="lang:python decode:true ">import scipy.stats as ss<br><br>X = ss.binom(25,0.04)<br><br>X.mean() # 1.0</pre><br>Es decir, de media habría 1 DVD defectuoso en el paquete. <strong>mean</strong> calcula la media de la distribución.<br><br>Para saber si hay que fiarse del vendedor vamos a calcular cuál era la probabilidad de que nos tocasen 4 DVDs defectuosos.<br><pre class="lang:default decode:true">import scipy.stats as ss<br><br>X = ss.binom(25,0.04)<br><br>pr = X.sf(3) # 0.016521575032415914</pre><br>Es decir, la probabilidad que ocurriese era del 1%. Podemos sospechar del fabricante. <strong>cdf</strong> calcula las probabilidades acumuladas. En este caso tenemos que calcular la probabilidad de que hubiese 4 o más fallos, Pr{X&gt;=4}. Una manera fácil de calcularlo es hacer 1-Pr{X&lt;4}. <strong>cdf</strong><strong>(n)</strong> nos permite calcular probabilidades acumuladas hasta N. Otra opción sería simplemente obtener la probabilidad de 0 DVDs defectuosos, 1 DVD defectuoso, de 2 DVDs defectuosos, de 3 DVDs defectuosos, sumarlo y restarlo de 1.<br><pre class="lang:python decode:true">import scipy.stats as ss<br><br>X = ss.binom(25,0.04)<br><br>pr = 1 - sum(X.pmf(x) for x in range(4))</pre><br><strong>pmf(n)</strong> devuelve la probabilidad de que X=N, Pr{X=N} Esto solo tiene sentido en ciertas distribuciones, las discretas, como es el caso de la binomial.<br><br>Podemos calcular la gráfica de esta distribución binomial.<br><pre class="lang:python decode:true">import scipy.stats as ss<br>import matplotlib.pyplot as plt<br><br>X = ss.binom(25,0.04)<br>x = np.arange(10)<br><br>plt.plot(x,X.pmf(x),"bo")<br>plt.vlines(x,0,X.pmf(x),"b")<br>plt.show()</pre><br><a href="https://files.adrianistan.eu/Binomial.png"><img class="aligncenter size-full wp-image-1190" src="https://files.adrianistan.eu/Binomial.png" alt="" width="640" height="480" /></a>En el gráfico también se puede ver que las probabilidades de tener 4 o más DVDs defectuosos son mínimas.<br><br></div><br><h2>Distribución hipergeométrica</h2><br>La distribución hipergeométrica es un modelo en el que se considera una población finita de tamaño N en la cual hay M individuos con una determinada característica y se seleccionan n y queremos saber la probabilidad de que haya cierto número de individuos con esa característica en la selección. Para trabajar con estas distribuciones, SciPy trae <strong>hypergeom</strong>.<br><br><em>Ejemplo: Se formó un jurado de 6 personas de un grupo de 20 posibles miembros de los cuales 8 eran mujeres y 12 hombres. El jurado seelecionó aleatoriamente, pero solamente tenía 1 mujer. ¿Hay motivos para dudarde la aletoriedad de la selección?</em><br><pre class="lang:python decode:true ">import scipy.stats as ss<br><br>X = ss.hypergeom(20,6,8)<br><br>X.cdf(1) # 0.18730650154798736</pre><br>La probabilidad de que ocurriese lo que ocurrió es del 18,7%, una probabilidad suficientemente alta como para pensar que no hubo manipulación. Podemos dibujar esta hipergeométrica:<br><br><a href="https://files.adrianistan.eu/Hipergeometrica.png"><img class="aligncenter size-full wp-image-1191" src="https://files.adrianistan.eu/Hipergeometrica.png" alt="" width="640" height="480" /></a>Como se puede observar en la gráfica el caso más probable, con cerca del 35% de posibilidades era que hubiese dos mujeres seleccionadas. Destacar también, que la probabilidad de que haya 7 mujeres en el jurado es cero, porque solo hay 6 plazas en el jurado.<br><h2>Distribución de Poisson</h2><br>La distribución de Poisson recoge sucesos independientes que ocurren en un soporte continuo. El número medio de sucesos por unidad de soporte se le conoce como <strong>λ</strong> y caracteriza la distribución. <strong>poisson</strong> nos permite crear distribuciones de este tipo.<br><br>Algunos ejemplos de distribuciones de Poisson: número de clientes que llegan cada hora a cierto puesto de servicio, número de averías diarias de un sistema informático, número de vehículos que pasan diariamente por un túnel, número de defectos por kilómetro de cable, ...<br><br><em>Ejemplo: </em><em>La impresora de una pequeña red informática recibe una media de 0</em><em>.1 </em><em>peticiones por segundo. Suponiendo que las peticiones a dicha impresora son </em><em>independientes y a ritmo constante, ¿cuál es la probabilidad de un máximo de 2 </em><em>peticiones en un segundo? Si la cola de la impresora tiene un comportamiento </em><em>deficiente cuando recibe más de 10 peticiones en un minuto, ¿cuál es la </em><em>probabilidad de que ocurra esto?</em><br><pre class="lang:python decode:true  ">import scipy.stats as ss<br><br>X = ss.poisson(0.1)<br>X.cdf(2) # 0.99984534692973537<br><br>Y = ss.poisson(6)<br>Y.sf(10) # 0.042620923582537995</pre><br><a href="https://files.adrianistan.eu/Poisson.png"><img class="wp-image-1193 size-full" src="https://files.adrianistan.eu/Poisson.png" alt="" width="640" height="480" /></a> Variable Y: número de peticiones a la impresora en un minuto (y la probabilidad de que suceda)<br><h2>Distribución exponencial</h2><br>Para modelizar el intervalo entre dos sucesos consecutivos que siguen una distribución de Poisson se usa la distribución exponencial de parámetro <strong>λ</strong>.<br><br><em>Ejemplo: </em><em>El proceso de accesos a una página web se produce de una forma </em><em>estable e independiente, siendo el intervalo entre dos accesos consecutivos una </em><em>v.a. exponencial. Sabiendo que, de media, se produce un acceso cada minuto,</em><em>¿cuál es la probabilidad de que no se produzcan accesos en 4 minutos? y ¿cuál es</em><em>la probabilidad de que el tiempo transcurrido entre dos accesos consecutivos sea </em><em>inferior a 90 segundos?</em><br><br>Esta distribución en SciPy es un poco rara, ya que no está implementada como podría esperarse.<br><pre class="lang:python decode:true ">import scipy.stats as ss<br><br>ss.expon.sf(4,loc=0,scale=1) # 0.018315638888734179<br>ss.expon.cdf(1.5,loc=0,scale=1) # 0.77686983985157021</pre><br><h2>Distribución normal</h2><br>Probablemente el modelo de distribución más usado y conocido. Lo usamos para describir variables reales continuas.<br><br><em>Ejemplo: La duración de un determinado componente electrónico, en horas, es una v.a. que se distribuye según una N(2000,40). ¿Cuál es la probabilidad de que la duración de una de esas componentes sea superior a 1900 horas? ¿y de que esté entre 1850 y 1950 horas?</em><br><pre class="lang:python decode:true ">import scipy.stats as ss<br><br>X = ss.norm(2000,40)<br>X.sf(1900) # 0.99379033467422384<br>X.cdf(1950) - X.cdf(1850) # 0.10556135638165455<br></pre><br>Podemos representar esta variable.<br><pre class="lang:python decode:true">import scipy.stats as ss<br>import numpy as np<br>import matplotlib.pyplot as plt<br><br>X = ss.norm(2000,40)<br>x = np.arange(X.ppf(0.01),X.ppf(0.99))<br><br>plt.plot(x,X.pdf(x),"r")<br>plt.show()</pre><br><a href="https://files.adrianistan.eu/Normal.png"><img class="aligncenter size-full wp-image-1195" src="https://files.adrianistan.eu/Normal.png" alt="" width="640" height="480" /></a>Estos modelos no son perfectos, pero son lo suficientemente flexibles para ser un buen punto de partida.]]></description>
                <comments>https://blog.adrianistan.eu/estadistica-python-distribucion-binomial-normal-poisson-parte-vi</comments>
                <pubDate>Fri, 08 Dec 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Estadística en Python: cálculo de probabilidades (Parte V)</title>
                <link>https://blog.adrianistan.eu/estadistica-python-calculo-probabilidades-parte-v</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/estadistica-python-calculo-probabilidades-parte-v</guid>
                <description><![CDATA[Ahora entramos en una de mis partes favoritas de la estadística, el cálculo de probabilidades, sin embargo va a ser muy teórico, sin apenas Python. En primer lugar vamos a definir algunos conceptos:<br><ul><br> 	<li><strong>Experimento</strong> cualquier proceso de obtención de una observación o medida en el que se suponen fijos ciertos factores. Los experimentos puede ser <strong>deterministas</strong> si solo es posible un resultado (aunque sea desconocido) y <strong>aleatorio</strong><strong>s</strong>. Llamamos <strong>azar</strong> a los factores que no controlamos de un experimento aleatorio.</li><br> 	<li><strong>Probabilidad:</strong> la incertidumbre de observar un determinado resultado antes de que se realice el experimento.</li><br> 	<li><strong>Suceso</strong>: el resultado o conjunto de resultados de un experimento aleatorio</li><br> 	<li><strong>Espacio muestral</strong>: el conjunto de todos los resultados posibles de un experimento aleatorio</li><br> 	<li><strong>Suceso complementario de A</strong>: lo que ocurre cuando no ocurre A</li><br> 	<li><strong>Suceso seguro</strong>: Aquel que ocurre siempre. Se representa con <strong>Ω</strong></li><br> 	<li><strong>Suceso imposible</strong>: Aquel que no forma parte del espacio muestral</li><br> 	<li><strong>Sucesos incompatibles</strong>: Aquellos que no pueden ocurrir de forma simultánea</li><br></ul><br>El cálculo de probabilidades nos sirve para valorar el riesgo de nuestras decisiones, anticipar eventos y valorar si nuestras hipótesis eran razonables.<br><br>Para el cálculo de probabilidades vamos a seguir la <strong>axiomática de Kolmogoroff</strong>.<br><h2>Regla de Laplace</h2><br>La ley fundamental de las probabilidades, define la probabilidad como la razón entre el número de casos favorables y el número de casos totales.<br><p style="text-align: center;">[latex]<br>Pr\{A\} = \frac{k}{n}<br>[/latex]</p><br>Esto es muy sencillo de utilizar y no voy a poner código Python. Para calcular tanto el número de casos favorables como el de casos totales es recomendable tener nociones de <strong>combinatoria</strong>, que resulta extremadamente útil en ese tipo de situaciones.<br><h2>Probabilidad condicionada</h2><br>¿Qué ocurre si disponemos de información suplementaria? Formulado de otra forma, ¿qué pasa si queremos calcular la probabilidad de A sabiendo B ha ocurrido? ¿Daría el mismo resultado? La respuesta es que no, la <strong>probabilidad de A condicionada por B</strong> se define de la siguiente forma:<br><p style="text-align: center;">[latex]<br>Pr\{A | B\} = \frac{Pr\{A \cap B \}}{Pr\{B\}}<br>[/latex]</p><br>¿Cómo se calcula la probabilidad de la intersección de A y B?<br><p style="text-align: center;">[latex]<br>Pr\{A \cap B\} = Pr\{A\}Pr\{B|A\}<br>[/latex]</p><br>Aquí nos damos cuenta que si A es independiente de B, la probabilidad de su intersección es simplemente el producto de Pr{A} y Pr{B}.<br><h2>Teorema de Bayes</h2><br>La generalización de lo anterior es el conocido <strong>teorema de Bayes</strong>. Podemos usarlo para resolver una gran cantidad de problemas.<br><br><em>En la provincia de Soria, el negocio de acceso a Internet se reparte entre dos operadores, Timofónica y Robafone y dos únicas marcas de routers, Xisco y Nuaweii. En Soria, la cuota de mercado de Timofónica es del 60% y de Robafone el resto. El 70% de los usuarios dispone de router Xisco y el 30% de ambas marcas. Además se sabe que la probabilidad de corte de acceso es 0.1 para usuarios de Timofónica, 0.15 para Robafone y 0.05 para routers Xisco.</em><br><br><em>¿Cuál es la probabilidad de que a un usuario se le corte el Internet?</em><br><br>Primero vamos a definir un diccionario <strong>pr</strong> con las probabilidades que nos da el enunciado. Tenemos varias probabilidades relacionadas con un usuario: operador, router, fallos condicionados, ...<br><pre class="lang:python decode:true">pr = dict()<br>pr["Timofónica"] = 0.6<br>pr["Robafone"] = 0.4<br>pr["Xisco"] = 0.7<br>pr["Xisco Y Nuaweii"] = 0.3<br>pr["Corte | Timofónica"] = 0.1<br>pr["Corte | Robafone"] = 0.15<br>pr["Corte | Xisco"] = 0.05<br></pre><br>Para calcular la probabilidad de corte de un usuario hay que sumar la probabilidad de ser usuario de una compañía y tener un corte y de ser de otra compañía y tener un corte.<br><pre class="lang:python decode:true">pr["Corte"] =  pr["Corte | Timofónica"]*pr["Timofónica"] + pr["Corte | Robafone"]*pr["Robafone"]</pre><br>En este caso Pr{Corte} = 0.12. La probabilidad de que un usuario cualquiera de Soria tenga un corte es del 12%.<br><br><em>Si se sabe que un usuario tiene la línea cortada, ¿cuál es la probabilidad de que tenga router Xisco en casa?</em><br><br>En este caso se pide Pr{Xisco|Corte}. Según el teorema de Bayes, esto es:<br><p style="text-align: center;">[latex]<br>Pr\{Xisco|Corte\} = \frac{Pr\{Xisco \cap Corte\}}{Pr\{Corte\}} = \frac{Pr\{Xisco\}Pr\{Corte | Xisco\}}{Pr\{Corte\}}<br>[/latex]</p><br><br><pre class="lang:python decode:true ">pr["Xisco | Corte"] = pr["Xisco"]*pr["Corte | Xisco"]/pr["Corte"]</pre><br>Que da una probabilidad de 0,29. Es decir, si el usuario tiene un corte, la probabilidad de que en su casa tenga un router Xisco es del 29%.<br><br><em>¿Cuál es la probabilidad de que se produzca un corte a un usuario que no tiene un router Xisco?</em><br><br>En este caso se pide Pr{Corte | Nuaweii}. Y tenemos un pequeño problema y es que no sabemos la probabilidad de que un usuario tenga en su casa Nuaweii. Con un poco de manipulación matemática podemos obtener una expresión que no depende de Pr{Nuaweii}.<br><p style="text-align: center;">[latex]<br>Pr\{Corte|Nuaweii\} = \frac{Pr\{Corte \cap Nuaweii\}}{Pr\{Nuaweii\}} \\ = \frac{Pr\{Corte \cap (\Omega-Xisco)\}}{1-Pr\{Xisco\}} = \frac{Pr\{Corte - Corte \cap Xisco\}}{1-Pr\{Xisco\}} \\ = \frac{Pr\{Corte\}-Pr\{Corte \cap Xisco\}}{1-Pr\{Xisco\}}<br>[/latex]</p><br><br><pre class="lang:python decode:true">pr["Corte | Nuaweii"] = (pr["Corte"] - pr["Xisco"]*pr["Corte | Xisco"])/(1-pr["Xisco"])</pre><br><h2>Funciones asociadas</h2><br><strong>Función de probabilidad</strong>: Una función que devuelve la probabilidad de ser obtenido un valor en un experimento aleatorio. La suma de las funciones de probabilidad de todos los valores que puede tomar la variable es 1.<br><br><strong>Función de distribución F(x)</strong> Una función que devuelve la probabilidad de obtener un valor igual o menor al valor en un experimento aleatorio. Esta función lo que hace es ir acumulando.<br><br><strong>Función de densidad f(x):</strong> Como en variables aleatorias continuas no tiene sentido hablar de función de probabilidad (siempre sería 0), se define la función de densidad, como la función que da la probabilidad de que una variable aleatoria esté entre A y B.<br><br>Como es lógico es posible pasar entre función de densidad y de distribución mediante integreación y derivación.<br><h2>Medidas asociadas</h2><br><strong>Esperanza matemática (μ)</strong> o media poblacional<br><p style="text-align: center;">[latex]<br>\mu = E(x) = \int_{-\inf}^{\inf} xf(x)dx<br>[/latex]</p><br><strong>Mediana</strong>: el X que da como resultado 0.5 en la función de densidad, F(X) = 0.5<br><br><strong>Varianza:</strong><br>[latex]<br>Var(X) = \sigma^2 = E((X-\mu)^2)<br>[/latex]<br><br>Y con esto dejamos este capítulo teórico pero necesario para el siguiente (que será muy útil).<br><br>&nbsp;<br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/estadistica-python-calculo-probabilidades-parte-v</comments>
                <pubDate>Sun, 03 Dec 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Curve, una tarjeta para dominarlas a todas</title>
                <link>https://blog.adrianistan.eu/curve-una-tarjeta-dominarlas-todas</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/curve-una-tarjeta-dominarlas-todas</guid>
                <description><![CDATA[Hace poco llegó a mi conocimiento la existencia de <a href="https://www.imaginecurve.com/"><strong>Curve</strong></a>, se trata de una tarjeta que te permite tener todas tus tarjetas combinadas en una única tarjeta. Así, con la misma tarjeta, puedes pagar una compra con tu tarjeta del BBVA y después pagar con la de Bankia.<br><br>La tarjeta no tenía comisiones, así que decidí pedirla, aunque solo fuese por probarla. Me bajé la app, me registré y el envío llegó a España desde Reino Unido en unos días.<br><br><a href="https://files.adrianistan.eu/DSC_0005-1.jpg"><img class="aligncenter size-large wp-image-1177" src="https://files.adrianistan.eu/DSC_0005-1-1024x576.jpg" alt="" width="840" height="473" /></a>Se trata de una tarjeta MasterCard, aceptada en multitud de establecimientos y cajeros. Tiene NFC, <a href="https://blog.adrianistan.eu/2017/07/27/lo-debes-las-tarjetas-credito-debito-prepago/">chip EMV</a> y banda magnética. Para activarla debemos realizar una compra con chip, usando el PIN que se nos mostrará en la aplicación. Por su uso no nos cobran nada, pero no la recomiendo para retirar dinero de cajeros, pues en España nos cobrarán (para eso mejor <strong>Revolut</strong> o <strong>N26</strong>). La tarjeta la controlamos a través de la aplicación:<br><br><a href="https://files.adrianistan.eu/Screenshot_2017-11-24-22-16-04.png"><img class="aligncenter wp-image-1178 size-medium" src="https://files.adrianistan.eu/Screenshot_2017-11-24-22-16-04-169x300.png" alt="" width="169" height="300" /></a><br><br><a href="https://files.adrianistan.eu/Screenshot_2017-11-24-22-16-49.png"><img class="wp-image-1181 size-medium" src="https://files.adrianistan.eu/Screenshot_2017-11-24-22-16-49-169x300.png" alt="" width="169" height="300" /></a> Tarjeta de N26 en la aplicación de Curve<br><br><a href="https://files.adrianistan.eu/Screenshot_2017-11-24-22-16-04.png"></a>En la aplicación podemos seleccionar entre las tarjetas que tenemos agregadas y ver los gastos que hemos realizados con ellas.<br><br><a href="https://files.adrianistan.eu/Screenshot_2017-11-24-22-17-06.png"><img class="wp-image-1182 size-medium" src="https://files.adrianistan.eu/Screenshot_2017-11-24-22-17-06-169x300.png" alt="" width="169" height="300" /></a> Tarjetas que tengo agregadas a mi tarjeta Curve<br><br>Para añadir una tarjeta hay varios métodos, dependiendo de la tarjeta en cuestión. El más habitual será hacer un cargo para posteriormente devolver la cantidad. Si la tarjeta no tiene protecciones de PIN 3D, nos pedirá que indiquemos un número que aparece en el concepto del cargo.<br><br><a href="https://files.adrianistan.eu/Screenshot_2017-11-24-22-16-40.png"><img class="wp-image-1180 size-medium" src="https://files.adrianistan.eu/Screenshot_2017-11-24-22-16-40-169x300.png" alt="" width="169" height="300" /></a> No podemos usar una tarjeta hasta que no esté verificada<br><br>Actualmente Curve dispone de un sistema de referidos. Por cada amigo que invites tú ganas 5£ y tu amigo gana 5£. Curve es una muy buena tarjeta, que permite controlar todas tus tarjetas desde un único sitio. Es la tarjeta que actualmente llevo en la cartera para pagar (y la de Revolut para los cajeros). Mi código de amigo, por el cual os darán 5£ al hacer vuestra primera compra es:<br><p style="text-align: center;"><strong>ZC6PR</strong></p><br>Para haceros con la tarjeta necesitáis registraros desde la aplicación para <a href="https://play.google.com/store/apps/details?id=com.imaginecurve.curve.prd">Android</a> o para <a href="https://itunes.apple.com/us/app/curve-all-your-cards-in-one/id1049397112?mt=8">iOS</a>.]]></description>
                <comments>https://blog.adrianistan.eu/curve-una-tarjeta-dominarlas-todas</comments>
                <pubDate>Fri, 24 Nov 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Easter egg en el comando man de GNU&#x2F;Linux</title>
                <link>https://blog.adrianistan.eu/easter-egg-comando-man-gnu-linux</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/easter-egg-comando-man-gnu-linux</guid>
                <description><![CDATA[Recientemente se ha sabido de un <strong>easter egg</strong> escondido en el comando <em>man</em> de GNU/Linux. Se trata de una referencia al popular grupo de música sueco, <strong>ABBA</strong>.<br><br><a href="https://files.adrianistan.eu/ABBA-new-album-announced-842965.jpg"><img class="aligncenter size-full wp-image-1166" src="https://files.adrianistan.eu/ABBA-new-album-announced-842965.jpg" alt="" width="590" height="350" /></a><br><br>Si conocéis la discografía de ABBA mínimamente seguro que os sonará la canción: <em>Gimme! Gimme! Gimme! (A Man After Midnight)</em><br><br><iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/XEjLoHdbVeE" frameborder="0" allowfullscreen="allowfullscreen"></iframe><br>Pues exactamente ocurre eso. Si ejecutamos <em>man</em> a las 00:30 (after midnight), nos saltará un <strong>gimme, gimme, gimme</strong>. Sí, es un chsite terrible, pero así es el humor de los programadores de <em>man</em>. Este easter egg fue introducido hace ahora 6 años a raíz de <a href="https://twitter.com/marnanel/status/132280557190119424">una broma en Twitter</a>.<br><h2>Ejecutando el easter egg</h2><br>Existen dos maneras de ver el easter egg. La primera es esperar hasta las 00:30 y entonces ejecutar <em>man</em>. Otra opción es usar <strong>faketime</strong> para simular en el comando que la hora es 00:30.<br><br><a href="https://files.adrianistan.eu/ManEasterEgg.png"><img class="aligncenter size-full wp-image-1165" src="https://files.adrianistan.eu/ManEasterEgg.png" alt="" width="314" height="118" /></a><br><br>Este easter egg ha provocado algún que otro en su interacción con otros programas, por lo que los programadores han considerado quitarlo. No obstante, debido al revuelo causado, se ha reducido su impacto y ahora solamente se muestra si se ejecuta man sin más argumentos.]]></description>
                <comments>https://blog.adrianistan.eu/easter-egg-comando-man-gnu-linux</comments>
                <pubDate>Tue, 21 Nov 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Estadística en Python: análisis de datos multidimensionales y regresión lineal (Parte IV)</title>
                <link>https://blog.adrianistan.eu/estadistica-python-analisis-datos-multidimensionales-regresion-lineal-parte-iv</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/estadistica-python-analisis-datos-multidimensionales-regresion-lineal-parte-iv</guid>
                <description><![CDATA[Hasta ahora hemos tratado con una única variable por separado. Ahora vamos a ver qué podemos hacer con varias variables en la misma muestra (conjunto de datos). Nos interesará saber si están relacionadas o no (<strong>independencia</strong> o no). Si existe relación (estan <strong>correlacionadas</strong>) vamos a construir un modelo de <strong>regresión lineal</strong>.<br><h2>Distribución conjunta de frecuencias</h2><br>En el caso de dos variables, podemos construir una distribución conjunta de frecuencias. Se trata de una tabla de doble entrada donde cada dimensión corresponde a cada variable y el valor de las celdas representa la frecuencia del par. Para ello podemos usar <strong>crosstab</strong> también (de hecho, su uso original es este).<br><br><em>Ejemplo: En las votaciones a alcalde de la ciudad de Valladolid se presentaban Rafael, Silvia y Olga. Analiza los resultados e informa de quién fue el ganador de las elecciones. ¿Quién fue el candidato favorito en el barrio de La Rondilla?</em><br><pre class="lang:default decode:true " title="votos.csv">usuario,voto,distrito<br>Nerea,Rafael,Centro<br>Esteban,Olga,Centro<br>Ismael,Silvia,Centro<br>Silvia,Rafael,Centro<br>Susana,Rafael,Centro<br>Laura,Rafael,Centro<br>Raquel,Olga,Centro<br>Eduardo,Olga,La Rondilla<br>Javier,Silvia,La Rondilla<br>Saturnino,Rafael,La Rondilla<br>Segundo,Olga,La Rondilla<br>Celia,Silvia,La Rondilla<br>Olga,Rafael,La Rondilla<br>Casimiro,Silvia,La Rondilla<br>Rafael,Silvia,La Rondilla</pre><br><pre class="lang:python decode:true">import pandas as pd<br><br>df = pd.read_csv("votos.csv")<br>tab = pd.crosstab(df["voto"],df["distrito"],margins=True) # margins si queremos generar la fila y columna All<br>total = tab["All"][:-1]<br>winner = total.idxmax()<br>print("El ganador de las elecciones fue "+str(winner))<br><br>rondilla = tab["La Rondilla"][:-1]<br>winner_rondilla = rondilla.idxmax()<br>print("El ganador en el distrito de La Rondilla fue "+str(winner_rondilla))</pre><br><a href="https://files.adrianistan.eu/VotosCrosstab.png"><img class="aligncenter size-full wp-image-1150" src="https://files.adrianistan.eu/VotosCrosstab.png" alt="" width="652" height="460" /></a>Como podéis ver, un humando podría haber sacado estas conclusiones observando simplemente la tabla conjunta de frecuencias. ¿Quién tiene más votos en total? Rafael, con 6 en All (la suma de los distritos). ¿Quién ha sacado más votos en La Rondilla? Silvia, con 4 en la columna de La Rondilla. Por último, ¿votó más gente en el Centro o en La Rondilla? Votaron más en La Rondilla (8 votos), que en el Centro (7 votos).<br><br>A las frecuencias <strong>All</strong> se las llama comúnmente <strong>distribuciones marginales</strong>. Cuando discriminamos las frecuencias a un solo valor de una variable, se habla de <strong>distribuciones condicionadas</strong>, en este caso hemos usado la distribución de votos condicionada al distrito La Rondilla. Estas distribuciones son univariantes como habréis sospechado.<br><h2>Gráfico XY o bivariante</h2><br>Una manera muy útil de observar posibles correlaciones es con el gráfico XY, solamente disponible para distribuciones bivariantes. Cada observación se representa en el plano como un punto. En Matplotlib podemos dibujarlo con <strong>scatter</strong>.<br><br><em>Ejemplo: Represente el gráfico XY de las variables ingresos y gastos de las familias.</em><br><pre class="lang:default decode:true " title="ingresos_gastos.csv">id,ingresos,gastos<br>1,1000,800<br>2,1100,885<br>3,1100,880<br>4,1200,1000<br>5,1400,1000<br>6,900,750<br>7,600,550<br>8,2000,1200<br>9,1200,1000<br>10,1250,1000<br>11,3000,2400<br>12,2200,1700<br>13,1700,1300<br>14,1650,1220<br>15,1825,1500<br>16,1435,1200<br>17,980,800<br>18,1050,800<br>19,2105,1680<br>20,1280,1020<br>21,1590,1272<br>22,1300,1040<br>23,1200,1000<br>24,1110,890<br>25,850,680</pre><br><pre class="lang:python decode:true">import pandas as pd<br>import matplotlib.pyplot as plt<br><br>df = pd.read_csv("ingresos_gastos.csv")<br>plt.scatter(df["ingresos"],df["gastos"])<br>plt.xlabel("Ingresos")<br>plt.ylabel("Gastos")<br>plt.show()</pre><br><a href="https://files.adrianistan.eu/IngresosGastos.png"><img class="aligncenter size-full wp-image-1151" src="https://files.adrianistan.eu/IngresosGastos.png" alt="" width="640" height="480" /></a>En la imagen podemos ver cada dato representado por un punto. En este ejemplo puede apreciarse como los puntos estan en torno a una línea recta invisible.<br><h2>Covarianza</h2><br>Para medir la relación entre dos variables podemos definir la covarianza:<br><p style="text-align: center;">[latex]<br>cov_{x,y}=\frac{\sum_{i=1}^{N}(x_{i}-\bar{x})(y_{i}-\bar{y})}{N}<br>[/latex]</p><br>Pandas trae el método <strong>cov</strong> para calcular la matriz de covarianzas. De esta matriz, obtendremos el valor que nos interesa.<br><pre class="lang:python decode:true ">import pandas as pd<br><br>df = pd.read_csv("ingresos_gastos.csv")<br><br>covarianza = df.cov()["ingresos"]["gastos"]<br></pre><br>¿Y la covarianza qué nos dice? Por si mismo, bastante poco. Como mucho, si es positivo nos dice que se relacionarían de forma directa y si es negativa de forma inversa. Pero la covarianza está presente en muchas fórmulas.<br><h2>Coeficiente de correlación lineal de Pearson</h2><br><p style="text-align: center;">[latex]<br>r_{x,y}=\frac{cov_{x,y}}{s_{x}s_{y}}<br>[/latex]</p><br>Uno de los métodos que usa la covarianza (aunque Pandas lo va a hacer solo) es el coeficiente de correlación lineal de Pearson. Cuanto más se acerque a 1 o -1 más correlacionadas están las variables. Su uso en Pandas es muy similar a la covarianza.<br><pre class="lang:default decode:true ">import pandas as pd<br><br>df = pd.read_csv("ingresos_gastos.csv")<br>r = df.corr(method="pearson")["ingresos"]["gastos"]</pre><br>En este ejemplo concreto, el coeficiente de correlación de Pearson nos da <strong>0.976175</strong>. Se trata de un valor lo suficientemente alto como para plantearnos una correlación lineal. Es decir, que pueda ser aproximado por una recta. Si este coeficiente es igual a 1 o -1, se puede decir que una variable es fruto de una transformación lineal de la otra.<br><h2>Ajuste lineal</h2><br>Vamos a intentar encontrar un modelo lineal que se ajuste a nuestras observaciones y nos permita hacer predicciones. Esta recta se llamará <strong>recta de regresión</strong> y se calcula de la siguiente forma:<br><p style="text-align: center;">[latex]<br>\hat{y}-\bar{y}=\frac{cov_{x,y}}{s_{x}^2}(x-\bar{x})<br>[/latex]</p><br>Usando las funciones de varianza, media y covarianza Pandas no es muy complicado hacer una recta de regresión:<br><pre class="lang:python decode:true">def recta(x):<br>    pendiente = df.cov()["ingresos"]["gastos"]/df["ingresos"].var()<br>    return pendiente*(x-df["ingresos"].mean())+df["gastos"].mean()</pre><br>Que podemos probar visualmente:<br><pre class="lang:python decode:true">import pandas as pd<br>import numpy as np<br>import matplotlib.pyplot as plt<br><br>df = pd.read_csv("ingresos_gastos.csv")<br><br>def recta(x):<br>    pendiente = df.cov()["ingresos"]["gastos"]/df["ingresos"].var()<br>    return pendiente*(x-df["ingresos"].mean())+df["gastos"].mean()<br><br>line = [recta(x) for x in np.arange(3000)]<br>plt.scatter(df["ingresos"],df["gastos"])<br>plt.plot(line)<br>plt.show()</pre><br><a href="https://files.adrianistan.eu/Correlacion.png"><img class="aligncenter size-full wp-image-1156" src="https://files.adrianistan.eu/Correlacion.png" alt="" width="640" height="480" /></a>Sin embargo SciPy ya nos trae un método que calcula la pendiente, la ordenada en el origen, el coeficiente de correlación lineal de Pearson y mucho más en un solo lugar. Es mucho más eficiente, se trata de <strong>linregress</strong>.<br><pre class="lang:python decode:true">import pandas as pd<br>import numpy as np<br>import matplotlib.pyplot as plt<br>from scipy import stats as ss<br><br>df = pd.read_csv("ingresos_gastos.csv")<br>pendiente, ordenada, pearson, p, error = ss.linregress(df["ingresos"],df["gastos"])<br><br>def recta(x):<br>    return pendiente*x + ordenada<br><br>recta = np.vectorize(recta)<br>linea = recta(np.arange(3000))<br>plt.scatter(df["ingresos"],df["gastos"])<br>plt.plot(linea)<br>plt.show()</pre><br>Además, para calcular los valores del gráfico, he usado <strong>vectorize</strong> de NumPy, que permite mapear los arrays nativos de NumPy. Más eficiente. Mismo resultado.<br><h2>La ley de Ohm</h2><br><a href="https://files.adrianistan.eu/Georg_Simon_Ohm3.jpg"><img class="aligncenter size-full wp-image-1157" src="https://files.adrianistan.eu/Georg_Simon_Ohm3.jpg" alt="" width="337" height="428" /></a>¿Quién no ha oído hablar de la Ley de Ohm? Se trata de una ley que relaciona la diferencia de potencial con el amperaje dando lugar a la resistencia. La ley fue enunciada por George Simon Ohm, aunque no exactamente como la conocemos hoy en día. En este ejemplo vamos a deducir de cero la ley de Ohm. <em>Este ejercicio se puede hacer en casa con datos reales si se dispone de un polímetro (dos mejor) y una fuente de alimentación con tensión regulable. Este ejercicio pueden hacerlo niños sin problema.</em><br><br><strong>Olvida todo lo que sepas de la ley de Ohm</strong><br><br>Es posible apreciar que en un circuito con una bombilla, si introducimos una pieza cerámica, la intensidad de la bombilla disminuye.<br><br><a href="https://files.adrianistan.eu/Bodacious-Borwo-Blorr-1.png"><img class="wp-image-1159 size-large" src="https://files.adrianistan.eu/Bodacious-Borwo-Blorr-1-1024x419.png" alt="" width="840" height="344" /></a> Cuando la corriente no atraviesa la resistencia<br><br><a href="https://files.adrianistan.eu/Bodacious-Borwo-Blorr-2.png"><img class="size-large wp-image-1160" src="https://files.adrianistan.eu/Bodacious-Borwo-Blorr-2-1024x419.png" alt="" width="840" height="344" /></a> Cuando la corriente atraviesa la resistencia<br><br>¿Qué ocurre exactamente? ¿Por qué la bombilla tiene menos intensidad en el segundo caso?<br><br>Vamos a aislar la resistencia. Ponemos un voltímetro y un amperímetro y vamos cambiando la tensión de entrada. Anotamos la corriente medida en cada caso.<br><br><a href="https://files.adrianistan.eu/Bodacious-Borwo-Blorr-3.png"><img class="aligncenter size-large wp-image-1161" src="https://files.adrianistan.eu/Bodacious-Borwo-Blorr-3-1024x419.png" alt="" width="840" height="344" /></a><br><pre class="lang:default decode:true" title="voltaje_intensidad.csv">voltaje,intensidad<br>5,1.01<br>6,1.19<br>7,1.4<br>8,1.62<br>9,1.81<br>10,2.01</pre><br>Podemos intentar hacer un ajuste lineal a estos datos. De modo, que una vez sepamos la intensidad, podamos predecir el voltaje.<br><pre class="lang:python decode:true">import pandas as pd<br>from scipy import stats as ss<br><br>df = pd.read_csv("voltaje_intensidad.csv")<br>pendiente, ordenada_origen, pearson, p, error = ss.linregress(df["intensidad"],df["voltaje"])<br><br>print(pearson) # 0.99969158942375369 es un valor que indica una muy fuerte correlación lineal<br></pre><br>Como Pearson nos da un número muy próximo a 1, podemos definir un <strong>modelo matemático</strong> siguiendo la <strong>regresión lineal</strong>.<br><br>Este modelo matemático se define así:<br><pre class="lang:python decode:true">def voltaje(intensidad):<br>    return intensidad*pendiente + ordenada_origen</pre><br>Y es lo que se conoce como la <strong>Ley de Ohm</strong>. En realidad, la ordenada en el origen tiene un valor muy cercano a cero, podemos quitarlo.<br><br>Así nos queda un modelo capaz de predecir lel voltaje en base a la intensidad y la pendiente de la recta. Ahora puedes probar cambiando la resistencia y observando que siempre ocurre lo mismo. Tenemos el <strong>voltaje</strong> por la <strong>pendiente</strong> del modelo. Este valor de la pendiente es lo que en física se llama <strong>resistencia</strong> y se mide en <strong>ohmios</strong>. Así se nos queda entonces la ley de Ohm que todos conocemos:<br><p style="text-align: center;">[latex]<br>V= IR<br>[/latex]</p><br>En este caso la pendiente equivalía a <strong>4.94 Ω</strong>, valor muy cercano a los <strong>5 Ω</strong> que dice el fabricante.]]></description>
                <comments>https://blog.adrianistan.eu/estadistica-python-analisis-datos-multidimensionales-regresion-lineal-parte-iv</comments>
                <pubDate>Wed, 15 Nov 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Crónica de la 62 SEMINCI</title>
                <link>https://blog.adrianistan.eu/cronica-la-62-seminci</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/cronica-la-62-seminci</guid>
                <description><![CDATA[Ya acabó la <strong>SEMINCI</strong>, la Semana Internacional de Cine de Valladolid. Este año he sido uno de los 5 miembros del jurado joven de la sección <strong>Punto de Encuentro</strong>. La sección reúne los primeros y segundos largometrajes de los directores.<br><br><iframe width="640" height="480" src="https://player.vimeo.com/video/14994952?title=0&amp;byline=0&amp;portrait=0" frameborder="0" webkitallowfullscreen="webkitallowfullscreen" mozallowfullscreen="mozallowfullscreen" allowfullscreen="allowfullscreen"></iframe><br><br>El sábado comenzamos el visionado de las películas en el <strong>Teatro Zorrilla</strong> con la película <strong>Paris la Blanche,</strong> que comentaré más tarde y con la gala inaugural en el <strong>Teatro Calderón</strong>.<br><br><iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/eraN9XNvJp4" frameborder="0" allowfullscreen="allowfullscreen"></iframe><br><br><a href="https://files.adrianistan.eu/DSC_0009.jpg"><img class="size-large wp-image-1126" src="https://files.adrianistan.eu/DSC_0009-1024x576.jpg" alt="" width="840" height="473" /></a> Fernando Colomo llega por la alfombra verde mientras hay un grupo que lucha por el pueblo palestino<br><br>Ese mismo día, ya por la noche, tocó <strong>Haporetzet</strong>. Al día siguiente vi 4 películas: <strong>The Nile Hilton Incident, La Librería, Adiós Entusiasmo</strong> y <strong>Aala Kaf Ifrit</strong>.<br><br><a href="https://files.adrianistan.eu/IMG-20171028-WA0009-e1510525700564.jpeg"><img class="size-large wp-image-1127" src="https://files.adrianistan.eu/IMG-20171028-WA0009-e1510525700564-576x1024.jpeg" alt="" width="576" height="1024" /></a> Mi acreditación<br><br>Los días prosiguieron viendo todos los días películas por la mañana y por la tarde, algunas eran entretenidas, otras te hacían replantear tu existencia. Me costó bastante conseguir mi acreditación, por un fallo que tuvieron con los códigos QR. Adicionalmente pedí entrada para ver una película islandesa aleatoria que resultó ser <strong>Eldjall</strong> de <em>Rúnar Rúnarsson </em>(Islandia ha sido el país invitado al festival) y acudí a <strong>La Noche del Corto Español</strong> y a la exposición de cortometrajes de la <strong>ECAM/ESCAC</strong>.<br><br><a href="https://files.adrianistan.eu/DSC_0021-e1510525843890.jpg"><img class="size-large wp-image-1128" src="https://files.adrianistan.eu/DSC_0021-e1510525843890-576x1024.jpg" alt="" width="576" height="1024" /></a> Teatro Zorrilla de Valladolid, aquí se proyectaban la mayoría de películas de Punto de Encuentro<br><br>El viernes nos reunimos para decidir cuál era la mejor película que habíamos visto. Fue una votación difícil, con triple empate en la primera ronda. Finalmente elegimos <strong>Aala Kaf Ifrit</strong> de <strong>Kaouther Ben Hania</strong>.<br><br><a href="https://files.adrianistan.eu/DSC_0005.jpg"><img class="size-large wp-image-1125" src="https://files.adrianistan.eu/DSC_0005-1024x576.jpg" alt="" width="840" height="473" /></a> Ray Loriga leyendo el palmarés de Sección Oficial<br><br>El último día acudí por la mañana a la lectura del palmarés. Quedé sorprendido por las decisiones de los otros jurados. Me hizo gracia que nada más acabar la lectura del palmarés, las azafatas empezasen a repartir revistas con todo el palmarés perfectamente explicado. La gala de clausura era por la tarde Originalmente no tenía entrada para la gala de clausura, pero 30 minutos antes de que empezara, me avisaron de que podía ir. A toda prisa me arreglé, para poder llegar a la alfombra verde y tomar asiento en el <strong>Teatro Calderón</strong>. Al finalizar la gala, se proyectó la película <strong>Sage Femme</strong>, que fue el broche cinéfilo.<br><br>Posteriormente no me dejaron entrar al cóctel pero sí pude acceder a la fiesta privada. Allí, entre algún famoso, bailé un poco. Y así acabó la SEMINCI.<br><br><a href="https://files.adrianistan.eu/DSC_0006.jpg"><img class="aligncenter size-large wp-image-1129" src="https://files.adrianistan.eu/DSC_0006-1024x576.jpg" alt="" width="840" height="473" /></a><br><h2>Películas de Punto de Encuentro</h2><br>Ahora viene lo divertido, hablar de las películas. ¡Empezamos!<br><br><a href="https://files.adrianistan.eu/AalaKafIfrit.jpg"><img class="size-full wp-image-1130 aligncenter" src="https://files.adrianistan.eu/AalaKafIfrit.jpg" alt="" width="600" height="400" /></a><strong>Aala Kaf Ifrit</strong>, <strong>ganadora del premio joven</strong>, se trata de una película muy dura sobre un caso real de una violación sita en Túnez. La historia, contada en 9 planos secuencia, genera una sensación de impotencia que jamás había experimentado. Son los propios policías los que han violado a la joven. La historia tiene mucha fuerza, contada sin morbo y donde se ve una evolución. El espectador va descubriendo poco a poco qué ha ocurrido exactamente. La actriz principal, Mariam Al Ferjani es impresionante. Como puntos negativos: el personaje que acompaña a la protagonista se ve demasiado plano. Los violadores tienen un aspecto ligeramente cómico al final de la cinta, lo que a algunas personas les provocó cierta confusión. Se trata de una película muy recomendable, pero hace falta estómago para verla.<br><br><a href="https://files.adrianistan.eu/Adios_entusiasmo-Punto-Encuentrook-Fichas.jpg"><img class="size-full wp-image-1131 aligncenter" src="https://files.adrianistan.eu/Adios_entusiasmo-Punto-Encuentrook-Fichas.jpg" alt="" width="600" height="400" /></a><strong>Adiós Entusiasmo</strong> me hizo perder el entusiasmo por la vida. La película trata sobre una madre que está encerrada en su habitación, mientras sus 3 hijas y su hijo viven con ella. Se trata de una película inconexa, ideal para buscarle los 3 pies al gato. Los planos, unos son claustrofóbicos y otros sin sentido. Hay personajes de los que no sabemos absolutamente nada, solo aparecen de repente en casa de la protagonista. La conclusión final de la película sorprendentemente no esclarece nada. <strong>Vladimir Durán</strong>, no me ha gustado tú película. Mencionar que fue la única película de la SEMINCI en la que estuve que no se llevó aplausos al final, sino patadas al suelo. Con esta película empecé a desarrollar cierto temor ante el cine argentino. Como veremos más adelante, este temor no estaba infundado.<br><br><a href="https://files.adrianistan.eu/Angels_Wear_Whitesportadacopy.jpg"><img class="aligncenter size-full wp-image-1132" src="https://files.adrianistan.eu/Angels_Wear_Whitesportadacopy.jpg" alt="" width="600" height="400" /></a><strong>Angels Wear White</strong> trata sobre una violación de unas menores. Se narran dos historias de forma paralela, por un lado las niñas, inconscientes de lo que ha ocurrido y que solo quieren volver a jugar y la limpiadora del hotel, testigo de lo sucedido pero que lucha por sobrevivir en China. En mi opinión es una película bastante normalita, los personajes se sienten bien y la historia atrapa. No obstante, las dos historias se alternan cada tanto tiempo que muchas veces perdemos el hilo de la otra historia.<br><br><a href="https://files.adrianistan.eu/arponptookok.jpg"><img class="aligncenter size-full wp-image-1133" src="https://files.adrianistan.eu/arponptookok.jpg" alt="" width="600" height="400" /></a><strong>Arpón</strong> es otra película argentina. ¡Vaya! Lo cierto es que de las tres películas argentinas que se presentaron esta es la más aceptable. Trata sobre un director de colegio aficionado a las prostitutas que descubre que tiene que hacerse cargo de Cata, una joven del instituto. La película tiene demasiados fallos: la relación entre Cata y el director no se aclara nunca, hay cosas importantes que se quedan sin resolver (¿cómo pueder ser que después de casi matar a un proxeneta, para el tío esto no sea una preocupación?) y la película en general te deja con ganas de más. ¿Es esto solo lo que querías contar? La película podía tener más recorrido con el escenario de partida. ¿Por qué parece que todas las cosas interesantes que podían ocurrir no pasan?<br><br><a href="https://files.adrianistan.eu/As-Duas-Irenes3portadacopy.jpg"><img class="aligncenter size-full wp-image-1134" src="https://files.adrianistan.eu/As-Duas-Irenes3portadacopy.jpg" alt="" width="600" height="400" /></a><strong>As Duas Irenes, premio del público,</strong> es una película brasileña, muy bien hecha, entretenida de ver, que te deja con buen sabor de boca. Se trata de dos hermanastras que no sabían que compartían padre y de como se hacen amigas. La protagonista es Irene, de clase alta, la hermana mediana, que además descubre gracias a su hermanastra que hay otro mundo fuera de lo que su familia considera normal. El argumento no es demasiado original, pero la película en general cumple. El final es un gran punto también para esta cinta.<br><br><a href="https://files.adrianistan.eu/Ayudameptook.jpg"><img class="aligncenter size-full wp-image-1135" src="https://files.adrianistan.eu/Ayudameptook.jpg" alt="" width="600" height="400" /></a><strong>Ayúdame a pasar la noche</strong> fue una de mis favoritas. He de ser sincero, cuando leí sobre la película tenía toda la pinta de ser una tragedia pura y dura. Nada más lejos de la realidad, se trata de un drama disfrazado de comedia. Divertida de ver, trata el tema de una familia al borde de la bancarrota por culpa de la madre, que ha desarrollado ludopatía. Los personajes masculinos, cada uno de diferente edad, afrontan de forma paralela la misma situación de forma diferente. El propio director vino a Valladolid, <strong>José Ramón Chávez Delgado</strong>, donde pudimos mantener una conversación. Película muy recomendable, con un final con un toque cómico muy logrado.<br><br><a href="https://files.adrianistan.eu/Haporetzet-Supernova-Fichas.jpg"><img class="aligncenter size-full wp-image-1136" src="https://files.adrianistan.eu/Haporetzet-Supernova-Fichas.jpg" alt="" width="600" height="400" /></a><strong>Haporetzet</strong> es sin duda una de las películas con un argumento más original de esta SEMINCI. Esta película alemana-israelí trata sobre la vida de Alex, cuya madre de repente desaparece. Para más inri, su casa sufre un robo, pero de ahí saca ideas para avanzar en el mundo. Muy buen trabajo de la actriz principal, <strong><span class="peliculaFichaTecnicaApartadoItem">Lihi</span></strong><span class="peliculaFichaTecnicaApartadoItem"><strong> Kornowski</strong>. Quizá el ritmo no es el más adecuado, pero personalmente fue una película muy interesante. Fue otra de mis favoritas.</span><br><br><a href="https://files.adrianistan.eu/HoyPartido-PuntoEncuentro-Fichas.jpg"><img class="aligncenter size-full wp-image-1137" src="https://files.adrianistan.eu/HoyPartido-PuntoEncuentro-Fichas.jpg" alt="" width="600" height="400" /></a><strong>Hoy partido a las 3</strong> es un despropósito. Se trata de una película en la que cuentas los minutos que quedan para irte. El argumento no tiene ningún sentido, no es más que un pretexto para que decenas de personajes interactúen entre ellos. No hay un personaje principal que hile la película. A ratos da la impresión de que estamos ante un documental grabado con un móvil. La película es soporífera y no aporta nada interesante al espectador. El peor cine argentino.<br><br><a href="https://files.adrianistan.eu/NapadidShodanDisappearancePTOENCUENTROFICHAS.jpg"><img class="size-full wp-image-1138" src="https://files.adrianistan.eu/NapadidShodanDisappearancePTOENCUENTROFICHAS.jpg" alt="" width="600" height="400" /></a><br><br><strong>Napadid Shodan</strong> es una película iraní que trata sobre una chica y su pareja y los problemas de acudir al servicio médico para asuntos sexuales sin estar casados. Se ve reflejada una Irán conservadora. La película es interesante pero no pasa de ahí. Los personajes no aportan mucho y el final me dejó un poco frío.<br><br><a href="https://files.adrianistan.eu/NEVERSTEADYPTOENCUENTROFICHAS.jpg"><img class="aligncenter size-full wp-image-1139" src="https://files.adrianistan.eu/NEVERSTEADYPTOENCUENTROFICHAS.jpg" alt="" width="600" height="400" /></a><strong>Never Steady, Never Still</strong> nos cuenta la historia de una mujer enferma de párkinson. Su marido, apoyo incondicional, sufre un infarto y muere. Su hijo está en ese momento de la vida en el que quiere volverse adulto, independizándose. Es una película dura, con excesivo énfasis en el sufrimiento de Judy, la protagonista. Además, la historia paralela del hijo no termina de encajarme del todo en esta historia. Se trata de una película demasiado larga para lo que es.<br><br><a href="https://files.adrianistan.eu/ParisLABLANCHEPTODEENCUENTROFICHAS.jpg"><img class="aligncenter size-full wp-image-1140" src="https://files.adrianistan.eu/ParisLABLANCHEPTODEENCUENTROFICHAS.jpg" alt="" width="600" height="400" /></a><strong>Paris La Blanche</strong> es una película que parte de una muy buena historia, pero no termina de encajar. Rekia, argelina, lleva sin ver a su marido 40 años. Este fue buscando trabajo y nunca volvió. Rekia, sigue los pasos de su marido y va a Francia, tratando de encontrarle. La película es demasiado lenta, los personajes secundarios no encajan muy bien (¿de dónde han sacado a esa gente?) y te hacen preguntarte qué propósito tiene que sean así. El personaje de Rekia es muy entrañable, pero el final me dejó frío.<br><br><a href="https://files.adrianistan.eu/spina-stillPTODEENCUENTROFICHAS.jpg"><img class="aligncenter size-full wp-image-1141" src="https://files.adrianistan.eu/spina-stillPTODEENCUENTROFICHAS.jpg" alt="" width="600" height="400" /></a><strong>Špína, premio Punto de Encuentro,</strong> es una película eslovaca que trata también sobre una violación. En este caso, la joven es violada por su propio profesor de matemáticas y entra en un estado de shock que hace que la internen en un psiquiátrico. Al contrario que <strong>Aala Kaf Ifrit</strong>, esta película es mucho más fría, menos pasional que la tunecina. El espectador espera que la joven cuente lo sucedido, en una sociedad que la va a apoyar, y el hecho de que no lo haga genera impotencia. Fue una sorpresa que está película ganase el premio del jurado no-joven, pues sobre el mismo tema de la violación creemos que <strong>Aala Kaf Ifrit</strong> es mucho mejor, aunque sí que es cierto que son dos maneras de manejar la situación totalmente distintas. Por otro lado en Spina el espectador sabe desde el principio todos los detalles, mientras que en la tunecina, el espectador descubre poco a poco qué ha ocurrido exactamente.<br><br><a href="https://files.adrianistan.eu/stebuklasptofichas.jpg"><img class="aligncenter size-full wp-image-1142" src="https://files.adrianistan.eu/stebuklasptofichas.jpg" alt="" width="600" height="400" /></a><strong>Stebuklas</strong> es una película lituana que nos narra la llegada de un americano al pueblo de sus ancestros. Allí, la protagonista regenta una granja de cerdos que ha visto días mejores. El pueblo en general está deprimido por la caída del comunismo y la crisis económica que ello ha provocado. Se trata de una película divertida a momentos, que explora los tópicos americanos (el americano tiene cierto parecido a Donald Trump) y amena. Con la mejor banda sonora de esta sección Punto de Encuentro. Su único y gran fallo es el final. Al principio la película promete mucho y es muy interesante, pero se desinfla al llegar al final. Demasiado. Un final decepcionante. Además, la película cuenta con unas referencias religiosas un tanto turbias, que podían sobrar. Fue una de mis favoritas.<br><br><a href="https://files.adrianistan.eu/Šventasisportadaptoencuentro.jpg"><img class="aligncenter size-full wp-image-1143" src="https://files.adrianistan.eu/Šventasisportadaptoencuentro.jpg" alt="" width="600" height="400" /></a><strong>Šventasis</strong>, es una película lituana que nos cuenta la historia de un mecánico que pierde su trabajo por la crisis y como intenta mejorar su vida. Sin embargo, la mala suerte hace que vaya perdiendo la esperanza. La película parte de algo interesante, comienza correctamente pero en un determinado punto vemos que la película no va a avanzar más. Es bastante predecible y no es muy interesante. Además, la trama del chico de YouTube, que podía aportar algo, se resuelve de la manera más estúpida posible, hasta tal punto de que no hubiese afectado nada al desarrollo de la película esa subtrama.<br><br>Con este resumen de las películas me despido.]]></description>
                <comments>https://blog.adrianistan.eu/cronica-la-62-seminci</comments>
                <pubDate>Sun, 12 Nov 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Estadística en Python: media, mediana, varianza, percentiles (Parte III)</title>
                <link>https://blog.adrianistan.eu/estadistica-python-media-mediana-varianza-percentiles-parte-iii</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/estadistica-python-media-mediana-varianza-percentiles-parte-iii</guid>
                <description><![CDATA[Siguiendo en nuestro camino de la <strong>estadística descriptiva</strong>, hoy vamos a ver como calcular ciertas medidas relativas a una variable.<br><br><a href="https://files.adrianistan.eu/pandas_logo.png"><img class="aligncenter size-full wp-image-1100" src="https://files.adrianistan.eu/pandas_logo.png" alt="" width="600" height="125"></a><br><h3>Fichero de ejemplo</h3><br><pre class="lang:default decode:true " title="notas.csv">alumno,nota<br>Araceli,9<br>Manuel,5<br>Pablo,7<br>Íñigo,4<br>Mario,3<br>Raúl,4<br>Verónica,6<br>Darío,10<br>Laura,4<br>Silvia,6<br>Eduardo,2<br>Susana,8<br>María,5</pre><br><h2>Medidas de centralización: media, mediana y moda</h2><br>La <strong>media aritmética</strong> se define como la suma de N elementos dividida entre N. Se trata una medida bastante conocida entre la gente, aunque tiene el inconveniente de que es muy susceptible a valores extremos.<br><br>La <strong>mediana</strong> es el valor que dentro del conjunto de datos es menor que el 50% de los datos y mayor que el 50% restante.<br><br>La <strong>moda</strong> es el valor más repetido (solo aplicable a variables discretas).<br><br>Para calcular estas medidas, simplemente seleccionamos la <strong>variable estadística</strong> del DataFrame y usamos los métodos <strong>mean, median</strong> y <strong>mode</strong> respectivamente.<br><br><em>Ejemplo: Calcula la media, la mediana y la moda de las notas de los alumnos en el examen</em><br><pre class="lang:python decode:true ">import pandas as pd<br><br>df = pd.read_csv("notas.csv")<br><br>media = df["nota"].mean()<br>mediana = df["nota"].median()<br>moda = df["nota"].mode()<br>print("""<br>    Media: %d<br>    Mediana: %d<br>    Moda: %d<br>""" % (media,mediana,moda))</pre><br><h2>Medidas de posición: cuartiles y percentiles</h2><br>El concepto es igual al de <strong>mediana</strong>, salvo que aquí la división ya no es en el 50%. El 25% de las observaciones es menor que el primer cuartil. Los cuartiles abarcan el 25%, 50% y 75% de las observaciones. Los percentiles son una generalización con cualquier porcentaje.<br><br><em>Ejemplo: ¿Cuál es la nota que tiene como mínimo el 10% más nota de la clase?</em><br><br>Este enunciado nos pide calcular el percentil 90.<br><br>Usamos <strong>quantile</strong> y el porcentaje.<br><pre class="lang:python decode:true ">p90 = df["nota"].quantile(0.9)</pre><br>El resultado es que el 10% con más nota de la clase ha sacado un 8,8 como mínimo. Mencionar que existen distintos tipos de interpolación para este cálculo. En la referencia podemos consultar cual nos conviene más.<br><h2>Medidas de dispersión: desviación típica, rango, IQR, coeficiente de variación</h2><br>La <strong>desviación típica</strong> mide la dispersión de los datos respecto a la media. Se trata de la raíz cuadrada de la varianza, que en sí misma no es una medida de dispersión. Para calcular la desviación típica usamos <strong>std</strong> y <strong>var</strong> para la varianza. (ddof=0 es necesario si quieres seguir la definición de desviación típica y varianza de algunas bibliografías, la razón es que hay un parámetro de ajuste que Pandas pone a 1, pero otras librerías ponen a 0). En Excel es la diferencia que hay entre DESVEST.M (ddof=1) y DESVEST.P (ddof=0).<br><pre class="lang:python decode:true">std = df["nota"].std(ddof=0)<br>var = df["nota"].var(ddof=0)<br>assert(np.sqrt(var) == std)</pre><br>El rango es la diferencia entre el máximo y el mínimo y el rango intercuartílico o <strong>IQR</strong> es la diferencia entre el tercer y el primer cuartil.<br><pre class="lang:python decode:true ">rango = df["nota"].max() - df["nota"].min()<br>iqr = df["nota"].quantile(0.75) - df["nota"].quantile(0.25)</pre><br>El <strong>coeficiente de variación</strong> es una medida que sirve para comparar entre dos muestras, cuál varía más y cuál es más estable. Es una simple división, de la desviación típica sobre la media, sin embargo, SciPy nos ofrece una función ya preparada.<br><pre class="lang:python decode:true ">import pandas as pd<br>import scipy.stats as ss<br><br>df = pd.read_csv("notas.csv")<br><br>cv = df["nota"].std(ddof=0) / df["nota"].mean()<br>cv2 = ss.variation(df["nota"])<br>assert(cv == cv2)</pre><br><h2>Medidas de asimetría</h2><br>Para saber si los datos estan repartidos de forma simétrica existen varios coeficientes: Pearson, Fisher, Bowley-Yule, etc<br><br>Para no liarnos demasiado, podemos usar la función <strong>skew</strong> de SciPy.<br><pre class="lang:default decode:true ">asimetria = ss.skew(df["nota"])</pre><br>Para valores cercanos a 0, la variable es simétrica. Si es positiva tiene cola a la derecha y si es negativa tiene cola a la izquierda.<br><br>Y con esto hemos visto los datos que se pueden extraer de una sola variable.]]></description>
                <comments>https://blog.adrianistan.eu/estadistica-python-media-mediana-varianza-percentiles-parte-iii</comments>
                <pubDate>Sat, 04 Nov 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>¿Cómo aumenté las visitas a mi blog?</title>
                <link>https://blog.adrianistan.eu/como-aumente-visitas-blog</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/como-aumente-visitas-blog</guid>
                <description><![CDATA[Cada vez más gente visita mi blog, es algo que me alegra y me llena de orgullo. Saber que algo a lo que le dedicas tu tiempo limitado sirve a otras personas es muy satisfactorio. Este año ha habido muchos más lectores y sobre todo, muchas más interacciones. Observa la gráfica:<br><br><a href="https://files.adrianistan.eu/Visitas20162017.png"><img class="aligncenter size-full wp-image-1116" src="https://files.adrianistan.eu/Visitas20162017.png" alt="" width="1011" height="228" /></a>El 2016 está en naranja y el 2017 en azul. Como se observa, el crecimiento es estable. ¿Qué pasos seguí para aumentar el número de visitas?<br><ul><br> 	<li><strong>Mantén el blog vivo</strong>: es importante ir realizando entradas de forma más o menos constante. No te digo que te marques un calendario (yo no lo tengo) pero sí saber cuando llevas demasiado tiempo sin darle amor al blog. <strong>Se visual</strong>, usa una imagen como mínimo en cada post y dentro de ellos recurre a imágenes si es posible.</li><br></ul><br><a href="https://files.adrianistan.eu/2000x2000-bigfailure-def-1800px.jpg"><img class="size-large wp-image-1117" src="https://files.adrianistan.eu/2000x2000-bigfailure-def-1800px-1024x749.jpg" alt="" width="840" height="614" /></a> En la constancia está la clave del éxito<br><ul><br> 	<li><strong>Configura una lista de correo</strong>: el correo electrónico es algo que todo el mundo tiene. Un usuario más avanzado es capaz de configurar RSS, pero todos van a poder suscribirse a la lista de correo. Además, el email es algo que la gente suele revisar con frecuencia.</li><br></ul><br>&nbsp;<br><ul><br> 	<li><strong>Conoce a tu audiencia</strong>: revisa los posts más populares y que más interacciones han generado. Para mí esto ha sido un dato muy valioso. Muchas veces para mí ha sido una sorpresa que ciertos posts tuvieran más éxito que otros. Aunque en mi caso es un blog personal y sigo haciendo lo que me da la gana la mayor parte del tiempo, sí que es cierto que muchas veces uso esta información para <strong>priorizar</strong> ciertos artículos. Para ayudarte en esta tarea existen los <a href="https://www.mdirector.com/crm-retargeting/data-management-platform-dmp.html">Data Management Platform</a>, como Oracle Bluekai y Adobe Audience Manager.</li><br></ul><br><a href="https://files.adrianistan.eu/Gonsalves_CommunityPortrait.jpg"><img class="size-full wp-image-1118" src="https://files.adrianistan.eu/Gonsalves_CommunityPortrait.jpg" alt="" width="398" height="498" /></a> Conoce a los usuarios y sus intereses<br><ul><br> 	<li><strong>Promociónate</strong>: este año he mejorado la presencia en Facebook, aunque no ha sido muy enriquecedor. Sin embargo, gracias a <strong>Telegram</strong> y los grupos he conseguido llegar a mucha gente. Para mí Telegram se ha convertido en un sitio ideal para promocionar mi blog. Para mí sorpresa también he recibido unas cuantas visitas desde Instagram, lo cuál es muy interesante, aunque el Instagram que tengo es más personal que otra cosa.</li><br></ul><br><a href="https://files.adrianistan.eu/GonsalvesWoodlandArena.jpg"><img class="size-full wp-image-1119" src="https://files.adrianistan.eu/GonsalvesWoodlandArena.jpg" alt="" width="709" height="478" /></a> Promociónate, muchas veces parece que nadie te ve pero en realidad estás consolidando y generando audiencia<br><ul><br> 	<li><strong>Hazle la vida fácil a tus lectores</strong>: la web tiene que ser elegante y bonita, los botones para compartir en redes sociales deben estar bien situados, pero no insistas excesivamente, pues genera mala imagen. Comprueba que la web cargue rápido y que se visualice correctamente en todo tipo de dispositivos. Estas cosas cuentan para el ránking de Google. La publicidad está bien, pero <strong>no tiene que ser intrusiva</strong>. Esto quiere decir que: nada de popups y tampoco anuncios con sonido.</li><br></ul><br><a href="https://files.adrianistan.eu/Gonsalves_TheLabyrinth.jpg"><img class="size-full wp-image-1120" src="https://files.adrianistan.eu/Gonsalves_TheLabyrinth.jpg" alt="" width="448" height="260" /></a> Que tu blog y un laberinto no tengan nada en común ayuda bastante<br><ul><br> 	<li><strong>Suerte</strong>: al fin y al cabo también necesitas algo de suerte. En mi caso este año publiqué el post de <a href="https://blog.adrianistan.eu/2017/04/19/novedades-de-c17/">Novedades en C++17</a> que fue portada en meneame. Eso genera muchísimas visitas de golpe. Varios conocidos se pusieron en contacto conmigo al reconocer un artículo de mi blog gozando de tal popularidad momentánea. A día de hoy, ese artículo sigue generando visitas todos los días, por encima de otros artículos.</li><br></ul><br><em>Los cuadros son de los pintores Rob Gonsalves y <span class="st">Stephan Schmitz</span></em><br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/como-aumente-visitas-blog</comments>
                <pubDate>Thu, 02 Nov 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Estadística en Python: manipulando datos en Pandas (Parte II)</title>
                <link>https://blog.adrianistan.eu/estadistica-python-manipulando-datos-pandas-parte-ii</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/estadistica-python-manipulando-datos-pandas-parte-ii</guid>
                <description><![CDATA[Antes de pasar a otros temas vamos a mencionar como podemos manipular los DataFrame en <a href="http://pandas.pydata.org/">Pandas</a>. Imaginemos que tenemos una tabla con datos de estatura y peso. Podemos generar una nueva columna con el índice de masa corporal. Veamos como se puede hacer<br><h3>Fichero de ejemplo</h3><br><pre class="lang:default decode:true" title="pesos.csv">nombre,peso,altura<br>Basilio,67,1.5<br>Arturo,80,1.7<br>Cristina,50,1.4<br>Alfonso,100,2.0<br>Nerea,70,1.8</pre><br><h2>Seleccionado datos</h2><br>A veces queremos quedarnos con parte de los datos que cumplen una condición. Hay varias maneras de hacerlo.<br><br><em>Ejemplo: Quédate con los datos de Nombre y Altura de los pacientes con peso igual o superior a 70</em><br><pre class="lang:python decode:true">import pandas as pd<br><br>df = pd.read_csv("pesos.csv")<br><br>tab = df.loc[df["peso"] &gt;= 70,["nombre","altura"]]</pre><br><pre class="lang:python decode:true">import pandas as pd<br><br>df = pd.read_csv("pesos.csv")<br><br>tab = df[df["peso"] &gt;= 70][["nombre","altura"]]</pre><br><pre class="lang:python decode:true">import pandas as pd<br><br>df = pd.read_csv("pesos.csv")<br><br>tab = df.query("peso &gt;= 70")[["nombre","altura"]]</pre><br>Cualquiera de estos tres métodos pueden usarse indistintamente.<br><br><a href="https://files.adrianistan.eu/DataFrameQuery-1.png"><img class="aligncenter size-full wp-image-1106" src="https://files.adrianistan.eu/DataFrameQuery-1.png" alt="" width="341" height="73" /></a><br><h2>Apply</h2><br>Apply es una función de DataFrame muy potente que permite aplicar una función a todos las columnas o a todas las filas.<br><br><em>Ejemplo: Calcule el IMC (Índice de Masa Corporal) con los valores de la tabla</em><br><pre class="lang:python decode:true ">import pandas as pd<br><br>df = pd.read_csv("pesos.csv")<br><br>def imc(x):<br>    return x["peso"]/(x["altura"]**2)<br><br>df["imc"] = df.apply(imc,axis=1)<br>print(df)</pre><br><a href="https://files.adrianistan.eu/DataFrameApply.png"><img class="aligncenter size-full wp-image-1103" src="https://files.adrianistan.eu/DataFrameApply.png" alt="" width="637" height="293" /></a><br><h2>Drop</h2><br>¿Qué pasa si queremos borrar algún dato o columna?<br><br>Si queremos borrar columnas:<br><pre class="lang:default decode:true ">df.drop(["peso"],axis=1)</pre><br>Si queremos borrar datos:<br><pre class="lang:python decode:true">df.drop(ELEMENTOS,inplace=True)<br><br># puede haber una condicion compleja<br>df.drop(df[df["altura"] &lt; 1.6].index,axis=0,inplace=True)</pre><br><h2>Construyendo el DataFrame a mano</h2><br>Normalmente los datos los leeremos de algún archivo o base de datos (<strong>read_csv</strong>, <strong>read_json, read_html, read_sql, read_hdf, read_msgpack, read_excel, read_pickle, read_gbq, read_parquet, </strong>...) pero puede darse el caso de que necesitemos ingresar los datos manualmente. El constructor de <strong>DataFrame</strong> admite diccionarios, arrays de NumPy y arrays de tuplas.<br><pre class="lang:python decode:true">import pandas as pd<br>import numpy as np<br><br>datos_dict = {"peso": [50,60], "altura": [1.6,1.7]}<br>df = pd.DataFrame(data=datos_dict)<br><br>datos_numpy = np.array([[50,1.6],[70,1.7]])<br>df = pd.DataFrame(data=datos_numpy,columns=("peso","altura"))<br><br>datos_tuple = [(50,1.6),(70,1.7)]<br>df = pd.DataFrame(data=datos_tuple,columns=("peso","altura"))</pre><br>&nbsp;<br><h2>Concatenar DataFrames</h2><br>Si tenemos varios DataFrames de características similares (columnas iguales) podemos unirlos. Hay que tener cuidado con los índices. Si el tema de los índices te da igual, usa <strong>ignore_index</strong>.<br><pre class="lang:python decode:true">import pandas as pd<br><br>df = pd.read_csv("pesos.csv")<br><br>datos1 = [("Emilio",78,1.6),("Rosa",80,1.8)]<br>df1 = pd.DataFrame(data=datos1,columns=("nombre","peso","altura"))<br><br>datos2 = [("Agustín",75,1.6),("Ana",90,1.8)]<br>df2 = pd.DataFrame(data=datos2,columns=("nombre","peso","altura"))<br><br>df = pd.concat([df,df1,df2],ignore_index=True)<br></pre><br><a href="https://files.adrianistan.eu/DataFrameConcat.png"><img class="aligncenter size-full wp-image-1107" src="https://files.adrianistan.eu/DataFrameConcat.png" alt="" width="500" height="206" /></a><br><h2>Join DataFrames</h2><br>Si vienes del mundo SQL quizá te suene el tema de los JOIN. En Pandas existe un potente sistema de <strong>join</strong>, similar al usado en las bases de datos SQL más importantes y con excelente rendimiento. Pandas soporta joins de tipo LEFT, RIGHT, OUTER e INNER.<br><pre class="lang:python decode:true">import pandas as pd<br><br>df = pd.read_csv("pesos.csv")<br><br>otros_datos = [("Nerea",19),("Irena",21)]<br>tab_edad = pd.DataFrame(data=otros_datos,columns=("nombre","edad"))<br><br>tab_right = pd.merge(df,tab_edad,on="nombre",how="right")<br>tab_left = pd.merge(df,tab_edad,on="nombre",how="left")<br>tab_inner = pd.merge(df,tab_edad,on="nombre",how="inner")<br>tab_outer = pd.merge(df,tab_edad,on="nombre",how="outer")</pre><br><a href="https://files.adrianistan.eu/DataFrameJoin.png"><img class="aligncenter size-full wp-image-1108" src="https://files.adrianistan.eu/DataFrameJoin.png" alt="" width="443" height="325" /></a>Con esto ya sabemos lo básico para manejarnos con DataFrames de Pandas<br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/estadistica-python-manipulando-datos-pandas-parte-ii</comments>
                <pubDate>Wed, 01 Nov 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Estadística en Python: Pandas, NumPy, SciPy (Parte I)</title>
                <link>https://blog.adrianistan.eu/estadistica-python-pandas-numpy-scipy-parte-i</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/estadistica-python-pandas-numpy-scipy-parte-i</guid>
                <description><![CDATA[Recientemente SciPy anunció que lanzaba la versión 1.0 tras 16 años en desarrollo. Con motivo de este acontecimiento voy a realizar una serie de tutoriales sobre estadística en Python. Quiero hacer especial énfasis en la resolución de problemas estadísticos, con problemas reales que vamos a ir resolviendo. Usaremos <a href="https://www.python.org/">Python 3</a>, <a href="http://www.numpy.org/">NumPy</a>, <a href="http://pandas.pydata.org/">Pandas</a>, <a href="https://www.scipy.org/">SciPy</a> y <a href="http://matplotlib.org/">Matplotlib</a> entre otras.<br><h2>¿Por qué Python para estadística?</h2><br>Python se ha convertido en uno de los lenguajes más usados en la comunidad de <em>data science</em>. Se trata de un lenguaje cómodo para los matemáticos, sobre el que es fácil iterar y cuenta con unas librerías muy maduras.<br><h2>Instalando el stack estadístico en Python</h2><br>Yo voy a usar Python 3. Tanto si estamos en Windows, macOS o Linux podemos instalar NumPy, SciPy, Pandas y Matplotlib con este simple comando<br><pre class="lang:default decode:true ">pip3 install numpy scipy matplotlib pandas --user</pre><br><h3>¿Para qué sirve cada cosa?</h3><br><strong>NumPy</strong> sirve para realizar cálculos numéricos con matrices de forma sencilla y eficiente y tanto SciPy como Pandas la usan de forma interna. NumPy es la base del stack científico de Python.<br><br><strong>SciPy</strong> es una colección de módulos dedicados a diversas áreas científicas. Nosotros usaremos principalmente el módulo <strong>stats</strong>.<br><br><strong>Pandas</strong> es una librería que permite manipular grandes conjuntos de datos en tablas con facilidad. Además permite importar y exportar esos datos.<br><br><strong>Matplotlib</strong> permite realizar gráficos y diagramas con facilidad, mostrarlos en pantalla o guardarlos a archivos.<br><h2>Estadística descriptiva</h2><br>Vamos a comenzar con la parte de la estadística que trata de darnos un resumen del conjunto de datos.<br><br>Para ello vamos a definir el concepto de <strong>variable estadística</strong> como la magnitud o cualidad de los individuos que queremos medir (estatura, calificaciones en el examen, dinero en la cuenta,...). Las variables pueden ser <strong>cualitativas</strong> o <strong>cuantitativas</strong> y dentro de las cuantitativas pueden ser <strong>continuas</strong> y <strong>discretas</strong>.<br><h3>Fichero de ejemplo</h3><br>En este post voy a usar este fichero para los ejemplos (guardalo como <strong>notas.csv</strong>).<br><pre class="lang:default decode:true" title="notas.csv">alumno,nota<br>Araceli,9<br>Manuel,5<br>Pablo,7<br>Íñigo,4<br>Mario,3<br>Raúl,4<br>Verónica,6<br>Darío,10<br>Laura,4<br>Silvia,6<br>Eduardo,2<br>Susana,8<br>María,5</pre><br><h2>Cargando datos</h2><br>Para cargar datos y manipular los datos usamos <strong>Pandas</strong>. Pandas permite cargar datos de distintos formatos: CSV, JSON, HTML, <a href="https://support.hdfgroup.org/HDF5/">HDF5</a>, SQL, <a href="https://msgpack.org/">msgpack</a>, Excel, ...<br><br>Para estos ejemplos usaremos CSV, valores separados por comas:<br><pre class="lang:python decode:true">import pandas as pd<br><br>df = pd.read_csv("notas.csv")</pre><br><strong>df</strong> es un objeto de tipo <strong>DataFrame</strong>. Es la base de Pandas y como veremos, se trata de un tipo de dato muy flexible y muy fácil de usar. Si ahora hacemos <strong>print</strong> a df obtenemos algo así:<br><br><a href="https://files.adrianistan.eu/PandasReadCSV.png"><img class="aligncenter size-full wp-image-1093" src="https://files.adrianistan.eu/PandasReadCSV.png" alt="" width="641" height="254" /></a><br><h2>Tabla de frecuencias</h2><br>Si tenemos una variable <strong>discreta</strong> o <strong>cualitativa</strong> una cosa habitual que se suele hacer es construir la tabla de frecuencias. Con ella sabemos cuantas veces se repite el valor de la variable. Para crear tablas de frecuencia usamos <strong>crosstab</strong>. En <strong>index</strong> indicamos la variable que queremos contar y en <strong>columns</strong> especificaos el nombre de la columna de salida. crosstab devuelve otro <strong>DataFrame</strong> independiente.<br><br><a href="https://files.adrianistan.eu/TablaFrecuencias.png"><img class="aligncenter size-full wp-image-1094" src="https://files.adrianistan.eu/TablaFrecuencias.png" alt="" width="126" height="132" /></a><br><br><em>Ejemplo: ¿Cuántos alumnos han sacado un 5 en el examen?</em><br><pre class="lang:python decode:true ">import pandas as pd<br><br># Leer datos<br>df = pd.read_csv("notas.csv")<br><br># Generar tabla de frecuencias<br>tab = pd.crosstab(index=df["nota"],columns="frecuencia")<br>print(tab)<br><br># Buscar el elemento 5 (el elemento que cumple la condición de que su índice es igual a 5)<br>fila = tab.loc[tab.index == 5]<br># Obtenemos el valor "frecuencia" de la fila<br>x = fila["frecuencia"]<br>x = int(x)<br>print("%d alumnos han sacado un 5" % x)<br></pre><br><em>Ejemplo: ¿Cuántos alumnos han aprobado (sacar 5 o más)?</em><br><pre class="lang:python decode:true ">import pandas as pd<br><br>df = pd.read_csv("notas.csv")<br><br>tab = pd.crosstab(index=df["nota"],columns="frecuencia")<br>print(tab)<br><br>x = tab.loc[tab.index &gt;= 5]["frecuencia"].sum()<br>x = int(x)<br>print("%d alumnos han aprobado el examen" % x)</pre><br>En estos ejemplo usamos <strong>loc</strong> para devolver las filas que cumplan la condición descrita entre corchetes. En el último ejemplo como el resultado son varias filas, nos quedamos con la parte de las frecuencias y sumamos todo, para así obtener el resultado final.<br><h2>Diagrama de sectores</h2><br>Una forma sencilla de visualizar datos que ya han sido pasados por la tabla de frecuencias es el <strong>diagrama de sectores</strong>. Usando <strong>Matplotlib</strong> podemos generar gráficos en los que podemos personalizar todo, pero cuyo uso básico es extremadamente simple.<br><pre class="lang:python decode:true ">import pandas as pd<br>import matplotlib.pyplot as plt<br><br>df = pd.read_csv("notas.csv")<br><br>tab = pd.crosstab(index=df["nota"],columns="frecuencia")<br><br>plt.pie(tab,labels=tab.index)<br>plt.xlabel("Notas del examen")<br>plt.savefig("notas.png")</pre><br><a href="https://files.adrianistan.eu/notas.png"><img class="wp-image-1095 size-full" src="https://files.adrianistan.eu/notas.png" alt="" width="640" height="480" /></a> Gráfica generada por Matplotlib<br><br><em>Ejemplo: Haz un diagrama de sectores donde se vea claramente el porcentaje de aprobados frente al de suspensos</em><br><pre class="lang:python decode:true ">import numpy as np<br>import pandas as pd<br>import matplotlib.pyplot as plt<br><br>df = pd.read_csv("notas.csv")<br><br>tab = pd.crosstab(index=df["nota"],columns="frecuencia")<br><br>aprobados = tab.loc[tab.index &gt;= 5]["frecuencia"].sum()<br>suspensos = tab.loc[tab.index &lt; 5]["frecuencia"].sum()<br><br>data = np.array([aprobados,suspensos])<br>plt.pie(data,labels=["Aprobados","Suspensos"],autopct="%1.1f%%")<br>plt.xlabel("Notas del examen")<br>plt.savefig("notas.png")</pre><br><a href="https://files.adrianistan.eu/notas-1.png"><img class="aligncenter size-full wp-image-1096" src="https://files.adrianistan.eu/notas-1.png" alt="" width="640" height="480" /></a>Destacar que aquí hemos usado <strong>np.array</strong> para crear un array de NumPy en vez de una lista nativa de Python.<br><h2>Diagrama de barras</h2><br>De forma similar es posible generar un diagrama de barras:<br><br><em>Ejemplo: Genere un diagrama de barras de las notas obtenidas por los alumnos en el examen</em><br><pre class="lang:python decode:true ">import pandas as pd<br>import matplotlib.pyplot as plt<br><br>df = pd.read_csv("notas.csv")<br><br>tab = pd.crosstab(index=df["nota"],columns="frecuencia")<br><br>plt.bar(tab.index,tab["frecuencia"])<br>plt.xlabel("Notas del examen")<br>plt.savefig("notas.png")</pre><br><a href="https://files.adrianistan.eu/DiagramaBarras.png"><img class="aligncenter size-full wp-image-1097" src="https://files.adrianistan.eu/DiagramaBarras.png" alt="" width="640" height="480" /></a><br><br><iframe width="300" height="150" style="width: 120px; height: 240px;" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" src="//rcm-eu.amazon-adsystem.com/e/cm?lt1=_blank&amp;bc1=000000&amp;IS2=1&amp;bg1=FFFFFF&amp;fc1=000000&amp;lc1=0000FF&amp;t=adrarrcal-21&amp;o=30&amp;p=8&amp;l=as4&amp;m=amazon&amp;f=ifr&amp;ref=as_ss_li_til&amp;asins=B075X4LT6K&amp;linkId=abf6e9ad3af7b28c31c5357525e2b08c"></iframe><br><br>&nbsp;<br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/estadistica-python-pandas-numpy-scipy-parte-i</comments>
                <pubDate>Tue, 31 Oct 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>¡Comienza la SEMINCI!</title>
                <link>https://blog.adrianistan.eu/comienza-la-seminci</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/comienza-la-seminci</guid>
                <description><![CDATA[Hoy da comienzo la SEMINCI, se trata del festival de cine de Valladolid. Es uno de los festivales más longevos de cine de España. Esta será la 62º edición del festival que empezó siendo de cine religioso y actualmente su lema es <em>Cine de autor</em>.<br><br><a href="https://files.adrianistan.eu/DSC_0001.jpg"><img class="aligncenter size-large wp-image-1088" src="https://files.adrianistan.eu/DSC_0001-1024x576.jpg" alt="" width="840" height="473" /></a>Este año, el festival, dentro del Premio de la Juventud, y más concretamente, en la sección Punto de Encuentro he tenido el honor de ser nombrado jurado. Para poder llevar a cabo mi tarea tendré que ver todas las películas de la sección Punto de Encuentro, aunque también tenemos derecho a ver las películas de sección oficial, así como asistir a la gala inaugural (pero no a la de clausura curiosamente).<br><br><a href="https://files.adrianistan.eu/DSC_0002.jpg"><img class="aligncenter size-large wp-image-1089" style="image-orientation: 90deg;" src="https://files.adrianistan.eu/DSC_0002-1024x576.jpg" alt="" width="840" height="473" /></a><br><br>Las películas que se van a proyectar en la SEMINCI dentro de la sección Punto de Encuentro son:<br><ul><br> 	<li>Napadid Shodan</li><br> 	<li>Paris La Blanche</li><br> 	<li>Aala Kaf Ifrit</li><br> 	<li>Haporetzet</li><br> 	<li>Angels Wear White</li><br> 	<li>Adiós Entusiasmo</li><br> 	<li>Špína</li><br> 	<li>Stebuklas</li><br> 	<li>Ayúdame a pasar la noche</li><br> 	<li>Never steady, never still</li><br> 	<li>Hoy partido a las 3</li><br> 	<li>Šventasis</li><br> 	<li>As duas Irenes</li><br> 	<li>Arpón</li><br></ul><br>Si estás por Valladolid estos días y vas a acurdir a alguna proyección de la SEMINCI, no dudes en contactar conmigo, seguro que estaré cerca. Podremos tomar unas cañas mientras debatimos asuntos de alta incurnia: ¿Tabs o espacios? ¿Ennio Morricone o Hans Zimmer?<br><br>Así mismo animo a todas las personas a venir a Valladolid a disfrutar de la SEMINCI. Os esperamos.]]></description>
                <comments>https://blog.adrianistan.eu/comienza-la-seminci</comments>
                <pubDate>Fri, 20 Oct 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Interfaces gráficas multiplataforma en C# con .NET Core y Avalonia</title>
                <link>https://blog.adrianistan.eu/interfaces-graficas-multiplataformas-c-net-core-avalonia</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/interfaces-graficas-multiplataformas-c-net-core-avalonia</guid>
                <description><![CDATA[Microsoft sorprendió a todos con la publicación de .NET Core el 27 de junio de 2016. Por primera vez, la plaatforma <a href="http://dot.net">.NET</a> se volvía multiplataforma, con soporte a macOS y GNU/Linux. Y además se volvía software libre, con licencia MIT y con un desarrollo transparente donde cualquiera puede proponer mejoras, subir parches, etc...<br><br>Esto posibilita tener soporte de primer nivel para uno de los lenguajes mejor diseñados actualmente, C#, que hasta entonces tenía un soporte de segunda en Linux, a través de <a href="http://www.mono-project.com/">Mono</a>.<br><br>Inicialmente el soporte de Microsoft a .NET Core abarca: aplicaciones de consola, aplicaciones web ASP.NET y UWP. Mucha gente se desanimó por no tener WinForms o WPF en .NET Core. Sin embargo eso no quiera decir que no se puedan hacer interfaces gráficas en .NET Core. Aquí os presento la librería Avalonia, que aunque está en beta todavía, funciona sorprendentemente bien y en un futuro dará mucho de que hablar.<br><br><a href="https://github.com/AvaloniaUI/Avalonia">Avalonia</a> funciona en Windows, macOS, GNU/Linux, Android e iOS. Usa XAML para las interfaces y su diseño se asemeja a WPF, aunque se han hecho cambios aprovechando características más potentes de C#. Avalonia no es WPF multiplataforma, es mejor.<br><h2>Creando un proyecto</h2><br>En primer lugar, tenemos que instalar .NET Core 2.0. Se descarga en la página oficial. A continuación comprobamos que se ha instalado correctamente con:<br><pre class="lang:default decode:true ">dotnet --version</pre><br>Creamos ahora un nuevo proyecto de tipo consola con <strong>new</strong><br><pre class="lang:default decode:true ">dotnet new console -o GitHubRepos</pre><br>Si todo va bien nos saldrá algo parecido a esto:<br><br><a href="https://files.adrianistan.eu/DotNetNew.png"><img class="aligncenter size-full wp-image-1075" src="https://files.adrianistan.eu/DotNetNew.png" alt="" width="839" height="159" /></a>Se nos habrán generado una carpeta obj y dos archivos: <strong>Program.cs</strong> y <strong>GitHubRepos.csproj</strong>. El primero es el punto de entrada de nuestro programa, el otro es el fichero de proyecto de C#.<br><br>Vamos a probar que todo está en orden compilando el proyecto.<br><pre class="lang:default decode:true">dotnet run</pre><br><h2><a href="https://files.adrianistan.eu/DotNetRun.png"><img class="aligncenter size-full wp-image-1076" src="https://files.adrianistan.eu/DotNetRun.png" alt="" width="461" height="44" /></a>Añadiendo Avalonia</h2><br>Vamos a añadir ahora Avalonia. Instalar dependencias antes en C# era algo complicado. Ahora es muy sencillo, gracias a la integración de <strong>dotnet</strong> con <strong>NuGet</strong>.<br><pre class="lang:default decode:true ">dotnet add package Avalonia<br>dotnet add package Avalonia.Win32<br>dotnet add package Avalonia.Skia.Desktop<br>dotnet add package Avalonia.Gtk3</pre><br><a href="https://files.adrianistan.eu/DotNetAdd.png"><img class="aligncenter size-full wp-image-1077" src="https://files.adrianistan.eu/DotNetAdd.png" alt="" width="842" height="284" /></a>¡Ya no tenemos que hacer nada más! Nuestra aplicación será compatible ya con Windows y GNU/Linux. Para el resto de plataformas, hay más paquetes disponibles en NuGet, sin embargo desconozco su grado de funcionamiento. Solo he probado la librería en Windows 7, Windows 10, Ubuntu y Debian.<br><h2>Program.cs</h2><br>Program.cs define el punto de entrada a la aplicación. Aquí en una aplicación Avalonia podemos dejarlo tan simple como esto:<br><pre class="lang:c# decode:true" title="Program.cs">using System;<br>using Avalonia;<br><br>namespace GitHubRepos<br>{<br>    class Program<br>    {<br>        static void Main(string[] args)<br>        {<br>            AppBuilder<br>                .Configure&lt;App&gt;()<br>                .UsePlatformDetect()<br>                .Start&lt;MainWindow&gt;();<br>        }<br>    }<br>}</pre><br>Básicamente viene a decir que arranque una aplicación Avalonia definida en la clase <strong>App</strong> con la ventana <strong>MainWindow</strong>. A continuación vamos a definir esas clases.<br><h2>App.cs y App.xaml</h2><br>En Avalonia tenemos que definir una clase que represente a la aplicación, normalmente la llamaremos App. Crea un fichero llamado <strong>App.cs</strong> en la misma carpeta de <strong>Program.cs</strong>, con un contenido tan simple como este:<br><pre class="lang:c# decode:true" title="App.cs">using Avalonia;<br>using Avalonia.Markup.Xaml;<br><br>namespace GitHubRepos<br>{<br>    public class App : Application<br>    {<br>        public override void Initialize()<br>        {<br>            AvaloniaXamlLoader.Load(this);<br>        }<br>    }<br>}</pre><br>Lo que hacemos aquí es pedir al intérprete de XAML que lea App.xaml simplemente y por lo demás, es una mera clase hija de <strong>Application</strong>.<br><br>El fichero <strong>App.xaml</strong> contiene definiciones XAML que se aplican a todo el programa, como el estilo visual:<br><pre class="lang:default decode:true " title="App.xaml">&lt;Application xmlns="https://github.com/avaloniaui"&gt;<br>  &lt;Application.Styles&gt;<br>    &lt;StyleInclude Source="resm:Avalonia.Themes.Default.DefaultTheme.xaml?assembly=Avalonia.Themes.Default"/&gt;<br>    &lt;StyleInclude Source="resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default"/&gt;<br>  &lt;/Application.Styles&gt;<br>&lt;/Application&gt;</pre><br><h2>MainWindow.cs y MainWindow.xaml</h2><br>Ahora nos toca definir una clase para la ventana principal de la aplicación. El código para empezar es extremadamente simple también:<br><pre class="lang:c# decode:true " title="MainWindow.cs">using Avalonia;<br>using Avalonia.Controls;<br>using Avalonia.Markup.Xaml;<br><br>namespace GitHubRepos<br>{<br>    public class MainWindow : Window<br>    {<br>        public MainWindow()<br>        {<br>            Initialize();<br>        }<br>        private void Initialize()<br>        {<br>            AvaloniaXamlLoader.Load(this);<br>        }<br>    }<br>}</pre><br>Aquí hacemos lo mismo que con App.cs, mandamos cargar el fichero XAML. Este fichero XAML contiene los widgets que va a llevar la ventana. Parecido a HTML, <a href="https://es.wikipedia.org/wiki/QML">QML</a> de Qt, <a href="https://glade.gnome.org/">Glade</a> de GTK o <a href="https://en.wikipedia.org/wiki/FXML">FXML</a> de Java o <a href="https://es.wikipedia.org/wiki/XML-based_User-interface_Language">XUL</a> de Mozilla.<br><pre class="lang:default decode:true" title="MainWindow.xaml">&lt;Window xmlns="https://github.com/avaloniaui"<br>        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"<br>        Title="GitHub Repositories"&gt;<br>        &lt;StackPanel HorizontalAlignment="Center"&gt;<br>                &lt;Button Content="¡Hola mundo!"&gt;&lt;/Button&gt;<br>        &lt;/StackPanel&gt;<br>&lt;/Window&gt;</pre><br><h2>GitHubRepos.csproj</h2><br>Antes de poder compilar es necesario modificar el fichero de proyecto para incluir los ficheros XAML en el binario. Se trata de añadir un nuevo <strong>ItemGroup</strong> y dentro de él un <strong>EmbeddedResource</strong>.<br><pre class="lang:c# decode:true " title="GitHubRepos.csproj">&lt;Project Sdk="Microsoft.NET.Sdk"&gt;<br>  &lt;PropertyGroup&gt;<br>    &lt;OutputType&gt;Exe&lt;/OutputType&gt;<br>    &lt;TargetFramework&gt;netcoreapp2.0&lt;/TargetFramework&gt;<br>  &lt;/PropertyGroup&gt;<br>  &lt;ItemGroup&gt;<br>    &lt;PackageReference Include="Avalonia" Version="0.5.1" /&gt;<br>    &lt;PackageReference Include="Avalonia.Skia.Desktop" Version="0.5.1" /&gt;<br>    &lt;PackageReference Include="Avalonia.Win32" Version="0.5.1" /&gt;<br>  &lt;/ItemGroup&gt;<br>  &lt;!-- HE AÑADIDO ESTO --&gt;<br>  &lt;ItemGroup&gt;<br>    &lt;EmbeddedResource Include="**\*.xaml"&gt;<br>      &lt;SubType&gt;Designer&lt;/SubType&gt;<br>    &lt;/EmbeddedResource&gt;<br>  &lt;/ItemGroup&gt;<br>  &lt;!-- HASTA AQUÍ --&gt;<br>&lt;/Project&gt;</pre><br>Ahora ya podemos compilar con <strong>dotnet run</strong>.<br><br>Nos deberá salir algo como esto:<br><h2><a href="https://files.adrianistan.eu/Captura-de-pantalla-26.png"><img class="aligncenter size-large wp-image-1078" src="https://files.adrianistan.eu/Captura-de-pantalla-26-1024x576.png" alt="" width="840" height="473" /></a>Añadiendo clicks</h2><br>Vamos a darle vidilla a la aplicación añadiendo eventos. Para ello primero hay que darle un nombre al botón, un ID. Usamos <strong>Name</strong> en XAML. También usaremos un <strong>TextBlock</strong> para representar texto.<br><pre class="lang:c# decode:true" title="MainWindow.xaml">&lt;Window xmlns="https://github.com/avaloniaui"<br>        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"<br>        Title="GitHub Repositories"&gt;<br>        &lt;StackPanel HorizontalAlignment="Center"&gt;<br>                &lt;Button Name="lanzar" Content="Lanzar dado"&gt;&lt;/Button&gt;<br>                &lt;TextBlock Name="resultado"&gt;No has lanzado el dado todavía&lt;/TextBlock&gt;<br>        &lt;/StackPanel&gt;<br>&lt;/Window&gt;</pre><br>Ahora en <strong>MainWindow.cs</strong>, en el constructor, podemos obtener referencia a los objetos XAML con <strong>Find</strong>.<br><pre class="lang:c# decode:true " title="MainWindow.cs">using Avalonia;<br>using Avalonia.Controls;<br>using Avalonia.Interactivity;<br>using Avalonia.Markup.Xaml;<br>using System;<br><br>namespace GitHubRepos<br>{<br>    public class MainWindow : Window<br>    {<br>        Button lanzar;<br>        TextBlock resultado;<br>        public MainWindow()<br>        {<br>            Initialize();<br><br>            lanzar = this.Find&lt;Button&gt;("lanzar");<br>            lanzar.Click += LanzarDado;<br><br>            resultado = this.Find&lt;TextBlock&gt;("resultado");<br>        }<br>        private void Initialize()<br>        {<br>            AvaloniaXamlLoader.Load(this);<br>        }<br><br>        private void LanzarDado(object sender, RoutedEventArgs e)<br>        {<br>            var r = new Random().Next(0,6) + 1;<br>            resultado.Text = $"Dado: {r}";   <br>        }<br>    }<br>}</pre><br>Y ya estaría. También comentar que la función <strong>LanzarDado</strong> puede ser <strong>async</strong> si nos conviene. A partir de ahora ya puedes sumergirte en el código de Avalonia (¡porque documentación todavía no hay!) y experimentar por tu cuenta.<br><h2><a href="https://files.adrianistan.eu/Captura-de-pantalla-27.png"><img class="aligncenter size-large wp-image-1079" src="https://files.adrianistan.eu/Captura-de-pantalla-27-1024x576.png" alt="" width="840" height="473" /></a>Un ejemplo real, GitHubRepos</h2><br>Ahora os voy a enseñar un ejemplo real de la comodidad que supone usar Avalononia con .NET Core. He aquí un pequeño programa que obtiene la lista de repositorios de un usuario y los muestra por pantalla.<br><pre class="lang:c# decode:true " title="MainWindow.cs">using Avalonia;<br>using Avalonia.Controls;<br>using Avalonia.Interactivity;<br>using Avalonia.Markup.Xaml;<br>using System;<br>using System.Net.Http;<br>using System.Net.Http.Headers;<br>using System.Collections.Generic;<br>using System.Runtime.Serialization.Json;<br>using System.Linq;<br><br>namespace GitHubRepos<br>{<br>    public class MainWindow : Window<br>    {<br>        Button refresh;<br>        TextBox username;<br>        TextBlock status;<br>        ListBox repos;<br>        public MainWindow()<br>        {<br>            Initialize();<br><br>            refresh = this.Find&lt;Button&gt;("refresh");<br>            refresh.Click += RefreshList;<br><br>            username = this.Find&lt;TextBox&gt;("username");<br>            status = this.Find&lt;TextBlock&gt;("status");<br>            repos = this.Find&lt;ListBox&gt;("repos");<br>        }<br>        private void Initialize()<br>        {<br>            AvaloniaXamlLoader.Load(this);<br>        }<br><br>        private async void RefreshList(object sender, RoutedEventArgs e)<br>        {<br>            var user = username.Text;<br>            status.Text = $"Obteniendo repositorios de {user}";<br>            var client = new HttpClient();<br>            client.DefaultRequestHeaders.Accept.Clear();<br>            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/vnd.github.v3+json"));<br>            client.DefaultRequestHeaders.Add("User-Agent", "GitHubRepos - Avalonia");<br>            try{<br>                var downloadTask = client.GetStreamAsync($"https://api.github.com/users/{user}/repos");<br><br>                var serializer = new DataContractJsonSerializer(typeof(List&lt;GitHubRepo&gt;));<br>                var repoList = serializer.ReadObject(await downloadTask) as List&lt;GitHubRepo&gt;;<br><br>                repos.Items = repoList.OrderByDescending(t =&gt; t.Stars).Select(repo =&gt; {<br>                    var item = new ListBoxItem();<br>                    item.Content=$"{repo.Name} - {repo.Language} - {repo.Stars}";<br>                    return item;<br>                });<br>                status.Text = $"Repositorios de {user} cargados";<br>            }catch(HttpRequestException){<br>                status.Text = "Hubo un error en la petición HTTP";<br>            }catch(System.Runtime.Serialization.SerializationException){<br>                status.Text = "El fichero JSON es incorrecto";<br>            }<br>        }<br>    }<br>}</pre><br>Y este es su correspondiente XAML:<br><pre class="lang:default decode:true" title="MainWindow.xaml">&lt;Window xmlns="https://github.com/avaloniaui"<br>        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"<br>        Title="GitHub Repositories"<br>        Width="300"<br>        Height="500"&gt;<br>        &lt;Grid&gt;<br>                &lt;Grid.RowDefinitions&gt;<br>                        &lt;RowDefinition Height="50"/&gt;<br>                        &lt;RowDefinition /&gt;<br>                &lt;/Grid.RowDefinitions&gt;<br>        <br>                &lt;StackPanel Grid.Row="0" HorizontalAlignment="Center"&gt;<br>                        &lt;StackPanel Orientation="Horizontal"&gt;<br>                                &lt;TextBox Name="username"&gt;aarroyoc&lt;/TextBox&gt;<br>                                &lt;Button Name="refresh" Content="Actualizar"&gt;&lt;/Button&gt;<br>                        &lt;/StackPanel&gt;<br>                        &lt;TextBlock Name="status"&gt;&lt;/TextBlock&gt;<br>                &lt;/StackPanel&gt;<br>                &lt;ListBox Grid.Row="1" ScrollViewer.VerticalScrollBarVisibility="Visible" SelectionMode="Single" Name="repos"&gt;&lt;/ListBox&gt;<br>        &lt;/Grid&gt;<br>&lt;/Window&gt;</pre><br>Adicionalmente, he usado una clase extra para serializar el JSON.<br><pre class="lang:c# decode:true " title="GitHubRepo.cs">using System.Runtime.Serialization;<br><br>namespace GitHubRepos{<br>    [DataContract(Name="repo")]<br>    public class GitHubRepo{<br><br>        [DataMember(Name="name")]<br>        public string Name;<br><br>        [DataMember(Name="language")]<br>        public string Language;<br>        [DataMember(Name="stargazers_count")]<br>        public int Stars;<br>    }<br>}</pre><br>El resultado es bastante satisfactorio:<br><br><a href="https://files.adrianistan.eu/GitHubRepos.png"><img class="aligncenter size-full wp-image-1080" src="https://files.adrianistan.eu/GitHubRepos.png" alt="" width="310" height="546" /></a><br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/interfaces-graficas-multiplataformas-c-net-core-avalonia</comments>
                <pubDate>Fri, 13 Oct 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Diversión con punteros en Rust: bloques unsafe</title>
                <link>https://blog.adrianistan.eu/diversion-punteros-rust-bloques-unsafe</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/diversion-punteros-rust-bloques-unsafe</guid>
                <description><![CDATA[Hola, soy Adrián Arroyo y bienvenidos a un nuevo episodio de <em>Diversión con Punteros</em>.<br><br><a href="https://files.adrianistan.eu/FunWithPointers.jpg"><img class="aligncenter size-large wp-image-1057" src="https://files.adrianistan.eu/FunWithPointers-1024x576.jpg" alt="" width="840" height="473" /></a>Hoy vamos a hablar de un tema apasionante. Los bloques <strong>unsafe</strong> de Rust así como de los <strong>raw pointers</strong>. ¿Has programado en C? Si es así, los <strong>raw pointers</strong> de Rust son exactamente iguales a los punteros de C. Si no sabes lo que es un puntero, te lo explico.<br><h2>¿Qué es un puntero?</h2><br>Un puntero es un tipo de variable que en vez de almacenar el dato, almacena la posición en memoria donde se encuentra el dato.<br><br>En lenguajes en lo que todo es un objeto (como Python), nunca trabajamos con los datos reales, sino siempre con punteros, pero el lenguaje lo gestiona de forma automática. En lenguajes más cercanos al metal por contra sí que suele dejarse esta opción.<br><br><a href="https://files.adrianistan.eu/Puntero.png"><img class="wp-image-1058 size-full" src="https://files.adrianistan.eu/Puntero.png" alt="" width="400" height="200" /></a> Nuestro puntero es la variable que contiene 0x00ffbea0 y que apunta a la dirección de memoria donde se encuentra el dato<br><br>Rust tiene distintos tipos de punteros: <strong>Box, Rc, Arc, Vec, </strong>... Estos punteros son transparentes al usuario y muchas veces no tenemos que preocuparnos de su funcionamiento. Sin embargo, muchas veces queremos tener un control más fino del ordenador. Esto lo lograremos con los <strong>raw pointers</strong>. Se trata de punteros con los que podemos operar y desreferenciar.<br><br>Crear <strong>raw pointers</strong> no supone ningún problema, pero acceder al valor al que apuntan en memoria sí. Podría darse el caso de que no existiera valor alguno o hubiese sido modificado. En los punteros normales, el compilador de Rust se encarga de que no ocurra, pero en los raw pointers el compilador no lo puede saber. Es por ello, que para acceder al valor de un <strong>raw_pointer</strong> necesitas usar bloques de código <strong>unsafe</strong>, código inseguro en Rust.<br><h2>Creando un raw pointer</h2><br>Lo primero que hay que saber es que existen dos tipos de raw pointers en Rust, los mutables y los inmutables.<br><br>Los punteros inmutables tienen el tipo <strong>*const T</strong> y los mutables el tipo <strong>*mut T</strong>.<br><pre class="lang:rust decode:true">fn main(){ <br>    let numero = 5; <br>    let puntero = &amp;numero as *const i32; <br>    println!("Address: 0x{:x}", puntero as usize); <br>    println!("Value: {}",numero); <br>}</pre><br>&nbsp;<br><br>En este ejemplo, creamos una variable con valor 5 y le creamos un puntero, que contiene la dirección de memoria donde está el dato. Para representar la dirección de memoria se suele usar la notación hexadecimal. Antes debemos hacer un cast a <strong>usize</strong>. <strong>usize</strong> es un tipo en Rust cuyo tamaño depende de la máquina en cuestión (32 bits en máquinas de 32 bits, 64 bits en máquinas de 64 bits), siendo usado para representar direcciones de memoria, puesto que tiene el tamaño exacto para almacenarlas.<br><br>Hasta ahora no hemos usado <strong>unsafe</strong>. Esto es porque no hemos probado a acceder al valor. Para acceder a un valor, o <strong>deferrenciar</strong>, usamos el operador <strong>*</strong>.<br><br><pre class="lang:rust decode:true"><br>fn main(){<br>    let numero = 5;<br>    let puntero = &amp;numero as *const i32;<br>    println!("Address: 0x{:x}", puntero as usize);<br>    println!("Value: {}",numero);<br>    unsafe{<br>        println!("Value: {}",*puntero);<br>    }<br>}<br></pre><br><br>Ambos prints imprimen 5. Hasta aquí no hemos hecho nada interesante con punteros. Todo esto era más fácil hacerlo sin punteros. Veamos alguna aplicación práctica de los punteros.<br><h2>Modificar datos sin control</h2><br>Si te pongo este código, ¿me puedes decir que salida dará?<br><br><pre class="lang:rust decode:true"><br>fn main(){<br>    let mut numero = 5;<br>    let puntero = &amp;mut numero as *mut i32;<br>    println!("Address: 0x{:x}", puntero as usize);<br>    unsafe{<br>        scary_things(puntero);<br>    }<br>    println!("Value: {}",numero);<br>}<br></pre><br><br>Uno podría pensar que como en ningún sitio reasignamos numero, y numero es una variable de tipo <strong>i32</strong>, que implementa <strong>Copy</strong>, es imposible modificarle el valor. Y eso es correcto en las reglas de Rust normales, pero en unsafe, podemos pasar el puntero hacia otras funciones (los punteros también son <strong>Copy</strong>, ocupan el tamaño de un <strong>usize</strong>). Y esas funciones pueden modificar los datos en memoria a su antojo. Así, pues, la respuesta correcta es indeterminado. Hacer esto es una mala práctica, pero en ocasiones se puede ganar rendimiento o interactuar con una librería de C usando estos métodos.<br><br><pre class="lang:rust decode:true"><br>unsafe fn scary_things(p: *mut i32) {<br>    *p = 12;<br>}<br><br>fn main(){<br>    let mut numero = 5;<br>    let puntero = &amp;mut numero as *mut i32;<br>    println!("Address: 0x{:x}", puntero as usize);<br>    unsafe{<br>        scary_things(puntero);<br>    }<br>    println!("Value: {}",numero);<br>}<br></pre><br><br>Esta sería la versión completa del programa.<br><h2>Aritmética de punteros</h2><br>Una vez tenemos acceso a memoria podemos acceder a <strong>cualquier</strong> parte de memoria (en sistemas operativos modernos, memoria que esté asignada a nuestro programa). En C simplemente podíamos operar con el puntero como si fuese un número, con sumas, restas, multiplicaciones y divisiones. Estas operaciones eran un poco traicioneras porque eran relativas a la máquina. Sumar 1 a un puntero de int equivalía en realidad a sumar 4 al puntero en una máquina de 32 bits. En Rust esto no se permite, pero a cambio tenemos métodos que nos permiten hacer lo mismo. El más importante es <strong>offset</strong>. El offset nos permite desplazarnos por la memoria hacia delante y hacia atrás.<br><br><pre class="lang:rust decode:true"><br>fn main(){<br>    let mut numero = 5;<br>    let b = 35;<br>    let c = 42;<br>    let puntero = &amp;mut numero as *mut i32;<br>    println!("Address: 0x{:x}", puntero as usize);<br>    unsafe{<br>        *puntero.offset(1) = 120;<br>    }<br>    println!("Value: {}",numero);<br>    println!("Value: {}",b);<br>    println!("Value: {}",c);<br>}<br></pre><br><br>Este programa parte de una suposición para funcionar. Y es que numero, b y c están contiguos en memoria y en el <strong>mismo</strong><strong> orden</strong> que como los que he declarado. En el puntero tenemos la dirección a numero, es decir, a 5. Sin embargo, si avanzamos en la memoria una posición llegaremos a al 35, y si avanzamos dos, llegamos a 42. Entonces podemos editar el contenido de esa memoria. Al acabar el programa b vale 120. Hemos modificado el valor y ni siquiera b se había declarado como <strong>mut</strong>. Esto os recuerdo, usadlo solo en casos excepcionales.<br><h2><a href="https://files.adrianistan.eu/UnsafeWorldModificarB.png"><img class="aligncenter size-full wp-image-1059" src="https://files.adrianistan.eu/UnsafeWorldModificarB.png" alt="" width="577" height="117" /></a>Reservar memoria al estilo C</h2><br>Estas cosas empiezan a tener utilidad en cuanto podemos usar memoria dinámica al estilo C, es decir, con <strong>malloc</strong>, <strong>free</strong>, <strong>calloc</strong> y compañía. El equivalente a <strong>malloc</strong> en Rust suele ser <strong>Box</strong> o <strong>Vec </strong>y es lo que debemos usar. Box sabe que espacio en memoria tiene que reservar de antemano y Vec ya está preparado para ir creciendo de forma segura.<br><br><pre class="lang:rust decode:true"><br>extern crate libc;<br><br>use libc::{malloc,free};<br>use std::mem::size_of;<br><br>fn main(){<br>    unsafe {<br>        let puntero = malloc(10*size_of::&lt;i32&gt;()) as *mut i32;<br>        for i in 0..10 {<br>            *puntero.offset(i) = 42;<br>        }<br>        for i in 0..10{<br>            println!("{}",*puntero.offset(i));<br>        }<br>        free(puntero as *mut libc::c_void);<br>    }<br>}<br></pre><br><br>En este caso usamos <strong>malloc</strong> como en C para generar un array de forma dinámica con espacio suficiente para almacenar 10 elementos de tamaño <strong>i32</strong>.<br><br>Con esto ya hemos visto el lado oscuro de Rust, la parte <strong>unsafe</strong>. No hemos visto como llamar a funciones de C directamente, algo que también require usar bloques <strong>unsafe</strong>.<br><br>Como vemos, Rust no nos limita a la hora de hacer cualquier cosa que queramos, solo que nos reduce a los bloques unsafe, para que nosotros mismos tengamos mejor control de lo que hagamos.]]></description>
                <comments>https://blog.adrianistan.eu/diversion-punteros-rust-bloques-unsafe</comments>
                <pubDate>Thu, 12 Oct 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Cheatsheet de contenedores de Rust</title>
                <link>https://blog.adrianistan.eu/cheatsheet-contenedores-rust</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/cheatsheet-contenedores-rust</guid>
                <description><![CDATA[Raph Levien de Google ha publicado la siguiente infografía o <em>cheatsheet</em> donde podemos ver como funciona cada contenedor de datos en Rust. Es un recurso muy interesante bajo licencia Creative Commons 4.0 BY, que si bien no es necesario para poder programar en Rust, puede sernos de utilidad cuando trabajemos a bajo nivel con el lenguaje.<br><br><a href="https://files.adrianistan.eu/RustContainer.png"><img class="aligncenter size-large wp-image-1054" src="https://files.adrianistan.eu/RustContainer-1024x573.png" alt="" width="840" height="470" /></a><a href="https://docs.google.com/presentation/d/1q-c7UAyrUlM-eZyTo1pd8SZ0qwA_wYxmPZVOQkoDmH4/edit#slide=id.p">Enlace original</a>]]></description>
                <comments>https://blog.adrianistan.eu/cheatsheet-contenedores-rust</comments>
                <pubDate>Tue, 10 Oct 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Premio Nobel de Medicina</title>
                <link>https://blog.adrianistan.eu/premio-nobel-medicina</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/premio-nobel-medicina</guid>
                <description><![CDATA[Para que quede constancia, dejo a mi blog y a todos vosotros como notarios y lanzo mi predicción:<br><p style="text-align: center;"><em>El español Francis Mojica ganará el Premio Nobel de Medicina de este año</em></p><br><a href="https://files.adrianistan.eu/Mojica.jpg"><img class="aligncenter size-full wp-image-1051" src="https://files.adrianistan.eu/Mojica.jpg" alt="" width="980" height="653" /></a>]]></description>
                <comments>https://blog.adrianistan.eu/premio-nobel-medicina</comments>
                <pubDate>Sun, 01 Oct 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Crear ventanas y botones en Rust con GTK</title>
                <link>https://blog.adrianistan.eu/crear-ventanas-botones-rust-gtk</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/crear-ventanas-botones-rust-gtk</guid>
                <description><![CDATA[Cuando salió el Macintosh allá por 1984, fue novedosa su interfaz gráfica. En efecto, fue la primera interfaz gráfica popular y ha servido de inspiración para muchos otros sistemas de ventanas.<br><br>Hoy vamos a introducir una manera de crear interfaces gráficas de usuario (GUI) con Rust. Para ello usaremos GTK. GTK funciona sobre GNU/Linux, macOS y Windows (aunque es un poco más complicado de lo que debiera).<br><br>Para ello usaremos la fantástica crate <strong>gtk</strong> del proyecto <a href="http://gtk-rs.org/">Gtk-rs</a>.<br><h2>Instalando gtk</h2><br>Añade al fichero Cargo.toml las siguiente líneas:<br><br><pre class="lang:default decode:true"><br>[dependencies]<br>gtk = &quot;0.2.0&quot;<br></pre><br><br>Y ejecuta cargo build.<br><h2>Creando una ventana</h2><br>Lo primero que vamos a hacer es crear una ventana.<br><br>Importamos gtk e iniciamos GTK.<br><br><pre class="lang:rust decode:true"><br>extern crate gtk;<br><br>use gtk::prelude::*;<br><br>fn main() {<br>    if gtk::init().is_err() {<br>        println!(&quot;Failed to initialize GTK.&quot;);<br>        return;<br>    }<br></pre><br><br>Ahora podemos crear una ventana. <strong>GtkWindow</strong> es un <strong>struct</strong> con métodos asociados, por lo que podemos ajustar ciertos parámetros. En este código parece que estamos usando orientación a objetos, pero recuerdo que Rust técnicamente no tiene <strong>clases</strong> ni <strong>herencia</strong>.<br><br><pre class="lang:rust decode:true"><br>    let window = gtk::Window::new(gtk::WindowType::Toplevel);<br><br>    window.set_title(&quot;Adrianistan - GTK - Rust&quot;);<br>    window.set_border_width(10);<br>    window.set_position(gtk::WindowPosition::Center);<br>    window.set_default_size(350, 70);<br></pre><br><br>Ahora vamos a añadir un evento para que cuando se pulse la X en nuestra ventana, se cierre el programa. Para ello tenemos que entender como funciona GTK. Cuando programamos en GTK lo que hacemos es configurar la aplicación y posteriormente ceder la ejecución a una función <strong>gtk_main</strong> que procesa todos los eventos según haya sido configurado. Para configurar los eventos usaremos callbacks, que en Rust se pueden implementar con <strong>closures</strong>. Las funciones que en GTK nos permiten conectar eventos siempre tienen el prefijo <strong>connect</strong>.<br><br><pre class="lang:rust decode:true"><br>    window.connect_delete_event(|_, _| {<br>        gtk::main_quit();<br>        Inhibit(false)<br>    });<br></pre><br><br>Ahora vamos a mostrar la ventana. Por defecto en GTK todos los widgets (todo lo que se muestra en pantalla) está oculto. Para mostrar todos los widgets que se han añadido a la ventana se suele usar <strong>show_all</strong>, que va a haciendo <strong>show</strong> de forma recursiva. Por último, le damos el control de la aplicación a <strong>gtk::main</strong>.<br><br><pre class="lang:rust decode:true"><br>    window.show_all();<br>    gtk::main();<br>}<br></pre><br><br>Una vez hecho esto, si compilamos con <strong>cargo run</strong> ya deberíamos ver una preciosa ventana GTK.<br><br><a href="https://files.adrianistan.eu/Captura-de-pantalla-19.png"><img class="aligncenter size-large wp-image-1043" src="https://files.adrianistan.eu/Captura-de-pantalla-19-1024x576.png" alt="" width="840" height="473" /></a>Y por supuesto, si pulsamos la X, la aplicación se cierra.<br><h2>Layouts y botones en GTK</h2><br>Vamos ahora a añadir dos cosas: un botón y un label que nos de un número del dado. Para poner varios elementos en una aplicación GTK es recomendable usar un layout. Voy a usar el layout Box, que permite agrupar los widgets de forma vertical u horizontal. Para los números aleatorios voy a usar la crate <strong>rand</strong>. El código final sería así:<br><br><pre class="lang:rust decode:true"><br>extern crate gtk;<br>extern crate rand;<br><br>use gtk::prelude::*;<br>use rand::distributions::{IndependentSample, Range};<br><br>fn pick(a: i32, b: i32) -&gt; i32 {<br>    let between = Range::new(a, b);<br>    let mut rng = rand::thread_rng();<br>    between.ind_sample(&amp;mut rng)<br>}<br><br>fn main() {<br>    if gtk::init().is_err() {<br>        println!(&quot;Failed to initialize GTK.&quot;);<br>        return;<br>    }<br><br>    let window = gtk::Window::new(gtk::WindowType::Toplevel);<br><br>    window.set_title(&quot;Adrianistán - GTK - Rust&quot;);<br>    window.set_border_width(10);<br>    window.set_position(gtk::WindowPosition::Center);<br>    window.set_default_size(350, 70);<br><br>    window.connect_delete_event(|_, _| {<br>        gtk::main_quit();<br>        Inhibit(false)<br>    });<br><br>    let vbox = gtk::Box::new(gtk::Orientation::Vertical,10);<br><br>    let button = gtk::Button::new_with_label(&quot;Tirar el dado&quot;);<br><br>    let label = gtk::Label::new(&quot;No has tirado el dado todavía&quot;);<br><br>    let l = label.clone();<br>    button.connect_clicked(move |_| {<br>        let dado = pick(1,7);<br>        let text: String = format!(&quot;Dado: {}&quot;,dado);<br>        l.set_text(text.as_str());<br>    });<br>    <br>    vbox.add(&amp;button);<br>    vbox.add(&amp;label);<br>    window.add(&amp;vbox);<br><br>    window.show_all();<br>    gtk::main();<br>}<br></pre><br><br>Aquí pasan varias cosas interesantes. La primera es que usamos <strong>add</strong> para ir añadiendo de forma jerárquica los widgets. Debajo de Window está Box y debajo de Box tenemos Button y Label.<br><br>Por otra parte, vemos que la función asociada al evento del click de los botones es <strong>connect_clicked</strong>. Bien, en este ejemplo he introducido algo importante. Estoy modificando un widget desde un evento relacionado a otro widget. ¿Esto como se lleva con las reglas de propiedad/ownership de Rust? Bastante mal. Rust no puede saber si cuando se ejecuta el evento tenemos acceso al widget en cuestión que vamos a modificar. Afortunadamente, la API de gtk-rs ha sido diseñada con esto en cuenta y simplemente podemos hacer una llamada a <strong>clone</strong> para obtener otra referencia al objeto, que podemos pasar al closure (con <strong>move</strong>). Este clonado no lo es tal, sino que hace uso de <strong>Rc</strong>. Simplemente se nos presenta de forma transparente.<br><h2>El ejemplo final: dibujando con Cairo</h2><br>En este último ejemplo voy a añadir un widget donde se podrá ver la cara del dado que ha salido. Para ello uso un <strong>GtkDrawingArea</strong>, que permite usar la API de <strong>Cairo</strong>.<br><br><pre class="lang:rust decode:true"><br>extern crate gtk;<br>extern crate rand;<br><br>use gtk::prelude::*;<br>use rand::distributions::{IndependentSample, Range};<br>use std::rc::Rc;<br>use std::cell::Cell;<br><br>fn pick(a: i32, b: i32) -&gt; i32 {<br>    let between = Range::new(a, b);<br>    let mut rng = rand::thread_rng();<br>    between.ind_sample(&amp;mut rng)<br>}<br><br>fn main() {<br>    if gtk::init().is_err() {<br>        println!(&quot;Failed to initialize GTK.&quot;);<br>        return;<br>    }<br><br>    let window = gtk::Window::new(gtk::WindowType::Toplevel);<br><br>    window.set_title(&quot;Adrianistán - GTK - Rust&quot;);<br>    window.set_border_width(10);<br>    window.set_position(gtk::WindowPosition::Center);<br>    window.set_default_size(350, 70);<br><br>    window.connect_delete_event(|_, _| {<br>        gtk::main_quit();<br>        Inhibit(false)<br>    });<br><br>    let vbox = gtk::Box::new(gtk::Orientation::Vertical,10);<br><br>    let button = gtk::Button::new_with_label(&quot;Tirar el dado&quot;);<br><br>    let label = gtk::Label::new(&quot;No has tirado el dado todavía&quot;);<br><br>    let r = Rc::new(Cell::new(0));<br><br>    let random = r.clone();<br>    let drawingarea = gtk::DrawingArea::new();<br>    drawingarea.set_size_request(300,300);<br>    drawingarea.connect_draw(move |widget,cr|{<br>        let width: f64 = widget.get_allocated_width() as f64;<br>        let height: f64 = widget.get_allocated_height() as f64;<br>        cr.rectangle(0.0,0.0,width,height);<br>        cr.set_source_rgb(1.0,1.0,1.0);<br>        cr.fill();<br><br>        cr.set_source_rgb(0.,0.,0.);<br>        let random = random.get();<br>        if random == 1 || random == 3 || random == 5{<br>            cr.arc(width/2.0,height/2.,height/10.,0.0,2.0*std::f64::consts::PI);<br>        }<br>        cr.fill();<br>        if random == 2 || random == 3 || random == 4 || random == 5 || random == 6 {<br>            cr.arc(width/4.,height/4.,height/10.,0.0,2.0*std::f64::consts::PI);<br>            cr.arc(3.*width/4.,3.*height/4.,height/10.,0.0,2.0*std::f64::consts::PI);<br>        }<br>        cr.fill();<br><br>        if random == 4 || random == 5 || random == 6 {<br>            cr.arc(3.*width/4.,height/4.,height/10.,0.0,2.0*std::f64::consts::PI);<br>            cr.arc(width/4.,3.*height/4.,height/10.,0.0,2.0*std::f64::consts::PI);<br>        }<br>        cr.fill();<br>        <br>        if random == 6 {<br>            cr.arc(width/2.,height/4.,height/10.,0.0,2.0*std::f64::consts::PI);<br>            cr.arc(width/2.,3.*height/4.,height/10.,0.0,2.0*std::f64::consts::PI);<br>        }<br>        cr.fill();<br>        Inhibit(false)<br>    });<br><br>    let l = label.clone();<br>    let dado = r.clone();<br>    let da = drawingarea.clone();<br>    button.connect_clicked(move |_| {<br>        dado.set(pick(1,7));<br>        let text: String = format!(&quot;Dado: {}&quot;,dado.get());<br>        l.set_text(text.as_str());<br>        da.queue_draw();<br>    });<br>    <br>    vbox.add(&amp;button);<br>    vbox.add(&amp;label);<br>    vbox.add(&amp;drawingarea);<br>    window.add(&amp;vbox);<br><br>    window.show_all();<br>    gtk::main();<br>}<br></pre><br><br><a href="https://files.adrianistan.eu/SinTirarDado.png"><img class="aligncenter size-full wp-image-1044" src="https://files.adrianistan.eu/SinTirarDado.png" alt="" width="383" height="460" /></a><a href="https://files.adrianistan.eu/Dado3.png"><img class="aligncenter size-full wp-image-1045" src="https://files.adrianistan.eu/Dado3.png" alt="" width="386" height="454" /></a><a href="https://files.adrianistan.eu/Dado5.png"><img class="aligncenter size-full wp-image-1046" src="https://files.adrianistan.eu/Dado5.png" alt="" width="395" height="471" /></a> <a href="https://files.adrianistan.eu/Dado6.png"><img class="aligncenter size-large wp-image-1047" src="https://files.adrianistan.eu/Dado6.png" alt="" width="390" height="467" /></a>Con esto tenemos lo básico para empezar a diseñar GUIs con GTK y Rust.<br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/crear-ventanas-botones-rust-gtk</comments>
                <pubDate>Fri, 29 Sep 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Y sigo sin entender nada pero da igual</title>
                <link>https://blog.adrianistan.eu/sigo-sin-entender-nada</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/sigo-sin-entender-nada</guid>
                <description><![CDATA[Hoy hace ya unos años, un susodicho fue parido. Los médicos habían predicho que nacería una semana antes, pero en un inesperado giro de los acontecimientos (plot twist para los modernos), el que iba a ser Virgo nació Libra. Y así el destino de este personaje cambió para siempre.<br><br>Desde entonces ha ido tratando de obtener respuestas, para llegar a la conclusión de que lo más interesante son las preguntas.<br><br><iframe src="https://www.youtube-nocookie.com/embed/7DA2MKuI6fs" width="560" height="315" frameborder="0" allowfullscreen="allowfullscreen"></iframe><br><br>¿Y qué toca hacer hoy? Otros años, llegada esta fecha publicaba algo en lo que había estado trabajando, sin embargo este año no tengo nada que enseñar. Así que habrá que inventar una nueva tradición. ¡Escuchemos canciones de Monty Python todos juntos!<br><br><iframe src="https://www.youtube-nocookie.com/embed/fUspLVStPbk" width="560" height="315" frameborder="0" allowfullscreen="allowfullscreen"></iframe><br><br>Quizá este año haya sido uno en los que más haya crecido personalmente. Para ser sinceros, al cumplir años el año anterior creía que este sería mi año definitivo con las mujeres. No ha sido así, aunque por el camino he ido recopilando anécdotas que solo mis más íntimos amigos conocen y que el resto de la humanidad no conocerá hasta que me aproxime a mi lecho de muerte. Sin embargo, ahora mismo no me parece algo tan importante. Quizá la clave sea dejarle de dar la importancia que le estaba dando antes. Creo que he leído a Feynman en el mejor momento que podía de mi vida, y ha supuesto una enorme influencia en mí.<br><br>Entrar en la asociación BEST, sin apenas saber mucho de ella, ha sido una de las cosas que más me ha cambiado: en mi manera de ver el mundo, en mi manera de relacionarme con la gente. Todavía tengo mucho por disfrutar de esta aventura, he conocido a multitud de gente. Curiosamente quedé por primera vez con ellos un 24 de septiembre, aunque las copas se alargaron hasta ya el día 25, mi cumpleaños. Qué curioso todo ¿verdad?<br><br><iframe src="https://www.youtube-nocookie.com/embed/JrdEMERq8MA" width="560" height="315" frameborder="0" allowfullscreen="allowfullscreen"></iframe><br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/sigo-sin-entender-nada</comments>
                <pubDate>Mon, 25 Sep 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Leer de teclado en Rust</title>
                <link>https://blog.adrianistan.eu/leer-teclado-rust</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/leer-teclado-rust</guid>
                <description><![CDATA[En muchas aplicaciones es necesario leer datos de teclado. Si bien esto podría considerarse sencillo en lenguaje como <strong>Python</strong> o <strong>Ruby</strong>, lo cierto es que sí se quiere hacer bien es complicado. Funciones como <strong>scanf</strong> de C son consideradas inseguras y en Java, hasta la llegada de la clase <strong>java.util.</strong><strong>Scanner</strong> era un dolor de cabeza. En Rust no es distinto, es por ello que muchos tutoriales de Rust obvian esta parte. No obstante, leer de teclado no es tan difícil, como veremos a continuación.<br><br><a href="https://files.adrianistan.eu/Teclado.jpg"><img class="aligncenter size-full wp-image-1034" src="https://files.adrianistan.eu/Teclado.jpg" alt="" width="558" height="412" /></a><br><h2>read_line</h2><br>El método principal para leer de teclado es <strong>read_line</strong>, que nos lee una línea como <strong>String</strong>. Para acceder a read_line primero necesitamos tener on objeto <strong>stdin</strong>. La manera más fácil de hacerlo es usar el módulo <strong>std::io</strong>.<br><br>El procedimiento es el siguiente, en primer lugar creamos una variable de tipo String vacía y mutable donde se va a alojar el resultado, posteriormente leemos y tratamos el resultado.<br><br><pre class="lang:rust decode:true"><br>use std::io;<br><br>fn main() {<br>    println!(&quot;Dime tu nombre: &quot;);<br>    let mut input = String::new();<br>    io::stdin().read_line(&amp;mut input);<br>    println!(&quot;Tu nombre es {}&quot;,input.trim());<br>}<br></pre><br><br>Como vemos, al leer la línea también se nos guarda el salto de línea. Si queremos quitarlo podemos usar <strong>trim</strong>.<br><br>Este código sin embargo generará una <strong>advertencia</strong> por el compilador y es que read_line genera devuelve un valor, concretamente un <strong>Result</strong>, que como vimos, sirven para <a href="https://blog.adrianistan.eu/2017/07/03/gestion-errores-rust-option-result/">el manejo de errores en Rust</a>. Si no queremos tratar este Result con especial interés, podemos usar <strong>ok</strong> y opcionalmente especificar un mensaje de error con <strong>expect</strong>.<br><br><pre class="lang:rust decode:true"><br>use std::io;<br><br>fn main() {<br>    println!(&quot;Dime tu nombre: &quot;);<br>    let mut input = String::new();<br>    io::stdin().read_line(&amp;mut input).ok().expect(&quot;Error al leer de teclado&quot;);<br>    println!(&quot;Tu nombre es {}&quot;,input.trim());<br>}<br></pre><br><br>Si quieres tratar el error mejor puedes, pero read_line no suele fallar.<br><h2>Leyendo enteros</h2><br>Hasta aquí todo sencillo, porque leíamos <strong>String</strong>, en lo que entra todo lo que el usuario puede meter. Pero, ¿y si queremos leer un número de teclado? La cosa se complica. Normalmente se lee de teclado como <strong>String</strong> y luego se intenta pasar a número. Veamos como.<br><br><pre class="lang:rust decode:true"><br>use std::io;<br>use std::str::FromStr;<br><br>fn main() {<br>    println!(&quot;Dime tu edad: &quot;);<br>    let mut input = String::new();<br>    io::stdin().read_line(&amp;mut input).ok().expect(&quot;Error al leer de teclado&quot;);<br>    let edad: u32 = u32::from_str(&amp;input.trim()).unwrap();<br>    let frase = if edad &gt;= 18 {<br>        &quot;Mayor de edad&quot;<br>    }else{<br>        &quot;Menor de edad&quot;<br>    };<br>    println!(&quot;{}&quot;,frase);<br>}<br></pre><br><br>Como vemos, hay que importar <strong>std::str::FromStr</strong> para tener disponible las operaciones <strong>from_str</strong> en los tipos elementales. También se observa que hemos hecho un <strong>unwrap</strong>, porque from_str devuelve un Result. Este error sin embargo conviene que lo tratemos con más cuidado, pues es bastante probable que salte.<br><h2>Un ejemplo ideal</h2><br>En este código vamos a ver como pedir un entero, asegurándonos de que el usuario realmente introduce un entero e insistiendo hasta que finalmente introduce un entero válido.<br><br><pre class="lang:rust decode:true"><br>use std::io;<br>use std::io::Write;<br>use std::str::FromStr;<br>use std::num::ParseIntError;<br><br>fn read_input() -&gt; Result&lt;u32,ParseIntError&gt; {<br>    print!(&quot;Dime tu edad: &quot;);<br>    io::stdout().flush().ok();<br>    let mut input = String::new();<br>    io::stdin().read_line(&amp;mut input).ok().expect(&quot;Error al leer de teclado&quot;);<br>    let input = input.trim();<br>    let edad: u32 = u32::from_str(&amp;input)?;<br>    Ok(edad)<br>}<br><br>fn main() {<br>    let edad;<br>    loop {<br>        if let Ok(e) = read_input(){<br>            edad = e;<br>            break;<br>        }else{<br>            println!(&quot;Introduce un número, por favor&quot;);<br>        }<br>    }<br>    let frase = if edad &gt;= 18 {<br>        &quot;Mayor de edad&quot;<br>    }else{<br>        &quot;Menor de edad&quot;<br>    };<br>    println!(&quot;{}&quot;,frase);<br>}<br></pre><br><br>He decidido separar la parte de pedir el número a otra función que devuelve Result para así poder usar el operador <strong>?</strong>. También he usado <strong>print!</strong> y <strong>io::stdout().flush()</strong> en vez de <strong>println!</strong> para que tanto el mensaje como la entrada se realice en la misma línea y quede más bonito.<br><br><a href="https://files.adrianistan.eu/RustInputTest.png"><img class="aligncenter size-full wp-image-1033" src="https://files.adrianistan.eu/RustInputTest.png" alt="" width="586" height="136" /></a><br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/leer-teclado-rust</comments>
                <pubDate>Fri, 15 Sep 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>¿Por qué ha estado Adrianistán 10 días offline?</title>
                <link>https://blog.adrianistan.eu/ha-estado-adrianistan-10-dias-offline</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/ha-estado-adrianistan-10-dias-offline</guid>
                <description><![CDATA[Si eres de ese tipo de personas que visitan frecuentemente páginas nada recomendables como esta, es posible que te hayas percatado de un ligero detalle. Adrianistán no iba.<br><br><iframe class="giphy-embed" src="https://giphy.com/embed/3o72F8t9TDi2xVnxOE" width="480" height="327" frameborder="0" allowfullscreen="allowfullscreen"></iframe><br><br>Sí, ya sé que ahora mismo habréis perdido toda la confianza en mí, pero voy a detallaros qué es lo que pasó exactamente.<br><br><strong>tl;dr</strong>: La nota del servicio técnico de Vodafone que le pongo tiende asintóticamente a cero.<br><br><a href="https://files.adrianistan.eu/Asintota.png"><img class="aligncenter size-full wp-image-1028" src="https://files.adrianistan.eu/Asintota.png" alt="" width="626" height="373" /></a><br><h2>Agosto</h2><br>Agosto es el mes favorito del año para toquetear los servidores.<br><br><iframe class="giphy-embed" src="https://giphy.com/embed/Dl2seYrwPvfjO" width="325" height="480" frameborder="0" allowfullscreen="allowfullscreen"></iframe><br><br>La primera quincena estuve de excavación arqueológica en Numancia, gracias a un campo de trabajo organizado por la Diputación de Soria. Así que durante esa quincena no hice nada. Si realmente os interesa, puedo explicar como fue la experiencia.<br><br>Sin embargo al llegar ya podía empezar a tocar el servidor. Quizá algunos ya lo sabéis, pero esta web está alojada en mi casa, en una Raspberry Pi 2. Al menos eso era antes, gracias a páginas chinas y a suculentas promociones de bancos que han acabado vendidos a 1 € conseguí hacerme con una Orange Pi PC, una Raspberry Pi 3, así como switch y cables Ethernet. Me dispuse a montar un clúster para este blog y algún proyecto extra más que tengo entre manos. El resultado es <strong>Mastín de Jade</strong>.<br><br><a href="https://files.adrianistan.eu/MastinDeJade.jpg"><img class="aligncenter size-large wp-image-1026" src="https://files.adrianistan.eu/MastinDeJade-1024x576.jpg" alt="" width="840" height="473" /></a>En un par de días tuve todo funcionando. Reinstalé todo de cero (¡los backups funcionan!) y de paso modifiqué la arquitectura general. Ahora WordPress está en la Pi 3, no en la 2 pero la base de datos MariaDB se ha quedado en la Pi 2.<br><br>¿Por qué te cuento esto? En realidad el hecho de modificar la arquitecura ligeramente ha provocada que no estuviese 100% operativa desde que se solucionase el <strong>verdadero problema</strong>.<br><br><iframe class="giphy-embed" src="https://giphy.com/embed/d2Z32wTIuPl6iHuw" width="480" height="360" frameborder="0" allowfullscreen="allowfullscreen"></iframe><br><br>&nbsp;<br><h2>Septiembre</h2><br>Septiembre es un mes complicado para mí. El 1 de septiembre salí de casa para ser organizador de un curso BEST en Valladolid. Sí, es mi ciudad, pero eso no quiere decir que pasase/durmiese en casa. El 2 de septiembre llegó la mala noticia. El internet había dejado de funcionar en mi casa. Dejé de registrar visitas en Google Analytics. Algo pasaba. En principio se pensó que el router estaba mal. Pero no. El fallo parecía estar en el módem. ¿Cómo es que tengo módem a día de hoy? Para encontrar respuesta tendremos que retroceder en el tiempo...<br><br><iframe class="giphy-embed" src="https://giphy.com/embed/zZeCRfPyXi9UI" width="480" height="270" frameborder="0" allowfullscreen="allowfullscreen"></iframe><br><br>&nbsp;<br><h2>El pasado</h2><br>Hace muchos años, en Castilla y León existía una compañía de telecomunicaciones llamada <strong>Retecal</strong>. Esta compañía ofrecía servicios de televisión por cable entre otros. En su momento se contrató televisión por cable en mi casa y se montó una instalación. Esta instalación se basa en cables coaxiales. En un determinado momento (próximo a la llegada de la TDT), quitamos la televisión por cable pero la instalación no se desmontó.<br><br><iframe src="https://www.youtube-nocookie.com/embed/bq-LQ3Y0aW4" width="560" height="315" frameborder="0" allowfullscreen="allowfullscreen"></iframe><br><br><em>Este anuncio ya da bastante pena</em><br><br>Años más tarde, decidimos abandonar Orange como ISP y nos pasamos a Vodafone-ONO. Contratamos fibra óptica, pero como la velocidad contratada podía pasar por el cable coaxial sin problemas nos dijeron que podían reutilizar la instalación de Retecal. Es decir, la fibra óptica llega hasta mi portal, lugar donde engancha al antiguo cable de Retecal para llegar a casa. Ese es el motivo por el que hace falta un módem aparte.<br><h2>Septiembre otra vez</h2><br>El módem no tiene encendidas las luces de siempre. Definitivamente el problema está por ese lado. Sin yo poder asistir a la ceremonia, el técnico visita la instalación y ¡sorpresa!, el módem tampoco está estropeado. El técnico verifica que el problema es que llega una señal muy débil al módem. Sin embargo en ese momento, la empresa subcontratada por Vodafone ya no se encarga de eso, hay que llamar a <strong>otra</strong> empresa subcontratada para verificar el cableado entre mi casa y la centralita.<br><blockquote class="imgur-embed-pub" lang="en" data-id="YXhT6fA"><a href="//imgur.com/YXhT6fA">We Bare Bears</a></blockquote><br><script async src="//s.imgur.com/min/embed.js" charset="utf-8"></script><br><br>A partir de ese momento, todo es infructuoso, pasan los días y no viene nadie. Llamadas a Vodafone todos los días que muchas veces se resumen en: ¿<em>ha probado a reiniciar el router?</em>.La situación era tan desesperante que ya buscábamos otra compañía. Si Vodafone no nos lo iba a arreglar, al menos contratábamos a otra empresa y que montase otra instalación.<br><br>Finalmente llego a casa el 11 de septiembre y sigue sin ir. Mencionar que no solo no tenía Internet, sino que el teléfono fijo también estaba inoperativo por compartir instalación.<br><br><a href="https://files.adrianistan.eu/AlfredoDuro.gif"><img class="aligncenter size-full wp-image-1030" src="https://files.adrianistan.eu/AlfredoDuro.gif" alt="" width="362" height="200" /></a><br><br>Finalmente el día 13 aparece un técnico de otra empresa. No hay nadie en casa. Gracias a un vecino consigue entrar en los cuadros, ojo, no de la planta baja, sino del primer piso y encuentra el problema allí. Llego del Carrefour justo cuando el técnico ya casi ha terminado. Resulta que justamente de camino me había quedado unos minutos de más viendo como demolían una casa con una excavadora (superpoder de jubilado desbloqueado) y llegué por los pelos.<br><br>¡El caso es que ya iba todo! Le dimos las gracias y me puse a comprobar que todo siguiese en orden.<br><br><a href="https://files.adrianistan.eu/Rajoy.gif"><img class="aligncenter size-full wp-image-1029" src="https://files.adrianistan.eu/Rajoy.gif" alt="" width="370" height="304" /></a><br><br>Evidentemente todo lo relacionado con <strong>Mastín de Jade</strong> necesitaba reajustes, el router había perdido la IP asignada y me tocó modificar los dominios. También me tocó ajustar toda la LAN, ya que el router perdió la memoria y me asignó IPs distintas a cada pieza del clúster.<br><br>En realidad hasta hoy mismo no ha funcionado de verdad, pues se me olvidó modificar una configuración de MariaDB y debido al cambio de IPs, no aceptaba conexiones entrantes desde el WordPress.<br><br>Y esta ha sido la historia de por qué Adrianistán ha estado offline ni más ni menos que 11 días.<br><br><a href="https://files.adrianistan.eu/AnalyticsDown.png"><img class="size-full wp-image-1027" src="https://files.adrianistan.eu/AnalyticsDown.png" alt="" width="955" height="161" /></a> Adivináis qué días estuvo Vodafone dándome largas<br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/ha-estado-adrianistan-10-dias-offline</comments>
                <pubDate>Fri, 15 Sep 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Autómatas celulares unidimensionales en Python</title>
                <link>https://blog.adrianistan.eu/automatas-celulares-unidimensionales-python</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/automatas-celulares-unidimensionales-python</guid>
                <description><![CDATA[Estaba yo leyendo este verano un libro titulado <em>Think Complexity</em> cuando en un capítulo empezó a hablar de los autómatas celulares unidimensionales. El tema me interesó y por eso esta entrada. Veamos primero a qué nos referimos cuando hablamos de esto.<br><br>Cuando hablamos a autómatas celulares, nos referimos a pequeñas entidades independientes pero que interaccionan entre sí. Celulares porque son la unidad elemental del universo donde van a existir y autómatas porque deciden por ellas mismas, basadas en un conjunto de reglas predefinido, cuando el tiempo avanza de forma discreta (es decir, a <em>pasos</em>).<br><br>Este concepto abstracto puede visualizarse con facilidad si nos imaginamos una rejilla. Cada celda es una célula capaz de cambiar su estado según su entorno.<br><br>Los autómatas celulares fueron objeto de estudio de <strong>Stephen Wolfram</strong>, matemático conocido por haber diseñado el programa <em>Mathemathica</em> y <em>Wolfram Alpha</em>.<br><br><a href="https://files.adrianistan.eu/UpC1-Kjf.jpg"><img class="aligncenter size-full wp-image-1020" src="https://files.adrianistan.eu/UpC1-Kjf.jpg" alt="" width="400" height="400" /></a><br><br>Los autómatas celulares unidimensionales son aquellos que forman parte de un universo unidimensional. Es decir, cada célula tiene una vecina a su izquierda y a su derecha. En los bordes se pueden definir varios comportamientos pero el concepto no varía. Pensemos en ello como una tira de celdas.<br><br><a href="https://files.adrianistan.eu/Tira.png"><img class="aligncenter size-full wp-image-1001" src="https://files.adrianistan.eu/Tira.png" alt="" width="200" height="50" /></a><br><br>El estudio de estos autómatas es interesante, ya que pueden generarse patrones/situaciones muy complejas en base a unas reglas sencillas.<br><h2>¿Cómo se definen las reglas?</h2><br>Wolfram usó un sistema para definir las reglas de estos autómatas que hoy conocemos como <em>Wolfram Code</em>. Se basa en definir una tabla con los estados presentes de la célula y sus vecinas así como el valor que deberá adoptar en esa situación la célula. Como Wolfram usó células con solo dos estados, todo está en binario, y la parte baja de la tabla es un número de 8 bits. Este número se suele pasar a decimal y así se identifica.<br><table><br><tbody><br><tr><br><th>Estados presentes</th><br><td>111</td><br><td>110</td><br><td>101</td><br><td>100</td><br><td>011</td><br><td>010</td><br><td>001</td><br><td>000</td><br></tr><br><tr><br><th>Estado futuro</th><br><td>0</td><br><td>0</td><br><td>1</td><br><td>1</td><br><td>0</td><br><td>0</td><br><td>1</td><br><td>0</td><br></tr><br></tbody><br></table><br>Esta tabla representa la Regla 50, porque 00110010 en binario es 50.<br><h2>¿Cómo se representan?</h2><br>Una manera muy interesante de representar estos autómatas es poner cada <em>paso</em> en una fila distinta dentro de una imagen.<br><br><a href="https://files.adrianistan.eu/TiempoCA.png"><img class="aligncenter size-full wp-image-1002" src="https://files.adrianistan.eu/TiempoCA.png" alt="" width="300" height="200" /></a>Una vez que sabemos esto vamos a hacer un programa en Python que nos permita observar la evolución de estos autómatas.<br><br>Usaremos el procedimiento original, que es empezar con todos los estados de los autómatas en 0 salvo el del autómata central, que será 1.<br><h2>La clase Automata</h2><br>La clase autómata va a contener las reglas, así como un ID y el estado que posee. Además, por cuestiones técnicas conviene guardar el estado anterior que tuvo.<br><br><pre class="lang:python decode:true"><br>class Automata(object):<br><br>    rules = list()<br><br>    def __init__(self,idx=0):<br>        self.idx = idx<br>        self.state = False<br>        self.statePrev = False<br></pre><br><br>Como podéis ver, <strong>rules</strong> no lleva self, es decir, va a ser una variable compartida entre todas las instancias de Automata. Esto es porque las reglas son idénticas a todos los autómatas.<br><h2>La clase World</h2><br>Ahora vamos a definir el universo donde residen estos autómatas. Este universo almacena una lista con los autómatas, se encarga de actualizarlos según las normas y de dibujarlos usando <strong>PIL</strong>. También he insertado el código que codifica las normas según el número en decimal.<br><br><pre class="lang:python decode:true"><br>class World(object):<br>    def __init__(self,rule=50):<br>        self.rule = rule<br>        self.im = Image.new(&quot;L&quot;,(WIDTH,HEIGHT))<br>        self.data = np.zeros(WIDTH*HEIGHT,dtype=np.uint8)<br>        b = bin(rule)[2:].zfill(8)<br>        Automata.rules = [True if c == &quot;1&quot; else False for c in b]<br>        self.list = list()<br>        self.step = 0<br>    <br>    def add(self):<br>        automata = Automata(len(self.list))<br>        self.list.append(automata)<br><br>    def update(self):<br>        for automata in self.list:<br><br>            automata.statePrev = automata.state<br>            p = self.list[automata.idx - 1].statePrev if automata.idx &gt; 0 else False<br>            n = self.list[automata.idx + 1].state if automata.idx &lt; len(self.list)-1 else False<br>            s = automata.state<br><br>            if p and s and n:<br>                automata.state = automata.rules[0]<br>            elif p and s and not n:<br>                automata.state = automata.rules[1]<br>            elif p and not s and n:<br>                automata.state = automata.rules[2]<br>            elif p and not s and not n:<br>                automata.state = automata.rules[3]<br>            elif not p and s and n:<br>                automata.state = automata.rules[4]<br>            elif not p and s and not n:<br>                automata.state = automata.rules[5]<br>            elif not p and not s and n:<br>                automata.state = automata.rules[6]<br>            elif not p and not s and not n:<br>                automata.state = automata.rules[7]<br>            <br><br>    def draw_row(self):<br>        if self.step == 0:<br>            middle = len(self.list) // 2<br>            self.list[middle].state = True<br>        for i,automata in enumerate(self.list):<br>            if automata.state:<br>                self.data[self.step*HEIGHT+i] = 255<br>        self.step += 1<br>    def save(self):<br>        self.im.putdata(self.data)<br>        self.im.save(&quot;RULE-%d.png&quot; % self.rule)<br></pre><br><br>Con esto ya lo tenemos casi todo. Ahora faltaría poner en marcha todo. La idea es simplemente crear una instancia de World, hacer unas cuantas llamadas a add, y después ir haciendo el ciclo update/draw_row. Una vez hayamos acabado, hacemos save y obtendremos un PNG con la imagen.<br><h2>Código completo</h2><br><br><pre class="lang:python decode:true"><br>import numpy as np<br>from PIL import Image<br><br>WIDTH = 5001<br>HEIGHT = 5001<br><br>class Automata(object):<br><br>    rules = list()<br><br>    def __init__(self,idx=0):<br>        self.idx = idx<br>        self.state = False<br>        self.statePrev = False<br><br>class World(object):<br>    def __init__(self,rule=50):<br>        self.rule = rule<br>        self.im = Image.new(&quot;L&quot;,(WIDTH,HEIGHT))<br>        self.data = np.zeros(WIDTH*HEIGHT,dtype=np.uint8)<br>        b = bin(rule)[2:].zfill(8)<br>        Automata.rules = [True if c == &quot;1&quot; else False for c in b]<br>        print(Automata.rules)<br>        self.list = list()<br>        self.step = 0<br>    <br>    def add(self):<br>        automata = Automata(len(self.list))<br>        self.list.append(automata)<br><br>    def update(self):<br>        for automata in self.list:<br><br>            automata.statePrev = automata.state<br>            p = self.list[automata.idx - 1].statePrev if automata.idx &gt; 0 else False<br>            n = self.list[automata.idx + 1].state if automata.idx &lt; len(self.list)-1 else False<br>            s = automata.state<br><br>            if p and s and n:<br>                automata.state = automata.rules[0]<br>            elif p and s and not n:<br>                automata.state = automata.rules[1]<br>            elif p and not s and n:<br>                automata.state = automata.rules[2]<br>            elif p and not s and not n:<br>                automata.state = automata.rules[3]<br>            elif not p and s and n:<br>                automata.state = automata.rules[4]<br>            elif not p and s and not n:<br>                automata.state = automata.rules[5]<br>            elif not p and not s and n:<br>                automata.state = automata.rules[6]<br>            elif not p and not s and not n:<br>                automata.state = automata.rules[7]<br>            <br><br>    def draw_row(self):<br>        if self.step == 0:<br>            middle = (len(self.list) // 2)<br>            self.list[middle].state = True<br>        for i,automata in enumerate(self.list):<br>            if automata.state:<br>                self.data[self.step*HEIGHT+i] = 255<br>        self.step += 1<br>    def save(self):<br>        self.im.putdata(self.data)<br>        self.im.save(&quot;RULE-%d.png&quot; % self.rule)<br>    def __str__(self):<br>        s = str()<br>        for l in self.list:<br>            s += &quot;T&quot; if l.state else &quot;F&quot;<br>        return s<br><br>def world_run(rule):<br>    world = World(rule)<br>    for _ in range(WIDTH):<br>        world.add()<br><br>    for _ in range(HEIGHT):<br>        world.draw_row()<br>        world.update()<br>    world.save()<br><br>def main():<br>    rule = input(&quot;Rule: &quot;)<br>    try:<br>        rule = int(rule)<br>        if 255 &gt;= rule &gt;= 0:<br>            world_run(rule)<br>            print(&quot;Check for the generated RULE-%d.png file&quot; % rule)<br>        else:<br>            raise ValueError<br>    except ValueError:<br>        print(&quot;Please, insert a number between 0 and 255&quot;)<br>        main()<br><br>if __name__ == &quot;__main__&quot;:<br>    main()<br></pre><br><br><h2>Algunas reglas importantes</h2><br><h3>Regla 30</h3><br>Una de las más importantes a nivel matemático. Ha sido objeto de mucho estudio, sin embargo no vamos a entrar en detalles más allá de su aspecto visual.<br><br><a href="https://files.adrianistan.eu/Rule-30.png"><img class="aligncenter size-large wp-image-1004" src="https://files.adrianistan.eu/Rule-30-1024x1024.png" alt="" width="840" height="840" /></a><br><br><a href="https://files.adrianistan.eu/Rule-30-detalle.png"><img class="size-full wp-image-1005" src="https://files.adrianistan.eu/Rule-30-detalle.png" alt="" width="744" height="744" /></a> Vista ampliada<br><h3>Regla 110</h3><br>Esta regla es también muy interesante. ¡Se demostró que era Turing completa!<br><br><a href="https://files.adrianistan.eu/Rule-110.png"><img class="aligncenter size-large wp-image-1006" src="https://files.adrianistan.eu/Rule-110-1024x1024.png" alt="" width="840" height="840" /></a><br><br><a href="https://files.adrianistan.eu/Rule-110-detalle.png"><img class="size-large wp-image-1007" src="https://files.adrianistan.eu/Rule-110-detalle-1024x1024.png" alt="" width="840" height="840" /></a> Vista en detalle<br><h2>Regla 126</h2><br>Esta regla no es tan importante, pero personalmente me parece muy bonita.<br><br><a href="https://files.adrianistan.eu/Rule-126.png"><img class="aligncenter size-large wp-image-1008" src="https://files.adrianistan.eu/Rule-126-1024x1024.png" alt="" width="840" height="840" /></a><br><br><a href="https://files.adrianistan.eu/Rule-126-detalle.png"><img class="size-full wp-image-1009" src="https://files.adrianistan.eu/Rule-126-detalle.png" alt="" width="500" height="500" /></a> Vista ampliada<br><h3>Reglas 57 y 99</h3><br>Son dos reglas isomorfas. Es decir, son en realidad la misma regla pero aplicada a lados distintos. Elijo estas dos porque se aprecia muy bien el isomorfismo.<br><br><a href="https://files.adrianistan.eu/Rule-57.png"><img class="size-large wp-image-1010" src="https://files.adrianistan.eu/Rule-57-1024x1024.png" alt="" width="840" height="840" /></a> Regla 57<br><br><a href="https://files.adrianistan.eu/Rule-99.png"><img class="size-large wp-image-1011" src="https://files.adrianistan.eu/Rule-99-1024x1024.png" alt="" width="840" height="840" /></a> Regla 99<br><h2>Regla 169</h2><br><a href="https://files.adrianistan.eu/Rule-169.png"><img class="aligncenter size-large wp-image-1012" src="https://files.adrianistan.eu/Rule-169-1024x1024.png" alt="" width="840" height="840" /></a><br><br><a href="https://files.adrianistan.eu/Rule-169-detalle.png"><img class="size-full wp-image-1013" src="https://files.adrianistan.eu/Rule-169-detalle.png" alt="" width="500" height="500" /></a> Vista en detalle<br><h3>Regla 129</h3><br><h2><a href="https://files.adrianistan.eu/Rule-129.png"><img class="aligncenter size-large wp-image-1014" src="https://files.adrianistan.eu/Rule-129-1024x1024.png" alt="" width="840" height="840" /></a>Regla 90</h2><br>Es el famoso <a href="https://blog.adrianistan.eu/2017/05/24/triangulo-sierpinski-javascript/">triángulo de Sierpinski</a>.<br><h3><a href="https://files.adrianistan.eu/Rule-90.png"><img class="aligncenter size-large wp-image-1015" src="https://files.adrianistan.eu/Rule-90-1024x1024.png" alt="" width="840" height="840" /></a>Regla 150</h3><br><h2><a href="https://files.adrianistan.eu/Rule-150.png"><img class="aligncenter size-large wp-image-1016" src="https://files.adrianistan.eu/Rule-150-1024x1024.png" alt="" width="840" height="840" /></a>Regla 105</h2><br>Esta regla no tiene isomorfo.<br><br><a href="https://files.adrianistan.eu/Rule-105.png"><img class="aligncenter size-large wp-image-1017" src="https://files.adrianistan.eu/Rule-105-1024x1024.png" alt="" width="840" height="840" /></a>En este artículo no he querido entrar en las complejidades matemáticas de todo esto. Es algo que todavía no entiendo así que no sería sincero por mi parte exponerlo.<br><h2>Bonus: Richard Feynman y Steve Jobs</h2><br>Quien me conoce sabe de sobra que uno de los personajes de la historia que más ha influido en mi vida es <a href="https://blog.adrianistan.eu/2017/02/16/esta-usted-broma-sr-feynman/">Richard Feynman</a>. Debo reconocer que entré en un estado de éxtasis al descubrir que Feynman y Wolfram no solo trabajaron juntos, sino que lo hicieron alrededor de la regla 30 antes mostrada. También me sorprendió que Steve Jobs y Wolfram resultasen ser <em>amigos de toda la vida</em>. No dejo de sorprenderme de los contactos de ciertos personajes históricos entre sí.<br><br><a href="https://files.adrianistan.eu/Slide006.jpg"><img class="size-full wp-image-1019" src="https://files.adrianistan.eu/Slide006.jpg" alt="" width="700" height="637" /></a> Feynman a la izquierda, Wolfram a la derecha]]></description>
                <comments>https://blog.adrianistan.eu/automatas-celulares-unidimensionales-python</comments>
                <pubDate>Mon, 28 Aug 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Todo lo que debes sobre las tarjetas de crédito, débito y prepago</title>
                <link>https://blog.adrianistan.eu/lo-debes-las-tarjetas-credito-debito-prepago</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/lo-debes-las-tarjetas-credito-debito-prepago</guid>
                <description><![CDATA[<blockquote>Este art&iacute;culo es bastante extenso. Si te interesan los detalles m&aacute;s t&eacute;cnicos puedes ir al final.</blockquote>
<p><br />Nacieron en la primera mitad del siglo XX. Originalmente, estas primeras tarjetas no eran m&aacute;s que una l&iacute;nea de cr&eacute;dito de un determinado establecimiento. En 1924 por ejemplo, General Petroleum Corporation emite una tarjeta para poder adquirir gasolina. En realidad, lo que suced&iacute;a al usar esa tarjeta era que contra&iacute;amos una deuda con la compa&ntilde;&iacute;a, que posteriormente hab&iacute;a que pagar en efectivo. De este modo, funcionaban de forma similar a como cuando dejamos que nos apunten algo en la cuenta del bar.<br /><br />Esto cambi&oacute; en 1949, cuando Franck McNamara estaba cenando con unos amigos y discutiendo precisamente sobre este tipo de tarjetas. Finalmente al llegar a la hora de pagar, McNamara pudo comprobar que se hab&iacute;a dejado la cartera en casa. Tuvo que llamar a su mujer para que le llevase el dinero. Pas&oacute; tal verg&uuml;enza que se propuso acabar para siempre con este tipo de situaciones. As&iacute; es como naci&oacute; <strong>Diners Club</strong>, la primera compa&ntilde;&iacute;a de tarjetas de cr&eacute;dito del mundo.<br /><br /><a href="https://files.adrianistan.eu/FrankXMcNamara.jpg"><img class="wp-image-960 size-full" src="https://files.adrianistan.eu/FrankXMcNamara.jpg" alt="" width="300" height="285" /></a> Frank X. McNamara<br /><br /><a href="https://files.adrianistan.eu/DinersClub.jpg"><img class="size-large wp-image-961" src="https://files.adrianistan.eu/DinersClub-1024x683.jpg" alt="" width="840" height="560" /></a> Una de las primeras tarjetas Diners Club<br /><br />Otros dicen que no llam&oacute; a su mujer sino que sali&oacute; del aprieto dando su tarjeta de visita y anotando la cantidad debida. Sea cual sea la historia verdadera, lo cierto es que el sistema de funcionamiento de una <strong>tarjeta de cr&eacute;dito</strong> es ese. Nosotros cuando pagamos con una tarjeta de este tipo estamos emitiendo deuda, que a final de mes o cuando m&aacute;s nos convenga, pagamos &iacute;ntegramente o a plazos. Diners cobraba comisiones de mantenimiento a los poseedores de las tarjetas y comisiones por transacci&oacute;n a los establecimientos que las admit&iacute;an. Esta es la principal diferencia respecto a las <strong>tarjetas de d&eacute;bito</strong> y las <strong>tarjetas prepago</strong>.<br /><br /><a href="https://files.adrianistan.eu/BankAmericard.jpg"><img class="size-full wp-image-962" src="https://files.adrianistan.eu/BankAmericard.jpg" alt="" width="500" height="333" /></a> Logotipos en un establecimiento de Master Charge (actual MasterCard) y BankAmericard (actual VISA)<br /><br />A partir de ese momento empiezan a surgir m&aacute;s compa&ntilde;&iacute;as, <strong>Bank AmeriCard</strong> o <strong>Master Charge</strong> (VISA y MasterCard actualmente). En Espa&ntilde;a se aceptaron tarjetas de cr&eacute;dito por primera vez en el a&ntilde;o 1954 y por aquel entonces eran simples tarjetas de cart&oacute;n con un n&uacute;mero y su titular, que los hoteles y restaurantes de la &eacute;poca anotaban para posteriormente reclamar el pago a <strong>Diners</strong>. La primera tarjeta emitida por un banco espa&ntilde;ol no llegar&iacute;a hasta 1978, por el Banco de Bilbao y se trataba de una AmeriCard. Las tarjetas de cr&eacute;dito son el tipo de tarjetas m&aacute;s admitidas en el mundo y suelen llevar asociadas ventajas tales como seguros, programas de puntos, ... Al pasarnos una factura a final de mes es m&aacute;s f&aacute;cil saber cuanto hemos gastado en un mes. Sin embargo estas tarjetas tienen un gran peligro. Nos dejan a nuestra disposici&oacute;n una gran cantidad de dinero, y si no somos capaces de pagar las cuotas a tiempo podemos enfrentarnos a unos intereses de devoluci&oacute;n en la mayor&iacute;a de los casos de dos cifras.<br /><br /><a href="https://files.adrianistan.eu/BilbaoVISA.jpg"><img class="size-large wp-image-963" src="https://files.adrianistan.eu/BilbaoVISA-1024x416.jpg" alt="" width="840" height="341" /></a> Las primeras tarjetas de cr&eacute;dito emitidas en Espa&ntilde;a fueron las BankAmericard del Banco de Bilbao<br /><br />Como vemos, las tarjetas de cr&eacute;dito no necesitaban ninguna tecnolog&iacute;a en especial para funcionar, las de d&eacute;bito y las prepago, s&iacute; que lo necesitan. No obstante, todas las tarjetas de cr&eacute;dito hoy en d&iacute;a usan tecnolog&iacute;a, de hecho existen tarjetas que operan en varios modos, cr&eacute;dito y d&eacute;bito. &iquest;C&oacute;mo funcionan las tarjetas de d&eacute;bito?<br /><br />Las <strong>tarjetas de d&eacute;bito</strong> son lo m&aacute;s parecido a pagar en efectivo. Cuando pagamos, estamos pagando con nuestro dinero. En algunas se nos permite caer en n&uacute;meros rojos (VISA Debit) mientras que en otros se comprueba la existencia de fondos (VISA Electron). Inmediatamente a la compra, el dinero se descuenta de la cuenta bancaria asociada. Estas tarjetas suelen tener gastos de mantenimiento menores (en Espa&ntilde;a los bancos las suelen dar gratis) y son pr&aacute;cticamente igual de admitidas que las de cr&eacute;ditos en cajeros autom&aacute;ticos, establecimientos y compras por Internet. No obstante, las tarjeta de d&eacute;bito no son aceptadas en todos los sitios, por dos motivos:<br /><br /></p>
<ul>
<ul>
<li>Las tarjetas de d&eacute;bito (como VISA Electron) requieren conexi&oacute;n a la cuenta bancaria de tu banco. Este proceso es m&aacute;s complicado que simplemente anotar que debes 50&euro; a fulanito. Otras tarjetas como VISA Debit pueden funcionar en puntos de pago offline ya que pueden dejarte en n&uacute;meros rojos (por tanto, el comerciante se asegura que siempre va a recibir el dinero, a&uacute;n sin saber cuanto dinero tienes).</li>
</ul>
</ul>
<p>&nbsp;</p>
<ul>
<ul>
<li>Las tarjetas de d&eacute;bito no dejan gastar m&aacute;s dinero del que hay en cuenta (o pueden dejar, pero no demasiado).</li>
</ul>
</ul>
<p><br /><br />Por estos dos motivos, puede ser que una tarjeta de d&eacute;bito no llegue a funcionar en alg&uacute;n pa&iacute;s extranjero (aunque es cada vez m&aacute;s raro) o simplemente no se admitan para poder as&iacute; cobrarte gastos imprevistos (alquileres de coches, hoteles, ...). Si bien en esto las tarjetas de d&eacute;bito han ido mejorando y en muchas existe la posibilidad de <em>reservar</em> una cantidad de dinero de la tarjeta. Eso quiere decir que al hacerel primer pago o la entrada, se nos cobra una parte y se nos reserva otra. Esa cantidad reservada a efectos pr&aacute;cticos no podemos usarla para nada pero t&eacute;cnicamente sigue siendo nuestro. Cuando se acaba el plazo y no ha hecho falta recurrir a ese dinero de reserva, el comerciante libera ese dinero y ya lo podemos usar en nuestra tarjeta.<br /><br />Las <strong>tarjetas prepago</strong> funcionan con tecnolog&iacute;a similar a las de d&eacute;bito, salvo por el detalle de que no tienen asociada una cuenta bancaria como tal. En vez de eso, las tarjetas prepago tienen una cuenta propia, que debemos recargar antes de efectuar pagos o retirar dinero. Las tarjetas prepago se han vuelto muy populares para menores de edad y compras por Internet, donde solo se recarga la tarjeta con la cantidad m&aacute;xima que consideremos necesario.<br /><br /><a href="https://files.adrianistan.eu/Antes.png"><img class="size-large wp-image-986" src="https://files.adrianistan.eu/Antes-1024x576.png" alt="" width="840" height="473" /></a> La tarjeta Antes de BBVA es una tarjeta prepago<br /><br />Por &uacute;ltimo, existe otro tipo de tarjeta, aunque apenas usadas en compras, se trata de las <strong>tarjetas monedero</strong>. Su diferencia principal respecto a las <strong>tarjetas prepago</strong> reside en como se realiza el pago. En una tarjeta prepago, se realiza una conexi&oacute;n al emisor de la tarjeta, como con las de d&eacute;bito, mientras que en una monedero no. En una tarjeta monedero el dinero se almacena en la propia tarjeta, de forma cifrada. Este sistema no es tan popular y en Espa&ntilde;a que yo sepa ning&uacute;n banco emite tarjetas de este tipo. Son usadas sin embargo en algunos sistemas de transporte p&uacute;blico o de empresas, donde se puede descontar el dinero instant&aacute;neamente, aunque no haya conexi&oacute;n con un servidor central. La forma de recargar estas tarjetas es acudir con la propia tarjeta a un punto de recarga, no pudiendo realizarse por Internet o sin la tarjeta.<br /><br /><a href="https://files.adrianistan.eu/BonobusCajaDuerp.jpg"><img class="size-full wp-image-964" src="https://files.adrianistan.eu/BonobusCajaDuerp.jpg" alt="" width="700" height="525" /></a> Tarjeta monedero emitida por Caja Duero para el transporte urbano de Valladolid<br /><br />Mencionar tambi&eacute;n las <strong>tarjetas ATM</strong>. Estas tarjetas &uacute;nicamente pueden usarse en los cajeros para sacar dinero, no pudi&eacute;ndose usar en las compras. De este modo funcionan similar a algunas <strong>libretas de ahorro</strong>. Sin embargo, he de reconocer que personalmente no he visto ninguna tarjeta ATM.</p>
<h2>Redes bancarias</h2>
<p><br />Si nos centramos en tarjetas de cr&eacute;dito, d&eacute;bito y prepago tenemos que tener en cuenta las redes interbancarias. Estas permiten las conexiones entre las redes de los distintos bancos y permiten por ejemplo que podamos sacar dinero en un cajero de <strong>Espa&ntilde;aDuero</strong> cuando nuestra tarjeta la ha emitido <strong>N26 Bank</strong> o que podamos pagar en el extranjero. En el sistema bancario, no existe nada parecido a Internet (la red de redes) y cada red tiene sus propios puntos de acceso. As&iacute; pues, no todos los cajeros tienen por qu&eacute; poder conectarse a todas las redes existentes, con los consiguientes problemas (no poder sacar dinero, no poder pagar,...). En tarjetas de d&eacute;bito esto es m&aacute;s problem&aacute;tico. En Espa&ntilde;a existen tres redes locales aunque funcionan tambi&eacute;n redes internacionales.<br /><br /></p>
<ul>
<ul>
<li>Servired, la mayor red intranacional de Espa&ntilde;a, con el BBVA, Bankinter, Deutsche Bank, Sabadell, Bankia, CaixaBank, Cajamar, las cajas rurales que est&aacute;n unidas por el Banco Cooperativo Espa&ntilde;ol, Triodos Bank, Self Bank, Banco Mediolanum, Caixa Geral, Laboral Kutxa, Abanca y otros bancos m&aacute;s peque&ntilde;os.</li>
</ul>
</ul>
<p>&nbsp;</p>
<ul>
<ul>
<li>4B, originalmente para Banco Santander, Banco Popular, Banco Pastor y Banesto, ahora tambi&eacute;n ING, Targobank, Openbank, Banca March, Cetelem, Inversis, Andbank y alguno m&aacute;s.</li>
</ul>
</ul>
<p>&nbsp;</p>
<ul>
<ul>
<li>Euro6000, usada antiguamente por las cajas de ahorros, hoy en d&iacute;a de sostiene gracias a Unicaja, Espa&ntilde;aDuero, Ibercaja, Kutxabank, Liberbank y EVO Banco.</li>
</ul>
</ul>
<p><br /><br /><a href="https://files.adrianistan.eu/Servired.jpg"><img class="size-full wp-image-965" src="https://files.adrianistan.eu/Servired.jpg" alt="" width="640" height="419" /></a> Cajero perteneciente a la red intranacional Servired. Tambi&eacute;n aparecen logos de PLUS, Cirrus, Tarjeta 6000, Eurocheque, Eurocard,...<br /><br /><a href="https://files.adrianistan.eu/Telebanco4B.jpg"><img class="size-large wp-image-966" src="https://files.adrianistan.eu/Telebanco4B-1024x768.jpg" alt="" width="840" height="630" /></a> Indicativo del Banco Pastor y Telebanco 4B<br /><br /><a href="https://files.adrianistan.eu/Euro6000IberCaja.jpg"><img class="size-full wp-image-967" src="https://files.adrianistan.eu/Euro6000IberCaja.jpg" alt="" width="674" height="392" /></a> IberCaja, con sede en Zaragoza, es una de las entidades que sigue dentro de Euro 6000<br /><br />Estas redes se comunican con otras redes similares de otros pa&iacute;ses o bien pueden recurrir a una red internacional, como <strong>Cirrus</strong>. Aunque despu&eacute;s de la crisis de 2008 muchos bancos y cajas se han fusionado entre s&iacute;, parece que las tres redes siguen operando de forma independiente.<br /><br /><a href="https://files.adrianistan.eu/Cirrus_2016.svg_.png"><img class="size-large wp-image-968" src="https://files.adrianistan.eu/Cirrus_2016.svg_-1024x796.png" alt="" width="840" height="653" /></a> Logo actual de la red interbancaria Cirrus</p>
<h2>Tarjetas</h2>
<p><br />Vamos ahora a ver las principales empresas que se dedican a las tarjetas:<br /><br /><strong>VISA</strong> (anteriormente Bank AmeriCard) es quiz&aacute; la tarjeta m&aacute;s conocida del mundo. Es la tarjeta l&iacute;der en Espa&ntilde;a. A nivel global es la segunda red m&aacute;s importante (solo superada por <strong>UnionPay</strong>). Las tarjetas VISA operan a trav&eacute;s de la red interbancaria <strong>PLUS</strong>. VISA dispone de varios tipos de tarjeta: VISA, VISA Debit, VISA Electron y V Pay. VISA es de cr&eacute;dito, <strong>VISA Debit</strong> es la versi&oacute;n m&aacute;s libre de las de d&eacute;bito. Permite dejarte en n&uacute;meros rojos ya que al hacer un pago no se comprueba la disponibilidad de fondos. Es por ello que la VISA Debit puede usarse en algunos sitios donde solo se admiten tarjetas de cr&eacute;dito por ese mismo motivo, el comerciante se puede asegurar que va a recibir su dinero aunque no sepa si tu cuenta tiene fondos. <strong>VISA Electron</strong> por contra, verifica que haya fondos suficientes en la cuenta, as&iacute; que necesita que exista una conexi&oacute;n con el banco. Desde hace no mucho existe <strong>V Pay</strong>, una tarjeta de d&eacute;bito mucho m&aacute;s moderna, emitidas solo con chip y que solo funcionan en <strong>SEPA</strong> (Single Euro Payments Area). Han sido dise&ntilde;adas para ser usadas en transacciones peque&ntilde;as y por lo general funcionan de forma similar a <strong>VISA Electron</strong>.<br /><br /><a href="https://files.adrianistan.eu/Visa_Electron.jpg"><img class="size-full wp-image-969" src="https://files.adrianistan.eu/Visa_Electron.jpg" alt="" width="729" height="456" /></a> Logo antiguo de VISA Electron<br /><br /><a href="https://files.adrianistan.eu/PLUS.png"><img class="size-large wp-image-970" src="https://files.adrianistan.eu/PLUS-1024x648.png" alt="" width="840" height="532" /></a> PLUS es la red interbancaria de VISA<br /><br /><a href="https://files.adrianistan.eu/VPay.png"><img class="size-large wp-image-971" src="https://files.adrianistan.eu/VPay-941x1024.png" alt="" width="840" height="914" /></a> V Pay es la gama m&aacute;s moderna de VISA, s&oacute;lo v&aacute;lida en territorios SEPA<br /><br /><strong>MasterCard</strong>, la gran competidora de VISA en Espa&ntilde;a. MasterCard entro en el mercado europeo con las adquisiciones de <strong>Eurocard</strong> y <strong>Europay</strong>. Su red interbancaria es <strong>Cirrus</strong>. Posee varias tarjetas: MasterCard, Debit MasterCard y Maestro. MasterCard es la l&iacute;nea de tarjetas de cr&eacute;dito, Debit MasterCard es una l&iacute;nea de tarjetas de d&eacute;bito compatibles con puntos offline y n&uacute;meros rojos (como VISA Debit) y Maestro es comparable a VISA Electron, ya que requiere comprobaci&oacute;n de fondos. No obstante, Maestro tambi&eacute;n puede funcionar como tarjeta prepago y las tarjetas Maestro son distintas al resto de tarjetas MasterCard y VISA. En ciertos pa&iacute;ses, Maestro funciona como Debit MasterCard, por lo que no requiere autorizaci&oacute;n electr&oacute;nica. Las tarjetas Maestro tambi&eacute;n son compartidas en algunos pa&iacute;ses con alg&uacute;n proveedor local de tarjetas, como <strong>Girocard</strong> en Alemania. Maestro fue de las primeras tarjetas donde no hab&iacute;a que firmar, sino que se introduc&iacute;a un PIN al comprar, tanto si se compra usando chip como con banda magn&eacute;tica. Muchas Maestro que no pueden usarse en el extranjero e Internet. En Espa&ntilde;a, las tarjetas Maestro nunca han llegado a ser populares.<br /><br /><a href="https://files.adrianistan.eu/MasterCard-Cirrus-and-Maestro.jpg"><img class="size-full wp-image-972" src="https://files.adrianistan.eu/MasterCard-Cirrus-and-Maestro.jpg" alt="" width="640" height="113" /></a> Logos antiguos de MasterCard, Cirrus y Maestro<br /><br /><strong>American Express</strong> es la tercera entidad de tarjetas en Espa&ntilde;a. Sin embargo, debido a sus altas comisiones, es f&aacute;cil encontrar tiendas que no la admitan.<br /><br /><a href="https://files.adrianistan.eu/AmericanExpress.png"><img class="aligncenter size-large wp-image-973" src="https://files.adrianistan.eu/AmericanExpress-1024x1024.png" alt="" width="840" height="840" /></a><br /><br /><strong>Discover</strong> y <strong>Diners Club</strong> son bastante usadas en Estados Unidos y otros pa&iacute;ses como Croacia. En Espa&ntilde;a son bastante dif&iacute;ciles de encontrar y se suelen aceptar &uacute;nicamente en establecimientos tur&iacute;sticos. Hasta hace a&ntilde;os eran empresas con tarjetas independientes, no obstante, <strong>Discover</strong> adquiri&oacute; Diners Club hace a&ntilde;os. Discover se centrar&aacute; en el mercado dom&eacute;stico estadounidense y Diners Club, que era m&aacute;s usada internacionalmente, seguir&aacute; con la expansi&oacute;n internacional.<br /><br /><a href="https://files.adrianistan.eu/Discover.jpg"><img class="aligncenter size-full wp-image-974" src="https://files.adrianistan.eu/Discover.jpg" alt="" width="1000" height="750" /></a><br /><br /><strong>JCB</strong>, se trata de una entidad japonesa de tarjetas. Es usada sobretodo por japoneses y coreanos.<br /><br /><a href="https://files.adrianistan.eu/JCB_logo.svg_.png"><img class="aligncenter size-full wp-image-975" src="https://files.adrianistan.eu/JCB_logo.svg_.png" alt="" width="200" height="155" /></a><br /><br /><strong>UnionPay</strong>, es la compa&ntilde;&iacute;a de tarjetas m&aacute;s grande del mundo, aunque la mayor parte de sus transacciones se concentran en China. Solo las emiten bancos chinos.<br /><br /><a href="https://files.adrianistan.eu/UnionPay_logo.svg_.png"><img class="size-large wp-image-976" src="https://files.adrianistan.eu/UnionPay_logo.svg_-1024x657.png" alt="" width="840" height="539" /></a> UnionPay es la tarjeta m&aacute;s usada del mundo desde 2015<br /><br /><strong>RuPay</strong>, son unas tarjetas de amplio uso creadas con intervenci&oacute;n del gobierno de la India con el deseo de que todos los habitantes de la India pudieran disponer de esta forma de pago.<br /><br /><a href="https://files.adrianistan.eu/1200px-RuPay.svg_.png"><img class="aligncenter size-large wp-image-977" src="https://files.adrianistan.eu/1200px-RuPay.svg_-1024x724.png" alt="" width="840" height="594" /></a><br /><br />Otras tarjetas pueden ser <strong>Bancomat</strong> (Italia),&nbsp;<strong>Elo</strong> (Brasil) o <strong>Dankort</strong> (Dinamarca). Este tipo de tarjetas, debido a su poca popularidad suelen ser compatibles con VISA y MasterCard en el extranjero, mientras que en sus pa&iacute;ses de origen usan su red interna.<br /><br /><a href="https://files.adrianistan.eu/Elofundo.png"><img class="size-full wp-image-978" src="https://files.adrianistan.eu/Elofundo.png" alt="" width="518" height="207" /></a> Elo es muy usada dentro de Brasil</p>
<h2>&iquest;C&oacute;mo funcionan las tarjetas?</h2>
<p><br />Ahora vamos a ver como se puede pagar con las tarjetas de cr&eacute;dito, d&eacute;bito y prepago. En primer lugar comentemos algo sobre el n&uacute;mero de las tarjetas. Estas tienen normalmente 16 d&iacute;gitos, aunque pueden llegar a 19 d&iacute;gitos. Se numeran por lotes, siendo los primeros d&iacute;gitos los correspondientes a la empresa. Es posible reconocer la empresa detr&aacute;s de la tarjeta seg&uacute;n su n&uacute;mero o <strong>PAN</strong>.<br /><br />Los <strong>PAN</strong> siguen una estructura, que aunque presenta longitud variable, est&aacute; bien definida.<br /><br /><a href="https://files.adrianistan.eu/UnoE.png"><img class="aligncenter size-large wp-image-979" src="https://files.adrianistan.eu/UnoE-1024x576.png" alt="" width="840" height="473" /></a><br /><br />El primer d&iacute;gito corresponde al <strong>Major Industry Identifier (MII)</strong>. Indica a que sector pertenece la empresa de la que es la tarjeta. Los sectores son:</p>
<table>
<tbody>
<tr>
<th>D&iacute;gito</th>
<th>Sector</th>
</tr>
<tr>
<td>0</td>
<td>Uso interno</td>
</tr>
<tr>
<td>1</td>
<td>Aerol&iacute;neas</td>
</tr>
<tr>
<td>2</td>
<td>Aerol&iacute;neas y otros</td>
</tr>
<tr>
<td>3</td>
<td>Viajes, entretenimiento, finanzas</td>
</tr>
<tr>
<td>4</td>
<td>Finanzas</td>
</tr>
<tr>
<td>5</td>
<td>Finanzas</td>
</tr>
<tr>
<td>6</td>
<td>Finanzas</td>
</tr>
<tr>
<td>7</td>
<td>Petroleras</td>
</tr>
<tr>
<td>8</td>
<td>Salud y telecomunicaciones</td>
</tr>
<tr>
<td>9</td>
<td>Sin asignar</td>
</tr>
</tbody>
</table>
<p><br />Aunque actualmente, la mayor&iacute;a de tarjetas son de finanzas. El <strong>MII</strong> y los siguientes 5 d&iacute;gitos forman el <strong>BIN</strong> o <strong>Bank Identification Number</strong> y sirven para idenficar al banco emisor de la tarjeta y enrutarlo. Los siguientes d&iacute;gitos excepto el &uacute;ltimo forman parte del identificador de tarjeta personal, asignado por el banco a sus clientes. El &uacute;ltimo d&iacute;gito es de comprobaci&oacute;n y se calcula con el algoritmo de Luhn.</p>
<table>
<tbody>
<tr>
<th>N&uacute;mero(s) de inicio</th>
<th>Longitud n&uacute;mero de tarjeta</th>
<th>Empresa</th>
</tr>
<tr>
<td>22 a 27</td>
<td>16</td>
<td>MasterCard</td>
</tr>
<tr>
<td>30</td>
<td>14</td>
<td>Diners Club</td>
</tr>
<tr>
<td>34</td>
<td>15</td>
<td>American Express</td>
</tr>
<tr>
<td>35</td>
<td>16</td>
<td>JCB</td>
</tr>
<tr>
<td>36</td>
<td>14</td>
<td>Diners Club</td>
</tr>
<tr>
<td>37</td>
<td>15</td>
<td>American Express</td>
</tr>
<tr>
<td>38, 39</td>
<td>14</td>
<td>Diners Club</td>
</tr>
<tr>
<td>4</td>
<td>16</td>
<td>VISA</td>
</tr>
<tr>
<td>5018, 502, 503, 506</td>
<td>12 a 16</td>
<td>Maestro</td>
</tr>
<tr>
<td>5019</td>
<td>16</td>
<td>Dankort</td>
</tr>
<tr>
<td>51 a 55</td>
<td>16</td>
<td>MasterCard</td>
</tr>
<tr>
<td>56 a 58</td>
<td>12 a 19</td>
<td>Maestro</td>
</tr>
<tr>
<td>60</td>
<td>16</td>
<td>Discover</td>
</tr>
<tr>
<td>62</td>
<td>16 a 19</td>
<td>UnionPay</td>
</tr>
<tr>
<td>622, 64, 65</td>
<td>16</td>
<td>Discover</td>
</tr>
<tr>
<td>639, 6220</td>
<td>12 a 19</td>
<td>Maestro</td>
</tr>
<tr>
<td>88</td>
<td>16 a 19</td>
<td>UnionPay</td>
</tr>
</tbody>
</table>
<p><br />Esta tabla no es exhaustiva pero puede servir de referencia r&aacute;pida. Por ejemplo, aunque VISA empieza siempre por 4, no todas las que empiezan por 4 son VISA siempre, ya que existen tarjetas <strong>Elo</strong> que tambi&eacute;n empiezan por 4. No obstante, se trata de rangos tan peque&ntilde;os que la lista ser&iacute;a muy larga si fuese exhaustiva.</p>
<h3>Banda magn&eacute;tica y firma</h3>
<p><br />Estas tarjetas, mejorando las de cart&oacute;n, agilizan el proceso de pago pero ni introducen ninguna medida de seguridad extra. Se definen seg&uacute;n el est&aacute;ndar ISO/IEC 7813 e ISO/IEC 7811. Incluyen una banda magn&eacute;tica desde la que se puede leer el n&uacute;mero de la tarjeta, la fecha de caducidad, los nombres del titular y algunos datos t&eacute;cnicos. Las tarjetas tienen 3 bandas o <em>tracks</em>.<br /><br /><a href="https://files.adrianistan.eu/Tracks.gif"><img class="size-full wp-image-983" src="https://files.adrianistan.eu/Tracks.gif" alt="" width="686" height="135" /></a> Los 3 tracks de las bandas magn&eacute;ticas y su posici&oacute;n<br /><br />El primer y segundo track llevan informaci&oacute;n redudante, de forma que si una banda est&aacute; da&ntilde;ada puede leerse la otra, sin embargo no siguen el mismo formato.</p>
<table>
<tbody>
<tr>
<th>Nombre del campo</th>
<th>Longitud</th>
<th>Uso</th>
</tr>
<tr>
<td>Start Sentinel (SS)</td>
<td>1 caracter</td>
<td>Indica que comienza el track 1, siempre es %</td>
</tr>
<tr>
<td>Format Code (FC)</td>
<td>1 caracter</td>
<td>Indica el tipo de tarjeta, B para cr&eacute;dito/d&eacute;bito</td>
</tr>
<tr>
<td>Primary Account Number (PAN)</td>
<td>Hasta 19 digitos</td>
<td>Normalmente, el n&uacute;mero de la tarjeta que est&aacute; impreso</td>
</tr>
<tr>
<td>Field Separato]]></description>
                <comments>https://blog.adrianistan.eu/lo-debes-las-tarjetas-credito-debito-prepago</comments>
                <pubDate>Thu, 27 Jul 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Timers en systemd, reemplazo a cron</title>
                <link>https://blog.adrianistan.eu/timers-systemd-reemplazo-cron</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/timers-systemd-reemplazo-cron</guid>
                <description><![CDATA[Odiado por muchos, alabado por otros, <strong>systemd</strong> se ha impuesto como el init por excelencia del mundo Linux. Las distros más populares como Debian, Ubuntu, Fedora, openSUSE y Arch Linux usan systemd por defecto. Hoy vengo a hablaros de una característica interesante de <strong>systemd</strong> que son son los timers. Se trata de poder definir que arranquen programas/servicios cada cierto tiempo o a una hora, su versatilidad es tal que pueden sustituir a <strong>cron</strong> en la mayoría de casos. Veamos como usar los timers.<br><h2>Creando un servicio</h2><br>Para crear un timer primero necesitamos crear un servicio simple que ejecute el comando que queramos al ser iniciado.<br><br>Crea el archivo <em>miapp.service<strong> </strong></em>y ponlo en <em>/etc/systemd/system/</em>.<br><br><pre class="lang:default decode:true"><br>[Unit]<br>Description=Cat Mania GIF<br><br>[Service]<br>ExecStart=/home/pi/charlatan/catmania.py<br></pre><br><br>&nbsp;<br><h2>Creando el timer</h2><br>Para el timer es necesario un archivo con extensión <strong>timer</strong> en <em>/etc/systemd/system/</em>. Se suele usar el mismo nombre que el que tiene el servicio para escenarios simples. El archivo <em>miapp.timer</em> quedaría así:<br><br><pre class="lang:default decode:true"><br>[Unit]<br>Description=Run Cat Mania daily<br><br>[Timer]<br>OnCalendar=daily<br>RandomizedDelaySec=5min<br>Unit=catmania.service<br><br>[Install]<br>WantedBy=timers.target<br></pre><br><br>&nbsp;<br><br>Aquí es donde podemos realizar toda la magia de los timers. Los timers de systemd tienen dos modos de funcionamiento. En el primer modo, systemd ejecutará el timer cada cierto tiempo desde el arranque del sistema ( o algo más tarde si no queremos saturar el sistema). El otro modo es el modo <strong>calendario</strong> donde podemos especificar fechas y horas con wildcards y patrones. Además, es posible modificar la hora exacta de ejecución del timer con las propiedades de <em>Randomize</em> y que evitan que si hay varios timers configurados a la vez, no lo hagan exactamente a la vez (pero siempre dentro de un margen). Otras opciones para OnCalendar pueden ser:<br><pre class="lang:default decode:true"><br>OnCalendar=Wed,Sun *-*-10..15 12:00:00<br></pre><br><br>En ese caso, el timer se activaría los miércoles y domingos que cayesen entre el 10 y el 15 de todos los meses a las 12 horas.<br><br><h2>Activando el timer</h2><br>Una vez estén copiados los archivos service y timer ejecutamos:<br><br><pre class="lang:default decode:true"><br>systemctl start miapp.timer<br>systemctl enable miapp.timer<br></pre><br><br>Ya solo queda esperar a que systemd lance el servicio en la fecha y hora especificados.<br><h2>Monitorización</h2><br>Los timers están integrados con systemd, por lo que los logs se registran con <strong>journalctl</strong>. Podemos inspeccionarlos de forma sencilla:<br><br><pre class="lang:default decode:true"><br>journalctl -f -u miapp.service<br>journalctl -f -u miapp.timer<br></pre><br><br>Para revisar los timers instalados y activos en el sistema puedes usar el comando:<br><br><pre class="lang:default decode:true"><br>systemtctl list-timers<br></pre><br><br>Y ya estaría. De este modo podemos usar systemd como reemplazo de cron. Yo personalmente lo prefiero, ya que me parece una sintaxis más clara.]]></description>
                <comments>https://blog.adrianistan.eu/timers-systemd-reemplazo-cron</comments>
                <pubDate>Tue, 25 Jul 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Formulario de registro en Elm</title>
                <link>https://blog.adrianistan.eu/formulario-registro-elm</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/formulario-registro-elm</guid>
                <description><![CDATA[Recientemente he estado experimentando con <a href="http://elm-lang.org">Elm</a>, un lenguaje funcional puro y a la vez framework de backend. De momento he programado ya he visto unas cuántas cosas y puedo decir que me gusta bastante, que es un soplo de aire fresco en el mundo web y que sinceramente no creo que vaya a triunfar algún día. Sin embargo para proyectos personales, lo veo como alternativa superior a Angular, React y Vue.<br><br>Uno de los ejercicios que puede venir bien para entender Elm es implementar un formulario de registro con normas para la contraseña. Según vayamos escribiendo se nos informa sobre qué errores tiene la contraseña para ser segura y cuando lo sea mostramos el botón de registro.<br><br>Es un código un poco tonto pero me apetecía compartirlo.<br><br><pre class="lang:default decode:true"><br>import Html exposing (..)<br>import Html.Attributes exposing (..)<br>import Html.Events exposing (onInput)<br>import Char<br>import String<br>import List<br><br>main : Program Never Model Msg<br>main = Html.program {init = init, view = view, update = update, subscriptions = subscriptions}<br><br>type alias Model =<br>{<br>    name : String,<br>    password : String,<br>    passwordAgain : String<br>}<br><br>init : (Model, Cmd Msg)<br>init = (Model &quot;&quot; &quot;&quot; &quot;&quot;, Cmd.none)<br><br>subscriptions : Model -&gt; Sub Msg<br>subscriptions model = Sub.none<br><br>type Msg = Name String | Password String | PasswordAgain String<br><br>update : Msg -&gt; Model -&gt; (Model, Cmd Msg)<br>update msg model =<br>    case msg of<br>        Name name -&gt; ({ model | name = name},Cmd.none)<br>        Password pass -&gt; ({model | password = pass},Cmd.none)<br>        PasswordAgain pass -&gt; ({model | passwordAgain = pass},Cmd.none)<br><br>view : Model -&gt; Html Msg<br>view model =<br>    div []<br>        [<br>            input[type_ &quot;text&quot;, placeholder &quot;Name&quot;, onInput Name][],<br>            input[type_ &quot;password&quot;, placeholder &quot;Password&quot;, onInput Password][],<br>            input[type_ &quot;password&quot;, placeholder &quot;Confirm&quot;, onInput PasswordAgain][],<br>            viewValidate model<br>        ]<br><br>viewValidate : Model -&gt; Html Msg<br>viewValidate model =<br>    let list = checkMinLength model :: checkPassword model :: checkUpper model :: checkLower model :: checkDigit model :: []<br>    wrongList = List.filter (\(x,y) -&gt; not x) list<br>    in<br>    if List.length wrongList == 0 then<br>        div [] [button[][text &quot;Submit&quot;]]<br>    else<br>        div [] (List.map showError wrongList )<br><br>showError : (Bool,String) -&gt; Html Msg<br>showError (_,error) =<br>    div [style[(&quot;color&quot;,&quot;red&quot;)]] [text error]<br><br>checkPassword : Model -&gt; (Bool, String)<br>checkPassword model =<br>    if model.password == model.passwordAgain then<br>        (True,&quot;&quot;)<br>    else<br>        (False,&quot;Passwords do not match&quot;)<br><br>checkMinLength : Model -&gt; (Bool,String)<br>checkMinLength model =<br>    if String.length model.password &gt; 7 then<br>        (True,&quot;OK&quot;)<br>    else<br>        (False,&quot;Password must have a minimum length of 8 characters&quot;)<br><br>checkUpper : Model -&gt; (Bool,String)<br>checkUpper model =<br>    let isUp = String.any Char.isUpper model.password<br>    in<br>    if isUp then<br>        (True,&quot;&quot;)<br>    else<br>        (False,&quot;The password must contain an upper letter&quot;)<br><br>checkLower : Model -&gt; (Bool,String)<br>checkLower model =<br>    let isDown = String.any Char.isLower model.password<br>    in<br>    if isDown then<br>        (True,&quot;&quot;)<br>    else<br>        (False,&quot;The password must contain a lower letter&quot;)<br><br>checkDigit : Model -&gt; (Bool,String)<br>checkDigit model =<br>    let isDig = String.any Char.isDigit model.password<br>    in<br>    if isDig then<br>        (True,&quot;&quot;)<br>    else<br>        (False,&quot;The password must contain a digit&quot;)<br></pre><br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/formulario-registro-elm</comments>
                <pubDate>Sat, 15 Jul 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>¿Qué hacer si pierdes o te roban el móvil?</title>
                <link>https://blog.adrianistan.eu/pierdes-te-roban-movil</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/pierdes-te-roban-movil</guid>
                <description><![CDATA[Nunca pensamos que pueda pasar y sin embargo, constantemente se dan situaciones donde esto pueda pasar. Me refiero a que perdamos o nos roben el móvil. Hoy en día los móviles son una extensión nuestra y son vitales para nuestro funcionamiento. Por tanto podemos pasarlo bastante mal <a href="https://www.clarin.com/next/herramientas-localizar-celular-robado_0_HyCuS73mb.html">si te roban el móvil</a> o lo pierdes.<br><br>Afortunadamente, los móviles actuales cuentan con sistemas que permiten localizar el móvil (siempre que el teléfono siga encendido claro está).<br><br>Si tu dispositivo tiene Android, la opción más común es usar el <strong>Android Device Manager</strong>. Se trata de una aplicación integrada de serie en los dispositivos Android con Google Play. Adicionalmente existe en Google Play y en la <a href="https://www.google.com/android/find">página web</a> una aplicación para localizar el dispositivo.<br><br><a href="https://files.adrianistan.eu/AndroidDeviceManager.png"><img class="aligncenter size-large wp-image-943" src="https://files.adrianistan.eu/AndroidDeviceManager-1024x494.png" alt="" width="840" height="405" /></a>Tiene varias opciones, entre ellas es muy interesante la de reproducir sonido si estamos cerca del teléfono y no lo vemos y la de borrar los datos, si vemos difícil recuperar el teléfono, que al menos no lleguen a cualquier mano.<br><br>Para usuarios de <strong>iOS</strong> existe una opción similar llamada <strong>Find my iPhone</strong>. Se activa desde la web de <strong>iCloud</strong>. Su funcionamiento es muy similar a Android Device Manager aunque se integra también con Apple Pay, la plataforma de pagos de Apple, para bloquear los pagos.<br><br>Estos métodos son los oficiales de los fabricantes para cada sistema operativo. No obstante, existen más aplicaciones de terceros. En dispositivos iOS tenemos la aplicación Life360 GPS Tracking, que nos permite conocer la ubicación GPS de familiares y amigos si ellos nos dan su autorización expresa. Puede ser interesante dejar configurado este servicio con un amigo para así obtener la ubicación precisa en caso de pérdida o sustracción. Una aplicación similar pero compatible con Android e iOS es Instamapper.<br><br>Pero existen todavía más métodos, quizá no tengas más opciones ya que no configuraste tu teléfono anteriormente. En ese caso te preguntarás <a href="https://tecnobeta.net/como-rastrear-celular-gratis/">cómo rastrear un celular por número</a>. Existen webs gratuitas, que afirman ser capaces de localizar los dispositivos. Algunas de ellas son: <strong>www.sat-gps-locate.com</strong> y<strong> www.themobiletracker.com. </strong>En estas webs solo hay que introducir el número completo de teléfono (con prefijo de país) y localizan el teléfono. Según las propias webs la precisión es de 10 metros en Europa y de 25 metros en Latinoamérica. Usan una combinación de tecnología GSM (que disponen todos los teléfonos) y GPS.<br><br>Existen muchas aplicaciones, vamos a mencionar otra conocida, llamada SeekDroid. De funcionamiento a Android Device Manager, tiene algunas opciones extra que pueden interesarnos. Podemos enviar mensajes al dispositivo que tiene el ladrón y de ese modo advertirle de qué te has dado cuenta del robo. El inconveniente que tiene es que necesita conexión a Internet para operar. Si tu teléfono tiene contratado datos no es inconveniente, pero si no lo tiene solo podrás localizarlo cuando el ladrón se conecte por Wi-Fi.<br><br>En todo caso recuerda usar estos métodos sabiamente y sin abusar. Rastrear a una persona mayor de edad sin su consentimiento puede ser un delito. Usa estos métodos solo para rastrear tu propio teléfono y el de familiares y amigos que te dejen.<br><br>&nbsp;<br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/pierdes-te-roban-movil</comments>
                <pubDate>Fri, 07 Jul 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Rust 101, tutorial de Rust en español</title>
                <link>https://blog.adrianistan.eu/rust-101-tutorial-rust-espanol</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/rust-101-tutorial-rust-espanol</guid>
                <description><![CDATA[<p>Rust es un lenguaje imperativo, orientado a objetos y funcional, con gran soporte a concurrencia y de prop&oacute;sito general. En este tutorial de Rust daremos nuestros primeros pasos en Rust, un lenguaje que nace de las necesidades de tener un lenguaje que fuese r&aacute;pido, concurrente y seguro. Aprenderemos a amar/odiar al estricto compilador y veremos algunas de las grandes ideas detr&aacute;s de Rust. Sin embargo, esto requiere mayor atenci&oacute;n de nuestra parte ya que Rust no es <em>otro lenguaje basado en C</em>. Algunas de sus caracter&iacute;sticas son:</p>
<ul>
<li>Abstracciones sin costo</li>
<li>Seguridad de memoria garantizada</li>
<li>Eliminaci&oacute;n de las condiciones de carrera</li>
<li>Generalizaci&oacute;n basada en <em>traits</em></li>
<li>Comparaci&oacute;n de patrones</li>
<li>Inferencia de tipos</li>
</ul>
<p>Rust toma influencias de muchos lenguajes, para dar lugar a un estilo &uacute;nico de programaci&oacute;n que costar&aacute; m&aacute;s de lo habitual aprender a usar de forma efectiva. Rust toma influencia de C++, OCaml, Haskell y en menor medida de Erlang, Alef, Limbo, Swift y otros lenguajes menos conocidos.</p>
<blockquote>Si aprecias el trabajo detr&aacute;s de este tutorial, no dudes en apoyarme a trav&eacute;s de una donaci&oacute;n v&iacute;a <a href="https://www.paypal.me/aarroyoc/5">PayPal</a> o alguna criptodivisa (ver margen derecho). Tambi&eacute;n se aceptan mejoras al texto, tan solo ponte en <a href="https://blog.adrianistan.eu/contacto/">contacto</a> conmigo.</blockquote>
<h2>&Iacute;ndice del tutorial de Rust</h2>
<ol>
<li>Instalando Rust</li>
<li><a href="https://blog.adrianistan.eu/2017/07/03/variables-tipos-datos-rust/">Variables y tipos de datos en Rust</a></li>
<li><a href="https://blog.adrianistan.eu/2017/07/03/referencias-prestamos-en-rust/">Referencias y pr&eacute;stamos en Rust</a></li>
<li><a href="https://blog.adrianistan.eu/2017/07/03/funciones-closures-rust/">Funciones y closures en Rust</a></li>
<li><a href="https://blog.adrianistan.eu/2017/07/03/estructuras-control-rust/">Estructuras de control en Rust</a></li>
<li><a href="https://blog.adrianistan.eu/2017/07/03/structs-traits-poo-rust/">Structs, traits y POO en Rust</a></li>
<li><a href="https://blog.adrianistan.eu/2017/07/03/gestion-errores-rust-option-result/">Gesti&oacute;n de errores en Rust, Option y Result</a></li>
<li><a href="https://blog.adrianistan.eu/2017/07/03/concurrencia-en-rust/">Concurrencia en Rust</a></li>
<li><a href="https://blog.adrianistan.eu/2017/07/03/box-rc-refcell-punteros-inteligentes-rust/">Box, Rc y RefCell, punteros inteligentes en Rust</a></li>
<li><a href="https://blog.adrianistan.eu/2017/07/03/cargo-modulos-rust/">Cargo y m&oacute;dulos en Rust</a></li>
<li><a href="https://blog.adrianistan.eu/2017/07/03/tests-en-rust/">Tests en Rust</a></li>
<li><a href="https://blog.adrianistan.eu/2017/07/03/documentacion-con-rustdoc/">Documentaci&oacute;n con rustdoc</a></li>
<li><a href="https://blog.adrianistan.eu/2017/09/15/leer-teclado-rust/">Leer de teclado en Rust</a></li>
<li><a href="https://blog.adrianistan.eu/2017/09/29/crear-ventanas-botones-rust-gtk/">Crear ventanas y botones en Rust con GTK</a></li>
<li><a href="https://blog.adrianistan.eu/2017/12/30/leer-escribir-json-rust-serde/">Leer y escribir JSON en Rust con Serde</a></li>
<li><a href="https://blog.adrianistan.eu/2018/01/01/yew-frontend-rust-angular-react/">Yew, crea webapps al estilo Angular/React en Rust</a></li>
<li><a href="https://blog.adrianistan.eu/2017/04/15/tutorial-rocket-echa-volar-tus-webapps-rust/">Construye un servidor web o una API con Rocket</a></li>
<li><a href="https://blog.adrianistan.eu/2017/01/17/tutorial-piston-programa-juegos-rust/">Programa juegos en Rust con Piston</a> (actualmente recomiendo ggez antes que Piston)</li>
<li><a href="https://blog.adrianistan.eu/2018/02/23/futures-tokio-programar-forma-asincrona-rust/">Futures y Tokio: programaci&oacute;n as&iacute;ncrona</a></li>
<li><a href="https://blog.adrianistan.eu/2017/10/12/diversion-punteros-rust-bloques-unsafe/">Diversi&oacute;n con punteros: bloques unsafe en Rust</a></li>
<li><a href="https://blog.adrianistan.eu/2018/06/07/bindings-rust-c-bindgen/">Bindings entre Rust y C/C++ con bindgen</a></li>
</ol>
<h2>Otros art&iacute;culos sobre Rust en el blog</h2>
<ul>
<li><a href="https://blog.adrianistan.eu/2017/01/27/tutorial-neon-combina-node-js-rust/">Tutorial de Neon</a></li>
<li><a href="https://blog.adrianistan.eu/2017/01/31/tutorial-maud-motor-plantillas-html-rust/">Tutorial de Maud</a></li>
<li><a href="https://blog.adrianistan.eu/2017/06/02/webassembly-tontos-usando-rust/">WebAssembly y Rust</a></li>
<li><a href="https://blog.adrianistan.eu/2017/10/10/cheatsheet-contenedores-rust/">Cheatsheet de contenedores en Rust</a></li>
</ul>
<h2>Instalando Rust</h2>
<p>Rust funciona en una <a href="https://forge.rust-lang.org/platform-support.html">gran variedad de sistemas operativos</a> y emite c&oacute;digo compatible con muchos otros. El compilador oficial de Rust usa LLVM de forma interna, por la que las mejoras que se realicen sobre LLVM mejoran de forma indirecta el compilador de Rust. Otros lenguajes que usan LLVM son: C++ (con el compilador Clang), Julia, Swift, FORTRAN (con el compilador Flang), D (con el compilador LDC), Haskell (a trav&eacute;s de GHC) y muchos m&aacute;s con otros compiladores. Mencionar que tambi&eacute;n existe un <a href="https://gcc.gnu.org/wiki/RustFrontEnd">compilador de Rust bajo la suite GCC</a> pero es experimental y no se recomienda su uso. Para instalar el compilador oficial de Rust visitamos la <a href="https://www.rust-lang.org/">p&aacute;gina oficial de Rust</a>. <a href="https://files.adrianistan.eu/RustWeb.png"><img class="aligncenter size-large wp-image-844" src="https://files.adrianistan.eu/RustWeb-1024x500.png" alt="" width="840" height="410" /></a> Hacemos click en Instalar. Descargamos el archivo y seguimos los pasos en pantalla. <a href="https://files.adrianistan.eu/RustInstall.png"><img class="aligncenter size-large wp-image-845" src="https://files.adrianistan.eu/RustInstall-1024x500.png" alt="" width="840" height="410" /></a></p>
<p>Lo que acabamos de hacer es usar <strong>rustup</strong>. Se trata de un programa que nos ayudar&aacute; mucho y que nos permite instalar varias versiones de Rust en una misma m&aacute;quina as&iacute; como actualizar las mismas. Es una herramienta parecida a rvm de Ruby o nvm de Node.js. <strong>rustup</strong> nos instala <strong>rustc</strong>, <strong>cargo</strong> y <strong>rustdoc</strong>. Comprobemos que Rust se ha instalado correctamente, hagamos un Hola Mundo. Crea un archivo llamado <em>hola.rs</em> (rs es la extensi&oacute;n de los archivos de c&oacute;digo fuente de Rust). Debe contener lo siguiente:</p>
<pre class="lang:rust decode:true">fn main() {
    println!("&iexcl;Hola mundo!");
}
</pre>
<p>Podemos compilar con <strong>rustc</strong>.</p>
<pre class="lang:default decode:true">rustc hola.rs -o hola
</pre>
<p>Como en GCC, la opci&oacute;n -o sirve para especificar el nombre del ejecutable de salida. <strong>println!</strong> es una macro que nos permite escribir en pantalla. Permite realizar interpolaciones, usando llaves. Por ejemplo: <em>println!("Mi nombres es {}","Adri&aacute;n");</em> Equivale a: <em>println!("Mi nombre es Adri&aacute;n");</em></p>
<h2><a href="https://files.adrianistan.eu/RustHelloCLI.png"><img class="aligncenter size-full wp-image-846" src="https://files.adrianistan.eu/RustHelloCLI.png" alt="" width="646" height="126" /></a></h2>
<h2>Usando rustup</h2>
<p>He aqu&iacute; algunos comandos &uacute;tiles para manejar <strong>rustup</strong> de forma efectiva.</p>
<h3>Mostrar que versi&oacute;n est&aacute; actualmente activa</h3>
<pre class="lang:default decode:true">rustup show
</pre>
<h3>Actualizar los entornos de Rust</h3>
<pre class="lang:default decode:true">rustup update
</pre>
<p><a href="https://files.adrianistan.eu/rustup.png"><img class="aligncenter size-full wp-image-847" src="https://files.adrianistan.eu/rustup.png" alt="" width="643" height="340" /></a></p>
<h3>Instalar la versi&oacute;n nightly de Rust</h3>
<pre class="lang:default decode:true">rustup install nightly
</pre>
<h3>Ejecutar esta un comando con un entorno determinado</h3>
<pre class="lang:default decode:true">rustup run nightly rustc hola.rs -o hola
</pre>
<h3>Cambiar el entorno por defecto</h3>
<pre class="lang:default decode:true">rustup default nightly / rustup default stable
</pre>
<h3>A&ntilde;adir una plataforma al entorno (para cross-compiling)</h3>
<pre class="lang:default decode:true">rustup target add arm-linux-androideabi (Android ARM)
</pre>
<p>Con esto acabamos esta parte sobre como configurar el entorno. A continuaci&oacute;n entraremos en profundidad ya en el lenguaje en s&iacute;.</p>]]></description>
                <comments>https://blog.adrianistan.eu/rust-101-tutorial-rust-espanol</comments>
                <pubDate>Mon, 03 Jul 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Concurrencia en Rust</title>
                <link>https://blog.adrianistan.eu/concurrencia-en-rust</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/concurrencia-en-rust</guid>
                <description><![CDATA[Rust destaca por su soporte nativo a la concurrencia. Aquí veremos exactamente qué es lo que hace.<br><h2>Crear threads</h2><br>Todas las funciones relacionadas con threads están en <strong>std::thread</strong> así que primero hemos de importarlo con <strong>use</strong>. Para crear un thread usamos <strong>thread::spawn</strong>, que toma una función como argumento. Esta operación devuelve un <strong>JoinHandler</strong> que puede ser usado para esperar a que finalicen los hilos de ejecución.<br><br><pre class="lang:rust decode:true"><br>use std::thread;<br><br>fn main(){<br>    let handle = thread::spawn(||{<br>        for i in 1..100{<br>            println!(&quot;Attens ou va t'en - France Gall&quot;);<br>        }<br>    });<br>    handle.join();<br>    println!(&quot;Hilo finalizado&quot;);<br>}<br></pre><br><br>Aquí ya se presenta una cuestión interesante, ¿qué pasa si queremos llevar datos del hilo principal al nuevo thread? Si lo intentamos hacer, Rust se quejará. En realidad, tenemos que activar la closure con <strong>move</strong>. Lo que hace es transferir la propiedad del dato a la closure. Con eso podemos procesarlo tranquilamente en el hilo nuevo, siempre y cuando no intentemos acceder a él desde el hilo principal.<br><br><pre class="lang:rust decode:true"><br>use std::thread;<br><br>fn main(){<br>    let v = vec![&quot;Attens ou va t'en - Paul Mauriat&quot;,&quot;Attens ou va t'en - France Gall&quot;];<br>    let handle = thread::spawn(move ||{<br>        for i in v{<br>            println!(&quot;{}&quot;,i);<br>        }<br>    });<br>    handle.join();<br>    println!(&quot;Hilo finalizado&quot;);<br>}<br></pre><br><br><h2>Mensajes</h2><br>Una de las opciones que permite Rust para concurrencia es el paso de mensajes.<br><br><pre class="lang:rust decode:true"><br>use std::thread;<br>use std::sync::mpsc;<br>use std::time::Duration;<br><br><br>fn main(){<br>    let (tx,rx) = mpsc::channel();<br><br>    let h = thread::spawn(move ||{<br>        thread::sleep(Duration::new(5,0));<br>        let val = String::from(&quot;Attens ou va t'en - France Gall&quot;);<br>        tx.send(val).unwrap();<br>    });<br><br>    loop {<br>        if let Ok(msg) = rx.try_recv() {<br>            println!(&quot;{}&quot;,msg);<br>            break;<br>        }<br>    }<br>    // Opción síncrona<br>    //let msg = rx.recv().unwrap();<br>    //println!(&quot;{}&quot;,msg);<br><br>    // Opción Iterable<br>    // for msg in rx{<br>    //     println!(&quot;{}&quot;,msg);<br>    // }<br>}<br></pre><br><br>Tx es el objeto usado para transmitir y Rx para recibir. Tx lo puede tener cualquier hilo para mandar mensajes mientras que Rx está limitado al hilo principal. Existen varias formas de recibir los mensajes. <strong>try_recv</strong> no bloquea el hilo de ejecución, por lo que puede usarse en un bucle con <strong>if-let</strong>. <strong>recv</strong> es síncrono y bloquea el hilo de ejecución si hace falta. Tx también es <strong>Iterable</strong> así que podemos leer los mensajes en un bucle <strong>for-in</strong>.<br><h2>Mutex y Arc</h2><br>Cuando queremos compartir memoria en Rust tenemos que recurrir a una combinación de <strong>Mutex</strong> y <strong>Arc</strong>.<br><br><strong>Mutex</strong> provee de gestión del acceso a memoria. Para acceder al dato es necesario hacer <strong>lock</strong>. No es estrictamente necesario hacer unlock una vez hayamos modificado el dato, pues cuando Rust libere memoria, también hará unlock. Sin embargo, Mutex por sí solo no es suficiente.<br><br><strong>Arc</strong> (Atomic Reference Counter) permite tener datos con múltiples dueños. Esto funciona con una especie de recolector de basura y es útil porque cuando hablamos de hilos podría darse la situación de que el hilo dueño muriese y un hilo con los datos prestados todavía siguiese vivo. Con <strong>clone</strong> conseguimos una copia para mover libremente que en realidad referencia al mismo dato en memoria.<br><br><pre class="lang:rust decode:true"><br>use std::thread;<br>use std::sync::{Mutex,Arc};<br><br>fn main(){<br>    let counter = Arc::new(Mutex::new(42));<br>    let mut handles = vec![];<br><br>    for _ in 0..10 {<br>        let counter = counter.clone();<br>        let handle = thread::spawn(move || {<br>            let mut num = counter.lock().unwrap();<br><br>            *num += 1;<br>        });<br>        handles.push(handle);<br>    }<br><br>    for handle in handles {<br>        handle.join().unwrap();<br>    }<br><br>    println!(&quot;Result: {}&quot;, *counter.lock().unwrap());<br>}<br></pre><br><br>Con esto ya tendremos lo suficiente para manejar concurrencia en Rust. Por supuesto, esto es más complejo que esto y si de verdad quieres aprovechar el potencial al máximo quizá debas revisar la documentación de las traits <strong>Send</strong> y <strong>Sync</strong>.<br><br>&nbsp;<br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/concurrencia-en-rust</comments>
                <pubDate>Mon, 03 Jul 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Funciones y closures en Rust</title>
                <link>https://blog.adrianistan.eu/funciones-closures-rust</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/funciones-closures-rust</guid>
                <description><![CDATA[Rust posee una sintaxis para definir funciones muy similar a la de otros lenguajes. Veamosla en acción.<br><h2>Funciones simples</h2><br>Las funciones se definen con la palabra reservada <strong>fn</strong>. Posteriormente indicamos el nombre de la función y los argumentos de entrada. Si la función devuelve un valor se debe poner una flecha y el tipo del valor de devolución. Para devolver un valor se puede usar <strong>return</strong> o se puede dejar la última línea sin punto y coma. Funciona de forma similar a como vimos con <strong>let</strong> y las asignaciones complejas.<br><br><pre class="lang:rust decode:true"><br>fn suma(a: i32, b: i32) -&gt; i32{<br>    a+b<br>}<br><br>fn main(){<br>    let a = 5;<br>    let b = 42;<br>    let c = suma(a,b);<br>    println!(&quot;{}&quot;,c);<br>}<br></pre><br><br>Rust como tal no admite devolver varios valores a la vez, pero es posible usar tuplas y simularlo.<br><br><pre class="lang:rust decode:true"><br>fn string_length_and_lines(txt: &amp;String) -&gt; (usize,usize) {<br>    (txt.len(),txt.lines().count())<br>}<br><br>fn main(){<br>    let s = String::from(&quot;Europe's Skies - Alexander Rybak\nSuper Strut - Deodato\nEl Cóndor Pasa - Uña Ramos&quot;);<br>    let (length,lines) = string_length_and_lines(&amp;s);<br>    println!(&quot;La lista de canciones tiene una longitud de {} caracteres y {} líneas&quot;,length,lines);<br>}<br></pre><br><br><h2>Funciones de primer nivel</h2><br>En Rust las funciones son elementos de primer nivel, lo que quiere decir que pueden pasarse por argumentos entre funciones. Veamos un ejemplo. En este caso uso un <strong>for-in</strong> que todavía no hemos visto, pero la idea es comprobar como se puede pasar una función a través de un argumento.<br><br><pre class="lang:rust decode:true"><br>fn ladrar(){<br>    println!(&quot;Guau&quot;)<br>}<br><br>fn hacer_n_veces(f: fn(), n: i64){<br>    for _ in 0..n{<br>        f();<br>    }<br>}<br><br>fn main(){<br>    hacer_n_veces(ladrar,42);<br>}<br></pre><br><br>&nbsp;<br><h2>Genericidad</h2><br>Las funciones en Rust admiten genericidad, lo que quiere decir que la misma función es compatible con distintos tipos, siempre respetando las normas. Usualmente, para denominar al tipo genérico se usa T. T se sustituye en cada caso por el tipo en cuestión. Entonces si en el primer parámetro de la función hemos puesto un i32 y T también está en el segundo, este también tiene que ser i32.<br><br><pre class="lang:rust decode:true"><br>fn mayor&lt;T: PartialOrd + Copy&gt;(n: &amp;Vec&lt;T&gt;) -&gt; T{<br>    let mut max: T = n[0];<br>    for &amp;i in n.iter(){<br>        if i &gt; max{<br>            max = i;<br>        }<br>    }<br>    max<br>}<br><br>fn main(){<br>    let v: Vec&lt;i32&gt; = vec![1,2,7,8,123,4];<br>    let max = mayor(&amp;v);<br>    println!(&quot;{}&quot;,max);<br>}<br></pre><br><br>En este ejemplo además vemos genericidad restringida, la cuál es la más habitual. Básicamente T puede ser cualquier tipo pero debe de implementar las <strong>traits</strong> PartialOrd (para hacer la comparación) y <strong>Copy</strong> (elementos que se copian, en vez de moverse). La genericidad en Rust es un campo muy extenso, no obstante para entenderla bien, tendremos que entrar en las <strong>traits</strong>, ya en otro capítulo.<br><h2>Closures</h2><br>Los closures o funciones anónimas son funciones sin nombre que se definen y se consumen. En lenguajes como Python existen bajo la palabra reservada <strong>lambda</strong>. En Rust se definen con barras verticales.<br><br><pre class="lang:rust decode:true"><br>fn main(){<br>    let potencia = |x| x*x;<br>    let pot_5 = potencia(5);<br>    println!(&quot;{}&quot;,pot_5);<br>}<br></pre><br><br>Como es de imaginar podemos usar expresiones más complejas entre llaves.<br><br><pre class="lang:rust decode:true"><br>fn main(){<br>    let potencia_sup = |x| {<br>        let y = x+1;<br>        y*y<br>    }; <br>    let pot_5 = potencia_sup(5);<br>    println!(&quot;{}&quot;,pot_5);<br>}<br></pre><br><br>Con esto sabemos lo suficiente de funciones y genericidad para avanzar a otros temas.]]></description>
                <comments>https://blog.adrianistan.eu/funciones-closures-rust</comments>
                <pubDate>Mon, 03 Jul 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Variables y tipos de datos en Rust</title>
                <link>https://blog.adrianistan.eu/variables-tipos-datos-rust</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/variables-tipos-datos-rust</guid>
                <description><![CDATA[Seguimos en <a href="https://blog.adrianistan.eu/rust-101-tutorial-rust-espanol/">Rust 101, el tutorial de Rust en español</a>, y ahora vamos a ver como trabaja Rust con las variables. En este aspecto no se distingue demasiado de otros lenguajes imperativos, aunque vamos a ver que la inferencia de tipos nos va a ayudar. También vamos a ver tipos de datos más complejos.<br><h2>Declaración de variables</h2><br>En Rust las variables de declaran usando la palabra reservada <strong>let</strong>. El tipo de la variable puede inferirse pero en ocasiones podemos ayudar al compilador indicando el tipo del que queramos que sea la variable.<br><br><pre class="lang:rust decode:true"><br>fn main(){<br>    let x = 42;<br>    let url = &quot;http://adrianistan.eu&quot;;<br><br>    println!(&quot;{}&quot;,url);<br>}<br></pre><br><br>Sin embargo hay una diferencia importante respecto a otros lenguajes y es que aquí las variables serán por defecto inmutables. Tratar de hacer esto:<br><br><pre class="lang:rust decode:true"><br>x += 5;<br></pre><br><br>causará un error de compilación. No obstante, esta limitación puede saltarse aplicando el modificador <strong>mut</strong>. Aunque las variables puedan volverse mutables esto ya nos avisa de que Rust tiene muy presente conceptos de programación funcional más pura (como podemos tener en <a href="http://haskell.org">Haskell</a>, <a href="http://elm-lang.org">Elm</a>, F# u OCaml).<br><br><pre class="lang:rust decode:true"><br>fn main(){<br>    let mut x = 42;<br>    x += 5;<br>    println!(&quot;{}&quot;,x);<br>}<br><br>Para espeficicar un tipo usamos una notación de dos puntos después de &lt;strong&gt;let&lt;/strong&gt;.<br><br><pre class="lang:rust decode:true"><br>fn main(){<br>    let x: i32 = 5; // int 32 bits<br>    let y: f64 = 5; // float 64 bits<br>}<br></pre><br><br><h2>Arrays y vectores</h2><br>Hasta ahora hemos podido ver en acción variables de tipos de datos más o menos primitivos. Ahora vamos a ver dos estructuras de datos similares, aunque diferentes. Digamos para simplificar, que los arrays tienen tamaño fijo y los vectores no.<br><br><pre class="lang:rust decode:true"><br>fn main() {<br>        let mut notas_array: [i32;5] = [0;5];<br>        notas_array[0] = 1;<br>        notas_array[1] = 6;<br><br>        let mut notas_vec: Vec&lt;i32&gt; = vec!();<br>        notas_vec.push(1);<br>        notas_vec.push(6);<br><br>        println!(&quot;Nota 2: {}&quot;,notas_array[1]);<br>        println!(&quot;Nota 2: {}&quot;,notas_vec[1]);<br>}<br></pre><br><br>En este caso inicializamos un array todo a cero de 5 posiciones y también un vector. He dejado las marcas de los tipos, aunque no son necesarias.<br><h2>Constantes, let shadowing y casting</h2><br>Rust soporta constantes con la palabra reservada <strong>const</strong>. La convención es usar SCREAMING_SNAKE_CASE para las constantes. El valor de una constante es inmutable y a diferencia de <strong>let</strong>, no se puede reescribir. ¿Espera, las variables con <strong>let</strong> se pueden reescribir? En efecto, es posible definir una variable posteriormente, de tipo totalmente distinto con el mismo nombre. En este ejemplo vamos a ver el uso de <strong>const</strong> y el uso de <em>let shadowing</em>.<br><br><pre class="lang:rust decode:true"><br>const PI: f64 = 3.14;<br><br>fn main(){<br>    let x = 42;<br>    let x = (x as f64) + PI;<br>    println!(&quot;{}&quot;,x);<br><br>}<br></pre><br><br>Además podemos apreciar como se realiza <strong>casting</strong> en Rust. Se usa la palabra reservada <strong>as</strong> y se indica el tipo al que queremos hacer cast. En este ejemplo de <em>let shadowing</em> podemos apreciar como no solo hemos redefinido x, sino que lo hemos hecho de otro tipo. En la primera línea, x es de tipo i32, en la segunda pasa a ser f64.<br><h2>Tuplas</h2><br>Las tuplas pueden resultar algo novedoso a quien venga de ciertos lenguajes, pero no son elementos realmente nuevos. Podría entenderse una tupla como una estructura sin nombre. Se trata de una especie de array donde cada elemento puede ser de un tipo, pero espeificado de antemano. Lo veremos mejor con ejemplos.<br><br><pre class="lang:rust decode:true"><br>fn main(){<br>    let tupla = (42,&quot;Adrianistán&quot;,true);<br>    let (random,country,has_beers) = tupla;<br>    println!(&quot;{}&quot;,random);<br>    let (_,country,_) = tupla;<br>    println!(&quot;{}&quot;,country);<br>    let has_beers = tupla.2;<br><br>}<br></pre><br><br>Aquí podemos observar como se forma una tupla, compuesta por un entero, un texto y un booleano. La sentencia con let desestructura la tupla y se obtienen 3 variables con los valores de los 3 campos de la tupla. Es posible que en algunas situaciones no nos importe algún campo. Para ignorar algún campo, dejamos una barra baja. También existe la sintaxis punto para acceder a elementos de la tupla. Para ello indicamos el orden dentro de la tupla del elemento que queremos. No obstante, no es tan claro como desestructurar la tupla.<br><h2>Expresiones avanzadas con let</h2><br>let admite cualquier expresión. Esto es una práctica habitual en muchos lenguajes imperativos, sin embargo, las expresiones en Rust son más amplias de lo que vemos a simple vista.<br><br><pre class="lang:rust decode:true"><br>fn main(){<br>    let age = 65;<br>    let x = if age &gt; 17 {<br>        &quot;Mayor de edad&quot;<br>    }else{<br>        &quot;Menor de edad&quot;<br>    };<br>    println!(&quot;{}&quot;,x);<br><br>}<br></pre><br><br>Por ejemplo, una estructura condicional if-else es una expresión. Un ojo avizado podría decir que este código está mal, que falta el punto y coma. Realmente no es así, en Rust si algo no lleva punto y coma se vuelve una expresión. Más bien, al no llevar punto y coma no se vuelve un <em>statement</em>. Esto puede aplicarse a cualquier tipo de código que no deja de ser una expresión.<br><br><pre class="lang:rust decode:true"><br>fn main(){<br>    let age = 65;<br>    let a = 10;<br>    let b = 25;<br>    let x = {<br>        let u = a*b;<br>        u+age<br>    };<br>    println!(&quot;{}&quot;,x);<br><br>}<br></pre><br><br>Y como véis, se puede complicar hasta niveles muy elevados. Al principio puede no ser muy natural, pero es bastante cómodo en muchas situaciones.<br><br>Con esto acabamos nuestra visita a las variables.]]></description>
                <comments>https://blog.adrianistan.eu/variables-tipos-datos-rust</comments>
                <pubDate>Mon, 03 Jul 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Documentación con rustdoc</title>
                <link>https://blog.adrianistan.eu/documentacion-con-rustdoc</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/documentacion-con-rustdoc</guid>
                <description><![CDATA[La documentación es una parte importante y muchas veces olvidada de un proyecto de software. Rust cuenta desde el principio con una herramienta destinada a que escribir documentación sea poco doloroso, se trata de <strong>rustdoc</strong>.<br><h2>Comentarios en Markdown</h2><br>Los comentarios de <strong>rustdoc</strong> empiezan con 3 barras y pueden llevar Markdown. Se escriben encima de la función, estructura, etc que describamos.<br><br><pre class="lang:rust decode:true"><br>/// Perfil almacena los datos del perfil de un usuario en nuestra webapp<br>struct Perfil{<br>    username: String,<br>    password: String,<br>    url: Option&lt;String&gt;<br>}<br><br>impl Perfil{<br>    /// Genera un nuevo Perfil<br>    /// # Ejemplo<br>    /// ```<br>    /// let user = Perfil::new(&quot;The42&quot;,&quot;1234&quot;);<br>    /// ```<br>    pub fn new(u: &amp;str, p: &amp;str) -&gt; Perfil{<br>        Perfil {username: String::from(u), password: String::from(p), url: None}<br>    }<br>}<br></pre><br><br>Mencionar que el código del comentario es sometido a tests también por cargo test. Para generar la documentación basta con escribir <em>cargo doc</em> y Cargo generará la documentación en formato HTML.<br><h2>Consultando documentación</h2><br>El mejor sitio para leer la documentación de Rust es <a href="http://docs.rs">Docs.rs</a>. Docs.rs ejecuta <em>cargo doc</em> a todas las <strong>crates</strong> de Crates.io y las expone al público sin costo.<br><br><a href="https://files.adrianistan.eu/DocsRS.png"><img class="aligncenter size-large wp-image-872" src="https://files.adrianistan.eu/DocsRS-1024x500.png" alt="" width="840" height="410" /></a><br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/documentacion-con-rustdoc</comments>
                <pubDate>Mon, 03 Jul 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Tests en Rust</title>
                <link>https://blog.adrianistan.eu/tests-en-rust</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/tests-en-rust</guid>
                <description><![CDATA[Mientras los programas no puedan verificarse de forma matemática de forma sencilla, la única manera de asegurarse que un programa más o menos funciona es con tests. Rust soporta tests de forma nativa. Gracias a la directiva <strong>#[test]</strong>.<br><h2>Definiendo una función de test</h2><br>Una función de test es cualquiera que lleve la directiva <strong>#[test]</strong>. Normalmente, estos tests se dejan dentro de un módulo con la directiva <strong>#[cfg(test)]</strong>.<br><br><pre class="lang:rust decode:true"><br>fn suma(a: i32,b: i32) -&gt; i32{<br>    a+b<br>}<br><br>#[cfg(test)]<br>mod test{<br><br>    #[test]<br>    fn suma(){<br>        assert_eq!(super::suma(4,5),9);<br>    }<br>}<br></pre><br><br>La macro <strong>assert_eq!</strong> provocará un <strong>panic!</strong> si sus argumentos no coinciden. También es posible hacer fallar los tests llamando a <strong>panic!</strong> manualmente. ¿Cómo se ejecutan estos test te preguntarás? Sencillo, con <strong>cargo test</strong>. Automáticamente Cargo selecciona las funciones de test y las ejecuta todas, generando un informe de tests existosos y tests que han fallado.<br><br><a href="https://files.adrianistan.eu/CargoTest.png"><img class="aligncenter size-full wp-image-869" src="https://files.adrianistan.eu/CargoTest.png" alt="" width="505" height="125" /></a>Obviamente, existen más opciones dentro de los tests. <strong>assert!</strong> comprobará que una expresión sea <strong>true</strong>. <strong>#[should_panic]</strong> se deberá indicar en aquellas funciones de test en lo que lo correcto sea que ocurra un <strong>panic!</strong>. Por otro lado, la trait <strong>Debug</strong> es interesante.<br><br>Es posible ejecutar tests de forma manual, con <em>cargo test NOMBRE_TEST<strong>.</strong></em><br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/tests-en-rust</comments>
                <pubDate>Mon, 03 Jul 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Referencias y préstamos en Rust</title>
                <link>https://blog.adrianistan.eu/referencias-prestamos-en-rust</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/referencias-prestamos-en-rust</guid>
                <description><![CDATA[Continuando con <a href="https://blog.adrianistan.eu/rust-101-tutorial-rust-espanol/">Rust 101, tutorial de Rust en español</a> vamos a adentrarnos en un concepto clave para entender y programar en Rust. Rust es un lenguaje seguro como hemos dicho, esto implica que no puede haber NULLs ni tampoco fugas de memoria. Es decir, tiene que gestionar la memoria de forma eficiente pero dejarla directamente al programador, ya que son una fuente de bugs peligrosa. Algunos lenguajes disponen de recolector de basura (GC, <em>Garbage Collector</em>), que reduce la eficiencia del lenguaje. Go, Swift o Java usan GC. Luego existen lenguajes que dejan la memoria al descubierto, como C y C++. Rust toma un enfoque diferente, ya que no deja la memoria al descubierto ni usa GC. Para ello el compilador realiza una tarea de dueños y préstamos que veremos a continuación.<br><h2>Las reglas</h2><br><ol><br> 	<li>Cada valor en Rust tiene una variable que es su dueña</li><br> 	<li>Un valor solo puede tener un dueño a la vez</li><br> 	<li>Cuando el dueño desaparece, el valor lo hace a su vez, de forma automática</li><br></ol><br>Así que cuando una variable desaparece del <em>entorno</em>, el dato del que era dueño es liberado. Estas reglas vistas así permiten poca flexibilidad en la programación. Es por ello que los dueños pueden prestar los valores.<br><h2>La trait Copy y la trait Clone</h2><br>En primer lugar hay que distinguir distinto comportamiento según el tipo de un valor. Si implementan la trait Copy (la mayoría de tipos primitivos), entonces su comportamiento por defecto es de copia. Son datos en los que la copia es barata y rápida y no influye que existan varias copias de lo mismo. Cualquier cosa que no requería <em>malloc</em> en C puede ser Copy en Rust. Se trata de valores que se almacenan en el stack.<br><br>Otra trait es Clone, que permite hacer copias de datos más complejos, por ejemplo de un vector. Veamos un ejemplo de un código sin Copy pero con Clone.<br><br><pre class="lang:rust decode:true"><br>fn main(){<br>    let s1 = String::from(&quot;Adios - Xavier Cugat&quot;);<br>    let s2 = s1;<br>    println!(&quot;{}&quot;,s1); // ERRROR<br><br>}<br></pre><br><br>Este código da error porque el tipo String no implementa Copy. Entonces la línea <em>let s2 = s1;</em> lo que ha hecho en realidad ha sido mover el valor. Mover significa que le ha transferido el ser dueño del valor de la cadena de texto a s2. Por tanto s1 ya no es dueña del valor y no puede operar con él. Esto pasa en los tipos que no implementan Copy, que transfieren la propiedad a otra variable. Si queremos hacer una <em>copia</em> real, tendremos que recurrir al clonado. El tipo String implementa Clone así que es posible generar otro dato String exactamente igual pero independiente al original.<br><br><pre class="lang:rust decode:true"><br>fn main(){<br>    let s1 = String::from(&quot;Adios - Xavier Cugat&quot;);<br>    let s2 = s1.clone();<br>    println!(&quot;{}&quot;,s1);<br><br>}<br></pre><br><br><h2>Implicaciones</h2><br>Esto tiene una implicación sorprendente y es que pasar una variable tal cual a una función si no es del tipo Copy implica que ¡perdemos el acceso a ese valor! Pongamos un ejemplo:<br><br><pre class="lang:rust decode:true"><br>fn main(){<br>    let s1 = String::from(&quot;Bolero - Maurice Ravel &quot;);<br>    f(s1);<br>    println!(&quot;{}&quot;,s1);<br>}<br></pre><br><br>Este código da error. Al hacer la llamada a la función f hemos transferido la propiedad del valor de s1 a f. Por ello, cuando intentamos hacer el print no vamos a poder ya que s1 ya no es dueña de la cadena de texto. Para solucionar estos problemas tenemos los préstamos.<br><h2>Prestando en Rust</h2><br>Rust permite prestar los valores de los que una variable es dueña de dos maneras: solo lectura o con escritura. Siempre ha de cumplirse la norma de que solo puede haber una con permiso de escritura pero puede haber infinidad con permiso de lectura. Y nunca se pueden dar las dos cosas a la vez.<br><br>El préstamo de lectura se realiza con el operador &amp;, que lo que hace es una referencia de lectura al valor. La variable sigue siendo sueña así del valor, solo lo ha prestado y entrega una referencia.<br><br><pre class="lang:rust decode:true"><br>fn main(){<br>    let s1 = String::from(&quot;Bolero - Maurice Ravel &quot;);<br>    f(&amp;s1);<br>    println!(&quot;{}&quot;,s1);<br>}<br></pre><br><br>Este código ya sí funciona y podemos ver en pantalla <a href="https://www.youtube.com/watch?v=r30D3SW4OVw" target="_blank" rel="noopener">Bolero - Maurice Ravel</a>.<br><br>Si queremos hacer el préstamo en modo escritura, hemos de usar &amp;mut. Sin entrar muchos detalles, así funcionaría un préstamo de escritura.<br><br><pre class="lang:rust decode:true"><br>fn f(s: &amp;mut String){<br>    s.push_str(&quot; &amp; Adios - Xavier Cugat&quot;);<br>}<br><br>fn main(){<br>    let mut s1 = String::from(&quot;Bolero - Maurice Ravel&quot;);<br>    f(&amp;mut s1);<br>    println!(&quot;{}&quot;,s1);<br>}<br></pre><br><br>Así tenemos por pantalla la frase <em>Bolero - Maurice Ravel &amp; <a href="https://www.youtube.com/watch?v=noWtmrsEQfw">Adios - Xavier Cugat</a></em>.<br><br>Este tipo de sintaxis, a priori compleja, nos ayudará mucho con la concurrencia pero de momento ya hemos visto suficiente.<br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/referencias-prestamos-en-rust</comments>
                <pubDate>Mon, 03 Jul 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Structs, traits y POO en Rust</title>
                <link>https://blog.adrianistan.eu/structs-traits-poo-rust</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/structs-traits-poo-rust</guid>
                <description><![CDATA[En este apartado vamos a tratar uno de los temas más densos de Rust, que son las estructuras, los traits y la orientación a objetos en general. Antes de empezar conviene hacerse una pregunta.<br><h2>¿Es Rust orientado a objetos?</h2><br>La respuesta no es unánime. Bajo ciertas definiciones lo es, bajo otras no. Lo cierto es que aquí vamos a suponer que lo es, aunque no implementa <strong>clases</strong> y <strong>herencia</strong>.<br><h2>Structs en Rust</h2><br>Antes de avanzar a los objetos, vamos a ir con los <strong>structs</strong> o estructuras, similares a las que podemos tener en C. Las estructuras son datos agrupados por clave-valor.<br><br><pre class="lang:rust decode:true"><br>struct Punto{<br>    x: i32,<br>    y: i32<br>}<br><br>struct Rectangulo{<br>    origen: Punto,<br>    ancho: i32,<br>    alto: i32<br>}<br><br>fn main(){<br>    let p = Punto {x: 50, y: 50};<br>    println!(&quot;Punto X: {}&quot;,p.x);<br>}<br></pre><br><br>Se crean con la sintaxis de llave. Hasta aquí todo nada raro. Pero en Rust, las estructuras pueden y deben llevar asociado métodos. Estos métodos se definen en las <strong>traits</strong>. Piensa en ello como si fuesen interfaces. No obstante, algunas <strong>trait</strong><strong>s</strong> pueden tener implementación automática. Estas <strong>traits</strong> las llamaremos <strong>derivables</strong>. Usando la anotación <strong>#[derive()]</strong> podemos indicarle a una estructura que derive de una <strong>trait</strong>. ¿Recuerdas las traits <strong>Clone</strong> y <strong>Copy</strong>? Son de este tipo y salvo que queramos cosas más especiales, con hacer una anotación nos bastará. ¿No te parece que Punto es una estructura que debe ser copiada en vez de movida? Pues simplemente añadimos las <strong>traits</strong> correspondientes.<br><br><pre class="lang:rust decode:true"><br>#[derive(Copy,Clone)]<br>struct Punto{<br>    x: i32,<br>    y: i32<br>}<br><br>struct Rectangulo{<br>    origen: Punto,<br>    ancho: i32,<br>    alto: i32<br>}<br><br>fn main(){<br>    let p = Punto {x: 50, y: 50};<br>    println!(&quot;Punto X: {}&quot;,p.x);<br>}<br></pre><br><br>Así, hemos modificado el comportamiento del <strong>struct</strong>. Además, hemos ganado métodos en la estructura. Ahora podemos hacer p.clone() y obtener una copia. Esto ha sido gracias a la trait <strong>Clone</strong>, la cuál es necesaria para los elementos que quieran ser <strong>Copy</strong>. Esto además significa que podremos pasar la estructura a todas las funciones con genericidad cuya única condición sea que implementemos <strong>Copy.</strong><br><h2>Métodos asociados a la estructura</h2><br>Hemos visto que <strong>Clone</strong> ha añadido un método a la estructura, como si fuese una clase de la que hubiérmamos heredado. Nosotros también podemos definir métodos asociados a la estructura. Se hace con la palabra reservada <strong>impl</strong>.<br><br><pre class="lang:rust decode:true"><br>#[derive(Copy,Clone)]<br>struct Punto{<br>    x: i32,<br>    y: i32<br>}<br><br>struct Rectangulo{<br>    origen: Punto,<br>    ancho: i32,<br>    alto: i32<br>}<br><br>impl Rectangulo{<br>    pub fn area(&amp;self) -&gt; i32{<br>        self.ancho*self.alto<br>    }<br>}<br><br>fn main(){<br>    let p = Punto {x: 50, y: 50};<br>    println!(&quot;Punto X: {}&quot;,p.x);<br>    let rectangulo = Rectangulo {origen: p,ancho: 20, alto: 20};<br>    println!(&quot;Área: {}&quot;,rectangulo.area());<br>}<br></pre><br><br>Aquí hemos definido el método area asociado a Rectangulo. Es importante destacar que aunque parezca una definición de clase, aquí no hay herencia posible y tampoco hay constructores. El primer argumento de la función area es <strong>self</strong>, o lo que es lo mismo, una referencia a la propia estructura.<br><br>Además vemos <strong>pub</strong>, lo que indica que es un método público.<br><br>Como hemos dicho, hay ciertas <strong>traits</strong> que se autoimplementan, pero otras requieren de código de integración. Vamos a implementar dos <strong>traits</strong> en Rectangulo, <strong>PartialOrd</strong> para las comparaciones y <strong>std::fmt::Display</strong> para definir que se debe mostrar cuando hagamos <strong>println!</strong> a Rectangulo. <strong>PartialOrd</strong> a su vez requiere <strong>PartialEq</strong> presente.<br><br><pre class="lang:rust decode:true"><br>use std::cmp::Ordering;<br><br>#[derive(Copy,Clone)]<br>struct Punto{<br>    x: i32,<br>    y: i32<br>}<br><br>struct Rectangulo{<br>    origen: Punto,<br>    ancho: i32,<br>    alto: i32<br>}<br><br>impl Rectangulo{<br>    pub fn area(&amp;self) -&gt; i32{<br>        self.ancho*self.alto<br>    }<br>}<br><br>impl PartialEq for Rectangulo{<br>    fn eq(&amp;self,other: &amp;Rectangulo) -&gt; bool{<br>        self.area() == other.area()<br>    }<br>}<br><br>impl PartialOrd for Rectangulo{<br>    fn partial_cmp(&amp;self, other: &amp;Rectangulo) -&gt; Option&lt;Ordering&gt;{<br>        if self.area() == other.area() {<br>            Some(Ordering::Equal)<br>        }else if self.area() &gt; other.area() {<br>            Some(Ordering::Greater)<br>        }else{<br>            Some(Ordering::Less)<br>        }<br>    }<br>}<br><br>impl std::fmt::Display for Rectangulo{<br>    fn fmt(&amp;self,f: &amp;mut std::fmt::Formatter) -&gt; std::fmt::Result{<br>        write!(f,&quot;Origen: ({},{}) - Area: {}&quot;,self.origen.x,self.origen.y,self.area())<br>    }<br><br>}<br><br>fn main(){<br>    let p = Punto {x: 50, y: 50};<br>    println!(&quot;Punto X: {}&quot;,p.x);<br>    let r1 = Rectangulo {origen: p,ancho: 20, alto: 20};<br>    println!(&quot;{}&quot;,r1);<br>    let r2 = Rectangulo {origen: Punto{x: 3, y: 4}, ancho: 30, alto: 30};<br>    println!(&quot;{}&quot;,r2);<br>    if r1 &lt; r2 {<br>        println!(&quot;r2 es más grande&quot;);<br>    }<br><br>}<br></pre><br><br>Aquí ya estamos viendo código Rust en estado puro. Sin embargo todavía no hemos visto como crear <strong>traits</strong> nosotros mismos.<br><h2>Traits</h2><br>Una <strong>trait</strong> es similar a una interfaz y deben de ser usadas mediante la composición, no la herencia. Piensa como si el objeto estuviese hecho con piezas de puzzle. Unas le dan una funcionalidad, otras otra y junt con código propio, se consigue crear un objeto completo. Así funciona la POO en Rust.<br><br>Imagina que existe la <strong>trait</strong> Pintable. Podremos crear métodos que admitan mediante genericidad restringida solo a objetos que implementen Pintable. Y si queremos que Rectangulo sea uno de ellos solo hay que implementar las funciones de Pintable sin definir. Pero Pintable puede incluir métodos ya hecho que funcionan con independencia del objeto en cuestión. Esos métodos pueden ser reescritos si creemos conveniente.<br><br><pre class="lang:rust decode:true"><br>impl Pintable for Rectangulo{<br>    fn pintar(&amp;self) {<br>        // pintar rectangulo<br>    }<br>}<br><br>trait Pintable{<br>    fn pintar(&amp;self);<br>    fn limpiar(){<br>        // limpiar pantalla<br>    }<br>}<br></pre><br><br>Con esto acabamos un capítulo muy importante del tutorial. En el siguiente hablaremos de algo que ya se ha dejado ver como es Option y Result y trataremos la gestión de errores.<br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/structs-traits-poo-rust</comments>
                <pubDate>Mon, 03 Jul 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Box, Rc y RefCell, punteros inteligentes en Rust</title>
                <link>https://blog.adrianistan.eu/box-rc-refcell-punteros-inteligentes-rust</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/box-rc-refcell-punteros-inteligentes-rust</guid>
                <description><![CDATA[Hasta ahora hemos trabajado con tipos primitivos, con estructuras o con tipos de la librería estándar como <strong>String</strong> o <strong>Vec</strong>. Sin embargo, ¿si queremos implementar algo similar a <strong>String</strong> como lo haríamos? Aquí entran en juego, los punteros inteligentes, punteros con capacidades extra. Los más importantes son <strong>Box,</strong> <strong>Rc y RefCell</strong>.<br><h2>Box, reservar memoria en el heap</h2><br>Box es parecido a malloc de C. Reservan memoria en la parte alta de la memoria. El uso principal de Box en Rust es el de implementar estructuras de datos cíclicas o recursivas.<br><br><pre class="lang:rust decode:true"><br>fn main() {<br>    let n = Box::new(42);<br>    println!(&quot;n = {}&quot;, n);<br>}<br></pre><br><br>No es muy útil usarlo así. Solo compensa usarlo en situaciones donde es necesario que la variable ocupe un tamaño fijo, que a priori es indeterminado.<br><h2>Rc, ¡viva la multipropiedad!</h2><br><strong>Rc</strong> es un puntero inteligente algo más interesante. Permite que un dato sea propiedad de varias variables a la vez. Funciona con un mecanismo de recolector de basura. La clave está en que cuando hagamos <strong>clone</strong> de un <strong>Rc</strong> no obtendremos una copia exacta, sino una referencia más al dato original. Esto permite ahorrar memoria. Veamos en un ejemplo, como Regex se comparte entre varias variables. Se trata del mismo Regex, en ningún momento ocurre una duplicidad en memoria.<br><br><pre class="lang:rust decode:true"><br>extern crate regex;<br><br>use regex::Regex;<br>use std::rc::Rc;<br><br><br>fn main() {<br>    let r = Rc::new(Regex::new(r&quot;([A-Za-z0-9])([.]|[_]|[A-Za-z0-9])+@gmail.com&quot;).unwrap());<br>    if r.is_match(&quot;pucela_segunda@gmail.com&quot;) {<br>        println!(&quot;Correo válido de Gmail&quot;);<br>    }else{<br>        println!(&quot;Correo no válido de Gmail&quot;);<br>    }<br><br>    let puntero = r.clone();<br>    println!(&quot;Reference Count: {}&quot;,Rc::strong_count(&amp;puntero));<br>    puntero.is_match(&quot;perro@gmail.com&quot;);<br>    r.is_match(&quot;_pepe@gmail.com&quot;);<br>}<br></pre><br><br>La línea <em>Reference Count</em> nos dice cuantas variables tienen ahora mismo acceso a ese dato. <strong>Rc</strong> funciona a la perfección en un solo hilo, pero si quieres hacer lo mismo entre hilos debes usar <strong>Arc</strong>. <strong>Rc</strong> por sí mismo, solo permite lecturas, es cuando lo juntamos con <strong>RefCell</strong> cuando obtenemos soporte de escritura.<br><h2>RefCell, saltándonos las normas</h2><br>En primer lugar, <strong>RefCell</strong> es muy interesante y poderoso, pero no es recomendable usarlo si se pueden usar otros métodos. Digamos que <strong>RefCell</strong> permite llevar las normas del compilador de Rust sobre préstamos y dueños al runtime. Esto puede provocar crasheos así que normalmente se usa con <strong>Rc</strong> que previene estas situaciones.<br><br><pre class="lang:rust decode:true"><br>use std::rc::Rc;<br>use std::cell::RefCell;<br><br>fn main(){<br>    let n = Rc::new(RefCell::new(42));<br>    let x = n.clone();<br>    let y = n.clone();<br>    *x.borrow_mut() += 10;<br>    *y.borrow_mut() += 10;<br>    println!(&quot;N: {:?}&quot;,n.borrow());<br>}<br></pre><br><br>El resultado de este código es 62. Por tanto, hemos conseguido que distintas variables pudiesen mutar el dato.<br><br>Con esto ya hemos visto los punteros inteligentes más importantes de Rust. Existe alguno más como <strong>Cell</strong> que sin embargo, no es tan usado.<br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/box-rc-refcell-punteros-inteligentes-rust</comments>
                <pubDate>Mon, 03 Jul 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Cargo y módulos en Rust</title>
                <link>https://blog.adrianistan.eu/cargo-modulos-rust</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/cargo-modulos-rust</guid>
                <description><![CDATA[El software va creciendo poco a poco en un proyecto y es vital que esté bien organizado. Rust incluye un potente sistema de módulos para manejar estas situaciones y <strong>Cargo</strong> nos ayuda a gestionar las dependencias del proyecto.<br><h2>Módulos</h2><br>Los módulos se definen con la palabra reservada <strong>mod</strong> y por defecto todo es privado. Es necesario indicar <strong>pub</strong> para hacer esos campos públicos. Con <strong>use</strong> podemos acortar la manera con la que hacemos referencia a los componentes del módulo. Así pues, <strong>use</strong> es similar a <strong>using namespace</strong> de C++.<br><br><pre class="lang:rust decode:true"><br>mod network{<br>    pub fn version() -&gt; String{<br>        String::from(&quot;Network 1.0.0&quot;)<br>    }<br>    pub mod server{<br>        fn private_thing(){<br><br>        }<br>        pub fn public_thing(){<br><br>        }<br>    }<br>}<br><br>fn main() {<br>    println!(&quot;{}&quot;,network::version());<br>    {<br>        use network::server::public_thing;<br>        public_thing();<br>    }<br>}<br></pre><br><br>Los módulos resultan una opción más interesante si tenemos múltiples archivos. Por norma general, si un módulo se define en el fichero de entrada del compilador, este se busca en dos sitios:<br><ul><br> 	<li>Un fichero <em>nombre_del_modulo.rs</em></li><br> 	<li>Un fichero <em>nombre_del_modulo/mod.rs</em> (opción recomendada para módulos de varios archivos)</li><br></ul><br>Por lo que el código anterior puede sustituirse por esto en el fichero principal:<br><br><pre class="lang:rust decode:true"><br>mod network;<br><br>fn main() {<br>    println!(&quot;{}&quot;,network::version());<br>    {<br>        use network::server::public_thing;<br>        public_thing();<br>    }<br>}<br></pre><br><br>Y esto en un fichero <em>network.rs</em><br><br><pre class="lang:rust decode:true"><br>pub fn version() -&gt; String{<br>    String::from(&quot;Network 1.0.0&quot;)<br>}<br>pub mod server{<br>    fn private_thing(){<br><br>    }<br>    pub fn public_thing(){<br><br>    }<br>}<br></pre><br><br>Podemos usar <strong>use-as</strong> para modificar el nombre con el que se importa una función.<br><br><pre class="lang:rust decode:true"><br>mod network;<br><br>fn main() {<br>    println!(&quot;{}&quot;,network::version());<br>    {<br>        use network::server::public_thing as super_thing;<br>        super_thing();<br>    }<br>}<br></pre><br><br>&nbsp;<br><h2>Crates</h2><br>El código en Rust se organiza en <strong>crates</strong>, que son colecciones de módulos que se distribuyen. Piensa en ello como si fuesen <strong>gemas</strong> de Ruby o <strong>librerías</strong> de C++. Las <strong>crates</strong> se pueden compartir con la gente. El mayor repositorio de <strong>crates</strong> de la comunidad Rust es <a href="https://crates.io/">crates.io</a>.<br><br><a href="https://files.adrianistan.eu/CratesIO.png"><img class="aligncenter size-large wp-image-866" src="https://files.adrianistan.eu/CratesIO-1024x500.png" alt="" width="840" height="410" /></a>Las <strong>crates</strong> se pueden generar con <strong>rustc</strong> o con <strong>cargo</strong>, aunque es habitual usar la segunda opción. Vamos a ver primero como usaríamos una <strong>crate</strong> externa. En este caso voy a usar <strong>regex</strong>. Sé que en esta parte no vas a saber como descargar <strong>regex</strong> y usarla, pero voy a explicar el código.<br><br><pre class="lang:rust decode:true"><br>extern crate regex;<br><br>use regex::Regex;<br><br>fn main() {<br>    let r = Regex::new(r&quot;([A-Za-z0-9])([.]|[_]|[A-Za-z0-9])+@gmail.com&quot;).unwrap();<br>    if r.is_match(&quot;pucela_segunda@gmail.com&quot;) {<br>        println!(&quot;Correo válido de Gmail&quot;);<br>    }else{<br>        println!(&quot;Correo no válido de Gmail&quot;);<br>    }<br>}<br></pre><br><br>Básicamente se usa <strong>extern crate</strong> para importar una <strong>crate</strong> externa, en este caso regex. Con <strong>use</strong> lo único que hacemos es acortar la manera de llamar a las funciones. Si no estuviese podríamos poner perfectamente <em>regex::Regex::new</em> en vez de <em>Regex::new</em> y funcionaría.<br><h2>Cargo</h2><br><strong>Cargo</strong> es una herramienta que cumple dos funciones dentro del ecosistema Rust. Por un lado es un gestor de dependencias y por otro lado gestiona también las compilaciones.<br><br>En su parte de gestor de dependencias nos permite especificar que <strong>crates</strong> necesita el proyecto para compilar, su versión y si la <strong>crate</strong> lo soporta, con qué características activadas.<br><br>En su parte de gestor de compilaciones, realiza la compilación con <strong>rustc</strong> y añade las flags pertinentes al compilador. También se le pueden especificar cosas más complejas, aunque en Rust no suele ser habitual tener configuraciones de compilación excesivamente complejas.<br><br>Todo lo relativo a Cargo se define en el archivo <strong>Cargo.toml</strong>. Que tiene una estructura similar a esta:<br><br><pre class="lang:rust decode:true"><br>[package]<br>name = &quot;super_app&quot;<br>version = &quot;0.1.0&quot;<br>authors = [&quot;Adrián Arroyo Calle&quot;]<br><br>[dependencies]<br>regex = &quot;0.2.2&quot;<br></pre><br><br>En la sección <strong>dependencies</strong> puedes añadir línea por línea la <strong>crate</strong> que te haga falta. Consulta la documentación de cada <strong>crate</strong> para esto, pues puede haber variaciones.<br><h2>Comandos básicos de Cargo</h2><br><h3>Crear un proyecto con Cargo</h3><br><em>cargo new --bin mi_proyecto </em># si queremos que sea ejecutable<br><br><em>cargo new mi_crate</em> # si queremos que sea una crate<br><h3>Compilar y ejecutar</h3><br><em>cargo run</em><br><br><em>cargo build</em> # solo compilar<br><br><em>cargo build --release</em> # compilar en modo Release<br><br>Cargo automáticamente se encarga de obtener las dependencias en la fase de compilación.<br><h3>Instalar aplicaciones</h3><br><em>cargo install APLICACION</em><br><br>Muchas herramientas de pueden distribuir a través de Cargo. Por ejemplo, <strong>Racer</strong>, un programa que sirve de autocompletado para IDEs como Eclipse o Visual Studio.<br><h3>Ejecutar tests</h3><br><em>cargo test</em><br><h3>Generar documentación</h3><br><em>cargo doc</em><br><h2>Plugins de Cargo</h2><br>Cargo es extensible gracias a plugins. Algunos interesantes son <strong>Clippy</strong>, <strong>cargo-audit, rustfmt,</strong> ...]]></description>
                <comments>https://blog.adrianistan.eu/cargo-modulos-rust</comments>
                <pubDate>Mon, 03 Jul 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Estructuras de control en Rust</title>
                <link>https://blog.adrianistan.eu/estructuras-control-rust</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/estructuras-control-rust</guid>
                <description><![CDATA[Los lenguajes de programación imperativos no serían nada sin sus estructuras de control. En Rust no son muy diferentes respecto a otros lenguajes. Ya hemos visto de pasada que existe <strong>if-else</strong> y <strong>for-in</strong>. Ahora veamos la lista entera junto con un operador muy importante en Rust, <strong>match</strong>.<br><h2>Condicionales, if, if-else, if-let</h2><br>En Rust <strong>if-else</strong> funciona de forma parecida a otros lenguajes, quizá lo único destacable sea que no hacen falta los paréntesis.<br><br><pre class="lang:rust decode:true"><br>fn main(){<br>    let ano = 1998;<br>    if ano &gt; 1975{<br>        println!(&quot;Deberías escuchar a Ennio Morricone&quot;);<br>    }else if ano &gt; 1600{<br>        println!(&quot;Deberías escuchar a Bach&quot;);<br>    }else{<br>        println!(&quot;Deberías escuchar a tu monje benedictino más próximo&quot;);<br>    }<br>}<br></pre><br><br>En realidad la sintaxis del <strong>if</strong> no es tan simple, ya que existe una versión vitaminada, pensada para tratar con Option y Result. Se trata de <strong>if-let</strong>, una construcción tomada de Swift y que sirve para ejecutar una porción de código sólo si el valor existe (en el caso de Option) o no ha habido fallos (con Result).<br><br><pre class="lang:rust decode:true"><br>fn main(){<br>    let url = Some(&quot;https://blog.adrianistan.eu&quot;);<br><br>    if let Some(url) = url {<br>        println!(&quot;{}&quot;,url);<br>    }<br><br>    let magic_box: Result&lt;String,String&gt; = Ok(String::from(&quot;Aquí no hay nada&quot;));<br>    if let Ok(magic_box) = magic_box {<br>        println!(&quot;{}&quot;,magic_box);<br>    }<br><br>    let url: Option&lt;&amp;'static str&gt; = None;<br>    if let Some(url) = url {<br>        println!(&quot;{}&quot;,url);<br>    }<br>    <br>    let magic_box: Result&lt;String,String&gt; = Err(String::from(&quot;No tienes la llave de la caja&quot;));<br>    if let Ok(magic_box) = magic_box {<br>        println!(&quot;{}&quot;,magic_box);<br>    }<br><br>}<br></pre><br><br><h2>Condicionales, match</h2><br><strong>match</strong> es un potente condicional en Rust basado en concordancia de patrones. Podemos pensar en ello como un <strong>switch</strong> vitaminado, pero realmente es capaz de hacer muchas más cosas. Como curiosidad, mencionar que ciertos lenguajes de programación como Haskell usan la concordancia de patrones muy a menudo y consiguen obtener un código muy claro.<br><br>Para demostrar el uso del match primero vamos a construir una tupla con cuatro valores.<br><br><pre class="lang:rust decode:true"><br>fn buscar(t: (bool,&amp;'static str,&amp;'static str,i32)) -&gt; &amp;'static str{<br>    match t {<br>        (true,&quot;Mike Oldfield&quot;,&quot;The Bell&quot;,1992) =&gt; &quot;Gran canción&quot;,<br>        (true,&quot;Mike Oldfield&quot;,_,1992) =&gt; &quot;Tubular Bells II probablemente&quot;,<br>        (true,autor,_,_) =&gt; autor,<br>        (false,..) =&gt; &quot;Disco no existente&quot;,<br>        _ =&gt; &quot;¿Qué me has pasado exactamente?&quot;<br>    }<br>}<br><br>fn main(){<br>    let tupla = (true,&quot;Mike Oldfield&quot;,&quot;The Bell&quot;,1992);<br><br>    let busqueda = buscar(tupla);<br>    println!(&quot;{}&quot;,busqueda);<br><br>}<br></pre><br><br><strong>match</strong> realiza comparaciones de arriba a abajo y devuelve la expresión después de la flecha. Como os habréis imaginado, al ser una expresión directamente no se pone punto y coma y no hace falta hacer return en la función de ningún tipo.<br><br>La primera condición del match es clara: si es exactamente igual la tupla a la descrita se devuelve <em>Gran canción</em>. En la segundo vemos una barra baja. Ya hemos hablado de él, pero vamos a recordarlo. Se trata del operador que usamos cuando algo nos da igual. Similar a un asterisco en la terminal de Linux.<br><br>El tercer caso es interesante pues nos permite comprobar concordancia en el primer campo y a su vez nos permite extraer el valor del segundo campo, que queda en la variable autor.<br><br>El cuarto caso es para los perezosos, simplemente con que cumpla una condición, el resto ni nos molestamos en escribir. Usando los dos puntos hacemos que todos los false vayan al mismo sitio.<br><br>Por último pero no menos importante, tenemos la barra baja. Cualquier tupla que no haya caído antes en otro caso caerá en este. (En este ejemplo no puede llegar a funcionar, pues con los casos anteriores ya hemos cubierto el espectro. El compilador de Rust nos avisa de que no hay posibilidad de que alguna vez se alcance ese código).<br><br>Existen más opciones, una barra horizontal <strong>|</strong> nos servirá para hacer un OR. Si trabajamos con números, <strong>1..10</strong> representa un intervalo en el que caen los valores admitidos.<br><br>Por descontando, <strong>match</strong> no solo funciona en tuplas, sino que tiene una gran variedad de usos.<br><h2>Bucles, loop y while</h2><br>Estos bucles funcionan prácticamente igual que en otros lenguajes. <strong>loop</strong> es un bucle infinito que solo se rompe con <strong>break</strong>. <strong>while </strong>tiene una condición que se comprueba al inicio.<br><br><pre class="lang:rust decode:true"><br>fn main(){<br>    let mut n = 0;<br>    loop {<br>        if n &gt; 100{<br>            break;<br>        }<br>        println!(&quot;No soy pesado&quot;);<br>        n+=1;<br>    }<br><br>    while n &lt; 200{<br>        println!(&quot;No soy pesado&quot;);<br>        n+=1;<br>    }<br>}<br></pre><br><br>Es conveniente usar los bucles <strong>while</strong> cuando tenemos una bandera o una condición inesperada, pero nunca para recorrer elementos de una lista o similares.<br><h2>Bucles, for-in</h2><br>En Rust se toma el ejemplo de lenguajes como Python y se deja atrás el bucle <strong>for</strong> de C. El bucle <strong>for-in</strong> recorre todos los elementos de un <strong>Iterable</strong>. Un <strong>Iterable</strong> es cualquier cosa que implemente la trait <strong>Iterable</strong>, y que como en lenguajes como Python y JavaScript con los generadores, puede no ser necesariamente una lista, simplemente algo sobre lo que se pueda iterar. Estos elementos son perezosos y solo se calculan cuando son necesarios, abriendo la posibilidad a listas infinitas. Los vectores son <strong>Iterables</strong>, veamos como usarlos.<br><br><pre class="lang:rust decode:true"><br>fn main(){<br>    let v = vec![&quot;Haskell&quot;,&quot;Elm&quot;,&quot;Python&quot;,&quot;C++&quot;,&quot;JavaScript&quot;,&quot;Rust&quot;,&quot;Java&quot;];<br>    for s in v{<br>        println!(&quot;{}&quot;,s);<br>    }<br>}<br></pre><br><br>Como vemos, es bastante simple. Este programa imprime cada lenguaje de programación por pantalla. El código no es muy complicado.<br><br>¿Y si resulta que quiero iterar N veces y no tengo un <strong>Iterable</strong> a mano? Similar al <strong>range</strong> de Python, existe una sintaxis compacta y clara.<br><br><pre class="lang:rust decode:true"><br>fn main(){<br>    let n = 100;<br>    for i in 0..n{<br>        println!(&quot;{}&quot;,i);<br>    }<br>}<br></pre><br><br>La sintaxis de dos puntos permite iterar en un rango en Z cerrado por la izquierda y abierto por la derecha. Muchas cosas de la librería estándar son iterables y cualquier array o vector puede serlo, así que este será probablemente el bucle que más uses.<br><br>Con esto acabamos las estructuras de control, listos para entrar en temas más avanzados.<br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/estructuras-control-rust</comments>
                <pubDate>Mon, 03 Jul 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Gestión de errores en Rust, Option y Result</title>
                <link>https://blog.adrianistan.eu/gestion-errores-rust-option-result</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/gestion-errores-rust-option-result</guid>
                <description><![CDATA[¿Conoces el error del billón de dólares? Tony Hoare es un reputado estudioso de la ciencia de la computación. Cuando se encontraba diseñando ALGOL W, se le ocurrió incorporar la referencia a NULL. Sin embargo, los errores informáticos que ha propiciado su existencia han supuesto pérdidas económicas superiores al billón de dólares. Es por ello que él mismo denominó a su creación, el error del billón de dólares.<br><br>El uso de NULL puede traer muchas consecuencias negativas, pero como el propio Hoare dijo: <em>era tan fácil de implementar que no pude resistirme</em>. Además, usarlo es fácil. Existen lenguajes sin NULL desde hace tiempo pero su uso no es muy frecuente y ciertamente, añaden complejidad a la programación. Rust soluciona esto con dos objetos muy sencillos de utilizar, Option y Result.<br><h2>Option y Result</h2><br>Ambos son objetos que sirven para llevar cualquier otro valor. Hemos de desempapelarlos para sacar el verdadero valor, si es que existe. Option en realidad es un <strong>enum</strong> con dos posibles valores: <strong>Some</strong> y <strong>None</strong>. Result también es un <strong>enum</strong> con dos posibles valores: <strong>Ok</strong> y <strong>Err</strong>.<br><h2>¿Cuándo usar Option y cuándo usar Result?</h2><br>Esta duda es muy frecuente ya que funcionan de manera muy similar. La regla general es que Result tiene que usarse con errores y si no se tratan, cerrar el programa, mientras que Option lleva cosas que pueden estar o no, pero su inexistencia no conlleva un error.<br><h2>Uso</h2><br>Option es algo más sencilla de usar.<br><br><pre class="lang:rust decode:true"><br>let opt: Option&lt;i32&gt; = Some(42);<br>let opt: Option&lt;i32&gt; = None;<br></pre><br><br>En Result hay que indicar el tipo del valor correcto y del valor de error que se comunique.<br><br><pre class="lang:rust decode:true"><br>let operacion_peligrosa: Result&lt;i32,String&gt; = Ok(42);<br>let operacion_peligrosa: Result&lt;i32,String&gt; = Err(String::from(&quot;La operación ha fallado&quot;));<br></pre><br><br><h2>if-let</h2><br>Option y Result pueden integrarse con estructuras de control para un manejo idomático de estas situaciones. Un ejemplo es <strong>if-let</strong>. Imagina un perfil de una aplicación web. Hay campos obligatorios, como usuario y contraseña, pero otros no, como la página web.<br><br><pre class="lang:rust decode:true"><br>struct Perfil{<br>    username: String,<br>    password: String,<br>    url: Option&lt;String&gt;<br>}<br><br>impl Perfil{<br>    fn new(u: String, p: String) -&gt; Self{<br>        Perfil {username: u, password: p, url: None}<br>    }<br>}<br><br>fn main(){<br>    let mut p1 = Perfil::new(String::from(&quot;aarroyoc&quot;),String::from(&quot;1234&quot;));<br>    let mut p2 = Perfil::new(String::from(&quot;The42&quot;),String::from(&quot;incorrect&quot;));<br><br>    p1.url = Some(String::from(&quot;https://blog.adrianistan.eu&quot;));<br><br>    for perfil in [p1,p2].iter() {<br>        let url = perfil.url.clone();<br>        if let Some(url) = url{<br>            println!(&quot;URL: {}&quot;,url);<br>        }<br>    }<br><br>}<br></pre><br><br><h2>try!</h2><br><strong>try!</strong> es una macro que permite manejar con mayor claridad las operaciones que puedan generar muchos errores. <strong>try!</strong> solo se puede usar en funciones que devuelvan Result. Básicamente, <strong>try!</strong> devuelve inmediatamente el Err correspondiente si la operación resultante no ha sido exitosa, permitiendo una salida antes de tiempo y un código mucho más limpio.<br><br><pre class="lang:rust decode:true"><br>fn peligro() -&gt; Result&lt;i32,String&gt;{<br>    Err(String::from(&quot;Operación inválida&quot;))<br>}<br><br>fn funcion_error() -&gt; Result&lt;i32,String&gt;{<br>    let n = try!(peligro()); // aquí ya se sale de la función<br>    Ok(n)<br>}<br><br>fn main(){<br>    let n = funcion_error().unwrap();<br>    <br>}<br></pre><br><br>La macro <strong>try!</strong> es tan usada dentro de Rust que dispone de azúcar sintáctico, el símbolo de interrogación <strong>?</strong>.<br><br><pre class="lang:rust decode:true"><br>fn peligro() -&gt; Result&lt;i32,String&gt;{<br>    Err(String::from(&quot;Operación inválida&quot;))<br>}<br><br>fn funcion_error() -&gt; Result&lt;i32,String&gt;{<br>    let n = peligro()?;<br>    Ok(n)<br>}<br><br>fn main(){<br>    let n = funcion_error().unwrap();<br>}<br></pre><br><br>&nbsp;<br><h2>Unwrap</h2><br>Vamos a observar las operaciones de desempapelado que existen para Option y Result. La más básica es <strong>unwrap</strong>. Intenta desempapelar el valor, si resulta que el Result/Option era un Err/None, el programa crashea.<br><br><strong>unwrap_or</strong> intenta desempapelar y si el valor no existe, se sustituye por el valor indicado en el or.<br><br><strong>unwrap_or_else</strong> es similar a <strong>unwrap_or</strong> pero toma una función como parámetro. Así es posible crear una cadena de intentos.<br><br><pre class="lang:rust decode:true"><br>fn main(){<br>    // let n = function_error().unwrap(); crash<br>    let n = function_error().unwrap_or(7);<br>    let n = funcion_error().unwrap_or_else(|t|{<br>        7<br>    });<br>    println!(&quot;{}&quot;,n);<br>}<br></pre><br><br>Existen muchas más opciones, como <strong>and, </strong><strong>expect</strong> o iterar sobre un Option/Result.<br><h2>panic!</h2><br>Si nos gustan los crasheos (!) podemos forzarlos con la macro <strong>panic!</strong>. Crashea el programa con el mensaje que indiquemos<br><br><pre class="lang:rust decode:true"><br>fn main(){<br>    panic!(&quot;Adiós muy buenas&quot;);<br>}<br></pre><br><br>Y con esto hemos visto lo suficiente del manejo de errores en Rust.]]></description>
                <comments>https://blog.adrianistan.eu/gestion-errores-rust-option-result</comments>
                <pubDate>Mon, 03 Jul 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Compra ordenadores seminuevos y ayuda al medio ambiente</title>
                <link>https://blog.adrianistan.eu/compra-ordenadores-seminuevos-ayuda-al-medio-ambiente</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/compra-ordenadores-seminuevos-ayuda-al-medio-ambiente</guid>
                <description><![CDATA[Todas las persona que habitamos en la Tierra dejamos un impacto. Muchas veces no nos damos cuenta, pero ahí lo dejamos. Dejamos una huella para la posteridad. En sociedades como la nuestra gastamos mucho más de lo necesario. Un buen paso para combatir este desperdicio de recursos es adquirir tecnología de segunda mano. Mucha gente piensa que este tipo de tecnología estará desfasada, no obstante, con la información y ayuda adecuada, puede aguantar todavía muchos años más.<br><br>Los <a href="https://welagon.com/ordenadores/ordenadores-segunda-mano.html">ordenadores seminuevos</a> son un claro ejemplo de un producto que puede aguantar muchos años más. Estos vienen frecuentemente de usuarios profesionales y exigentes con sus equipos. Cuando vamos al detalle nos encontramos de que son equipos con prestaciones muy decentes para el mercado actual y a unos precios considerablemente más bajos. Son productos de gama alta de la generación anterior, que en la generación actual, sin llegar a ser tope de gama, no dejan de ser un buen partido. Pero no solo eso, sino que ayudaremos a reducir el impacto medioambiental de nuestras compras.<br><br><a href="https://files.adrianistan.eu/optiplex-desktop-7010-overview2.jpg"><img class="aligncenter size-full wp-image-838" src="https://files.adrianistan.eu/optiplex-desktop-7010-overview2.jpg" alt="" width="358" height="442" /></a><br><br>En el caso de los ordenadores de segunda mano, una ventaja que hay respecto a otras tecnologías es el gran impacto que tiene el software. Esto significa que los componentes de hardware (lo físico del ordenador) están muy condicionados por la capa de software (programas) que se ejecutan por encima. Si tenemos un sistema actualizado, con las últimas versiones de los programas, tendremos un equipo seguro, fiable y con nuevas características de poco en poco que mejoren lo ya existente. Actualmente, los sistemas operativos como Windows han reducido su nivel de consumo y no será un problema de ningún tipo tener la última versión de Windows 10 en estos equipos.<br><br>No obstante, yo en lo personal recomiendo siempre que sea posible usar un sistema operativo Linux de forma habitual. Suelen estar mejor actualizados y su rendimiento es mejor, incluso en equipos más antiguos. El único requisito para dar el paso es asegurarse que todos los programas que vayamos a usar o bien sean compatibles con Linux o bien existan reemplazos. Si acostumbras a usar Adobe Photoshop, quizá Linux no sea para ti; si sin embargo solo realizas tareas de ofimática con Word y Excel, quizá puedas usar sin problemas LibreOffice, presente en Linux.<br><br><a href="https://files.adrianistan.eu/libreoffice.jpg"><img class="aligncenter size-full wp-image-839" src="https://files.adrianistan.eu/libreoffice.jpg" alt="" width="600" height="450" /></a><br><br>A muchos nos puede asaltar la duda: "Vale, muy bien, los ordenadores seminuevos son más baratos, plenamente funcionales y ayudan al medio ambiente, ¿pero y si tengo algún fallo con el equipo?". Efectivamente, se trata de una duda razonable. Por suerte, actualmente existen vendedores que no solo ofrecen equipos de segunda mano sino que se hacen cargo de la garantía desde la fecha en que compramos el producto. Como si fuese un equipo nuevo.<br><br>En definitiva, esta alternativa a comprar un equipo nuevo, es más respetuosa con el medio ambiente, con equipos igual de potentes y fiables que muchos en el mercado y a un precio reducido. En particulares, la diferencia se nota. Pero en empresas donde se compran ordenadores en más cantidades y muchas veces no se necesitan grandes equipos para la realización de tareas ofimáticas, la compra de ordenadores seminuevos puede suponer un gran ahorro.<br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/compra-ordenadores-seminuevos-ayuda-al-medio-ambiente</comments>
                <pubDate>Wed, 28 Jun 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Cuando &#x27;bad_style&#x27; se convierte en algo ofensivo</title>
                <link>https://blog.adrianistan.eu/cuando-bad_style-se-convierte-algo-ofensivo</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/cuando-bad_style-se-convierte-algo-ofensivo</guid>
                <description><![CDATA[Voy a hacer un breve comentario sobre un asunto que ha levantado polémica dentro del mundo de Rust. Se trata de <a href="https://github.com/rust-lang/rust/issues/41646">esta issue</a>. Básicamente viene a decir que usar el término <em>bad</em> para referirse a el estilo de programación de Rust no estándar es incorrecto. Que puede ofender a la gente. Particularmente lo encuentro jocoso, pero hay mucha gente que no. La discusión se ha bloqueado (alguien diría que censurado) y parece que finalmente se ha adoptado la decisión de eliminar <em>bad</em> por ser ofensivo.<br><br>Con gran acierto el autor profetizó que esta issue traería mucho <a href="https://es.wikipedia.org/wiki/Ley_de_Parkinson_de_la_trivialidad">bikeshedding</a>, aunque no únicamente en el nombre de la sustitución. Sin embargo me parece que este tema merece que le dedique un poco de tiempo.<br><br>En primer lugar se culpa de que usar <em>bad</em> hace a la gente sentirse mal, que es un término pasivo-agresivo y que echa las culpas al programador por usarlo. ¿Qué hace <em>bad_style</em>? Es un lint del compilador de Rust que emite advertencias si usamos una convención distinta la habitual. Por ejemplo,si usamos camelCase en vez de snake_case obtendremos un warning por este linter.<br><br>A mí en particular en condiciones normales me daría igual el término exacto de este linter, pero los argumentos que han dado me han parecido muy soprendentes. Supuestamente si algo tiene la palabra <em>bad</em> podría haber gente que se sienta mal y le afecte, que los principiantes se sientan bienvenidos en una comunidad con un lenguaje que no te hace usar la palabra bad cuando vas a saltarte la convención (y además les desmotiva).<br><br>El caso es que me resulta difícil entender como el flag para que el compilador no ejecute el susodicho linter pueda herir los sentimientos de alguien. El compilador no es algo personal, no te está insultando a ti, es una herramienta, y sí, existen cosas malas. Si llevamos esto al extremo el compilador no debería dar errores sino "sintaxis malointerpretada" Programar en <em>bad_style</em> es una mala práctica porque reduce la interoperabilidad y legibilidad entre proyectos. No importa si es camelCase o snake_case pero todos estamos de acuerdo que si un lenguaje adopta una forma por convención es mucho mejor adherirse a ella, podremos leer código ajeno más rápidamente y mejor. Si realmente te afecta emocionalmente usar <em>bad_style</em> porque es <em>bad</em> entonces quizá tu problema sea otro, mucho más grave y que necesites ayuda de un profesional.<br><br>Por otra parte la discusión presenta la más que alarmante falta de visión más allá del mundo angloparlante que presenta mucha gente. No es que les ignoran, es que creen que ciertas cosas que son aplicables a EEUU son aplicables al resto de culturas del mundo.<br><br>Fue en otra conversación donde se propuso cambiar <em>stupid</em> por <em>foolish</em> o <em>rude</em>, ya que era menos ofensivo. Como si estúpido en español fuese un gran insulto. Para mí estúpido puede llegar a ser un insulto tierno. Sin embargo el grupo de trabajo dedicado a revisar las palabras prefería abolir sus usos con la excusa de que nadie pudiera sentirse ofendido. Yo no lo sabía pero existen detectores automáticos de estas palabras y muchas otras que no deben ser usadas. Para mí todo esto me parece sacado de contexto de forma excesivo y aunque no es algo único a la informática, es aquí donde me lo encuentro más a menudo. Sobre este asunto estoy bastante de acuerdo con Slavoj Zizek.<br><br>https://www.youtube.com/watch?v=5dNbWGaaxWM<br><br>No sé donde quedó aquello de que no había libertad de expresión si no había libertad de ofender a la gente como decían Orwell o Chomsky. Cada vez más las comunidades que dicen ser <em>welcoming</em> me alejan más. Dije antes que este asunto no solo ha pasado en el mundo de Rust. Efectivamente, en Node.js también eliminaron <em>suicide</em> del módulo <em>cluster</em> porque era ofensivo también. No pudieron eliminar <em>kill</em> y <em>host</em> porque eran ya términos históricos de UNIX, aunque les hubiese gustado.]]></description>
                <comments>https://blog.adrianistan.eu/cuando-bad_style-se-convierte-algo-ofensivo</comments>
                <pubDate>Tue, 20 Jun 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Tutorial de CouchDB</title>
                <link>https://blog.adrianistan.eu/tutorial-couchdb</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/tutorial-couchdb</guid>
                <description><![CDATA[Hoy vengo a hablar de <a href="http://couchdb.apache.org">CouchDB</a>, una base de datos que no es precisamente nueva ni es precisamente famosa, pero que en mi opinión tiene unas características únicas.<br><br><a href="https://files.adrianistan.eu/couchDB.png"><img class="aligncenter size-full wp-image-823" src="https://files.adrianistan.eu/couchDB.png" alt="" width="800" height="600" /></a><br><br>En primer lugar vayamos con la teoría. Apache CouchDB es una base de datos NoSQL que almacena documentos JSON y tiene una potente API REST. Está programada en Erlang y es un proyecto Apache desde 2008. Usa JavaScript para la operativa interna. Visto este resumen uno podría pensar que estamos ante un clon de <a href="http://mongodb.com">MongoDB</a> (a pesar de que CouchDB es mucho más antigua) pero no. CouchDB parte de una filosofía distinta, ser la base de datos de la era de la web y se nota mucho como toma decisiones que la alejan de las bases de datos relacionales y de las bases de datos NoSQL más populares.<br><h2>La API REST</h2><br>Una parte fundamental de CouchDB es la API REST, que es la única API que dispone. CouchDB de este modo puede funcionar de dos formas:<br><ul><br> 	<li>En un modelo tradicional, con una servidor que haga las peticiones. Este servidor puede estar programado en cualquier cosa (Java, PHP, Node.js, C#, Python, Ruby, Elixir,...) y que no es muy distinto a como nos conectamos por ejemplo a MongoDB o a MySQL.</li><br> 	<li>En un modelo innovador donde la base de datos está conectada directamente a Internet y no hace falta ningún servidor que gestione la comunicación. Este modelo da lugar a las CouchApps, aplicaciones CRUD pero sin servidor propiamente dicho, solo base de datos y un alojamiento estático de archivos. A través de JavaScript en el lado del cliente podemos obtener la información, conectándose directamente el navegador con la base de datos.</li><br></ul><br>La posibilidad de hacer esto último es una de las mayores ventajas de CouchDB en mi opinión, ya que según el proyecto, esto puede ahorrar mucho tiempo y prevenir muchos posibles bugs en operaciones tontas. El desafío de este artículo será crear una aplicación en CouchDB que pudiera simular a una aplicación CRUD cualquiera. Esta aplicación reducirá costes respecto a una tradicional ya que eliminamos la parte del servidor, solo mantenemos la base de datos (y un proxy).<br><h2>Instalando CouchDB</h2><br>CouchDB está en casi todas las distros Linux. Es posible que no esté disponible la última versión, pero para lo que vamos a hacer no es necesario. Yo voy a usar CouchDB 1.4, una versión bastante antigua, pero es la que tiene Debian en sus repos. En Ubuntu tenéis CouchDB 1.6 y en la página oficial está la versión 2.0<br><h2>Creando una base de datos</h2><br>En CouchDB las bases de datos son cubos sin fondo. Podemos arrojar documentos JSON sin control.<br><br>Vamos a usar la API REST para crear la base de datos "supermercado".<br><br><pre class="lang:default decode:true"><br>curl -X PUT http://127.0.0.1:5984/supermercado<br></pre><br><br>Podemos comprobar que la base de datos ha sido creada con GET<br><br><pre class="lang:default decode:true"><br>curl -X GET http://127.0.0.1:5984/supermercado<br></pre><br><br>En CouchDB ciertas rutas que comienzan por barra baja son especiales. Por ejemplo si queremos pedir una lista de todas las bases de datos podemos hacer GET a _all_dbs.<br><br><pre class="lang:default decode:true"><br>curl -X GET http://127.0.0.1:5984/_all_dbs<br></pre><br><br><a href="https://files.adrianistan.eu/CouchDB-AllDbs.png"><img class="aligncenter size-full wp-image-824" src="https://files.adrianistan.eu/CouchDB-AllDbs.png" alt="" width="651" height="160" /></a><br><br>Para borrar la base de datos usaríamos DELETE<br><br><pre class="lang:default decode:true"><br>curl -X DELETE http://127.0.0.1:5984/supermercado<br></pre><br><br>Pero no lo hagas todavía. Si has pensado en lo que hemos hecho te estarás alarmando. Hemos creado una base de datos, por HTTP y no se nos ha pedido ninguna autorización ni nada. Por defecto CouchDB es inseguro ya que arranca en un modo llamado <em>Admin Party</em>, pero rápidamente veremos que si vamos a exponer CouchDB a Internet vamos a necesitar seguridad, y CouchDB la trae. Pero antes, vamos a trabajar con documentos.<br><h2>Insertando un documento</h2><br>Insertar un documento es tan fácil como tirar documentos a la papelera (bueno, quizá no tan fácil).<br><br><pre class="lang:default decode:true"><br>curl -X PUT http://127.0.0.1:5984/supermercado/UUID -d '{&quot;nombre&quot;: &quot;Manzana&quot;, &quot;tipo&quot; : &quot;fruta&quot;, &quot;precio&quot; : 5}'<br></pre><br><br>Donde UUID es un UUID. Si no tienes un UUID a mano, CouchDB te ofrece uno a través de _uuids.<br><br><pre class="lang:default decode:true"><br>curl -X GET http://127.0.0.1:5984/_uuids?count=10<br></pre><br><br>En realidad no es estrictamente necesario poner un UUID, pero es la convención habitual. Una vez hagamos el PUT obtendremos de respuesta un JSON con tres campos muy importantes. Ok para decirnos si la operación fue bien o no, _id, con el ID del documento (es el UUID de antes) y el _rev. Este último campo, nos indica con que versión del documento hemos interactuado, en nuestro caso, era la primera así que hay un 1 delante. Esto es muy importante en CouchDB y tiene que ver con como gestiona la concurrencia. CouchDB es eventualmente consistente. Quiere decir que en una situación de escritura y lectura simultáneas, la base de datos no se bloquea sino que puede mandar distintas versione de los documentos. Cuando editemos un documentos tendremos que indicar  sobre que versión hacemos la modificación, y si algún otro cliente se nos adelantó y modificó el documento antes, tendremos que basarnos en su revisión para que sea aceptada por la base de datos. Hay que tener que CouchDB no es un sistema de control de versiones, las revisiones antiguas pueden desaparecer sin previo aviso. Esta característica, la de ser eventualmente consistente, también facilita mucho su desempeño en clústeres, pero no vamos a entrar en ello. De momento hay que saber que en CouchDB, a diferencia de otras bases de datos, para actualizar un documento necesitaremos su ID y su revisión.<br><br>Para ver el documento conociendo su ID hacemos un simple GET<br><br><pre class="lang:default decode:true"><br>curl -X GET http://127.0.0.1:5984/supermercado/UUID<br></pre><br><br>Nos devuelve el documento entero junto con las propiedades _id y _rev.<br><h2>Actualizando un documento</h2><br>El precio de las manzanas ha cambiado, vamos a actualizarlo. El proceso es muy simple, es como insertar un documento pero añadimos la revisión sobre la que queremos hacerlo. Es importante mencionar que CouchDB no preserva nada en las actualizaciones. Si quieres dejar campos sin tocar, tendrás que volver a subirlos.<br><br><pre class="lang:default decode:true"><br>curl -X PUT http://127.0.0.1:5984/supermercado/98c003b03bc8aa87cb05983d1c000713 -d '{&quot;_rev&quot;: &quot;1-eba25568090eb2dfffad770b55147a67&quot;,&quot;nombre&quot;: &quot;Manzana&quot;, &quot;tipo&quot; : &quot;fruta&quot;, &quot;precio&quot; : 4}'<br></pre><br><br>Para borrar documentos necesitas usar DELETE indicando el número de revisión.<br><br><pre class="lang:default decode:true"><br>curl -X DELETE http://127.0.0.1:5984/supermercado/98c003b03bc8aa87cb05983d1c000713?rev=2-298fdb46385be60609b242b3e5cc3566<br></pre><br><br>(pero no lo hagas)<br><h2>Vistas</h2><br>Todo muy bonito, pero, ¿cómo accedo a la información? En SQL usaríamos SELECT, en bases como MongoDB lanzaríamos un find. En CouchDB hay que usar vistas, que se programan en JavaScript y siguen un esquema MapReduce (que recordemos, funciona muy bien en paralelo). Esto se puede hacer de varias formas. Se puede usar Fauxton, una interfaz web de CouchDB, se puede usar Erica, una herramienta para facilitar la creación y mantenimiento de CouchApps o se puede hacer con la API REST, ya que las vistas se definen en documentos JSON también.<br><br>Tenemos que crear un Design Doc. Los design doc son muy útiles y permiten por ejemplo validar documentos que vayan a entrar en la base de datos (podemos definir schemas así) o definir las vistas. Un design doc simple es así. Definimos una vista llamada all con una operación map.<br><br>&nbsp;<br><br><pre class="lang:js decode:true"><br>{<br>    &quot;views&quot; : {<br>        &quot;all&quot; : {<br>            &quot;map&quot; : &quot;function(doc){ emit(null,doc); }&quot;<br>        }<br>    }<br>}<br></pre><br><br>Si lo tenemos guardado en un archivo JSON<br><br><pre class="lang:default decode:true"><br>curl -H &quot;Content-Type: application/json&quot; --data @super.json -X PUT http://127.0.0.1:5984/supermercado/_design/super<br></pre><br><br>Ahora vamos a ejecutar la vista<br><br><pre class="lang:default decode:true"><br>curl -X GET http://127.0.0.1:5984/supermercado/_design/super/_view/all<br></pre><br><br>Obtenemos un objeto JSON con todos los documentos que contiene la base de datos supermercado. Por supuesto es posible crear distintos documentos de diseño, con distintas vistas cada uno.<br><br>Las vistas solo las debería poder crear el administrador de la base de datos, por lo que al contrario que en otras bases de datos, el cliente no podrá realizar operaciones que no hayan sido definidas antes. En realidad, no es del todo cierto, ya que en CouchDB 2.0 se añadió Mango, un lenguaje de consulta declarativo que permite realizar cierta operativa sin usar las vistas, pero no es tan potente.<br><br>Otro ejemplo:<br><br><pre class="lang:js decode:true"><br>{<br>    &quot;views&quot; : {<br>        &quot;by-price&quot; : {<br>            &quot;map&quot; : &quot;function(doc){ emit(doc.precio,doc); }&quot;<br>        }<br>    }<br>}<br></pre><br><br>Esta vista puede ser llamada con parámetros<br><br><pre class="lang:default decode:true"><br>curl -X GET http://127.0.0.1:5984/supermercado/_design/super/_view/by-price?descending=true&amp;amp;limit=1<br></pre><br><br>CouchDB realiza la operación de ordenado por su cuenta con el valor del key que devolvemos.<br><h2>La base de datos en el salvaje oeste</h2><br>Ahora vamos a ver como exponer CouchDB al salvaje Internet sin comprometer seguridad. En primer lugar toca hablar de los usuarios. Como hemos dicho, CouchDB arranca en el modo <em>Admin Party</em>. Este modo será desactivado en cuanto creemos un usuario como admin. CouchDB soporta además usuarios tradicionales, una característica muy útil que veremos después como usar.<br><br>Para crear un usuario como admin lanzamos esta petición:<br><br><pre class="lang:default decode:true"><br>curl -X PUT http://127.0.0.1:5984/_config/admins/aarroyoc -d '&quot;MiPassword&quot;'<br></pre><br><br>A partir de ahora ya no estamos en una admin party y si intentamos crear una base de datos veremos que CouchDB nos deniega el acceso. Sin embargo, otras tareas como subir documentos a bases de datos ya existentes todavía funcionan.<br><br>Para protegernos mejor debemos saber los tipos de autorización que soporta CouchDB. En primer lugar soporta HTTP Basic Auth, un método en el que mandamos el usuario y la contraseña en texto plano en cada petición. Esto no es para nada seguro, pero combinado con SSL no es mala opción. Sin embargo sigue sin ser la mejor solución para los expertos en seguridad. CouchDB también acepta autenticación por cookies. Las cookies duran 10 minutos por defecto y se generan haciendo una petición a _session con formato de formulario HTTP.<br><br><pre class="lang:default decode:true"><br>curl -vX POST http://127.0.0.1:5984/_session -H &quot;Content-Type:application/x-www-form-urlencoded&quot; -d &quot;name=aarroyoc&amp;amp;password=MiPassword&quot;<br></pre><br><br>Que nos devuelve una cookie válida para operar en la base de datos<br><br><a href="https://files.adrianistan.eu/Cookie.png"><img class="aligncenter size-full wp-image-826" src="https://files.adrianistan.eu/Cookie.png" alt="" width="657" height="388" /></a>Existen plugins que permiten gestionar desde CouchDB autorizaciones más diversas, como por ejemplo OpenID o OAuth2, pero pueden complicarnos mucho el desarrollo.<br><br>Los usuarios normales se guardan en la base de datos especial _users y se les puede asignar roles, con permisos para cada base de datos. Por defecto, se pueden crear cuentas de usuario nuevas de forma tan simple como así:<br><br><pre class="lang:default decode:true"><br>curl -X PUT http://127.0.0.1:5984/_users/org.couchdb.user:USUARIO -d '{&quot;name&quot; : &quot;USUARIO&quot;, &quot;password&quot; : &quot;MiPassword&quot;, &quot;type&quot; : &quot;user&quot;, &quot;roles&quot; : []}'<br></pre><br><br>Es importante seguir el esquema <em>org.couchdb.user:USUARIO</em> para los documentos. Los roles solo los puede ajustar una cuenta de admin, así que en las peticiones anónimas deberá ir vacío.<br><br>Si en algún momento quieres saber cuántos usuarios tiene la base de datos, puedes usar la vista predefinida <em>_all_docs</em>.<br><br><pre class="lang:default decode:true"><br>curl -X GET http://usuarioadmin:contraseñaadmin@127.0.0.1:5984/_users/_all_docs<br></pre><br><br>Estos documentos de usuarios por defecto son privados pero podemos mostrar cierta información para el resto de usuarios, sobretodo si añadimos campos extra a los documentos<br><br><pre class="lang:default decode:true"><br>curl -X PUT http://usuarioadmin:contraseñaadmin@127.0.0.1:5984/_config/couch_httpd_auth/public_fields -d '&quot;name&quot;'<br></pre><br><br>Hará visible el campo name a todas las peticiones GET anónimas a ese documento de usuario.<br><h2>Accesos a la base de datos</h2><br>Ahora vamos a ver como controlar las lecturas y escrituras de una base de datos en concreto. Aquí creo que CouchDB tiene limitaciones y que debe ser algo en lo que enfocarse en futuras versiones pues es un control muy limitado. Si fuese una base de datos interna no sería mucho problema, pero teniendo en cuenta que va a estar expuesta a Internet sería necesario algo más potente. Eso no quiere decir que no se puedan hacer cosas, pero vamos a tener que tirar de funciones JavaScript internas.<br><br>Por un lado, tenemos un documento especia en cada base de datos llamado _security. Este contiene listas de admins y miembros. Las listas pueden estar vacías, contener usuarios o contener roles. En caso de que la lista de miembros este vacía se considera que todos los usuarios son miembros (incluido los anónimos). Los miembros pueden leer y escribir todos los archivos. Esto es muy insuficiente. Por poner un caso completo, yo necesitaba:<br><ul><br> 	<li>Que todos los usuarios pudieran leer los documentos (incluido anónimos)</li><br> 	<li>Que todos los usuarios registrados pudieran crear documentos</li><br> 	<li>Que solo se pudiesen modificar los documentos creados por cada usuario</li><br></ul><br>Esto se puede hacer si obligamos a los documentos a que especifiquen un dueño. Todo lo podemos hacer en esta función:<br><br><pre class="lang:js decode:true"><br>function (new_doc, old_doc, userCtx){<br>    if(!userCtx.name)<br>        throw({forbidden: &quot;Not authorized&quot;});<br><br>    if(!new_doc.owner)<br>        throw({forbidden: &quot;Plase, set an owner&quot;});<br><br>    if(new_doc.owner != userCtx.name)<br>        throw({forbidden: &quot;Owner in document should be the same as user&quot;})<br><br>    if(old_doc!=null)<br>        if(old_doc.owner != userCtx.name &amp;&amp; userCtx.roles.indexOf(&quot;_admin&quot;) &lt; 0)<br>            throw({forbidden: &quot;Not your document&quot;});<br>    return;<br>}<br></pre><br><br>Esta función puede ser subida a CouchDB con la API HTTP dentro de un design doc. El design doc quedaría así:<br><br><pre class="lang:js decode:true"><br>{<br>        &quot;_rev&quot; : &quot;7-670f7428b5a5afb25ec61382024f0733&quot;,<br>        &quot;views&quot; : {<br>                &quot;all&quot; : {<br>                        &quot;map&quot; : &quot;function(doc){ emit(doc.name,doc); }&quot;<br>                },<br>                &quot;by-price&quot; : {<br>                        &quot;map&quot; : &quot;function(doc){ emit(doc.precio,doc); }&quot;<br>                }<br>        },<br>        &quot;validate_doc_update&quot;:&quot;function (new_doc, old_doc, userCtx){\n    if(!userCtx.name)\n        throw({forbidden: \&quot;Not authorized\&quot;});\n\n    if(!new_doc.owner)\n\tthrow({forbidden: \&quot;Plase, set an owner\&quot;});\n\n    if(new_doc.owner != userCtx.name)\n        throw({forbidden: \&quot;Owner in document should be the same as user\&quot;})\n\n    if(old_doc!=null)\n        if(old_doc.owner != userCtx.name &amp;&amp; userCtx.roles.indexOf(\&quot;_admin\&quot;) &lt; 0)\n            throw({forbidden: \&quot;Not your document\&quot;});\n    return;\n} \n&quot;<br>}<br><br></pre><br><br>Por supuesto, en _rev irá la revisión que toque. Este sistema es más potente de lo que parece ya que podemos controlar todos los detalles de la operación. Es por ello que muchas veces la función validate_doc_update es la más compleja de los documentos de diseño. Para ayudarme un poco, estoy usando Node.js para leer archivos JavaScript y pasarlos a una cadena JSON válida.<br><h2>CORS y SSL</h2><br>Vamos a lanzar CouchDB a Internet. En primer lugar, un par de detalles. CouchDB idealmente vivirá en un dominio separado a la web estática. Para que las peticiones JavaScript funcionen hay que activar CORS. También vamos a proteger los datos así como los usuarios y contraseñas transmitidos. Todo esto podemos hacerlo del tirón con nginx:<br><br>&nbsp;<br><br><pre class="lang:default decode:true"><br>server {<br>        listen 443 ssl http2;<br>        ssl_certificate /etc/letsencrypt/live/alejandria.adrianistan.eu/fullchain.pem;<br>        ssl_certificate_key /etc/letsencrypt/live/alejandria.adrianistan.eu/privkey.pem;<br>        server_name alejandria.adrianistan.eu;<br>        location / {<br>                add_header Access-Control-Allow-Origin *;<br>                proxy_pass http://localhost:5984;<br>                proxy_redirect off;<br>                proxy_set_header Host $host;<br>                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;<br>                proxy_set_header X-Forwarded-Ssl on;<br>        }<br><br>        location /_utils/ {<br>                return 404;<br>        }<br>}<br></pre><br><br>¡Listo! Ya tenemos una instancia de CouchDB directamente conectada a Internet. Como podéis comprobar es una alternativa muy interesante en muchos casos. Sin embargo, ciertas cosas son un poco diferentes y más complejas de realizar. Mencionar que CouchDB también es posible usarse con un servidor de por medio en un esquema tradicional. En este caso, podemos usar la cuenta de admin para crear bases de datos personales para cada usuario. Este enfoque es necesario si necesitamos que los datos sean privados o queremos afinar más que roles tienen acceso a una base datos. CouchDB anima a crear tantas bases de datos como necesitemos, varias por usuario si es necesario, para satisfacer nuestros requerimientos. Esto no tiene gran impacto en el rendimiento tal y como está diseñado.<br><br>Puede hacerse de forma ligera, con una pequeña app que gestione el alta de usuarios y una vez creada la base de datos y ajustados los permisos se puede usar la API REST de CouchDB desde el lado del cliente con total normalidad, con protecciones de lectura y escritura más precisas.<br><br>Este modelo que hemos presentado es ideal para aplicaciones cuya base de datos principal sea pública y accesible y con unos pequeños ajustes puede adaptarse a muchas situaciones. Espero que os haya gustado el artículo. Nos hemos dejado cosas como la replicación entre nodos, los ficheros binarios o attachments ydos complementos a las vistas llamados show y lists, que permiten renderizar HTML o XML entre otras cosas, con los datos de salida de una vista si es necesario.<br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/tutorial-couchdb</comments>
                <pubDate>Thu, 15 Jun 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>WebAssembly para tontos (usando Rust)</title>
                <link>https://blog.adrianistan.eu/webassembly-tontos-usando-rust</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/webassembly-tontos-usando-rust</guid>
                <description><![CDATA[<blockquote>Este post usa Emscripten para generar WebAssembly con Rust. Hoy día Emscripten no es necesario, pero no he podido actualizar el tutorial</blockquote><br>Una de las cosas que más me han sorprendido del mundo web en estos años fue el proyecto WebAssembly. Un proyecto que pretendía traer un bytecode unificado para navegadores. Un proyecto que permitiría compilar prácticamente cualquier lenguaje a la web sin necesidad de tocar JavaScript.<br><br>El proyecto surgía de iniciativas fracasadas de Google (PNaCl) y de Mozilla (asm.js). Pero a este proyecto se unieron Microsoft y Apple, por lo que la compatibilidad estaba asegurada.<br><br>WebAssembly es un bytecode (como el de Java o el de .NET) que puede ser ejecutado por un navegador, cada navegador implementa su máquina virtual. También es posible usarlo en otros entornos relacionados con el mundo JavaScript como Node.js. Sin embargo entre los objetivos de WebAssembly está no estar atado a JavaScript, por lo que la especificación puede ser implementada por cualquier otro tipo de entorno. Actualmente WebAssembly no tiene recolector de basura y no tiene acceso directo a las Web API. No obstante, sigue siendo un proyecto interesante. Vamos a ver como usar WebAssembly con Rust.<br><h2>Instalando Rust y Emscripten</h2><br>Instala Rust, la versión estable es compatible con lo que queremos. Recomiendo usar Rustup.<br><pre id="5b07" class="graf graf--pre graf-after--p">curl <a class="markup--anchor markup--pre-anchor" href="https://sh.rustup.rs" target="_blank" rel="noopener noreferrer" data-href="https://sh.rustup.rs">https://sh.rustup.rs</a> -sSf | sh</pre><br>El paso clave es instalar un nuevo target, el target de WASM32 (WebAssembly de 32 bits).<br><pre id="832e" class="graf graf--pre graf-after--p">rustup target add wasm32-unknown-emscripten</pre><br>Por supuesto también hace falta instalar Emscripten.<br><br><a href="https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html#download-and-install">Descarga la versión portable de Emscripten aquí</a>. Descomprime y ejecuta<br><pre class="graf graf--pre graf-after--p">source .<strong class="markup--strong markup--pre-strong">/</strong>emsdk_env.sh<br>emsdk update<br>emsdk install latest<br>emsdk activate latest<br>source ./emsdk_env.sh<br>emcc -v (para comprobar)</pre><br>Emscripten ya estará instalado junto con Clang y las piezas claves de LLVM necesarias.<br><h2>Escribiendo el programa en Rust</h2><br>Vamos a escribir un programa simple. Un hola mundo.<br><br>&nbsp;<br><pre class="lang:rust decode:true">fn main(){<br>    println!("Hola mundo - WebAssembly + Rust");<br>}<br></pre><br>Compilamos con rustc<br><br>&nbsp;<br><pre class="lang:default decode:true">rustc --target=wasm32-unknown-emscripten main.rs -o main.html<br></pre><br>Esto genera diversos archivos: main.html, main.js, main.wasm y main.asm.js (para compatibilidad con navegadores que no tienen WebAssembly). El fichero .wasm contiene el bytecode, si intentas abrirlo verás que es ilegible. Sin embargo, Chrome, Firefox, Edge, Safari y Node.js entenderán ese archivo. Probamos el fichero main.html en Firefox (cuya última versión soporta WebAssembly por defecto):<br><br><a href="https://files.adrianistan.eu/WebASM-Rust.png"><img class="aligncenter size-large wp-image-811" src="https://files.adrianistan.eu/WebASM-Rust-1024x480.png" alt="" width="840" height="394" /></a><br>Usando este sistema compilamos aplicaciones enteras. Si se ajustan ciertos parámetros de Emscripten y se usa una crate adecuada en Rust puede usarse para generar juegos 3D usando WebGL escritos 100% en Rust.<br><h2>Cargas librerías en Rust desde JavaScript</h2><br>En el paso anterior vimos como compilar a WASM aplicaciones enteras. Ahora vamos a compilar el código de Rust a una librería y vamos a cargarlo con JavaScript.<br><br>La librería va a ser muy simple:<br><pre class="lang:rust decode:true">#[no_mangle]<br>pub fn random_number() -&gt; i32 {<br>    42<br>}<br><br>fn main() {<br><br>}<br><br></pre><br>Ahora compilamos el fichero a WebAssembly<br><pre class="lang:default decode:true">rustc --target=wasm32-unknown-emscripten random.rs<br></pre><br>Ahora vamos a cargar el fichero random.wasm. Para ello usaremos la ayuda de random.js, que contiene el código necesario para cargar el fichero WASM así como definir los imports que el código Rust espera (variables de entorno, funciones globales, etc).<br><br>&nbsp;<br><pre class="lang:default decode:true">&lt;!DOCTYPE html&gt;<br>&lt;html&gt;<br>	&lt;head&gt;<br>		&lt;meta charset="utf-8"/&gt;<br>	&lt;/head&gt;<br>	&lt;body&gt;<br>		&lt;script&gt;<br>		var Module = {<br>			wasmBinaryFile: "random.wasm",<br>			onRuntimeInitialized: main,<br>		};<br>		function main() {<br>			var random_number = Module.cwrap("random_number","number",[]);<br>			alert(random_number());<br>		}<br>		&lt;/script&gt;<br>		&lt;script src="random.js"&gt;&lt;/script&gt;<br>	&lt;/body&gt;<br>&lt;/html&gt;<br></pre><br><a href="https://files.adrianistan.eu/Rust42.png"><img class="aligncenter size-large wp-image-812" src="https://files.adrianistan.eu/Rust42-1024x495.png" alt="" width="840" height="406" /></a>Usando este sistema, podemos ir añadiendo código WASM poco a poco en nuestras webs.<br><br>&nbsp;<br><h2>Conclusión</h2><br>Como vemos, ya es posible hoy día usar WebAssembly en nuestros proyectos. Para crear el código WebAssembly podemos usar Rust. El código WASM puede interactuar con el código JavaScript existente. ¿Qué opináis de WebAssembly? ¿Creéis que puede suponer un antes y un después en el mundo web o se seguirá usando JavaScript de forma masiva?<br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/webassembly-tontos-usando-rust</comments>
                <pubDate>Fri, 02 Jun 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Triángulo de Sierpinski en JavaScript</title>
                <link>https://blog.adrianistan.eu/triangulo-sierpinski-javascript</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/triangulo-sierpinski-javascript</guid>
                <description><![CDATA[Un amigo me propuso esta mañana que viera un vídeo de Numberphile, concretamente uno titulado <a href="https://www.youtube.com/watch?v=kbKtFN71Lfs">Chaos Game</a>. El vídeo es bastante interesante y habla de como de una aparente aleatoriedad es posible sacar fractales y patrones regulares. Esta misma mañana al llegar a casa y antes de comer me he picado y me he puesto a implementar el algoritmo en JavaScript usando el Canvas de HTML5. El resultado lo tenéis aquí:<br><br><a href="http://adrianistan.eu/sierpinski"><img class="aligncenter size-large wp-image-806" src="https://files.adrianistan.eu/Captura-de-pantalla-28-1024x640.png" alt="" width="840" height="525" /></a><br><p style="text-align: center;"><a href="http://adrianistan.eu/sierpinski/">http://adrianistan.eu/sierpinski/</a></p><br>Y el código que lleva es el siguiente:<br><br><pre class="lang:js decode:true"><br>const COLOR_LIST = [&quot;red&quot;,&quot;green&quot;,&quot;yellow&quot;,&quot;pink&quot;,&quot;brown&quot;,&quot;purple&quot;,&quot;cyan&quot;,&quot;blue&quot;,&quot;orange&quot;];<br><br>function punto(x,y){<br>    var p = {<br>        x:x,<br>        y:y<br>    };<br>    return p;<br>}<br><br>function puntoMedio(p,q){<br>    var m = {<br>        x: Math.round((p.x+q.x)/2),<br>        y: Math.round((p.y+q.y)/2)<br>    };<br>    return m;<br>}<br><br>function getRandomColor(){<br>    return COLOR_LIST[Math.floor(COLOR_LIST.length * Math.random())];<br>}<br><br>function dibujarPunto(ctx,p,size){<br>    ctx.fillStyle = getRandomColor();<br>    ctx.fillRect(p.x,p.y,size,size);<br>}<br><br>function $(id){<br>    return document.getElementById(id);<br>}<br><br>function get(id){<br>    return Math.round(document.getElementById(id).value);<br>}<br><br>function main(){<br>    var canvas = document.getElementById(&quot;canvas&quot;);<br>    var ctx = canvas.getContext(&quot;2d&quot;);<br><br>    var interval;<br><br>    $(&quot;start&quot;).addEventListener(&quot;click&quot;,function(){<br><br>        var size = get(&quot;size&quot;);<br>        var vertex = [punto(get(&quot;a-x&quot;),get(&quot;a-y&quot;)),punto(get(&quot;b-x&quot;),get(&quot;b-y&quot;)),punto(get(&quot;c-x&quot;),get(&quot;c-y&quot;))];<br><br>        let p = punto(get(&quot;s-x&quot;),get(&quot;s-y&quot;));<br><br>        dibujarPunto(ctx,p,size);<br><br>        interval = setInterval(function(){<br>            var q = vertex[Math.floor(3*Math.random())];<br>            p = puntoMedio(p,q);<br>            dibujarPunto(ctx,p,size);<br>        },get(&quot;speed&quot;));<br>    });<br><br>    $(&quot;stop&quot;).addEventListener(&quot;click&quot;,function(){<br>        clearInterval(interval);<br>    });<br><br>    $(&quot;reset&quot;).addEventListener(&quot;click&quot;,function(){<br>        ctx.fillStyle = &quot;white&quot;;<br>        ctx.fillRect(0,0,600,600);<br>    });<br>}<br><br>window.addEventListener(&quot;DOMContentLoaded&quot;,main);<br></pre><br><br>Con distinto número de vértices existen otros fractales, también muy chulos. Incluso en el vídeo de Numberphile realizan un fractal con un gran parecido a una hoja de helecho, usando dos triángulos y una ligera modificación del algoritmo.<br><br>Un saludo y soñad con fractales.<br>]]></description>
                <comments>https://blog.adrianistan.eu/triangulo-sierpinski-javascript</comments>
                <pubDate>Wed, 24 May 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>¿Nos vemos en la LechazoConf?</title>
                <link>https://blog.adrianistan.eu/nos-vemos-la-lechazoconf</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/nos-vemos-la-lechazoconf</guid>
                <description><![CDATA[El próximo sábado 27 de mayo tendrá lugar en Valladolid la <a href="https://lechazoconf.com/">LechazoConf</a>. Uno de los mayores eventos de informática de la región.<br><br><a href="https://lechazoconf.com/"><img class="aligncenter wp-image-800 size-full" src="https://files.adrianistan.eu/LechazoConf.png" alt="" width="1023" height="640" /></a><br><br>Este evento cuenta con ponencias muy interesantes sobre el éxito y el fracaso (o al menos interesantes sobre el papel). Además, se podrá comer lechazo.<br><br>Yo estaré por allí, gracias a una invitación que tengo, así que si quieres: hablarme, comentarme, explicarme, insultarme, agradecerme, increparme, consolarme, abrazarme, besarme, matarme, vacilarme, comer unas patatas juntos, pasear juntos, debatir sobre metafísica, hacer travesuras por la ciudad o simplemente explicarte como conseguir la clave del WiFi o explicarte como instalar Windows sin que parezca que estés haciendo el vago, no dudes en buscarme.<br><br>PD: Me llamo Adrián Arroyo, no creo que me lo cambie para cuando sea la Lechazo.<br><br>PD2: Aunque nos hagamos muy buenos amigos, recuerda que mi lechazo es mi lechazo y el tuyo el tuyo]]></description>
                <comments>https://blog.adrianistan.eu/nos-vemos-la-lechazoconf</comments>
                <pubDate>Mon, 22 May 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Anrokku, un videojuego tipo puzzle</title>
                <link>https://blog.adrianistan.eu/anrokku-videojuego-tipo-puzzle</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/anrokku-videojuego-tipo-puzzle</guid>
                <description><![CDATA[<strong>Anrokku</strong> es un juego de puzles que he programado estas semanas. Las reglas son simples, somos una ambulancia y tenemos que salir del parking debido a una emergencia. Desafortunadamente el parking es un caos y los coches bloquean la salida. Tu labor es ir moviendo los coches para lograr que la ambulancia salga del parking. Y cuantos menos movimientos hagas mejor.<br><br><a href="https://files.adrianistan.eu/Anrokku.png"><img class="aligncenter size-large wp-image-789" src="https://files.adrianistan.eu/Anrokku-1024x578.png" alt="" width="840" height="474" /></a><br><br>En el menú principal podremos seleccionar a cuál de los 20 niveles queremos jugar. No podremos jugar a todos a la vez, es necesario habernos pasado los niveles anteriores para desbloquear el siguiente.<br><br><a href="https://files.adrianistan.eu/Anrokku-2.png"><img class="aligncenter size-large wp-image-790" src="https://files.adrianistan.eu/Anrokku-2-1024x578.png" alt="" width="840" height="474" /></a><br><br>Ya en el juego tenemos que ir arrastrando los coches con el ratón para que la ambulancia pueda irse por la derecha. <del>El juego está programando en Python 2.7 usando GTK</del>. Anrokku ahora mismo funciona con Python 3 y GTK 3. El renderizado está hecho en un GtkDrawingArea donde he usado Cairo. ¿Quiéres jugar? El juego completo es gratuito y open source bajo la liencia MIT en GitHub. No obstante, puedes descargar el archivo ZIP con el juego desde este enlace.<br><p style="text-align: center;"><a href="https://github.com/AdrianArroyoCalle/Anrokku/releases/download/Anrokku-1.1/Anrokku-1.1.zip">Descargar Anrokku</a></p><br><p style="text-align: left;">Después de descargar y descomprimir el archivo. Ejecuta el fichero main.py haciendo doble click o desde la terminal:</p><br><p style="text-align: center;">python3 main.py</p><br><p style="text-align: left;">¡Quiero ver muchas pantallas como esta!</p><br><p style="text-align: left;"><a href="https://files.adrianistan.eu/Anrokku-3.png"><img class="aligncenter size-large wp-image-791" src="https://files.adrianistan.eu/Anrokku-3-1024x578.png" alt="" width="840" height="474" /></a></p><br><p style="text-align: left;"> El juego almacena los récords obtenidos en cada nivel. Puede ser interesante repetir los niveles para hacerlo en el menor número posible de movimientos. En el primer nivel he conseguido ganar en 32 movimientos, ¿alguien se atreve a superarme?</p>]]></description>
                <comments>https://blog.adrianistan.eu/anrokku-videojuego-tipo-puzzle</comments>
                <pubDate>Mon, 15 May 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Mandando paquetes ICMP ECHO personalizados con Python</title>
                <link>https://blog.adrianistan.eu/mandando-paquetes-icmp-echo-personalizados-python</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/mandando-paquetes-icmp-echo-personalizados-python</guid>
                <description><![CDATA[Estos días he estado experimentando un poco con una característica de las redes. En concreto he intentando ver si era posible lanzar un paquete a la red cuyo origen es una dirección broadcast y el receptor, al mandar la respuesta, lo hiciera sin darse cuenta a todos los equipos de la red. Esto en efecto podría usarse para inutilizar una red por Denial of Service.<br><h2>ICMP ECHO</h2><br>La prueba la he hecho con un ICMP ECHO pero podría hacerse con otros protocolos también, como DHCP.<br><br>Un ICMP ECHO es lo que hace el famoso comando <strong>ping</strong> o <strong>ping6</strong> (en IPv6). Se trata de un protocolo de la capa de red (capa 3 en TCP/IP). ICMP dispone de varias operaciones para funcionar correctamente (Internet Control Message Protocol, controlar la red) pero la mayoría de ellas no se utilizan. Una que sí se usa es el ECHO, que permite mandar una información a un host y el host nos tiene que devolver lo mismo.<br><br>¿Cómo sabe a donde tiene que enviar la respuesta? A la IP de origen del primer paquete claro, pero ¿y si mentimos? ¿Y si le ponemos que la IP de origen es la IP de broadcast? Supuestamente, enviaría la respuesta a todos los demás hosts de la subred.<br><br><a href="https://files.adrianistan.eu/Captura-de-pantalla-9.png"><img src="https://files.adrianistan.eu/Captura-de-pantalla-9-1024x640.png" alt="" width="840" height="525" class="aligncenter size-large wp-image-784" /></a><br><br><h2>Script en Python</h2><br>Para poder hacer los datagramas IP personalizados y poder mentir, usé raw sockets. Estos solo me han funcionado en Linux. El código es muy simple, la parte que más me costó fue el checksum pues las operaciones con bytes en Python son un poco curiosas, no existiendo el tipo byte pero si bytes.<br><br><pre class="lang:python decode:true"><br>import socket<br>import struct<br>import random<br>import binascii<br>import sys<br>import functools<br><br>ICMP_CODE = socket.getprotobyname(&quot;icmp&quot;)<br>ICMP_ECHO = 8<br>IP_SRC = &quot;192.168.0.255&quot;<br>IP_DST = &quot;192.168.0.255&quot;<br><br>def main():<br>    s = socket.socket(socket.AF_INET,socket.SOCK_RAW,socket.IPPROTO_ICMP)<br>    s.setsockopt(socket.SOL_IP,socket.IP_HDRINCL,1)<br>    s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)<br>    packet_id = int((100000* random.random()) % 65535)<br>    packet = create_packet(packet_id)<br>    #print (&quot;Size of packet: &quot;+str(sys.getsizeof(packet)))<br>    s.sendto(packet,(IP_DST,0))<br><br>def create_header(data):<br>    version = 4 # IP version<br>    ihl = 5 # Numero de Bytes<br>    DF = 0<br>    Tlen = sys.getsizeof(data) + 20 # Longitud del datagrama<br>    ID = 1774 # ID de paquete<br>    Flag = 0 # Opciones<br>    Fragment = 0 # Numero de fragmento<br>    TTL = 128 # Tiempo de vida<br>    Proto = socket.IPPROTO_ICMP # Protocolo de los datos<br>    ip_checksum = checksum(data) # Checksum de los datos<br>    SIP = socket.inet_aton(IP_SRC) # Source IP address<br>    DIP = socket.inet_aton(IP_DST) # Destination IP address<br>    ver_ihl = (version &lt;&lt; 4) + ihl<br>    f_f = (Flag &lt;&lt; 13) + Fragment<br>    ip_hdr =  struct.pack(&quot;!BBHHHBBH4s4s&quot;, ver_ihl,DF,Tlen,ID,f_f,TTL,Proto,ip_checksum,SIP,DIP)<br>    return ip_hdr<br><br>def checksum(msg):<br>    flag = False<br>    suma = 0<br>    for byte in msg:<br>        if not flag:<br>            suma += (byte &lt;&lt; 8)<br>        else:<br>            suma += byte<br>        flag = not flag<br>    resto = suma &amp; 0xFFFF0000<br>    resto = resto &gt;&gt; 8<br>    suma += resto<br>    check = (~suma) &amp; 0x0000FFFF<br>    return check<br><br>def create_data(id):<br>    header = struct.pack(&quot;bbHHh&quot;, ICMP_ECHO, 0, 0, id, 1)<br>    data = b&quot;42&quot;<br>    header = struct.pack(&quot;bbHHh&quot;, ICMP_ECHO, 0, socket.htons(checksum(header+data)), id, 1)<br>    return header + data<br><br>def create_packet(packet_id):<br>    data = create_data(packet_id)<br>    header = create_header(data)<br>    return header+data<br>while True:<br>    main()<br><br></pre><br><br>Espero que a alguien le resulte interesante el código. Es un ejemplo de como generar datagramas sin la ayuda del sistema operativo. Además es importante saber que este tipo de programas solo pueden ejecutarse como root. Yo, por las pruebas que pude hacer, creo que los sistemas operativos actuales no son tontos y este tipo de ataques ya son conocidos. No pude capturar con Wireshark ninguno que fuese respuesta a broadcast, mientras que si la IP, aun siendo falsa, parecía real, sí que podía capturarla.<br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/mandando-paquetes-icmp-echo-personalizados-python</comments>
                <pubDate>Wed, 10 May 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>La belleza de MIPS</title>
                <link>https://blog.adrianistan.eu/la-belleza-mips</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/la-belleza-mips</guid>
                <description><![CDATA[Todos los ordenadores, móviles y en general, cualquier dispositivo que lleva software necesita un procesador. Los procesadores se agrupan por familias, familias de procesadores que se programan igual, en un lenguaje llamado ensamblador. La más popular es Intel x86, presente en cualquier PC y en algunos móviles, tablets y servidores. Pero no voy a hablaros hoy de x86, ni de ARM, sino de MIPS. El ensamblador hecho bello. Adentrémonos en este mundo. Si nunca has visto ensamblador, este es tu momento. Si ya lo has visto, quizá te apetezca recordar algunas cosas.<br><h2>MIPS, reinventemos la rueda... pero bien</h2><br>Los orígenes de la arquitectura MIPS se remontan a 1981 cuando John L. Hennessy y su equipo de la Universidad de Stanford buscan implementar un procesador lo más simple posible. Al contrario que en las arquitecturas de tipo CISC lo que se busca en una de tipo de RISC como MIPS es definir las instrucciones más simples posibles y optimizar estas. ARM es otro ejemplo de arquitectura RISC. Para optimizar aún más, MIPS hace un uso intensivo de la segmentación.<br><br>Los procesadores MIPS ganaron popularidad rápidamente. Fueron usados por Silicon Graphics (SGI) para sus workstations con sistema operativo IRIX. Allí fueron usadas en estudios de Hollywood, centros científicos, etc En su máximo esplendor estas máquinas acogieron grandes avances, como la invención de OpenGL y del sistema de archivos XFS.<br><br>Esta relación de MIPS con el mundo multimedia le hizo popular entre las consolas. La Nintendo 64, la PlayStation 2 o la PSP llevan procesador MIPS.<br><br><img class="alignnone size-large" src="https://upload.wikimedia.org/wikipedia/commons/c/c3/Silicon_Graphics_Indy.jpg" width="4216" height="4216" /><br><br>Finalmente MIPS ha entrado en cierta decadencia. SGI cerró y las consolas decidieron apostar por otro tipo de procesadores. Sin embargo MIPS sigue vivo. Es usado en routers y algunos móviles. Existe Loongson, una variación de la aquitectura MIPS usada en China. Incluso ha habido placas estilo Raspberry Pi con MIPS, como por ejemplo, la Creator CI20.<br><h2>Cuatro principios del diseño</h2><br>Estos principios se aplican en MIPS.<br><ul><br> 	<li>La simplicidad favorece la regularidad<br><ul><br> 	<li>La regularidad facilita la implementación</li><br> 	<li>La simplicidad mejora el rendimiento a menor coste</li><br></ul><br></li><br> 	<li>Cuanto más pequeño más rápido</li><br> 	<li>Mejorar en lo posible los casos más frecuentes</li><br> 	<li>Un buen diseño requiere buenas soluciones de compromiso</li><br></ul><br><h2>Tres tipos de instrucciones</h2><br>La belleza de MIPS se basa en su diseño, claro, conciso y con pocas excepciones. Empecemos por lo básico, en MIPS todas las instrucciones son de 32 bits y son de una de estas tres categorías:<br><ul><br> 	<li>R, operan con tres registros</li><br> 	<li>I, operan con dos registros y una constante</li><br> 	<li>J, operan con una dirección de memoria</li><br></ul><br><h2>32 registros de 32 bits</h2><br>En MIPS existen 32 registros. Bastantes si los comparamos con x86 de Intel. ¿Qué es un registro? Es donde se guardan los datos que se van a necesitar en las operaciones. Como si fueran cajas donde podemos guardar cosas, las cajas de la CPU. Salvo un par de ellos especiales, en realidad todos son iguales, no obstante, por convenio ciertos registros se usan para unas cosas y otros para otras.<br><br><img class="wp-image-777 size-full" src="https://files.adrianistan.eu/RegistrosMIPS.png" alt="" width="737" height="423" /> Tomado de Wikipedia<br><h2>Operaciones básicas</h2><br>Las operaciones básicas son la suma, la resta, la suma inmediata, los desplazamientos, las operaciones lógicas and, or y nor, las operaciones de carga y guardado en memoria, el comparador menor que, los branch y los saltos. Y ya esta. Todo lo demás se implementa mediante pseudoinstrucciones, es decir, instrucciones que el ensamblador convierte en varias instrucciones básicas. Muchos se habrán sorprendido que no haya operación de multiplicar. O que no haya move. O que solo se pueda comparar con menor que. Todas esas cosas las podemos hacer pero debemos ser conscientes de que son pseudoinstrucciones y MIPS en realidad no tiene esas operaciones registradas. Veamos un ejemplo de código MIPS. Primero tenemos el código en C equivalente, y después el código MIPS.<br><br><pre class="lang:c++ decode:true"><br>a =  b + c + 5<br></pre><br><br><br><pre class="lang:default decode:true"><br># a en $s0, b en $s1, c en $s2<br>add $s0, $s1, $s2 # Sumamos $s1 + $s2 y ponemos el resultado en $s0<br>addi $s0, $s0, 5 # Sumamos a $s0 la constante 5 y dejamos el resultado en $s0<br></pre><br><br>Por norma general en MIPS cuando queramos usar una constante en vez de un registro bastará con poner una i (de inmmediato) al final. Así en vez de usar una instrucción de tipo R usaremos una de tipo I.<br><h2>Branch y jump</h2><br>La magia de los computadores reside en que son capaces de tomar decisiones. Pueden ejecutar bucles y evaluar condiciones. En ensamblador este tipo de cosas se hacen con saltos. Sin embargo MIPS incluye dos tipos distintos de <em>saltos</em>.<br><br>Los branch evalúan condiciones y saltan en caso de que se cumpla a una posición del código N bytes por arriba o por debajo de la dirección de memoria actualmente almacenada en $ra.<br><br>Los jump saltan a una dirección de memoria de forma absoluta. Como MIPS es de 32 bits y las instrucciones también son de 32 bits resulta evidente que no es posible saltar a cualquier dirección de memoria, no al menos en una simple instrucción. Los bits que faltan, los más significativos se dejan a los mismos que había en donde se hizo el jump.<br><br>Esto parece muy complicado pero veamos que no lo es tanto en la práctica. Como recomendación, usa jumps para saltar a subrutinas y usa branch para hacer ifs y bucles.<br><br><pre class="lang:c++ decode:true"><br>int x = 5;<br>do{<br>    x--;<br>}while(x!=0);<br></pre><br><br><br><pre class="lang:default decode:true"><br># x en $s0<br>.text<br>main:<br>    addi $s0, $zero, 5 # Cargamos 5 a $s0<br>    # es equivalente a la pseudoinstrucción li $s0, 5<br>bucle:<br>    addi $s0, $s0, -1 # las constantes también pueden ser negativas<br>    bne $s0, $zero, bucle # branch on not equal. Si $s0 != $zero, se salta a bucle. Si no, se sigue para abajo<br></pre><br><br><br><pre class="lang:c++ decode:true"><br>int double(int x){<br>    return x*2;<br>}<br>...<br>int y = 5;<br>y = double(y);<br></pre><br><br><br><pre class="lang:default decode:true"><br>.text<br>main:<br>    addi $s0, $zero, 5<br>    add $a0, $s0, $zero #podriamos usar la pseudoinstruccion move<br>    jal double<br>    move $s0, $v0 #pseudoinstruccion para copiar de $v0 a $s0<br>    li $v0, 10<br>    syscall # ejecuta una syscall, con código el que hay en $v0 (10 vamos a suponer que es salir del programa)<br>double:<br>    sll $v0, $a0, 1<br>    jr $ra<br></pre><br><br>¿Qué hace jal? Llama a la subrutina double ejecutando un salto y poniendo $ra a la siguiente dirección (para retomar el ciclo de ejecución normal cuando salgamos de la subrutina con jr $ra).<br><h2>Cargando datos</h2><br>Hasta ahora hemos trabajado siempre con datos que ya estaban en los registros. ¿Qué pasa si queremos recorrer un array por ejemplo? Un array es un tipo de dato más complejo, que no entra en un registro. Este ejemplo suma los números del array.<br><br><pre class="lang:c++ decode:true"><br>int* x = [1,2,3,4,5];<br>int suma = 0;<br>for(int i =0;i&lt;4;i++){<br>    suma = suma + x[i]<br>}<br></pre><br><br><br><pre class="lang:default decode:true"><br>.data<br>x: .word 1,2,3,4,5<br>.text<br>main:<br>	li $s0, 0<br>	li $t9, 5<br>	la $t0, x<br>bucle:<br>	lw $t1, 0($t0)<br>	add $s0, $s0, $t1<br>	addi $t0, $t0, 4<br>	addi $t9, $t9, -1<br>	bne $zero, $t9, bucle<br>	li $v0, 10<br>	syscall<br></pre><br><br>Usamos la cabecera data para registrar datos en el stack que el sistema operativo repartirá como pueda. Le llamamos x y contiene WORDS, que en MIPS son 4 bytes, es decir, 32 bits.<br><br><strong>la</strong> es una pseudoinstrucción (usa lui y ori internamente) que sirve para cargar direcciones de 32 bits en un registro. En este caso cargamos la posición de donde empieza el vector x en $t0. $t0 es un puntero ahora.<br><br><strong>lw</strong> es la instrucción importante aquí, significa <em>load word</em> y permite cargar palabras de la memoria a un registro. Le indicamos donde queremos que se guarde ($t1), y donde está el dato en memoria ($t0). Adicionalmente le indicamos el desplazamiento, que digamos es una constante que podemos aplicar para cargar unos bytes antes o después de lo que indique el registro. Esto es muy útil, pero en este ejemplo no tiene sentido usarse y lo he dejado a 0.<br><br>¿Por qué sumamos 4 a $t0 en cada pasada? Hemos dicho que los WORD en MIPS ocupan 32 bits, 4 bytes. Pues en esa operación estamos moviendo el puntero 4 bytes más en la memoria, para pasar al siguiente elemento del vector. Esto es aritmética de punteros. ¡Chachi pistachi!<br><h2>El emulador Mars</h2><br>¿Ya has visto por qué MIPS es tan bonito? A diferencia de otros ensambladores, la sintaxis de MIPS es muy regular, con comportamientos predecibles aunque no conozcamos exactamente la instrucción. Si quieres probar tus destrezas en MIPS existen varios simuladores. El que uso ahora mismo se llama Mars, está hecho en Java y es bastante completo. Es posible inspeccionar la memoria al completo, ir paso a paso en cada instrucción, insertar breakpoints y observar el valor de los registros en cada momento.<br><br><a href="https://files.adrianistan.eu/MARS.png"><img class="aligncenter size-large wp-image-778" src="https://files.adrianistan.eu/MARS-1024x547.png" alt="" width="840" height="449" /></a><a href="http://computerscience.missouristate.edu/~vollmar/MARS/download.htm">Descargar Mars</a><br><br>También si nos vemos con fuerzas podremos pasar a hardware real con Linux. OpenWrt, presente en routers, o Debian, en la Creator CI20 pueden ser buenas opciones.<br><h2>Conclusión</h2><br>Seguro que si habías programado con anterioridad en otro ensamblador esto te ha parecido muy sencillo. Y es que MIPS no es especialmente complejo.]]></description>
                <comments>https://blog.adrianistan.eu/la-belleza-mips</comments>
                <pubDate>Wed, 03 May 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>¿Qué pasa con Elixir?</title>
                <link>https://blog.adrianistan.eu/que-pasa-con-elixir</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/que-pasa-con-elixir</guid>
                <description><![CDATA[Desde hace un tiempo oigo a bastante gente hablar de <a href="http://elixir-lang.org/">Elixir</a>. Elixir es un lenguaje de programación de propósito general que vio la luz en 2011. Con bastantes aspectos de un lenguaje de programación funcional y dinámico, está especificamente diseñado para manejar la concurrencia. Se ejecuta sobre la máquina virtual BEAM, originalmente diseñada para Erlang y es capaz de ejecutar código métodos escritos en Erlang sin ninguna complicación extra. Además dispone de un sistema de compilación llamado <strong>mix</strong>, un gestor de paquetes llamado <strong>hex</strong> y la forma de escribir documentación es similar a Python (existe <strong>help</strong>(método), ¡aleluya!). Elixir además dispone de hot swapping, es decir, permite cambiar el código en ejecución sin necesidad de parar el programa. También le caracteriza tener un buen soporte a operaciones asíncronas.<br><h2>¿Es Elixir el nuevo Ruby?</h2><br>Mucha gente ha querido llamar a Elixir el Erlang escrito bonito. Muchos lo comparan con Ruby pues sí que comparten alguna característica común en su sintaxis y una filosofía en general de simplicidad. Además Elixir es mucho más rápido y está mucho mejor diseñado para concurrencia y aplicaciones tolerantes a fallos (como su hermano mayor Erlang).<br><br>Sin embargo, ¿es justo comparar Ruby con Elixir?<br><h2>Entra Phoenix en acción</h2><br>Mucha de la popularidad de Ruby se la debe a su framework Rails. Usado por una gran cantidad de sitios en la web, supuso para muchos su primer contacto con Ruby y la razón de que se quedasen aprendiendo el lenguaje.<br><br>Curiosamente, Elixir también tiene un framework web que atrae a desarrolladores. Se trata de <a href="http://www.phoenixframework.org/">Phoenix</a>.<br><br><a href="https://files.adrianistan.eu/Phoenix.png"><img class="aligncenter size-large wp-image-770" src="https://files.adrianistan.eu/Phoenix-1024x499.png" alt="" width="840" height="409" /></a>Phoenix se presenta como un framework para la nueva web. Aplicaciones realtime, tolerantes a fallos y de alto rendimiento sin perder las facilidades y comodidades de frameworks anteriores. La gente que lo ha usado cuenta maravillas. ¿Has usado Phoenix? Cuéntanos tu experiencia en los comentarios.<br><h2>Probando Elixir con su REPL</h2><br>Elixir viene con un REPL, es decir, una terminal donde línea a línea podemos ir introduciendo código Elixir. Se ejecuta con el comando <strong>iex</strong>.<br><br>Podemos hacer un hola mundo con <strong>IO.puts</strong>.<br><br><a href="https://files.adrianistan.eu/HelloWorldElixir.png"><img class="aligncenter size-full wp-image-771" src="https://files.adrianistan.eu/HelloWorldElixir.png" alt="" width="643" height="148" /></a>Podemos probar a definir un módulo de suma:<br><br><pre class="lang:ruby decode:true"><br>defmodule Adder do<br>    def add(a,b) do<br>        a+b<br>    end<br>end<br><br>Adder.add 42,15<br></pre><br><br><a href="https://files.adrianistan.eu/Adder.png"><img class="aligncenter size-full wp-image-772" src="https://files.adrianistan.eu/Adder.png" alt="" width="642" height="293" /></a><em>Lo que se ve en amarillo es el bytecode compilado para BEAM del módulo Adder</em><br><br>Aunque llegado el momento quizá nos interese crear alguna función anónima.<br><br><pre class="lang:ruby decode:true"><br>add = fn a,b -&gt; a+b end<br>x = 42<br>IO.puts add.(x,15)<br></pre><br><br>Es fácil encontrar documentación en español sobre Elixir si deseas investigar más sobre el tema.<br><br>Sin duda Elixir parece interesante. ¿Será un mero hype o dentro de unos años Elixir será uno de los lenguajes más populares? Ciertas webs han empezado a recomendar echar un vistazo a Elixir y Phoenix si queremos crear una nueva aplicación web.]]></description>
                <comments>https://blog.adrianistan.eu/que-pasa-con-elixir</comments>
                <pubDate>Sun, 30 Apr 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Polvo</title>
                <link>https://blog.adrianistan.eu/polvo</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/polvo</guid>
                <description><![CDATA[“Polvo eres y en polvo te convertirás” pensaba Félix. Y lo cierto es que en ese momento estaba hecho polvo.  Su trabajo era estresante. ¡Fax! ¿Habéis hablado con los inversores japoneses? ¡Rápido, que se acaba el plazo de entrega! ¡Fotocópiame este dossier! ¡Lleva a las niñas a la fotocopiadora! Espera, espera, ¿por qué tenía que llevar a las niñas a la fotocopiadora? Estaba delirando de nuevo. Una visita rutinaria a la máquina de café le despertaría momentáneamente.<br><br><img class="alignnone size-large" src="http://www.mx.all.biz/img/mx/catalog/30096.jpeg" width="360" height="550" /><br><br>¡Pero si no hay café! Me han traído una máquina de chucherías. ¿Qué clase broma es esta? Si tiene hasta condones, como si fuese a echar un polvo aquí en la oficina. El disgusto de Félix era tal que fue a hablar con su jefe. Un asunto que él consideraba de máxima prioridad. ¡Voy a ir al jefe a hablarle de este asunto, es de vital importancia tener una máquina de café! Al llegar al despacho descubrió que sí que había gente en la oficina que usaba los condones. Fingió no haber visto nada y fue a la planta de arriba. Quizá allí tendrían néctar negro.<br><br>Al subir se encontró con Emma. Era la típica persona superficial e interesada. Ella fue quien le dijo que no cobraría paga extra en navidad por su baja productividad. Esta vez, y por primera vez en mucho tiempo, dijo algo interesante para Félix. Arriba han quitado la máquina de café. ¿Por qué lo primero que me ha dicho al verme es sobre el café? ¿Está insinuando que no hago nada más que tomar café? Instintivamente le propinó un golpe a Emma que la dejó inconsciente en el suelo. ¡Cómo puede pensar que soy un cafeinómano! ¡Y como se atrevió a decirme que mis niñas estarían mejor sin su padre! ¡Por fin muerde el polvo! Al poco tiempo recapacitó. ¡Dios! ¿Qué he hecho? ¿La habré matado? Tenía tanto miedo que no se paró a comprobarlo. Miremos el lado positivo, nadie me ha visto. Podría hacer pasar que fue un accidente. ¿Pero y si cuando recupera la consciencia me acusa a mí? ¿Y si me despiden? ¡Perderé todo lo que tengo!<br><br>Ahora Félix no quería perder los faxes, los gritos y el estrés. En cierta parte empezó a sentir nostalgia. Son parte de mí. Si me despiden, ¿qué seré? ¡Tengo derecho a quedarme con mis problemas! Tengo que deshacerme de ella sin levantar sospechas. Entró en una sala vacía con cuidado de que nadie le viese. Dentro de la sala repleta de polvo, destacaba una máquina de café. Esa máquina no tenía polvo, ¡era la que estaba antes en su planta!<br><br>Se apresuró a acabar con lo que quedaba de Emma. Cortó. Descuartizó. Aplastó. Ya no era él. Pero hacía mucho tiempo que ya no era él. Se empezó a dar cuenta que el cuerpo muerto de Emma sangraba y podía delatarle. Una bombilla se le iluminó y decidió escurrir la sangre en el tanque de la máquina de café.<br><br><a href="https://files.adrianistan.eu/Holzmehl.jpg"><img class="aligncenter size-large wp-image-766" src="https://files.adrianistan.eu/Holzmehl-907x1024.jpg" alt="" width="840" height="948" /></a><br><br>A la mañana siguiente volvió a aparecer la máquina de café. Al parecer había habido quejas. Como todos los días fue a por su café. Tenía un tono más rojo que de costumbre, pero estaba delicioso. Un compañero le comentó a Félix que Emma había desaparecido sin dejar rastro. Seguramente no andará muy lejos. Miró el café, sonrió y siguió trabajando. De todos modos nadie distingue el polvo en el viento y Emma ha volado.]]></description>
                <comments>https://blog.adrianistan.eu/polvo</comments>
                <pubDate>Wed, 26 Apr 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Novedades de C++17</title>
                <link>https://blog.adrianistan.eu/novedades-de-c17</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/novedades-de-c17</guid>
                <description><![CDATA[Después de tres años de trabajo, C++17 ha sido finalmente estandarizado. Esta nueva versión de C++ incorpora y elimina elementos del lenguaje, con el fin de ponerlo al día y convertirlo en un lenguaje moderno y eficaz. El comité ISO de C++ se ha tomado muy en serio su labor, C++11 supuso este cambio de mentalidad, que se ha mantenido en C++14 y ahora en C++17, la última versión de C++.<br><br><a href="https://files.adrianistan.eu/c17-programming-language.jpg"><img src="https://files.adrianistan.eu/c17-programming-language.jpg" alt="" width="900" height="500" class="aligncenter size-full wp-image-756" /></a><br><br>Repasemos las noveades que incorpora C++17 respecto a C++14<br><h3>if-init</h3><br>Ahora podemos incluir una sentencia de inicialización antes de la condición en sentencias if y switch. Esto es particularmente útil si queremos operar con un objeto y desconocemos su validez.<br><br><pre class="lang:c++ decode:true"><br>// ANTES<br>Device dev = get_device();<br>if(dev.isOk()){<br>    dev.hacerCosas();<br>}<br><br>// AHORA<br>if(Device dev = get_device(); dev.isOk()){<br>    dev.hacerCosas();<br>}<br></pre><br><br><h3>Declaraciones de descomposición</h3><br><br>Azúcar sintántico que permite mejorar la legibiliad en ciertas situaciones. Por ejemplo, en el caso de las tuplas, su uso se vuelve trivial.<br><br><pre class="lang:c++ decode:true"><br>// FUNCIÓN QUE DEVUELVE TUPLA<br>std::tuple&lt;int, std::string&gt; funcion();<br><br>// C++14<br>auto tup = funcion();<br>int i = std::get&lt;0&gt;(tup);<br>std::string s = std::get&lt;1&gt;(tup);<br><br>// C++17<br>auto [i,s] = funcion();<br><br></pre><br><br>Esto funciona para multitud de estructuras de datos, como estructuras, arrays, std::array, std::map,...<br><br><pre class="lang:c++ decode:true"><br>std::map m = ...;<br>for(auto &amp;&amp; [key, value] : m){<br><br>}<br></pre><br><br><h3>Deduction Guides</h3><br><br>Ahora es menos necesario que nunca indicar los tipos en ciertas expresiones. Por ejemplo, al crear pares y tuplas:<br><br><pre class="lang:c++ decode:true"><br>// ANTES<br>auto p = std::pair&lt;int,std::string&gt;(42,&quot;Adrianistan&quot;);<br><br>// AHORA<br>auto p = std::pair(42,&quot;Adrianistan&quot;);<br></pre><br><br>Esto por supuesto también sirve para estructuras y otras construcciones:<br><br><pre class="lang:c++ decode:true"><br>template&lt;typename T&gt;<br>struct Thingy<br>{<br>  T t;<br>};<br><br>// Observa<br>Thingy(const char *) -&gt; Thingy&lt;std::string&gt;;<br><br>Thingy thing{&quot;A String&quot;}; // thing.t es de tipo std::string<br></pre><br><br><h3>template auto</h3><br><br><pre class="lang:c++ decode:true"><br>// ANTES<br>template &lt;typename T, T v&gt;<br>struct integral_constant<br>{<br>   static constexpr T value = v;<br>};<br>integral_constant&lt;int, 2048&gt;::value<br>integral_constant&lt;char, 'a'&gt;::value<br><br>// AHORA<br>template &lt;auto v&gt;<br>struct integral_constant<br>{<br>   static constexpr auto value = v;<br>};<br>integral_constant&lt;2048&gt;::value<br>integral_constant&lt;'a'&gt;::value<br></pre><br><br><h3>Fold expressions</h3><br><br>Imagina que quieres hacer una función suma, que admita un número ilimitado de parámetros. En C++17 no se necesita apenas código.<br><br><pre class="lang:c++ decode:true"><br>template &lt;typename... Args&gt;<br>auto sum(Args&amp;&amp;... args) {<br>   return (args + ... + 0);<br>}<br></pre><br><br><h3>Namespaces anidados</h3><br><br>Bastante autoexplicativo<br><br><pre class="lang:c++ decode:true"><br>// ANTES<br><br>namespace A{<br>    namespace B {<br>        bool check();<br>    }<br>}<br><br>// AHORA<br><br>namespace A::B {<br>    bool check();<br>}<br></pre><br><br><h3>Algunos [[atributos]] nuevos</h3><br><br><h4>[[maybe_unused]]</h4><br>Se usa para suprimir la advertencia del compilador de que no estamos usando una determinada variable.<br><br><pre class="lang:c++ decode:true"><br>int x = 5;<br>[[maybe_unused]] bool azar = true;<br>x = x + 10<br></pre><br><br><h4>[[fallthrough]]</h4><br><br>Permite usar los switch en cascada sin advertencias del compilador.<br><pre class="lang:c++ decode:true"><br>switch (device.status())<br>{<br>case sleep:<br>   device.wake();<br>   [[fallthrough]];<br>case ready:<br>   device.run();<br>   break;<br>case bad:<br>   handle_error();<br>   break;<br>}<br></pre><br><br><h3>Variables inline</h3><br><br>Ahora es posible definir variables en múltiples sitios con el mismo nombre y que compartan una misma instancia. Es recomendable definirlas en un fichero de cabecera para luego reutilizarlas en ficheros fuente.<br><br><pre class="lang:c++ decode:true"><br>// ANTES<br>// en una cabecera para que la usasen los demás<br>extern int x;<br><br>// solo en un fichero fuente, para inicializarla<br>int x = 42;<br>// AHORA<br><br>// en la cabecera<br>inline int x = 42;<br><br></pre><br><br><h3>if constexpr</h3><br><br>Ahora es posible introducir condicionales en tiempo de compilación (similar a las macros #IFDEF pero mejor hecho). Estas expresiones con constexpr, lo que quiere decir que son código C++ que se evalúa en tiempo de compilación, no de ejecución.<br><br><pre class="lang:c++ decode:true"><br>template&lt;class T&gt;<br>void f (T x)<br>{<br>    if  constexpr(std:: is_integral &lt;T&gt;::value)  {<br>        implA(x);<br>    }<br>    else  if  constexpr(std:: floating_point &lt;T&gt;::value)  {<br>        implB(x);<br>    }<br>    else<br>    {<br>        implC(x);<br>    }<br>}<br></pre><br><br><h3>std::optional</h3><br><br>Tomado de la programación funcional, se incorpora el tipo optional, que representa un valor que puede existir o no. Este tipo ya existe en Rust bajo el nombre de Option y en Haskell como Maybe.<br><br><pre class="lang:c++ decode:true"><br>std::optional opt = f();<br>if(opt)<br>    g(*opt);<br><br>// otra opción de uso si queremos proveer de un reemplazo<br>std::optional opt = f();<br>std::cout &lt;&lt; opt.value_or(0) &lt;&lt; std::endl;<br></pre><br><br><h3>std::variant</h3><br><br>Descritas como las unions pero bien hechas. Pueden contener variables de los tipos que nosotros indiquemos.<br><br><pre class="lang:c++ decode:true"><br>std::variant&lt;int, double, std::vector&gt; precio; // precio puede ser un int, un double o un std::vector<br><br>// comprobar si el valor en un variant es de un determinado tipo<br>if(std::holds_alternative&lt;double&gt;(precio))<br>    double x = std::get&lt;double&gt;(precio);<br></pre><br><br><h3>std::any</h3><br><br>Si con std::variant restringimos los posibles tipos de la variable a los indicados, con std::any admitimos cualquier cosa.<br><br><pre class="lang:c++ decode:true"><br>std::any v = ...;<br>if (v.type() == typeid(int)) {<br>   int i = any_cast&lt;int&gt;(v);<br>}<br></pre><br><br><h3>std::filesystem</h3><br>Se añade a la librería estándar este namespace con el tipo path y métodos para iterar y operar con directorios. Dile adiós a las funciones POSIX o Win32 equivalentes.<br><br><pre class="lang:c++ decode:true"><br>#include &lt;filesystem&gt;<br>#include &lt;iostream&gt;<br>namespace fs = std::filesystem;<br><br>void main(){<br>  fs::path dir = &quot;/&quot;;<br>  dir /= &quot;sandbox&quot;;<br>  fs::path p = dir / &quot;foobar.txt&quot;;<br>  std::cout &lt;&lt; p.filename() &lt;&lt; &quot;\n&quot;;<br>  fs::copy(dir, &quot;/copy&quot;, fs::copy_options::recursive);<br>}<br></pre><br><br><h3>Algoritmos en paralelo</h3><br><br>Muchos de los algoritmos de STL ahora pueden ejecutarse en paralelo bajo demanda. Con std::execution::par indicamos que queremos que el algoritmo se ejecute en paralelo.<br><br><pre class="lang:c++ decode:true"><br>std::sort(std::execution::par, first, last);<br></pre><br><br><h3>¿Qué novedades se esperan en C++20?</h3><br>Ya hemos visto lo que trae C++17. Ahora veremos que se espera que traiga C++20 en 2020.<br><ul><br>    <li>Módulos. Reemplazar el sistema de includes</li><br>    <li>Corrutinas. Mejorar la programación asíncrona</li><br>    <li>Contratos. Mejorar la calidad del código</li><br>    <li>Conceptos. Mejorar la programación genérica</li><br>    <li>Redes. Estandarizar la parte de red en C++ tal y como se ha hecho con std::filesystem</li><br>    <li>Rangos. Nuevos contenedores</li><br></ul><br><br><h2>Referencias:</h2><br><br><ul><br><li><a href="https://usingstdcpp.org/using-stdcpp-2016/programa-2016/cpp17-ya-esta-aqui-o-casi/">https://usingstdcpp.org/using-stdcpp-2016/programa-2016/cpp17-ya-esta-aqui-o-casi/</a></li><br><li><a href="https://github.com/tvaneerd/cpp17_in_TTs/blob/master/ALL_IN_ONE.md">https://github.com/tvaneerd/cpp17_in_TTs/blob/master/ALL_IN_ONE.md</a></li><br></ul><br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/novedades-de-c17</comments>
                <pubDate>Wed, 19 Apr 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Tutorial de Rocket, echa a volar tus webapps con Rust</title>
                <link>https://blog.adrianistan.eu/tutorial-rocket-echa-volar-tus-webapps-rust</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/tutorial-rocket-echa-volar-tus-webapps-rust</guid>
                <description><![CDATA[Previamente ya hemos hablado de <a href="https://blog.adrianistan.eu/2016/11/20/usando-iron-web-framework-rust/">Iron como un web framework</a> para Rust. Sin embargo desde que escribí ese post ha surgido otra librería que ha ganado mucha popularidad en poco tiempo. Se trata de Rocket. Un web framework que propone usar el rendimiento que ofrece Rust sin sacrificar la facilidad de uso de otros lenguajes.<br><h2>Rocket lleva las pilas cargadas</h2><br>A diferencia de Iron, Rocket incluye bastantes prestaciones por defecto con soporte para:<br><ul><br> 	<li>Plantillas</li><br> 	<li>Cookies</li><br> 	<li>Formularios</li><br> 	<li>JSON</li><br> 	<li>Soporte para rutas dinámicas</li><br></ul><br><h2><a href="https://files.adrianistan.eu/Rocket.png"><img class="aligncenter size-large wp-image-746" src="https://files.adrianistan.eu/Rocket-1024x499.png" alt="" width="840" height="409" /></a>Un "Hola Mundo"</h2><br>Rocket necesita una versión nightly del compilador de Rust. Una vez lo tengas creamos una aplicación con Cargo.<br><pre class="lang:default decode:true">cargo new --bin rocket_app<br></pre><br>Ahora modificamos el fichero Cargo.toml generado en la carpeta rocket_app para añadir las siguientes dependencias:<br><pre class="lang:default decode:true">rocket = "0.2.4"<br>rocket_codegen = "0.2.4"<br>rocket_contrib = "*"<br></pre><br>Editamos el archivo src/main.rs para que se parezca algo a esto:<br><pre class="lang:rust decode:true">#![feature(plugin)]<br>#![plugin(rocket_codegen)]<br><br>extern crate rocket;<br><br>#[get("/")]<br>fn index() -&gt; &amp;'static str {<br>    "El cohete ha despegado"<br>}<br><br>fn main() {<br>    rocket::ignite().mount("/",routes![index]).launch();<br>}<br></pre><br>Con esto iniciamos Rocket y dejamos a la función index que gestione las peticiones GET encaminadas a /. El servidor devolverá <em>El cohete ha despegado</em>.<br><br>Ahora si ejecutamos <strong>cargo run</strong> veremos algo similar a esto:<br><br><a href="https://files.adrianistan.eu/RocketLaunch.png"><img class="aligncenter size-full wp-image-749" src="https://files.adrianistan.eu/RocketLaunch.png" alt="" width="668" height="440" /></a>Vemos que el servidor ya está escuchando en el puerto 8000 y está usando todos los cores (en mi caso 4) del ordenador.<br><h2>Configurar Rocket</h2><br>Rocket dispone de varias configuraciones predeterminadas que afectan a su funcionamiento. Para alternar entre las configuraciones debemos usar variables de entorno y para modificar las configuraciones en sí debemos usar un fichero llamado Rocket.toml.<br><br>Las configuraciones por defecto son: dev (development), stage (staging) y prod (production). Si no indicamos nada, Rocket se inicia con la configuración dev. Para arrancar con la configuración de producción modificamos el valor de ROCKET_ENV.<br><pre class="lang:default decode:true">ROCKET_ENV=prod cargo run --release<br></pre><br>Sería el comando para arrancar Rocket en modo producción. En el archivo Rocket.toml se puede modificar cada configuración, estableciendo el puerto, el número de workers y parámetros extra pero no vamos a entrar en ello.<br><h2>Rutas dinámicas</h2><br>Rocket soporta rutas dinámicas. Por ejemplo, si hacemos <em>GET  /pelicula/Intocable</em> podemos definir que la parte del nombre de la película sea un parámetro. Esto hará que la función encargada de /pelicula/Intocable y de /pelicula/Ratatouille sea la misma.<br><pre class="lang:rust decode:true">#![feature(plugin)]<br>#![plugin(rocket_codegen)]<br><br>extern crate rocket;<br><br>#[get("/pelicula/&lt;pelicula&gt;")]<br>fn pelicula(pelicula: &amp;str) -&gt; String {<br>    format!("Veo que te gusta {}, a mi también!",pelicula)<br>}<br><br>#[get("/")]<br>fn index() -&gt; &amp;'static str {<br>    "El cohete ha despegado"<br>}<br><br>fn main() {<br>    rocket::ignite().mount("/",routes![index,pelicula]).launch();<br>}<br></pre><br>Los argumentos de la función son los parámetros de la petición GET. ¿Qué pasa si no concuerda el tipo de la función con lo que se pasa por HTTP? Nada. Sencillamente Rocket ignora esa petición, busca otra ruta (puede haber sobrecarga de rutas) y si encuentra otra que si satisfaga los parámetros será esa la escogida. Para especificar el orden en el que se hace la sobrecarga de rutas puede usarse rank. En caso de no encontrarse nada, se devuelve un error 404.<br><h2>POST, subir JSON y formularios</h2><br>Rocket se integra con Serde para lograr una serialización/deserialización con JSON inocua. Si añadimos las dependencias <strong>serde,</strong> <strong>serde_json</strong> y <strong>serde_derive</strong> al fichero Cargo.toml podemos tener un método que acepete una petición POST solo para mensajes del tipo application/json con deserialización incorporada.<br><pre class="lang:rust decode:true">#![feature(plugin)]<br>#![plugin(rocket_codegen)]<br><br>#[macro_use] extern crate rocket_contrib;<br>#[macro_use] extern crate serde_derive;<br>extern crate serde_json;<br>extern crate rocket;<br><br>use rocket_contrib::{JSON, Value};<br><br>#[derive(Serialize,Deserialize)]<br>struct User{<br>    name: String,<br>    email: String<br>}<br><br>#[post("/upload", format="application/json", data="&lt;user&gt;")]<br>fn upload_user(user: JSON&lt;User&gt;) -&gt; JSON&lt;Value&gt; {<br>    JSON(json!({<br>        "status" : 200,<br>        "message" : format!("Usuario {} registrado con éxito",user.email)<br>    }))<br>}<br><br>fn main() {<br>    rocket::ignite().mount("/",routes![upload_user]).launch();<br>}<br></pre><br>Si el JSON no se ajusta a la estructura User simplemente se descarta devolviendo un error 400.<br><br>Lo mismo que es posible hacer con JSON puede hacerse con formularios usando el trait FromForm.<br><pre class="lang:rust decode:true">#![feature(plugin,custom_derive)]<br>#![plugin(rocket_codegen)]<br><br>#[macro_use] extern crate rocket_contrib;<br>#[macro_use] extern crate serde_derive;<br>extern crate serde_json;<br>extern crate rocket;<br><br>use rocket_contrib::{JSON, Value};<br>use rocket::request::{FromForm, Form};<br><br>#[derive(FromForm)]<br>struct User{<br>    name: String,<br>    email: String<br>}<br><br>#[post("/upload", data="&lt;user&gt;")]<br>fn upload_user(user: Form&lt;User&gt;) -&gt; String {<br>    format!("Hola {}",user.get().name)<br>}<br><br>fn main() {<br>    rocket::ignite().mount("/",routes![upload_user]).launch();<br>}<br></pre><br><h2>Errores</h2><br>En Rocket, como es lógico, es posible crear páginas personalizadas para cada error.<br><pre class="lang:rust decode:true">#![feature(plugin,custom_derive)]<br>#![plugin(rocket_codegen)]<br><br>#[get("/")]<br>fn index() -&gt; &amp;'static str {<br>    "El cohete ha despegado"<br>}<br><br>#[error(404)]<br>fn not_found() -&gt; &amp;'static str {<br>    "La página no ha podido ser encontrada"<br>}<br><br>fn main() {<br>    rocket::ignite().mount("/",routes![index]).catch(errors![not_found]).launch();<br>}<br></pre><br>La lista de métodos que manejan errores hay que pasarla en el método catch de rocket::ignite<br><h2>Respuestas</h2><br>Rocket nos permite devolver cualquier cosa que implemente el trait <strong>Responder</strong>. Algunos tipos ya lo llevan como String, File, JSON, Option y Result. Pero nada nos impide que nuestros propios tipos implementen Responder. Con Responder tenemos el contenido y el código de error (que en la mayoría de casos será 200). En el caso de Result es muy interesante, pues si Err contiene algo que implementa Responder, se devolverá la salida que implemente también, pudiendo así hacer mejores respuestas de error, mientras que si no lo hacen se llamará al método que implemente el error 500 de forma genérica. Con Option, si el valor es Some se devolverá el contenido, si es None se generará un error 404.<br><pre class="lang:rust decode:true">#![feature(plugin,custom_derive)]<br>#![plugin(rocket_codegen)]<br><br>#[macro_use] extern crate rocket_contrib;<br>extern crate rocket;<br><br>use rocket::response::{self, Responder, Response};<br>use std::io::Cursor;<br>use rocket::http::ContentType;<br><br>struct Pelicula{<br>    nombre: &amp;'static str,<br>    pais: &amp;'static str<br>}<br><br>impl&lt;'r&gt; Responder&lt;'r&gt; for Pelicula{<br>    fn respond(self) -&gt; response::Result&lt;'r&gt; {<br>        Response::build()<br>        .sized_body(Cursor::new(format!("La película {} se hizo en {}",self.nombre,self.pais)))<br>        .header(ContentType::new("text","plain"))<br>        .ok()<br>    }<br>}<br><br>#[get("/pelicula/&lt;pelicula&gt;")]<br>fn pelicula(pelicula: &amp;str) -&gt; Result&lt;Pelicula,String&gt; {<br>    let intocable = Pelicula{<br>        nombre: "Intocable",<br>        pais: "Francia"<br>    };<br>    let madMax = Pelicula{<br>        nombre: "Mad Max",<br>        pais: "Estados Unidos"<br>    };<br>    match pelicula {<br>        "Intocable" =&gt; Ok(intocable),<br>        "Mad Max" =&gt; Ok(madMax),<br>        _ =&gt; Err(format!("No existe esa película en nuestra base de datos"))<br>    }<br>}<br><br>#[get("/")]<br>fn index() -&gt; Result&lt;String,String&gt; {<br>    Err(format!("No implementado"))<br>}<br><br>#[error(404)]<br>fn not_found() -&gt; &amp;'static str {<br>    "La página no ha podido ser encontrada"<br>}<br><br>fn main() {<br>    rocket::ignite().mount("/",routes![index,pelicula]).catch(errors![not_found]).launch();<br>}<br></pre><br>Este ejemplo para /pelicula/Intocable devolverá: La película Intocable se hizo en Francia mientras que para /pelicula/Ratatouille dirá No existe esa película en nuestra base de datos.<br><br>También es posible devolver plantillas. Rocket se integra por defecto con Handlebars y Tera, aunque no es muy costoso añadir cualquier otra como Maud.<br><h2>Conclusión</h2><br>Rocket es un prometedor web framework para Rust, bastante idiomático, que se integra muy bien con el lenguaje. Espero con ansia las nuevas veriones. Es posible que la API cambie bastante hasta que salga la versión 1.0, no obstante así es como ahora mismo funciona.]]></description>
                <comments>https://blog.adrianistan.eu/tutorial-rocket-echa-volar-tus-webapps-rust</comments>
                <pubDate>Sat, 15 Apr 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Rust en 5 minutos - #PicnicCode2017</title>
                <link>https://blog.adrianistan.eu/rust-5-minutos-picniccode2017</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/rust-5-minutos-picniccode2017</guid>
                <description><![CDATA[https://www.youtube.com/watch?v=nlZDKgpxDAk<br><br>El pasado 17 de marzo fue el Picnic Code en la Universidad de Valladolid. En el evento, organizado por el GUI y Cylicon Valley, tuve el honor de dar una Lightning Talk. Se trata de una charla de 5 minutos muy rápidos para exponer una idea. Mi Lightning Talk titulada Rust en 5 minutos iba dirigida a enseñar, sin entrar en muchos detalles, aquellas características que hacen de Rust un lenguaje seguro. No estaba nervioso hasta que subí al escenario... ¡y entonces ya empecé a estarlo! Hay algunos fallos frutos de los nervios y las diapositivas... bueno, podían haber funcionado mejor.<br><br>En cualquier caso, estáis invitados a ver Rust en 5 minutos.]]></description>
                <comments>https://blog.adrianistan.eu/rust-5-minutos-picniccode2017</comments>
                <pubDate>Tue, 28 Mar 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>The Pitts Circus, la primera película financiada con Ethereum</title>
                <link>https://blog.adrianistan.eu/the-pitts-circus-la-primera-pelicula-financiada-ethereum</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/the-pitts-circus-la-primera-pelicula-financiada-ethereum</guid>
                <description><![CDATA[Todos los días me sorprendo de las cosas que son capaces de hacer la gente con <a href="https://blog.adrianistan.eu/2016/03/02/ethereum-y-smartcontracts/">Ethereum</a>. Uno de las primeras apliaciones que surgieron fue el crowdfunding distribuido. ¿Te imaginas un Kickstarter P2P? Pues con Ethereum es posible implementarlo.<br><br>Siguiendo estas ideas surge <a href="http://the-pitts-circus.com/">The Pitts Circus</a>, la primera película financiada con Ethereum.<br><br><a href="https://files.adrianistan.eu/pittsfamilycircus4-200x300.jpg"><img class="aligncenter size-full wp-image-723" src="https://files.adrianistan.eu/pittsfamilycircus4-200x300.jpg" alt="" width="200" height="300" /></a><br><h2>La película</h2><br>Se trata de una cienta de terror-comedia que incorpora las habilidades, talento cómico y naturaleza única de una familia de circo real de Australia. La familia circense Pitts ha estado de gira por los cinco continentes con su espectáculo. Cecil Pitt también es un músico consumado, forma parte de la banda musical <a href="https://www.facebook.com/The-Barkers-Vale-Brothers-142213279155096/">Barkers Vale Brothers</a>. Él se hará cargo de la banda sonora de la película ya que participó en todos los <a href="http://akenevilthing.com/projects/">episodios de Karnydale (otro proyecto de los productores de la cinta)</a> La familia contará con: los dos padres, un chico de 11 años, una niña de 6 años y un niño de 1 año. Disfruta de este cuento de hadas circense, ambientado en el remoto oeste de Australia y Suiza, con acróbatas, payasos, un sacerdote, niños diabólicos y caníbales.<br><h2>¿Cómo funciona?</h2><br>Inicialmente se han creado 666 acciones del proyecto, estas se compran vía Ethereum y el smart contract asociado se encargará de repartirnos los beneficios que genere la película (si los hubiera) durante 20 años. Para evitar situaciones similares a lo ocurrido con la DAO, el crowdfunding no se basa exclusivamente en Ethereum sino que hay un respaldo legal en Suiza. La compañía productora es aKenEvilThing, una empresa fundada en 2014 en Suiza con varios proyectos anteriores.<br><br>Si quiéres colaborar con la financiación del proyecto lo único que tienes que hacer es enviar una cantidad determinada de Ether según el número de acciones que quieras comprar (1 acción = 10 ETH) a la dirección que aparece en su página web.<br><br>Ahora mismo el equipo está de rodaje y se espera que para finales de 2017 ya pueda presentarse a algunos festivales de cine. Algunos actores confirmados aparte de la familia Pitts son: <strong>Carlos Henriquez</strong> y <strong>Matto Kämpf</strong>.<br><br><iframe src="https://player.vimeo.com/video/120114056" width="640" height="360" frameborder="0" allowfullscreen="allowfullscreen"></iframe><br><br>&nbsp;<br><h2>Entrevista con Tony Caradonna</h2><br><strong>Pregunta: Tony, ¿puedes contarnos un poco acerca de la película?</strong><br><strong>Tony Caradonna:</strong> “The Pitts Circus es una película de horror-comedia ficticia protagonizada por los “Pitts”; una familia real de un circo a las afueras de Australia, justo donde las primeras escenas están siendo filmadas antes de viajar a Suiza para terminar el rodaje. En 2018 la película independiente se presentará en festivales de cine antes de su lanzamiento en cines selectos en todo el mundo y por Video-on-Demand. Lo que realmente nos emociona es que la película es la primera del mundo que se financia completamente con Ethereum ".<br><strong>Pregunta: Como realizaron esto? Y por qué decidieron financiar el film con Ethereum?</strong><br><strong>Tony Caradonna:</strong> "La película es financiada por 666 acciones de crowdfunded, con un costo por acción de diez Ethereum (que son aproximadamente 97 dólares americanos para el momento en que se escribió esta nota). Los accionistas recibirán el 50% de los ingresos de la película por un período de 20 años garantizados por contratos inteligentes: contratos auto ejecutables basados en leyes suizas que utilizan protocolos informáticos basados en la cadena de bloques de Ethereum para verificar y hacer cumplir las cláusulas acordadas por sus firmantes. Esto se traduce en un acuerdo de contrato más rápido y seguro para todos. El Pitts Circus es uno de los primeros proyectos rentables en el mundo real que está conectado a la cadena de bloques, por lo que a mi parecer hace que sea una gran manera de mostrar a la gente común la propuesta de valor en blockchain y moneda digital”.<br><strong>Pregunta: ¿Qué más podemos esperar de esta película y la compañía productora?</strong><br><strong>Tony Caradonna:</strong> "Queremos ver más participación de la comunidad en la producción de películas, desde el casting hasta el argumento. Hemos desarrollado nuestro propio activo accionario para ayudar a conseguir lo que llamamos EMV-coin (Ethereum Movie Coin). Nuestras monedas EMV se distribuirán gratuitamente a todos nuestros accionistas, con una ICO planeada para adquirir monedas adicionales en el futuro y que podría aumentar la participación de los consumidores en futuras producciones. Las monedas permitirán a los consumidores agregar o votar por propuestas, nuevos contenidos y la dirección estratégica de futuras producciones. Estamos creando un caso real de negocios a escala mundial que muestra lo que es posible hacer con la plataforma de Ethereum ".<br><strong>Pregunta: ¿Entonces la moneda EMV es un activo que nos da la posibilidad de decidir sobre futuras películas permitiéndonos votar para evitar historias exageradas y finales que dan paso a nuevas experiencias en pantalla?</strong> ¿Las monedas EMV también benefician el ROI de la película?<br><strong>Tony Caradonna:</strong> "Sí, exactamente. Este enfoque de toma de decisiones de producción significa que los consumidores ven lo que quieren ver. Los contribuyentes pueden intercambiar nuevas ideas con la posibilidad de que la comunidad vote para que se produzca alguna de ellas. Esto equivale a menores costos de producción para la película que sugiera la comunidad, y mayores ventas, ya que las ideas han sido previamente validadas por el consumidor. La combinación de presupuestos de producción más bajos con mayores ingresos recompensa a los inversionistas con mejores beneficios. Los poseedores de monedas EMV se benefician del contenido que ellos mismos ayudan a generar”.<br><strong>Pregunta: ¿Qué puedes ver para la industria cinematográfica en el futuro después del lanzamiento de The Pitts Circus?</strong><br><strong>Tony Caradonna:</strong> "Puedo prever que muchas películas se producirán con un modelo similar al nuestro a través de la contribución de la comunidad. Los presupuestos de producción más bajos que estas películas exigen llevarán a que muchas nuevas ideas puedan llegar a la pantalla grande, y que cualquiera pueda aparecer en los créditos de una película. Si Ethereum continúa en su actual avance, espero que el modelo de producción en comunidad de EMV se vuelva muy popular, con muchas personas deseando comprar monedas EMV en los intercambios para también poder participar en el modelo que hemos creado. Esto resultaría muy favorable para el precio en que se cotice en el mercado el EMV ".<br><strong>Pregunta: ¿Otros planes para el futuro?</strong><br><strong>Tony Caradonna</strong>: "Hemos realizado varias alianzas de negocio con compañías relacionadas al área de las criptodivisas que ayudarán a apoyar la red de monedas EMV incluyendo: Ledger Wallet, Trezor y Ether Card para que el intercambio de monedas de EMV y Ether sea seguro y simple, COVAL para cifrar Bitcoin y Ether en archivos de audio con su plataforma 'Vocal'. Estas alianzas permitirán la colocación de productos digitales en el film”.<br><h5>Sobre Tony:</h5><br>Tony Caradonna tiene un Master en Física y Filosofía. Financió sus estudios haciendo espectáculos como comediante y artista de circo. Estuvo de gira con varios circos en todo el mundo. Allí conoció a Ken Fanning, un director y artista de circo. También conoció a la Familia Pitts, una Familia Australiana de Circo. Su primer encuentro fue en una gira con otro circo australiano 2000/2001. Han trabajado juntos regularmente en varias ocasiones desde entonces. Actualmente, el financiamiento del Swiss National Language Cooperation Arts funding ya está concedido y aún está pendiente un Financiamiento del Swiss National Film Arts]]></description>
                <comments>https://blog.adrianistan.eu/the-pitts-circus-la-primera-pelicula-financiada-ethereum</comments>
                <pubDate>Fri, 17 Feb 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>¿Está usted de broma Sr. Feynman?</title>
                <link>https://blog.adrianistan.eu/esta-usted-broma-sr-feynman</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/esta-usted-broma-sr-feynman</guid>
                <description><![CDATA[Acabo de leer un libro, un señor libro, uno de los que más me han gustado recientemente. Y ha sido un placer leerlo.<br><br><a href="http://amzn.to/2lS8mt1"><img class="aligncenter wp-image-719 size-large" src="https://files.adrianistan.eu/DSC_0028-e1487195512172-576x1024.jpg" width="576" height="1024" /></a>Se trata de <a href="http://amzn.to/2lS8mt1"><em>¿Está usted de broma Sr. Feynman?</em></a> y es una especie de autobiografía de la vida del físico Richard Feynman. El libro se estructura en anécdotas que va contando que si bien suelen tener un elemento de inicio cronologicamente ordenado con el resto, cada anécdota puede estructurarse de forma diferente.<br><br>El libro es muy divertido y derrocha originalidad. Es un claro ejemplo de por qué prefiero los libros de no ficción, ya que superan a la ficción y por mucho.<br><br>La vida de Richard Feynman, uno de los grandes físicos del siglo XX, es una completa inspiración. Una invitación a ser curioso, a no tener miedo al qué dirán, a dejar de preocuparse y a disfrutar de las cosas de la vida.<br><br>Algunas anécdotas interesantes:<br><ul><br> 	<li>La apertura de cajas fuertes en Los Alamos</li><br> 	<li>Cabrear a la censura de Los Alamos</li><br> 	<li>Introducirse en el mundo de la pintura y llegar a realizar un cuadro para un burdel</li><br> 	<li>Tocar la frigideira en una banda de Río de Janeiro</li><br> 	<li>Recibir un premio Nobel</li><br> 	<li>Dar un seminario de biología en Harvard (acabando justo él de terminar física)</li><br> 	<li>Hablar en un idioma inventado en una actuación de scouts</li><br> 	<li>Ligar en un local de carretera</li><br> 	<li>Y muchas más</li><br></ul><br>Mi más sincera recomendación. Yo ahora empiezo con la segunda parte <em>¿Qué te importa lo que piensen los demás?</em><br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/esta-usted-broma-sr-feynman</comments>
                <pubDate>Thu, 16 Feb 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>¿Cómo borrar el historial?</title>
                <link>https://blog.adrianistan.eu/como-borrar-el-historial</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/como-borrar-el-historial</guid>
                <description><![CDATA[Los sistemas informáticos actuales son auténticos engullidores de datos. Prácticamente cualquier acción que hagamos queda registrada. Muchas veces no nos importa demasiado y es incluso conveniente para nosotros pero a veces es necesario eliminar nuestros preciados datos.<br><br>Un rastro digital que dejamos cuando navegamos por la web es el historial. Como si fuese Pulgarcito, el navegador va guardando las páginas que hemos visitado y su orden. Esto puede ser interesante si queremos volver a visitar una página que visitamos hace unos días pero no nos acordamos de su nombre exacto.<br><br><a href="https://files.adrianistan.eu/HistorialFirefox.png"><img class="wp-image-711 size-full" src="https://files.adrianistan.eu/HistorialFirefox.png" width="336" height="706" /></a> El botón que da acceso al historial en Firefox. En otros navegadores es similar.<br><br><a href="https://files.adrianistan.eu/HistorialWindows.png"><img class="size-large wp-image-712" src="https://files.adrianistan.eu/HistorialWindows-1024x561.png" alt="" width="840" height="460" /></a> Como puedes observar es fácil saber en qué páginas has estado antes. Incluyendo redes sociales.<br><br>Pero no creas que el historial se guarda solo en los navegadores (Explorer, Chrome, Firefox, Opera, Safari, ...) sino que ciertas webs también construyen sus propios historiales. YouTube o Google construyen sus propios historiales que construyen si el usuario ha iniciado sesión en el sistema.<br><br>Estos historiales también se pueden borrar como veremos más adelante.<br><br>No obstante, el navegador guarda otros datos de nuestra navegación. Cualquier persona que accediese a nuestro ordenador. Estos datos son las cookies (muy famosas por los insistentes avisos), la caché, los fomularios y las contraseñas que hayamos decidido guardar para nuestra comodidad.<br><br>Las cookies son necesarias para la navegación web tal como la entendemos hoy, sin embargo puede ser interesante borrarlas de forma regular. ¿Sabes por qué Google implementó la navegación privada en Chrome? Porque quería evitar que la gente borrase sus cookies cuando visitaban sitios de los que no querían dejar rastro en su ordenador. De ese modo Chrome seguía teniendo las cookies y los usuarios entran en un modo de incógnito. No obstante, eliminar las cookies de forma manual sigue siendo algo recomendable.<br><br>La caché la forman archivos (estilos, imágenes, fuentes,...) que se descargan la primera vez que visitamos una página. Estos se guardan en nuestro ordenador para que una vez accedamos de nuevo a esa web esta tarde menos en cargar. Es por ello que la caché está llena de archivos que se supone que nunca van a cambiar. Aun así, esta caché puede delatar las páginas que visitamos.<br><br>Para obtener más información los compañeros de <a href="http://borrar-historial.com">http://borrar-historial.com</a> se han pegado un currazo dando métodos efectivos y fiables de como borrar un historial.<br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/como-borrar-el-historial</comments>
                <pubDate>Wed, 15 Feb 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Tutorial de Maud, motor de plantillas HTML para Rust</title>
                <link>https://blog.adrianistan.eu/tutorial-maud-motor-plantillas-html-rust</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/tutorial-maud-motor-plantillas-html-rust</guid>
                <description><![CDATA[Seguimos aprendiendo en el blog sobre interesantes proyectos hechos para Rust. Ya hemos visto <a href="https://blog.adrianistan.eu/2016/11/20/usando-iron-web-framework-rust/">Iron</a>, <a href="https://blog.adrianistan.eu/2017/01/17/tutorial-piston-programa-juegos-rust/">Piston</a> y <a href="https://blog.adrianistan.eu/2017/01/27/tutorial-neon-combina-node-js-rust/">Neon</a>. Hoy veremos <a href="https://maud.lambda.xyz/">Maud</a>, un potente motor de plantillas que se centra en la eficiencia. Maud se compara a otras soluciones como Razor, ERB, Liquid,  Handlebars o Jade pero esta vez escribiremos nuestro HTML en Rust. ¿Locura? No, y de hecho funciona de forma bastante transparente. Vamos a verlo en acción<br><br><a href="https://files.adrianistan.eu/screenshot-lambda.xyz-2017-01-27-22-07-51.png"><img class="size-full wp-image-700" src="https://files.adrianistan.eu/screenshot-lambda.xyz-2017-01-27-22-07-51.png" alt="" width="605" height="374" /></a> Comparativa de velocidad de diferentes motores. Maud es el más rápido (menos es mejor)<br><h2>Instalando Maud</h2><br>Maud usa plugins del compilador, una característica que a día de hoy no está activado ni en el canal estable ni el canal beta, solamente en el canal nightly. Para obtener una copia de Rust nightly lo ideal es usar <a href="http://rustup.rs">Rustup</a>.<br><br>Una vez hecho eso, creamos un nuevo proyecto y añadimos las dependencias de Maud al fichero Cargo.toml.<br><br><pre class="lang:default decode:true"><br>maud = &quot;*&quot;<br>maud_macros = &quot;*&quot;<br></pre><br><br><h2>Una simple plantilla</h2><br>Ahora abrimos el archivo src/main.rs y vamos a empezar a usar Maud.<br><br><pre class="lang:rust decode:true"><br>#![feature(plugin)]<br>#![plugin(maud_macros)]<br><br>extern crate maud;<br><br>fn main(){<br>        let name = &quot;Adrián&quot;;<br>        let markup = html!{<br>            p { &quot;Hola, soy &quot; (name) &quot; y estoy usando Maud&quot;}<br>        };<br>        println!(&quot;{}&quot;, markup.into_string());<br>}<br><br></pre><br><br>La potencia de Maud se ve en la mega-macro <strong>html!</strong>. En esta macro escribiremos la plantilla que será compilada de forma nativa a Rust, lo que nos asegura una velocidad de ejecución excepcional. En este caso la salida será una etiqueta P de párrafo con la variable interpolada.<br><br><a href="https://files.adrianistan.eu/MaudP.png"><img class="aligncenter size-full wp-image-699" src="https://files.adrianistan.eu/MaudP.png" alt="" width="641" height="96" /></a>Simple, ¿no?<br><h2>PreEscaped y otros elementos básicos</h2><br>Por defecto en Maud todos el texto se convierte a HTML seguro. Es decir, no se pueden introducir etiquetas nuevas en el texto. Si por alguna razón necesitásemos añadir etiquetas nuevas podemos usar PreEscaped, que no realiza esta transformación de seguridad. Veamos el siguiente código:<br><br><pre class="lang:rust decode:true"><br>#![feature(plugin)]<br>#![plugin(maud_macros)]<br><br>extern crate maud;<br><br>use maud::PreEscaped;<br><br>fn main(){<br>        let name = &quot;Adrián&quot;;<br>        let markup = html!{<br>                p { &quot;Hola, soy &quot; (name) &quot; y estoy usando Maud&quot; }<br>                p { &quot;&lt;h5&gt;Esto no funcionará&lt;/h5&gt;&quot; }<br>                p { (PreEscaped(&quot;&lt;h5&gt;Esto sí funcionará&lt;/h5&gt;&quot;)) }<br>        };<br>        println!(&quot;{}&quot;, markup.into_string());<br>}<br><br></pre><br><br>El primer H5 se convertirá a código HTML seguro, es decir, no añadirá la etiqueta, en cambio se verá h5 en la web. Por contra con PreEscaped se añadirá la etiqueta h5 tal cual.<br><br>Los elementos son muy fáciles de añadir en Maud y por lo general no deberías usar PreEscaped salvo contadas ocasiones. Veamos como añadir más etiquetas.<br><br><pre class="lang:rust decode:true"><br>#![feature(plugin)]<br>#![plugin(maud_macros)]<br><br>extern crate maud;<br><br>fn main(){<br>        let name = &quot;Adrián&quot;;<br>        let markup = html!{<br>                p { &quot;Hola, soy &quot; (name) &quot; y estoy usando Maud&quot; }<br>                p {<br>                        &quot;Si la montaña no viene a Mahoma&quot;<br>                        br /<br>                        &quot;Mahoma va la montaña&quot;<br>                        small em &quot;Atribuido a Francis Bacon&quot;<br>                }<br>        };<br>        println!(&quot;{}&quot;, markup.into_string());<br>}<br><br></pre><br><br>En este ejemplo vemos como las etiquetas que no llevan texto como BR o INPUT debemos cerrarlas con una barra. Por otro lado es posible aglutinar varios niveles de etiquetas en una sola línea ( SMALL-&gt;EM-&gt;Texto).<br><h2>Atributos, clases e IDs</h2><br>En Maud es posible asignar atributos también, usando literales o variables. Para los atributos de texto la sintaxis es muy parecida a HTML.<br><br><pre class="lang:rust decode:true"><br>#![feature(plugin)]<br>#![plugin(maud_macros)]<br><br>extern crate maud;<br><br>fn main(){<br>        let name = &quot;Adrián&quot;;<br>        let amazon = &quot;http://www.amazon.com&quot;;<br>        let markup = html!{<br>                p { &quot;Hola, soy &quot; (name) &quot; y estoy usando Maud&quot; }<br>                p {<br>                        &quot;Este texto contiene enlaces a &quot;<br>                        a href=&quot;http://www.google.com&quot; &quot;Google&quot;<br>                        &quot; y a &quot;<br>                        a href=(amazon) &quot;Amazon&quot;<br>                }<br>        };<br>        println!(&quot;{}&quot;, markup.into_string());<br>}<br></pre><br><br>Además existen en HTML atributos vacíos. Es decir, atributos que con su sola presencia basta y normalmente no llevan valor asignado.<br><br><pre class="lang:rust decode:true"><br>#![feature(plugin)]<br>#![plugin(maud_macros)]<br><br>extern crate maud;<br><br>fn main(){<br>        let name = &quot;Adrián&quot;;<br>        let allow_editing = true;<br>        let markup = html!{<br>                p { &quot;Hola, soy &quot; (name) &quot; y estoy usando Maud&quot; }<br>                p contenteditable?[allow_editing] {<br>                }<br>        };<br>        println!(&quot;{}&quot;, markup.into_string());<br>}<br></pre><br><br>En este caso el atributo contenteditable se añade si la variable allow_editing es true. Si queremos añadir atributos vacíos en cualquier circunstancia podemos simplemente quitar [allow_editing] y dejar p contenteditable? {}.<br><br>Los IDs y las clases se añaden usando la sintaxis esperable, puntos y almohadillas.<br><br><pre class="lang:rust decode:true"><br>#![feature(plugin)]<br>#![plugin(maud_macros)]<br><br>extern crate maud;<br><br>fn main(){<br>        let name = &quot;Adrián&quot;;<br>        let markup = html!{<br>                p { &quot;Hola, soy &quot; (name) &quot; y estoy usando Maud&quot; }<br>                p.red.font-big#editor contenteditable? {<br>                }<br>        };<br>        println!(&quot;{}&quot;, markup.into_string());<br>}<br></pre><br><br><h2>Estructuras de control</h2><br>Maud soporta las estructuras de control básicas de Rust, if/else, if/let, for in y match.<br><br><pre class="lang:rust decode:true"><br>#![feature(plugin)]<br>#![plugin(maud_macros)]<br><br>extern crate maud;<br><br>fn main(){<br>        let loggedIn = false;<br>        let email = Some(&quot;mariscal@example.com&quot;);<br>        let fonts = [&quot;Arial&quot;,&quot;Times New Roman&quot;,&quot;Verdana&quot;];<br>        let markup = html!{<br>                @if loggedIn {<br>                        h1 { &quot;Has iniciado sesión&quot; }<br>                } @else {<br>                        h1 { &quot;Por favor, inicia sesión primero&quot; }<br>                }<br><br>                @if let Some(mail) = email {<br>                        p { &quot;Su email es &quot; (mail) }<br>                }<br>                ol {<br>                        @for font in &amp;fonts {<br>                                li (font)<br>                        }<br>                }<br><br>        };<br>        println!(&quot;{}&quot;, markup.into_string());<br>}<br></pre><br><br>Como vemos, Maud posee suficientes características para ser interesante. Maud además permite extender el motor para representar cualquier tipo de dato. Por defecto Maud mirá si está implementado std::fmt::Display pero si queremos añadir etiquetas extra este método no funcionará. En cambio si se implementa la trait maud::Render tendrás control total sobre como va a mostrar Maud las variables de ese tipo. En la crate <em>maud_extras</em> se encuentran implementaciones por ejemplo de Markdown para Maud.<br><br>Maud se integra además con web frameworks de Rust, en especial con Iron y con Rocket. Sin duda, una de mis crates favoritas.]]></description>
                <comments>https://blog.adrianistan.eu/tutorial-maud-motor-plantillas-html-rust</comments>
                <pubDate>Tue, 31 Jan 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Tutorial de Neon - Combina Node.js con Rust</title>
                <link>https://blog.adrianistan.eu/tutorial-neon-combina-node-js-rust</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/tutorial-neon-combina-node-js-rust</guid>
                <description><![CDATA[Hoy en día muchas webs se diseñan con Node.js. Es una solución fantástica para respuestas rápidas pero numerosos benchmarks han demostrado que su rendimiento empeora en respuestas complejas. Estos mismos benchmarks recomiendan usar Java o .NET si preveemos que nuestra aplicación web va a generar respuestas complejas. Sin embargo renunciar a las ventajas de Node.js no es del agrado de muchos. Afortunadamente hay otra solución, usar Rust. Todo ello gracias a <a href="https://www.neon-bindings.com/">Neon</a>.<br><br>Con Neon podemos generar módulos para Node.js que son escritos y compilados en Rust con las ventajas que supone desde un punto de vista de rendimiento y con la certeza de que en Rust la seguridad está garantizada.<br><br>Usando Neon puedes desarrollar tu aplicación en Node.js y si alguna parte tiene problemas de rendimiento sustituirla por su equivalente en Rust. Para el ejemplo voy a hacer un módulo de Markdown.<br><h2>Instalando Neon</h2><br>En primer lugar instalamos la herramienta de Neon desde npm.<br><br><pre class="lang:default decode:true"><br>npm install -g neon-cli<br></pre><br><br>Una vez esté instalado podemos usar la herramienta de Neon para construir un esqueleto de módulo. Este esqueleto tendrá dos partes, un punto de entrada para Node.js y otro para Rust.<br><br><pre class="lang:default decode:true"><br>neon new PROYECTO<br></pre><br><br><a href="https://files.adrianistan.eu/NeonNew.png"><img class="aligncenter size-full wp-image-695" src="https://files.adrianistan.eu/NeonNew.png" alt="" width="644" height="367" /></a>Hacemos un npm install como nos indica. Esto no solo obtendrá dependencias de Node.js sino que también se encargará de compilar el código nativo en Rust.<br><h2>El código Node.js</h2><br>Nuestro módulo contiene un archivo de Node.js que sirve de punto de entrada. Allí lo único que se debe hacer es cargar el módulo en Rust y hacer de pegamento. Puede ser algo tan simple como esto:<br><br><pre class="lang:js decode:true"><br>var addon = require(&quot;../native&quot;);<br><br>module.exports = addon; // se exportan todos los métodos del módulo nativo<br></pre><br><br>Aunque si queremos añadir un tratamiento específico también es posible.<br><br><pre class="lang:js decode:true"><br>var addon = require(&quot;../native&quot;);<br><br>module.exports = {<br>    render: function(str){<br>        return addon.render(str);<br>    }<br>}<br></pre><br><br><h2>El código en Rust</h2><br>Nos dirigimos ahora al archivo <em>native/src/lib.rs</em>. Ahí definimos los métodos nativos que va a tener el módulo. Lo hacemos a través de la macro <strong>register_module!</strong>.<br><br><pre class="lang:rust decode:true"><br>register_module!(m,{<br>    m.export(&quot;render&quot;,render)<br>});<br></pre><br><br>Ahora vamos a implementar la función render, que toma el texto en Markdown y lo devuelve en HTML.<br><br><pre class="lang:rust decode:true"><br>fn render(call: Call) -&gt; JsResult&lt;JsString&gt; {<br>    let scope = call.scope; // obtener el contexto<br>    let md: Handle&lt;JsString&gt; = try!(try!(call.arguments.require(scope,0)).check::&lt;JsString&gt;()); // obtener primer argumento como JsString. aquí puede hacerse tratamiento de fallos<br>    let string = md.value(); // Pasar JsString a String<br>    let html: String = markdown::to_html(&amp;string); // usamos la crate markdown para renderizar a html<br>    Ok(JsString::new(scope, &amp;html).unwrap()) // devolvemos un JsString con el contenido del HTML<br>}<br><br></pre><br><br>Las funciones que interactuan con Node deben devolver un JsResult de un tipo JsXXX, por ejemplo, JsString, JsUndefined o JsInteger. Siempre aceptan un argumento llamado de tipo Call que nos da toda la información necesaria y que podemos usar para sacar argumentos. El scope o contexto es muy importante y lo deberemos usar en las funciones que interactúen con Node.<br><h2>Código completo del fichero Rust</h2><br><br><pre class="lang:rust decode:true"><br>#[macro_use]<br>extern crate neon;<br>extern crate markdown;<br><br>use neon::vm::{Call, JsResult};<br>use neon::js::JsString;<br>use neon::mem::Handle;<br><br>fn render(call: Call) -&gt; JsResult&lt;JsString&gt; {<br>    let scope = call.scope;<br>    let md: Handle&lt;JsString&gt; = try!(try!(call.arguments.require(scope,0)).check::&lt;JsString&gt;());<br>    let string = md.value();<br>    let html: String = markdown::to_html(&amp;string);<br>    Ok(JsString::new(scope, &amp;html).unwrap())<br>}<br><br>register_module!(m, {<br>    m.export(&quot;render&quot;, render)<br>});<br></pre><br><br>Y no te olvides de añadir la dependencia <em>markdown</em> al fichero Cargo.toml.<br><h2>Probándolo</h2><br>Es muy fácil probarlo. Con el REPL de Node podemos probar partes del módulo antes de publicarlo a npm.<br><br>Para abrir el REPL ejecuta Node sin argumentos<br><br><pre class="lang:default decode:true"><br>node<br></pre><br><br>E introduce línea por línea lo que quieras probar:<br><br><pre class="lang:default decode:true"><br>var md = require(&quot;./&quot;);<br>md.render(&quot;__Esto es Markdown__&quot;);<br></pre><br><br>Verás el resultado por la pantalla:<br><br><a href="https://files.adrianistan.eu/NodeMd.png"><img class="aligncenter size-full wp-image-696" src="https://files.adrianistan.eu/NodeMd.png" alt="" width="642" height="96" /></a><br><br>Ahora que ya sabemos que funciona podemos publicarlo a npm si queremos con:<br><br><pre class="lang:default decode:true"><br>npm publish<br></pre><br><br>Aunque recuerda revisar antes el fichero package.json para especificar la licencia y la descripción. Una vez esté publicado su uso en un nuevo proyecto será muy sencillo y de forma transparente se compilará el código nativo.<br><br><pre class="lang:js decode:true"><br>var md = require(&quot;rust-markdown&quot;);<br>var http = require('http');<br>var fs = require(&quot;fs&quot;);<br><br>var server = http.createServer((req, res) =&gt; {<br>  fs.readFile(&quot;index.md&quot;,&quot;utf-8&quot;,function(err,data){<br>     var html = md.render(data);<br>     res.statusCode = 200;<br>     res.setHeader('Content-Type', 'text/html');<br>     res.end(html);<br>  });<br>});<br><br>server.listen(8080, &quot;127.0.0.1&quot;, () =&gt; {<br>  console.log(&quot;Server running&quot;);<br>});<br><br><br><br></pre><br>&nbsp;<br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/tutorial-neon-combina-node-js-rust</comments>
                <pubDate>Fri, 27 Jan 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Tutorial de Piston, programa juegos en Rust</title>
                <link>https://blog.adrianistan.eu/tutorial-piston-programa-juegos-rust</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/tutorial-piston-programa-juegos-rust</guid>
                <description><![CDATA[Ya he hablado de Rust varias veces en este blog. La última vez fue en el <a href="https://blog.adrianistan.eu/2016/11/20/usando-iron-web-framework-rust/">tutorial de Iron</a>, que os recomiendo ver si os interesa el tema del desarrollo web backend.<br><br>Hoy vamos a hablar de <a href="http://www.piston.rs">Piston</a>. Piston es una de las librerías más antiguas del ecosistema Rust. Surgida cuando todavía no existía Cargo, esta librería está pensada para el desarrollo de juegos. No es la única que existe en Rust pero sí la más conocida. Piston es una librería que te enseñará Rust de la mejor forma. Y ahora quiero disculparme, porque Piston no es una librería, son un montón, pero eso lo veremos enseguida. En primer lugar creamos un proyecto nuevo con Cargo.<br><pre>cargo new --bin ejemplo_piston<br>cd ejemplo_piston</pre><br>Ahora abrimos el archivo Cargo.toml, vamos a añadir las dependencias necesarias. Las dependencias en Piston son un poco complicadas, veamos:<br><ul><br> 	<li>Existen las dependencias core, implementan la API fundamental pero no pueden usarse por separado, son window, input y event_loop. Se usan a través de <em>piston</em>.</li><br> 	<li>Los backends de window, existen actualmente 3 backends: glutin, glfw, sdl2. Se importan manualmente.</li><br> 	<li>Graphics, una API 2D, no presente en core, pero al igual que las dependencias core necesita un backend.</li><br> 	<li>Los backends de graphics son varios: opengl, gfx y glium.</li><br> 	<li>Existe una dependencia que nos deja todo montado, <em>piston_window</em>. Esta trae por defecto el core de Piston, glutin, graphics y gfx.</li><br> 	<li>Luego existen dependencias extra, como por ejemplo para cargar texturas, estas las podremos ir añadiendo según las necesite el proyecto.</li><br></ul><br>Para simplificar añadimos <em>piston_window</em> únicamente:<br><br>&nbsp;<br><br><pre class="lang:rust decode:true"><br>[package]<br>name = &quot;piston_example&quot;<br>version = &quot;0.1.0&quot;<br>authors = [&quot;Adrián Arroyo Calle&quot;]<br><br>[dependencies]<br>piston_window = &quot;0.59.0&quot;<br></pre><br><br>&nbsp;<br><br>Ahora abrimos el archivo <em>main.rs</em>. Añadimos la crate de piston_window y los módulos que vamos a usar.<br><br><pre class="lang:rust decode:true"><br>extern crate piston_window;<br><br>use piston_window::*;<br>use std::path::Path;<br></pre><br><br>&nbsp;<br><br>Así mismo definimos un par de cosas para el resto del programa, la versión de OpenGL que usará Piston internamente y una estructura para guardar los movimientos de teclado.<br><br><pre class="lang:rust decode:true"><br>const OPENGL: OpenGL = OpenGL::V3_1;<br><br>struct Movement{<br>    up: bool,<br>    down: bool,<br>    left: bool,<br>    right: bool<br>}<br></pre><br><br>&nbsp;<br><br>En la función main podemos crear la ventana, especificando título y tamaño. Más opciones como V-Sync, pantalla completa y demás también están disponibles.<br><br><pre class="lang:rust decode:true"><br>fn main() {<br><br>    let mut window: PistonWindow = WindowSettings::new(&quot;Piston - Adrianistan&quot;,[640,480])<br>        .exit_on_esc(true)<br>        .opengl(OPENGL)<br>        .build()<br>        .unwrap();<br></pre><br><br>&nbsp;<br><br>Ahora cargamos la tipografía Sinkin Sans, que vamos a usar para dibujar texto en pantalla. Como hay dos posibles localizaciones comprobamos esos dos lugares antes de salir del programa si no se consigue cargar la fuente.<br><br><pre class="lang:rust decode:true"><br>    let mut glyphs = Glyphs::new(Path::new(&quot;SinkinSans.ttf&quot;),window.factory.clone()).unwrap_or_else(|_|{<br>        let glyphs = Glyphs::new(Path::new(&quot;target/debug/SinkinSans.ttf&quot;),window.factory.clone()).unwrap_or_else(|_|{<br>            panic!(&quot;Failed to open the font file. Check that SinkinSans.tff is in the folder&quot;);<br>        });<br>        glyphs<br>    });<br></pre><br><br>&nbsp;<br><br>Inicializamos la estructura de movimientos, generamos las dimensiones iniciales del rectángulo (que será un cuadrado en este caso), su color y la posición del ratón.<br><br><pre class="lang:rust decode:true"><br>    let mut mov = Movement{<br>        up: false,<br>        down: false,<br>        left: false,<br>        right: false<br>    };<br><br>    let mut dims = rectangle::square(50.0,50.0,100.0);<br>    let mut rect_color = color::BLACK;<br><br>    let mut mc: [f64; 2] = [0.0,0.0];<br></pre><br><br>&nbsp;<br><br>Ahora viene la parte importante, el bucle de eventos. El bucle va a funcionar infinitamente generando eventos por el camino (pueden ser eventos de inactividad también). Usamos la función draw_2d para dibujar en 2D. Hay dos maneras de dibujar un rectángulo, en primer lugar tenemos la forma abreviada y en segundo lugar una más completa que permite más opciones. Por último dibujamos el texto usando la fuente y realizando una transformación para que no quede el texto en la posición 0,0.<br><br><pre class="lang:rust decode:true"><br> while let Some(e) = window.next() {<br>        window.draw_2d(&amp;e, |c, g| {<br>            clear([0.5, 0.5, 0.5, 1.0], g);<br>            rectangle([1.0, 0.0, 0.0, 1.0], // color rojo, rgba<br>                        [0.0, 0.0, 100.0, 100.0], // dimensiones<br>                        c.transform, g); // transormacion y donde se va a dibujar<br><br>            let rect = Rectangle::new(rect_color);<br>            rect.draw(dims,&amp;c.draw_state,c.transform,g);<br>            text(color::BLACK,18,&quot;¡Saludos desde Piston!&quot;,&amp;mut glyphs,c.transform.trans(100.0,200.0),g); // aplicamos una transormacion, movemos las X 100 y las Y 200<br>        });<br></pre><br><br>&nbsp;<br><br>A continuación vamos a tratar cada evento de forma independiente, como todos los métodos devuelven Option, hemos de usar esta sintaxis con Some. En primer lugar tenemos un UpdateEvent, que básicamente nos informa del tiempo delta transcurrido. Recomiendo usar este evento para realizar los cambios en las geometrías, en este caso para mover el rectángulo.<br><br><pre class="lang:rust decode:true"><br>if let Some(upd_args) = e.update_args() {<br>            let dt = upd_args.dt;<br>            <br>                if mov.right {<br>                    dims[0] += dt*100.0;<br>                }<br>                if mov.left {<br>                    dims[0] -= dt*100.0;<br>                }<br>                if mov.up {<br>                    dims[1] -= dt*100.0;<br>                }<br>                if mov.down {<br>                    dims[1] += dt*100.0;<br>                }<br>        }<br></pre><br><br>Los siguientes dos eventos son opuestos, uno se activa cuando pulsamos una tecla y el otro cuando la soltamos. Comprobamos la tecla y modificamos la estructura movement en consecuencia.<br><br><pre class="lang:rust decode:true"><br>if let Some(Button::Keyboard(key)) = e.press_args() {<br>            if key == Key::W {<br>                mov.up = true;<br>            }<br>            if key == Key::S {<br>                mov.down = true;<br>            }<br>            if key == Key::A {<br>                mov.left = true;<br>            }<br>            if key == Key::D {<br>                mov.right = true;<br>            }<br>        };<br>        if let Some(Button::Keyboard(key)) = e.release_args() {<br>            if key == Key::W {<br>                mov.up = false;<br>            }<br>            if key == Key::S {<br>                mov.down = false;<br>            }<br>            if key == Key::A {<br>                mov.left = false;<br>            }<br>            if key == Key::D {<br>                mov.right = false;<br>            }<br>        };<br></pre><br><br>Por último, si queremos comprobar clicks del ratón hacemos algo similar. He añadido código para que cambio el color del rectángulo si pulsamos sobre él.<br><br><pre class="lang:rust decode:true"><br>if let Some(Button::Mouse(mb)) = e.release_args() {<br>            if mb == MouseButton::Left {<br>                let x = mc[0];<br>                let y = mc[1];<br>                if x &gt; dims[0] &amp;&amp; x &lt; dims[0] + dims[2] { if y &gt; dims[1] &amp;&amp; y &lt; dims[1] + dims[3] {<br>                        rect_color = if rect_color == [1.0,0.0,0.0,0.7]{<br>                            [0.0,1.0,0.0,0.7]<br>                        } else if rect_color == [0.0,1.0,0.0,0.7] {<br>                            [0.0,0.0,1.0,0.7]<br>                        } else{<br>                            [1.0,0.0,0.0,0.7]<br>                        }<br>                    }<br>                }<br>                <br>            }<br>        }<br></pre><br><br>A continuación un pequeño evento que guarda la última posición del ratón.<br><br><pre class="lang:rust decode:true"><br>        if let Some(mouse_cursor) = e.mouse_cursor_args() {<br>            mc = mouse_cursor;<br>        }<br>    }<br>}<br></pre><br><br>&nbsp;<br><br>Y con esto ya tenemos hecho un ejemplo en Piston.<br><br><a href="https://files.adrianistan.eu/Piston2DEjemplo.png"><img class="aligncenter size-full wp-image-686" src="https://files.adrianistan.eu/Piston2DEjemplo.png" alt="" width="657" height="518" /></a> <a href="https://files.adrianistan.eu/Piston2DEjemplo2.png"><img class="aligncenter size-large wp-image-687" src="https://files.adrianistan.eu/Piston2DEjemplo2.png" alt="" width="653" height="520" /></a><a href="https://files.adrianistan.eu/PistonEjemplo3.png"><img class="aligncenter size-full wp-image-688" src="https://files.adrianistan.eu/PistonEjemplo3.png" alt="" width="656" height="519" /></a><br><br>Si quieres tener un ejecutable para Windows sin que se muestre primero la consola debes compilar la versión que vas a distribuir con unos parámetros especiales. Si usas Rust con GCC usarás:<br><pre>cargo rustc --release -- -Clink-args="-Wl,--subsystem,windows"</pre><br>Si por el contrario usas Visual C++:<br><pre>cargo rustc --release -- -Clink-args="/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup"</pre><br>&nbsp;<br><br>Piston todavía se encuentra en fuerte desarrollo, en la API estan documentados todos los métodos pero aun así muchas veces no se sabe como hacer ciertas cosas. Piston soporta además 3D, contando con una librería especializada en vóxels. Veremos como evoluciona esta librería.]]></description>
                <comments>https://blog.adrianistan.eu/tutorial-piston-programa-juegos-rust</comments>
                <pubDate>Tue, 17 Jan 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>9front, un fork de Plan9 desarrollado por dementes</title>
                <link>https://blog.adrianistan.eu/9front-fork-plan9-desarrollado-dementes</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/9front-fork-plan9-desarrollado-dementes</guid>
                <description><![CDATA[Hace poco, por IRC conocí la existencia de este sistema operativo, <a href="http://9front.org/">9front</a>, derivado de Plan9. En principio pensé que sería un fork normal y corriente. Cuando entré en su página descubrí sin embargo que era algo completamente distinto.<br><br><a href="https://files.adrianistan.eu/9communitykissingerclinton.png"><img class="size-full wp-image-662" src="https://files.adrianistan.eu/9communitykissingerclinton.png" alt="" width="500" height="343" /></a> La 9community, formada por Henry Kissinger y Hillary Clinton, promotores del nuevo orden mundial<br><br>Su página web está cargada de minimalismo y memes, la gran mayoría de ellos inspirados en la guerra fría. Al entrar respiramos la esencia de 9front. Por ejemplo, el servidor web de la página es werc. Werc se define a sí mismo como el anti-framework anti-web. Sin bases de datos, solo ficheros y ¡programado en rc! Rc es un componente fundamental de 9front, una piedra angular. Se trata del lenguaje de shell de 9front. El equivalente en Linux sería Bash.<br><br>Werc es solo una de las cosas que acoge bajo su paraguas la organización Cat-V (de System-V, marcándose un chiste Unix de cuidado). El lema de Cat-V, <em>Considered harmful</em> (considerado peligroso) nos alerta de que encontraremos contenido que ha sido ajeno para la mayoría de la población. ¿Realmente estamos entrando en Cat-V en la iluminación de Internet? ¿Es entrar en Cat-V un equivalente moderno a salir de la cueva de Platón?<br><br>Algunos proyectos de Cat-V son:<br><ul><br> 	<li>9P, un sistema de archivos distribuido. Es usado por 9front.</li><br> 	<li>Acme, un editor de texto hecho por Rob Pike</li><br> 	<li>Sam, otro editor de texto, también de Rob Pike</li><br> 	<li>Fortune, una colección de archivos fortune de varios sistemas UNIX. Parémonos ante <a href="http://fortunes.cat-v.org/openbsd/">este fortune</a> etiquetado dentro de OpenBSD:</li><br></ul><br><blockquote><br><pre>(1) Alexander the Great was a great general.<br>(2) Great generals are forewarned.<br>(3) Forewarned is forearmed.<br>(4) Four is an even number.<br>(5) Four is certainly an odd number of arms for a man to have.<br>(6) The only number that is both even and odd is infinity.<br><br>Therefore, Alexander the Great had an infinite number of arms.</pre><br></blockquote><br>Corramos un tupido velo y sigamos.<br><br>¡La web oficial de Glenda!<br><br><a href="https://files.adrianistan.eu/glenda_space_medium.jpg"><img class="wp-image-663 size-full" src="https://files.adrianistan.eu/glenda_space_medium.jpg" width="239" height="276" /></a> Por si te lo preguntas, sí, la mascota de Go se encuentra inspirada en Glenda<br><br>El subdominio <em>harmful</em> está reservado para pensamientos y para expresar opiniones.<br><br><a href="https://files.adrianistan.eu/pc.jpg"><img class="aligncenter size-full wp-image-664" src="https://files.adrianistan.eu/pc.jpg" alt="" width="609" height="504" /></a>Se habla de economía, películas, periodismo, relaciones personales (de tratar con estúpidos, un artículo muy bueno), de lo políticamente correcto, de ciencia, de la seguridad post 11S, de sociedad (y para mi sorpresa contiene una opinión que yo también tengo pero que apenas he visto, el matrimonio debe ser eliminado del sistema legal de los países para pasar a ser algo cultural/religioso, similar al bautizo), se habla de software, de estándares y de palabras. Pretenden hablar de: SQL, fútbol, la Matrix, <a href="http://harmful.cat-v.org/software/svn/">svn</a>, postmodernismo, gnu, religión, vi, educación, apache, respeto, teoría de cuerdas, complejidad, comida 'orgánica', etanol, igualdad, metadata, lógica de "sentirse bien", teoría del trabajo, ...<br><br>HTTP 0.2, un intento de simplificar HTTP.<br><br>NineTimes, la web de noticias del mundo Plan9, Inferno y 9front.<br><br>Y por supuesto, un archivo de citas célebres.<br><h2>Volvamos a 9front</h2><br>Continué en la página de 9front, vi que tenía un Código de Conducta (algo que han introducido algunos proyectos open source pero que bajo mi punto de vista es innecesario y en algunos casos peligroso). Sin embargo en 9front no hay un código de conducta, hay varios. Cada vez que te metes en la página sale uno distinto. Y todos son muy <em>random</em>. Desde el credo en latín hasta una especie de receta para crear desarrolladores químicamente. La mitad son cánticos de alabanza a 9front. Yo veo en esto una sátira perfecta de lo que son algunos Códigos de Conducta.<br><br>Luego vemos para descargar las ISO, pero a estas alturas es lo que menos importa. A continuación encontramos una recopilación de webs alternativas para 9front. Hay una basada en 4chan, pero esta me gusta más:<br><br><a href="https://files.adrianistan.eu/screenshot-9front-org-2017-01-16-00-49-20.png"><img class="aligncenter size-large wp-image-665" src="https://files.adrianistan.eu/screenshot-9front-org-2017-01-16-00-49-20-1024x500.png" alt="" width="840" height="410" /></a>Y llegamos a mi sección favorita, <strong>Propaganda</strong>.<br><br><a href="https://files.adrianistan.eu/9boot01.png"><img class="size-full wp-image-666" src="https://files.adrianistan.eu/9boot01.png" alt="" width="500" height="551" /></a> Porque el boot de 9front es explosivo, nuclearmente explosivo<br><br><a href="https://files.adrianistan.eu/9community05.png"><img class="size-full wp-image-667" src="https://files.adrianistan.eu/9community05.png" alt="" width="500" height="350" /></a> La comunidad 9front<br><br><a href="https://files.adrianistan.eu/9computerasfuck.png"><img class="aligncenter size-full wp-image-669" src="https://files.adrianistan.eu/9computerasfuck.png" alt="" width="500" height="375" /></a><br><br><a href="https://files.adrianistan.eu/9direction05.png"><img class="size-full wp-image-670" src="https://files.adrianistan.eu/9direction05.png" alt="" width="500" height="488" /></a> Los altos mandos de 9front<br><br><a href="https://files.adrianistan.eu/9frontfell02.png"><img class="size-full wp-image-671" src="https://files.adrianistan.eu/9frontfell02.png" alt="" width="500" height="384" /></a> The front fell off<br><br><a href="https://files.adrianistan.eu/9frontfell04.png"><img class="aligncenter size-full wp-image-672" src="https://files.adrianistan.eu/9frontfell04.png" alt="" width="500" height="357" /></a> <a href="https://files.adrianistan.eu/9frontsystem03.png"><img class="aligncenter size-full wp-image-673" src="https://files.adrianistan.eu/9frontsystem03.png" alt="" width="500" height="510" /></a> <a href="https://files.adrianistan.eu/9frontsystem10.png"><img class="aligncenter size-full wp-image-674" src="https://files.adrianistan.eu/9frontsystem10.png" alt="" width="500" height="333" /></a> <a href="https://files.adrianistan.eu/9inferno02.png"><img class="aligncenter size-full wp-image-675" src="https://files.adrianistan.eu/9inferno02.png" alt="" width="450" height="580" /></a> <a href="https://files.adrianistan.eu/9kbdfs01.png"><img class="aligncenter size-full wp-image-676" src="https://files.adrianistan.eu/9kbdfs01.png" alt="" width="500" height="411" /></a><br><br><a href="https://files.adrianistan.eu/9man01.png"><img class="size-full wp-image-677" src="https://files.adrianistan.eu/9man01.png" alt="" width="500" height="380" /></a> Man es un comando muy propenso a chistes<br><br><a href="https://files.adrianistan.eu/9mothra02.png"><img class="size-full wp-image-678" src="https://files.adrianistan.eu/9mothra02.png" alt="" width="500" height="394" /></a> Mothra es el navegador web de 9front<br><br><a href="https://files.adrianistan.eu/9times.png"><img class="size-full wp-image-679" src="https://files.adrianistan.eu/9times.png" alt="" width="496" height="367" /></a> Orwell dirige NineTimes<br><br><a href="https://files.adrianistan.eu/9timesync01.png"><img class="size-full wp-image-680" src="https://files.adrianistan.eu/9timesync01.png" alt="" width="500" height="579" /></a> El comando timesync no es más que una modernización de Stonehenge<br><br><a href="https://files.adrianistan.eu/thinkaboutthefuture.front_.png"><img class="aligncenter size-full wp-image-681" src="https://files.adrianistan.eu/thinkaboutthefuture.front_.png" alt="" width="500" height="647" /></a>Posiblemente haga una entrada seria hablando de <a href="http://9front.org/">9front</a>, pero es que la comunidad 9front tiene mucha tela que cortar.<br><br>Y donde dije dementes, quise decir genios. Son absolutos genios.]]></description>
                <comments>https://blog.adrianistan.eu/9front-fork-plan9-desarrollado-dementes</comments>
                <pubDate>Mon, 16 Jan 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Un nuevo lenguaje de programación para juegos</title>
                <link>https://blog.adrianistan.eu/nuevo-lenguaje-programacion-juegos</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/nuevo-lenguaje-programacion-juegos</guid>
                <description><![CDATA[En la inocentada sobre Rust puse un vídeo de Jonathan Blow titulado <strong>Ideas about a new programming language for games</strong>. En el vídeo, Blow analiza los problemas que presenta C++ para el desarrollo de juegos y por qué según él ni Go ni D ni Rust consiguen mejorar la situación. El lenguaje de programación perfecto para juegos debería tener las siguientes características:<br><ul><br> 	<li>Poca fricción</li><br> 	<li>Placer por programar</li><br> 	<li>Rendimiento</li><br> 	<li>Simplicidad</li><br> 	<li>Diseñado para buenos programadores</li><br></ul><br>Con poca fricción se refiere a que la tarea de programar no debe añadir mucha complejidad para solucionar problemas que tendríamos si programásemos de la forma más simple posible. Fricción es para él RAII en C++. Fricción es la gestión de errores en Rust. Fricción se entiende como código que no añade significativamente nada pero que es necesario para un correcto funcionamiento. Fricción es rellenar papeleo de Hacienda. Muchos defensores de estas posturas argumentan que en realidad esa productividad perdida se recupera con el tiempo al reducir el número de bugs que pueden ocurrir. Blow dice que según su experiencia en juegos AAA realmente no compensa. Tardas más tiempo solventado bugs potenciales que bugs reales. Su solución no es evitar al 100% este tipo de bugs (como hace Rust) sino habilitar unas herramientas potentes que ayuden a solucionar estos bugs si alguna vez suceden.<br><br><a href="https://files.adrianistan.eu/screenshot-www.youtube.com-2017-01-08-12-36-31.png"><img class="aligncenter size-full wp-image-650" src="https://files.adrianistan.eu/screenshot-www.youtube.com-2017-01-08-12-36-31.png" alt="" width="855" height="481" /></a>Esto se relaciona con el placer por programar. Un lenguaje que te invite a programar, a experimentar, donde te sientas a gusto. Muchos lenguajes han perdido esa esencia. Con el tiempo muchos lenguajes se han ido complicando de forma innecesaria y se han ido volviendo pesadillas. Ruby sería el caso contrario, un lenguaje que conserva ese placer. Pero Ruby no entra en juego por razones obvias de rendimiento.<br><br>Con rendimiento básicamente dice que cualquier lenguaje que disponga de GC (recolector de basura) no es válido. Incluso Go, que tiene un GC mucho mejor que Java o la plataforma .NET no lo considera correcto.<br><br>Con simplicidad se busca legibilidad y potencia. El lenguaje debe ser uniforme, con cohesión en todos sus elementos.<br><br>Y con diseñado para buenos programadores se refiere a que el lenguaje no debe suponer que el programador es idiota e intentar corregir sus errores. Debe permitir hacer virguerías si así lo desea el programador. Rust está bien pues permite tener código <em>unsafe</em>. Justo lo que se necesita para hacer virguerías. Pero hace falta más trabajo en este aspecto pues supone un cambio de mentalidad.<br><h2>La idea detrás de RAII es incorrecta</h2><br>Mucha gente opina que RAII es una de las mejores cosas que han pasado en la programación. Muchos lenguajes presuponen RAII. D por ejemplo considera que RAII es la manera correcta de programar. <em>Resource Acquisition Is Initialization</em> consiste en que cada vez que vamos a acceder a un recurso tenemos que codificarlo en una clase, inicializar el recurso en un constructor y liberar el recurso en un destructor. Añades operadores para permitir copia, ... Este sistema presenta una elevada fricción. Y además no funciona bien, en el sentido de que todo se acaba sobrecomplicando. Alejándose de esa simplicidad que estamos buscando.<br><br>Uno de los principales problemas de este patrón de diseño es que no existe un <em>recurso.</em> Es una generalización errónea de varios conceptos. Un recurso puede ser memoria, otro recurso puede ser un archivo, una entrada de teclado, etc El problema es que estos recursos son demasiado diferentes como para ser tratados con un mismo patrón de forma óptima. Mientras RAII puede ser interesante hablando de archivos, es una muy mala opción si hablamos de memoria. Porque la memoria es el recurso más importante para un programador. Se podría simplificar diciendo que un programador lo único que hace es modificar la memoria constantemente.<br><br><a href="https://files.adrianistan.eu/screenshot-www.youtube.com-2017-01-08-12-54-00.png"><img class="aligncenter size-full wp-image-651" src="https://files.adrianistan.eu/screenshot-www.youtube.com-2017-01-08-12-54-00.png" alt="" width="854" height="480" /></a>Pero muchos de los usos de RAII tienen que ver con las excepciones. Y a Blow tampoco le gustan las excepciones. La gestión de errores en C es pésima pero las excepciones son muy complejas. Una de las cosas más complejas que implementan los lenguajes de programación que disponen de ellas. Y la implementación de C++ más todavía. Blow se lamenta de que haya gente que siga pensando que es una buena idea. Reduce la claridad del código, complica el flujo del programa. RAII en C++ ayuda a que en caso de que se de una excepción los recursos puedan ser liberados.<br><br>No solo lo dice él, sino que enlaza el siguiente vídeo: <em>Systematic Error Handling in C++</em> por Andrei Alexandrescu.<br><br><iframe src="https://channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2012-Andrei-Alexandrescu-Systematic-Error-Handling-in-C/player" width="960" height="540" frameborder="0" allowfullscreen="allowfullscreen"></iframe><br><a href="https://files.adrianistan.eu/screenshot-www.youtube.com-2017-01-08-13-04-35.png"><img class="aligncenter size-full wp-image-652" src="https://files.adrianistan.eu/screenshot-www.youtube.com-2017-01-08-13-04-35.png" alt="" width="854" height="479" /></a><br><h2>Un mejor sistema que las excepciones</h2><br>Go implementa múltiples valores de retorno (al contrario que la mayoría de lenguajes derivados de C donde solo de devuelve una cosa). Go lo soporta de forma nativa. Pero Matt Newport le responde como podría hacer eso en C++11 con <em>std::tie</em>.<br><br><pre class="lang:c++ decode:true"><br>#include &lt;iostream&gt;;<br>#include &lt;tuple&gt;;<br>#include &lt;functional&gt;;<br> <br>std::tuple&lt;int, int&gt; f()<br>{<br>    int x = 5;<br>    return std::make_tuple(x, 7); // return {x,7}; en C++17<br>}<br> <br>int main()<br>{<br>    int a, b;<br>    std::tie(a, b) = f();<br>    std::cout &lt;&lt; a &lt;&lt; &quot; &quot; &lt;&lt; b &lt;&lt; &quot;\n&quot;;<br>}<br></pre><br><br>Rust, como Go, soporta esto de forma nativa:<br><br><pre class="lang:c++ decode:true"><br>fn f() -&gt; (i32,i32) {<br>	(4,7)<br>}<br><br>fn main() -&gt; () {<br>	let (a,b) = f();<br>	println!(&quot;A es {}, B es {}&quot;,a,b);<br>}<br></pre><br><br>Aunque no es la manera en la que Rust maneja los errores. En su lugar posee Option y Result que en C++17 también van a ser implementados en <em>std::optional</em> y que es en realidad un concepto presente en Haskell.<br><h2>Sintaxis exasperante</h2><br>En la charla Blow sigue hablando y comenta que desde un punto de visto purista y correcto la sintaxis de punteros de C++11 es incorrecta. Que <em>std::unique_ptr&lt;Vector3[]&gt;</em> implica que quieres un Unique Ptr basado en Vector3 pero en realidad la idea correcta sería quiero un Vector3 con características de Unique Ptr. Lo mismo es aplicable para <em>std::shared_ptr</em>. Este tipo de punteros no deberían estar expresados de esta forma sino que deberían entrar en la sintaxis del lenguaje, por su utilidad práctica.<br><br><a href="https://files.adrianistan.eu/screenshot-www.youtube.com-2017-01-08-13-31-14.png"><img class="aligncenter size-full wp-image-653" src="https://files.adrianistan.eu/screenshot-www.youtube.com-2017-01-08-13-31-14.png" alt="" width="853" height="480" /></a>En Rust, el equivalente a <em>std::unique_ptr</em> sería <em>Box</em> que es el puntero más usado. El equivalente a <em>std::shared_ptr </em>sería <em>Rc</em>, no tan usado pero disponible.<br><br>Blow sigue hablando en este vídeo y en el siguiente de más errores que tiene C++, aunque de menor importancia. En todo caso, Blow sigue en el desarrollo de su compilador de Jai. C++ ha avanzado un montón y me ha llegado a sorprender que existiesen cosas como constexpr y los módulos de C++, una solución a los archivos de cabecera que son tan tediosos de escribir.<br><br>Si tenéis tiempo y sabéis inglés miráos el vídeo original. Creo que esta mucho mejor explicado que esto. Y también <a href="http://blog.mattnewport.com/raii/">la respuesta de Matt Newport en la que dice que C++ SÍ es válido</a> para todo lo que dice Blow.<br><iframe src="https://www.youtube-nocookie.com/embed/TH9VCN6UkyQ" width="560" height="315" frameborder="0" allowfullscreen="allowfullscreen"></iframe>]]></description>
                <comments>https://blog.adrianistan.eu/nuevo-lenguaje-programacion-juegos</comments>
                <pubDate>Sun, 08 Jan 2017 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Feliz añ... ¡Dame los datos!</title>
                <link>https://blog.adrianistan.eu/feliz-an-dame-los-datos</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/feliz-an-dame-los-datos</guid>
                <description><![CDATA[Como viene siendo habitual hay que dedicar una entrada del blog a despedir el año y desearos lo mejor para el 2017.<br><br><iframe src="https://www.youtube-nocookie.com/embed/eh2fpnwAPC0" width="560" height="315" frameborder="0" allowfullscreen="allowfullscreen"></iframe><br><br>Pero no nos pongamos emotivos todavía. Queda lo mejor. Analizar los datos de Google Analytics de esta y otras webs que he creado.<br><h2>Un poco de historia</h2><br>Como muchos sabréis ya, el blog ahora mismo funciona con WordPress, más concretamente en una Raspberry Pi 2 con Raspbian y WordPress se ejecuta sobre PHP7, con MariaDB de base de datos y nginx de servidor web. Antes no era así. Originalmente era un blog en Blogger muy simple. Después lo pasé a Jekyll con GitHub Pages usando un tema que cree sobre la marcha. Este tema tenía un diseño "peculiar" y tenía algunos problemas. Por eso inicié la transición a un tema nuevo basado en Material Design. Pero aquello no duró mucho, era más lento y daba más fallos. Al final decidí pasarme a WordPress y de paso comprar el dominio <em>adrianistan.eu</em>. Esta transición la hice en junio y hay que tenerla en cuenta ya que cambié las propiedades de Analytics en el proceso. El blog antiguo además no ha dejado de funcionar (para no romper enlaces) y lo tenéis en http://adrianistan.eu/blog/<br><h2>blog.adrianistan.eu - Muy satisfecho</h2><br>El flamante nuevo blog, usando WordPress y en chupando electricidad en mi casa ha tenido 3982 sesiones con 5213 visitas a páginas.<br><br><a href="https://files.adrianistan.eu/BlogAdrianistanAnalytics.png"><img class="aligncenter size-large wp-image-636" src="https://files.adrianistan.eu/BlogAdrianistanAnalytics-1024x177.png" alt="" width="840" height="145" /></a>El pico más alto corresponde al 14 de julio y es el día siguiente a la publicación del <a href="https://blog.adrianistan.eu/2016/07/13/tutorial-gnu-cash/">Tutorial de GnuCash</a>, artículo que como veremos ahora sigue siendo de los más visitados. El segundo pico, ya el 21 de noviembre fue el día siguiente a la publicación del artículo sobre <a href="https://blog.adrianistan.eu/2016/11/20/usando-iron-web-framework-rust/">Iron, un framework web escrito en Rust</a>.<br><br>Respecto a los idiomas, nada raro, todas las variaciones de español que existen, algunos idiomas como Catalán, Gallego, Euskera o Portugués que pueden ser perfectamente de gente que entiende ambos idiomas. Inglés también está muy alto, teniendo en cuenta que es la configuración por defecto de muchas cosas tampoco es raro.<br><br><a href="https://files.adrianistan.eu/MapBlog.png"><img class="aligncenter size-full wp-image-637" src="https://files.adrianistan.eu/MapBlog.png" alt="" width="606" height="402" /></a><br><br>Analytics ha contabilizado accesos desde 70 países. La mayor parte del tráfico viene de España, con mucha diferencia, seguido de México y en tercer lugar Estados Unidos. En cuarto lugar Argentina. Le siguen Colombia, Venezuela, Chile, Perú, Ecuador y el top 10 lo termina Brasil. Me parece curioso, en undécimo puesto encontramos a Alemania. Y me ha sorprendido, no esperaba que estuviese tan arriba. Esta por encima de otros países latinoamericanos, supongo que esto se debe a los españoles que se han ido a trabajar a Alemania.<br><br><a href="https://files.adrianistan.eu/BrowserBlog.png"><img class="aligncenter size-large wp-image-638" src="https://files.adrianistan.eu/BrowserBlog-1024x468.png" alt="" width="840" height="384" /></a>La mayoría de vosotros entráis con Google Chrome. En segundo lugar se posiciona Firefox. Con un porcentaje ya bastante reducido está Safari, que supongo que será sobre todo tráfico de iOS, aunque como veremos más adelante recibo más visitas desde macOS que desde iOS. Y este año hemos dado el <em>sorpasso</em>, el blog recibió más visitas con Edge que con Internet Explorer. Mención especial a NetNewsWire Browser, que me hizo tener que buscarlo en Google.<br><br><a href="https://files.adrianistan.eu/BlogOS.png"><img class="aligncenter size-large wp-image-639" src="https://files.adrianistan.eu/BlogOS-1024x399.png" alt="" width="840" height="327" /></a>En sistemas operativos, primero Windows, después Android y a continuación Linux de escritorio. Esta tendencia la tengo en las otras webs y es algo que se lleva dando desde hace algún tiempo. Me sorprende que macOS posicione por encima de iOS. Se ve que cada vez hay más Macs en el mundo hispanohablante.<br><h3>¿De dónde vienen las ovejas descarriadas que entran aquí?</h3><br>Analytics dice que un alto porcentaje vienen de búsquedas en Google, Menéame aporta bastantes visitas, Google+ sigue trayéndome gente, Reddit también y Facebook ha empezado a traer usuarios de forma más constante. Pero lo más importante es el RSS y Feedly. Mucha gente viene a través de esos canales, lo cuál me alegra pues la gente que accede por allí suelen ser lectores que ya han decidido quedarse. El correo electrónico parece funcionar bien, aunque hay pocos suscriptores. Y me sorprende que al menos 16 personas hayan accedido al blog a través de mi perfil de Instagram. ¿En serio?<br><h3>¿Y en qué <del>corral</del> artículo caen?</h3><br><a href="https://files.adrianistan.eu/PagesBlog.png"><img class="aligncenter size-large wp-image-640" src="https://files.adrianistan.eu/PagesBlog-1024x297.png" alt="" width="840" height="244" /></a>Los 3 tutoriales: CMake, WiX y GnuCash han sido muy populares. La calculadora de 4 bits lleva desde el 2013 siendo uno de los artículos más leídos siempre en mi blog.<br><br>Las conclusiones que saco es que Rust interesa, Bitcoin y Ethereum no tanto pero también. Esos dos temas van a ser importantes para este año 2017.<br><h2>adrianistan.eu - El año del boom</h2><br>adrianistan.eu reúne por un lado el blog hasta antes de junio, las descargas de Kovel, los complementos de Firefox y alguna cosilla más.<br><br><a href="https://files.adrianistan.eu/Boom.png"><img class="aligncenter size-large wp-image-641" src="https://files.adrianistan.eu/Boom-1024x415.png" alt="" width="840" height="340" /></a>14889 sesiones y 12532 usuarios. El artículo de enero de 2016 titulado <a href="https://blog.adrianistan.eu/2016/01/10/programar-c-2016/">Cómo programar en C (en 2016)</a> fue un absoluto éxito, llegando a la portada de Menéame. Fue además enlazado desde sitios como CyberHades o ElHacker. El artículo era una mera traducción a la que añadí un par de cosas al final. No recuerdo ahora mismo como lo encontré, pero me pareció bueno y a la vez el típico artículo que a veces pasa desapercibido. Le pedí si podía hacer la traducción y me dijo que sí. Hubo mucho debate sobre el artículo lo que además le dio repercusión.<br><br><a href="https://files.adrianistan.eu/Country.png"><img class="aligncenter size-large wp-image-642" src="https://files.adrianistan.eu/Country-1024x390.png" alt="" width="840" height="320" /></a>En el mapa veo ya empiezo a ver cosas raras. España es el país líder, pero el segundo es... ¡¿Rusia?! Eso dice Analytics, pero creo que es mentira o mejor dicho, no es exactamente verdad. Esta propiedad ha recibido muchos hits desde unos dominios y mi teoría es que todos vienen de Rusia y son en realidad bots. El tercer país es Estados Unidos, seguido de México, Alemania, Argentina, Francia, Colombia, Venezuela y Perú. En total se han registrado datos de 153 países. Puntos extra para los que han entrado desde la isla de Mayotte y la isla de Guam.<br><br><a href="https://files.adrianistan.eu/Browser.png"><img class="aligncenter size-large wp-image-643" src="https://files.adrianistan.eu/Browser-1024x439.png" alt="" width="840" height="360" /></a>Aquí gana Firefox, algo obvio debido a que parte del sitio está dedicado a alojar complementos de Firefox. Y aquí Rusia me vuelve a intrigar. YaBrowser es un navegador ruso y tiene un porcentaje considerable de visitas. Muy extraño todo. No aparecen en la imagen pero me han llamado la atención estos tres navegadores: Coc Coc, Dooble y PlayFreeBrowser.<br><br><a href="https://files.adrianistan.eu/Sources.png"><img class="aligncenter size-large wp-image-644" src="https://files.adrianistan.eu/Sources-1024x473.png" alt="" width="840" height="388" /></a>Según mi teoría, site-auditor.online, rank-checker.online y monetizationking.net han estado generando tráfico falso. La verdad es que es muy intrigante. Las instalaciones de complementos siguen trayendo un buen tráfico.<br><br>Vamos con otra web.<br><h2>Phaser.js Hispano - Una comunidad interesada</h2><br>La web ha tenido muy buen recibimiento con gente interesada. Me da pena no poder escribir artículos para Phaser.js Hispano porque realmente hay gente que los está buscando y los necesita. Para paliar estos previsibles vacíos de contenidos he creado Gamedev Hispano, un foro donde se puede ir aportando poco a poco. El foro no ha tenido una buena acogida inicialmente, pero es un proyecto que puede tener futuro y no me voy a rendir todavía.<br><h2>TucTum - Muchos usuarios desisten</h2><br>TucTum es un nodo de GNU Social que te paga por la cantidad de me gustas que reciban tus publicaciones. El programa funciona pero la gente no se hace a TucTum. Entiendo que la interfaz no es tan sencilla pero el principal problema es, a mi modo de ver, que no hay gente suficiente como para retener a la gente que se va hace cuenta. Muchos se hacen cuenta y al ver que no hay nadie con quien hablar se van. No tengo nada preparado para TucTum este 2017.<br><h2>Gaming on Haiku - El sitio que me plantea cambios</h2><br>No voy a hablar mucho de Gaming on Haiku (2550 sesiones y 1933 usuarios) pero este sitio me ha hecho plantearme cosas. Primero, fue el lugar donde probé WordPress antes de usarlo en este blog. Además ha sido un sitio en inglés y mi impresión ha sido que con mucho menos esfuerzo he logrado mucha más repercusión. Enlaces en muchas más páginas y mayor interacción social. Pero también es un sitio de nicho, mucho más fácil de categorizar que este blog. La pregunta es: ¿qué parte de su éxito corresponde al idioma y qué parte a la temática de la página?<br><br>Llegados a este punto, ¿merece la pena convertir Adrianistán en un blog en inglés? Sin duda, algo que me voy a plantear muy seriamente en este 2017, por supuesto, vuestras opiniones me importan y podéis decir que pensáis al respecto.]]></description>
                <comments>https://blog.adrianistan.eu/feliz-an-dame-los-datos</comments>
                <pubDate>Sat, 31 Dec 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Mozilla desiste, Rust será sustituido por Jai</title>
                <link>https://blog.adrianistan.eu/mozilla-desiste-rust-sera-sustituido-jai</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/mozilla-desiste-rust-sera-sustituido-jai</guid>
                <description><![CDATA[La lucha por un nuevo lenguaje de programación que sustituya a C comenzó hace años con los siguientes contendientes: C++14, Go, Rust y D. Finalmente Mozilla ha abandonado el desarrollo del lenguaje de programación Rust. Se ha decidido, no con polémica, iniciar una transición hacia Jai, el lenguaje de programación diseñado por Jonathan Blow.<br><br><a href="https://files.adrianistan.eu/Rust.jpg"><img class="aligncenter size-large wp-image-100" src="https://files.adrianistan.eu/Rust-1024x576.jpg" alt="Rust" width="840" height="473" /></a><br><br>Muchos conocerán a Jonathan Blow por ser el creador de juegos como Braid o The Witness, pero además, lleva trabajando en Jai desde 2014.<br><br><iframe src="https://www.youtube-nocookie.com/embed/UTqZNujQOlA" width="560" height="315" frameborder="0" allowfullscreen="allowfullscreen"></iframe><br><br>Jai actualmente es un lenguaje que transpila a C++, para posteriormente ser compilado por un compilador normal de C++. La idea detrás de Jai es ser <em>el lenguaje de programación de videojuegos</em>. Tras haber trabajado muchos años con C++, Blow ha diseñado un lenguaje que mejore las partes malas de C, conservando sus partes buenas. Blow criticó al lenguaje Rust asegurando que usar Rust era como <em>la casa que tiene sus esquinas plastificadas pero luego te deja una pistola cargada sobre la mesa</em>.<br><br>Brendan Eich, creador de JavaScript ha declarado:<br><blockquote>En el fondo sabíamos que podría pasar. En Mozilla solo sabemos diseñar lenguajes horribles. De hecho a mí me dejaron hacer JavaScript y ya sabemos lo que pasó</blockquote><br>Ahora Mozilla se ha acercado a Blow para aprender más sobre Jai, un lenguaje mucho más práctico. Jai permite al programador hacer lo que quiera, pero opcionalmente y a través de <em>syntactic sugar</em> es optimizado y analizado para llegar a mejorar en rendimiento incluso a C.<br><br>James Gosling, creador de Java ha dicho:<br><blockquote>Nosotros diseñamos Java para que fuese muy difícil escribir código malo. Cualquier mejora sobre eso se merece mis aplausos, lástima que no se oigan porque la JVM todavía está arrancando.</blockquote><br>A día de hoy no existen compiladores de Jai disponibles al público y la única copia se cree que está en manos de Jonathan Blow. La única documentación existente<a href="https://github.com/BSVino/JaiPrimer/blob/master/JaiPrimer.md"> se encuentra recopilada en GitHub</a> por fans de Jai.<br><br>Definitivamente este año no ha sido muy bueno para Mozilla, que ya ha tenido que abandonar otros proyectos como Firefox OS o Matchstick, que prometían innovación pero que por A o por B no han sabido llegar a buen puerto.<br><br><strong>ACTUALIZACIÓN:</strong> ¡Feliz día de los inocentes!]]></description>
                <comments>https://blog.adrianistan.eu/mozilla-desiste-rust-sera-sustituido-jai</comments>
                <pubDate>Wed, 28 Dec 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Fundamentalismo ateo</title>
                <link>https://blog.adrianistan.eu/fundamentalismo-ateo</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/fundamentalismo-ateo</guid>
                <description><![CDATA[De todo hay en la viña del señor como diría el refranero castellano:<br><br><a href="https://files.adrianistan.eu/Fundamentalismo.png"><img class="aligncenter size-large wp-image-625" src="https://files.adrianistan.eu/Fundamentalismo-443x1024.png" alt="fundamentalismo" width="443" height="1024" /></a>Nada más amigos porque me voy a la puta mierda porque soy borrico pedazo de estúpido desinformado, con falta de cultura.<br><br><em>Reflexión seria: ¿Por qué tengo la impresión de que está surgiendo una especie de fundamentalismo ateo? ¿Por qué atacar tan fervientemente? A mi modo de ver, personas como Cristian50088 son la antítesis de lo que debe ser un buen científico. Deprenden arrogancia y caen en falacias dignas de fundamentalistas religiosos. </em><br><br><em>Hace tiempo que llevo viendo que las secciones de comentarios de ciertos vídeos de YouTube están llenos de ateos exaltados. Personas que aprovechan a la mínima para despotricar contra cualquier persona que quiera hacer una mínima observación. Y lo peor de todo es que para mí son hipócritas. Richard Dawkins admitió en un vídeo que el "ser ateo" no es en sentido estricto una mejora sobre "ser creyente" ya que en ambos casos se presupone la existencia o falta de ella de un Dios. Sin embargo, en el asunto Dios no es que haya muchas pruebas que digamos. Por tanto, la posición idónea es el escepticismo y no la completa negación de Dios, porque estaríamos cayendo en el mismo error de los creyentes de afirmar/negar sin pruebas. Un escéptico, un agnóstico, ... va a vivir su vida de forma prácticamente igual que el ateo. Pero existe esa sutil diferencia sobre como ha llegado a ello.</em><br><br>Lo de las faltas de respeto, lo dejo para otro momento.<br><br>&nbsp;<br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/fundamentalismo-ateo</comments>
                <pubDate>Wed, 21 Dec 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Crónica de un vídeo de citas célebres</title>
                <link>https://blog.adrianistan.eu/cronica-video-citas-celebres</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/cronica-video-citas-celebres</guid>
                <description><![CDATA[Vuelvo a la carga con un problema que me ha tenido entretenido un rato y que me parece interesante contaros.<br><br>Como muchos sabréis, este año me pasé el videojuego <a href="https://blog.adrianistan.eu/2016/03/25/analisis-the-witness/">The Witness</a>. Es un juego muy interesante y que os recomiendo. El caso es que nunca llegué a escuchar todas las citas célebres del juego, y en YouTube solo encontré vídeos sueltos con subtítulos en... francés. Así que me propuse hacer un vídeo que recogiese todas las citas célebres del juego, con subtítulos en Español. Antes esta situación hay dos opciones:<br><ul><br> 	<li>Lo que una persona normal haría sería buscar en la guía las localizaciones de las citas e ir grabándolas.</li><br> 	<li>Lo que haría un perturbado sería meterse en los archivos del juego, decodificar los archivos e implementar un complejo sistema para generar el vídeo.</li><br></ul><br>Por supuesto, hice lo último.<br><br><a href="https://files.adrianistan.eu/TheWitnessQuotes.png"><img class="aligncenter size-large wp-image-619" src="https://files.adrianistan.eu/TheWitnessQuotes-1024x576.png" alt="thewitnessquotes" width="840" height="473" /></a><br><h2>Encontrando archivos del juego</h2><br>Los archivos del juego no están escondidos, se encuentran en un archivo llamado <em>data-pc.zip</em>. Hasta ahí fácil. Una vez dentro encontramos cientos de archivos con unas extensiones peculiares que nos informan de lo que hay dentro (.texture, .lightmap,...). Sin embargo los archivos de sonido no los encontramos de manera sencilla. Necesitan una conversión. Estaba ya buscando soluciones (sospecho que tiene que ver con Audiokinetic Wwise) cuando <a href="https://www.reddit.com/r/TheWitness/comments/451hlx/exploring_the_data_files_major_spoilers/">encontré en reddit a un buen samaritano</a> que había subido, ya decodificados, los archivos de sonido a Mega.<br><br>Al ver los archivos observé cantidad de ficheros .WAV y muy poquitos .OGG. Eso ya nos da una pista, pues las citas célebres han tenido que ser codificadas como Ogg, ya que si fuesen con WAV el fichero sería demasiado grande. Extraigo los archivos y borro todos los WAV, pues sé que ahí no están.<br><br>Pero no hemos acabado. Los Ogg no solo eran de citas célebres, también había efectos de sonido largos. Afortunadamente, los archivos de efectos de sonido solían llevar un prefijo común (<em>amb_, spec_,...</em>).<br><h2>Los subtítulos</h2><br>Tenemos los ficheros de audio de las citas en formato Ogg. Ahora hacen falta los subtítulos. Están fuera de ese fichero ZIP gigante y no es difícil encontrarlos. En concreto el archivo <em>es_ES.subtitles</em>. Sin embargo, una vez lo abres descubres la primera sorpresa. Es un formato del que desconocía su existencia. Os pongo un poco para ver si alguien es capaz de saber el formato:<br><br>&nbsp;<br><br><pre class="lang:default decode:true"><br>: tagore_voyage<br><br>= 1.000000<br>Creía que mi viaje había llegado al final<br>al estar al límite de mis fuerzas...<br><br>= 6.700000<br>que el camino ante mí estaba cerrado,<br>las provisiones agotadas<br>y que había llegado la hora de refugiarse en la silenciosa oscuridad.<br><br>= 17.299999<br>Pero encuentro que la voluntad no se me acaba,<br><br>= 21.299999<br>y cuando las palabras se apagan en la lengua,<br>surgen nuevas melodías del corazón;<br><br>= 27.200001<br>y cuando se pierde el antiguo camino,<br>una nueva tierra revela sus maravillas.<br><br>= 32.700001<br><br>= 32.709999<br>Rabindranath Tagore, 1910<br><br><br>: tagore_end<br><br>= 1.000000<br>En mi vida te he buscado con mis canciones.<br>Fueron ellas las que me guiaron de puerta en puerta,<br><br>= 8.800000<br>y con ellas me he sentido a mí,<br>buscando y tocando mi mundo.<br><br>= 14.400000<br><br>= 14.900000<br>Fueron mis canciones las que me enseñaron<br>todas las lecciones que he aprendido<br><br>= 18.900000<br>me mostraban caminos secretos,<br>condujeron mi vista hacia más de una estrella<br>en el horizonte de mi corazón.<br><br>= 25.500000<br><br>= 26.100000<br>Me guiaron todo el día<br>hacia los misterios del país del placer y el dolor<br><br>= 32.500000<br>y, finalmente,<br>¿a las puertas de qué palacio me han<br>guiado en el atardecer al final de mi viaje?<br><br>= 39.299999<br><br><br>: tagore_boast<br><br>= 1.000000<br>Presumí ante los hombres de conocerte.<br><br>= 4.300000<br>Ven tus imágenes en mis trabajos.<br>Vienen y me preguntaban &quot;¿Quién es él?&quot;<br><br>= 10.900000<br>No tengo respuesta para ellos.<br>Les digo &quot;No sabría decirlo&quot;.<br><br>= 16.799999<br>Me culpan y se marchan con desprecio.<br>Y tú te sientas ahí, sonriendo.<br><br>= 23.500000<br><br>= 24.700001<br>Mis historias sobre ti quedan en canciones duraderas.<br>El secreto sale a borbotones de mi corazón.<br><br>= 31.000000<br>Vienen y me piden<br>&quot;Cuéntame todo lo que significan&quot;.<br><br>= 34.900002<br>No tengo respuesta para ellos.<br>Les digo &quot;Ah, ¡quién sabrá!&quot;<br><br>= 40.500000<br><br>= 41.000000<br>Sonríen y se marchan con desprecio absoluto.<br>Y tú te sientas ahí, sonriendo.<br><br>= 48.000000<br><br>= 49.500000<br>- Rabindranath Tagore, 1910<br></pre><br><br>Pero no me iba a detener. Así que empecé a diseñar un programa que permitiese traducir este archivo a un archivo SRT normal y corriente. Para ello usaría Regex a saco (me leí el libro, para algo me tendría que servir).<br><br><a href="https://files.adrianistan.eu/regexp.png"><img class="aligncenter size-large wp-image-620" src="https://files.adrianistan.eu/regexp-529x1024.png" alt="regexp" width="529" height="1024" /></a><br><br>Para hacer el programa usé Node.js. Sí, se que para este tipo de cosas el mejor lenguaje es Perl, o un derivado como Ruby pero todavía no he aprendido lo suficiente de Ruby como para plantearmelo. JavaScript cuenta de forma estándar (tanto Node.js como navegador) con la clase RegExp, que permite ejecutar expresiones regulares y esa es la que he usado.<br><br>Finalmente conseguí hacer un script de Node.js, sin dependencias externas, que traduciese este archivo subtitles en un SRT.<br><h2>Generando un vídeo para cada cita</h2><br>Ya tenemos el audio, tenemos los subtítulos en un formato conocido. Vamos ahora a generar un vídeo. Primero necesitaremos una imagen de fondo. Pillo una cualquiera de Internet y empiezo a hacer pruebas con <strong>ffmpeg</strong>. El formato de salida va a ser MP4 codificado con H264 porque realmente es el formato que más rápido se codifica en mi ordenador.<br><br>Nada más empezar empiezo a ver que los subtítulos no están funcionando, no se fusionan con la imagen y el audio. Al parecer es un problema que involucra a fontconfig, ffmpeg y Windows. Sí, estaba usando Windows hasta ahora.<br><br>Me muevo a Debian y ahora ya funciona bien el fusionado de subtítulos.<br><br>Ahora intento unir dos vídeos con ffmpeg también. Fracaso. Lo vuelvo a intentar, FRACASO. Si os digo que la mayor parte del tiempo que me ha llevado este proyecto ha sido encontrar como concatenar varios MP4 en ffmpeg sin que me diese errores extraños quizá no os lo creeríais, pero es verídico. No me creeríais porque la wiki de ffmpeg lo explica correctamente y si buscáis por Internet os van a decir lo mismo. ¿Qué era lo que pasaba?<br><ol><br> 	<li>Las dimensiones de los vídeos no cuadraban<br><ol><br> 	<li>Esto fue obvio y fue lo primero que pensé. ffmpeg tiene un filtro de escalado, pero por alguna razón no funcionaba. La razón era que estaba usando dos veces la opción "-vf" (filtro de vídeo), una con los subtítulos y otra con el escalado. ffmpeg no admite nos veces la opción, si quieres aplicar dos filtros de vídeo tienes que usar una coma entre ellos.</li><br></ol><br></li><br> 	<li>Formato de píxeles<br><ol><br> 	<li>Este era el verdadero problema. Normalmente no suele pasar, pero como las imágenes de los dos vídeos venían de fuentes distintas, ffmpeg usó un formato de píxeles distinto en cada una. Forzando a ffmpeg a usar siempre "yuv420p" funcionó y la concatenación se pudo realizar.</li><br></ol><br></li><br></ol><br>Probé también con <strong>mkvmerge</strong>, pero me decía que la longitud de los códecs era distinta. No entendí el error hasta que no me enteré que había sido el formato de píxeles, cada vídeo usaba uno distinto en su codificación.<br><br>El comando necesario para generar cada vídeo fue entonces:<br><br><pre class="lang:default decode:true"><br>ffmpeg -loop 1 -i imagen.jpg -i audio.ogg -c:v libx264 -tune stillimage -pix_fmt yuv420p -s 1280x720 -vf scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:(ow-iw)/2:(oh-ih)/2,subtitles=subtitulos.srt video.mp4<br></pre><br><br><h2>Concatenar los vídeos</h2><br>Para concatenar los vídeos es necesario tener un archivo de texto donde se indiquen los archivos y su orden, siguiendo este formato:<br><br><pre class="lang:default decode:true"><br># Archivo de concatenacion ffmpeg<br>file 'video1.mp4'<br>file 'video2.mp4'<br>file 'video3.mp4'<br></pre><br><br>Luego, su uso es bastante sencillo:<br><br><pre class="lang:default decode:true"><br>ffmpeg -f concat -i videos.txt -c:v copy video.mp4<br></pre><br><br><h2>El script final</h2><br>Ahora solo hacía falta convertir todos los archivos de audio en vídeo con sus subtítulos. Usando un script de bash se puede hacer esto:<br><br><pre class="lang:default decode:true"><br>for f in *.ogg; do<br>	node main.js &quot;${f%.*}&quot;<br>done<br></pre><br><br>Y el código de main.js es el siguiente. main.js se encarga de traducir los ficheros subtitles a SRT, de llamar a ffmpeg y de añadir el vídeo a la lista de videos.txt para la posterior concatenación.<br><br><pre class="lang:js decode:true"><br>var fs = require(&quot;fs&quot;);<br>var spawn = require(&quot;child_process&quot;).spawn;<br><br>var ZONE = process.argv[2];<br>var IMG = process.argv[2]+&quot;.jpg&quot;;<br><br>function timeFormat(SEC,MILISEC){<br>	var date = new Date(SEC * 1000);<br>	var regex = new RegExp(&quot;([0-9]+:[0-9]+:[0-9]+)&quot;);<br>	var str = regex.exec(date.toUTCString())[1] + &quot;,&quot; + MILISEC.substring(0,3);<br>	return str;<br>}<br><br>var witness = fs.readFileSync(&quot;es_ES.subtitles&quot;,&quot;utf-8&quot;);<br>var srt = fs.createWriteStream(ZONE+&quot;.srt&quot;);<br><br>var regex = &quot;: &quot;+ZONE+&quot;\n\n= ([0-9]+).([0-9]+)\n&quot;;<br><br>var LINE = 1;<br><br>while(!(new RegExp(regex + &quot;\n: &quot;,&quot;g&quot;).test(witness))){<br>	var start = new RegExp(regex,&quot;g&quot;);<br>	var match = start.exec(witness);<br>	<br>	var START_TIME_SEC = parseInt(match[match.length - 2]);<br>	var START_TIME_MILISEC = match[match.length - 1];<br>	var TEXT = &quot;&quot;;<br><br>	do{<br>		var text = new RegExp(regex + &quot;(.+)\n&quot;,&quot;g&quot;);<br>		var exec = text.exec(witness);<br>		if(exec!=null){<br>			TEXT = TEXT + &quot;\n&quot; + exec[exec.length - 1];<br>			regex = regex + &quot;(.+)\n&quot;;<br>		}<br>	}while(exec != null);<br>	<br>	regex = regex + &quot;\n= ([0-9]+).([0-9]+)\n&quot;;<br>	var time = new RegExp(regex,&quot;g&quot;);<br>	var end_time = time.exec(witness);<br>	if(end_time != null){<br>		var END_TIME_SEC = parseInt(end_time[end_time.length - 2]);<br>		var END_TIME_MILISEC = end_time[end_time.length - 1];<br>		srt.write(LINE + &quot;\n&quot;);<br>		srt.write(timeFormat(START_TIME_SEC,START_TIME_MILISEC)+&quot; --&amp;amp;amp;amp;gt; &quot;+timeFormat(END_TIME_SEC,END_TIME_MILISEC));<br>		srt.write(TEXT);<br>		srt.write(&quot;\n\n&quot;);<br>	}else{<br>		srt.write(LINE + &quot;\n&quot;);<br>		srt.write(timeFormat(START_TIME_SEC,START_TIME_MILISEC)+&quot; --&amp;amp;amp;amp;gt; &quot;+timeFormat(5*60,&quot;000000&quot;));<br>		srt.write(TEXT);<br>		srt.write(&quot;\n\n&quot;);<br>		break;<br>	}<br>	LINE++;<br>}<br><br>srt.end();<br><br>var ffmpeg = spawn(&quot;ffmpeg&quot;,[&quot;-loop&quot;,&quot;1&quot;,&quot;-i&quot;,IMG,&quot;-i&quot;,ZONE+&quot;.ogg&quot;,&quot;-c:v&quot;,&quot;libx264&quot;,&quot;-tune&quot;,&quot;stillimage&quot;,&quot;-pix_fmt&quot;,&quot;yuv420p&quot;,&quot;-s&quot;,&quot;1280x720&quot;,&quot;-shortest&quot;,&quot;-vf&quot;,&quot;scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:(ow-iw)/2:(oh-ih)/2,subtitles=&quot;+ZONE+&quot;.srt&quot;,ZONE+&quot;.mp4&quot;]);<br><br>ffmpeg.stderr.on('data', (data) =&amp;amp;amp;amp;gt; {<br>  console.log(`stderr: ${data}`);<br>});<br><br>fs.appendFileSync(&quot;video.txt&quot;,&quot;file '&quot;+ZONE+&quot;.mp4'\n&quot;);<br></pre><br><br>Se trata de un programa que hice deprisa y corriendo y aunque el código es feo (o eso me parece a mi), la verdad es que me ha servido.<br><h2>Y el resultado...</h2><br><iframe src="https://www.youtube-nocookie.com/embed/lXvHJsFwUR0" width="560" height="315" frameborder="0" allowfullscreen="allowfullscreen"></iframe><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/cronica-video-citas-celebres</comments>
                <pubDate>Sun, 18 Dec 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Las 12 Uvas Game Jam</title>
                <link>https://blog.adrianistan.eu/las-12-uvas-game-jam</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/las-12-uvas-game-jam</guid>
                <description><![CDATA[¿Quiéres hacer un juego en 15 días? ¿Estás listo para experimentar con ideas innovadoras y divertirte en el proceso?<br><br><a href="http://gamedevhispano.com/topic/6/las-12-uvas-game-jam"><img class="aligncenter size-full wp-image-615" src="https://files.adrianistan.eu/Las12Uvas.jpg" alt="las12uvas" width="1000" height="656" /></a>El foro Gamedev Hispano presenta su primera game jam, un evento donde tendrás que desarrollar un juego de cero, teniéndolo que acabar antes de que termine el año.<br><br>Se trata de las 12 uvas game jam y cualquiera puede participar tanto de forma individual como por equipos. Esta competición tiene pocas reglas y es ideal si es tu primera game jam, aunque seguro que si has participado en otras también te gustará. El tema elegido es: <strong><em>Hasta la vista 2016</em></strong>.<br><br>Se puede programar el juego como quieras (Unity, Phaser, libGDX, Corona, LUA,...) siempre que sea capaz de funcionar al menos en Windows, Linux, Mozilla Firefox (HTML5) o Android. Se valorará creatividad y diversión, pero también se puntuará el uso que le dan los participantes al foro.<br><br>¿A qué esperas? <a href="http://gamedevhispano.com/topic/6/las-12-uvas-game-jam">Tienes toda la información en el foro</a>.<br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/las-12-uvas-game-jam</comments>
                <pubDate>Tue, 13 Dec 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Nace el foro Gamedev Hispano</title>
                <link>https://blog.adrianistan.eu/nace-foro-gamedev-hispano</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/nace-foro-gamedev-hispano</guid>
                <description><![CDATA[Hoy tengo el placer de anunciar la inauguración del foro <a href="http://gamedevhispano.com">Gamedev Hispano</a>.<br><br>Se trata de un punto de encuentro para preguntar, debatir y descubrir sobre desarrollo de juegos en la lengua de Cervantes. Inicialmente enfocado al desarrollo de juegos HTML5 (Phaser y Babylon), el foro huye de ser "otro más" de Unity/Unreal y se centra en un desarrollo de juegos quizá para algunos más artesanal.<br><br><a href="http://gamedevhispano.com"><img class="aligncenter size-large wp-image-611" src="https://files.adrianistan.eu/screenshot-gamedevhispano.com-2016-12-05-15-00-18-1024x499.png" alt="screenshot-gamedevhispano-com-2016-12-05-15-00-18" width="840" height="409" /></a><br><br>Aunque Phaser y Babylon hayan sido los frameworks elegidos para el foro, nadie está excluido. Existen subforos de SDL, Ogre3D, PyGame, libGDX y Godot y foros de temática general como <em>¡Enseña tu juego! </em>y <em>Multimedia</em>.<br><br>Os animo a todos a registraros en el foro y a crear un hilo. Que la gente participe y colabore para ayudarse mutuamente.<br><p style="text-align: center;"><a href="http://gamedevhispano.com">Gamedev Hispano</a></p>]]></description>
                <comments>https://blog.adrianistan.eu/nace-foro-gamedev-hispano</comments>
                <pubDate>Mon, 05 Dec 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Viernes de Complot en Adrianistán</title>
                <link>https://blog.adrianistan.eu/viernes-complot-adrianistan</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/viernes-complot-adrianistan</guid>
                <description><![CDATA[Adrianistán es un lugar atípico, por ejemplo, a veces sus ingenieros se preocupan de Haiku, se pasan el día mirando mini-PCs y cuando entran en Wikipedia/Quora necesitan a alguien que les rescate.<br><br>[video width="480" height="270" mp4="https://files.adrianistan.eu/giphy.mp4" loop="true" autoplay="true"][/video]<br><br>Pero en Adrianistán también existe el dinero y a los adrianistaníes (sobre todo los que no forman parte del complot definitivo) les gusta ahorrar. Los muggles celebran su Black Friday, nosotros celebramos el Viernes de Complot. Por una vez al año, Adrianistán sufre de un consumismo compulsivo y todo se vuelve publicidad.<br><br>[video width="480" height="480" mp4="https://files.adrianistan.eu/complot.mp4" loop="true" autoplay="true"][/video]<br><br>Si compras algo de lo que hay en este artículo que sepas que estarás ayudando económicamente a Adrianistán. Todos los libros han pasado lso filtros del comité de actividades anti-42.<br><h2>Libros</h2><br><a href="http://amzn.to/2g7R7lb"><img class="wp-image-587 size-medium" src="https://files.adrianistan.eu/51mPcAohrL._SX333_BO1204203200_-201x300.jpg" alt="51mpcaohrl-_sx333_bo1204203200_" width="201" height="300" /></a> ¿Está usted de broma, Sr. Feynman?<br><br><a href="http://amzn.to/2gjLS1d"><img class="wp-image-588 size-medium" src="https://files.adrianistan.eu/sapiens-196x300.jpg" alt="sapiens" width="196" height="300" /></a> Sapiens, una breve historia de la humanidad<br><br><a href="http://amzn.to/2gjMVOO"><img class="wp-image-589 size-medium" src="https://files.adrianistan.eu/profeta-191x300.jpg" alt="profeta" width="191" height="300" /></a> El Profeta<br><br><a href="http://amzn.to/2gjKYS7"><img class="wp-image-590 size-medium" src="https://files.adrianistan.eu/pendulo-196x300.jpg" alt="pendulo" width="196" height="300" /></a> El péndulo de Foucault<br><br><a href="http://amzn.to/2fJ2mjr"><img class="size-medium wp-image-591" src="https://files.adrianistan.eu/meta-197x300.jpg" alt="La Meta" width="197" height="300" /></a> La Meta<br><br><a href="http://amzn.to/2gEeFBY"><img class="size-medium wp-image-592" src="https://files.adrianistan.eu/divina-comedia-198x300.jpg" alt="La Divina Comedia" width="198" height="300" /></a> La Divina Comedia<br><h2>Vuelos</h2><br><a href="http://www.anrdoezrs.net/click-8041960-12757937-1479726239000" target="_top"><img class="size-full wp-image-593" src="https://files.adrianistan.eu/Airfrance-A-380.jpg" alt="AirFrance ha preparado unas ofertas de Viernes de Complot muy interesantes. ¡Aprovecha y visita a tu familia si vives lejos!" width="635" height="358" /></a> AirFrance ha preparado unas ofertas de Viernes de Complot muy interesantes. ¡Aprovecha y visita a tu familia si vives lejos!<br><br><a href="http://www.anrdoezrs.net/click-8041960-12234753-1464277367000"><img class="size-medium" src="http://www.ftjcfx.com/image-8041960-12234753-1464277367000" alt="Vueling, una compañía aérea que ofrece grandes destinos a bajo precio" width="300" height="250" /></a> Vueling, una compañía aérea que ofrece grandes destinos a bajo precio<br><h2><img src="http://www.tqlkg.com/image-8041960-12757937-1479726239000" width="1" height="1" border="0" />Hoteles</h2><br><a href="http://www.kqzyfj.com/click-8041960-12720230-1479118610000"><img class="size-full" src="http://www.tqlkg.com/image-8041960-12720230-1479118610000" alt="Accor Hoteles ofrece descuentos especiales en hoteles por todo el mundo." width="300" height="300" /></a> Accor Hoteles ofrece descuentos especiales en hoteles por todo el mundo.<br><h2>Mini PCs y microcontroladores</h2><br><a href="http://amzn.to/2fKhux7"><img class="wp-image-595 size-medium" src="https://files.adrianistan.eu/raspi-300x273.png" alt="Raspberry Pi 3" width="300" height="273" /></a> Raspberry Pi 3<br><br><a href="     http://s.click.aliexpress.com/e/BImYRFe"><img class="wp-image-596 size-medium" src="https://files.adrianistan.eu/OrangePi-300x300.jpg" alt="orangepi" width="300" height="300" /></a> Orange Pi 2<br><br><a href="http://amzn.to/2fwnITp"><img class="size-medium wp-image-597" src="https://files.adrianistan.eu/ODroidC2-300x213.jpg" alt="ODROID C2" width="300" height="213" /></a> ODROID C2<br><br><a href="     http://s.click.aliexpress.com/e/BIYZzbqhttp://"><img class="size-medium wp-image-598" src="https://files.adrianistan.eu/BeagleBone-300x200.jpg" alt="BeagleBone Black" width="300" height="200" /></a> BeagleBone Black<br><br><a href="     http://s.click.aliexpress.com/e/FMjmYRN"><img class="size-medium wp-image-599" src="https://files.adrianistan.eu/BananaPi-300x202.jpg" alt="Banana Pi" width="300" height="202" /></a> Banana Pi<br><br><a href="http://amzn.to/2fwhEdD"><img class="size-medium wp-image-600" src="https://files.adrianistan.eu/PineA64-300x300.jpg" alt="Pine A64+" width="300" height="300" /></a> Pine A64+<br><br><a href="http://redirect.viglink.com/?key=71a343f453cb32958bbd43540a0abbd7&amp;u=http%3A%2F%2Fwww.dx.com%2Fes%2Fp%2Fesp8266-esp-12e-development-board-serial-wi-fi-module-w-built-in-ch340g-driver-ic-micro-usb-cable-406735%3Ftc%3DEUR%26gclid%3DCj0KEQiAvNrBBRDe3IOwzLn6_O4BEiQAmbK-DgaAzezItZOCrX7qlZxRVSFtI_IGKS4qh8cVuLNjP-saAtTP8P8HAQ%23.WDdn8GW7rIU"><img class="size-medium wp-image-601" src="https://files.adrianistan.eu/ESP8266-300x300.jpg" alt="ESP8266 (NodeMcu)" width="300" height="300" /></a> ESP8266 (NodeMcu)<br><br><a href="http://redirect.viglink.com/?key=71a343f453cb32958bbd43540a0abbd7&amp;u=http%3A%2F%2Fwww.dx.com%2Fes%2Fp%2Funo-r3-development-board-microcontroller-mega328p-atmega16u2-compat-for-arduino-blue-black-215600%3Ftc%3DEUR%26gclid%3DCj0KEQiAvNrBBRDe3IOwzLn6_O4BEiQAmbK-DkBpfgaDcVt3NwhxbyGTyzNO14mA1zGGlbn08Q_KKwEaApOt8P8HAQ%23.WDdob2W7rIU"><img class="size-full wp-image-602" src="https://files.adrianistan.eu/Arduino.jpg" alt="Arduino UNO" width="180" height="180" /></a> Arduino UNO<br><br><a href="http://redirect.viglink.com/?key=71a343f453cb32958bbd43540a0abbd7&amp;u=http%3A%2F%2Fwww.miniinthebox.com%2Fes%2Fnano-v3-0-avr-atmega328-p-20au-modulo-board-cable-usb-para-arduino-azul-negro_p638909.html%3Fprm%3D2.5.1.1"><img class="size-medium wp-image-603" src="https://files.adrianistan.eu/Nano-300x300.jpg" alt="Arduino Nano" width="300" height="300" /></a> Arduino Nano<br><br><a href="     http://s.click.aliexpress.com/e/RfeyNNR"><img class="size-medium wp-image-604" src="https://files.adrianistan.eu/Launchpad_hw_serial-300x224.jpg" alt="TI Launchpad MSP430" width="300" height="224" /></a> TI Launchpad MSP430<br><br>&nbsp;<br><h2>Dominios</h2><br><a href="http://www.jdoqocy.com/click-8041960-10911706-1477923087000"><img class="size-full" src="http://www.awltovhc.com/image-8041960-10911706-1477923087000" alt="1&amp;1 es un veterano en Internet. Dominios y servidores a precios competitivos." width="300" height="250" /></a> 1&amp;1 es un veterano en Internet. Dominios y servidores a precios competitivos.<br><br><a href="http://ad.zanox.com/ppc/?40527732C90670508T"><img class="size-medium" src="http://ad.zanox.com/ppv/?40527732C90670508" alt="GoDaddy, uno de los registrar DNS más populares del mundo" width="300" height="250" /></a> GoDaddy, uno de los registrar DNS más populares del mundo.<br><h2>Otras promociones</h2><br><a href="http://www.jdoqocy.com/click-8041960-12755904-1479495780000"><img class="size-medium" src="http://www.lduhtrp.net/image-8041960-12755904-1479495780000" alt="MiniInTheBox, uno de las tiendas online de gagdets más grandes de Internet te ofrece descuentos de Viernes de Complot" width="300" height="250" /></a> MiniInTheBox, uno de las tiendas online de gagdets más grandes de Internet te ofrece descuentos de Viernes de Complot.<br><br><a href="http://www.tkqlhce.com/click-8041960-12695531-1474917392000"><img class="size-medium" src="http://www.awltovhc.com/image-8041960-12695531-1474917392000" alt="TomTop, con el código 3TTEU obtendrás 3 euros de descuento" width="500" height="500" /></a> TomTop, con el código 3TTEU obtendrás 3 euros de descuento.<br><br><a href="http://www.anrdoezrs.net/click-8041960-12737261-1478260879000"><img class="size-medium" src="http://www.tqlkg.com/image-8041960-12737261-1478260879000" alt="Intimissimi, 20% de descuento en gran parte de su lencería" width="300" height="250" /></a> Intimissimi, 20% de descuento en gran parte de su lencería con motivo del Viernes de Complot.<br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/viernes-complot-adrianistan</comments>
                <pubDate>Thu, 24 Nov 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Usando Iron, un web framework para Rust</title>
                <link>https://blog.adrianistan.eu/usando-iron-web-framework-rust</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/usando-iron-web-framework-rust</guid>
                <description><![CDATA[<blockquote>Actualmente, si quiéres hacer una aplicación web con Rust, te recomiendo <a href="https://blog.adrianistan.eu/2017/04/15/tutorial-rocket-echa-volar-tus-webapps-rust/">Rocket</a>, es mucho más sencillo de usar, más potente, más rápido y tiene más usuarios.</blockquote><br><a href="https://blog.adrianistan.eu/rust-101-tutorial-rust-espanol/">Rust</a> cada día atrae a más desarrolladores. Es eficiente y es robusto. Mozilla ha sido la principal impulsora de este lenguaje para ser usado en entornos tan complejos como el propio Firefox.<br><br>Hoy vamos a introducirnos en el mundo del desarrollo web con Rust. Cuando la gente oye desarrollo web normalmente se piensa en lenguajes como PHP, Python, Ruby o JavaScript. Estos son lenguajes con los que es rápido desarrollar algo, aunque son mucho menos eficientes y es más fácil cometer errores debido a que son interpretados directamente. Un paso por encima tenemos a Java y C#, que cubren en parte las carencias de los otros lenguajes mencionados. Ha llegado la hora de hablar de Rust. Si bien es cierto que hay web frameworks en C++, nunca han sido muy populares. ¿Será Rust la opción que finalmente nos permita tener aplicaciones web con una eficiencia nativa?<br><br>Existen varios web frameworks en Rust, para este tutorial vamos a usar <a href="http://ironframework.io">Iron</a>, el más popular según Crates.io. Quizá te interese echar un vistazo también a <a href="https://blog.adrianistan.eu/2017/04/15/tutorial-rocket-echa-volar-tus-webapps-rust/">Rocket</a>, mi preferido.<br><br><img class="alignnone size-large wp-image-578" src="https://files.adrianistan.eu/IronFramework-1024x499.png" alt="ironframework" width="840" height="409" /><br><h2>Crear proyecto e instalar Iron</h2><br>Lo primero que hay que hacer es crear un nuevo proyecto en Rust, lo hacemos gracias a Cargo.<br><pre class="lang:default decode:true">cargo new --bin MundoRust<br><br>cd MundoRust<br></pre><br>Ahora editamos el fichero Cargo.toml para añadir las dependencias que vamos a usar.<br><pre class="lang:default decode:true">[package]<br>name = "MundoRust"<br>version = "0.1.0"<br>authors = ["Adrián Arroyo Calle"]<br><br>[dependencies]<br>iron = "0.4.0"<br>router = "0.4.0"<br>staticfile = "0.3.1"<br>mount = "0.2.1"<br><br></pre><br>Ahora obtenemos las dependencias especificadas con Cargo.<br><pre class="lang:default decode:true">cargo run<br></pre><br><h2>Hola Mundo con Iron</h2><br>Vamos a empezar a programar en Rust. Vamos a hacer una simple aplicación que devuelva "Hola Rustáceos" por HTTP.<br><br>Editamos el archivo src/main.rs<br><pre class="lang:rust decode:true">extern crate iron;<br><br>use iron::prelude::*;<br><br>fn hola(_: &amp;mut Request) -&gt; IronResult&lt;Response&gt; {<br>    Ok(Response::with((iron::status::Ok, "Hola Rustaceos")))<br>}<br><br>fn main() {<br>    Iron::new(hola).http("0.0.0.0:80").unwrap();<br>}<br></pre><br><img class="alignnone size-large wp-image-577" src="https://files.adrianistan.eu/HolaRustaceos-1024x523.png" alt="holarustaceos" width="840" height="429" /><br><h2>Usando router para el enrutamiento</h2><br>Hemos hecho un pequeño servidor HTTP con Iron. Pero nos falta algo, que sea capaz de manejar rutas. Que miweb.com/hola no sea lo mismo que miweb.com/adios. Iron por defecto no trae enrutador, pero es muy habitual usar Router, que ya hemos instalado antes por conveniencia.<br><pre class="lang:rust decode:true">extern crate iron;<br>extern crate router;<br><br>use iron::prelude::*;<br>use router::Router;<br><br>fn get_page(r: &amp;mut Request) -&gt; IronResult&lt;Response&gt;{<br>    let path = r.url.path();<br>    Ok(Response::with((iron::status::Ok, format!("Hola, peticion GET {}",path[0]))))<br>}<br><br>fn submit(_: &amp;mut Request) -&gt; IronResult&lt;Response&gt;{<br>    Ok(Response::with((iron::status::Ok, "Peticion POST")))<br>}<br><br>fn main() {<br>    let mut router = Router::new();<br><br>    router.get("/:page", get_page, "page");<br>    router.post("/submit", submit, "subdmit");<br><br>    Iron::new(router).http("0.0.0.0:80").unwrap();<br><br>}<br></pre><br><img class="alignnone size-large wp-image-579" src="https://files.adrianistan.eu/GetIron-1024x541.png" alt="getiron" width="840" height="444" /><br><h2>Archivos estáticos</h2><br>Para gestionar los ficheros estáticos vamos a usar staticfile y mount, otras dos librerías para Iron.<br><pre class="lang:rust decode:true">extern crate iron;<br>extern crate router;<br>extern crate staticfile;<br>extern crate mount;<br><br>use iron::prelude::*;<br>use router::Router;<br>use staticfile::Static;<br>use mount::Mount;<br><br>fn get_page(r: &amp;mut Request) -&gt; IronResult&lt;Response&gt;{<br>    let path = r.url.path();<br>    Ok(Response::with((iron::status::Ok, format!("Hola, peticion GET {}",path[0]))))<br>}<br><br>fn main() {<br>    let mut router = Router::new();<br><br>    router.get("/:page", get_page, "page");<br><br>    let mut mount = Mount::new();<br>    mount.mount("/public",Static::new("static/"));<br>    mount.mount("/",router);<br><br>    Iron::new(mount).http("0.0.0.0:80").unwrap();<br>}<br></pre><br><img class="alignnone size-large wp-image-580" src="https://files.adrianistan.eu/IronStaticFile-1024x524.png" alt="ironstaticfile" width="840" height="430" /><br><br>&nbsp;<br><br>Hemos dado nuestros primeros pasos en Iron, un web framework para Rust. Iron es completamente modular y soporta muchas más cosas que aquí no hemos visto. Gran parte de su funcionalidad se implementa a través de middleware, como en otros web frameworks populares.]]></description>
                <comments>https://blog.adrianistan.eu/usando-iron-web-framework-rust</comments>
                <pubDate>Sun, 20 Nov 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Usar la vulnerabilidad DirtyCoW para convertirse en root en Linux</title>
                <link>https://blog.adrianistan.eu/usar-la-vulnerabilidad-dirtycow-convertirse-root-linux</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/usar-la-vulnerabilidad-dirtycow-convertirse-root-linux</guid>
                <description><![CDATA[Hace ya unas semanas salió a la luz la vulnerabilidad conocida como DirtyCoW, registrada bajo el nombre de CVE-2016-5195. Esta vulnerabiliad permite a cualquier usuario con acceso a la máquina de forma limitada llegar a convertirse en root y tomar el control total de la máquina. Tal y como nos decía <a href="http://lamiradadelreplicante.com/2016/10/21/dirty-cow-vulnerabilidad-en-linux-que-permite-escalada-de-privilegios/">La Mirada del Replicante</a>:<br><blockquote>Clasificada de forma oficial como <a href="https://access.redhat.com/security/cve/cve-2016-5195"><strong>CVE-2016-5195</strong></a> y descubierto por <strong>Phil Oester</strong> –un experto en seguridad y desarrollador de Linux–, <strong>Dirty COW</strong> (<em>copy-on-write</em>) permite una <strong>escalada de privilegios</strong> de forma remota o local.<br><br>....<br><br>La vulnerabilidad ha estado <strong>presente durante muchos años</strong> (desde la versión 2.6.22 del kernel, liberada en <strong>2007</strong>) e incluso Linus fue consciente de ella en su momento.</blockquote><br>Hoy vamos a ver como aprovechar esta situación. Válida para todos los sistemas con kernel Linux, incluido Android (aunque allí el procedimiento no es tan sencillo).<br><br><img class="alignnone size-full wp-image-569" src="https://files.adrianistan.eu/dirty-cow.jpg" alt="dirty-cow" width="900" height="587" /><br><h2>Comprobando requisitos</h2><br>En primer lugar la vulnerabilidad requiere disponer de acceso a la máquina con un usuario no-privilegiado. Puede ser de forma local o remota vía SSH, pero este requisito ya hace que a muchos servidores no les afecte de manera directa.<br><br>La vulnerabilidad además ya se encuentra parcheada en muchas distros GNU/Linux y no se podrá realizar. Es por tanto requisito que la versión del kernel esté entre 2.6.22 y 4.4.26. Puedes comprobar la versión del kernel con el comando uname.<br><pre>uname -a</pre><br><h2>Descargando el exploit</h2><br>Ahora el siguiente paso es descargar el exploit. Yo voy a usar el exploit diseñado por <span class="pl-c">Gabriele Bonacini</span> y que lo podéis descargar desde GitHub, bien con <a href="https://github.com/aarroyoc/CVE-2016-5195/archive/master.zip">un fichero ZIP</a> o bien mediante <a href="https://github.com/aarroyoc/CVE-2016-5195">el programa Git</a>. Puedes descargarlo desde la línea de comandos así:<br><pre>wget -O dirtycow.zip https://github.com/AdrianArroyoCalle/CVE-2016-5195/archive/master.zip<br><br>unzip dirtycow.zip<br><br>cd CVE-2016-5195-master</pre><br>Ahora debemos compilar el pequeño programa en C++ que se ha descargado. Simplemente escribimos en la terminal:<br><pre>make</pre><br><h2>Ejecutando el exploit</h2><br>Ya estamos listos para ejecutar el exploit. Abrimos desde la terminal el programa que acabamos de compilar.<br><pre>./dcow</pre><br>Este exploit, si funciona correctamente, nos dirá por pantalla que la contraseña del root es <em>dirtyCowFun</em>. Si tarda demasiado el programa entonces el exploit no ha funcionado. ¿Qué es lo que ha hecho? Aprovechando la vulnerabilidad Dirty CoW ha logrado cambiar, solo en memoria, el contenido del archivo /etc/passwd, que es donde Linux guarda las contraseñas. Este cambio como repito solo se produce en la memoria, pues el archivo físico en el disco sigue inalterado. Si reiniciamos la máquina habrá que repetir el proceso. Esto tiene la gravedad de que es fácil no darse cuenta de si la vulnerabilidad ha sido explotada, el atacante simplemente reinicia la máquina con reboot y la contraseña del root vuelve a su estado habitual.<br><br>Sabiendo esto, ya podemos escribir con total seguridad<br><pre>su</pre><br>Y cuando nos pregunte la contraseña, escribimos <em>dirtyCowFun</em>.<br><br>Enhorabuena, si has llegado hasta aquí conseguiste escalar privilegios y ya eres root. Ahora no seas demasiado malo, te recuerdo que un gran poder conlleva una gran responsabilidad.]]></description>
                <comments>https://blog.adrianistan.eu/usar-la-vulnerabilidad-dirtycow-convertirse-root-linux</comments>
                <pubDate>Sat, 05 Nov 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Tutorial de WebExtensions (II) - Popup y Tabs</title>
                <link>https://blog.adrianistan.eu/tutorial-webextensions-ii-popup-tabs</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/tutorial-webextensions-ii-popup-tabs</guid>
                <description><![CDATA[Continuamos con el tutorial de WebExtensions, hoy veremos un elemento muy útil para muchas extensiones que es el popup.<br><h2>¿Qué es el popup?</h2><br>El popup es un panel desplegable que se acciona al pulsar un botón de acción. Este panel es una página HTML que puede contener código JavaScript.<br><br><img class="alignnone size-full wp-image-563" src="https://files.adrianistan.eu/PopUp.png" alt="popup" width="911" height="618" /><br><br>El popup es el elemento que permite al usuario un mayor control sobre la extensión y es ideal tanto para mostrar información como para contener acciones. De hecho, WebExtensions limita el número de botones de acción que puede crear una extensión a uno. Si queremos simular tener más botones deberemos usar un popup que despliegue un pequeño menú.<br><h2>Definir el popup en manifest.json</h2><br>Para definir el popup tenemos que indicar la ubicación del fichero HTML correspondiente al popup.<br><br><pre class="lang:js decode:true"><br>	&quot;browser_action&quot; : {<br>		&quot;default_icon&quot; : &quot;icons/icono_guay.png&quot;,<br>		&quot;default_title&quot; : &quot;Mi PopUp guay&quot;,<br>		&quot;default_popup&quot; : &quot;popup/index.html&quot;<br>},<br></pre><br><br>Con eso ya tenemos definido el popup. Se mostrará el botón en la barra superior y al hacer click se abrirá el popup.<br><h2>El PopUp en sí</h2><br>El PopUp es un fichero HTML sin el mayor misterio. Para definir el tamaño del PopUp usaremos CSS:<br><br><pre class="lang:css decode:true"><br>body{<br>    width: 400px;<br>    height: 400px;<br>}<br></pre><br><br>Y el navegador adaptará el PopUp a esas dimensiones. A ese archivo HTML le podemos añadir archivos JavaScript mediante el procedimiento habitual en la web, vía una etiqueta script. Lo interesante viene en estos archivos de JavaScript pues cuentan con más privilegios. Desde ellos podemos acceder a las APIs retringidas a extensiones así como librarnos de limitaciones impuestas a las webs (como la política que bloquea los XMLHttpRequests ejecutados a dominios ajenos a nuestra página).<br><br>Para usar las APIs restringidas debemos pedir permiso en el fichero manifest.json. Por ejemplo, vamos a jugar con las pestañas. Para ello usamos los permisos tabs y activeTab.<br><br><pre class="lang:js decode:true"><br>    &quot;permissions&quot; : [&quot;tabs&quot;,&quot;activeTab&quot;],<br></pre><br><br><h2>Tabs (pestañas) en WebExtensions</h2><br>La API se encuentra en chrome.tabs y dispone de varios métodos y eventos. Vamos a ver los más importantes:<br><ul><br> 	<li>chrome.tabs.query - Obtener información de las pestañas<br><ul><br> 	<li>Este método es muy flexible, lo podemos comparar a hacer un SELECT en SQL, pues obtiene una lista de pestañas que cumplen los parámetros que hemos indicado. Por ejemplo para comprobar la pestaña actual, usaremos la propiedad active:<br><br><pre class="lang:js decode:true"><br>chrome.tabs.query({&quot;active&quot;: true},function(tabs){<br>    var tab = tabs[0];<br>    // tab es la pestaña actualmente activa <br>});<br></pre><br><br>Otra opción muy interesante es para averiguar que pestañas tienen abierta una URL que cumple un patrón. Además los parámetros de búsqueda se pueden combinar:<br><br><pre class="lang:js decode:true"><br>chrome.tabs.query({&quot;url&quot; : &quot;*://*.adrianistan.eu/*&quot;, &quot;status&quot; : &quot;loading&quot;, &quot;muted&quot; : true},function(tabs){<br>  // tabs (que puede estar vacío) contiene todas las URL que estan en un adrianistan.eu (subdominios incluidos),<br>  // se encuentran cargando<br>  // y el usuario ha silenciado su sonido<br>});<br></pre><br><br></li><br></ul><br></li><br> 	<li>chrome.tabs.create - Permite crear nuevas pestañas<br><ul><br> 	<li>Su uso básico es muy simple<br><br><pre class="lang:js decode:true"><br>chrome.tabs.create({&quot;url&quot; : &quot;https://blog.adrianistan.eu/&quot;},function(tab){<br>  // tab es la pestaña creada<br>});<br></pre><br><br></li><br></ul><br></li><br> 	<li>chrome.tabs.executeScript - Esta función permite inyectar a la pestaña un script de contenido (Content Script).<br><ul><br> 	<li>Ahora no vamos a entrar en como se realizaría la comunicación entre los scripts Background y los Content. El código se puede pasar por una cadena de texto o por un archivo, siendo preferible esta última opción. Se puede especificar además cuando queremos que se ejecute dentro del ciclo de carga de la pestaña.<br><br><pre class="lang:js decode:true"><br>chrome.tabs.executeScript(tab.id (opcional), {&quot;code&quot; : &quot;alert('Hola');&quot;},function(){<br><br>});<br><br>O<br><br>chrome.tabs.executeScript({&quot;file&quot; : &quot;miarchivo.js&quot;, &quot;runAt&quot; : &quot;document_start&quot;},function(){<br><br>});<br></pre><br><br></li><br></ul><br></li><br> 	<li>chrome.tabs.insertCSS - Permite inyectar CSS en la pestaña<br><ul><br> 	<li>Su uso es exactamente igual al de chrome.tabs.executeScript<br><br><pre class="lang:js decode:true"><br>chrome.tabs.insertCSS({&quot;file&quot; : &quot;misestilos.css&quot;},function(){<br><br>});<br></pre><br><br></li><br></ul><br></li><br> 	<li>chrome.tabs.sendMessage - Enviar un mensaje a la pestaña<br><ul><br> 	<li>Hasta ahora no hemos visto la separación entre background y content scripts. Esta función permite enviar datos desde los scripts background a los content, que tendrán una función de escucha. La menciono, pero lo veremos más adelante en profundidad.</li><br></ul><br></li><br></ul><br>Por último, veamos algunos de los eventos que puede generar la API.<br><ul><br> 	<li>chrome.tabs.onUpdated - Es llamado cada vez que una pestaña sufre una modificación en sus datos (cambio de URL, quitar el sonido, finalizar una carga, etc)<br><ul><br> 	<li>Es muy posible que necesitemos filtrar el contenido que nos da, para fijarnos si se ha producido en una URL que nos interesa o en alguna condición relevante. Eso no obstante, es responsabilidad del programador.<br><br><pre class="lang:js decode:true"><br>chrome.tabs.onUpdated.addListener(function(tabId,changeInfo,tab){<br>  // el objeto changeInfo presenta una estructura similar al de búsqueda con chrome.tabs.query<br>  // este objeto solo contiene valores para aquellos parámetros que han cambiado<br>  // con tab podemos acceder a la pestaña al completo<br>});<br></pre><br><br></li><br></ul><br></li><br></ul><br>Con esto ya podemos hacer extensiones interesantes, la APIs de PopUp y de Tabs son de las más usadas en WebExtensions.]]></description>
                <comments>https://blog.adrianistan.eu/tutorial-webextensions-ii-popup-tabs</comments>
                <pubDate>Thu, 03 Nov 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Tutorial de WebExtensions (I) - manifest.json y conceptos</title>
                <link>https://blog.adrianistan.eu/tutorial-webextensions-i-manifest-json-conceptos</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/tutorial-webextensions-i-manifest-json-conceptos</guid>
                <description><![CDATA[WebExtensions es un sistema para desarrollar extensiones de navegador. Realmente se trata de la API original de Google Chrome y Chromium. Esta API ya había sido diseñada teniendo en cuenta las extensiones de Firefox basadas en XUL y XPCOM. Simplifica mucho el proceso de tareas comunes, no requiere reinicios y es más segura al no permitir tanto control sobre el navegador.<br><br><img class="wp-image-498 size-full" src="https://files.adrianistan.eu/q036q.jpg" alt="WebExtensions" width="636" height="358" /> Tomada de http://tenfourfox.blogspot.com.es/2015/08/okay-you-want-webextensions-api.html<br><br>Posteriormente Firefox respondería con Jetpack y Addon SDK, un sistema similar pero escrito encima de XUL y XPCOM, incompatible pero que no necesitaba reinicios y tampoco perdía potencia pues las APIs de XPCOM seguían allí.<br><br>Luego Opera decide abandonar Presto y usar Blink, el motor de Chromium. Con respecto a su sistema de extensiones, planean ser 100% compatibles con Chrome.<br><br>Firefox también ha de realizar cambios internos, concretamente se inició el trabajo (electrolysis) de hacer Firefix multiproceso. Esto rompía gran parte de las APIs internas de Firefox. Todos los complementos necesitarían ser reescritos. Ya de paso, teniendo en cuenta que hay que diseñar una nueva API para extensiones, se toma la de Chrome y se renombra WebExtensions. Posteriormente <a href="https://developer.microsoft.com/en-us/microsoft-edge/platform/documentation/extensions/">Microsoft afirma que Edge</a> será compatible con extensiones de Chrome o WebExtensions. Esta es la historia de como 4 navegadores se pusieron de acuerdo en estandarizar la API de desarrollo de extensiones para navegadores.<br><br>En esta serie de tutoriales vamos a ver como programar nuestra extensión usando WebExtensions.<br><h2>manifest.json</h2><br>El fichero manifest.json de una extensión es muy importante y es obligatorio. Contiene metadatos de la extensión así como permisos de los que dispone la extensión y las acciones que define.<br><br>En primer lugar vamos a distinguir entre distintos tipos de scripts distintos.<br><ul><br> 	<li>Background scripts: estos scripts se ejecutan en el <em>fondo</em> del navegador. Se ejecutan al iniciar el navegador. Podemos configurar eventos y entonces se mantendrán a la escucha durante toda la sesión. Estos scripts no pueden acceder a las páginas web.</li><br> 	<li>Content scripts: estos scripts se añaden a determinadas páginas según un patrón en el archivo manifest.json o desde un background script. Estos scripts interactúan directamente con las páginas en los que han sido <em>adjuntados</em>.</li><br></ul><br>Veamos ahora un fichero manifest.json<br><br>&nbsp;<br><br><pre class="lang:js decode:true"><br>{<br>	&quot;manifest_version&quot; : 2,<br>	&quot;name&quot; : &quot;Bc.vc shortener&quot;,<br>	&quot;version&quot; : &quot;2.0.0&quot;,<br>	&quot;description&quot; : &quot;Shorthener for Bc.vc&quot;,<br>	&quot;homepage_url&quot; : &quot;http://adrianistan.eu/norax/&quot;,<br>	&quot;icons&quot; : {<br>		&quot;16&quot; : &quot;icon-16.png&quot;,<br>		&quot;48&quot; : &quot;icon-48.png&quot;,<br>		&quot;64&quot; : &quot;icon-64.png&quot;,<br>		&quot;96&quot; : &quot;icon-96.png&quot;,<br>		&quot;128&quot; : &quot;icon-128.png&quot;<br>	},<br>	&quot;applications&quot; : {<br>		&quot;gecko&quot; : {<br>			&quot;id&quot; : &quot;@bc-vc&quot;,<br>			&quot;strict_min_version&quot; : &quot;45.*&quot;<br>		}<br>	},<br>	&quot;permissions&quot; : [&quot;notifications&quot;,&quot;contextMenus&quot;,&quot;clipboardWrite&quot;,&quot;activeTab&quot;,&quot;storage&quot;,&quot;http://bc.vc/&quot;],<br>	&quot;browser_action&quot; : {<br>		&quot;default_title&quot; : &quot;Bc.vc shortener&quot;,<br>		&quot;default_icon&quot; : &quot;icon-64.png&quot;<br>	},<br>	&quot;background&quot; : {<br>		&quot;scripts&quot; : [&quot;background/install.js&quot;,&quot;background/main.js&quot;]<br>	},<br>	&quot;options_ui&quot; : {<br>		&quot;page&quot; : &quot;options/options.html&quot;,<br>		&quot;chrome_style&quot; : true<br>	}<br>}<br></pre><br><br>manifest_version siempre es 2. Al principio rellenamos metadatos sencillos, name, description, version, homepage_url,... Posteriormente debemos añadir los iconos. Dependiendo de lo que queramos no hacen falta todos los iconos. Por ejemplo, el icono de 16x16 solo es necesario si nuestra extensión define un menú contextual del botón derecho. La sección applications ha sido añadida por Mozilla y originalmente no está en Chrome. Define la compatibilidad entre distintos navegadores y versiones, sin embargo, por ahora solo Firefox tiene en cuenta esto.<br><br>A continuación se definen los permisos. Una extensión necesita pedir permiso para acceder a ciertas APIs más privilegiadas. Por ejemplo, para mostrar notificaciones o para realizar peticiones HTTP a servidores remotos.<br><br>A continuación vemos la <em>acción del navegador</em>. A diferencia de otros sistemas de extensiones, WebExtensiones solo permite un botón en la interfaz del navegador. Es una manera excelente de separar funcionalidades, aunque si vienes de Firefox verás como limita a muchas extensiones, algunas de las cuáles modificaban la interfaz por completo. La acción del navegador puede hacer dos cosas: abrir un panel popup o generar un evento en el background script. Dependiendo de como sea la experiencia de usuario que prefieras te convendrá una opción u otra (en este caso, como no hemos definido un popup html <em>default_popup</em>, tendremos que escuchar el evento browserAction en un background script). Similar a <em>browser_action</em> existe <em>page_action</em>, la única diferencia es el lugar donde aparece el botón.<br><br><img src="http://i.stack.imgur.com/k0Ayh.png" width="184" height="87" /> Page Action a la izquierda<br><br><img src="http://i.stack.imgur.com/UL65l.png" width="400" height="38" /> Browser Action a la derecha<br><br>Después podemos asignar archivos de background scripts. Por último <em>options_ui</em> es la manera adecuada de crear una página de opciones. Será una página HTML, pero de eso hablaremos en otro tutorial.<br><br>Por último vamos a ver los content scripts<br><br><pre class="lang:js decode:true"><br>&quot;content_scripts&quot;: [<br>  {<br>    &quot;matches&quot;: [&quot;*://*.mozilla.org/*&quot;],<br>    &quot;js&quot;: [&quot;borderify.js&quot;],<br>    &quot;css&quot; : [&quot;style.css&quot;],<br>    &quot;all_frames&quot; : false,<br>    &quot;run_at&quot; : &quot;document_end&quot;<br>  }<br>]<br></pre><br><br>Una manera de añadir content scripts es desde el manifest.json, especificando un patrón en <em>matches</em>. Podemos añadir JavaScript y CSS.<br><br>Otra opción es <em>web_accesible_resources</em>. Se trata de elementos de la extensión que pueden ser cargados desde una página web. Por ejemplo, si un content script quiere mostrar una imagen en una web, esta deberá estar indicada en web_accesible_resources.<br><br><pre class="lang:js decode:true"><br>&quot;web_accesible_resources&quot; : [&quot;imagen/zorro.png&quot;]<br><br>...<br><br>chrome.extension.getURL(&quot;imagen/zorro.png&quot;); // desde el content script<br></pre><br><br>Este fichero es el más importante de una WebExtension. En el siguiente tutorial veremos los background scripts combinados con browser actions.]]></description>
                <comments>https://blog.adrianistan.eu/tutorial-webextensions-i-manifest-json-conceptos</comments>
                <pubDate>Sat, 01 Oct 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Phaser.js Hispano, aprende a hacer videojuegos en HTML5</title>
                <link>https://blog.adrianistan.eu/phaser-js-hispano-aprende-videojuegos-html5</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/phaser-js-hispano-aprende-videojuegos-html5</guid>
                <description><![CDATA[<a href="http://www.phaser-hispano.gq">Phaser.js Hispano</a> es un sitio web, de mi propia creación, donde escribo tutoriales para aprender a usar Phaser, una de las librerías más populares para crear juegos 2D con JavaScript.<br><br><img class="alignnone size-full wp-image-478" src="https://files.adrianistan.eu/Phaser.png" alt="Phaser" width="696" height="584" /><br><br>Su éxito se basa en su simplicidad. No trata de reinventar la rueda con conceptos extraños e innovadores. Simplemente hace lo que muchas otras librerías hacen, de un modo claro, sin complicaciones. Fue creado por Richard Davey, alias photonstorm. Comenzó como una librería privada suya, pues él realiza juegos de navegador como trabajo. Con el tiempo fue mejorando, se hizo opensource y ahora cuenta con muchos usuarios aunque gran parte del desarrollo lo sigue realizando Richard. El motor ha sido usado con éxito en infinidad de juegos y a día de hoy me parece la opción más madura y efectiva de realizar un juego HTML5 con JavaScript.<br><br>Phaser usa Pixi.js como motor de renderizado, lo que permite a los juegos usar WebGL o Canvas 2D indistintamente. Además si manejas Pixi verás que algunas partes de la API de Phaser son similares. Los juegos funcionan en cualquier dispositivo que soporte HTML5, desde ordenadores de mesa (Windows/Mac/Linux) hasta televisores y consolas (WiiU, Xbox One, Chromecast,...) pasando por los omnipresentes móviles y tablets, que a día de hoy son las plataformas que prefieren los jugadores para jugar a juegos sencillos, de poca profundidad.<br><br>¿Estas listo para probar Phaser? He realizado una lista con, al menos, los elementos de los que me gustaría tratar en la web Phaser.js Hispano. <a href="http://www.phaser-hispano.gq/p/la-gran-guia-de-phaserjs-en-espanol.html">La Gran Guía de Phaser en Español</a>, úsalo como índice para tus consultas. Si tienes alguna duda no dudes en expresarla. ¿Quiéres tratar algún tema en particular? ¿Conocías la librería antes?]]></description>
                <comments>https://blog.adrianistan.eu/phaser-js-hispano-aprende-videojuegos-html5</comments>
                <pubDate>Thu, 01 Sep 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>La Criptonovela del verano: una historia en tres capítulos (Capítulo 3)</title>
                <link>https://blog.adrianistan.eu/la-criptonovela-del-verano-una-historia-tres-capitulos-capitulo-3</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/la-criptonovela-del-verano-una-historia-tres-capitulos-capitulo-3</guid>
                <description><![CDATA[Ya hemos lo que sucedió en el mundo Bitcoin con el tamaño de bloques y también el hard fork en Ethereum causado por la pérdida de fondos en la DAO. Ahora veremos otro robo, con un desenlace totalmente distinto, el caso de Bitfinex.<br><h2>Capítulo 3: El robo de Bitfinex</h2><br>Bitfinex es una casa de intercambio, donde la gente puede depositar sus bitcoins y conseguir otra moneda y viceversa. En el caso de Bitfinex el soporte al trading está muy presente.<br><br><img class="alignnone size-full wp-image-533" src="https://files.adrianistan.eu/Bitfinex.png" alt="Bitfinex" width="1000" height="600" /><br><br>El 3 de agosto saltó a noticia, incluso la prensa generalista de hizo eco, había habido un robo en la plataforma. Al poco se aclaró que el problema no era un fallo de seguridad de Bitcoin sino de la propia plataforma de Bitfinex. La cantidad sustraída ascendió a más de 65 millones de dólares al cambio. Ese mismo día la cotización de Bitcoin cayó un 20%. A estas alturas el caso podría recordarnos al de Mt. Gox, pero aquí acaban las similitudes.<br><br><img class="size-full wp-image-534" src="https://files.adrianistan.eu/MtGox.jpg" alt="CEO de Mt. Gox en los buenos tiempos" width="960" height="541" /> CEO de Mt. Gox en los buenos tiempos<br><br>Bitfinex bloqueó la plataforma y decidió que quitaría el 36% a todos los que tuviesen depositado dinero en la plataforma. No importa que tuviesen dólares, Litecoins, Ethereum, ... les afecta a todos. Bitfinex además tuvo una curiosa idea, compensó a sus usuarios con BFX, una moneda especial. Esta moneda representa el dinero sustraído, con valor nominal de 1$. Es decir, si por la quita del 36% perdiste 500$, Bitfinex te recompensa con 500 BFX.<br><br>Ante esta curiosa respuesta quedan dos posibilidades:<br><ul><br> 	<li>Cuando Bitfinex logre recuperar el dinero sustraído fruto de su actividad recompre los BFX a valor nominal. Esto implicaría que Bitfinex pagaría con sus bolsillos las pérdidas del robo y los usuarios recuperarían su dinero íntegramente.</li><br> 	<li>Intercambiar los BFX por otra moneda, aquí uno puede asumir las pérdidas (pues el BFX siempre valdrá menos que un dólar a nivel de mercado, ya que la devolución no es algo garantizado) o comprar todavía más BFX con la esperanza de que suban.</li><br></ul><br>Nada más salir BFX, su valor experimentó un descenso pronunciado, llegando a caer hasta un valor de 0,3 $.<br><br>Sin embargo esta solución no ha gustado a muchos usuarios que plantean <a href="https://www.bitfinexlawsuit.com/">emprender acciones legales contra la compañía</a>.<br><br>A modo de comparación, si recordáis, con Ethereum ante un robo de una cantidad inferior se llegó al hard fork, sin embargo en este caso tal situación no se ha planteado, pues se considera que el fallo no lo tuvo en ningún caso Bitcoin sino Bitfinex.<br><br>¿Te parece correcta la solución de Bitfinex ante el robo?<br><br>Este es el último capítulo de la serie <em>La Criptonovela del verano: una historia en 3 capítulos</em>. Podéis dejar en los comentarios si os ha gustado o no y por qué.]]></description>
                <comments>https://blog.adrianistan.eu/la-criptonovela-del-verano-una-historia-tres-capitulos-capitulo-3</comments>
                <pubDate>Tue, 30 Aug 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>La Criptonovela del verano: una historia en tres capítulos (Capítulo 2)</title>
                <link>https://blog.adrianistan.eu/la-criptonovela-del-verano-una-historia-tres-capitulos-capitulo-2</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/la-criptonovela-del-verano-una-historia-tres-capitulos-capitulo-2</guid>
                <description><![CDATA[<a href="https://blog.adrianistan.eu/2016/08/27/la-criptonovela-del-verano-una-historia-tres-capitulos-capitulo-1/">En el capítulo anterior vimos los intentos de tomar el control de Bitcoin por parte de Blockstream y la disputa por el tamaño de bloque con Core, Classic y Unlimited luchando por hacerse un hueco.</a> Veamos la situación en <a href="https://blog.adrianistan.eu/2016/03/02/ethereum-y-smartcontracts/">Ethereum</a>.<br><h2>Capítulo 2: Ethereum y la DAO, o el debate de si el código es la ley</h2><br>Ethereum es una plataforma basada en la cadena de bloques. Normalmente cuando se habla de cadena de bloques la gente piensa en Bitcoin y piensa que la única aplicación de esta tecnología son las criptodivisas. ¿Pero qué pasa si en vez de dinero transmitimos datos? ¿Y si esos datos tienen un procesado dentro de la propia plataforma? Pues eso es Ethereum, donde pueden funcionar aplicaciones descentralizadas en su máquina virtual, con la verificación entre nodos que nos da la cadena de bloques. Esto son los contratos inteligentes, para más información visita la entrada que escribí sobre <a href="https://blog.adrianistan.eu/2016/03/02/ethereum-y-smartcontracts/">Ethereum y Smart Contracts</a>.<br><br>El crecimiento de Ethereum ha sido impresionante, hasta tal punto de que la moneda propia de Ethereum, el Ether tiene niveles de capitalización de mercado y volumen que cualquier criptodivisa desearía y que solo Bitcoin es capaz de lograr.<br><br><img class="alignnone size-full wp-image-526" src="https://files.adrianistan.eu/EthereumMarketCap.png" alt="EthereumMarketCap" width="663" height="548" /><br><br>Los desarrolladores de Ethereum deciden crear el 30 de abril de 2016 una organización autónoma, un organismo regido por el código sin trabajadores y a la vez fondo de inversión para otras empresas y organizaciones basadas en Ethereum que repartía beneficios a sus inversores. Su nombre fue la DAO (siglas de <a class="mw-redirect" title="Decentralized Autonomous Organization" href="https://en.wikipedia.org/wiki/Decentralized_Autonomous_Organization">Decentralized Autonomous Organization</a>). Algunas de sus características eran:<br><ul><br> 	<li>Funcionamiento sobre la plataforma Ethereum sin jefes ni junta directiva</li><br> 	<li>Totalmente autónoma</li><br> 	<li>Opensource, programada en Solidity</li><br> 	<li>Opera sin la regulación de ninguna nación del mundo</li><br></ul><br>La DAO fue financiada gracias a financiación colectiva (<em>crowdfunding</em>) el 28 de mayo de 2016, con un éxito rotundo. La DAO batió récords y se convirtió en la campaña de <em>crowdfunding</em> más exitosa de la historia, recaudando 160 millones de dólares en monedas de Ethereum, Ether. Superaba así al mayor proyecto hasta la fecha que era el videojuego <em>Star Citizen</em>.<br><br><img class="wp-image-527 size-full" src="https://files.adrianistan.eu/DAO.jpg" alt="DAO" width="256" height="256" /> DAO, en chino, "el camino"<br><br>Se calculó que el 14% de todo el Ether minado en Ethereum se encontraba en la DAO. A partir del 28 de mayo las participaciones en la DAO podían ser intercambiadas como si se tratara de una criptodivisa más.<br><br>Al poco tiempo llegan los problemas, varias personas revisan el código de la DAO y encuentran vulnerabilidades graves que piden que sean corregidas.<br><br>Ya el 17 de junio, un hacker aprovechó una combinación de las vulnerabilidades descubiertas en la DAO previamente para sustraer un tercio de la cantidad depositada en la DAO. Estas vulnerabilidades no se creían explotables hast que el hacker encontró que las mismas se encontraban en otra parte del código y le dejarían replicar la DAO, pero bajo su control. Se intentó parar el ataque mandando SPAM a la red Ethereum. Al poco se lanzó un <em>soft fork</em> en Ethereum que limitaba la la cuenta DAO hija gastar ese dinero hasta que no hubiesen transcurrido 27 días, tiempo en el que se decidiría que hacer. Al cambio la cantidad del robo fue de 50 millones de dólares. El precio del Ether se desplomó. Esto suscitó un gran debate.<br><br>¿Era una brecha de seguridad? ¿O simplemente un método legal pero poco ético de cumplir las disposiciones del contrato inteligente? En Ethereum la norma era que el código es la ley, lo que se programa se cumple siempre sin excepción. El hacker cumplió los términos del contrato inteligente. ¿Era fallo del hacker o del desarrollador que escribió el contrato de forma pésima? ¿Se podía considerar un robo? Si recordamos, la DAO no estaba sujeta a ninguna legislación nacional.<br><br>La gente se dividió en dos bandos:<br><ul><br> 	<li>El primero consideraba que el hacker, aunque de forma poco ética, había cumplido los términos y disposiciones y por ello legalmente le pertenecería ese dinero.</li><br> 	<li>Otros que consideraban que esto debía de ser considerado una excepción y que había que encontrar una forma de devolver el dinero a la gente, incumpliendo el mandato de que el código es la ley irrefutable.</li><br></ul><br><img class="alignnone size-full wp-image-528" src="https://files.adrianistan.eu/DAOButton.jpg" alt="DAOButton" width="250" height="374" /><br><br>Los desarrolladores de Ethereum, que eran también algunos de los grandes inversores de la DAO, prefirieron la segunda opción. Ethereum fue programado para añadir la característica de devolver el dinero sustraído a partir de un determinado bloque que entraría en la cadena a partir del 17 de julio. Obviamente los usuarios del primer grupo sintieron que los principios de Ethereum se estaban viendo traicionados y anunciaron sus intenciones de proseguir su trabajo en Ethereum Classic.<br><br>Empresas como <a href="http://coinbase.com">Coinbase</a> o <a href="http://uphold.com">Uphold</a> apoyaron a Ethereum y se comprometieron a usar en sus nodos la versión que incluiría la devolución. Al igual que en Bitcoin, en Ethereum tenía que haber un consenso de la fuerza de cómputo suficiente para lograr un hard fork y dividir la cadena en dos. Se produjo el hard fork, en ese momento los nodos que no actualizaron su versión Ethereum pasaron a Classic.<br><br><img class="alignnone size-full wp-image-529" src="https://files.adrianistan.eu/Etc.png" alt="Etc" width="500" height="500" /><br><br>Ahora ambas plataformas conviven, con cadenas de bloques separadas. En Ethereum Classic prefieren código irreversible, resistente a censuras, con los ideales de que Ethereum es ese ordenador que nunca se apaga y que siempre ejecuta tus contratos. Se calcula que un 22% de los usuarios de Ethereum apoyan las pretensiones de Classic y que lo ocurrido con la DAO sienta un terrible precedente que podría desembocar en censura.<br><br>¿Cuál triunfará? Posiblemente ambas cadenas se mantengan, pero una de las dos tendrá que ser la mayoritaria. Ethereum tiene a su favor que la fundación Ethereum va a seguir programando tal y como tenía planeado, con nuevas actualizaciones que podrían beneficiar al ecosistema. Por otra parte el cambio de un algoritmo PoW a un algoritmo PoS que quiere realizar Ethereum podría quitarle las ganas a ciertos mineros. Estos se trasladarían a Classic, aunque aquí la opinión es que sería algo temporal ya que mantener el algoritmo PoW a Classic le podría pasar factura al largo plazo. Classic tiene a su favor su reputación de realmente inmutable, ajena a cualquier situación, una libertad anárquica, algo de lo que Ethereum ya no puede presumir.<br><br>¿Cuál es tu opinión al respecto? ¿Cuál fue la decisión correcta, la de Ethereum o la de Ethereum Classic?<br><br>En el siguiente capítulo veremos otro robo, en este caso a Bitcoin, a través de Bitfinex.]]></description>
                <comments>https://blog.adrianistan.eu/la-criptonovela-del-verano-una-historia-tres-capitulos-capitulo-2</comments>
                <pubDate>Sun, 28 Aug 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>La Criptonovela del verano: una historia en tres capítulos (Capítulo 1)</title>
                <link>https://blog.adrianistan.eu/la-criptonovela-del-verano-una-historia-tres-capitulos-capitulo-1</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/la-criptonovela-del-verano-una-historia-tres-capitulos-capitulo-1</guid>
                <description><![CDATA[Este verano el mundo de las criptodivisas nos ha deleitado con tres culebrones. Cada culebrón nos expone fortalezas y debilidades de estos sistemas y lo que es seguro es que forman parte de su historia, para bien o para mal.<br><h2>Capítulo 1: Bitcoin y su escalabilidad, tamaño del bloque</h2><br>Esto es una auténtica guerra. Bandos muy definidos, hostilidad, el debate comenzó hace ya tiempo (abril de 2015, puede que antes) pero sigue sin finalizar. ¿Debería Bitcoin aumentar el tamaño de los bloques? Y más inquietante, ¿cómo debería hacerse?<br><br><img class="alignnone size-full wp-image-516" src="https://files.adrianistan.eu/Guerra.jpg" alt="Guerra" width="1000" height="563" /><br><br>El protocolo actual de Bitcoin permite ejecutar un máximo de 7 transacciones por segundo (3 según otras fuentes). Este número era suficiente cuando Bitcoin surgió y solo lo usaban cuatro gatos pero ahora empieza a haber problemas que se agravarían aún más si se consigue popularizar Bitcoin. Por ello, para aumentar el número de transacciones simultáneas se hace preciso aumentar el tamaño del bloque (actualmente 1 MB) que contiene las transacciones pendientes de verificar.<br><br><img class="alignnone size-large wp-image-517" src="https://files.adrianistan.eu/TamañoDelBloque-1024x494.png" alt="TamañoDelBloque" width="840" height="405" /><br><br>El límite en el tamaño de los bloques fue una solución temporal creada por <strong>Satoshi Nakamoto</strong> hasta que se pudieran usar clientes ligeros, sin embargo nunca se ha modificado.<br><br>Esta cuestión no ha contado con el consenso habitual que se lograba para implementar otras características. Hay intereses en ambas direcciones. <a href="http://gavinandresen.ninja">Gavin Andresen</a> y Mike Hearn publicaron ya a finales de 2015 un fork, denominado <strong>Bitcoin XT</strong>, que implementaba la proposición <strong>BIP 101</strong>. Además añadía algunas mejoras de seguridad y usabilidad. El objetivo era lograr que el 11 de enero de 2016, al menos el 75% de los nodos de la red Bitcoin funcionasen con Bitcoin XT. Si lo conseguían, conseguirían imponer sus normas y Bitcoin Core (el Bitcoin original) se bifurcaría (un <em>hard fork</em>). Los mineros de cada red seguirían minando pero ya no funcionarían en la misma red, habría dos.<br><br><img class="size-large wp-image-518" src="https://files.adrianistan.eu/Gavin-Andresen-summit-1024x682.jpg" alt="Gavin Andresen, fue el designado por Satoshi Nakamoto para desarrollar Bitcoin" width="840" height="559" /> Gavin Andresen, fue el designado por Satoshi Nakamoto para desarrollar Bitcoin<br><br>Sin embargo esto generó mucha crítica en la comunidad. Por una parte tener dos cadenas de bloques podría suponer un peligro de doble gasto y la credibilidad de Bitcoin se vería afectada por haber "dos bitcoines". Otra crítica tuvo que ver con la privacidad. Bitcoin XT recogía las IP, también de usuarios que usaban Tor. Las críticas llegan al modelo de gobernación de Bitcoin pues en última estancia Bitcoin XT proponía cambiar la manera de tomar decisiones.<br><br><img class="size-full wp-image-519" src="https://files.adrianistan.eu/BitcoinXT.jpg" alt="Bitcoin XT guardaba las IP de los usuarios rompiendo con la privacidad característica de Bitcoin" width="725" height="483" /> Bitcoin XT guardaba las IP de los usuarios rompiendo con la privacidad característica de Bitcoin<br><br>Más tarde, Satoshi Nakamoto (o alguien que se hacía pasar por él) afirmaba que él veía la necesidad de cambios en Bitcoin Core, pero que Bitcoin XT le parecía peligroso. Finalmente Bitcoin XT no logró sus objetivos.<br><br>En este punto vamos a revisar los motivos que exponen aquellos que no quieren aumentar el tamaño del bloque.<br><ul><br> 	<li><em>Las tarifas de transacción son muy bajas y no subirán hasta que el espacio en el bloque sea escaso. Necesitamos un límite de tamaño por bloque para asegurar la escasez y por lo tanto ponerle un precio a las transacciones.</em></li><br> 	<li><em>¿Qué pasaría si el mercado fijase un tamaño de bloque tan grande que sólo Google se pudiera permitir mantener funcionando nodos completos?</em></li><br> 	<li><em>¿Y si las tarifas de transacción fijadas por el mercado no pagan lo suficiente como para mantener un poder computacional que proteja a la red de otros adversarios económicamente fuertes?</em></li><br> 	<li><em>¿Y si la competencia resulta en que la rentabilidad del minado es tan baja que saca del juego a los pools más pequeños y la minería Bitcoin acaba siendo un monopolio?</em></li><br></ul><br><em><strong>Argumentos tomados de <a href="http://elbitcoin.org/bloques-el-tamano-si-importa/">ElBitcoin.org</a></strong></em><br><br>Estos argumentos son muy parecidos entre sí y tienen que ver con la minería. Las objeciones al límite del tamaño del bloque tienen que ver con las recompensas por cómputo. Si el tamaño del bloque aumenta, los mineros van a tener que realizar más trabajo para recibir la recompensa por bloque minado (25 BTC). La otra recompensa, basada en las comisiones por transacción sigue siendo tan baja que sigue siendo preferible minar para conseguir un bloque entero. Los mineros van a preferir bloques pequeños. Además se expone la posibilidad de que los bloques sean tan grandes que Bitcoin acabe centralizándose. Esto último de hecho es algo que ya ocurre. Los grandes nodos chinos copan gran parte del mercado.<br><br>Sigamos con la historia. En febrero de 2016 una encuesta a usuarios de Bitcoin revelaba que el 90% de ellos querían aumentar el tamaño del bloque hasta por lo menos 2MB. Entonces aparece <strong>Bitcoin Classic</strong>. Bitcoin Classic, también diseñado por Gavin Andresen, propone aumentar el tamaño de bloque a 2MB. No realiza ningún cambio más sobre Bitcoin Core. El anuncio de este hard fork fue eliminado de Reddit, lo que hizo pensar a muchos que cierta parte del mundo Bitcoin censuraba a Classic.<br><br>Classic obtuvo el apoyo de mucha más gente en mucho menos tiempo. Empresas como <a href="http://coinbase.com">Coinbase</a>, <a href="http://bitcoin.com">Bitcoin.com</a>, <a href="http://xapo.com">Xapo</a>, <a href="http://blockchain.info">Blockchain.info</a>,... apoyaron Classic desde el principio, algo que evidenciaba el alejamiento de los desarrolladores de Core con parte de la comunidad. Para que Classic se imponga tiene que cumplirse la misma condición que con Bitcoin XT, 75% de la potencia de la red ha de funcionar usando Classic.<br><br><img class="alignnone size-full wp-image-523" src="https://files.adrianistan.eu/BitcoinClassic.jpg" alt="BitcoinClassic" width="990" height="550" /><br><br>Bitcoin Core no ha sido ajena y ha propuesto sus soft forks, pequeñas modificaciones, más conservadoras, que no eliminan la compatibilidad, proponiendo un modelo de "testigos segregados", que no aumenta el tamaño del bloque sino que reduce la información de cada transacción de manera que entran más transacciones en un bloque del mismo tamaño. La postura de Core fue apoyada por OneName, GreenAddress,...<br><br>Los testigos segregados han sido criticados ya que según parte de la comunidad solo resolverían el problema a corto plazo. Esta solución no obstante parece ser también del agrado de Classic, que añadiría ambas cosas: testigos segregados y aumento del tamaño de bloque. Este aumento primero sería fijo, subiendo a 2MB, posteriormente se trabajaría según la propuesta de Stephen Pair, CEO de <a href="http://bitpay.com">BitPay</a>, en un modelo de crecimiento dinámico. Sin embargo BitPay ya se ha adelantado y actualmente están desarrollando un fork de Bitcoin Core según sus propuestas, esta versión es <strong>BitPay Core</strong> y es experimental.<br><br>La cosa se complica más pues tenemos <strong>Bitcoin Unlimited</strong>. Se trata de un sistema donde cada nodo especificaría el tamaño de bloque que le gustase. Concretamente empezaría en un 1MB y el tamaño podría verse aumentado según el minero. Este proyecto sin embargo no ha atraído la atención suficiente y aunque sigue en activo, no cuenta con un apoyo suficiente.<br><br><img class="alignnone size-full wp-image-520" src="https://files.adrianistan.eu/Blockstream.png" alt="Blockstream" width="646" height="224" /><br><br>Ya en 2015 entra en juego también <strong>Blockstream</strong>, una compañía dedicada a sidechains o cadenas laterales (una cadena lateral es un producto paralelo pero que toma referencia en Bitcoin, similar a las monedas de distintos países con un patrón oro). Aprovechan la potencia de Bitcoin con otros propósitos. Su producto principal es <strong>Liquid</strong>. Las transacciones en Liquid no son procesadas por los mineros. Para usar la red lateral Liquid es necesario pagar a Blockstream una cuota mensual. Esto no sería ningún problema si no fuera porque muchos desarrolladores de Bitcoin Core están en nómina de Blockstream. Una parte de la comunidad les acusa de querer centralizar Bitcoin y para ello llevarán Bitcoin al colapso, con el tamaño de bloque sin modificar para que la gente use soluciones mejores, en este caso las de Blockstream. Si la gente se queda sin espacio en la cadena pública deberán pasarse a la cadena de Blockstream, que si tendrá espacio para tus transacciones.<br><br><img class="alignnone size-full wp-image-521" src="https://files.adrianistan.eu/Sidechain.png" alt="Sidechain" width="1000" height="540" /><br><br>La desconfianza en Blockstream creció cuando firmó contratos con mineros chinos para poder sustentar Core y las cadenas laterales derivadas de él, productos de Blockstream. Estos contratos han hecho sospechar a mucha gente, que empezó a demonizar contra ellos. Estos mensajes han sido borrados sistemáticamente de ciertos foros y subreddits que se creen en control de Blockstream. Así pues Blockstream parece querer controlar Bitcoin junto con la opinión pública.<br><br>Algunos afirman que Blockstream no durará mucho, que caerá, que Classic triunfará. Otros afirman que el resto de desarrolladores de Core no dejarán que Blockstream se oponga a la modifiación del tamaño de bloque y que Core evolucionará.<br><br><img class="alignnone size-full wp-image-522" src="https://files.adrianistan.eu/Bitcache.jpg" alt="Bitcache" width="875" height="493" /><br><br>Y ahora... <strong>Bitcache</strong>. El nuevo proyecto de <strong>Kim Dotcom</strong> (fundador de Megaupload). Todavía no ha visto la luz pero este proyecto cuenta con la aprobación de <a href="https://bnktothefuture.com/">BankToTheFuture</a>. Se trataría de un sistema que relacionaría archivos con transacciones Bitcoin y parece ser que resolvería los problemas de escalabilidad. Su salida se ha planeado para enero de 2017. Estaré atento.<br><br>Y en los siguientes capítulos<br><ul><br> 	<li>Ethereum y la DAO</li><br> 	<li>El robo de Bitfinex</li><br></ul><br>Suscríbete por correo electrónico para no perderte los dos episodios que vienen.]]></description>
                <comments>https://blog.adrianistan.eu/la-criptonovela-del-verano-una-historia-tres-capitulos-capitulo-1</comments>
                <pubDate>Sat, 27 Aug 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Programando para Haiku - Barras de menús - Parte III</title>
                <link>https://blog.adrianistan.eu/programando-haiku-barras-menus-parte-iii</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/programando-haiku-barras-menus-parte-iii</guid>
                <description><![CDATA[Seguimos con los <a href="https://blog.adrianistan.eu/2016/05/18/programando-haiku-bapplication-bwindow-bbutton-parte-i/">tutoriales de Haiku</a>, hoy veremos como crear barras de menú. Partimos del código de la ventana vacía, lo tienes en la foto junto con el comando de compilación.<br><br><a href="https://files.adrianistan.eu/MiAplicacionHaikuCodigo.png" target="_blank"><img class="wp-image-502 size-large" src="https://files.adrianistan.eu/MiAplicacionHaikuCodigo-1024x768.png" alt="MiAplicacionHaikuCodigo" width="840" height="630" /></a> Haz click en la imagen para verlo en grande<br><br>Debemos saber que en la API de BeOS hay tres clases que nos van a servir.<br><ul><br> 	<li>BMenuBar, se trata de la barra en sí, que esta pegada a la ventana. Un BMenuBar contiene BMenus.</li><br> 	<li>BMenu, son los menús en sí. Un menú es un elemento que contiene acciones, organizadas como si fuera una lista. Los menús no definen acciones, pero sí su organización. Así, a un BMenu le tendremos que añadir BMenuItems u otro BMenus (menús anidados).</li><br> 	<li>BMenuItem, son las acciones de los menús. Cada BMenuItem tiene la capacidad de generar un BMessage nuevo, listo para ser procesado por nuestra aplicación.</li><br></ul><br>En la imagen se puede ver perfectamente<br><br><img class="alignnone size-full wp-image-503" src="https://files.adrianistan.eu/BMenu.png" alt="BMenu" width="632" height="386" /><br><br>Vamos a crear una barra de menú con dos menús, uno de ellos simple y otro con un menú anidado y un separador.<br><br><pre class="lang:c++ decode:true"><br>#include &lt;AppKit.h&gt;<br>#include &lt;InterfaceKit.h&gt;<br>#include &lt;InterfaceDefs.h&gt;<br><br>#define NEW_FILE 3<br>#define EXPORT_AS 5<br><br>class VentanaPrueba : public BWindow{<br>	public:<br>		VentanaPrueba() : BWindow(BRect(100,100,900,700),&quot;Mi ventana&quot;,B_TITLED_WINDOW,0){<br>			AddChild(CreateMenuBar());<br>		}<br>		bool QuitRequested(){<br>			be_app_messenger.SendMessage(B_QUIT_REQUESTED);<br>			return BWindow::QuitRequested();<br>		}<br>		void MessageReceived(BMessage* msg){<br>			switch(msg-&gt;what){<br>				default:<br>					BWindow::MessageReceived(msg);<br>			}<br>		}<br>		BMenuBar* CreateMenuBar(){<br>			BMenuBar* bar = new BMenuBar(BRect(0,0,100,20),&quot;MenuBar&quot;);<br>			<br>			BMenu* file = new BMenu(&quot;File&quot;);<br>			BMenu* help = new BMenu(&quot;Help&quot;);<br>			BMenu* exportMenu = new BMenu(&quot;Export&quot;);<br>			<br>			bar-&gt;AddItem(file);<br>			bar-&gt;AddItem(help);<br>			<br>			/* FILE */<br>			BMenuItem* newFile = new BMenuItem(&quot;New file&quot;,new BMessage(NEW_FILE));<br>			newFile-&gt;SetShortcut('N',B_COMMAND_KEY);<br>			file-&gt;AddItem(newFile);<br>			<br>			file-&gt;AddItem(exportMenu);<br>			file-&gt;AddSeparatorItem();<br>			<br>			BMenuItem* quit = new BMenuItem(&quot;Quit&quot;,new BMessage(B_QUIT_REQUESTED));<br>			quit-&gt;SetShortcut('Q',B_COMMAND_KEY);<br>			file-&gt;AddItem(quit);<br>			<br>			/* EXPORT */<br>			BMenuItem* exportAs = new BMenuItem(&quot;Export as...&quot;,new BMessage(EXPORT_AS));<br>			exportMenu-&gt;AddItem(exportAs);<br>			<br>			/* HELP */<br>			BMenuItem* helpVersion = new BMenuItem(&quot;Help&quot;,NULL);<br>			help-&gt;AddItem(helpVersion);<br>			<br>			return bar;<br>		}<br>};<br><br>class AplicacionPrueba : public BApplication{<br>	public:<br>			VentanaPrueba* ventana;<br>			AplicacionPrueba() : BApplication(&quot;application/x-aplicacion-prueba&quot;){<br>				ventana = new VentanaPrueba();<br>				ventana-&gt;Show();	<br>			}<br>};<br><br>int main(int argc, char** argv){<br>	AplicacionPrueba app;<br>	return app.Run();<br>}<br></pre><br><br>Y el resultado es el siguiente:<br><br><img class="alignnone size-full wp-image-505" src="https://files.adrianistan.eu/BMenuBar.png" alt="BMenuBar" width="396" height="225" /><br><br>Como vemos, <em>SetShortcut</em> hace que los menús sean seleccionables con combinaciones de teclado. Hay que tener en cuenta que en BeOS, la tecla que en Windows normalmente Ctrl, es Alt. Así operaciones como copiar y pegar con Alt+C y Alt+V. Para responder al evento solo hace falta escuchar en la función <em>MessageReceived</em>. En el caso de B_QUIT_REQUESTED, el mensaje ya está implementado en la función <em>QuitRequested</em>.]]></description>
                <comments>https://blog.adrianistan.eu/programando-haiku-barras-menus-parte-iii</comments>
                <pubDate>Wed, 17 Aug 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Beta privada de TucTum, la red social que te paga</title>
                <link>https://blog.adrianistan.eu/beta-privada-tuctum-ga-la-red-social-te-paga</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/beta-privada-tuctum-ga-la-red-social-te-paga</guid>
                <description><![CDATA[<blockquote>Este post se conserva por motivos históricos, sin embargo TucTum fue cerrado dado su poco éxito</blockquote><br>Hace poco me enteré de que Tsu, la red social que pagaba a sus usuarios una parte de lo que generasen por publicidad, había cerrado. Me lo comentó un amigo, había intentado acceder desde la app de Android y ya no podía. Efectivamente, Tsu había cerrado. Llevaba acumulados unos cuantos euros (6 o 7 euros) y me sentó mal, porque además no han explicado los motivos del cierre. Había que hacer algo.<br><br><img class="alignnone size-full wp-image-493" src="https://files.adrianistan.eu/TucTum.png" alt="TucTum" width="597" height="162" /><br><br>Me gustaría inventar algo como Tsu, que permitiese a la gente compartir y hacer amigos mientran ganan dinero. Pero solo soy una persona, hacer una red social desde cero sería demasiado complicado. Entonces recordé GNU Social, una red social que suelo usar y cuyo código fuente está disponible. GNU Social está hecho en PHP, pero además tiene una completa API de plugins, por lo que no he tocado el código central de la red social, solo he diseñado un plugin que permite a los usuarios ganar dinero.<br><br><img class="alignnone size-large wp-image-494" src="https://files.adrianistan.eu/TucTumHome-1024x499.png" alt="TucTumHome" width="840" height="409" /><br><br>Se paga por cada favorito que recibas (obviamente la multicuenta estará baneada), cuando tengas 1.000.000 de satoshis puedes recibir el pago en Bitcoin. La cantidad de satoshis que da TucTum por cada favorito es algo que quiero estudiar. Por una parte quiero dar lo máximo posible pero que me permita mantener el servidor. En TucTum tampoco se pueden subir imágenes. Esto es algo temporal, si veo que TucTum despega habilitaré la subida de archivos multimedia. De momento recomiendo usar Imgur, del mismo modo que se hace en Reddit.<br><br>Otra característica es el acortamiento de enlaces usando <a href="https://blog.adrianistan.eu/2016/08/04/shortkin-gq/">ShortKin.gq</a>, también de mi propiedad.<br><h2>Beta privada</h2><br>TucTum entra en Beta privada, limitada por invitaciones. Voy a repartir 100 invitaciones entre la gente de este blog para registrarse en TucTum e intentar ganar dinero. Estas 100 personas podrán invitar a quien quieran a la red social, aunque aviso que todavía no hay sistema de referidos, por lo que no habrá reparto de beneficios por invitar a alguien.<br><br>Para pedir tu invitación puedes hacer lo siguiente:<br><ul><br> 	<li><a href="http://eepurl.com/b6NLlL">Suscribirte a la lista de correo</a></li><br> 	<li><a href="http://telegram.me/adrianistan">Suscribirte al canal de Telegram</a></li><br> 	<li>Dejar un comentario en esta entrada</li><br></ul><br>No hace falta que hagas las tres cosas, con que hagas una ya vale. Mientras uséis TucTum es interesante ir comunicándome vuestras sensaciones, que podría mejorar, que esta bien, que esta rematadamente mal,etc]]></description>
                <comments>https://blog.adrianistan.eu/beta-privada-tuctum-ga-la-red-social-te-paga</comments>
                <pubDate>Mon, 15 Aug 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>ShortKin.gq, un acortador de enlaces anónimo</title>
                <link>https://blog.adrianistan.eu/shortkin-gq</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/shortkin-gq</guid>
                <description><![CDATA[<a href="http://shortkin.gq/">ShortKin.gq</a> es un proyecto en el que he trabajado solamente dos días, sin embargo, creo que ya puede ser de utilidad. Se trata de un acortador de enlaces, gratuito y anónimo. Los enlaces durán para siempre pero nadie sabe quien los creó. El servicio no dispone de anuncios, por lo que puedes estar tranquilo y usarlo donde te apetezca, no será baneado de foros y redes sociales. Actualmente usa Heroku<br><br><img class="alignnone size-large wp-image-474" src="https://files.adrianistan.eu/ShortKing-1024x493.png" alt="ShortKing" width="840" height="404" /><br><h2>Bonus: API pública</h2><br>Puedes usar ShortKin.gq en tu propio proyecto a través de una API muy sencilla. Simplemente has de llamar a:<br><pre>http://shortkin.gq/s?url=URL</pre><br>Y se te devolverá el enlace acortado. Muy fácil de usar en tus aplicaciones. Si tus aplicaciones usan Node.js recuerda que puedes usar unos <a href="https://blog.adrianistan.eu/2015/10/31/acortar-enlaces-node-js/">paquetes de acortadores</a> que programé para que todo fuese más sencillo.]]></description>
                <comments>https://blog.adrianistan.eu/shortkin-gq</comments>
                <pubDate>Thu, 04 Aug 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Subir APK a Google Play automáticamente</title>
                <link>https://blog.adrianistan.eu/subir-apk-google-play-automaticamente</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/subir-apk-google-play-automaticamente</guid>
                <description><![CDATA[Hoy vamos a ver como subir un APK a Google Play directamente, usando la línea de comandos, sin pasar por el navegador. Perfecto para automatizar la publicación de actualizaciones.<br><br><img class="alignnone size-full wp-image-466" src="https://files.adrianistan.eu/desarrollador-android-680x394.jpg" alt="desarrollador-android-680x394" width="680" height="394" /><br><br>&nbsp;<br><br>Google Play, como muchos otros servicios de Google, dispone de una API. Gracias a ella podemos interactuar con los servicios de Google directamente desde nuestro código. Para ello necesitaremos un Client ID, un Client Secret y un refresh_token. En mi entrada sobre <a href="https://blog.adrianistan.eu/2016/07/26/crear-credenciales-oauth2-para-google/">como crear credenciales OAuth2 para Google</a> sabras como hacerlo. Busca la API de Google Play Android Developer API y actívala. Anota el client ID, el client secret y el refresh_token. Además es necesario que entres a la <a href="https://play.google.com/apps/publish/">consola de desarrollo de Google Play</a> y autorices tus credenciales en Configuración -&gt; Acceso a API<br><br><img class="alignnone size-large wp-image-465" src="https://files.adrianistan.eu/screenshot-play-google-com-2016-07-26-17-23-56-1024x499.png" alt="screenshot-play google com 2016-07-26 17-23-56" width="840" height="409" /><br><br>Ahora podemos pasar a escribir nuestro script de subida de APK. Para ello usaremos Node.js y el fantástico paquete <strong><a href="https://www.npmjs.com/package/googleapis">googleapis</a></strong>, que se encuentra en npm.<br><h2>Iniciar sesión en googleapis</h2><br>Primero instala el paquete con npm.<br><pre>npm install googleapis</pre><br>Teniendo a mano el client ID, el client secret y el refresh_token podemos iniciar sesión.<br><br><pre class="lang:js decode:true"><br>var CLIENT_ID=&quot;&quot;;<br>var CLIENT_SECRET=&quot;&quot;;<br>var REFRESH_TOKEN=&quot;&quot;;<br><br>var fs = require(&quot;fs&quot;);<br>var google = require(&quot;googleapis&quot;);<br>var androidpublisher = google.androidpublisher(&quot;v2&quot;); // Esta es la API que vamos a usar, AndroidPublisher v2<br>var OAuth2 = google.auth.OAuth2;<br><br>var auth = new OAuth2(CLIENT_ID,CLIENT_SECRET,&quot;&quot;);<br>auth.setCredentials({<br>	refresh_token: REFRESH_TOKEN<br>});<br><br>auth.refreshAccessToken(function(err,tokens){<br>	google.options({auth: auth});<br>	// Ya estamos dentro<br>});<br></pre><br><br><h2>Crear y publicar una edición</h2><br>¿Cuál es el procedimiento para subir un APK a Google Play usando la API?<br><ol><br> 	<li>Crear una nueva <em>edición</em></li><br> 	<li>Subir un APK a esa <em>edición</em></li><br> 	<li>Asignar el APK a algún canal de distribución</li><br> 	<li>Guardar y publicar los cambios de la <em>edición</em></li><br></ol><br>Cuando creemos una edición se nos dará un ID de edición. Lo usaremos en el resto de llamadas. También cuando subamos un APK nos darán un versionCode que usaremos para asignar a uno de los canales de distribución. Google Play dispone de 4 canales de distribución:<br><ul><br> 	<li>alpha: Solo para los usuarios que hayas autorizado explícitamente</li><br> 	<li>beta: Solo para los usuarios que hayas autorizado explícitamente</li><br> 	<li>production: La versión que tienen todos los usuarios que no están registrados en alpha y beta.</li><br> 	<li>rollout: Similar a production. Se indica un procentaje y Google se encargará de que solo el porcentaje de usuarios indicado tengan la actualización. Es ideal para probar nuevas características sin involucrar a todos tus usuarios.</li><br></ul><br>Finalmente el código final del script queda así.<br><br><pre class="lang:js decode:true"><br>var CLIENT_ID=&quot;&quot;;<br>var CLIENT_SECRET=&quot;&quot;;<br>var REFRESH_TOKEN=&quot;&quot;;<br><br>var fs = require(&quot;fs&quot;);<br>var google = require(&quot;googleapis&quot;);<br>var androidpublisher = google.androidpublisher(&quot;v2&quot;);<br>var OAuth2 = google.auth.OAuth2;<br><br>var auth = new OAuth2(CLIENT_ID,CLIENT_SECRET,&quot;&quot;);<br>auth.setCredentials({<br>	refresh_token: REFRESH_TOKEN<br>});<br><br>auth.refreshAccessToken(function(err,tokens){<br>	google.options({auth: auth});<br>	androidpublisher.edits.insert({<br>		packageName: &quot;ga.yayeyo.cronoquiz.historia&quot;<br>	},function(err,edit){<br>		console.dir(err);<br>		androidpublisher.edits.apks.upload({<br>			packageName: &quot;ga.yayeyo.cronoquiz.historia&quot;,<br>			editId: edit.id,<br>			uploadType: &quot;media&quot;,<br>			media: {<br>				mediaType: &quot;application/vnd.android.package-archive&quot;,<br>				body: fs.createReadStream(&quot;platforms/android/build/outputs/apk/android-release.apk&quot;)<br>			}<br>		},function(err,req){<br>			console.dir(err);<br>			var versionCode = req.versionCode;<br>			androidpublisher.edits.tracks.update({<br>				packageName: &quot;ga.yayeyo.cronoquiz.historia&quot;,<br>				editId: edit.id,<br>				track: &quot;production&quot;,<br>				resource: {<br>					versionCodes: [versionCode]<br>				}<br>			},function(err,req){<br>				console.dir(err);<br>				androidpublisher.edits.commit({<br>					packageName: &quot;ga.yayeyo.cronoquiz.historia&quot;,<br>					editId: edit.id<br>				},function(err,req){<br>					console.dir(err);<br>				});<br>			});<br>		});<br>	});<br>});<br></pre><br>]]></description>
                <comments>https://blog.adrianistan.eu/subir-apk-google-play-automaticamente</comments>
                <pubDate>Tue, 26 Jul 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Crear credenciales OAuth2 para Google</title>
                <link>https://blog.adrianistan.eu/crear-credenciales-oauth2-para-google</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/crear-credenciales-oauth2-para-google</guid>
                <description><![CDATA[Google dispone de muchas APIs a nuestra disposición de forma gratuita. Para poder usarla es necesario que hayamos iniciado sesión. Desde un navegador como Firefox o Chrome es muy sencillo, introducimos nuestro usuario y contraseña y ya podemos acceder a Gmail, YouTube, etc. Si queremos hacerlo desde nuestro propio programa también es posible, pero el procedimiento es distinto. Una vez hagamos esto podremos hacer lo mismo que haría un usuario, lo único que programándolo (leer los contactos, leer el correo, modificar suscripciones de YouTube, publicar en Blogger, etc).<br><h2>Crea un proyecto de API</h2><br>Crea un proyecto de Google y crea unos credenciales nuevos en la <a href="https://console.developers.google.com/apis/">consola de APIs de Google.</a><br><br><img class="alignnone size-large wp-image-460" src="https://files.adrianistan.eu/screenshot-console-developers-google-com-2016-07-26-14-52-43-1024x493.png" alt="screenshot-console developers google com 2016-07-26 14-52-43" width="840" height="404" /><br><br>Los credenciales pueden ser de varios tipos: clave de API, ID de OAuth y cuenta de servicio. La clave de API solo funciona en algunas API, el ID de OAuth es el sistema universal y la cuenta de servicio es para APIs de desarrollo. Creamos un ID de OAuth, pues es el método más universal.<br><br><img class="alignnone size-large wp-image-461" src="https://files.adrianistan.eu/screenshot-console-developers-google-com-2016-07-26-16-29-16-1024x493.png" alt="screenshot-console developers google com 2016-07-26 16-29-16" width="840" height="404" /><br><br>Dependiendo de dónde opere nuestra aplicación tendremos un método u otro. El método Web requiere disponer de un servidor web y es el sistema óptimo si tenemos una aplicación Web (por ejemplo, tenemos una red social y queremos una opción de leer contactos de Gmail, usaríamos Web, pues es el procedimiento más cómodo). Si tu programa no tiene un servidor web detrás (es un programa normal de Windows, una herramienta de línea de comandos, un plugin de Firefox,...) usa Otro. Voy a explicar como funciona Otro.<br><br><img class="alignnone size-large wp-image-462" src="https://files.adrianistan.eu/screenshot-console-developers-google-com-2016-07-26-16-30-31-1024x493.png" alt="screenshot-console developers google com 2016-07-26 16-30-31" width="840" height="404" /><br><br>Obtendremos un ID de cliente y un secreto de cliente. Guardamos esas dos claves, son muy importantes. Ahora vamos a activar las APIs. A la izquierda accedemos a Biblioteca. Buscamos la APIs que queramos usar y las activamos.<br><br><img class="alignnone size-large wp-image-463" src="https://files.adrianistan.eu/screenshot-console-developers-google-com-2016-07-26-16-36-27-1024x493.png" alt="screenshot-console developers google com 2016-07-26 16-36-27" width="840" height="404" /><br><h2>Obteniendo un access_token y un refresh_token</h2><br>Para poder llamar a la API de Google es necesario tener un access_token. Los access_token caducan a las pocas horas. Para poder seguir realizando operaciones en la API pasado ese tiempo necesitamos un refresh_token. El refresh_token sirve exclusivamente para pedir un access_token nuevo. Este no caduca, pero no garantiza que siempre nos devuelva un access_token pues puede que el usuario haya decidido eliminar el permiso a nuestro programa.<br><br>El procedimiento de OAuth2 es el siguiente:<br><ol><br> 	<li>Nuestra aplicación quiere autenticar al usuario en Google</li><br> 	<li>Nuestra aplicación genera una URL especial que debe abrir el usuario en el navegador</li><br> 	<li>En esa URL inicia sesión en Google el usuario, igual que si fuera a iniciar sesión en Gmail</li><br> 	<li>Se le informa al usuario de los permisos/APIs que nuestro programa quiere usar</li><br> 	<li>El usuario acepta o rechaza</li><br> 	<li>Si acepta ocurren varias cosas dependiendo del método. Si elegiste Otro se mostrará un código que deberás copiar en la aplicación.</li><br> 	<li>Ese código la aplicación lo envía a Google junto a nuestros datos de client ID y client secret.</li><br> 	<li>Google nos envía el access_token y el refresh_token.</li><br></ol><br><h2>Elaborar la URL de inicio de sesión</h2><br>En la URL de inicio de sesión figuran varios datos.<br><ul><br> 	<li>El tipo de petición</li><br> 	<li>Los permisos o APIs a las que queremos acceder (scopes)</li><br> 	<li>Nuestro ID de cliente</li><br> 	<li>Y la URL de redirección, que en nuestro caso, es especial.</li><br></ul><br>En este ejemplo vemos una petición, con dos APIs o permisos, Chrome WebStore y Google Play.<br><pre>https://accounts.google.com/o/oauth2/auth?response_type=code&amp;scope=https://www.googleapis.com/auth/chromewebstore+https://www.googleapis.com/auth/androidpublisher&amp;client_id=AQUI EL ID DE CLIENTE&amp;redirect_uri=urn:ietf:wg:oauth:2.0:oob</pre><br><h2>Validar el código</h2><br>El usuario nos dará el código que le haya dado Google. Nosotros ahora para obtener el access_token y el refresh_token tenemos que enviarlo de vuelta. En Curl el comando sería el siguiente:<br><pre>curl "https://accounts.google.com/o/oauth2/tken" -d "client_id=AQUI EL ID DE CLIENTE&amp;client_secret=AQUI EL CLIENT SECRET&amp;code=ESTE ES EL CÓDIGO QUE NOS DA EL USUARIO&amp;grant_type=authorization_code&amp;redirect_uri=urn:ietf:wg:oauth:2.0:oob"</pre><br>La respuesta, en un fichero JSON, contendrá el access_token y el refresh_token. En las librerías de APIs de Google muchas veces basta con especificar el client ID, el client secret y el refresh_token, pues se encargan de pedir automáticamente el access_token.]]></description>
                <comments>https://blog.adrianistan.eu/crear-credenciales-oauth2-para-google</comments>
                <pubDate>Tue, 26 Jul 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Tutorial de GnuCash en castellano</title>
                <link>https://blog.adrianistan.eu/tutorial-gnu-cash</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/tutorial-gnu-cash</guid>
                <description><![CDATA[<a href="http://gnucash.org/">GnuCash</a> es un programa de contabilidad y finanzas para familias y PYMES, software libre perteneciente al proyecto GNOME. Es un programa muy potente, por desgracia su uso puede no ser muy intuitivo.<br><br><img class="alignnone size-full wp-image-441" src="https://files.adrianistan.eu/GnuCash.png" alt="GnuCash" width="507" height="385" /><br><h2>Creando un libro</h2><br>Todo absolutamente todo se guarda en un único archivo, que puede ser de varios tipos (XML, sqlite3, MySQL, PostgreSQL). Cuando iniciemos GnuCash veremos este asistente que nos ayudará a crear un libro.<br><br><img class="alignnone size-large wp-image-435" src="https://files.adrianistan.eu/NuevaCuenta-1024x576.png" alt="NuevaCuenta" width="840" height="473" /><br><br>Lo primero que deberemos elegir será la moneda principal. Lo habitual es que elijáis la moneda oficial de vuestro país, en mi caso, el Euro.<br><br><img class="alignnone size-large wp-image-436" src="https://files.adrianistan.eu/SeleccionarMoneda-1024x576.png" alt="SeleccionarMoneda" width="840" height="473" /><br><br>Ahora aparecerán las opciones del libro de contabilidad. Estas opciones normalmente solo se aplican a empresas y están destinadas a generar facturas o hacer presupuestos. La única opción que quizá os interese es la de <em>Trading accounts</em> que es interesante si vais a manejar cuentas en varias monedas. Ahora tenemos que seleccionar una plantilla para tener cuentas ya generadas. Esto puede ser algo confuso, ya que es obligatorio seleccionar al menos una plantilla. Yo no recomiendo ninguna y prefiero empezar de cero, para eso selecciona la plantilla más básica que hay <em>Childcare expenses</em> y continúa hasta que estes ya en la vista principal de GnuCash.<br><br><img class="alignnone size-large wp-image-437" src="https://files.adrianistan.eu/Plantillas-1024x576.png" alt="Plantillas" width="840" height="473" /><br><br><img class="alignnone size-large wp-image-438" src="https://files.adrianistan.eu/VistaPrincipalGnuCash-1024x547.png" alt="VistaPrincipalGnuCash" width="840" height="449" /><br><br>A continuación vamos a dejar el libro vacío. Pulsa botón derecho sobre <em>Expenses </em>y luego clic en <em>Delete account...</em> Se te preguntará si quieres mover o borrar, selecciona la opción <em>Delete all subaccounts</em>.<br><h2>Creando cuentas</h2><br>GnuCash usa un sistema de doble entrada. Quiere decir que todos los gastos e ingresos deben ir anotados en dos partes. Por ejemplo, si anotamos en Gastos una comida en un restaurante, tenemos que anotar también el gasto en la cuenta de Metálico, que es de donde provino el dinero. Naturalemente no lo tenemos que hacer manualmente, cuando anotemos un ingreso o gasto en GnuCash este se encargará de anotarlo donde sea oportuno.<br><br>Ahora que tenemos el libro en blanco vamos a ir creando unas cuentas maestras y unas subcuentas. GnuCash dispone de los siguientes tipos de cuentas:<br><ul><br> 	<li><strong>Asset</strong> (Activos)<br><ul><br> 	<li>Son aquellas cosas que tienen valor y pueden usarse para pagar cosas. En esta categoría entra desde el dinero en metálico hasta las propiedades inmobiliarias.</li><br></ul><br></li><br> 	<li><strong>Bank</strong> (Banco)<br><ul><br> 	<li>Este subtipo se usa para anotar los fondos de una cuenta bancaria (que puede tener un interés) y para las tarjetas de débito.</li><br></ul><br></li><br> 	<li><strong>Cash</strong> (Metálico)<br><ul><br> 	<li>Este subtipo de cuenta nos permite anotar el dinero que poseemos en metálico</li><br></ul><br></li><br> 	<li><strong>Credit Card</strong> (Tarjeta de crétido)<br><ul><br> 	<li>Este subtipo de cuenta es específico para tarjetas de crédito</li><br></ul><br></li><br> 	<li><strong>Equity</strong><br><ul><br> 	<li>Este tipo está destinado a ofrecer los saldos de apertura de las cuentas. Como GnuCash es un sistema de partida doble es necesario que el dinero provenga de algún sitio. Normalmente vendrá de <strong>Income</strong> pero cuando abrimos una cuenta en la que ya teníamos dinero antes y queremos que GnuCash la contabilice, añadir el saldo de apertura a ingresos no es práctica recomendada. Así Equity permite tener saldos de apertura en las cuentas que no habían sido contabilizados antes por GnuCash.</li><br></ul><br></li><br> 	<li><strong>Expense</strong> (Gastos)<br><ul><br> 	<li>Aquí se anotan los gastos. Esta cuenta y sus subcuentas son las que más usaremos en el día a día.</li><br></ul><br></li><br> 	<li><strong>Income</strong> (Ingresos)<br><ul><br> 	<li>Aquí se anotan los ingresos. Es la segunda que más usaremos después de <strong>Expense</strong>.</li><br></ul><br></li><br> 	<li><strong>Liability</strong> (Obligaciones)<br><ul><br> 	<li>Aquí podemos anotar deudas que tenemos pendientes de pago.</li><br></ul><br></li><br> 	<li><strong>Mutual Fund</strong> (Fondo de Inversión)<br><ul><br> 	<li>Una cartera de valores de inversión compuesta de <strong>Stocks</strong>.</li><br></ul><br></li><br> 	<li><strong>Stock</strong> (Acción)<br><ul><br> 	<li>Un subtipo de cuenta que representa las acciones que se poseen de una empresa.</li><br></ul><br></li><br></ul><br>¿Son estas todos los tipos de cuenta que permite GnuCash? No, hay algunas más de tipo interno. Sin embargo con estos tipos tendremos suficientes para construir el árbol de cuentas de nuestro libro.<br><br>Creamos una nueva cuenta <em>New account...</em><br><br><img class="alignnone size-full wp-image-439" src="https://files.adrianistan.eu/NuevaCuentaActivos.png" alt="NuevaCuentaActivos" width="517" height="706" /><br><br>Lo primero es decidir el Account Type, cuando crees tu libro yo recomiendo hacer la primera la de Activos, del tipo Asset. ¿Qué nos pregunta el formulario?<br><ul><br> 	<li>Account name. Un nombre para la cuenta.</li><br> 	<li>Account code. Permite asignar un código alfanumérico a la cuenta. Es usado para determinar el orden de las cuentas cuando se generan informes. Además si trabajas con contabilidades más complejas sabrás que es habitual que en muchos sitios incluso pidan los gastos en números de cuenta estandarizados (en la administración por lo menos es así, así ellos pueden combinar todas las cuentas del mismo número y sacar datos globales). Es habitual que las cuentas padre acaben en cero. Es totalmente opcional y si usas GnuCash en casa simplemente deja ese campo vacío.</li><br> 	<li>Description. Una descripción de la cuenta</li><br> 	<li>Security/Currency. Si nuestra cuenta no es Stock o Mutual Fund sirve para indicar la moneda de la cuenta. Si sí que es una cuenta Stock o Mutual Fund sirve para indicar la acción en sí. Cabe destacar que en GnuCash es posible añadir subcuentas dentro de una cuenta cuya moneda sea distinta (una subcuenta en dólares dentro de la cuenta Activos que es en euros). Luego veremos como ajustar la tasa de conversión para los informes. También hay gente que prefiere que todas las cuentas sean de la misma moneda, en ese caso crean Activos en Euros y Activos en Dólares.</li><br> 	<li>Smallest fraction. La fracción más pequeña de la moneda con la que estemos trabajando. En general es habitual dejarlo por defecto, pero este ajuste nos será de utilidad con las criptodivisas.</li><br> 	<li>Account Color. El color de la cuenta. Uso puramente estético.</li><br> 	<li>Notes. Podemos guardar notas. Este campo lo considero de gran utilidad pues me permite guardar los códigos IBAN y BIC dentro de las cuentas bancarias.</li><br> 	<li>Hidden. Si quieres que no se vea. Yo lo quito siempre, pues me gusta ver el árbol completo.</li><br> 	<li>Placeholder. Una cuenta placeholder es específicamente diseñada para tener subcuentas. Quiere decir que no es posible anotar en ellas directamente, solo a través de alguna de sus subcuentas. En el caso de las cuentas principales recomiendo que todas sean placeholder, así siempre especificaremos la subcuenta donde se ha producido el ingreso/gasto.</li><br> 	<li>En la pestaña superior <em>Opening balance </em>podemos indicar el saldo de apertura. Se anotará también en una cuenta de tipo Equity.</li><br></ul><br>Creamos la cuenta y listo. Ahora vamos a crear muchas cuentas hasta que se adapte a nuestra situación. Mis consejos para diseñar el árbol son:<br><ul><br> 	<li>La cuenta Activos puede tener activo circulante o propiedades, sin embargo no recomiendo añadir propiedades, ni otros activos que no sean dinero en sí (obras de arte, etc) salvo casos muy específicos.</li><br> 	<li>Dentro de la cuenta Activos suelo crear otra cuenta para cada banco. Luego dentro de la cuenta de cada banco creo la correspondiente Bank account de las cuentas bancarias.</li><br> 	<li>Hay sistemas de pago (PayPal) y tarjetas (Revolut) que permiten más de una divisa simultánea. En ese caso yo suelo crear subcuentas, una en cada divisa que permita y de la que preveo tener fondos. Por ejemplo, en PayPal creo la cuenta de tipo Asset placeholder con dos subcuentas, PayPal EUR y PayPal USD.</li><br> 	<li>Las criptodivisas como Bitcoin pueden ser manejadas por GnuCash de manera extraoficial. Los desarrolladores se han negado a añadir el código de la moneda (BTC o XBT) hasta que aparezca en el estándar ISO. Sin embargo GnuCash viene con un montón de otras monedas. La moneda XXX, que no respresenta a ninguna real puede hacer de Bitcoin. Pero para que funcione bien es importante que el valor de Smallest Fraction sea el que permita más digitos decimales. Aun así GnuCash tendrá problemas con tantos decimales a los que acostumbra Bitcoin por lo que puede ser interesante que XXX sean Milibitcoins o Satoshis.</li><br></ul><br><img class="alignnone size-large wp-image-444" src="https://files.adrianistan.eu/ArbolDeCuentas-1024x501.png" alt="ArbolDeCuentas" width="840" height="411" /><br><h2>Cambios de divisa</h2><br>GnuCash permite que creemos cuentas en muchas divisas. Sin embargo, como puedes apreciar en la imagen superior, en la cuenta de PayPal, la moneda extranjera no aparece convertida y no es tenida en cuenta. Esto es porque por defecto GnuCash aplica un tipo de cambio 0, es decir, la moneda extranjera no vale nada. Puedes dejarlo así o puedes añadir el cambio. Hay dos formas, una manual y otra automática.<br><br>Vamos a <em>Tools -&gt; Price Editor</em>. Veremos una ventana vacía con botones a la derecha. Si tenemos Perl instalado en nuestro ordenador con el módulo <strong>Finance::Quote</strong> veremos el botón <em>Get Quotes </em>activado. Ese botón sirve para actualizar los precios de los cambios de divisa y de las acciones. Ese es el modo automático. Si necesitas instalar Perl en Windows puedes usar <a href="http://www.activestate.com/activeperl">ActivePerl</a> o <a href="http://strawberryperl.com/">Strawberry Perl</a>.<br><br>Si queremos hacer el cambio manualmente iremos a <em>Add</em>.<br><br><img class="alignnone size-full wp-image-445" src="https://files.adrianistan.eu/PriceEditor.png" alt="PriceEditor" width="364" height="306" /><br><br>En este caso vamos a añadir el precio para pasar de dólares USD a euros. El namespace es CURRENCY, security es la moneda a la que queramos hacer la transformación a nuestra moneda. Currency la dejamos tal cual. La fecha estará puesta a día de hoy. Esto es muy cómodo porque GnuCash permite guardar el historial de tipos de cambio. Así, cuando haya un nuevo tipo de cambio simplemente vuelves a dar a <em>Add...</em> y se guardará en una fecha distinta. GnuCash será lo suficientemente listo para saber que tipo de cambio ha de usar en cada momento. Por último <em>Price</em> es el precio de 1 dólar en euros. Hay muchos sitios de donde sacar esta información. En Google simplemente con escribir "1 USD to EUR" tendremos el resultado.<br><br><img class="alignnone size-full wp-image-446" src="https://files.adrianistan.eu/USDtoEUR.png" alt="USDtoEUR" width="814" height="628" /><br><h2>Anotar ingresos y gastos</h2><br>Cuando usemos GnuCash en nuestro día a día lo más normal es que anotemos ingresos y gastos. Es necesario haber creado cuentas de tipo <strong>Income</strong> y <strong>Expense</strong> para anotar ingresos y gastos respectivamente. Dentro es habitual crear subcuentas, cada una de una categoría de ingreso o gasto (sueldo, pensión, multas, restaurantes, ...).<br><br>Selecciona la cuenta y haz doble click o pulsa el botón <em>Open</em>, se abrirá en una nueva pestaña, donde podremos ver algo parecido a un libro de contabilidad del MundoReal. Allí podemos revisar las anotaciones anteriores y editarlas (aunque no es recomendable editar la contabilidad, ejem,...). Pero por defecto el foco se centra en la fila inferior, donde podemos introducir una nueva entrada al libro de contabilidad.<br><br><img class="alignnone size-large wp-image-447" src="https://files.adrianistan.eu/LibroGnuCash-1024x545.png" alt="LibroGnuCash" width="840" height="447" /><br><br>La fecha la podemos ajustar a cuando se realizó el ingreso/gasto. El número es solo necesario si eres una empresa y las facturas están numeradas. La descripción es un campo libre, donde explicamos el ingreso/gasto. La siguiente columna es <strong>Transfer</strong> donde debemos de elegir la cuenta de Activos donde se ingresará/eliminará la cantidad. Allí, en la cuenta del Activo también se guarda una copia. Recuerda GnuCash es un sistema de partida doble. Con el botón superior <em>Jump</em> es posible ir a la cuenta del Activo y viceversa, aunque no es necesario. La R es un indicador de reconciliación, no se toca. <em>Charge</em> son cargos que pudiera tener e <em>Income</em> es el beneficio. En una cuenta <strong>Expense</strong> las columnas son <em>Expense</em> y <em>Rebate</em>, de funcionamiento exactamente igual. Ambas tienen <em>Balance</em> que es el balance total.<br><br>Si tienes una transacción compleja donde los ingresos y los gastos ocurren en varios lugares (pagar el sueldo a un trabajador) puedes hacer una transacción partida o <em>Split</em>.<br><h2>Transferencias entre cuentas</h2><br><img class="alignnone size-full wp-image-448" src="https://files.adrianistan.eu/TransferenciaGnuCash.png" alt="TransferenciaGnuCash" width="620" height="598" /><br><br>Las transferencias entre cuentas de activos son sencillas de realizar. Simplemente busca en <em>Actions -&gt; Transfer</em>. En caso de que las cuentas tengan distinta divisa es posible ajustar el cambio. El botón <em>Fetch Rate</em> solo funcionará si tienes el plugin de Perl instalado. La columna de la izquierda es la cuenta de origen y a la derecha se sitúa la cuenta de destino.<br><h2>Programar operaciones</h2><br>Si sabemos que un ingreso o pago va a realizarse de manera periódica podemos automatizarlo. Cuando GnuCash se abra comprobará si desde que abriste el programa por última vez ha habido algún día con una operación programada y la realizará.<br><br>Vamos a <em>Actions -&gt; Schedule -&gt; Scheduled transactions editor</em>. En una nueva pestaña veremos un calendario y una lista de operaciones vacía. Creamos una nueva en <em>New</em>.<br><br>&nbsp;<br><br>En <em>Frequency</em> ajustamos cuando queremos que esta operación se realice. Otra opción es hacer una operación en el libro y desde allí crear una operación programada siguiendo los mismos parámetros que la ya realizada.<br><br><img class="alignnone size-large wp-image-450" src="https://files.adrianistan.eu/Nomina-1-1024x546.png" alt="Nomina" width="840" height="448" /><br><br>GnuCash no es solo esto, también dispone de informes y presupuestos, pero eso se verá en otro momento.<br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/tutorial-gnu-cash</comments>
                <pubDate>Wed, 13 Jul 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Programando para Haiku - File panel - Parte II</title>
                <link>https://blog.adrianistan.eu/programando-haiku-file-panel-parte-ii</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/programando-haiku-file-panel-parte-ii</guid>
                <description><![CDATA[Continuamos los tutoriales de programación en Haiku. Hoy veremos como usar el File Panel. ¿Qué es el File Panel? El File Panel es el diálogo que nos aparece cuando queremos abrir o guardar un archivo o carpeta. En todos los sistemas operativos gráficos es similar.<br><br><img title="" src="http://i.stack.imgur.com/seGHw.png" alt="File Panel de Windows" /><br><h2 id="bfilepanel">BFilePanel</h2><br>La clase básica es BFilePanel. Esta clase se encarga de mostrar esa ventanita que nos deja elegir el archivo o carpeta para abrir o para guardar. Lo primero que tenemos que saber si queremos hacer un File Panel es si va a ser para abrir un archivo existente o para guardar un nuevo archivo. Así, distinguimos entre B_OPEN_PANEL y B_SAVE_PANEL. Si estamos dentro de un B_OPEN_PANEL además indicaremos si aceptamos archivos, carpetas, enlaces simbólicos o alguna combinación de estas cosas. Por último, ¿cómo recibimos la información del panel? Pues usando BMessage, como es habitual en Haiku/BeOS. Pero hay que indicar quién va procesar el mensaje, el conocido como BMessenger. Veamos código:<br><br><pre class="lang:c++ decode:true"><br>const uint32 OPEN_FILE = 42;<br>BFilePanel* filepanel = new BFilePanel(B_OPEN_PANEL,new BMessenger(this),NULL,B_FILE_NODE,new BMessage(OPEN_FILE));<br>filepanel-&gt;Show();<br></pre><br><br>En este código creamos un file panel para abrir un archivo. El BMessenger encargado de procesarlo será el del mismo contexto en el que se ejecute este código. Hay que tener en cuenta que tanto BApplication como BWindow heredan de BMessenger y por tanto cualquier objeto de estas clases es apto. El siguiente parámetro es la carpeta por defecto, que con NULL la dejamos a elección de Haiku. Luego indicamos que queremos abrir archivos, no carpetas ni enlaces simbólicos. Por último especificamos el ID del BMessage que enviará el panel. Esto nos servirá para después saber que ID tenemos que leer dentro de la función MessageReceived del BMessenger. Por último mostramos el panel para que el usuario decida el archivo a abrir. Si la acción es cancelada también será disparado el mensaje, tendremos que comprobar si el usuario eligió el archivo o cerró el diálogo.<br><br><img title="" src="https://api.haiku-os.org/BFilePanel_example.png" alt="Haiku File Panel" /><br><h2 id="leerlarespuesta">Leer la respuesta</h2><br>Dentro de la función MessageReceived del BMessenger tenemos que accionar un caso especial si el ID del BMessage es el que hemos especificado en el panel.<br><br><pre class="lang:c++ decode:true"><br>void MiVentana::MessageReceived(BMessage* msg)<br>{<br>	switch(msg-&gt;what){<br>    	case READ_FILE: {<br>			if (msg-&gt;HasRef(&quot;refs&quot;)) {<br>	  			entry_ref ref;<br>	  			if (msg-&gt;FindRef(&quot;refs&quot;, 0, &amp;ref) == B_OK) {<br>					BEntry entry(&amp;ref, true);<br>					BPath path;<br>					entry.GetPath(&amp;path);<br>					std::cout &lt;&lt; &quot;El archivo es &quot; &lt;&lt; path.Path() &lt;&lt; std::endl; <br>			 	}<br>			}<br>			break;<br>  		}<br>    }<br><br>}<br></pre><br><br>Tenemos que comprobar si el mensaje tiene la propiedad "refs". La propiedad "refs" la ajusta el File Panel cuando se ha seleccionado un archivo. Si la propiedad existe entonces lo leemos. Leeremos una entry<em>ref. Un entry</em>ref es una entrada dentro del sistema de archivos. Sin embargo esta estructura es de bajo nivel y no sabe exactamente donde se ubica. BEntry representa localizaciones dentro del sistema de archivos. Se construye con un entry_ref y esta clase ya sabe donde se ubica de forma legible por un humano (o un programador perezoso). Si queremos saber la ruta del archivo antes tendremos que crear un objeto vacío BPath que llenaremos con contenido. Finalmente la ruta, como string, la podremos leer llamando a la función Path dentro del objeto BPath.<br><br>Ya hemos visto como se usan los file panel en Haiku. Los file panel de guardar archivo se programan exactamente igual cambiando esa pequeña flag al principio.]]></description>
                <comments>https://blog.adrianistan.eu/programando-haiku-file-panel-parte-ii</comments>
                <pubDate>Sat, 04 Jun 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Tutorial de Hugo en español, generador de sitios estáticos</title>
                <link>https://blog.adrianistan.eu/tutorial-hugo-espanol-generador-sitios-estaticos</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/tutorial-hugo-espanol-generador-sitios-estaticos</guid>
                <description><![CDATA[Los generadores de sitios estáticos son aplicaciones que dados unos ficheros generan un sitio web completo, listo para ser desplegado en Apache o Nginx. El generador es llamado bajo demanda del administrador por lo que, al contrario que en un CMS completo, este programa solo genera las páginas una sola vez, reduciendo considerablemente la carga de trabajo y el precio y aumentando en velocidad y rendimiento.<br><br><img class="alignnone size-large wp-image-410" src="https://files.adrianistan.eu/hugo-1024x499.png" alt="hugo" width="840" height="409" /><br><br>Los generadores más populares son:<br><table><br><tbody><br><tr><br><th>Nombre</th><br><th>Lenguaje</th><br><th>Plantillas</th><br><th>Licencia</th><br><th>Sitio web</th><br></tr><br><tr><br><td>Jekyll</td><br><td>Ruby</td><br><td>Liquid</td><br><td>MIT</td><br><td><a href="http://jekyllrb.com">http://jekyllrb.com</a></td><br></tr><br><tr><br><td>Hexo</td><br><td>JavaScript</td><br><td>EJS y Swig</td><br><td>MIT</td><br><td><a href="http://hexo.io">http://hexo.io</a></td><br></tr><br><tr><br><td>Hugo</td><br><td>Go</td><br><td>Go Template, Acer y Amber</td><br><td>Apache</td><br><td><a href="http://gohugo.io">http://gohugo.io</a></td><br></tr><br><tr><br><td>Pelican</td><br><td>Python</td><br><td>Jinja2</td><br><td>GPL</td><br><td><a href="http://blog.getpelican.com">http://blog.getpelican.com</a></td><br></tr><br></tbody><br></table><br>Además es también bastante conocido <a href="http://octopress.org/">Octopress</a> pero Octopress no es más que Jekyll con una colección de utilidades extra, el núcleo del programa sigue siendo Jekyll.<br><br>¿Por qué voy a elegir Hugo? Yo empecé con Jekyll y me gustó. Sin embargo Liquid no me acabó de convencer nunca y miré otras alternativas. Hexo me pareció excelente si lo que quieres hacer es un blog, funciona muy bien y es más rápido que Jekyll pero Jekyll tenía la ventaja de que se podía usar no solo en blogs, sino en cualquier web en general. Entonces oí hablar de Hugo. Hugo es el más rápido y el más flexible. No está enfocado solo en blogs, soporta todo lo que le eches. Sin embargo me parece que Hugo no es el más sencillo de configurar, así que aquí va el tutorial.<br><h2 id="instalandohugo">Instalando Hugo</h2><br>Hugo está hecho en Go, quiere decir que está compilado y por tanto hay una versión diferente para cada sistema operativo. Descarga la versión de tu sistema operativo <a href="https://github.com/spf13/hugo/releases">desde aquí</a>. Si usas GNU/Linux es posible que tu distro haya empaquetado ya Hugo. Búscalo.<br><br>Una vez lo tengamos instalado comprobamos que todo esté en orden:<br><pre><code>hugo version </code></pre><br>Por defecto Hugo no trae ningún tema. Si quieres instalarte uno y no crear uno de cero puedes clonarlos desde Git. Si quieres probar los temas antes de instalarlos no dejes de mirar <a href="http://themes.gohugo.io/">Hugo Themes</a><br><pre><code>git clone --recursive https://github.com/spf13/hugoThemes ~/themes </code></pre><br>Si queremos tener coloreado de sintaxis podemos usar Pygments. Si tienes PIP instalado es fácil.<br><pre><code>sudo pip install Pygments </code></pre><br>Además si quieres activar el autocompletado de Bash solo tienes que hacer<br><pre><code>sudo hugo gen autocomplete . /etc/bash_completion </code></pre><br>Y con esto ya tenemos Hugo instalado correctamente. Ejecuta:<br><pre><code>hugo new site MiSitioSupercalifragilisticoespialidoso </code></pre><br><img class="alignnone size-large wp-image-53" src="https://files.adrianistan.eu/hugo-themes-1024x499.png" alt="hugo-themes" width="840" height="409" /><br><h2 id="organizacinenhugo">Organización en Hugo</h2><br>En Hugo tenemos que tener muy en cuenta la organización de los ficheros. En primer lugar van los <strong>themes</strong>. Como puedes comprobar la carpeta <code>themes</code> generada esta vacía. Para ir probando los distintos temas puedes hacer un sencillo enlace simbólico entre la carpeta con los temas descargada y esta.<br><pre><code><br>rmdir themes <br>ln -s ../themes . </code></pre><br>Veamos el resto de carpetas:<br><ul><br> 	<li><strong>archetypes</strong>. Arquetipos. Son plantillas para cuando añadimos un nuevo elemento. Por ejemplo, podemos tener un arquetipo de post sobre un vídeo de YouTube. Es posible crear un arquetipo que contenga configuración ya específica (categorías, reproductor insertado, etc) y que cuando escribamos ya lo tengamos medio hecho. A la hora de generar el sitio los arquetipos de origen no son tenidos en cuenta.</li><br> 	<li><strong>config.toml</strong> (o config.json o config.yaml). Este archivo contiene la configuración del sitio.</li><br> 	<li><strong>content</strong>. Aquí va el contenido central de la web. Dentro de content debes crear tantas carpetas como secciones tengas (aunque se puede sobreescribir vía configuración, es práctica recomendada). Cada sección tiene asignado un layout distinto. Dentro de la carpeta de cada sección la organización es libre, los archivos suelen ser de Markdown, pero HTML puro también vale.</li><br> 	<li><strong>layouts</strong>. ¿Cómo se organiza el contenido? Los layouts son la respuesta. Por cada sección hay que crear mínimo dos layouts, uno para mostrar un contenido solamente y otro para múltiples contenidos del mismo tipo (listas).</li><br> 	<li><strong>data</strong>. Aquí puedes almacenar archivos en JSON, YAML o TOML a los que puedes acceder desde Hugo. Estos archivos pueden contener cualquier tipo de información, piensa en ellos como en una especie de base de datos.</li><br> 	<li><strong>static</strong>. El contenido estático, imágenes, JavaScript, CSS, que no deba ser procesado por Hugo debes ponerlo aquí.</li><br></ul><br><h2 id="configuracin">Configuración</h2><br>Dentro del fichero config.toml hay que editar unos cuantos valores.<br><pre class="lang:default decode:true ">baseurl = "mi-sitio.com" # La dirección base del sitio<br>languageCode = "es-es" # El idioma de la web<br>title = "" # El título de la web<br>theme = "bleak" # El tema que se va a aplicar al contenido<br>googleAnalytics = "" # Código de seguimiento de Google Analytics<br>disqusShortname = ""<br><br>[Params] # A estos parámetros se puede acceder de forma directa con .Site.Params.NOMBRE<br>Author = "Adrián Arroyo"</pre><br>&nbsp;<br><br>También es configurable <strong>Blackfriday</strong> el motor de Markdown de Hugo, aunque las opciones por defecto son más que suficientes.<br><h2 id="creandocontenido">Creando contenido</h2><br>Crea un archivo dentro de content. Puede estar dentro de una sección si así lo prefieres. En Hugo al igual que en Jekyll cada contenido tiene un <em>front matter</em>, es decir, los metadatos se añaden al principio en un formato que no se va a renderizar. Hugo soporta TOML, YAML y JSON. Si usamos TOML, los delimitadores del <em>front matter</em> serán <code>+++</code>, si usamos YAML <code>---</code> y si usamos JSON tenemos que poner un objeto con las llaves, <code>{}</code><br><pre class="lang:default decode:true">+++<br>title = "El título de la entrada"<br>description = "Una pequeña descripción"<br>tags = ["hola","otra","etiqueta"]<br>date = "2016-05-23"<br>categories = ["Sobre el blog"]<br>draft = true<br>+++<br><br>Aquí va el contenido en Markdown o HTML que va a ser renderizado.<br></pre><br>Podemos crear variables nuevas a las que podremos acceder desde .Params. Otras opciones predefinidas son type (que sobreescriben el valor de la sección), aliases (que permite hacer redirecciones), weight (la prioridad cuando el contenido sea ordenado) y slug (permite ajustar la URL del contenido).<br><h2 id="modificandoeltema">Modificando el tema</h2><br>Puedes modificar el tema usando la carpeta layouts. En el fondo un tema es una colección de layouts y recursos estáticos que son combinados con el tuyo. Si ya usas un tema y solo quieres realizar pequeñas modificaciones puedes editar el tema directamente. Si quieres añadir nuevas secciones o crear un tema de 0 entra a la carpeta layouts.<br><br>Hay varias subcarpetas dentro de layouts importantes:<br><ul><br> 	<li>_default. Es la que se usa cuando no hay otro disponible. Normalmente los temas sobreescriben esta carpeta. Si sobreescribes esta carpeta perderás el tema.</li><br> 	<li>index.html. La página de entrada a la web</li><br> 	<li>partials. En este carpeta se pueden guardar trozos HTML reutilizables para ser usados por los layouts.</li><br> 	<li>shortcodes. Son pequeños trozos de HTML reutilizables con parámetros de entrada para ser usados por el contenido.</li><br></ul><br>Dentro de cada layout (como en _default) tiene que haber mínimo dos archivos. Un archivo single.html que se usará cuando solo se tenga que representar una unidad de ese contenido y un archivo list.html que se usará cuando sea necesario mostrar un conjunto de esos contenidos.<br><br>Estos archivos han de programarse usando el motor de plantillas de Go y la API de Hugo. Un archivo <code>single.html</code> básico que muestra el título y el contenido tal cual sería así.<br><pre class="lang:default decode:true">{{ partial "header.html" . }}<br>{{ partial "subheader.html" . }}<br>&lt;section id="main"&gt;<br>  &lt;h1 id="title"&gt;{{ .Title }}&lt;/h1&gt;<br>  &lt;div&gt;<br>        &lt;article id="content"&gt;<br>           {{ .Content }}<br>        &lt;/article&gt;<br>  &lt;/div&gt;<br>&lt;/section&gt;<br>{{ partial "footer.html" . }}<br></pre><br>Dentro de las páginas list.html es práctica habitual definir una vista <code>li.html</code> como un elemento individual. Esos elementos individuales se unen para formar la lista en <code>list.html</code>.<br><h2 id="algunosextras">Algunos extras</h2><br>Los shortcodes son pequeños trozos de HTML que aceptan parámetros. Podemos usarlos en el contenido. Piensa en ellos como Mixins de CSS o funciones de JavaScript. Por ejemplo, para marcar un resaltado de sintaxis:<br><pre class="lang:default decode:true">{{&lt; highlight html &gt;}}<br>&lt;section id="mira-este-super-codigo"&gt;<br>	&lt;p class="html-is-broken"&gt;Rompiendo el HTML&lt;/p&gt;<br>&lt;/section&gt;<br>{{&lt; /highlight &gt;}}<br></pre><br>O un enlace dentro de nuestra web:<br><pre class="lang:default decode:true">{{&lt; ref "blog/este-es-otro-post-fantastico.md" &gt;}}<br></pre>]]></description>
                <comments>https://blog.adrianistan.eu/tutorial-hugo-espanol-generador-sitios-estaticos</comments>
                <pubDate>Thu, 26 May 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Programando para Haiku - BApplication, BWindow y BButton - Parte I</title>
                <link>https://blog.adrianistan.eu/programando-haiku-bapplication-bwindow-bbutton-parte-i</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/programando-haiku-bapplication-bwindow-bbutton-parte-i</guid>
                <description><![CDATA[Hoy voy a comenzar una serie de tutoriales donde explicaré la programación de una apliación en Haiku. Para ello vamos a usar C++ y la BeAPI. Siempre que tengais cualquier duda podéis visitar la documentación oficial: tanto la antigua de BeOS en el <a href="https://www.haiku-os.org/legacy-docs/bebook/">BeBook</a> y la nueva de Haiku en <a href="https://api.haiku-os.org/">Haiku API</a>. Aun así hay APIs nuevas que todavía no aparecen documentadas. En ese caso hay que recurrir al código fuente.<br><h2 id="librerasenhaiku">Librerías en Haiku</h2><br>Haiku (y BeOS) comparte con UNIX muchas características de bajo nivel. El tema de las librerías es uno de ellos. También han de empezar por lib y terminar por .so si son compartidas y .a si son estáticas. Para compilar también se usa GCC. Sin embargo hay una pequeña diferencia con el resto de sistemas UNIX. En UNIX normalmente disponemos de una librería del C, libc y una librería de funciones matemáticas, libm. En Haiku no existe libm, en cambio existen muchas más, libroot y libbe, para interactuar con el sistema de manera básica, libtracker, con funciones relacionadas con el explorador de archivos, libnetwork y libnetapi, con funciones de red, y muchas otras.<br><br>Además la API se divide en Kits, cada Kit se encarga de una tareas diferentes dentro del sistema operativo. AppKit, Game Kit, Interface Kit, Media Kit, Storage Kit, etc... Si queremos usar la funcionalidad de un kit tendremos que revisar que hemos añadido la librería correcta al compilador y que hemos añadido #include dentro del código que lo usemos.<br><h2 id="unholamundo">Un hola mundo</h2><br>Vamos a empezar por lo simple, una aplicación que muestre una ventana y ya.<br><br>Creamos un archivo de C++, será el punto de inicio de nuestra aplicación. Como sabéis, el punto de inicio de un programa de C o C++ es la función main.<br><br><pre class="lang:c++ decode:true"><br>int main(int argc, char** argv)<br>{<br>	AplicacionPrueba app;<br>    return app.Run();<br>}<br></pre><br><br>Hemos creado un objeto llamado app del tipo AplicacionPrueba y después hemos ejecutado la aplicación. AplicacionPrueba tiene que ser del tipo BApplication. Es la clase básica de todas las aplicaciones Haiku/BeOS. BApplication provee de mensajería entre los distintos procesos del programa (hay que tener en cuenta que BeOS se diseñó pensando en el multiproceso). Vamos a ver como definimos AplicacionPrueba<br><br><pre class="lang:c++ decode:true"><br>#include &lt;AppKit.h&gt;<br><br>class AplicacionPrueba : public BApplication {<br>	public:<br>    	VentanaPrueba* ventana;<br>    	AplicacionPrueba() : BApplication(&quot;application/x-applicion-prueba&quot;){<br>        	ventana = new VentanaPrueba();<br>            ventana-&gt;Show();<br>        }<br>};<br></pre><br><br>Las <strong>Application</strong> necesitan un MIME type, al igual que se usa para indicar los tipos de archivo. No es necesario que sea real. Además hemos creado un objeto VentanaPrueba y la mostramos. VentanaPrueba es del tipo <strong>BWindow</strong> y es la ventana básica de Haiku, lo que vemos. Veamos la definición:<br><br><pre class="lang:c++ decode:true"><br>class VentanaPrueba : public BWindow{<br>	public:<br>    	VentanaPrueba() : BWindow(BRect(100,100,900,700),&quot;Mi ventana&quot;, B_TITLED_WINDOW,0){<br>         // iniciar ventana<br>        }<br>        bool QuitRequested(){<br>        	be_app_messenger.SendMessage(B_QUIT_REQUESTED);<br>            return BWindow::QuitRequested();<br>        }<br>        void MessageReceived(BMessage* msg){<br>        	switch(msg-&gt;what){<br>            	default:<br>                	BWindow::MessageReceived(msg);<br>            }<br>        }<br>};<br></pre><br><br><strong>BWindow</strong> necesita un tamaño, que es indicado con BRect, un título, un estilo (por defecto es B<em>TITLED</em>WINDOW, pero podemos tener ventanas sin bordes o modal) y opciones varias. En las opciones varias podemos especificar que al cerrar la ventana se cierre la aplicación (B<em>QUIT</em>ON<em>WINDOW</em>CLOSE), que el usuario no pueda cambiar su tamaño (B<em>NOT</em>RESIZABLE), que no se pueda minimizar (B<em>NOT</em>MINIMIZABLE) y otras opciones por el estilo.<br><br>Además dentro de la clase hemos definido dos funciones virtuales, es decir, que tienen implementación por defecto de la clase padre, BWindow, pero nosotros podemos modificar su comportamiento.<br><br><strong>QuitRequested</strong> es llamada cuando algo pide el cierre de la ventana. El objeto global <strong>be<em>app</em>messenger</strong> es del tipo BApplication, pero está definido en todos los puntos de nuestra aplicación sin que nosotros hagamos nada. Gracias a este objeto podemos enviar mensajes entre procesos. En este caso enviamos el mensaje a la aplicación de B<em>QUIT</em>REQUESTED. Y luego llamamos a la función sin modificar.<br><br><strong>MessageReceived</strong> es muy importante. Se encarga de procesar todos los mensajes que recibe la ventana. Para distinguir los mensajes (que son del tipo BMessage) tenemos que inspeccionar la propiedad <strong>what</strong>. Se trata de un valor de tipo <strong>uint32</strong>. Hay algunos ya definidos por el sistema como B<em>QUIT</em>REQUESTED pero nosotros podemos definir más. Veremos más tarde como. De momento simplemente devolvemos el procesado de mensajes a BWindow padre.<br><br>Con esto ya podemos compilar.<br><pre><code>gcc -o AplicacionPrueba app.cpp -lbe -lroot </code></pre><br><h2 id="aadiendobviewbbuttonybgrouplayout">Añadiendo BView, BButton y BGroupLayout</h2><br>Ahora vamos a añadir cosas a nuestra ventana sosa. Ponemos una vista dentro de la ventana. Las vistas en Haiku son muy potentes pero eso lo trataré en otro momento. A esa vista le añadiremos un botón.<br><br><pre class="lang:c++ decode:true"><br>VentanaPrueba() : BWindow(BRect(100,100,900,700),&quot;Mi ventana&quot;, B_TITLED_WINDOW,0){<br>         // iniciar ventana<br>         BGroupLayout* sizer = new BGroupLayout(B_HORIZONTAL);<br>         BView* panel = new BView(Bounds(), NULL, B_FOLLOW_ALL_SIDES,<br>                           B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE |<br>                               B_FRAME_EVENTS | B_DRAW_ON_CHILDREN);<br>         panel-&gt;SetViewColor(220, 220, 220);<br>         panel-&gt;SetLayout(sizer);<br>         <br>         BButton* boton = new BButton(&quot;Hola Mundo&quot;,NULL);<br>         <br>         sizer-&gt;AddView(boton);<br>         AddChild(panel);<br>        }<br></pre><br><br>Aquí hemos hecho varias cosas. Por una parte he creado un layout horizontal. Es decir, dispongo el espacio de la ventana de manera horizontal, según se vayan añadiendo elementos lo harán a la derecha. Esto no estaba en BeOS y es particular de Haiku, pero recomiendo usar este sistema pues permite realizar un <strong>responsive design</strong>. Creamos una vista o panel. <strong>Bounds()</strong> indica que cubra todo el espacio disponible. El resto son propiedades de la vista más o menos estándar. Con SetViewColor le podemos poner un color de fondo, y con SetLayout le aplicamos el layout previamente creado.<br><br>Creamos un botón, que es del tipo <strong>BButton</strong>. BButton tiene muchos constructores si revisais la documentación pero este es muy cómodo si usamos el sistema de layouts. Simplemente indicamos el texto que va a mostrar y el mensaje que envía. En este caso NULL pues no vamos a poner ninguno.<br><br><strong>sizer-&gt;AddView()</strong> lo usamos para añadir el botón al layaout y AddChild para añadir la vista a la ventana. Puedes compilar.<br><h2 id="aadiendoeventosmensajeraconbmessagedilogoconbalert">Añadiendo eventos. Mensajería con BMessage. Diálogo con BAlert.</h2><br>Vamos ahora a crear un evento para el botón. Cuando pulsemos el botón mostrará un mensaje al usuario.<br><br>Los eventos se realizan por el sistema de mensajería basado en <strong>BMessage</strong> y BHandler. Para crear un BMessage necesitamos un ID, que es del tipo uint32. Eso es lo mínimo y con eso ya serviría para este caso.<br><br><pre class="lang:c++ decode:true"><br>const uint32 MOSTRAR_DIALOGO = 1;<br><br>...<br><br>BMessage* msg = new BMessage(MOSTRAR_DIALOGO);<br>BButton* boton = new BButton(&quot;Hola mundo&quot;,msg);<br><br>...<br></pre><br><br>Pero los mensajes pueden llevar información adicional de cualquier tipo. Por ejemplo si queremos añadir además una cadena de texto al mensaje usaremos <strong>AddString</strong>.<br><br><pre class="lang:c++ decode:true"><br>msg-&gt;AddString(&quot;NombrePropiedad&quot;,&quot;ValorPropiedad&quot;);<br></pre><br><br>Podremos recuperar el valor en cualquier momento con <strong>FindString</strong>.<br><br>Ahora si vamos a <strong>MessageReceived</strong> podemos añadir código que gestione este tipo de mensaje.<br><br><pre class="lang:c++ decode:true"><br>        void MessageReceived(BMessage* msg){<br>        	switch(msg-&gt;what){<br>            	case MOSTRAR_DIALOGO:<br>                BAlert* alert = new BAlert(&quot;Hola&quot;, &quot;Sabes pulsar el boton, eh?&quot;, &quot;Sip&quot;);<br>                alert-&gt;Go();<br>                break;<br>            	default:<br>                	BWindow::MessageReceived(msg);<br>            }<br>        }<br></pre><br><br>Con un simple case gestionamos el mensaje. Para mostrar un diálogo simple se puede usar <strong>BAlert</strong>. Es muy simple, indicamos el título, el contenido del mensaje y el texto del botón que aparecerá. Y con <strong>Go</strong> lo mostramos.<br><br>Esta ha sido la primera parte del tutorial. Os ha gustado. Hay algo que no haya quedado claro. Comentádmelo.<br><br><pre class="lang:c++ decode:true"><br>#include &lt;AppKit.h&gt;<br>#include &lt;InterfaceKit.h&gt;<br>#include &lt;Layout.h&gt;<br>#include &lt;GroupLayout.h&gt;<br><br>const uint32 MOSTRAR_DIALOGO = 1;<br><br>class AplicacionPrueba : public BApplication {<br>	public:<br>    	VentanaPrueba* ventana;<br>    	AplicacionPrueba() : BApplication(&quot;application/x-applicion-prueba&quot;){<br>        	ventana = new VentanaPrueba();<br>            ventana-&gt;Show();<br>        }<br>};<br><br>class VentanaPrueba : public BWindow{<br>	public:<br>VentanaPrueba() : BWindow(BRect(100,100,900,700),&quot;Mi ventana&quot;, B_TITLED_WINDOW,0){<br>         // iniciar ventana<br>         BGroupLayout* sizer = new BGroupLayout(B_HORIZONTAL);<br>         BView* panel = new BView(Bounds(), NULL, B_FOLLOW_ALL_SIDES,<br>                           B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE |<br>                               B_FRAME_EVENTS | B_DRAW_ON_CHILDREN);<br>         panel-&gt;SetViewColor(220, 220, 220);<br>         panel-&gt;SetLayout(sizer);<br>         <br>         BMessage* msg = new BMessage(MOSTRAR_DIALOGO);<br>         BButton* boton = new BButton(&quot;Hola Mundo&quot;,msg);<br>         <br>         sizer-&gt;AddView(boton);<br>         AddChild(panel);<br>        }<br>        bool QuitRequested(){<br>        	be_app_messenger.SendMessage(B_QUIT_REQUESTED);<br>            return BWindow::QuitRequested();<br>        }<br>        void MessageReceived(BMessage* msg){<br>        	switch(msg-&gt;what){<br>            	case MOSTRAR_DIALOGO:<br>                BAlert* alert = new BAlert(&quot;Hola&quot;, &quot;Sabes pulsar el boton, eh?&quot;, &quot;Sip&quot;);<br>                alert-&gt;Go();<br>                break;<br>            	default:<br>                	BWindow::MessageReceived(msg);<br>            }<br>        }<br>};<br><br>int main(int argc, char** argv)<br>{<br>	AplicacionPrueba app;<br>    return app.Run();<br>}<br></pre>]]></description>
                <comments>https://blog.adrianistan.eu/programando-haiku-bapplication-bwindow-bbutton-parte-i</comments>
                <pubDate>Wed, 18 May 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Valladolid Hyperlapse</title>
                <link>https://blog.adrianistan.eu/valladolid-hyperlapse</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/valladolid-hyperlapse</guid>
                <description><![CDATA[La gente de Novaer ha hecho un hyperlapse de Valladolid, la ciudad que me vio nacer y donde actualmente resido. Me ha gustado bastante y por eso os lo traigo, porque Valladolid no tiene fama de ser ciudad turística pero sigue siendo muy bonita. Quizá no tanto como Salamanca o Segovia, pero bonita igualmente.<br><br><iframe src="https://player.vimeo.com/video/165459021?portrait=0" width="500" height="281" frameborder="0" allowfullscreen="allowfullscreen"></iframe>]]></description>
                <comments>https://blog.adrianistan.eu/valladolid-hyperlapse</comments>
                <pubDate>Wed, 11 May 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>El Universo Mecánico</title>
                <link>https://blog.adrianistan.eu/el-universo-mecanico</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/el-universo-mecanico</guid>
                <description><![CDATA[Hoy voy a recomendaros una excelente serie documental sobre física. <strong>El Universo Mecánico</strong>.<br><br><img class="alignnone size-full wp-image-34" src="https://files.adrianistan.eu/eluniversomecanico.jpeg" alt="eluniversomecanico" width="274" height="320" /><br><br>La serie se organiza en 52 episodios, todos ellos muy interesantes, con una misma estructura. El profesor de CalTech (doctor David L. Goodstein) desde su clase con alumnos hará una introducción al tema en cuestión. Puede que haga un experimento, cuente una historia o incluso lea un poema. Después un narrador nos explicará la teoría, apoyándose de ejemplos, ecuaciones en pantalla y gráficos 3D (revolucionarios para la época).<br><br>El Universo Mecánico no es una serie superificial, no incide mucho en las ecuaciones, pero los conceptos pueden ser algo avanzados, dependiendo del nivel que tenga el espectador. De todos modos, la serie cuenta con un par de capítulos instrumentales donde se explican los conceptos de vector, derivada e integral, que pueden servir al espectador que no domine estos conceptos matemáticos.<br><br><img class="alignnone size-full wp-image-77" src="https://files.adrianistan.eu/movingcircles.jpg" alt="movingcircles" width="541" height="406" /><br><br>La serie no se deja prácticamente nada de la física, veremos desde partículas elementales hasta las ecuaciones de Maxwell pasando por óptica, temperatura, magnetismo, electricidad, gravitación, conservación del momento, ondas, movimiento armónico simple, ...<br><br>Aprenderemos no solo fórmulas y ecuaciones con sentido (o sin él) sino que comprenderemos la genialidad intrínseca de cada teoría. Veremos que no hay verdades inamovibles, que nunca podemos olvidar la historia y que aunque al final del día esa magnífica ecuación nos sirva para mandar esa nave a la luna, lo subyacente, lo real en sí, no es eso. La realidad es la realidad y nosotros nos intentamos acercar a ella con modelos matemáticos. Y como muchas veces hemos visto que dos cosas bien distintas (aparentemente) en realidad son el mismo efecto de la realidad. ¿Qué es una fuerza? Solamente un concepto teórico, que no existe en la realidad, para explicar algo que sucede en nuestro universo.<br><br>Pero aprenderemos además que todo está relacionado con todo, el arte, la poesía, la política, la filosofía y la física no son más que divisiones, en cierto modo arbitrarias que hacemos los humanos dentro de una realidad que no tiene divisiones, que no "colecciona sellos".<br><br><iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/XtMmeAjQTXc?list=PL8_xPU5epJddRABXqJ5h5G0dk-XGtA5cZ" frameborder="0" allowfullscreen></iframe><br><br>Para mí, una de las mejores series documentales de ciencia.]]></description>
                <comments>https://blog.adrianistan.eu/el-universo-mecanico</comments>
                <pubDate>Tue, 10 May 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>The Everything Building</title>
                <link>https://blog.adrianistan.eu/the-everything-building</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/the-everything-building</guid>
                <description><![CDATA[<strong>The Everything Building</strong> es un juego que fue presentado al Ludum Dare 34. Ludum Dare es una game jam, la más popular quizá. Se trata de hacer un juego en 72 horas. Normalmente hay un tema para el juego y si te ciñes a él podrás optar a ganar en más categorías. El tema de esta vez fue "Two game controls" (dos controles). The Everything Building quedó segundo en la general aunque en la categoría de diversión ganó.<br><br><img class="alignnone size-full wp-image-31" src="https://files.adrianistan.eu/elevator.png" alt="elevator" width="384" height="624" /><br><br>¡Probadlo! A mí me ha encantado, es un gran juego. Con solo dos controles (flecha arriba, flecha abajo) controlamos un ascensor. Tenemos que llevar a la gente de una planta a otra. Si hay demasiada gente espereando perdemos. Hasta ahí parece simple pero el tipo de personas que montan en el ascensor tiene efectos sobre el resto. Por ejemplo, existen las parejitas, con un corazón encima, que no van a revelar su planta hasta que no se encuentren con su pareja. Están los zombies que ahuyentan a la gente. Los perros ocupan poco espacio y entran 4 mientras que coches solo entran 1. Si llevas payasos con globos te será más fácil subir y más difícil bajar. Este comportamiento a la inversa si coges a un fortachón. Hay un personaje que puede cambiar el tipo de establecimiento de esa planta (que en definitiva es lo que genera los personajes especiales) y que nos puede servir para <em>jugar táctico</em>.<br><br>En el apartado técnico no hay ningún reproche, es ligero, tiene un diseño pixel art bellísimo, la música acompaña perfectamente y la jugabilidad está ajustada, ¡perderás en el mejor momento!<br><br>En definitiva, jugadlo, es de lo mejorcito que he visto en HTML5.<br><br><iframe src="http://oletus.github.io/elevator/" id="elevator" width="300" height="600" allowfullscreen onclick="document.getElementById('elevator').focus();"><br></iframe><br><br>Mis felicitaciones a Olli Ethuaho, Kimmo Keskinen, Sakari Leppä, Valtteri Heinonen y Anastasia Diatlova. El código está en GitHub: <a href="https://github.com/Oletus/elevator">https://github.com/Oletus/elevator</a>]]></description>
                <comments>https://blog.adrianistan.eu/the-everything-building</comments>
                <pubDate>Mon, 09 May 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>¿Qué ocurrirá con Bitcoin y los ordenadores cuánticos?</title>
                <link>https://blog.adrianistan.eu/ocurrira-bitcoin-los-ordenadores-cuanticos</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/ocurrira-bitcoin-los-ordenadores-cuanticos</guid>
                <description><![CDATA[Hay un tema que me preocupa desde hace un tiempo. Por un lado el auge de las criptodivisas como Bitcoin. Por otra parte, cada vez está más cerca la computación cuántica. Uno de los posibles usos de estos nuevos ordenadores será saltarse los sistemas de criptografía convencional. Así que me surge un dilema, ¿qué ocurrirá exactamente? ¿Habrá una crisis en el mundo Bitcoin?<br><br>La computación cuántica es inevitable. Ya vemos como Google y NASA están trabajando conjuntamente en modelos como <a href="http://www.xataka.com/investigacion/el-ordenador-cuantico-d-wave-2-de-google-no-siempre-es-tan-potente">D-Wave 2</a> (aunque este modelo está pensado para Machine Learning no para criptografía). Así que si partimos de que la computación cuántica va a llegar, solo queda estar preparados para cuando el salto ocurra, que creo que será dentro de poco.<br><br>En <a href="http://bitcoin.stackexchange.com/questions/6062/what-effects-would-a-scalable-quantum-computer-have-on-bitcoin">StackExchange Bitcoin</a> plantean esta pregunta<br><br><img class="alignnone size-full wp-image-16" src="https://files.adrianistan.eu/bitcoin-stackexchange.png" alt="bitcoin-stackexchange" width="746" height="314" /><br><br>Los ordenadores cuánticos afectarán a parte de la estructura Bitcoin.<br><br>El algoritmo ECDSA estará roto. Los ordenadores cuánticos encontrarán una manera sencilla de sacar la clave privada a partir de una clave pública. Significa que podrán acceder a tu cuenta con Bitcoins. Pero no es tan grave si se modifica el uso que le damos a Bitcoin y usamos las direcciones una única vez. La clave pública solo se envía cuando ya hemos gastado los Bitcoins así que si no reutilizamos la dirección nadie podrá acceder a tus Bitcoins.<br><br>Otro problema es el tema de los hashes. Actualmente se usa SHA256, el cuál es lo suficientemente seguro en computación tradicinal aunque con la potencia que tendrá la computación cuántica sería similar a descifrar SHA128 en un ordenador tradicional (algoritmo de Grove). En ese caso Bitcoin tiene un procedimiento para reemplazar el algoritmo rápidamente por un nuevo algoritmo diseñado con ordenadores cuánticos en mente que actualmente <a href="https://en.wikipedia.org/wiki/Post-quantum_cryptography">están en desarrollo</a>.<br><br>Pero lo que puede que además ocurra sea una hiperinflación, una centralización de la red. Puesto que en cuanto un ordenador cuántico entre en la red a minar, la dificultad aumentará drásticamente y el resto de mineros no podrán competir. El problema de la centralización lleva acarreado un problema de pérdida de confianza en la red ("si solo unos pocos controlan la red, ¿cómo sé que no están compinchados entre ellos?"). Si superan el 51% de la potencia de la red, pueden controlar toda la red. Podrá realizar transacciones inversas que él haya mandado. Puede bloquear las transacciones de tener confirmaciones. Puede bloquear al resto de mineros de minar un bloque válido que la red acepte.<br><br>El atacante no podrá, sin embargo, revertir las transacciones de otras personas, bloquear las transacciones (pueden tener 0 confirmaciones pero aparecerás), generar monedas por arte de magia, enviar monedas que nunca le han pertenecido. Así que en la mayoría de casos no sería tan rentable y en muchos casos lo que más dinero te de sea seguir las normas.<br><br>Conclusión, los ordenadores cuánticos pueden afectar a Bitcoin pero ya hay soluciones en marcha y no serán muy difíciles de implementar llegado el momento. Además el atacante podría no tener interés en llevar a cabo dicha acción pues el gasto que le llevaría seguiría siendo superior a lo que obtendría.<br><br>¿Qué opinas tú? ¿Crees que me he equivocado en algo?<br><br>Fuentes:<br><ul><br> 	<li><a href="http://bitcoin.stackexchange.com/questions/6062/what-effects-would-a-scalable-quantum-computer-have-on-bitcoin">http://bitcoin.stackexchange.com/questions/6062/what-effects-would-a-scalable-quantum-computer-have-on-bitcoin</a></li><br> 	<li><a href="http://bitcoin.stackexchange.com/questions/10323/how-vulnerable-is-bitcoin-to-quantum-algorithms?lq=1">http://bitcoin.stackexchange.com/questions/10323/how-vulnerable-is-bitcoin-to-quantum-algorithms?lq=1</a></li><br> 	<li><a href="https://bitcointalk.org/index.php?topic=133425.0">https://bitcointalk.org/index.php?topic=133425.0</a></li><br> 	<li><a href="https://www.quora.com/What-will-quantum-computing-such-as-D-Wave-do-to-bitcoin-mining">https://www.quora.com/What-will-quantum-computing-such-as-D-Wave-do-to-bitcoin-mining</a></li><br> 	<li><a href="https://bitcoinmagazine.com/articles/bitcoin-is-not-quantum-safe-and-how-we-can-fix-1375242150">https://bitcoinmagazine.com/articles/bitcoin-is-not-quantum-safe-and-how-we-can-fix-1375242150</a></li><br></ul>]]></description>
                <comments>https://blog.adrianistan.eu/ocurrira-bitcoin-los-ordenadores-cuanticos</comments>
                <pubDate>Sat, 07 May 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Sobre la Física - Parte 2 - ¿Qué es la luz?</title>
                <link>https://blog.adrianistan.eu/la-fisica-parte-2-la-luz</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/la-fisica-parte-2-la-luz</guid>
                <description><![CDATA[Respuesta corta: dualidad onda-pratícula Respuesta larga: la pregunta no tiene sentido y sigue leyendo<br><br><em>Antes de nada, puede que en algún sitio haya cometido un error garrafal de terminología o de concepto, en ese caso indicádmelo</em><br><h2 id="contexto">Contexto</h2><br>La dualidad onda-partícula hace referencia a la naturaleza de la luz. ¿Qué es la luz? ¿De qué está formada? Para explicarlo, tenemos que remontarnos al siglo XVII, tiempo de Newton y Huygens.<br><br>En esta época, la física despega y cada vez se proponen nuevas leyes para explicar fenómenos observados desde la antigüedad, pero que en la cultura clásica grecorromana y posteriormente en filosofía escolástica se trataban sin referencia a las matemáticas. Este concepto actual de relacionar y aplicar las matemáticas a la naturaleza es un pensamiento que nace en el renacimiento.<br><br>Al tratar el tema de la luz se realizan experimentos con conclusiones muy dispares, lo que genera gran controversia entre los pocos científicos que había en la época. Básicamente distinguimos dos teorías, no voy a explicar sus experimentos, solo el concepto.<br><br>Por un lado, Isaac Newton, propone que la luz es una partícula (una especie de pelotita) y actúa como tal. Tiene experimentos que lo corroboran.<br><br>Por otro lado, Huygens propone que la luz es una onda y actúa como tal. Tiene experimentos que lo corroboran.<br><br>Sin embargo los modelos de partícula y onda son imcompatibles entre sí y lo que explica una teoría no puede ser explicado por la otra y viceversa. Gran problema.<br><h2 id="maxwell">Maxwell</h2><br>Entonces llega Maxwell, ya en el siglo XIX y en un atisbo de genialidad, saca a relucir sus ecuaciones electromagnéticas. Estas ecuaciones son un punto de inflexión en la física, unifican mucho contenido disperso de un asunto que traía de cabeza a los físicos como era la electricidad y el magnetismo. Demuestra que estan relacionadas estas propiedades (o que realmente son lo mismo, según la interpretación) y realiza un curioso hallazgo y es que la relación entre campo eléctrico y campo magnético es... la velocidad de la luz. Esto servirá de punto de partida para que Einstein para su teoría de la relatividad, en la cual la velocidad de la luz es constante y no puede ser superada. Volvemos a Maxwell. Esa relación parece indicar que la luz es en realidad una onda electromagnética. ¿Parece que Huygens tenía razón, no?<br><br><img class="alignnone size-large wp-image-70" src="https://files.adrianistan.eu/maxwell-1024x454.jpg" alt="maxwell" width="840" height="372" /><br><br><img class="alignnone size-full wp-image-71" src="https://files.adrianistan.eu/maxwell-god.jpg" alt="maxwell-god" width="371" height="363" /><br><h2 id="efectofotoelctrico">Efecto Fotoeléctrico</h2><br>Pues tampoco, porque a principios de siglo XX se observa el efecto fotoeléctrico. Este efecto no se puede explicar de ninguna manera por la teoría electromagnética y Einstein en 1905 revoluciona el campo de la concepción materia-energía con una teoría cuantificada. Volvamos atrás al concepto de cuantificado.<br><br><img class="alignnone size-full wp-image-41" src="https://files.adrianistan.eu/FotoElectrico.gif" alt="FotoElectrico" width="325" height="303" /><br><br>Max Planck propone un modelo cuántico, el primero, para tratar de explicar comportamientos relacionados con el cuerpo negro. La teoría supone un cambio drástico porque supone admitir que no existen todos los valores de una variable, sino que las magnitudes físicas van a saltos. Estos saltos son los cuantos, de ahí el nombre de la física cuántica.<br><br>Einstein toma el concepto de los cuantos y los usa para explicar la naturaleza de la luz en el efecto fotoeléctrico. Para él la luz sigue siendo una onda pero a la vez se transmite en una especie de paquetes. Esos paquetes los llama fotones. Entonces llega el Efecto Compton, que a nivel de electrones demuestra que el fotón tiene comportamiento de partícula. Pero esto ya se vuelve difícil de explicar.<br><br>Las teorías de Maxwell funcionaban muy bien y habían sido puestas en práctica con una asombrosa precisión. Ahora estos nuevos experimentos contradecían la teoría de Maxwell. Y lo peor es que aunque se ha definido un comportamiento de partícula, resulta imposible tratar de encontrar la masa de la luz. ¿Cuántos gramos tiene la luz?<br><br>Desde entonces se habla de dualidad onda-partícula, la luz es partícula y onda a la vez.<br><h2 id="conclusin">Conclusión</h2><br>La conclusión a la que podemos llegar (seguro que hay más) es que la física no explica como ES la realidad, sino que planeta modelos que se ajustan a la realidad, pero el universo no es un ordenador o una calculadora. No es una gran ecuación. El universo es el universo, el universo no entiende de la razón y de lógica. Simplemente ES. Y nosotros podemos aplicar modelos, pero esos modelos no SON la realidad.<br><br>Así pues la luz es la luz y se manifiesta de formas distintas en nuestros modelos simplificados, reduccionistas. La pregunta entonces no tiene sentido. En general dudo que podamos llegar hasta el "final de la física" puesto que no tendremos nunca la certeza de que el universo se comporta de forma lógica y racional o se comporta de manera irracional, aunque tengamos modelos matemáticos que puedan predecir la realidad con una asombrosa precisión. No caigamos en el dogma del empirismo. No sobrepasemos los límites de la razón.]]></description>
                <comments>https://blog.adrianistan.eu/la-fisica-parte-2-la-luz</comments>
                <pubDate>Thu, 14 Apr 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Programando el Chromecast desde JavaScript</title>
                <link>https://blog.adrianistan.eu/programando-chromecast-desde-javascript</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/programando-chromecast-desde-javascript</guid>
                <description><![CDATA[Chromecast (o Google Cast) es un dongle, un pequeño aparato, que se conecta a una entrada HDMI y permite disfrutar de contenido multimedia a un precio bastante ajustado (35$). Oficialmente solo se puede programar usando los SDK de iOS, Android y Chrome pero la comunidad ha conseguido replicar el protocolo interno. Chromecast ha tenido varios protocolos de comunicación pero el importante para lo que vamos a realizar se llama CASTv2.<br><br><img class="alignnone size-large wp-image-20" src="https://files.adrianistan.eu/chromecast-box-1024x683.jpg" alt="chromecast-box" width="840" height="560" /><br><h2 id="cmofuncionachromecastpordentro">¿Cómo funciona Chromecast por dentro?</h2><br>Primero voy a explicar como funciona el Chromecast. Chromecast sigue la filosofía de los Chromebooks, en el que todo el sistema es el navegador web, en este caso Chrome. Una app se conecta a Chromecast a través del protocolo de descubrimiento, deben estar en la misma red Wi-Fi. La aplicación solicita iniciar una aplicación, para lo cual manda el ID de la aplicación que desea abrir. Chromecast busca en la base de datos de Google (<a href="https://cast.google.com">https://cast.google.com</a>, tasa de registro de 5$) y allí le indicará una URL.<br><br>Chromecast abre la web y ejecuta la aplicación, que usando una librería de JavaScript le permite comunicarse con la app original. Podemos distinguir sin embargo varias aplicaciones "preinstaladas". Son webs como el resto pero están alojadas por Google sin marca. Son DefaultMediaReceiver y StyledMediaReceiver. Se trata de dos reproductores multimedia básicos, que soportan vídeo en MP4 y WebM desde una URL, subtítulos, carátula y en el caso de StyledMediaReceiver es posible modificar un poco el aspecto del reproductor.<br><br><img class="alignnone size-full wp-image-19" src="https://files.adrianistan.eu/chromecast.png" alt="chromecast" width="785" height="823" /><br><h2 id="reproduciendounvdeoconchromecastnodejsydefaultmediareceiver">Reproduciendo un vídeo con Chromecast, Node.js y DefaultMediaReceiver</h2><br>Como hemos visto, reproducir un vídeo en Chromecast no es muy difícil. Para ello me voy a ayudar en la librería <a href="https://github.com/guerrerocarlos/chromecast-js">chromecast-js</a> que en última instancia remite a <a href="https://github.com/thibauts/node-castv2-client">node-castv2-client</a>.<br><br>En un ordenador, dentro de la misma red Wi-Fi que el Chromecast y con Node.js instalado ejecutamos:<br><pre><code><br>mkdir chromecast-video <br>cd chromecast-video <br>npm install chromecast-js <br></code></pre><br>Creamos un archivo de JavaScript con el siguiente contenido<br><br><pre class="lang:js decode:true"><br>var chromecastjs = require('chromecast-js'); // Obtenemos la librería<br><br>var browser = new chromecastjs.Browser(); // Iniciamos la búsqueda<br><br>browser.on('deviceOn', function(device){ // Cuando encuentre un dispositivo...<br>  device.connect(); // Nos conectamos a él<br>  device.on('connected', function(){ // Y cuando nos conectemos<br><br>    device.play('http://commondatastorage.googleapis.com/gtv-videos-bucket/big_buck_bunny_1080p.mp4', 60, function(){ // Mandamos reproducir el vídeo Big Buck Bunny, en MP4, no desde el principio, sino desde el primer minuto<br>        console.log('Reproduciendo en el Chromecast!');<br>    });<br><br>    setTimeout(function(){ // Pasados 30 segundos paramos el vídeo<br>        device.pause(function(){<br>            console.log('Paused!')<br>        });<br>    }, 30000);<br><br>    setTimeout(function(){ // Pasados otros 10 segundos más, se corta la retransmisión<br>        device.stop(function(){<br>            console.log('Stoped!')<br>        });<br>    }, 40000);<br><br>  })<br>})<br></pre><br><br>Es un ejemplo muy sencillo. Como veis, Chromecast se conecta directamente al vídeo que reproduce, el dispositivo de control solo es eso, solo controla, nunca envía la película.<br><h2 id="personalizandoconstyledmediareceiver">Personalizando con StyledMediaReceiver</h2><br>El módulo <code>chromecast-js</code> también permite usar el StyledMediaReceiver, cuyo funcionamiento es idéntico al de DefaultMediaReceiver pero se le puede personalizar el aspecto. Además añadimos subtítulos (formato WebVTT) y una carátula.<br><br><pre class="lang:js decode:true"><br>var chromecastjs = require('chromecast-js')<br><br>var browser = new chromecastjs.Browser()<br><br>var media = {<br>    url : 'http://commondatastorage.googleapis.com/gtv-videos-bucket/big_buck_bunny_1080p.mp4', // El vídeo<br>    subtitles: [{<br>        language: 'en-US',<br>        url: 'http://carlosguerrero.com/captions_styled.vtt',<br>        name: 'English',<br>    },<br>    {<br>        language: 'es-ES',<br>        url: 'http://carlosguerrero.com/captions_styled_es.vtt',<br>        name: 'Spanish',<br>    }<br>    ], // Los subítulos, un fichero para inglés y otro para español, en formato WebVTT<br>    cover: {<br>        title: 'Big Bug Bunny',<br>        url: 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/BigBuckBunny.jpg' // Carátula, se muestra cuando el vídeo se está cargando<br>    },<br>    subtitles_style: { <br>          backgroundColor: '#FFFFFFFF', // see http://dev.w3.org/csswg/css-color/#hex-notation<br>          foregroundColor: '#000FFFF', // see http://dev.w3.org/csswg/css-color/#hex-notation<br>          edgeType: 'DROP_SHADOW', // can be: &quot;NONE&quot;, &quot;OUTLINE&quot;, &quot;DROP_SHADOW&quot;, &quot;RAISED&quot;, &quot;DEPRESSED&quot;<br>          edgeColor: '#AA00FFFF', // see http://dev.w3.org/csswg/css-color/#hex-notation<br>          fontScale: 1.5, // transforms into &quot;font-size: &quot; + (fontScale*100) +&quot;%&quot;<br>          fontStyle: 'BOLD_ITALIC', // can be: &quot;NORMAL&quot;, &quot;BOLD&quot;, &quot;BOLD_ITALIC&quot;, &quot;ITALIC&quot;,<br>          fontFamily: 'Droid Sans',<br>          fontGenericFamily: 'CURSIVE', // can be: &quot;SANS_SERIF&quot;, &quot;MONOSPACED_SANS_SERIF&quot;, &quot;SERIF&quot;, &quot;MONOSPACED_SERIF&quot;, &quot;CASUAL&quot;, &quot;CURSIVE&quot;, &quot;SMALL_CAPITALS&quot;,<br>          windowColor: '#AA00FFFF', // see http://dev.w3.org/csswg/css-color/#hex-notation<br>          windowRoundedCornerRadius: 10, // radius in px<br>          windowType: 'ROUNDED_CORNERS' // can be: &quot;NONE&quot;, &quot;NORMAL&quot;, &quot;ROUNDED_CORNERS&quot;<br>    } // Aquí creamos un estilo para los subtítulos, la notación es fácil si ya conoces CSS<br>}<br><br><br>browser.on('deviceOn', function(device){<br>  device.connect()<br>  device.on('connected', function(){<br><br>    // Iniciamos la retransmisión, pero esta vez enviamos más información al Chromecast, usaremos StyledMediaReceiver<br>    device.play(media, 0, function(){<br>        console.log('Playing in your chromecast!')<br><br>        setTimeout(function(){<br>            console.log('subtitles off!')<br>            device.subtitlesOff(function(err,status){ // Desactivamos los subtítulos<br>                if(err) console.log(&quot;error setting subtitles off...&quot;)<br>                console.log(&quot;subtitles removed.&quot;)<br>            });<br>        }, 20000);<br><br>        setTimeout(function(){<br>            console.log('subtitles on!')<br>            device.changeSubtitles(1, function(err, status){ // Restablecemos los subtítulos, pero al Español<br>                if(err) console.log(&quot;error restoring subtitles...&quot;)<br>                console.log(&quot;subtitles restored.&quot;)<br>            });<br>        }, 25000);<br><br>        setTimeout(function(){<br>            device.pause(function(){<br>                console.log('Paused!')<br>            });<br>        }, 30000);<br><br>        setTimeout(function(){<br>            device.unpause(function(){<br>                console.log('unpaused!')<br>            });<br>        }, 40000);<br><br>        setTimeout(function(){<br>            console.log('I ment English subtitles!')<br>            device.changeSubtitles(0, function(err, status){<br>                if(err) console.log(&quot;error restoring subtitles...&quot;)<br>                console.log(&quot;English subtitles restored.&quot;)<br>            });<br>        }, 45000);<br><br>        setTimeout(function(){<br>            console.log('Increasing subtitles size...')<br>            device.changeSubtitlesSize(10, function(err, status){ // Cambiamos el tamaño de los subtítulos<br>                if(err) console.log(&quot;error increasing subtitles size...&quot;)<br>                console.log(&quot;subtitles size increased.&quot;)<br>            });<br>        }, 46000);<br><br>        setTimeout(function(){<br>            device.seek(30,function(){ // Nos movemos dentro del vídeo<br>                console.log('seeking forward!')<br>            });<br>        }, 50000);<br><br>        setTimeout(function(){<br>            console.log('decreasing subtitles size...')<br>            device.changeSubtitlesSize(1, function(err, status){<br>                if(err) console.log(&quot;error...&quot;)<br>                console.log(&quot;subtitles size decreased.&quot;)<br>            });<br>        }, 60000);<br><br>        setTimeout(function(){<br>            device.pause(function(){<br>                console.log('Paused!')<br>            });<br>        }, 70000);<br><br>        setTimeout(function(){<br>            device.seek(30,function(){<br>                console.log('seeking forward!')<br>            });<br>        }, 80000);<br><br>        setTimeout(function(){<br>            device.seek(30,function(){<br>                console.log('seeking forward!')<br>            });<br>        }, 85000);<br><br>        setTimeout(function(){<br>            device.unpause(function(){<br>                console.log('unpaused!')<br>            });<br>        }, 90000);<br><br><br>        setTimeout(function(){<br>            device.seek(-30,function(){<br>                console.log('seeking backwards!')<br>            });<br>        }, 100000);<br><br><br>        setTimeout(function(){<br>            device.stop(function(){<br>                console.log('Stoped!')<br>            });<br>        }, 200000);<br><br>    })<br>  })<br>}<br></pre><br><br>En definitiva, para muchas aplicaciones este módulo es más que suficiente. En caso de que queramos llevar un juego a Chromecast la cosa se complica. Tenemos que programar una app del tipo CustomMediaReceiver en HTML5 y luego su cliente (en Node.js o usando las librerías oficiales de Google para Android, iOS y Chrome). Si os ha gustado esta entrada y queréis saber como realizar esto último, compartid y comentad, me gustaría saber que opináis al respecto y podéis darme ideas.]]></description>
                <comments>https://blog.adrianistan.eu/programando-chromecast-desde-javascript</comments>
                <pubDate>Sat, 02 Apr 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Sobre la Física - Parte 1</title>
                <link>https://blog.adrianistan.eu/la-fisica-parte-1</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/la-fisica-parte-1</guid>
                <description><![CDATA[La filosofía de la ciencia. ¿Qué es la realidad? ¿Qué es la física? Es algo que me apasiona. Como es un tema denso serán escritos cortos.<br><br><iframe src="https://www.youtube-nocookie.com/embed/1RWOpQXTltA" width="560" height="315" frameborder="0" allowfullscreen="allowfullscreen"></iframe><br><br>¿Qué es la <strong>física</strong>? No es algo sencillo. Realmente no hay una respuesta universal. ¿Qué es la naturaleza, qué es la realidad? No son preguntas fáciles. La mayoría de la gente hoy día es empirista, de un grado u otro, una posición correcta, pero no es la única válida. Platón, aún cuando ha pasado mucho tiempo, sigue teniendo una teoría válida para explicar la realidad, alejada totalmente del empirismo. Y el empirismo radical conlleva al escepticismo como avisaba Kant. No poder estar seguros de nada, ¡terminamos en un punto peor que el de partida! Tenemos que asumir que la naturaleza es uniforme en sus comportamientos, pero eso es algo que <strong>nunca</strong> podrá ser demostrado. Dudas, dudas.<br><br><img class="alignnone size-full wp-image-11" src="https://files.adrianistan.eu/Ajedrezado.jpg" alt="Ajedrezado" width="245" height="388" /><br><br>¿Y las matemáticas? ¿Fueron descubiertas o fueron inventadas? Ahora mi opinión. La <strong>física</strong> no es la realidad. La <strong>física</strong> es un modelo matemático, muy preciso, pero alejado de lo real. "El Universo se puede expresar en una ecuación" es falso. La <strong>física</strong> no es más que una interpretación humana usando matemáticas. La física entonces no es más que una invención humana, no nos dice porque la realidad es tal como es. Dudas, dudas.<br><br>Lo más triste de todo es que hay gente que cree en la ciencia como un dogma, ajena a cualquier objeción. Pasamos del dogma bíblico al dogma de "El Método Científico". Salimos de una cueva y entramos en otra. Muy poca gente, muy pocos científicos pueden estar realmente fuera de la caverna. Dudas, dudas.<br><br>Y en estos temas la gente obvia el asunto de Dios. Asunto para nada cerrado. No como un Dios personalista, que juzga a la gente, pero ¿y como un estado ideal de perfección? Pero antes tenemos que definir la perfección. ¿O es Dios acaso otra cosa? Dudas, dudas.<br><br>¿Qué opináis? Me encantará oír vuestras opiniones al respecto y por supuesto, sois libres de criticarme. Intentaré desarrollar cada punto por separado, porque es extenso, de momento dejamos el asunto planteado.]]></description>
                <comments>https://blog.adrianistan.eu/la-fisica-parte-1</comments>
                <pubDate>Sun, 27 Mar 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Análisis de The Witness</title>
                <link>https://blog.adrianistan.eu/analisis-the-witness</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/analisis-the-witness</guid>
                <description><![CDATA[Recientemente me he pasado el juego de puzzles The Witness, la última obra de <a href="http://number-none.com/blow/">Jonathan Blow</a>. Me lo había recomendado un buen amigo y al poco me enganché. Voy a hacer un spoiler mínimo, lo justo para que os quedeis con los dientes largos.<br><br><strong>Nota musical:</strong><br><br><iframe src="https://embed.spotify.com/?uri=spotify:track:5zhuWncJsBKrQ1HhmAKNAg" width="300" height="380" frameborder="0"></iframe> <iframe src="https://embed.spotify.com/?uri=spotify:album:5ZQaqZOFLl0dntuuso108e" width="300" height="380" frameborder="0"></iframe><br><blockquote>Si te has pasado el juego al 100% lo entenderás</blockquote><br>The Witness se inicia de manera enigmática para el jugador acostumbrado a la jugabilidad Nintendo. Con jugabilidad Nintendo me refiero a la típica de juegos como Mario Party donde antes de cada juego se explican las instrucciones por escrito y se puede incluso practicar. La propia pantalla de carga produce intriga al jugador.<br><br><img class="alignnone size-large wp-image-118" src="https://files.adrianistan.eu/TheWitnessLoad-1024x576.jpg" alt="TheWitnessLoad" width="840" height="473" /><br><br>Aparecemos en un pasillo, en una isla, al principio solo podemos avanzar hacia delante. El juego nos indica que podemos usar las teclas WASD para movernos y que usemos el ratón. Es la única ayuda que nos dará.<br><br><img class="alignnone size-large wp-image-112" src="https://files.adrianistan.eu/TheWitness-1024x576.jpg" alt="TheWitness" width="840" height="473" /><br><br>Para avanzar tendremos que resolver puzzles. Todos los puzzles poseen la misma estructura, empezamos por un círculo gordo y tenemos que dibujar una ruta por la cuadrícula hasta el final, una línea que se sale de la cuadrícula. Pero no vale con llegar, tendremos que cumplir las condiciones que los símbolos geométricos y el entorno nos impongan. Al principio estos símbolos no aparecen pero según vayamos avanzando, empezaran a aparecer hexágonos, cuadrados amarillos, cuadrados azules, cuadrados blancos, estrellas rosas, triángulos y el entorno también influirá en la resolución del puzzle.<br><br><iframe src="https://www.youtube-nocookie.com/embed/SPMMKFX78x0" width="560" height="315" frameborder="0" allowfullscreen="allowfullscreen"></iframe><br><br>Una vez atravesamos la primera parte, se nos deja sueltos en la isla y entonces podemos hablar de un auténtico sandbox de puzzles.<br><br><img class="alignnone size-large wp-image-113" src="https://files.adrianistan.eu/TheWitnessApple-1024x576.jpg" alt="TheWitnessApple" width="840" height="473" /><br><br>El significado de cada símbolo, de porque un puzzle se resuelve de esta manera y no de otra es algo que queda para nosotros. Tenemos que resolver los puzzles, pero además tenemos que saber qué nos pide el puzzle. Y muchas veces creemos haber llegado a una conclusión que es incorrecta, y tenemos que replantearnos todo lo anterior. Muy bien pensado. Cuando ya sepamos el significado de los símbolos y como influye el entorno en el puzzle el reto ya no será descifrar su significado, sino resolverlo, puesto que serán más complejos. Un trabajo de científicos...<br><br><iframe src="https://www.youtube-nocookie.com/embed/CIUOHvmRJgs" width="560" height="315" frameborder="0" allowfullscreen="allowfullscreen"></iframe><br><br>El vídeo es un desbloqueable que encontramos en el juego si somos avispados, ya que las piezas del gran puzzle que es encontrar los vídeos se encuentran desperdigadas por la isla. En este fragmento vemos a James Burke al final de la serie Connections de la BBC (1978). The Witness está plagado de filosofía empirista por todas partes. Además de estos vídeos (que hay 6) nos encontramos con 43 audios con frases, explicaciones y pensamientos de Albert Einstein, William Faulkner, Feynman y gente relacionada con la filosofía de la ciencia y otros personajes de la filosofía zen moderna, como Gangaji y David Golding. Algunos audios son más estimulantes que otros, algunos te hacen pensar y otros son un poco rollo. Lo cierto es que crea una atmósfera que mezcla elementos zen y el empirismo.<br><br>El zen es tan importante que en un determinado momento nos preguntaremos si todo esto es un kōan, es decir un problema que el maestro plantea al alumno para comprobar sus progresos, muchas veces un problema absurdo que para ser resuelto el alumno debe desligarse del pensamiento racional común para así entrar en un sentido racional más elevado y así aumentar su nivel de conciencia para intuir lo que en realidad le está preguntando el maestro, que trasciende al sentido literal de las palabras. Una explosión mental en toda regla que nos acerca, en esos momentos de explosión al contacto más directo con la realidad, sin pasar por el pensamiento, pues este ha sido destrozado.<br><br>Sin embargo toda esta filosofía que planté a el juego no acaba bien, por lo menos desde mi punto de vista. El juego parece que según vas avanzando llegarás a una conclusión final, que te hará replantearte tu vida. Esto no ocurre. Quizá sea un reflejo de la vida. Creemos que tiene algún sentido, que algún día comprenderemos. Pero al final solo hemos dado unas pinceladas aquí y haya, pero seguimos sin tener "eso". "Eso" que acabaría con "aquello". Así que el final no es malo, pero quizá cuando lo veas por primera vez, después de haber estado jugando te sepa a poco. Es lógico. Pero tras un razonamiento, haciendo analogías con la vida, no me parece un final tan malo.<br><br>El apartado estético es alucinante, cada vez que lo pienso más me asombra. No es especialmente realista, podríamos llamarlo impresionista, con formas toscas, esquinas gigantes pero una iluminación y un colorido espectacular. Aparte de la belleza de por sí de la naturaleza y los edificios tenemos juegos de sombras:<br><br><img class="alignnone size-large wp-image-119" src="https://files.adrianistan.eu/TheWitnessMalabarista-1024x576.jpg" alt="TheWitnessMalabarista" width="840" height="473" /><br><br>En este caso por ejemplo, vemos la estatua de un pobre hombre, parece que gritando al cielo, una gran desgracia la ha acontecido. Pero su sombra, que combina con las piedras del suelo nos deja otra imagen, se encuentra haciendo malabares, un símbolo de felicidad. No todo es lo que parece.<br><br><img class="alignnone size-large wp-image-391" src="https://files.adrianistan.eu/TheWitnessGranEstatua-1024x576.png" alt="TheWitnessGranEstatua" width="840" height="473" /><br><br>&nbsp;<br><br>En esta imagen vemos la estatua una mujer tallada en la montaña. Tiene un porte de grandeza, se va a comer el mundo, es fuerte y mira al cielo, aspirando a lo más alto.<br><br><img class="alignnone size-large wp-image-117" src="https://files.adrianistan.eu/TheWitnessEstatuaAyuda-1024x576.jpg" alt="TheWitnessEstatuaAyuda" width="840" height="473" /><br><br>En cambio, desde el punto de vista correcto, la situación cambia por completo, la mujer está siendo ayudada por otra, parece que va a morir, que las cosas le van mal y la otra mujer, por encima de ella la intenta subir a donde está ella, un lugar mucho más estable.<br><br><img class="alignnone size-large wp-image-120" src="https://files.adrianistan.eu/TheWitnessPuzzleAmbiental-1024x576.jpg" alt="TheWitnessPuzzleAmbiental" width="840" height="473" /><br><br>&nbsp;<br><br>Y dejamos lo mejor para el final, los <strong>puzzles ambientales</strong>. Se trata de puzzles que aparecen en la naturaleza. En la imagen vemos uno muy sencillo, el del río, que además cuenta con panel explicativo. Cuando veamos uno de ellos simplemente nos dirigimos al punto gordo y vamos hasta el final. Veremos estrellitas resplandecer y si finalizamos la ruta se hará un gran estruendo. Hay muchísimos. Y muchos donde no te los esperas. Por haber hay hasta uno dentro de una película del cine, que, os adelanto, dura una hora el recorrido que tenemos que hacer.<br><br><img class="alignnone size-large wp-image-116" src="https://files.adrianistan.eu/TheWitnessEnvironmentalPuzzle-1024x640.jpg" alt="TheWitnessEnvironmentalPuzzle" width="840" height="525" /><br><br>¡Están por todas partes!<br><br><img class="alignnone size-full wp-image-115" src="https://files.adrianistan.eu/TheWitnessCastleBefore.jpg" alt="TheWitnessCastleBefore" width="620" height="373" /><br><br><img class="alignnone size-large wp-image-114" src="https://files.adrianistan.eu/TheWitnessCastleAfter-1024x576.jpg" alt="TheWitnessCastleAfter" width="840" height="473" /><br><br>Mi recomendación final es que un juego magnífico, llegar al final estándar no es muy difícil, pero el jugador que le entre el gusanillo puede aspirar a conseguir todos los vídeos, entrar al nivel de la cueva y desvelar el final secreto.<br><br><a href="http://rover.ebay.com/rover/1/1185-53479-19255-0/1?icep_ff3=9&amp;pub=5575150394&amp;toolid=10001&amp;campid=5337796800&amp;customid=&amp;icep_uq=The+Witness&amp;icep_sellerId=&amp;icep_ex_kw=&amp;icep_sortBy=12&amp;icep_catId=&amp;icep_minPrice=&amp;icep_maxPrice=&amp;ipn=psmain&amp;icep_vectorid=229501&amp;kwid=902099&amp;mtid=824&amp;kw=lg" target="_blank">Comprar en eBay</a> <a href="https://www.amazon.es/s/ref=as_li_ss_tl?_encoding=UTF8&amp;camp=3626&amp;creative=24822&amp;field-keywords=The%20Witness&amp;linkCode=ur2&amp;tag=adrarrcal-21&amp;url=search-alias%3Daps" target="_blank">Comprar en Amazon</a> <a href="http://adf.ly/1YkoSg" rel="nofollow">Comprar en Steam</a>]]></description>
                <comments>https://blog.adrianistan.eu/analisis-the-witness</comments>
                <pubDate>Fri, 25 Mar 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Enebro, un robot automático para FOREX en JavaScript</title>
                <link>https://blog.adrianistan.eu/enebro-robot-automatico-forex-javascript</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/enebro-robot-automatico-forex-javascript</guid>
                <description><![CDATA[Hoy en día el mundo de la bolsa y el trading está dominado por robots. Más del 70% de las transacciones que procesa Wall Street han sido generadas por algoritmos que funcionan ininterrumpidamente.<br><br>Probablemente tú también querrás usar robots. Hay soluciones para pequeños inversores como MetaTrader, pero voy a tratar de construir un bot de bolsa desde 0 en JavaScript. ¿Suena divertido?<br><br><img class="alignnone size-full wp-image-40" src="https://files.adrianistan.eu/forex.jpg" alt="forex" width="600" height="340" /><br><h2 id="pyalgotrading">PyAlgoTrading</h2><br>El diseño de mi robot, Enebro, va a estar basado en <a href="http://gbeced.github.io/pyalgotrade/">PyAlgoTrade</a>. Los componentes principales de PyAlgoTrade son 6:<br><ul><br> 	<li>Estrategias - Definen cuando comprar y cuando vender</li><br> 	<li>Entradas (o feeds) - Proveen datos. Pueden ser tantos datos financieros como datos provienientes de portales de noticias o Twitter.</li><br> 	<li>Brokers - Se encargan de ejecutar las órdenes</li><br> 	<li>DataSeries - Una abstracción usada para manejar los datos en su conjunto</li><br> 	<li>Indicadores técnicos - Realiza los cálculos de los indicadores técnicos usando datos de DataSeries.</li><br> 	<li>Optimizador - Cuando se realiza backtesting (probar la estrategia con datos pasados) este módulo permite realizarse en menos tiempo, distribuyendo el trabajo entre distintos ordenadores. Este módulo no lo voy a implementar en Enebro.</li><br></ul><br>Vamos a trabajar ya en el programa, que operará el par EURUSD. El programa es meramente educativo y no está tan bien programado como debería pero servirá para entender algún concepto.<br><h2 id="programandoentradasofeeds">Programando: Entradas o feeds</h2><br>Lo primero que vamos a implementar son la entrada de los datos. Voy a implementar dos formas de obtener la información, una es leer los datos desd e un fichero CSV. Esto servirá para realizar backtesting. Además voy a añadir la entrada de datos de <a href="https://uphold.com">Uphold</a>, para obtener los datos en tiempo real<br><br><pre class="lang:js decode:true"><br>&quot;use strict&quot;;<br>var csv = require(&quot;csv&quot;);<br>var fs = require(&quot;fs&quot;);<br>var Bar = require(&quot;./strategy&quot;).Bar;<br><br>class Feed{<br>        constructor(instruments){<br>                this.instruments = instruments;<br>        }<br>}<br><br>class UpholdFeed extends Feed{<br>        constructor(instruments){<br>                super(instruments);<br>                this.Uphold = require(&quot;uphold-sdk-node&quot;)({<br>                        &quot;key&quot; : CLIENT_ID,<br>                        &quot;secret&quot; : CLIENT_SECRET,<br>                        &quot;scope&quot; : SCOPE,<br>                        &quot;pat&quot; : TOKEN<br>                });<br>        }<br>        run(cb){<br>                var self = this;<br>                setInterval(function(){<br>                        self.Uphold.tickers(function(err,tickers){<br>                			var date = new Date();<br>                			var isodate = date.toISOString();<br>                			var ask = tickers[12].ask;<br>                			var bid = tickers[12].bid;<br>                			var media = (ask+bid)/2;<br>                			console.log(&quot;EURUSD: &quot;+ask+&quot;-&quot;+bid);<br>                            var bar = new Bar(media,media,media,media);<br>                            var bars = {};<br>                            bars[self.instruments] = bar;<br>                            cb(bars);<br>                        });<br>                },1000*60);<br>        }<br>}<br><br>class CSVFeed extends Feed{<br>        constructor(instruments,file){<br>                super(instruments);<br>                this.file = file;<br>        }<br>        run(cb){<br>                var reader = fs.createReadStream(this.file);<br>                var parser = csv.parse({<br>                        delimiter: &quot;;&quot;<br>                });<br>                var data = &quot;&quot;;<br>                var self = this;<br>                parser.on(&quot;readable&quot;,function(){<br>                        while(data = parser.read()){<br>                                // Run onBars;<br>                                var bar = new Bar(data[1],data[4],data[2],data[3]);<br>                                var bars = {};<br>                                bars[self.instruments] = bar;<br>                                cb(bars);<br>                        }<br>                });<br>                reader.on(&quot;data&quot;,function(chunk){<br>                        parser.write(chunk);<br>                })<br>        }<br>}<br><br>module.exports = {CSVFeed: CSVFeed, UpholdFeed: UpholdFeed};<br></pre><br><br>Veamos como funciona. Definimos una estrctura básica de Feed. Cada Feed hace referencia a un instrumento, en nuestro caso el instrumento es "EURUSD". Un feed necesita una función <code>run()</code> que será llamada una vez cuando tenga que funcionar el robot. A partir de ese momento el Feed asume la responsabilidad de proveer de datos al robot llamando siempre que haya nuevas barras (velas japonesas) a la función de callback <code>cb()</code>. En el caso de CSVFeed, la llamada a cb() se produce cada vez que se ha procesado una línea del fichero. En el caso de UpholdFeed, se envía una nueva barra cada minuto. En el caso de CSVFeed se pide un fichero en el constructor que se procesa con la librería <strong>csv</strong> y en el caso de Uphold, se usa la API para obtener los precios.<br><br>En esta primera parte hemos visto como usamos un objeto Bar que todavía no he enseñado, no es muy difícil.<br><br><pre class="lang:js decode:true"><br>class Bar{<br>        constructor(open,close,high,low){<br>                this.open = open;<br>                this.close = close;<br>                this.high = high;<br>                this.low = low;<br>        }<br>        getPrice(){<br>                return this.close;<br>        }<br>}<br></pre><br><br><h2 id="elbrker">El bróker</h2><br>Necesitamos un bróker, que se encargará de realizar las operaciones. Para realizar backtesting es necesario disponer de un bróker simulado, lo he llamando EnebroBroker. Soporta solo la operación de entrar en largo y salir de mercado.<br><br><pre class="lang:js decode:true"><br>class EnebroBroker{<br>        constructor(capital){<br>                this.capital = capital;<br>                this.register = {};<br>                this.benefit = 0;<br>        }<br>        enterLong(instrument,price){<br>                if(!this.register[instrument])<br>                        this.register[instrument] = [];<br>                this.register[instrument].push({<br>                        shares: Math.floor(this.capital/price),<br>                        price: price<br>                });<br>                this.strategy.open = true;<br>                this.strategy.onEnterOK();<br>        }<br>         exitMarket(instrument,price){<br>                var last = this.register[instrument].pop();<br>                var diff = price - last.price;<br>                var total = diff * last.shares;<br>                this.benefit += total;<br>                this.strategy.open = false;<br>                this.strategy.onExitOK();<br>                console.log(&quot;Operation Closed: &quot;+total);<br>                console.log(&quot;Benefit: &quot;+this.benefit);<br>        }<br>}<br></pre><br><br>Básicamente, se le añade un capital al iniciarse y usa TODO el capital en realizar la operación. La estrategia si queremos diversificar con Enebro es tener muchos brokers con una estrategia asignada a cada uno. Para realizar la compra especificamos el instrumento y el precio actual, para salir igual. Se nor informará en la pantalla del beneficio por la operación y del beneficio acumulado.<br><h2 id="laestrategia">La estrategia</h2><br>Veamos una estrategia sencilla usando la media móvil simple de 15.<br><br><pre class="lang:js decode:true"><br>class Strategy{<br>        constructor(feed,instrument,broker){<br>                this.setup();<br>                this.sma = new SMA(15);<br>                this.feed = feed;<br>                this.instrument = instrument;<br>                this.broker = broker;<br>                this.open = false;<br>                this.broker.strategy = this;<br>        }<br>        onBars(bars){<br>        <br>        }<br>        run(){<br>                var self = this;<br>                this.feed.run(function(bars){<br>                        self.onBars(bars);<br>                });<br>        }<br>        isOpen(){<br>                return this.open;<br>        }<br>}<br>class MMS extends Strategy{<br>        constructor(feed,instrument,broker){<br>                super(feed,instrument,broker);<br>        }<br>        onBars(bars){<br>                var bar = bars[this.instrument];<br>                this.sma.add(this.instrument,bar);<br>                if(!this.isOpen()){<br>                        // Buy<br>                        if(bar.getPrice() &lt; this.sma.get(this.instrument)){<br>                                this.broker.enterLong(this.instrument,bar.getPr$<br>                        }<br>                }else{<br>                        // Sell<br>                        if(bar.getPrice() &gt; this.sma.get(this.instrument)){<br>                                this.broker.exitMarket(this.instrument,bar.getP$<br>                        }<br>                }<br>        }<br>}<br></pre><br><br>Esta estrategia está basada en la media móvil simple, únicamente en eso. El feed provee de datos a la estrategia en la función <code>onBars</code>. Se actualiza el único indicador, la media móvil simple, <strong>SMA</strong>. Cuando se cumple la condición de compra y no hay operaciones abiertas, se entra en largos. Cuando hay operaciones abiertas y hay condición de venta, se vende. Cuando creamos la estrategia y la ejecutamos con <code>run()</code> tenemos que especificar el feed, el instrumento y el bróker.<br><h2 id="losindicadoressma">Los indicadores: SMA</h2><br>Hemos usado el indicador de la media móvil simple. ¿Cómo lo hemos definido?<br><br><pre class="lang:js decode:true"><br>class SMA{<br>        constructor(period){<br>                this.period = period;<br>                this.sma = {};<br>        }<br>        add(instrument,bar){<br>                if(!this.sma[instrument])<br>                        this.sma[instrument] = [];<br>                this.sma[instrument].push(bar);<br>        }<br>        get(instrument){<br>                if(this.sma[instrument].length &lt; this.period +1)<br>                        return 0;<br>                var suma = 0;<br>                for(var i=this.sma[instrument].length - this.period;i&lt;this.sma[$<br>                        suma += parseFloat(this.sma[instrument][i].getPrice());<br>                                        }<br>                return suma/this.period;<br>        }<br>}<br></pre><br><br>Simplemente almacena los datos y los usa para realizar la media cuando sea necesario.<br><h2 id="juntndolotodo">Juntándolo todo</h2><br>Ahora juntamos todo en el archivo <code>index.js</code><br><pre class="lang:js decode:true"><br>var CSVFeed = require(&quot;./feed&quot;).CSVFeed;<br>var UpholdFeed = require(&quot;./feed&quot;).UpholdFeed;<br>var MMS = require(&quot;./strategy&quot;).MMS;<br>var EnebroBroker = require(&quot;./strategy&quot;).EnebroBroker;<br>var UpholdBroker = require(&quot;./strategy&quot;).UpholdBroker;<br><br>var feed = new CSVFeed(&quot;EURUSD&quot;,&quot;DAT_ASCII_EURUSD_M1_201602.csv&quot;);<br>//var feed = new UpholdFeed(&quot;EURUSD&quot;);<br><br>//var broker = new UpholdBroker(8.69);<br>var broker = new EnebroBroker(100);<br><br>var strategy = new MMS(feed,&quot;EURUSD&quot;,broker);<br>strategy.run();<br></pre><br><h2 id="conclusin">Conclusión</h2><br>Espero que esta entrada al menos os sirva de inspiración para diseñar vuestro propio sistema de trading automático. Enebro es un simple experimento, pero si quereis aportar información, sugerir algo o preguntar, simplemente escribid en los comentarios.<br><br>Yo tengo mi propia versión de Enebro operativa, si alguien la quisiera, puede contactar conmigo. Además el plugin de bróker de Uphold, necesario para realizar las operaciones reales de FOREX también está disponible para quien lo quiera.]]></description>
                <comments>https://blog.adrianistan.eu/enebro-robot-automatico-forex-javascript</comments>
                <pubDate>Sat, 12 Mar 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Mapas interactivos en HTML5 con SnapSVG</title>
                <link>https://blog.adrianistan.eu/mapas-interactivos-html5-snapsvg</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/mapas-interactivos-html5-snapsvg</guid>
                <description><![CDATA[HTML5 ha llegado aquí y está para quedarse y puede usarse en prácticamente cualquier sitio. Además según la <a href="https://blog.adrianistan.eu/2016/01/08/bienvenido-2016-bienvenido-kaizen/">encuesta del blog</a> HTML5 era uno de los temas en los que estabais más interesados. Hoy vamos a ver como se puede hacer un mapa interactivo fácilmente, compatible con PCs, tabletas y móviles y veremos como los podemos animar.<br><br><iframe src="https://jsfiddle.net/aarroyoc/wyvjeoef/1/embedded/result,js,html/" width="100%" height="300" frameborder="0" allowfullscreen="allowfullscreen"></iframe><br><blockquote><em>Haz click en la provincia de Valladolid múltiples veces</em></blockquote><br><h2 id="snapsvg">SnapSVG</h2><br>Aquí entra en juego <a href="http://snapsvg.io">SnapSVG</a>. Se trata de una librería para JavaScript financiada y acogida por Adobe. Es una mejora de la ya popular librería para tratar SVG conocida como RaphaëlJS (ambas librerías son del mismo autor). Sin embargo, SnapSVG aporta muchas mejoras respecto a RaphaëlJS. La fundamental es que SnapSVG permite cargar archivos SVG ya existentes.<br><br><img class="alignnone size-large wp-image-105" src="https://files.adrianistan.eu/snapsvg-1024x343.png" alt="snapsvg" width="840" height="281" /><br><h2 id="mapasensvg">Mapas en SVG</h2><br>Actualmente es fácil encontrar mapas en SVG de cualquier territorio. Sin embargo para que sea fácil trabajar con ellos hay que procurar que estén preparados para interactuar con ellos. Es necesario que las etiquetas <code>&lt;path&gt;</code> posean un atributo <code>id</code> y sea fácilmente reconocible. En el caso del mapa de España que hay al principio, el mapa está muy bien organizado. Las provincias empiezan por pr, los enclaves por en y las islas por is. Así que Valladolid es <code>pr_valladolid</code> y Menorca es <code>is_menorca</code>. Encontrar mapas así ya puede ser más difícil pero no imposible.<br><h2 id="primerospasos">Primeros pasos</h2><br>En nuestro HTML creamos una etiqueta <code>&lt;svg&gt;</code> con un <code>id</code>, por ejemplo <code>id=papel</code>. Ya está. Ahora pasamos al JavaScript.<br><br>Primero necesitamos obtener un papel (Paper en la documentación), con la función Snap y un selector CSS obtenemos el papel que ya hemos creado.<br><br><pre class="lang:js decode:true"><br>var s = Snap(&quot;#papel&quot;);<br></pre><br><br>Ahora ya podemos usar todo el poder de SnapSVG, pero si queremos trabajar con un SVG ya existente el procedimiento es un poco distinto.<br><br><pre class="lang:js decode:true"><br>var s = Snap(&quot;#papel&quot;); // Obtenemos el papel<br>Snap.load(&quot;/mapa.svg&quot;,function(f){<br>	// Al cargar el mapa se nos devuelve un fragmento<br>    // los fragmentos contienen elementos de SVG<br>    // Como queremos añadir todos los elementos, los seleccionamos todos, como un único grupo<br>    // otra vez vemos los selectores CSS en acción<br>    var g = f.selectAll(&quot;*&quot;);<br>    // y ahora añadimos el grupo al papel<br>    s.append(g);<br>    <br>    // cuando querramos acceder a un elemento podemos usar un selector CSS<br>    var valladolid = s.select(&quot;#pr_valladolid&quot;);<br>});<br></pre><br><h2 id="atributos">Atributos</h2><br>Podemos alterar los atributos de estilo de SVG. Para quién no los conozca, funcionan igual que las propiedades CSS pero se aplican de manera distinta. Con SnapSVG podemos cambiar esos atributos en tiempo de ejecución. Por ejemplo, el relleno (propiedad <code>fill</code>).<br><br><pre class="lang:js decode:true"><br>s.attr({<br>	fill: &quot;red&quot;<br>});<br>// Cambia el relleno a rojo, afecta a los elementos inferiores, en este caso como es el papel, afecta a todo el SVG.<br></pre><br><h2 id="figurassimples">Figuras simples</h2><br>Podemos añadir figuras simples de manera muy sencilla<br><br><pre class="lang:js decode:true"><br>var rect = s.rect(0,0,100,20).attr({fill: &quot;cyan&quot;});<br>// Creamos un rectángulo de 100x20 en la posición (0,0) con relleno cyan.<br>// Luego lo podemos borrar<br>rect.remove();<br></pre><br><h2 id="eventosyanimaciones">Eventos y animaciones</h2><br>Ahora viene la parte interesante, eventos y animaciones. SnapSVG soporta varios tipos de evento en cada elemento. Veamos el click simple aunque existe doble click, ratón por encima, táctil (aunque click funciona en pantallas táctiles).<br><br><pre class="lang:js decode:true"><br>var murcia = s.select(&quot;#pr_murcia&quot;);<br>murcia.click(function(){<br>	murcia.attr({<br>    	fill: &quot;yellow&quot;<br>    });<br>});<br></pre><br><br><br>Podemos animar los elementos especificando las propiedades que cambian y su tiempo<br><br><pre class="lang:js decode:true"><br>murcia.animate({fill: &quot;purple&quot;},1000);<br></pre><br><br>SnapSVG es muy potente y permite realizar muchas más operaciones, como elementos arrastrables, grupos, patrones, filtros y más. El objetivo, según Adobe, es ser el jQuery de los documentos SVG.<br><br><img class="alignnone size-full wp-image-106" src="https://files.adrianistan.eu/snapsvg-game.png" alt="snapsvg-game" width="841" height="752" /><br><h2 id="escalarimagenautomticamente">Escalar imagen automáticamente</h2><br><img class="alignnone size-full wp-image-128" src="https://files.adrianistan.eu/viewBox.png" alt="viewBox" width="600" height="480" /><br><br>SVG es un formato vectorial así que podemos aumentar el tamaño sin tener que preocuparnos por los píxeles. Sin embargo si simplemente cambias el tamaño del elemento <code>&lt;svg&gt;</code> vía CSS verás que no funciona. Es necesario especificar un atributo <code>viewBox</code> y mantenerlo constante. Básicamente viewBox da las dimensiones reales del lienzo donde se dibuja el SVG. Si cambian <code>width</code> y <code>height</code> y <code>viewBox</code> también entonces la imagen no se escala, simplemente se amplía el área del lienzo. Algunos mapas en SVG no ofrecen viewBox. En ese caso espeficicamos como viewBox el tamaño original del fichero SVG. En el caso de querer ocupar toda la pantalla.<br><br><pre class="lang:js decode:true"><br>s.attr({ viewBox: &quot;0 0 800 600&quot;,width: window.innerWidth, height: window.innerHeight});<br>window.addEventListener(&quot;resize&quot;,function(){<br>	s.attr({ viewBox: &quot;0 0 800 600&quot;,width: window.innerWidth, height: window.innerHeight});<br>});<br></pre><br><h2 id="cordovayandroid">Cordova y Android</h2><br>SnapSVG se puede usar en <a href="http://cordova.apache.org">Apache Cordova</a>. Sin embargo yo he tenido problemas de rendimiento con la configuración por defecto en Android. Para solventar este problema he tenido que:<br><ul><br> 	<li><a href="https://crosswalk-project.org/documentation/cordova.html">Instalar el plugin Crosswalk de Android WebView</a></li><br> 	<li>Desactivar la aceleración por hardware en la aplicación.<br><ul><br> 	<li>En el AndroidManifest.xml poner <code>android:hardwareAccelerated="false"</code></li><br></ul><br></li><br></ul><br>Solo así conseguí un rendimiento decente dentro de Cordova.]]></description>
                <comments>https://blog.adrianistan.eu/mapas-interactivos-html5-snapsvg</comments>
                <pubDate>Sun, 06 Mar 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Ethereum y SmartContracts</title>
                <link>https://blog.adrianistan.eu/ethereum-y-smartcontracts</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/ethereum-y-smartcontracts</guid>
                <description><![CDATA[En estos últimos años han surgido toda una marea tecnológica siguiendo los principios de la cadena de bloques (blockchain en inglés) popularizada por Bitcoin. En un principio se extendieron las criptodivisas usando esta tecnología, las llamadas altcoins. Algunas se han vuelto muy famosas como Litecoin, Dogecoin, Ripple, Feathercoin, StartCoin, ReddCoin, Dash o incluso un intento de criptodivisa española, la PesetaCoin.<br><br>Si revisamos la lista de criptodivisas por capitalización de mercado en <a href="https://coinmarketcap.com/">CoinMarketCap.com</a> veremos que en el puesto número aparece una que no he mencionado, se trata de Ethereum. Pero no adelantemos acontecimientos.<br><br><img class="alignnone size-full wp-image-22" src="https://files.adrianistan.eu/CoinMarketCap.png" alt="CoinMarketCap" width="470" height="532" /><br><br>Hemos dicho que la tecnología de la cadena de bloques se ha usado para diseñar criptodivisas. Sin embergo, recientemente ha aparecido una nueva aplicación de esta tecnología. Los contratos inteligentes (o Smart Contracts).<br><h2 id="qusonloscontratosinteligentes">¿Qué son los contratos inteligentes?</h2><br>Podemos definir los contratos inteligentes como un tipo de aplicación informática que se encargan de ejecutar una cierta acción si se cumple la condición especificada. Podemos pensar en ellos como un contrato con cláusulas específicas según la casuística. Los contratos inteligentes además serían fácilmente verificables y a su vez seguros. La idea es que dadas unas condiciones, se ejecuten las acciones especificadas, sin ninguna excepción. El concepto de los contratos inteligentes surgió de manos de <a href="http://web.archive.org/web/20160831070942/http://szabo.best.vwh.net/smart_contracts_idea.html">Nick Szabo</a> en la década de los noventa. Y ahora ya tenemos la primera implementación de aquellas ideas en <a href="https://www.ethereum.org/">Ethereum</a>. Un ejemplo muy sencillo de contrato inteligente es una apuesta con un amigo en un partido de fútbol, cada uno apuesta por un equipo y deposita el dinero en el contrato. Cuando el partido haya finalizado el contrato ejecutará la cláusula correspondiente y enviará al dinero al afortunado.<br><h2 id="quesethereum">¿Qué es Ethereum?</h2><br><blockquote><em>Si piensas en el Bitcoin como una hoja de cálculo global, piensa en Ethereum como un sistema operativo global</em></blockquote><br><img class="alignnone size-full wp-image-35" src="https://files.adrianistan.eu/EthereumFrontier.png" alt="EthereumFrontier" width="415" height="411" /><br><br>Ethereum es una implementación de los smart contracts basada en la cadena de bloques. Es descentralizado, como Bitcoin. Los aplicaciones (los contratos inteligentes) en Ethereum se ejecutan sin nisiquiera la posibilidad de caída de la red, censura, fraude o intervención de terceras partes. Los contratos inteligentes simplemente se ejecutan, es <strong>imposible</strong> que no se ejecuten. Esa es la gran ventaja de Ethereum respecto al Internet como lo conocíamos antes.<br><br>Las aplicaciones en Ethereum se suben a la cadena de bloques y se ejecutan bajo demanda, con una potencia no muy elevada (piensa en un smartphone de 1999) pero con una cantidad de memoria y una capacidad de almacenamiento permanente ilimitados. Eso no significa que cualquiera pueda hacer lo que quiera con un programa, pues los contratos pueden estar diseñados para ignorar las peticiones hechas desde usuarios desconocidos. En último término, el objetivo de Ethereum es proveer una computación 100% determinista.<br><br><img class="alignnone size-large wp-image-36" src="https://files.adrianistan.eu/EthereumLogo-1024x576.png" alt="EthereumLogo" width="840" height="473" /><br><h2 id="cmofunciona">¿Cómo funciona?</h2><br>Usar Ethereum no es gratis, el sistema operativo global necesita <em>combustible</em>. Ese combustible es Ether, aunque en muchos sitios se le llama directamente Ethereum por estar ligado a la plataforma. Ether es una criptodivisa al estilo Bitcoin, pero se puede gastar directamente en ejecutar contratos inteligentes en Ethereum. Al igual que en Bitcoin, en Ethereum hay mineros, que ejecutan los contratos para comprobar que todos obtienen el mismo resultado. Esos mineros reciben su recompensa en Ether que pueden usar o vender en sitios como <a href="https://shapeshift.io/">ShapeShift</a>.<br><br>Además necesitaremos un cliente para subir y pedir la ejecución de los contratos inteligentes. Hay muchos, voy a hablar de los cuatro más importantes.:<br><ul><br> 	<li>Eth: el cliente en C++</li><br> 	<li>Geth: el cliente en Go (recomendado)</li><br> 	<li>Web3.js: una unión entre el navegador y Ethereum usando la interfaz RPC de otro cliente Ethereum</li><br> 	<li>Mist: se trata de un navegador basado en Electrum (o sea, Chromium) que integra las funciones de Ethereum. Podemos interactuar con las DApps directamente si usamos este navegador. Veremos que son las DApps más adelante.</li><br></ul><br>Desde Geth podemos sincronizarnos con la red Ethereum, minar para ganar Ether, ejecutar contratos en la red y subirlos.<br><br>Podemos ver como se ejecuta un contrato inteligente en acción en <a href="https://etherdice.io/">EtherDice</a>, un simple juego de apuestas con dado.<br><pre><code>eth.sendTransaction({from: eth.accounts[0], value: web3.toWei(1, 'ether'), to: '0x2faa316fc4624ec39adc2ef7b5301124cfb68777', gas: 500000}) </code></pre><br>Esta orden se introduce dentro de Geth. Básicamente está realizando un traspaso de fondos desde nuestra cuenta principal (eth.accounts[0], aunque se puede especificar otra si nos sabemos la dirección), el valor de la transacción que es la cantidad de Ether a traspasar. Ether tiene muchos submúltiplos, en este caso usa el Wei. 1 ether = 1000000000000000000 wei. Se especifica la dirección de destino y además el máximo de gas que estaríamos dispuesto a perder en la ejecución (no es posible cuanto va a costar una ejecución). Este valor máximo es el producto del gas por el precio del gas y representa el tope de weis que puede consumir el contrato antes de que se cancele. Con contratos muy probados valdría cualquier valor, pero si estás desarrollando un contrato de vendrá muy bien para que una programación errónea no liquide todos tus fondos antes de tiempo.<br><br>Bien, este ejemplo es muy sencillo. De manera más genérica usaríamos la función <strong>eth.contract</strong><br><pre><code>var greeter = eth.contract(ABI).at(Direccion); <br>greeter.greet(VALORES DE INPUT,{from: TuDireccion, gas: 50000}); </code></pre><br>Siendo la ABI la definición de la interfaz para poder interactuar con el contrato y la dirección es donde reside el contrato en sí. Luego llamamos a la función greet dentro del contrato, puede aceptar parámetros de entrada. Todo esto esta muy bien pero no hemos visto como son realmente los contratos todavía. Un ejemplo muy bueno es <a href="http://etheria.world/">Etheria</a><br><br><img class="alignnone size-full wp-image-37" src="https://files.adrianistan.eu/Etheria.png" alt="Etheria" width="728" height="381" /><br><h2 id="solidityylamquinavirtual">Solidity y la máquina virtual</h2><br>Los contratos se ejecutan en una máquina virtual llamada EVM (Ethereum Virtual Machine). Esta máquina virtual es Turing completa pero para evitar un colapso (bucles infinitos) tiene en cuenta el gas. Las operaciones en la EVM son lentas, porque cada contrato es ejecutado simultaneamente en el resto de nodos de la red, siendo el resultado final un resultado de consenso de la red. Se han diseñado varios lenguajes que compilan a EVM, pero sin duda el más popular es Solidity.<br><br><pre class="lang:default decode:true"><br>contract mortal {<br>    /* Define variable owner of the type address*/<br>    address owner;<br><br>    /* this function is executed at initialization and sets the owner of the contract */<br>    function mortal() { owner = msg.sender; }<br><br>    /* Function to recover the funds on the contract */<br>    function kill() { if (msg.sender == owner) suicide(owner); }<br>}<br><br>contract greeter is mortal {<br>    /* define variable greeting of the type string */<br>    string greeting;<br><br>    /* this runs when the contract is executed */<br>    function greeter(string _greeting) public {<br>        greeting = _greeting;<br>    }<br><br>    /* main function */<br>    function greet() constant returns (string) {<br>        return greeting;<br>    }<br>}<br></pre><br><br>Este sería un ejemplo de Greeter en Solidity. No voy a explicar la programación en Solidity, ni como se inician los contratos. Si hay demanda popular explicaré como se suben los programas y se inicializan.<br><h2 id="dapps">DApps</h2><br>Decentralized Apps, con la tecnología de Ethereum ha surgido un nuevo concepto. Aplicaciones web que se separan del concepto tradicional de cliente-servidor y se ejecutan de manera descentralizada. Estas aplicaciones, aunque siguen necesitando Internet pueden funcionar sin un servidor central si nuestro ordenador dispone de un nodo de Ethereum. Esto es precisamente lo que hace el navegador Mist. Otro aprovechamiento más tradicional de las DApps es dejar un servidor central que corra como nodo de Ethereum y tenga una IP asignada. Sin embargo este servidor central puede ser muy ligero, pues solo sirve de puerta de entrada a la red Ethereum. Este aprovechamiento funcionaría en navegadores tradicionales siempre que los gastos de la red corran a cuenta del administrador de la app. Un ejemplo de DApp que requiere usar el navegador Mist es <a href="http://ethereumwall.com/">EthereumWall</a>, la aplicación usa nuestros fondos para su funcionamiento y aunque tiene un servidor central estático para entregar los archivos HTML y el JavaScript, esto no sería necesario pues la lógica la hace la red Ethereum con nuestro nodo local en Mist.<br><br><img class="alignnone size-large wp-image-76" src="https://files.adrianistan.eu/Mist-1024x576.jpg" alt="Mist" width="840" height="473" /><br><h2 id="conclusin">Conclusión</h2><br>¿Qué os parecen los contratos inteligentes? ¿Qué os parece la plataforma Ethereum? ¿Tendrá futuro o es una moda pasajera? ¿Crees que puede revolucionar la manera de pensar la web? Comenta, quiero saber tu opinión.<br><br>Para más información no dudes en consultar el sitio oficial de <a href="https://www.ethereum.org">Ethereum</a> y <a href="https://ethereum.gitbooks.io">el libro oficial</a><br><br>Si crees que lo merece puedes enviarme: BTC(1A2j8CwiFEhQ4Uycsjhr3gQPbJxFk1LRmM), LTC(LXkefu8xYwyD7CcxWRfwHhSRTdk6Sp38Kt), DOGE(D7fvbHocEGS7PeexBV23ktWjgVL1y9RnoK), ReddCoin(RsHAsr6PVs8y4f5pGLS2cApcGpgw15TwUJ)]]></description>
                <comments>https://blog.adrianistan.eu/ethereum-y-smartcontracts</comments>
                <pubDate>Wed, 02 Mar 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>¿Cómo programar en C (en 2016)?</title>
                <link>https://blog.adrianistan.eu/programar-c-2016</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/programar-c-2016</guid>
                <description><![CDATA[<blockquote><em>Este artículo es una traducción del artículo <a href="http://matt.sh/howto-c">How to C in 2016</a>. Todo el contenido aparece originalmente en aquel artículo, yo solo me he limitado a traducirlo.</em></blockquote><br><img class="alignnone size-full wp-image-18" src="https://files.adrianistan.eu/c.jpg" alt="c" width="735" height="250" /><br><br>La primera regla de C es no escribir en C si puedes evitarlo.<br><br>Si te ves obligado a escribir en C, deberías seguir las reglas modernas.<br><br>C ha estado con nosotros desde principios de los 70. La gente a "aprendido C" en numerosos puntos de su evolución, pero el conocimiento normalmente se para después de aprender. Así pues todo el mundo piensa diferente sobre C según el año en que empezaron a aprenderlo.<br><br>Es importante no quedarse paralizado en las "cosas que aprendí en los 80/90" cuando programas en C.<br><br>Esta página asume que estás en una plataforma moderna, con estándares modernos y no tienes que mantener una compatibilidad con sistemas antiguos muy elevada. No debemos estar atados a estándares anticuados solo porque algunas compañías rechacen actualizar sistemas con más de 20 años de antigüedad.<br><h2 id="preliminar">Preliminar</h2><br>Standard C99 (C99 significa "Estándar C de 1999"; C11 significa "Estándar C de 2011", así que C11 &gt; C99)<br><ul><br> 	<li>clang, por defecto<br><ul><br> 	<li>C99 es la implementación de C por defecto en clang, no necesita opciones extra</li><br> 	<li>Sin embargo esta implementación no es realmente estándar. Si quieres forzar el estándar, usa <code>-std=c99</code></li><br> 	<li>Si quieres usar C11, debes especificar <code>-std=c11</code></li><br> 	<li>clang compila el código fuente más rápidamente que gcc</li><br></ul><br></li><br> 	<li>gcc necesita que especifiques <code>-std=c99</code> o <code>-std=c11</code><br><ul><br> 	<li>gcc compila más lentamente pero <em>a veces</em> genera ejecutables más rápidos</li><br> 	<li>gcc-5 establece por defecto <code>-std=gnu11</code>, así que debes seguir especificando una versión estándar <code>c99</code> o <code>c11</code>.</li><br></ul><br></li><br></ul><br>Optimizaciones<br><ul><br> 	<li><code>-O2, -O3</code><br><ul><br> 	<li>generalmente querrás <code>-O2</code>, pero algunas veces querrás <code>-O3</code>. Prueba tu código con ambos niveles (y entre distintos compiladores) y mantente con los ejecutables más eficientes y rápidos.</li><br></ul><br></li><br> 	<li><code>-Os</code><br><ul><br> 	<li><code>-Os</code> ayuda si te preocupa la eficiencia de la caché (que debería)</li><br></ul><br></li><br></ul><br>Advertencias<br><ul><br> 	<li><code>-Wall -Wextra -pedantic</code><br><ul><br> 	<li>las nuevas versiones de los compiladores tienen <code>-Wpedantic</code>, pero todavía aceptan el antiguo <code>-pedantic</code> por cuestiones de compatibilidad.</li><br> 	<li>durante las pruebas deberías añadir <code>-Werror</code> y <code>-Wshadow</code> en todas tus plataformas</li><br> 	<li>puede ser peliagudo enviar a producción con <code>-Werror</code> porque cada plataforma y cada compilador y cada librería pueden emitir distintas advertencias. Probablemente no querrás terminar la compilación entera de un usuario porque su versión de GCC en una plataforma que nunca habías visto se queja de manera nueva y sorprendente.</li><br> 	<li>algunas opciones más sofisticadas son <code>-Wstrict-overflow -fno-strict-aliasing</code></li><br> 	<li>especifica <code>-fno-strict-aliasing</code> o estate seguro de que solo accedes a los objetos con el tipo que tuvieron en su definición. Como mucho código en C ya existente se salta lo último es mucho más seguro usar <code>-fno-strict-aliasing</code> particularmente si no controlas todo el código que debes compilar.</li><br> 	<li>ahora mismo, clang reporta alguna sintaxis válida como advertencia, así que debes añadir <code>-Wno-missing-field-initializers</code></li><br> 	<li>GCC resolvió este problema después de GCC 4.7</li><br></ul><br></li><br></ul><br>Compilando<br><ul><br> 	<li>Unidades de compilación<br><ul><br> 	<li>La manera más común de compilar proyectos en C es generar un fichero objeto de cada fichero fuente y unirlo todos al final. Este procedimiento es muy bueno para el desarrollo incremental, pero no lo es para el rendimiento y la optimización. El compilador no puede detectar optimizaciones entre archivos con este método.</li><br></ul><br></li><br> 	<li>LTO - Link Time Optimization<br><ul><br> 	<li>LTO arregla el problema de las unidades de compilación generando además una representación intermedia que puede ser sujeta de optimizaciones entre archivos. Este sistema ralentiza el tiempo de enlazado significativamente pero <code>make -j</code> puede ayudar.</li><br> 	<li><a href="http://llvm.org/docs/LinkTimeOptimization.html">clang LTO</a> (<a href="http://llvm.org/docs/GoldPlugin.html">guía</a>)</li><br> 	<li><a href="https://gcc.gnu.org/onlinedocs/gccint/LTO-Overview.html">gcc LTO</a></li><br> 	<li>Ahora mismo, 2016, clang y gcc soportan LTO simplemente añadiendo <code>-flto</code> en las opciones tanto de compilación como de enlazado.</li><br> 	<li><code>LTO</code> todavía necesita asentarse. A veces, si tu programa tiene código que no usas directamente pero alguna librería sí, LTO puede borrarlo, porque detecta que en tu código no se hace nunca una llamada a esa función.</li><br></ul><br></li><br></ul><br>Arquitectura<br><ul><br> 	<li><code>-march=native</code><br><ul><br> 	<li>Le da al compilador permiso para usar todas las características de tu CPU</li><br> 	<li>otra vez, compara el funcionamiento con los distintos tipos de optimización y que no tengan efectos secundarios.</li><br></ul><br></li><br> 	<li><code>msse2</code> y <code>-msse4.2</code> pueden ser útiles si necesitas características que no están disponibles en el sistema desde el que compilas.</li><br></ul><br><h2 id="escribiendocdigo">Escribiendo código</h2><br><h3 id="tipos">Tipos</h3><br>Si te encuentras escribiendo <code>char</code> o <code>int</code> o <code>short</code> o <code>long</code> o <code>unsigned</code>, lo estás haciendo mal.<br><br>Para los programas modernos deberías incluir <code>#include &lt;stdint.h&gt;</code> y usar los tipos <em>estándar</em>.<br><br>Los tipos estándar comunes son:<br><ul><br> 	<li><code>int8_t</code>, <code>int16_t</code>, <code>int32_t</code>, <code>int64_t</code> - enteros con signo</li><br> 	<li><code>uint8_t</code>, <code>uint16_t</code>, <code>uint32_t</code>, <code>uint64_t</code> - enteros sin signo</li><br> 	<li><code>float</code> - coma flotante de 32 bits</li><br> 	<li><code>double</code> - coma flotante de 64 bits</li><br></ul><br>Te darás cuenta que ya no tenemos <code>char</code>. <code>char</code> está malinterpretado en C.<br><br>Los desarrolladores han abusado de <code>char</code> para representar un byte incluso cuando hacen operaciones sin signo. Es mucho más limpio usar <code>uint8_t</code> para representar un único byte sin signo y <code>uint8_t *</code> para representar una secuencia de bytes sin signo.<br><h4 id="unaexcepcinanuncachar">Una excepción a nunca-<code>char</code></h4><br>El <em>único</em> uso aceptable de <code>char</code> en 2016 es si una API ya existente necesita <code>char</code> (por ejemplo, <code>strncat</code>, <code>printf</code>,...) o si estás inicializando una cadena de texto de solo lectura (<code>const char *hello = "hello";</code>) porque el tipo de C para cadenas de texto sigue siendo <code>char *</code><br><br>Además, en C11 tenemos soporte Unicode nativo y el tipo para cadenas UTF-8 sigue siendo <code>char *</code> incluso para secuencias multibyte como <code>const char *abcgrr = u8"abc?";</code>.<br><h4 id="elsigno">El signo</h4><br>A estas alturas de la película no deberías escribir <code>unsigned</code> nunca en tu código. Podemos escribir sin usar la fea convención de C para tipos multi-palabra que restan legibilidad. ¿Quién quiere escribir <code>unsigned long long int</code> cuando puede escribir <code>uint64_t</code>? Los tipos de <code>&lt;stdint.h&gt;</code> son más explícitos, más exactos en su significado y son más compactos en su escritura y su legibilidad.<br><br>Pero podrías decir, "¡Necesito hacer cast a punteros a <code>long</code> para realizar aritmética de punteros sucia!"<br><br>Podrías decirlo. Pero estás equivocado.<br><br>El tipo correcto para aritmética de punteros es <code>uintptr_t</code> definido en <code>&lt;stddef.h&gt;</code>.<br><br>En vez de:<br><pre class="lang:c++ decode:true"><br>long diff = (long)ptrOld - (long)ptrNew;<br></pre><br><br>Usa:<br><br><pre class="lang:c++ decode:true"><br>ptrdiff&lt;em&gt;t diff = (uintptr&lt;/em&gt;t)ptrOld - (uintptr_t)ptrNew;<br></pre><br><br>Además:<br><br><br><pre class="lang:c++ decode:true"><br>printf(&quot;%p is unaligned by %&quot; PRIuPTR &quot; bytes\&quot;,(void *)p, ((uintptr_t)somePtr &amp;amp; (sizeof(void *) - 1)));<br></pre><br><h4 id="tiposdependientesdelsistema">Tipos dependientes del sistema</h4><br>Sigues argumentando, "¡en una plataforma de 32 bits quiero longs de 32 bits y en una de 64 bits quiero longs de 64 bits!"<br><br>Si nos saltamos la idea de que estás introduciendo deliberadamente código que dificulta la comprensión del código al tener tamaños distintos dependiendo de la plataforma, aún no tendrías necesidad de usar <code>long</code>.<br><br>En estas situaciones debes usar <code>intptr_t</code> que se define según la plataforma en que te encuentres.<br><br>En plataformas de 32 bits, <code>intptr_t</code> es <code>int32_t</code>.<br><br>En plataformas de 64 bits, <code>intptr_t</code> es <code>int64_t</code>.<br><br><code>intprt_t</code> también tiene una versión sin signo <code>uintptr_t</code>.<br><br>Para almacenar diferencias entre punteros, tenemos el <code>ptrdiff_t</code>.<br><h4 id="mximacapacidad">Máxima capacidad</h4><br>¿Quieres tener el entero con mayor capacidad de tu sistema?<br><br>La gente tiene a usar el más grande que conozca, en este caso <code>uint64_t</code> nos podrá almacenar el número más grande. Pero hay una manera más correcta de garantizar que podrá contener cualquier otro valor que se esté utilizando en el programa.<br><br>El contenedor más seguro para cualquier entero es <code>intmax_t</code> (también <code>uintmax_t</code>). Puedes asignar cualquier entero con signo a <code>intmax_t</code> sin pérdida de precisión. Puedes asignar cualquier entero sin signo a <code>uintmax_t</code> sin pérdida de precisión.<br><h4 id="eseotrotipo">Ese otro tipo</h4><br>Otro tipo que depende del sistema y es usado comúnmente es <code>size_t</code>.<br><br><code>size_t</code> se define como "un entero capaz de contener el mayor tamaño de memoria disponible".<br><br>En el lado práctico, <code>size_t</code> es el tipo que devuelve el operador <code>sizeof</code>.<br><br>En cualquier caso, la definición de <code>size_t</code> es prácticamente la misma que la de <code>uintptr_t</code> en todas las plataformas modernas.<br><br>También existe <code>ssize_t</code> que es <code>size_t</code> con signo y que devuelve <code>-1</code> en caso de error. (Nota: <code>ssize_t</code> es POSIX así que no se aplica esto en Windows).<br><br>Así que, ¿debería usar <code>sisze_t</code> para aceptar tamaños dependientes del sistema en mis funciones? Sí, cualquier función que acepte un número de bytes puede usar <code>size_t</code>.<br><br>También lo puedes usar en malloc, y <code>ssize_t</code> es usado en <code>read()</code> y <code>write()</code> (solo en sistemas POSIX).<br><h4 id="mostrandotipos">Mostrando tipos</h4><br>Nunca debes hacer cast para mostrar el valor de los tipos. Usa siempre los especificadores adecuados.<br><br>Estos incluyen, pero no están limitados a:<br><ul><br> 	<li><code>size_t</code> - <code>%zu</code></li><br> 	<li><code>ssize_t</code> - <code>%zd</code></li><br> 	<li><code>ptrdiff_t</code> - <code>%td</code></li><br> 	<li>valor del puntero - <code>%p</code> (muestra el valor en hexadecimanl, haz cast a <code>(void *)</code> primero)</li><br> 	<li>los tipos de 64 bits deben usar las macros <code>PRIu64</code> (sin signo) y <code>PRId64</code> (con signo)<br><ul><br> 	<li>es imposible especificar un valor correcto multiplataforma sin la ayuda de estas macros</li><br></ul><br></li><br> 	<li><code>intptr_t</code> - <code>"%" PRIdPTR</code></li><br> 	<li><code>uintptr_t</code> - <code>"%" PRIuPTR</code></li><br> 	<li><code>intmax_t</code> - <code>"%" PRIdMAX</code></li><br> 	<li><code>uintmax_t</code> - <code>"%" PRIuMAX</code></li><br></ul><br>Recordar que <code>PRI*</code> son macros, y las macros se tienen que expandir, no pueden estar dentro de una cadena de texto. No puedes hacer:<br><br><pre class="lang:c++ decode:true"><br>printf(&quot;Local number: %PRIdPTR\n\n&quot;, someIntPtr);<br></pre><br><br>deberías usar:<br><br><pre class="lang:c++ decode:true"><br>printf(&quot;Local number: %&quot; PRIdPTR &quot;\n\n&quot;, someIntPtr);<br></pre><br><br>Tienes que poner el símbolo '%' dentro de la cadena de texto, pero el especificador fuera.<br><h3 id="c99permitedeclaracionesdevariablesencualquiersitio">C99 permite declaraciones de variables en cualquier sitio</h3><br>Así que no hagas esto:<br><br><pre class="lang:c++ decode:true"><br>void test(uint8_t input) {<br>    uint32_t b;<br><br>    if (input &gt; 3) {<br>        return;<br>    }<br><br>    b = input;<br>}<br>[/cpp+<br><br>haz esto<br><br><pre class="lang:c++ decode:true"><br>void test(uint8_t input) {<br>    if (input &gt; 3) {<br>        return;<br>    }<br><br>    uint32_t b = input;<br>}<br></pre><br><br>Aunque si tienes un bucle muy exigente (un <em>tight loop</em>) las declaraciones a mitad de camino pueden ralentizar el bucle.<br><h3 id="c99permitealosbuclesfordeclararloscontadoresenlamismalnea">C99 permite a los bucles <code>for</code> declarar los contadores en la misma línea</h3><br>Así que no hagas esto<br><br><pre class="lang:c++ decode:true"><br>    uint32_t i;<br><br>    for (i = 0; i &lt; 10; i++)<br></pre><br><br>Haz esto:<br><br><pre class="lang:c++ decode:true"><br>for (uint32_t i = 0; i &lt; 10; i++)<br></pre><br><h3 id="lamayoradecompiladoressoportanpragmaonce">La mayoría de compiladores soportan <code>#pragma once</code></h3><br>Así que no hagas esto:<br><br><pre class="lang:c++ decode:true"><br><br>#ifndef PROJECT_HEADERNAME<br>#define PROJECT_HEADERNAME<br>.<br>.<br>.<br>#endif /* PROJECT_HEADERNAME */<br></pre><br><br>haz esto:<br><br><pre class="lang:c++ decode:true"><br>#pragma once<br></pre><br><br><code>#pragma once</code> le dice al compilador que solo incluya el archivo de cabecera una vez y no necesitas escribir esas tres líneas para evitarlo manualmente. Este pragma esta soportado por todos los compiladores modernos en todas las plataformas y está recomendado por encima de nombrar manualmente las cláusulas.<br><br>Para más detalles, observa la lista de compiladores que lo soportan en <a href="https://en.wikipedia.org/wiki/Pragma_once">pragma once</a><br><h3 id="cpermitelainicializacinestticadearraysyaasignadosmemoria">C permite la inicialización estática de arrays ya asignados memoria</h3><br>Así que no hagas:<br><br><pre class="lang:c++ decode:true"><br>    uint32_t numbers[64];<br>    memset(numbers, 0, sizeof(numbers));<br></pre><br><br>Haz esto:<br><br><pre class="lang:c++ decode:true"><br>uint32_t numbers[64] = {0};<br></pre><br><br><h3 id="cpermitelainicializacinestticadestructsyaasignadosenmemoria">C permite la inicialización estática de structs ya asignados en memoria</h3><br>Así que no hagas esto:<br><br><pre class="lang:c++ decode:true"><br>    struct thing {<br>        uint64_t index;<br>        uint32_t counter;<br>    };<br><br>    struct thing localThing;<br><br>    void initThing(void) {<br>        memset(&amp;localThing, 0, sizeof(localThing));<br>    }<br></pre><br><br>Haz esto:<br><br><pre class="lang:c++ decode:true"><br>    struct thing {<br>        uint64_t index;<br>        uint32_t counter;<br>    };<br><br>    struct thing localThing = {0};<br></pre><br><br><strong>NOTA IMPORTANTE:</strong> Si tu estructura tiene padding (relleno extra para coincidir con el alineamiento del procesador, todas por defecto en GCC, <code>__attribute__((__packed__))</code> para desactivar este comportamiento), el método de <code>{0}</code> no llenará de ceros los bits de padding. Si necesitases rellenar todo de ceros, incluido los bits de padding, deberás seguir usando <code>memset(&amp;localThing, 0, sizeof(localThing))</code>.<br><br>Si necesitas reinicializar un struct puedes declarar un struct global a cero para asignar posteriormente.<br><br><pre class="lang:c++ decode:true"><br>    struct thing {<br>        uint64_t index;<br>        uint32_t counter;<br>    };<br><br>    static const struct thing localThingNull = {0};<br>    .<br>    .<br>    .<br>    struct thing localThing = {.counter = 3};<br>    .<br>    .<br>    .<br>    localThing = localThingNull;<br></pre><br><h3 id="c99permitearraysdelongitudvariablevla">C99 permite arrays de longitud variable (VLA)</h3><br>Así que no hagas esto:<br><br><pre class="lang:c++ decode:true"><br>    uintmax_t arrayLength = strtoumax(argv[1], NULL, 10);<br>    void *array[];<br><br>    array = malloc(sizeof(*array) * arrayLength);<br><br>    /* remember to free(array) when you're done using it */<br></pre><br><br>Haz esto:<br><br><pre class="lang:c++ decode:true"><br>    uintmax_t arrayLength = strtoumax(argv[1], NULL, 10);<br>    void *array[arrayLength];<br><br>    /* no need to free array */<br></pre><br><br><strong>NOTA IMPORTANTE:</strong> Los VLA suelen situarse en el stack, junto a los arrays normales. Así que si no haces arrays de 3 millones de elementos normalmente, tampoco los hagas con esta sintaxis. Estos no son listas escalables tipo Python o Ruby. Si especificas una longitud muy grande en tiempo de ejecución tu programa podría empezar a hacer cosas raras. Los VLA están bien para situaciones pequeñas, de un solo uso y no se puede confiar en que escalen correctamente.<br><br>Hay gente que considera la sintaxis de VLA un antipatrón puesto que puede cerrar tu programa fácilmente.<br><br>NOTA: Debes estar seguro que <code>arrayLength</code> tiene un tamaño adecuado (menos de un par de KB se te darán para VLA). No puede asignar arrays <em>enormes</em> pero en casos concretos, es mucho más sencillo usar las capacidades de C99 VLA en vez de pringarse con malloc/free.<br><br>NOTA DOBLE: como puedes ver no hay ninguna verificación de entrada al usar VLA, así que cuida mucho el uso de las VLA.<br><h3 id="c99permiteindicarparmetrosdepunterosquenosesolapan">C99 permite indicar parámetros de punteros que no se solapan</h3><br>Mira la palabra reservada <a href="https://en.wikipedia.org/wiki/Restrict">restrict</a> (a veces, <code>__restrict</code>)<br><h3 id="tiposdelosparmetros">Tipos de los parámetros</h3><br>Si una función acepta arbitrariamente datos y longitud, no restrinjas el tipo del parámetro.<br><br>Así que no hagas:<br><pre class="lang:c++ decode:true"><br>void processAddBytesOverflow(uint8_t *bytes, uint32_t len) {<br>    for (uint32_t i = 0; i &lt; len; i++) {<br>        bytes[0] += bytes[i];<br>    }<br>}<br></pre><br><br>Haz esto:<br><br><pre class="lang:c++ decode:true"><br>void processAddBytesOverflow(void *input, uint32_t len) {<br>    uint8_t *bytes = input;<br><br>    for (uint32_t i = 0; i &lt; len; i++) {<br>        bytes[0] += bytes[i];<br>    }<br>}<br></pre><br><br>Los tipos de entrada definen la interfaz de tu código, no lo que tu código hace con esos parámetros. La interfaz del código dice "aceptar un array de bytes y una longitud", así que no quieres restringirles usar solo <code>uint8_t</code>. Quizá tus usuarios quieran pasar <code>char *</code> o algo más inesperado.<br><br>Al declarar el tipo de entrada como <code>void *</code> y haciendo cast dentro de tu función, los usuarios ya no tienen que pensar en abstracciones <em>dentro</em> de tu librería.<br><br>Algunos lectores afirman que podría haber problemas de alineamiento con este ejemplo, pero como estamos accediendo a los bytes uno por uno no hay problema en realidad. Si por el contrario tuviéramos tipos más grandes tendríamos que vigilar posibles problemas de alineamiento, mirar <a href="https://www.kernel.org/doc/Documentation/unaligned-memory-access.txt">Unaligned Memory Access</a><br><h3 id="parmetrosdedevolucin">Parámetros de devolución</h3><br>C99 nos da el poder de usar <code>&lt;stdbool.h&gt;</code> que define <code>true</code> como <code>1</code> y <code>false</code> como <code>0</code>.<br><br>Para valores de éxito/error, las funciones deben devolver <code>true</code> o <code>false</code>, nunca un entero especificando manualmente <code>1</code> y <code>0</code> (o peor, <code>1</code> y <code>-1</code> (¿o era <code>0</code> éxito y <code>1</code> error? ¿o era <code>0</code> éxito y <code>-1</code> error?))<br><br>Si una función modifica el valor de un parámetro de entrada, no lo devuelvas, usa dobles punteros.<br><br>Así que no hagas:<br><br><pre class="lang:c++ decode:true"><br>void *growthOptional(void *grow, size_t currentLen, size_t newLen) {<br>    if (newLen &gt; currentLen) {<br>        void *newGrow = realloc(grow, newLen);<br>        if (newGrow) {<br>            /* resize success */<br>            grow = newGrow;<br>        } else {<br>            /* resize failed, free existing and signal failure through NULL */<br>            free(grow);<br>            grow = NULL;<br>        }<br>    }<br><br>    return grow;<br>}<br></pre><br><br>Haz esto:<br><pre class="lang:c++ decode:true"><br>/* Return value:<br> *  - 'true' if newLen &gt; currentLen and attempted to grow<br> *    - 'true' does not signify success here, the success is still in '*_grow'<br> *  - 'false' if newLen &lt;= currentLen */<br>bool growthOptional(void **_grow, size_t currentLen, size_t newLen) {<br>    void *grow = *_grow;<br>    if (newLen &gt; currentLen) {<br>        void *newGrow = realloc(grow, newLen);<br>        if (newGrow) {<br>            /* resize success */<br>            *_grow = newGrow;<br>            return true;<br>        }<br><br>        /* resize failure */<br>        free(grow);<br>        *_grow = NULL;<br><br>        /* for this function,<br>         * 'true' doesn't mean success, it means 'attempted grow' */<br>        return true;<br>    }<br><br>    return false;<br>}<br></pre><br><br>O incluso mejor:<br><br><pre class="lang:c++ decode:true"><br>typedef enum growthResult {<br>    GROWTH_RESULT_SUCCESS = 1,<br>    GROWTH_RESULT_FAILURE_GROW_NOT_NECESSARY,<br>    GROWTH_RESULT_FAILURE_ALLOCATION_FAILED<br>} growthResult;<br><br>growthResult growthOptional(void **_grow, size_t currentLen, size_t newLen) {<br>    void *grow = *_grow;<br>    if (newLen &gt; currentLen) {<br>        void *newGrow = realloc(grow, newLen);<br>        if (newGrow) {<br>            /* resize success */<br>            *_grow = newGrow;<br>            return GROWTH_RESULT_SUCCESS;<br>        }<br><br>        /* resize failure, don't remove data because we can signal error */<br>        return GROWTH_RESULT_FAILURE_ALLOCATION_FAILED;<br>    }<br><br>    return GROWTH_RESULT_FAILURE_GROW_NOT_NECESSARY;<br>}<br></pre><br><h3 id="formato">Formato</h3><br>El estilo del código es muy importante.<br><br>Si tu proyecto tiene una guía de formato de 50 páginas, nadie te ayudará, pero si tu código tampoco se puede leer, nadie querrá ayudarte.<br><br>La solución es usar <strong>siempre</strong> un programa para formatear el código.<br><br>El único formateador de código usable en el 2016 es <a href="http://clang.llvm.org/docs/ClangFormat.html">clang-format</a>. clang-format tiene los mejores ajustes por defecto y sigue en desarrollo activo.<br><br>Aquí está el script que uso para formatear mi código:<br><br><pre class="lang:default decode:true"><br>#!/usr/bin/env bash<br><br>clang-format -style=&quot;{BasedOnStyle: llvm, IndentWidth: 4, AllowShortFunctionsOnASingleLine: None, KeepEmptyLinesAtTheStartOfBlocks: false}&quot; &quot;$@&quot;<br></pre><br><br>Luego lo llamo<br><pre><code>./script.sh -i *.{c,h,cc,cpp,hpp,cxx} </code></pre><br>La opción <code>-i</code> indica que sobrescriba los archivos con los cambios que realice, en vez de generar nuevos archivos o crear copias.<br><br>Si tienes muchos archivos, puedes hacer la operación en paralelo<br><br><pre class="lang:default decode:true"><br>#!/usr/bin/env bash<br><br># note: clang-tidy only accepts one file at a time, but we can run it<br>#       parallel against disjoint collections at once.<br>find . \( -name \*.c -or -name \*.cpp -or -name \*.cc \) |xargs -n1 -P4 cleanup-tidy<br><br># clang-format accepts multiple files during one run, but let's limit it to 12<br># here so we (hopefully) avoid excessive memory usage.<br>find . \( -name \*.c -or -name \*.cpp -or -name \*.cc -or -name \*.h \) |xargs -n12 -P4 cleanup-format -i<br></pre><br><br>Y ahora el contenido del script cleanup-tidy aquí.<br><br><pre class="lang:default decode:true"><br>#!/usr/bin/env bash<br><br>clang-tidy \<br>    -fix \<br>    -fix-errors \<br>    -header-filter=.* \<br>    --checks=readability-braces-around-statements,misc-macro-parentheses \<br>    $1 \<br>    -- -I.<br></pre><br><br><a href="http://clang.llvm.org/extra/clang-tidy/">clang-tidy</a> es una herramienta de refactorización basada en reglas. Las opciones de arriba activan dos arreglos:<br><ul><br> 	<li><code>readability-braces-around-statements</code> - fuerza a que todos los <code>if/while/for</code> tengan el cuerpo rodeado por llaves.<br><ul><br> 	<li>ha sido un error que C permitiese las llaves opcionales. Son causa de muchos errores, sobre todo al mantener el código con el tiempo, así que aunque el compilador te lo acepte, no dejes que ocurra.</li><br></ul><br></li><br> 	<li><code>misc-macro-parentheses</code> - añade automáticamente paréntesis alrededor de los parámetros usados en una macro.</li><br></ul><br><code>clang-tidy</code> es genial cuando funciona, pero para código complejo puede trabarse. Además, <code>clang-tidy</code> no formatea, así que necesitarás llamar a <code>clang-format</code> para formatear y alinear las nuevas llaves y demás cosas.<br><h3 id="legibilidad">Legibilidad</h3><br><h4 id="comentarios">Comentarios</h4><br>Comentarios con sentido, dentro del código, no muy extensos.<br><h4 id="estructuradearchivos">Estructura de archivos</h4><br>Intenta no tener archivos de más de 1000 líneas (1500 como mucho)<br><h3 id="otrosdetalles">Otros detalles</h3><br><h4 id="nuncausesmalloc">Nunca uses <code>malloc</code></h4><br>Usa siempre <code>calloc</code>. No hay penalización de rendimiento por tener la memoria limpia, llena de ceros.<br><br>Los lectores han informado de un par de cosas:<br><ul><br> 	<li><code>calloc</code> sí tiene un impacto en el rendimiento en asignaciones <strong>enormes</strong></li><br> 	<li><code>calloc</code> sí tiene un impacto en el rendimiento en plataformas extrañas (sistemas empotrados, videoconsolas, hardware de 30 años de antigüedad, ...)</li><br> 	<li>una buena razón para no usar <code>malloc()</code> es que no puede comprobar si hay un desbordamiento y es un potencial fallo de seguridad</li><br></ul><br>Todos son buenos puntos, razón por la que siempre debes probar el funcionamiento en todos los sistemas que puedas.<br><br>Una ventaja de usar <code>calloc()</code> directamente es que, al contrario que <code>malloc()</code>, <code>calloc()</code> puede comprobar un desbordamiento porque suma todo el tamaño necesario antes de que lo pida.<br><br>Algunas referencias al uso de <code>calloc()</code> se pueden encontrar aquí:<br><ul><br> 	<li><a href="https://blogs.fau.de/hager/archives/825">Benchmarking fun with calloc() and zero pages (2007)</a></li><br> 	<li><a href="https://en.wikipedia.org/wiki/Copy-on-write#Copy-on-write_in_virtual_memory_management">Copy-on-write in virtual memory management</a></li><br></ul><br>Sigo recomendando usar siempre <code>calloc()</code> para la mayoría de escenarios en 2016.<br><h4 id="nuncausesmemsetsipuedesevitarlo">Nunca uses memset (si puedes evitarlo)</h4><br>Nunca hagas <code>memset(ptr, 0, len)</code> cuando puedes inicializar una estructura (o un array) con <code>{0}</code>.<br><h2 id="genericsenc11">Generics en C11</h2><br>C11 ha añadido los <strong>Generics</strong>. Funcionan como un switch, que distingue entre los tipos y dependiendo del valor que se le de devuelve una u otra cosa. Por ejemplo:<br><br><pre class="lang:c++ decode:true"><br>#define probarGenerics(X) _Generic((X), char: 1, int32_t: 2, float: 3, default: 0)<br><br>probarGenerics('a') // devolverá 1<br>probarGenerics(2) // devolverá 2<br></pre>]]></description>
                <comments>https://blog.adrianistan.eu/programar-c-2016</comments>
                <pubDate>Sun, 10 Jan 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Kovel 1.0, diseña modelos en 3D usando vóxeles</title>
                <link>https://blog.adrianistan.eu/kovel-1-0-disena-modelos-3d-usando-voxeles</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/kovel-1-0-disena-modelos-3d-usando-voxeles</guid>
                <description><![CDATA[Hoy me he decidido y finalmente he decidido publicar la primera versión pública de <a href="http://adrianistan.eu/kovel/">Kovel</a>. Estuve trabajando en esta aplicación a finales de 2015 y no he hecho muchos cambios últimamente por lo que voy a publicarlo antes de que ¡se me olvide!<br><h2 id="queskovel">¿Qué es Kovel?</h2><br>Kovel es una aplicación para Linux, <a href="https://blog.adrianistan.eu/2014/11/17/que-nos-espera-en-haiku-beta/">Haiku</a> y Windows para diseñar modelos en 3D usando el concepto de los vóxeles. ¿Qué es un vóxel? Se ha definido un vóxel como un píxel con volumen, es decir, el equivalente a un píxel en entornos tridimensionales. Los vóxeles nunca existieron (los gráficos 3D no funcionan a través de vóxeles, son siempre vectoriales) y son simplemente un concepto artístico, muy sencillo de usar.<br><br><img class="alignnone size-large wp-image-57" src="https://files.adrianistan.eu/Kovel-1-1024x578.png" alt="Kovel-1" width="840" height="474" /><br><br><img class="alignnone size-large wp-image-60" src="https://files.adrianistan.eu/KovelRotate-1024x578.png" alt="KovelRotate" width="840" height="474" /><br><h2 id="cmofunciona">¿Cómo funciona?</h2><br>Es muy sencillo. Al crear un nuevo archivo seleccionaremos el tamaño de la rejilla. Por defecto la rejilla está puesta en 5. Esto quiere decir que el modelo tendrá una dimensión máxima de 5x5x5. Ahora seleccionamos el material. En esta versión solo hay colores puros como materiales, en futuras versiones habrá texturas también. Ahora simplemente hacemos click en los elementos de la rejilla. Vemos como se pone un vóxel en la posición que hemos indicado en la rejilla. Para subir y bajar de piso usamos los botones Up y Down. Podemos rotar y hacer zoom al modelos para centrarnos en determinadas áreas. En cualquier momento podemos deshacer. Los modelos se guardan como ficheros KVL. Es un formato que he tenido que inventar, es compacto y a la vez muy fácil de manipular. Está basado en BSON, la implementación binaria de JSON hecha por la gente de MongoDB. Pero además podemos exportar nuestras creaciones al formato Collada DAE (se puede abrir con Blender, Maya, etc).<br><br><img class="alignnone size-large wp-image-17" src="https://files.adrianistan.eu/BlenderKovel-1024x575.png" alt="BlenderKovel" width="840" height="472" /><br><h2 id="dndepuedoobtenerlo">¿Dónde puedo obtenerlo?</h2><br>Todo el código fuente está en <a href="http://github.com/AdrianArroyoCalle/kovel">GitHub</a> y se compila usando <a href="https://blog.adrianistan.eu/2016/01/09/kovel-1-0-disena-modelos-3d-usando-voxeles/">CMake</a>. Pero además hay disponible un PPA para usuarios de Ubuntu. Lamentablemente por temas de dependencias con CMake, solo está disponible en Wily (15.10) y Xenial (16.04), aunque si os descargais el DEB manualmente quizá os funcione también en Trusty (14.04) y Jessie (Debian 8). Los usuarios de Windows tienen un ejecutable también para 64 bits (no he compilado para 32 todavía) pero requiere las DLL de wxWidgets 3.0. Los usuarios de Haiku tendrán que conformarse de momento con el código fuente. Todas las descargas están en la página oficial de Kovel (incluidas las DLL).<br><div style="text-align: center;"><br><h5><a href="http://adrianistan.eu/kovel/">Kovel</a></h5><br></div><br><img class="alignnone size-large wp-image-58" src="https://files.adrianistan.eu/KovelHaiku-1024x576.png" alt="KovelHaiku" width="840" height="473" /><br><h2 id="algoms">¿Algo más?</h2><br>Sí, aparte de Kovel, la aplicación gráfica, también existe <code>kovelcli</code>, la interfaz de línea de comandos. Permite realizar la conversión de KVL a Collada DAE de manera automática.<br><br>Finalmente, doy las gracias a todos los que vayan a probar Kovel, un simple comentario con sugerencias o de agradecimiento si os ha servido vale mucho para mí. ¡Felices vóxeles!]]></description>
                <comments>https://blog.adrianistan.eu/kovel-1-0-disena-modelos-3d-usando-voxeles</comments>
                <pubDate>Sat, 09 Jan 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Bienvenido 2016, bienvenido kaizen</title>
                <link>https://blog.adrianistan.eu/bienvenido-2016-bienvenido-kaizen</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/bienvenido-2016-bienvenido-kaizen</guid>
                <description><![CDATA[¡Feliz año! ¡Feliz 2016! Ya os he felicitado, ahora vamos a lo serio.<br><br><img class="alignnone size-large wp-image-9" src="https://files.adrianistan.eu/2016-1024x666.jpg" alt="2016" width="840" height="546" /> <small>Imagen de <a href="https://www.flickr.com/photos/marcelamcgreal/23788366130/in/photolist-Cf6AtC-C5BB7k-Bns9B2-oXdto8-eKb75w-cuACJC-5Ktdsi-4uaqEp-Ch4PmJ-CwUEj5-CtuQUC-CTuvPz-CHLML9-CPccXS-BR2v2f-Cf1Ua8-CaWDeZ-Cf3BsC-BtWkXW-uapRPi-pADUBW-BP4VRy-CSz6hA-CnKs1N-cuAA6U-o61xtb-a7ZjRY-BNxrya-C8KJHP-ARTDqF-CKy6tB-BLLbJM-CMndCb-Cz7RV5-CrUE9L-Ci4MCB-zmEx6j-CAgTeU-Btwf4D-zWwQvb-CSVnRh-Ckg87x-Cpy9m9-CooyLs-Cije63-CGeZMV-BHvbCx-bLpCxx-Ckbkne-CNjaUK">Marcela</a></small><br><h2 id="cmomejorarelblog">¿Cómo mejorar el blog?</h2><br>Cada vez me interesa más este blog, cada vez le tengo más cariño, pero creo que no se está aprovechando su potencial. Así que, y siguiendo las pautas de la mejora continua (KAIZEN), os voy a pedir unas cosillas:<br><ul><br> 	<li>Rellenar la encuesta que se encuentra más abajo</li><br> 	<li>Compartir el blog en redes sociales</li><br> 	<li>Suscribirte por correo</li><br> 	<li>(y ya si donas por PayPal, ChangeTip o Flattr, eres un fiera)</li><br></ul><br>El objetivo es que el blog crezca, no en visitas, lo cual me importa pero no tanto como en la comunidad, una comunidad activa alrededor del blog.<br><br><iframe src="https://docs.google.com/forms/d/1ylRU1CKVfDdw9s4044ZWopSQOhqSFge6QgpNiBUxO3s/viewform?embedded=true" width="760" height="500" frameborder="0" marginwidth="0" marginheight="0">Cargando...</iframe>]]></description>
                <comments>https://blog.adrianistan.eu/bienvenido-2016-bienvenido-kaizen</comments>
                <pubDate>Fri, 08 Jan 2016 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Ya he logrado dominar el mundo</title>
                <link>https://blog.adrianistan.eu/ya-he-logrado-dominar-mundo</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/ya-he-logrado-dominar-mundo</guid>
                <description><![CDATA[Ya está. Ya podeis dejar de hacer lo que estabais haciendo. ¿Tratando de dominar el mundo antes que yo? Mala suerte. Ya he terminado. He logrado dominar el mundo hasta un punto que ya es irreversible. No os trastorneis, ni penseis en ello. Simplemente adorad a vuestro nuevo líder.<br><br><img class="alignnone size-full wp-image-94" src="https://files.adrianistan.eu/Piramide.jpg" alt="Piramide" width="1023" height="835" /><br><br>Quiero agradecer a todos aquellos que han hecho posible mi dominio. En especial a los <strong>illuminati</strong>, a la cienciología (ahora se llama <em>Scientology™</em>), a los programas de telebasura (bueno, en realidad a la televisión general puesto que TELEVISIÓN EN EL MUNDO = TELEBASURA GLOBAL), a la gente que da opiniones sin informarse antes (hay que majos son, que tierna es su manipulación), a las diversas sectas de nuestro bonito panorama internacional, a los aburridos (muy aburridos estaban), a los habitantes de Isla Malvada y por supuesto a mi kit de <em>Dominium mundi</em>, que le faltaban piezas (concretamente el <strong>Santo Grial</strong>, reclamé a los templarios pero no hicieron ni caso "No, nosotros no sabemos. Lo andábamos buscando también, fíjate que casualidad").<br><br><img class="alignnone size-full wp-image-78" src="https://files.adrianistan.eu/Mundo.gif" alt="Mundo" width="199" height="181" /><br><br>No voy a negarlo, me ha sido muy fácil dominar el mundo, tomar el control de todas las cosas y personas del orbe para poder ejercer poder y tiranía sin límites. Mi plan era absurdo pero eso lo comentaré otro día.<br><br><img class="alignnone size-full wp-image-29" src="https://files.adrianistan.eu/DominarMundo.jpg" alt="DominarMundo" width="516" height="126" /><br><br>Ahora voy a empezar a aplicar mi poder. Algunas ideas que tengo son:<br><ul><br> 	<li>Hacer una ola con todas las personas de la Tierra. Tengo mis dudas si en el hemisferio sur y debido al efecto Coriolis las personas girarán sus brazos en sentido opuesto.</li><br> 	<li>Registrar los silbidos y cobrar un canon</li><br> 	<li>Quitarles los caramelos a los niños</li><br> 	<li>Instaurar el culto a 42</li><br> 	<li>Inventarme un mito tipo La Eneida o Gilgamesh, donde yo soy el héroe supremo</li><br> 	<li>Hacer un pacto con los delfines, a fin de poner límites a los imperios. Ellos el agua, yo la superficie</li><br> 	<li>Introducirme en la organización de los Anunaki e ir escalando puestos socialmente poco a poco.</li><br> 	<li>Añadir una sustancia de control mental en la vacuna de la gripe, y si no funciona, reeducacioncilla (si, al estilo Ned Flanders)</li><br> 	<li>Publicar en una página web todas las cosas tiránicas que hago a modo de denuncia. Seré a la vez tirano y salvador. Pero usaré un pseudónimo guay tipo Sr. X</li><br> 	<li>Ir al pasado y matarme para así evitar la destrucción del mundo, pero no podré ir al pasado por las paradojas de "¿cómo llegaste al pasado para matarte, jovencito? ¿no estarías ya muerto y en ese caso no podrías haber ido al pasado a asesinarte?".</li><br></ul><br><img class="alignnone size-full wp-image-30" src="https://files.adrianistan.eu/DominarMundo-2.jpg" alt="DominarMundo-2" width="768" height="768" /><br><h4 id="sabasqu">¿Sabías qué...</h4><br>... la mayoría de los que han intentado dominar el mundo han fracasado?<br><br>... yo no?<br><br>... feliz día de los Santos Inocentes, aunque si te lo estabas creyendo quizá te habías tomado drogas de más?<br><br><img class="alignnone size-full wp-image-93" src="https://files.adrianistan.eu/Patos.jpg" alt="Patos" width="650" height="523" />]]></description>
                <comments>https://blog.adrianistan.eu/ya-he-logrado-dominar-mundo</comments>
                <pubDate>Mon, 28 Dec 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>El pájaro que nunca deja de beber</title>
                <link>https://blog.adrianistan.eu/pajaro-bebedor</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/pajaro-bebedor</guid>
                <description><![CDATA[No sé si alguien se acuerda de las dos apariciones estelares en Los Simpsons de un cacharro, con forma de pájaro, que bebe todo el agua. Como si tuviera el movimiento perpetuo.<br><br>Hace poco me encontré en una feria con el dichoso pájaro y no me pude resistir. ¡Me lo compré!<br><br>[video width="360" height="360" webm="https://files.adrianistan.eu/PajaroBebedor.webm"][/video]]]></description>
                <comments>https://blog.adrianistan.eu/pajaro-bebedor</comments>
                <pubDate>Wed, 23 Dec 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Mi primer debug. Primeros pasos con gdb, Valgrind y strace.</title>
                <link>https://blog.adrianistan.eu/primer-debug-primeros-pasos-gdb-valgrind-strace</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/primer-debug-primeros-pasos-gdb-valgrind-strace</guid>
                <description><![CDATA[¿A quién no le ha pasado? Estas programando en C++ y de repente cuando antes todo iba bien, ahora el programa se cierra inesperadamente (un <em>crash</em>) y no sabes el motivo. En algunos lenguajes como <a href="http://rust-lang.org">Rust</a>, el propio compilador y el lenguaje evitan estas situaciones, pero en C++ la situación es mucho más <em>estimulante</em>.<br><br>Recientemente, trabajando en <a href="http://adrianistan.eu/kovel/">Kovel</a> tuve uno de estos incidentes inesperados. Pero más inesperada fue su aparición, pues en <a href="http://debian.org">Debian</a>, donde programo actualmente, el programa se ejecutaba normalmente. Sin embargo en Windows el programa no llegaba a arrancar. Pensé que sería una diferencia Linux-Windows pero al probar en Fedora ocurrió lo mismo que en Windows, no llegaba a arrancar. Si encontraba el fallo en Fedora, que no se daba en Debian, resolvería también el fallo en Windows.<br><h2 id="preparandolaaplicacinyelentorno">Preparando la aplicación y el entorno</h2><br><h3 id="smbolosdedepuracin">Símbolos de depuración</h3><br>Aunque no es obligatorio, es recomedable compilar los ejecutables que vayamos a someter a depuración con símbolos de depuración. En Windows se usan archivos independientes (ficheros PDB) mientras que en Linux se usan los mismos ejecutables con más metadatos en su interior. En GCC simplemente hay que añadir la opción <strong>-g</strong> para retener los datos de depuración.<br><h3 id="ficheroscore">Ficheros core</h3><br>Ahora sería conveniente activar la generación de los ficheros <strong>core</strong> en el sistema. En algunas distro ya está activado:<br><pre><code>ulimit -c unlimited </code></pre><br>Los ficheros <strong>core</strong> los usaremos si nuestra aplicación se paró en un punto de difícil acceso o que no podemos recrear nosotros mismos.<br><h3 id="instalargdbvalgrindylossmbolosdelaslibreras">Instalar gdb, Valgrind y los símbolos de las librerías</h3><br>Ahora vamos a instalar el componente más importante, el debugger, la aplicación que usaremos para analizar la ejecución del programa.<br><br><img class="alignnone size-full wp-image-42" src="https://files.adrianistan.eu/gdb.png" alt="gdb" width="706" height="540" /><br><pre><code># En Fedora sudo dnf install gdb </code></pre><br>Además querremos tener los símbolos de depuración de las bibliotecas que use nuestro ejecutable. Con DNF, en Fedora, el proceso usa un comando específico:<br><pre><code>sudo dnf debuginfo-install wxGTK SDL libstdc++ # Y las librerías que usemos </code></pre><br>Y si queremos mantener los símbolos de depuración actualizados:<br><pre><code>sudo dnf --enablerepo=updates-debuginfo update </code></pre><br>Vamos a usar Valgrind también, aunque menos<br><pre><code>sudo dnf install valgrind </code></pre><br><h2 id="cazandoalvuelo">Cazando al vuelo</h2><br>Supongamos que sabemos como generar el error. Llamamos a nuestro programa desde <strong>gdb</strong>:<br><pre><code>gdb ./MiPrograma </code></pre><br>Entraremos en gdb, con su propios comandos de herramientas. Lo primero que haremos será iniciar el programa, con el comando <em>run</em> o <em>r</em><br><pre><code>(gdb) r </code></pre><br>El programa se iniciará. Nosotros provocaremos el error. Una vez lo hayamos provocado podremos introducir más comandos. Vamos a ver que pasos se han seguido para producir el error.<br><pre><code>(gdb) bt full </code></pre><br>Y desde aquí podemos inspeccionar que funciones fueron llamadas justo antes de que el programa <em>petase</em>. En este punto también podemos buscar el valor de ciertas variables que nos interesen con <em>p nombrevariable</em>.<br><h2 id="volviendoalpasado">Volviendo al pasado</h2><br>No sabemos como se produjo el error, pero tenemos un fichero <strong>core</strong> que nos va a permitir restablecer la situación del pasado para poder analizarla. Llamamos a gdb con el fichero core y nuestra aplicación.<br><pre><code>gdb ./MiPrograma ./core </code></pre><br>Una vez dentro podemos dirigirnos al punto crítico.<br><pre><code>(gdb) where </code></pre><br>Y analizamos como antes.<br><h2 id="valgrindyfugasdememoria">Valgrind y fugas de memoria</h2><br>Valgrind es muy usado para comprobar en que partes nuestro programa tiene fugas de memoria. En determinados casos puede ser más útil que gdb.<br><pre><code>valgrind --leak-check=yes ./MiPrograma </code></pre><br>Nuestro programa se ejecutará aproximadamente 20 o 30 veces más lento, pero se nos informará en todo momento de la gestión errónea de memoria que está produciéndose. En alguna situación será interesante saber de donde provienen estos fallos con mayor precisión, la opción <em>--track-origins=yes</em> es nuestra amiga.<br><pre><code>valgrind --leak-check=yes --track-origins=yes ./MiPrograma </code></pre><br>Valgrind es muy estricto y puede generar falsos positivos. Hay varias GUI disponibles para Valgrind, una de ellas es <a href="https://kcachegrind.github.io/html/Home.html">KCacheGrind</a>.<br><br><img class="alignnone size-large wp-image-55" src="https://files.adrianistan.eu/KCacheGrind-1024x859.gif" alt="KCacheGrind" width="840" height="705" /><br><br>Otra de ellas es <a href="https://web.archive.org/web/20170605084204/http://www.open-works.net:80/projects/valkyrie.html">Valkyrie</a><br><br><img class="alignnone size-large wp-image-127" src="https://files.adrianistan.eu/Valkyrie-1024x578.png" alt="Valkyrie" width="840" height="474" /><br><h2 id="ysialgnficheronoexiste">¿Y si algún fichero no existe?</h2><br>Para terminar vamos a suponer que nuestro programa falla porque hay un archivo que no logra encontrar y no puede abrirlo. Gracias a <strong>strace</strong> es posible saber que archivos está abriendo el programa.<br><pre><code>strace -eopen ./MiPrograma </code></pre><br>Y nos saldrá en tiempo real los archivos que ha abierto nuestro programa.<br><br><img class="alignnone size-large wp-image-107" src="https://files.adrianistan.eu/Strace-1024x578.png" alt="Strace" width="840" height="474" /><br><br>Y espero que con este pequeño resumen ya sepais que hacer cuando vuestro programa se cierra inesperadamente.]]></description>
                <comments>https://blog.adrianistan.eu/primer-debug-primeros-pasos-gdb-valgrind-strace</comments>
                <pubDate>Fri, 04 Dec 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Emulando a Linus Torvalds: Crea tu propio sistema operativo desde 0 (VIII)</title>
                <link>https://blog.adrianistan.eu/emulando-linus-torvalds-crea-propio-sistema-operativo-desde-0-viii</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/emulando-linus-torvalds-crea-propio-sistema-operativo-desde-0-viii</guid>
                <description><![CDATA[<blockquote><em>Este artículo lo escribí para el blog en español <a href="http://blog.desdelinux.net">DesdeLinux</a> el 23 de diciembre de 2014 y ahora lo dejo aquí, en mi blog personal. El artículo está tal cual, sin ninguna modificación desde aquella fecha.</em></blockquote><br>Volvemos a la serie de tutoriales sobre como crear nuestro propio sistema operativo. Supongo que este capítulo os gustará mucho porque por fin podremos interactuar con nuestro sistema operativo. Hoy leeremos la entrada del teclado. Para ello el esquema es similar al del timer. Tenemos que usar los IRQ igualmente así que empezaremos igual que con el timer.<br><br><pre class="lang:c++ decode:true"><br>ND_IRQ_InstallHandler(1,&amp;ND_Keyboard_Handler);<br></pre><br><br>Nuestro handler de teclado sin embargo es algo más complejo ya que vamos leyendo las teclas y las vamos depositando en un buffer.<br><br><pre class="lang:c++ decode:true"><br>extern &quot;C&quot;<br>void ND_Keyboard_Handler(struct regs* r)<br>{<br>    unsigned char scancode = ND::Keyboard::GetChar();<br>    if(scancode!=255)<br>    {<br>		ND::Screen::PutChar(scancode);<br>		stringBuffer[stringPos]=scancode;<br>		stringPos++;<br>    }<br>}<br></pre><br><br>Podemos comprobar como llamamos a una función llamada ND::Keyboard::GetChar. Allí obtenemos el caracter y después si no es un caracter vacío (aquí he usado 255, habría que usar un sistema mejor) ponemos el caracter en pantalla y lo almacenamos en un buffer simple de chars (esto también es susceptible de mejora, el sistema actual puede desbordarse).<br><br><pre class="lang:c++ decode:true"><br>unsigned char ND::Keyboard::GetChar()<br>{<br>	unsigned char scancode;<br>	scancode=(unsigned char)ND::Ports::InputB(0x60);<br>	if(scancode &amp; ND_KEYBOARD_KEY_RELEASE)<br>	{<br>		return 255;<br>	}else{<br>		return en_US[scancode];<br>	}<br>}<br> <br>char* ND::Keyboard::GetString()<br>{<br>	while(stringBuffer[stringPos-1]!='\n')<br>	{<br>	}<br>	stringPos=0;<br>	return stringBuffer;<br>}<br></pre><br><br>Aquí podemos ver como se obtiene la tecla que ha sido pulsada. En 0x60 siempre va a estar la última tecla pulsada. De hecho se puede leer directamente sin tener que usar el IRQ, pero entonces no sabremos indentificar cuando se ha producido un cambio. Allí comprobamos con la operación AND que el código de obtuvimos corresponde a una tecla que se ha dejado de pulsar.<br><br>En ese caso devolvemos 255 (porque luego lo ignoraremos) y en caso contrario la tecla ha sido pulsada. En ese caso devolvemos la posición de un array llamado en<em>US. ¿Qué información contiene este array? Este array es lo que llamaríamos un keymap o un mapa de caracteres. Como sabrán diferentes idiomas tienen diferentes teclados y no son compatibles ya que sobreescriben las teclas. Así en</em>US nos dará la tecla correspondiente a cada código y funcionará en un teclado americano.<br><br><pre class="lang:c++ decode:true"><br>unsigned char en_US[128]=<br>{<br>	0,27,'1','2','3','4','5','6','7','8','9','0','-','=', '\b',<br>	'\t','q','w','e','r','t','y','u','i','o','p','[',']','\n',<br>	0, /* Ctrl */<br>	'a','s','d','f','g','h','j','k','l',';',<br>	'\'','`',0, /* Left Shift */<br>	'\\','z','x','c','v','b','n','m',',','.','/', 0,/* Right shift */<br>	'*', 0, /* Alt */<br>	' ',<br>	0, /* Caps lock*/<br>	0,0,0,0,0,0,0,0,0,0, /* F1-F10 keys */<br>	0, /* Num lock */<br>	0, /* Scroll lock */<br>	0, /* Home key */<br>	0, /* Up arrow */<br>	0, /* Page up */<br>	'-',<br>	0, /* Left arrow */<br>	0,<br>	0, /* Right arrow */<br>	'+',<br>	0, /* End key */<br>	0, /* Down arrow */<br>	0, /* Page down */<br>	0, /* Insert key */<br>	0, /* Delete key */<br>	0,0,0,<br>	0, 0, /* F11-F12 Keys */<br>	0<br>};<br></pre><br><br>También había una función definida que obtenía una frase. El propósito es simplemente obtener acceso más fácilmente a los strings desde las aplicaciones que lo necesiten, de momento solo una. Hablo de NextShellLite, una versión reducida del posible futuro shell que tendría NextDivel. El propósito de NextShellLite es únicamente el de proveer de un shell reducido para ir probando poco a poco nuevas funcionalidades. No voy a poner el código del shell aquí pero lo he incluido dentro del código de NextDivel.<br><br>De momento no funciona como un programa aparte sino como una función que llama el kernel, principalmente porque todavía no añadimos la opción de ejecutar ejecutables. Y claro, unas imágenes de como funciona el shell con las nuevas funciones de entrada de teclado.<br><br><img class="alignnone size-full wp-image-88" src="https://files.adrianistan.eu/NextShellLite.png" alt="NextShellLite" width="600" height="356" />]]></description>
                <comments>https://blog.adrianistan.eu/emulando-linus-torvalds-crea-propio-sistema-operativo-desde-0-viii</comments>
                <pubDate>Thu, 03 Dec 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Emulando a Linus Torvalds: Crea tu propio sistema operativo desde 0 (VII)</title>
                <link>https://blog.adrianistan.eu/emulando-linus-torvalds-crea-propio-sistema-operativo-desde-0-vii</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/emulando-linus-torvalds-crea-propio-sistema-operativo-desde-0-vii</guid>
                <description><![CDATA[<blockquote><em>Este artículo lo escribí para el blog en español <a href="http://blog.desdelinux.net">DesdeLinux</a> el 25 de agosto de 2014 y ahora lo dejo aquí, en mi blog personal. El artículo está tal cual, sin ninguna modificación desde aquella fecha.</em></blockquote><br>Bienvenidos a otro post sobre cómo crear nuestro propio sistema operativo. Ha pasado mucho tiempo desde el último post, debido principalmente a un bug que encontré en lo que nos toca hoy. Veremos cómo manejar el reloj en arquitecturas x86.<br><br>Anteriormente habíamos activado los IRQ de manera genérica, pero hubo un pequeño problema ya que no los activábamos correctamente y pasábamos datos extra. Finalmente lo solucionamos <strong>carlosorta</strong> y yo y os puedo seguir comentando cómo seguir.<br><br>Bien, el reloj es un IRQ, concretamente el primero. Para configurarlo usaremos la función que definimos anteriormente para instalar de manera genérica los IRQ, la ND<em>IRQ</em>InstallHandler.<br><br><pre class="lang:c++ decode:true"><br>int ND_TIMER_TICKS=0;<br><br>void ND::Timer::Phase(int hz)<br>{<br>	int divisor=1193180/hz;<br>	ND::Ports::OutputB(0x43,0x36);<br>	ND::Ports::OutputB(0x40, divisor &amp; 0xFF);<br>	ND::Ports::OutputB(0x40, divisor &gt;&gt; 8);<br>}<br>void ND::Timer::Wait(int ticks)<br>{<br>	unsigned long eticks;<br>	eticks=ND_TIMER_TICKS+ticks;<br>	while(ND_TIMER_TICKS &lt; eticks)<br>	{<br>		<br>	}<br>}<br>void ND::Timer::Setup()<br>{<br>	ND::Screen::SetColor(ND_SIDE_FOREGROUND, ND_COLOR_BLACK);<br>	ND::Screen::PutString(&quot;\nSetup timer...&quot;);<br>	<br>	ND_IRQ_InstallHandler(0,&amp;ND_Timer_Handler);<br>	<br>	ND::Screen::SetColor(ND_SIDE_FOREGROUND,ND_COLOR_GREEN);<br>	ND::Screen::PutString(&quot;done&quot;);<br>}<br>extern &quot;C&quot;<br>void ND_Timer_Handler(struct regs* r)<br>{<br>	ND_TIMER_TICKS++;<br>	if(ND_TIMER_TICKS % 18 ==0)<br>	{<br>		ND::Screen::SetColor(ND_SIDE_FOREGROUND,ND_COLOR_BROWN);<br>		ND::Screen::PutString(&quot;\nOne more second&quot;); WE SHOULD DO A REFRESH SCREEN<br>	}<br>}<br></pre><br><br>El código se ejecuta de la siguiente manera: el sistema de inicialización llama a <strong>ND::Timer::Setup</strong>, que llama a <strong>ND<em>IRQ</em>InstallHandler</strong> para insertar en la primera posición, el IRQ0, una función de callback cuando el evento se produzca, esa es <strong>ND<em>Timer</em>Handler</strong> que aumenta los ticks. Como hemos puesto la velocidad del reloj a 18 Hz, como veremos más adelante, si lo dividiésemos entre 18 y nos diese entero habría pasado un segundo.<br><br>La función <strong>ND::Timer::Phase</strong> nos sirve para ajustar la velocidad del timer, ese número tan extravagante es 1.19 MHz que es un valor común. Bien, esta función la deberemos llamar si quisiésemos cambiar la velocidad del timer, por defecto va a 18,22 Hz, un valor peculiar que debió de decidir alguien dentro de <a href="http://ibm.com">IBM</a> y se ha quedado hasta nuestros días.<br><br>La función <strong>ND::Timer::Wait</strong> es bastante simple, solamente espera con un bucle <em>while</em> hasta que se hayan alcanzado los <em>ticks</em> necesarios para continuar.<br><br>En la imagen podemos comprobar que si descomentamos el código dentro del ND<em>Timer</em>Handler obtenemos esto:<br><br><img class="alignnone size-full wp-image-87" src="https://files.adrianistan.eu/NextDivelSegundos.png" alt="NextDivelSegundos" width="600" height="356" />]]></description>
                <comments>https://blog.adrianistan.eu/emulando-linus-torvalds-crea-propio-sistema-operativo-desde-0-vii</comments>
                <pubDate>Wed, 02 Dec 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Emulando a Linus Torvalds: Crea tu propio sistema operativo desde 0 (VI)</title>
                <link>https://blog.adrianistan.eu/emulando-linus-torvalds-crea-propio-sistema-operativo-desde-0-vi</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/emulando-linus-torvalds-crea-propio-sistema-operativo-desde-0-vi</guid>
                <description><![CDATA[<blockquote><em>Este artículo lo escribí para el blog en español <a href="http://blog.desdelinux.net">DesdeLinux</a> el 11 de enero de 2014 y ahora lo dejo aquí, en mi blog personal. El artículo está tal cual, sin ninguna modificación desde aquella fecha.</em></blockquote><br>Bien, después de un pequeño paréntesis seguimos con nuestra serie de tutoriales. Si retomamos el código anterior debemos de tener el ISR de la división por cero. Ahora debemos de rellenar el resto de las ISR para las que habíamos puesto mensaje (las 32 primeras). Bien ahora vamos a seguir programando interrupciones, vamos a hacer las IRQ también conocidas como Interrupts Requests. Estas IRQ se generan por los dispositivos de hardware tales como teclados, ratones, impresoras, etc. Inicialmente las primeras 8 IRQ se mapean automáticamente en las posiciones de la IDT del 8 al 15. Como hemos usado las 32 primeras para las excepciones ahora tenemos que remapearlas. Nosotros pondremos la IRQ desde la 32 hasta la 45. Para ello primero debemos remapear los los IRQ:<br><br><pre class="lang:c++ decode:true"><br>void ND::IRQ::Remap(int pic1, int pic2)<br>{<br>    #define PIC1 0x20<br>    #define PIC2 0xA0<br>    #define ICW1 0x11<br>    #define ICW4 0x01<br>    /* send ICW1 */<br><br>    ND::Ports::OutputB(PIC1, ICW1);<br>    ND::Ports::OutputB(PIC2, ICW1);<br><br>    /* send ICW2 */<br><br>    ND::Ports::OutputB(PIC1 + 1, pic1); /* remap */<br>    ND::Ports::OutputB(PIC2 + 1, pic2); /* pics */<br><br>    /* send ICW3 */<br><br>    ND::Ports::OutputB(PIC1 + 1, 4); /* IRQ2 -&gt; connection to slave */<br>    ND::Ports::OutputB(PIC2 + 1, 2);<br><br>    /* send ICW4 */<br><br>    ND::Ports::OutputB(PIC1 + 1, ICW4);<br>    ND::Ports::OutputB(PIC2 + 1, ICW4);<br><br>    /* disable all IRQs */<br><br>    ND::Ports::OutputB(PIC1 + 1, 0xFF);<br>}<br></pre><br>Ahora creamos una función para instalar los IRQ:<br><pre class="lang:c++ decode:true"><br>void ND::IRQ::Install()<br>{<br>    ND::Screen::SetColor(ND_SIDE_FOREGROUND,ND_COLOR_BLACK);<br>    ND::Screen::PutString(&quot;\nInstalling IRQ...&quot;);<br>    ND::IRQ::Remap(0x20,0x28);<br>    ND::IDT::SetGate(32,(unsigned)ND::IRQ::IRQ1,0x08,0x8E);<br>    ND::IDT::SetGate(33,(unsigned)ND::IRQ::IRQ2,0x08,0x8E);<br>    ND::IDT::SetGate(34,(unsigned)ND::IRQ::IRQ3,0x08,0x8E);<br>    ND::IDT::SetGate(35,(unsigned)ND::IRQ::IRQ4,0x08,0x8E);<br>    ND::IDT::SetGate(36,(unsigned)ND::IRQ::IRQ5,0x08,0x8E);<br>    ND::IDT::SetGate(37,(unsigned)ND::IRQ::IRQ6,0x08,0x8E);<br>    ND::IDT::SetGate(38,(unsigned)ND::IRQ::IRQ7,0x08,0x8E);<br>    ND::IDT::SetGate(39,(unsigned)ND::IRQ::IRQ8,0x08,0x8E);<br>    ND::IDT::SetGate(40,(unsigned)ND::IRQ::IRQ9,0x08,0x8E);<br>    ND::IDT::SetGate(41,(unsigned)ND::IRQ::IRQ10,0x08,0x8E);<br>    ND::IDT::SetGate(42,(unsigned)ND::IRQ::IRQ11,0x08,0x8E);<br>    ND::IDT::SetGate(43,(unsigned)ND::IRQ::IRQ12,0x08,0x8E);<br>    ND::IDT::SetGate(44,(unsigned)ND::IRQ::IRQ13,0x08,0x8E);<br>    ND::IDT::SetGate(45,(unsigned)ND::IRQ::IRQ14,0x08,0x8E);<br>    ND::IDT::SetGate(46,(unsigned)ND::IRQ::IRQ15,0x08,0x8E);<br>    ND::IDT::SetGate(47,(unsigned)ND::IRQ::IRQ16,0x08,0x8E);<br>    ND::Screen::SetColor(ND_SIDE_FOREGROUND,ND_COLOR_GREEN);<br>    ND::Screen::PutString(&quot;done&quot;);<br>    asm volatile(&quot;sti&quot;);<br>}<br></pre><br><br>La sentencia de asm <em>sti</em> nos activa los <strong>IRQ</strong>. Bien ahora vamos con algo similar a los ISR. Las funciones de un IRQ básico:<br><br><pre class="lang:c++ decode:true"><br>void ND::IRQ::IRQ1()<br>{<br>	asm volatile(<br>	&quot;cli \n&quot;<br>	&quot;pushl 0\n&quot;<br>	&quot;pushl 32\n&quot;<br>	&quot;jmp ND_IRQ_Common&quot;<br>	);<br>}<br></pre><br><br>Una parte común (igual que la de los ISR):<br><br><pre class="lang:c++ decode:true"><br>extern &quot;C&quot;<br>void ND_IRQ_Common()<br>{<br>    asm volatile(<br>    &quot;pusha \n&quot;<br>    &quot;push %ds\n&quot;<br>    &quot;push %es\n&quot;<br>    &quot;push %fs\n&quot;<br>    &quot;push %gs\n&quot;<br>    &quot;movw $0x10, %ax \n&quot;<br>    &quot;movw %ax, %ds \n&quot;<br>    &quot;movw %ax, %es \n&quot;<br>    &quot;movw %ax, %fs \n&quot;<br>    &quot;movw %ax, %gs \n&quot;<br>    &quot;movl %esp, %eax \n&quot;<br>    &quot;push %eax \n&quot;<br>    &quot;movl $ND_IRQ_Handler, %eax \n&quot;<br>    &quot;call *%eax \n&quot;<br>    &quot;popl %eax \n&quot;<br>    &quot;popl %ds \n&quot;<br>    &quot;popl %es \n&quot;<br>    &quot;popl %fs \n&quot;<br>    &quot;popl %gs \n&quot;<br>    &quot;popa \n&quot;<br>    &quot;addl 8, %esp \n&quot;<br>    &quot;iret \n&quot;<br>    );<br>}<br></pre><br><br>Y un handler básico:<br><br><pre class="lang:c++ decode:true"><br>extern &quot;C&quot;<br>void ND_IRQ_Handler(struct regs* r)<br>{<br>	void (*handler)(struct regs *r);<br>	if(r-&gt;int_no &gt;= 40)<br>	{<br>	ND::Ports::OutputB(0xA0,0x20);<br>	}<br>	ND::Ports::OutputB(0x20,0x20);<br>}<br></pre><br><br>Con esto ya deberíamos tener activados los IRQ aunque todavía no hagan nada. En el siguiente capítulo veremos como obtener datos a partir de estos IRQ como el reloj o el teclado.<br><br><img class="alignnone size-full wp-image-85" src="https://files.adrianistan.eu/NextDivel-IRQ.png" alt="NextDivel-IRQ" width="600" height="337" /><br><br>Y con esto termina el post de hoy. Como habeis podido comprobar ahora escribo menos regularmente debido a otros asuntos. Aun así seguiré hasta tener un sistema operativo más completo]]></description>
                <comments>https://blog.adrianistan.eu/emulando-linus-torvalds-crea-propio-sistema-operativo-desde-0-vi</comments>
                <pubDate>Tue, 01 Dec 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Emulando a Linus Torvalds: Crea tu propio sistema operativo desde 0 (V)</title>
                <link>https://blog.adrianistan.eu/emulando-linus-torvalds-crea-propio-sistema-operativo-desde-0-v</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/emulando-linus-torvalds-crea-propio-sistema-operativo-desde-0-v</guid>
                <description><![CDATA[<blockquote><em>Este artículo lo escribí para el blog en español <a href="http://blog.desdelinux.net">DesdeLinux</a> el 11 de enero de 2014 y ahora lo dejo aquí, en mi blog personal. El artículo está tal cual, sin ninguna modificación desde aquella fecha.</em></blockquote><br>En esta quinta entrega veremos una tabla bastante parecida a la GDT tanto en teoría como en uso, nos referimos a la <strong>IDT</strong>. Las siglas de IDT hacen referencia a <em>Interrupts Description Table</em> y es una tabla que se usa para manejar las interrupciones que se produzcan. Por ejemplo, alguien hace una división entre 0, se llama a la función encargada de procesar. Estas funciones son los ISR (<em>Interrupt Service Routines</em>). Así pues vamos a crear la IDT y añadir algunos ISR.<br><br>Lo primero vamos a declarar las estructuras correspondientes a la IDT:<br><br><pre class="lang:c++ decode:true"><br>struct Entry{<br>    uint16_t base_low;<br>    uint16_t sel;<br>    uint8_t always0;<br>    uint8_t flags;<br>    uint16_t base_high;<br>} __attribute__((packed));<br><br>struct Ptr{<br>    uint16_t limit;<br>    uint32_t base;<br>} __attribute__((packed));<br></pre><br><br>Como se observa si comparáis con la GDT la estructura Ptr es idéntica y la Entry es bastante parecida. Por consiguiente las funciones de poner una entrada (SetGate) e instalar (Install) son muy parecidas.<br><pre class="lang:c++ decode:true"><br>void ND::IDT::SetGate(uint8_t num,uint32_t base,uint16_t sel, uint8_t flags)<br>{<br>	idt[num].base_low=(base &amp; 0xFFFF);<br>	idt[num].base_high=(base &gt;&gt; 16) &amp; 0xFFFF;<br>	idt[num].sel=sel;<br>	idt[num].always0=0;<br>	idt[num].flags=flags;<br>}<br></pre><br><br>Instalar:<br><br><pre class="lang:c++ decode:true"><br>idtptr.limit=(sizeof(struct ND::IDT::Entry)*256)-1;<br>idtptr.base=(uint32_t)&amp;idt;<br>ND::Memory::Set(&amp;idt,0,sizeof(struct ND::IDT::Entry)*256);<br>ND::IDT::Flush();<br></pre><br><br>Si nos fijamos veremos que la función de instalar usa la función ND::Memory::Set que habíamos declarado en el otro post. También podemos apreciar como no hacemos ninguna llamada a SetGate todavía y llamamos a ND::IDT::Flush, para esta función usamos otra vez la sentencia asm volatile:<br><br><pre class="lang:c++ decode:true"><br>asm volatile(&quot;lidtl (idtptr)&quot;);<br></pre><br><br>Si todo va bien y hacemos un arreglo estético debería quedar así:<br><br><img class="alignnone size-full wp-image-84" src="https://files.adrianistan.eu/NextDivel-IDT.png" alt="NextDivel-IDT" width="600" height="338" /><br><br>Bien, ahora vamos a empezar a rellenar la IDT con interrupciones. Aquí voy a crear solo una pero para el resto se haría igual. Voy a hacer la interrupción de división por cero. Como bien sabrán en matemáticas no se puede dividir un número entre 0. Si esto ocurre en el procesador se genera una excepción ya que no puede continuar. En la IDT la primera interrupción en la lista (0) corresponde a este suceso.<br><br>Añadimos esto entre el seteo de memoria y el flush dentro de la función Install de la IDT:<br><br><pre class="lang:c++ decode:true"><br>ND::IDT::SetGate(0,(unsigned)ND::ISR::ISR1,0x08,0x8E);<br></pre><br><br>La función de callback va a ser ND::ISR::ISR1 que es bastante simple aunque debemos usar ASM:<br><br><pre class="lang:c++ decode:true"><br>void ND::ISR::ISR1()<br>{<br>	asm volatile(<br>    &quot;cli \n&quot;<br>    &quot;pushl 0 \n&quot;<br>    &quot;pushl 0 \n&quot;<br>    &quot;jmp ND_ISR_Common \n&quot;);<br>}<br></pre><br><br>ND<em>ISR</em>Common lo definiremos como una función en lenguaje C. Para ahorrar ficheros y mejorar legibilidad podemos usar extern “C”{}:<br><br><pre class="lang:c++ decode:true"><br>extern &quot;C&quot;<br><br>void ND_ISR_Common()<br>{<br>    asm volatile(<br>    &quot;pusha \n&quot;<br>    &quot;push %ds \n&quot;<br>    &quot;push %es \n&quot;<br>    &quot;push %fs \n&quot;<br>    &quot;push %gs \n&quot;<br>    &quot;movw $0x10, %ax \n&quot;<br>    &quot;movw %ax, %ds \n&quot;<br>    &quot;movw %ax, %es \n&quot;<br>    &quot;movw %ax, %fs \n&quot;<br>    &quot;movw %ax, %gs \n&quot;<br>    &quot;movl %esp, %eax \n&quot;<br>    &quot;push %eax \n&quot;<br>    &quot;movl $ND_ISR_Handler, %eax \n&quot;<br>    &quot;call *%eax \n&quot;<br>    &quot;popl %eax \n&quot;<br>    &quot;popl %ds \n&quot;<br>    &quot;popl %es \n&quot;<br>    &quot;popl %fs \n&quot;<br>    &quot;popl %gs \n&quot;<br>    &quot;popa \n&quot;<br>    &quot;addl 8, %esp \n&quot;<br>    &quot;iret \n&quot;<br>    );<br>}<br></pre><br>Este código en ASM puede ser un poco difícil de entender pero esto es así porque vamos a declarar una estructura en C para acceder a los datos que genere la interrupción. Obviamente si no quisieras eso podrías llamar simplemente en ND::ISR::ISR1 al Kernel Panic o algo por el estilo. La estructura tiene una forma tal que así:<br><pre class="lang:c++ decode:true"><br>struct regs{<br>	uint32_t ds;<br>	uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax;<br>	uint32_t int_no, err_code;<br>	uint32_t eip, cs, eflags, useresp, ss;<br>};<br></pre><br><br>Y por último hacemos la función ND<em>ISR</em>Handler (también con link del C) en que mostramos un kernel panic y una pequeña descripción del error según el que tenemos en una lista de errores.<br><br><pre class="lang:c++ decode:true"><br>extern &quot;C&quot;<br><br>void ND_ISR_Handler(struct regs *r)<br>{<br>	if(r-&gt;int_no &lt; 32) {<br>    	ND::Panic::Show(exception_messages[r-&gt;int_no]);<br>		for(;;);<br>	}<br><br>}<br></pre><br><br>Bien y con esto ya somos capaces de manejar esta interrupción. Con el resto de interrupciones pasaría parecido salvo que hay algunas que devuelven parámetros y usaríamos la estructura reg para obtenerlo. Sin embargo te preguntarás que como sabemos si funciona de verdad. Para probar si funciona vamos a introducir una sencilla línea después del ND::IDT::Install():<br><br><pre class="lang:c++ decode:true"><br>int sum=10/0;<br></pre><br><br>Si compilamos nos dará un warning y si tratamos de ejecutarlo nos saldrá una bonita pantalla:<br><br><img class="alignnone size-full wp-image-86" src="https://files.adrianistan.eu/NextDivel-ISR.png" alt="NextDivel-ISR" width="600" height="338" /><br><br>Y con esto termina este post, creo que es uno de los más extensos pero bastante funcional.]]></description>
                <comments>https://blog.adrianistan.eu/emulando-linus-torvalds-crea-propio-sistema-operativo-desde-0-v</comments>
                <pubDate>Mon, 30 Nov 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Emulando a Linus Torvalds: Crea tu propio sistema operativo desde 0 (IV)</title>
                <link>https://blog.adrianistan.eu/emulando-linus-torvalds-crea-propio-sistema-operativo-desde-0-iv</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/emulando-linus-torvalds-crea-propio-sistema-operativo-desde-0-iv</guid>
                <description><![CDATA[<blockquote><em>Este artículo lo escribí para el blog en español <a href="http://blog.desdelinux.net">DesdeLinux</a> el 5 de enero de 2014 y ahora lo dejo aquí, en mi blog personal. El artículo está tal cual, sin ninguna modificación desde aquella fecha.</em></blockquote><br>Bienvenidos de nuevo a esta serie de posts titulada “Emulando a Linus Torvalds”. Hoy veremos la GDT. Primero tenemos que ver que es la GDT. Según Wikipedia:<br><blockquote><em>The Global Descriptor Table or GDT is a data structure used by Intel x86-family processors starting with the 80286 in order to define the characteristics of the various memory areas used during program execution, including the base address, the size and access privileges like executability and writability</em></blockquote><br>Que traducido sería una Tabla de Descriptores Global, una estructura de datos usada en los procesadores Intel x86 desde el 80286 para definir las características de varias áreas de memoria usadas durante la ejecución del programa.<br><br>Resumiendo, si estamos en un procesador Intel x86 deberemos definir una GDT para un correcto uso de la memoria. Nosotros no vamos a hacer mucha complicación y vamos a definir 3 entradas en la tabla:<br><ul><br> 	<li>Una entrada NULL, obligatoria para todas las tablas.</li><br> 	<li>Una entrada para la sección data, usaremos el máximo, que en 32 bits son 4 GB.</li><br> 	<li>Una entrada para la sección code, usaremos el máximo, que en 32 bits son 4 GB.</li><br></ul><br>Como veis data y code usarán el mismo espacio. Bien, ahora vamos a implementarlo. Para ello usaremos dos estructuras, la primera se encargará de contener un puntero hacia los datos reales de nuestra GDT. Y la segunda será un array con las entradas de la GDT. Primero vamos a definirlas.<br><pre class="lang:c++ decode:true"><br>struct Entry{<br>	uint16_t limit_low;<br>    uint16_t base_low;<br>	uint8_t base_middle;<br>    uint8_t access;<br>    uint8_t granularity;<br>    uint8_t base_high;<br>} __attribute__((packed));<br><br>struct Ptr{<br>	uint16_t limit;<br>    uint32_t base;<br>} __attribute__((packed));<br></pre><br><br>Habrán observado un curioso __attribute__((packed)) al final de las estructuras. Esto le dice al GCC que no optimice las estructuras porque lo que queremos es pasar los datos tal cual al procesador. Ahora vamos a hacer una función para instalar la GDT. Antes deberemos haber declarado las estructuras, ahora vamos a inicializarlas.<br><br><pre class="lang:c++ decode:true"><br>struct ND::GDT::Entry gdt[3];<br>struct ND::GDT::Ptr gp;<br>void ND::GDT::Install()<br>{<br>	gp.limit=(sizeof(struct ND::GDT::Entry)*3)-1;<br>	gp.base=(uint32_t)&amp;gdt;<br>}<br></pre><br><br>Así conseguimos el construir el puntero que va hacia nuestra tabla de 3 entradas.<br><br>Ahora definimos una función común para poner los datos en las entradas<br><br><pre class="lang:c++ decode:true"><br>void ND::GDT::SetGate(int num, uint32_t base, uint32_t limit, uint8_t access,uint8_t gran)<br>{<br>	gdt[num].base_low=(base &amp; 0xFFFF);<br>	gdt[num].base_middle=(base &gt;&gt; 16) &amp; 0xFF;<br>	gdt[num].base_high=(base &gt;&gt; 24) &amp; 0xFF;<br>	gdt[num].limit_low=(limit &amp; 0xFFFF);<br>	gdt[num].granularity=(limit &gt;&gt; 16) &amp; 0x0F;<br>	gdt[num].granularity |= (gran &amp; 0xF0);<br>	gdt[num].access=access;<br>}<br></pre><br><br>Y la llamamos 3 veces desde la función de instalar<br><br><pre class="lang:c++ decode:true"><br>ND::GDT::SetGate(0,0,0,0,0); /* NULL segmente entry */<br>ND::GDT::SetGate(1,0,0xFFFFFFFF,0x9A,0xCF); /* 4 GiB for Code Segment */<br>ND::GDT::SetGate(2,0,0xFFFFFFFF,0x92,0xCF); /* 4 GiB for Data segment */<br></pre><br><br>Por último debemos decirle al procesador que tenemos una GDT, para que la cargue, y en nuestro caso al cargar el kernel con GRUB, sobreescribir la GDT de GRUB. Para cargar la GDT existe una instrucción en asm llamada lgdt (o lgdtl dependiendo de la sintaxis), vamos a usarla.<br><br><pre class="lang:c++ decode:true"><br>asm volatile(&quot;lgdtl (gp)&quot;);<br>asm volatile(<br>	&quot;movw $0x10, %ax \n&quot;<br>	&quot;movw %ax, %ds \n&quot;<br>    &quot;movw %ax, %es \n&quot;<br>    &quot;movw %ax, %fs \n&quot;<br>    &quot;movw %ax, %gs \n&quot;<br>    &quot;movw %ax, %ss \n&quot;<br>    &quot;ljmp $0x08, $next \n&quot;<br>    &quot;next: \n&quot;<br>);<br></pre><br><br>Bien una vez hayamos terminado esto nuestro sistema ya contará con GDT. En el siguiente capítulo veremos la IDT, una tabla muy parecida a la GDT pero con interrupciones. Yo he puesto unos mensajes de estado y confirmación con la GDT así que NextDivel ahora luce así:<br><br><img class="alignnone size-large wp-image-83" src="https://files.adrianistan.eu/NextDivel-GDT-1024x578.png" alt="NextDivel-GDT" width="840" height="474" />]]></description>
                <comments>https://blog.adrianistan.eu/emulando-linus-torvalds-crea-propio-sistema-operativo-desde-0-iv</comments>
                <pubDate>Sun, 29 Nov 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Emulando a Linus Torvalds: Crea tu propio sistema operativo desde 0 (III)</title>
                <link>https://blog.adrianistan.eu/emulando-linus-torvalds-crea-propio-sistema-operativo-desde-0-iii</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/emulando-linus-torvalds-crea-propio-sistema-operativo-desde-0-iii</guid>
                <description><![CDATA[<blockquote><em>Este artículo lo escribí para el blog en español <a href="http://blog.desdelinux.net">DesdeLinux</a> el 1 de enero de 2014 y ahora lo dejo aquí, en mi blog personal. El artículo está tal cual, sin ninguna modificación desde aquella fecha.</em></blockquote><br><img class="alignnone size-full wp-image-81" src="https://files.adrianistan.eu/NextDivel-3.png" alt="NextDivel-3" width="600" height="338" /><br><br>Continuamos esta serie de posts sobre cómo crear nuestro sistema operativo. Hoy no nos vamos a centrar en un tema sino que vamos a definir algunas funciones útiles de ahora en adelante. En primer lugar vamos a definir 3 funciones que cumplan la función de <strong>memcpy</strong>, <strong>memset</strong> y <strong>memcmp</strong>:<br><br><pre class="lang:c++ decode:true"><br>void* ND::Memory::Set(void* buf, int c, size_t len)<br>{<br>	unsigned char* tmp=(unsigned char*)buf;<br>	while(len--)<br>	{<br>		*tmp++=c;<br>	}<br>	return buf;<br>}<br>void* ND::Memory::Copy(void* dest,const void* src, size_t len)<br>{<br>	const unsigned char* sp=(const unsigned char*)src;<br>	unsigned char* dp=(unsigned char*)dest;<br>	for(;len!=0;len--) *dp++=*sp++;<br>	return dest;<br>}<br>int ND::Memory::Compare(const void* p1, const void* p2, size_t len)<br>{<br>	const char* a=(const char*)p1;<br>	const char* b=(const char*)p2;<br>	size_t i=0;<br>	for(;i&lt;len;i++)<br>	{<br>		if(a[i] &lt; b[i])<br>			return -1;<br>		else if(a[i] &gt; b[i])<br>			return 1;<br>	}<br>	return 0;<br>}<br></pre><br><br>Todas ellas se auto-implementan. Estas funciones yo las he sacado de una pequeña librería del C, la implementación suele ser parecida en todos los sistemas operativos. Ahora vamos a hacer 3 funciones simulares pero para manipular strings. Cumplirían la función de <strong>strcpy</strong>, <strong>strcat</strong> y <strong>strcmp</strong>.<br><br><pre class="lang:c++ decode:true"><br>size_t ND::String::Length(const char* src)<br>{<br>	size_t i=0;<br>	while(*src--)<br>		i++;<br>	return i;<br>}<br>int ND::String::Copy(char* dest, const char* src)<br>{<br>	int n = 0;<br>	while (*src)<br>	{<br>		*dest++ = *src++;<br>		n++;<br>	}<br>	*dest = '&#92;&#48;';<br>	return n;<br>}<br>int ND::String::Compare(const char *p1, const char *p2)<br>{<br>  int i = 0;<br>  int failed = 0;<br>  while(p1[i] != '&#92;&#48;' &amp;&amp; p2[i] != '&#92;&#48;')<br>  {<br>    if(p1[i] != p2[i])<br>    {<br>      failed = 1;<br>      break;<br>    }<br>    i++;<br>  }<br>  if( (p1[i] == '&#92;&#48;' &amp;&amp; p2[i] != '&#92;&#48;') || (p1[i] != '&#92;&#48;' &amp;&amp; p2[i] == '&#92;&#48;') )<br>    failed = 1;<br><br>  return failed;<br>}<br>char *ND::String::Concatenate(char *dest, const char *src)<br>{<br>  int di = ND::String::Length(dest);<br>  int si = 0;<br>  while (src[si])<br>    dest[di++] = src[si++];<br>  <br>  dest[di] = '&#92;&#48;';<br><br>  return dest;<br>}<br></pre><br><br>Vamos ahora con unas funciones bastante interesantes. Con estas funciones podremos leer y escribir en los puertos del hardware. Esto normalmente se hace con ASM y corresponde (en x86) a las instrucciones <strong>in</strong> y <strong>out</strong>. Para llamar de una manera fácil a ASM desde C se usa la instrucción <strong>asm</strong>, con el peligro que conlleva de que no es portable. A esta sentencia le añadimos el <strong>volatile</strong> para que GCC no intente optimizar ese texto. Por otra parte la instrucción asm tiene una forma curiosa de aceptar parámetros, pero eso creo que se entiende mejor viendo los ejemplos.<br><br><pre class="lang:c++ decode:true"><br>uint8_t ND::Ports::InputB(uint16_t _port)<br>{<br>	unsigned char rv;<br>	asm volatile(&quot;inb %1, %0&quot; : &quot;=a&quot;(rv) : &quot;dN&quot;(_port));<br>	return rv;<br>}<br>uint16_t ND::Ports::InputW(uint16_t port)<br>{<br>	uint16_t rv;<br>	asm volatile(&quot;inw %1, %0&quot; : &quot;=a&quot;(rv) : &quot;dN&quot;(port));<br>}<br>void ND::Ports::OutputB(uint16_t port, uint8_t value)<br>{<br>	asm volatile(&quot;outb %1, %0&quot; : : &quot;dN&quot;(port), &quot;a&quot;(value));<br>}<br></pre><br><br>Y hasta aquí el post 3, hoy no hemos hecho nada vistoso pero sí hemos definido una funciones que nos vendrán bien de cara a un futuro. Aviso a los usuarios de 64 bits que estoy trabajando en solucionar un <strong>bug</strong> que impide compilar correctamente en 64 bits. En el siguiente post veremos un componente importante de la arquitectura x86, la GDT.]]></description>
                <comments>https://blog.adrianistan.eu/emulando-linus-torvalds-crea-propio-sistema-operativo-desde-0-iii</comments>
                <pubDate>Sat, 28 Nov 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Emulando a Linus Torvalds: Crea tu propio sistema operativo desde 0 (II)</title>
                <link>https://blog.adrianistan.eu/emulando-linus-torvalds-crea-propio-sistema-operativo-desde-0-ii</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/emulando-linus-torvalds-crea-propio-sistema-operativo-desde-0-ii</guid>
                <description><![CDATA[<blockquote><em>Este artículo lo escribí para el blog en español <a href="http://blog.desdelinux.net">DesdeLinux</a> el 29 de diciembre de 2013 y ahora lo dejo aquí, en mi blog personal. El artículo está tal cual, sin ninguna modificación desde aquella fecha.</em></blockquote><br>Bienvenidos a otro post sobre como crear nuestro propio sistema operativo, en este caso NextDivel.<br><br>Si retomamos el código del <a href="https://blog.adrianistan.eu/2015/11/26/emulando-linus-torvalds-crea-propio-sistema-operativo-desde-0-i/">primer post</a> al final de todo nos debería haber salido algo como esto:<br><br><img class="alignnone size-full wp-image-80" src="https://files.adrianistan.eu/NextDivel-1.png" alt="NextDivel-1" width="600" height="338" /><br><br>Si esto es correcto podemos continuar. Voy a usar el sistema y la estructura que tengo en GitHub (http://github.com/AdrianArroyoCalle/next-divel) ya que es más cómodo para mí y para vosotros. Como se puede apreciar el texto es un texto básico, no resulta atractiv0. Puede parecer algo más del montón. Pero como dice el dicho, para gustos colores, y en nuestro sistema operativo habrá colores. Los primeros colores que vamos a poder poner van a ser los que definen las tarjetas VGA y son 16:<br><ol><br> 	<li>Negro</li><br> 	<li>Azul</li><br> 	<li>Verde</li><br> 	<li>Cyan</li><br> 	<li>Rojo</li><br> 	<li>Magenta</li><br> 	<li>Marrón</li><br> 	<li>Gris claro</li><br> 	<li>Gris oscuro</li><br> 	<li>Azul claro</li><br> 	<li>Verde claro</li><br> 	<li>Cyan claro</li><br> 	<li>Rojo claro</li><br> 	<li>Magenta claro</li><br> 	<li>Marrón claro</li><br> 	<li>Blanco</li><br></ol><br>Estos colores los vamos a definir en un header para tenerlo más a mano y quizá en un futuro formar parte de la API del sistema. Así creamos el archivo ND_Colors.hpp en el include de NextDivel.<br><br><pre class="lang:c++ decode:true"><br>#ifndef ND_COLOR_HPP<br>#define ND_COLOR_HPP<br><br>typedef enum ND_Color{ <br>	ND_COLOR_BLACK			= 0,<br>	ND_COLOR_BLUE			= 1,<br>	ND_COLOR_GREEN			= 2,<br>	ND_COLOR_CYAN			= 3,<br>	ND_COLOR_RED			= 4,<br>	ND_COLOR_MAGENTA		= 5,<br>	ND_COLOR_BROWN			= 6,<br>	ND_COLOR_LIGHT_GREY		= 7,<br>	ND_COLOR_DARK_GREY		= 8,<br>	ND_COLOR_LIGHT_BLUE		= 9,<br>	ND_COLOR_LIGHT_GREEN	= 10,<br>	ND_COLOR_LIGHT_CYAN		= 11,<br>	ND_COLOR_LIGHT_RED		= 12,<br>	ND_COLOR_LIGHT_MAGENTA	= 13,<br>	ND_COLOR_LIGHT_BROWN	= 14,<br>	ND_COLOR_WHITE			= 15<br><br>} ND_Color;<br>#endif<br></pre><br><br>A su vez vamos a definir nuevas funciones para escribir en pantalla de una manera más cómoda (no, todavía no vamos a implementar printf, sé que lo estais deseando). Crearemos un archivo y su header para un set de funciones relacionadas con la pantalla (ND<em>Screen.cpp y ND</em>Screen.hpp). En ellas vamos a crear funciones para: cambiar el color de las letras y el fondo, escribir frases y letras, limpiar la pantalla y desplazarnos por la pantalla. Seguimos usando las pantallas VGA pero ahora usaremos unos bytes que darán el color. ND_Screen.cpp quedaría como:<br><br><pre class="lang:c++ decode:true"><br>#include &lt;ND_Types.hpp&gt;<br>#include &lt;ND_Color.hpp&gt;<br>#include &lt;ND_Screen.hpp&gt;<br><br>uint16_t *vidmem= (uint16_t *)0xB8000;<br>ND_Color backColour = ND_COLOR_BLACK;<br>ND_Color foreColour = ND_COLOR_WHITE;<br>uint8_t cursor_x = 0;<br>uint8_t cursor_y = 0;<br><br>/**<br> * @brief Gets the current color<br> * @param side The side to get the color<br> * */<br>ND_Color ND::Screen::GetColor(ND_SIDE side)<br>{<br>	if(side==ND_SIDE_BACKGROUND){<br>		return backColour;<br>	}else{<br>		return foreColour;<br>	}<br>}<br>/**<br> * @brief Sets the color to a screen side<br> * @param side The side to set colour<br> * @param colour The new colour<br> * @see GetColor<br> * */<br>void ND::Screen::SetColor(ND_SIDE side, ND_Color colour)<br>{<br>	if(side==ND_SIDE_BACKGROUND)<br>	{<br>		backColour=colour;<br>	}else{<br>		foreColour=colour;<br>	}<br>}<br>/**<br> * @brief Puts the char on screen<br> * @param c The character to write<br> * */<br>void ND::Screen::PutChar(char c)<br>{<br>	uint8_t  attributeByte = (backColour &lt;&lt; 4) | (foreColour &amp; 0x0F);<br>	uint16_t attribute = attributeByte &lt;&lt; 8;<br>	uint16_t *location;<br>	if (c == 0x08 &amp;&amp; cursor_x)<br>	{<br>		cursor_x--;<br>	}else if(c == '\r')<br>	{<br>		cursor_x=0;<br>	}else if(c == '\n')<br>	{<br>		cursor_x=0;<br>		cursor_y=1;<br>	}<br>	if(c &gt;= ' ') /* Printable character */<br>	{<br>		location = vidmem + (cursor_y*80 + cursor_x);<br>		*location = c | attribute;<br>		cursor_x++;<br>	}<br>	if(cursor_x &gt;= 80) /* New line, please*/<br>	{<br>		cursor_x = 0;<br>		cursor_y++;<br>	}<br>	/* Scroll if needed*/<br>	uint8_t attributeByte2 = (0 /*black*/ &lt;&lt; 4) | (15 /*white*/ &amp; 0x0F);<br>	uint16_t blank = 0x20 /* space */ | (attributeByte2 &lt;&lt; 8);<br>	if(cursor_y &gt;= 25)<br>	{<br>       int i;<br>       for (i = 0*80; i &lt; 24*80; i++)<br>       {<br>           vidmem[i] = vidmem[i+80];<br>       }<br><br>       // The last line should now be blank. Do this by writing<br>       // 80 spaces to it.<br>       for (i = 24*80; i &lt; 25*80; i++)<br>       {<br>           vidmem[i] = blank;<br>       }<br>       // The cursor should now be on the last line.<br>       cursor_y = 24;<br>   }<br>}<br>/**<br> * @brief Puts a complete string to screen<br> * @param str The string to write<br> * */<br>void ND::Screen::PutString(const char* str)<br>{<br>	int i=0;<br>	while(str[i]) <br>	{<br>		ND::Screen::PutChar(str[i++]);<br>	}<br>}<br>/**<br> * @brief Cleans the screen with a color<br> * @param colour The colour to fill the screen<br> * */<br> void ND::Screen::Clear(ND_Color colour)<br>{<br>   // Make an attribute byte for the default colours<br>   uint8_t attributeByte = (colour /*background*/ &lt;&lt; 4) | (15 /*white - foreground*/ &amp; 0x0F);<br>   uint16_t blank = 0x20 /* space */ | (attributeByte &lt;&lt; 8);<br><br>   int i;<br>   for (i = 0; i &lt; 80*25; i++)<br>   {<br>       vidmem[i] = blank;<br>   }<br><br>   // Move the hardware cursor back to the start.<br>   cursor_x = 0;<br>   cursor_y = 0;<br>}<br>/**<br> * @brief Sets the cursor via software<br> * @param x The position of X<br> * @param y The position of y<br> * */<br>void ND::Screen::SetCursor(uint8_t x, uint8_t y)<br>{<br>	cursor_x=x;<br>	cursor_y=y;<br>}<br></pre><br><br>El header será muy básico así que no lo incluyo aquí, pero destacar la definición del tipo ND_SIDE<br><br><pre class="lang:c++ decode:true"><br>typedef enum ND_SIDE{<br>		ND_SIDE_BACKGROUND,<br>		ND_SIDE_FOREGROUND<br>}ND_SIDE;<br></pre><br><br>También mencionar que hacemos uso del header ND<em>Types.hpp, este header nos define unos tipos básicos para uint8</em>t, uint16<em>t, etc basado en los char y los int. Realmente este header es el en el estándar C99 y de hecho mi ND</em>Types.hpp es un copia/pega del archivo desde Linux, así que podeis intercambiarlos y no pasaría nada (solo hay definiciones, ninguna función).<br><br>Para probar si este código funciona vamos a modificar el punto de entrada en C del kernel:<br><br><pre class="lang:c++ decode:true"><br>	ND::Screen::Clear(ND_COLOR_WHITE);<br>	ND::Screen::SetColor(ND_SIDE_BACKGROUND,ND_COLOR_WHITE);<br>	ND::Screen::SetColor(ND_SIDE_FOREGROUND,ND_COLOR_GREEN);<br>	ND::Screen::PutString(&quot;NextDivel\n&quot;);<br>	ND::Screen::SetColor(ND_SIDE_FOREGROUND,ND_COLOR_BLACK);<br>	ND::Screen::PutString(&quot;Licensed under GNU GPL v2&quot;);<br></pre><br><br>Y si seguimos estos pasos obtendríamos este resultado<br><br><img class="alignnone size-full wp-image-81" src="https://files.adrianistan.eu/NextDivel-3.png" alt="NextDivel-3" width="600" height="338" /><br><br>Gracias a estas funciones que hemos creado podemos empezar a hacer pequeñas GUI, como por ejemplo un kernel panic que mostraremos cada vez que haya un error irrecuperable. Algo tal que así:<br><br><img class="alignnone size-large wp-image-82" src="https://files.adrianistan.eu/NextDivel-4-1024x578.png" alt="NextDivel-4" width="840" height="474" /><br><br>Y esta pequeña GUI la hicimos solamente con estas funciones:<br><pre class="lang:c++ decode:true"><br>void ND::Panic::Show(const char* error)<br>{<br>	ND::Screen::Clear(ND_COLOR_RED);<br>	ND::Screen::SetColor(ND_SIDE_BACKGROUND, ND_COLOR_WHITE);<br>	ND::Screen::SetColor(ND_SIDE_FOREGROUND, ND_COLOR_RED);<br>	ND::Screen::SetCursor(29,10); //(80-22)/2<br>	ND::Screen::PutString(&quot;NextDivel Kernel Error\n&quot;);<br>	ND::Screen::SetCursor(15,12);<br>	ND::Screen::PutString(error);<br>}<br></pre><br><br>Y aprovecho para daros las gracias por la excelente acogida que tuvo el primer post.]]></description>
                <comments>https://blog.adrianistan.eu/emulando-linus-torvalds-crea-propio-sistema-operativo-desde-0-ii</comments>
                <pubDate>Fri, 27 Nov 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Emulando a Linus Torvalds: Crea tu propio sistema operativo desde 0 (I)</title>
                <link>https://blog.adrianistan.eu/emulando-linus-torvalds-crea-propio-sistema-operativo-desde-0-i</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/emulando-linus-torvalds-crea-propio-sistema-operativo-desde-0-i</guid>
                <description><![CDATA[<blockquote><em>Este artículo lo escribí para el blog en español <a href="http://blog.desdelinux.net">DesdeLinux</a> el 27 de diciembre de 2013 y ahora lo dejo aquí, en mi blog personal. El artículo está tal cual, sin ninguna modificación desde aquella fecha.</em></blockquote><br>En esta serie vamos a emular a <strong>Linus Torvalds</strong>, vamos a crear nuestro sistema operativo desde 0. En este primer episodio vamos a ver el arranque y pondremos un texto en pantalla desde nuestro kernel.<br><br><img class="alignnone size-full wp-image-65" src="https://files.adrianistan.eu/LinusTorvalds.jpg" alt="LinusTorvalds" width="800" height="789" /><br><br>En mi caso el sistema operativo se llama <strong>NextDivel</strong>. La primera decisión que debemos hacer nada más plantearnos el sistema operativo es ¿cuál va a ser el bootloader?<br><br>Aquí existen múltiples variantes, e incluso podríamos crear uno nosotros; sin embargo, en este tutorial voy a usar GRUB, porque la mayoría conoce más o menos algo de él. Creamos una carpeta que será el root de nuestro sistema operativo y allí creamos la carpeta /boot/grub<br><pre><code>mkdir nextroot &amp;&amp; cd nextroot<br> mkdir -p boot/grub </code></pre><br>Allí creamos el fichero grub.cfg de la siguiente manera:<br><br><pre class="lang:default decode:true"><br>menuentry &quot;NextDivel&quot; {<br>	echo &quot;Booting NextDivel&quot;<br>	multiboot /next/START.ELF<br>	boot<br>}<br></pre><br><br>En este fichero hemos visto como <strong>GRUB</strong> cargará nuestro kernel, en este caso, en /next/START.ELF. Ahora debemos crear nuestro kernel.<br><br>Para ello necesitaremos el <strong>GCC</strong> y <strong>GAS</strong> (el ensamblador del proyecto <strong>GNU</strong>, suele venir con el gcc). Así pues vamos a crear el kernel.<br><br>Primero hacemos un archivo llamado kernel.asm. Este archivo contendrá el punto de inicio de nuestro kernel y además definirá el multiboot (una característica de algunos bootloaders como <strong>GRUB</strong>). El contenido de kernel.asm será:<br><br><pre class="lang:default decode:true"><br>.text<br>.globl start<br>start:<br>jmp multiboot_entry<br>.align 4<br>multiboot_header:<br>.long 0x1BADB002<br>.long 0x00000003<br>.long -(0x1BADB002+0x00000003)<br>multiboot_entry:<br>movl $(stack + 0x4000), %esp<br>call NextKernel_Main<br>loop: hlt<br>jmp loop<br>.section ".bss"<br>.comm stack,0x4000<br></pre><br><br>Todo lo relacionando con multiboot es simplemente seguir la especificación nada más. Todo empezará en start, llamará a multiboot_entry, habremos definido el multiboot header en los primeros 4k y lo pondremos (con movl).<br><br>Más tarde llamamos a NextKernel_Main que es nuestra función en C del kernel. En el loop hacemos un halt para parar el ordenador. Esto se compila con:<br><pre><code>as -o kernel.o -c kernel.asm </code></pre><br>Ahora vamos a entrar a programar en C. Pensarás que ahora todo es pan comido, ponemos un <strong>printf</strong> en <strong>main</strong> y ya está, lo hemos hecho.<br><br>Pues no, ya que printf y main son funciones que define el sistema operativo, ¡pero nosotros lo estamos creando! Solo podremos usar las funciones que nosotros mismos definamos.<br><br>En capítulos posteriores hablaré de como poner nuestra propia libraría del C (glibc, bionic, newlibc) pero tiempo al tiempo. Hemos hablado que queremos poner texto en pantalla, bueno veremos como lo hacemos.<br><br>Hay dos opciones, una es llamar a la <strong>BIOS</strong> y otra es manejar la memoria de la pantalla directamente. Vamos a hacer esto último pues es más claro desde C y además nos permitirá hacerlo cuando entremos en modo protegido.<br><br>Creamos un fichero llamado NextKernel_Main.c con el siguiente contenido:<br><br><pre class="lang:c++ decode:true"><br>int NextKernel_Main(/*struct multiboot *mboot_ptr*/)<br>{<br>	const char* str=&quot;NextDivel says Hello World&quot;, *ch;<br>	unsigned short* vidmem=(unsigned short*)0xb8000;<br>	unsigned i;<br>	for(ch=str, i=0;*ch;ch++, i++)<br>		vidmem[i]=(unsigned char) *ch | 0x0700;<br><br>	return 0;<br>}<br></pre><br><br>Con esto manipulamos directamente la memoria <strong>VGA</strong> y caracter a caracter lo vamos escribiendo. Compilamos desactivando la stdlib:<br><pre><code>gcc -o NextKernel_Main.o -c NextKernel_Main.c -nostdlib -fPIC -ffreestanding </code></pre><br>Si has llegado hasta aquí querrás probar ya tu nuevo y flamante sistema operativo, pero todavía no hemos terminado. Necesitamos un pequeño fichero que diga al compilador en que posición del archivo dejar cada sección. Esto se hace con un linker script. Creamos link.ld:<br><br><pre class="lang:default decode:true"><br>ENTRY(start)<br>SECTIONS<br>{<br>	. = 0x00100000;<br><br>	.multiboot_header :<br>	{<br>		*(.multiboot_header)<br>	}<br>    .text :<br>    {<br>        code = .; _code = .; __code = .;<br>        *(.text)<br>        . = ALIGN(4096);<br>    }<br><br>    .data :<br>    {<br>        data = .; _data = .; __data = .;<br>        *(.data)<br>        *(.rodata)<br>        . = ALIGN(4096);<br>    }<br><br>    .bss :<br>    {<br>        bss = .; _bss = .; __bss = .;<br>        *(.bss)<br>        . = ALIGN(4096);<br>    }<br><br>    end = .; _end = .; __end = .;<br>}<br></pre><br><br>Con esto definimos la posición de cada sección y el punto de entrada, start, que hemos definido en kernel.asm. Ahora ya podemos unir todo este mejunje:<br><pre><code>gcc -o START.ELF kernel.o NextKernel_Main.o -Tlink.ld -nostdlib -fPIC -ffreestanding -lgcc </code></pre><br>Ahora copiamos START.ELF al /next dentro de nuestra carpeta que simula el root de nuestro sistema operativo. Nos dirigimos a la carpeta root de nuestro sistema operativo nuevo con la consola y verificamos que hay dos archivos: uno /boot/grub/grub.cfg y otro /next/START.ELF.<br><br>Vamos al directorio superior y llamamos a una utilidad de creación ISOs con GRUB llamada <em>grub-mkrescue</em><br><pre><code>grub-mkrescue -o nextdivel.iso nextroot </code></pre><br>Una vez hayamos hecho esto tendremos una ISO. Esta ISO puede abrirse en ordenadores <strong>x86</strong> (64 bits también) y máquinas virtuales. Para probarlo, voy a usar <strong>QEMU</strong>. Llamamos a QEMU desde la línea de comandos:<br><pre><code>qemu-system-i386 nextdivel.iso </code></pre><br>Arrancará <strong>SeaBIOS</strong> y más tarde tendremos GRUB. Después si todo va correcto veremos nuestra frase. Pensarás que esto es difícil, te respondo, sí lo es.<br><br>Realmente crear un sistema operativo es difícil y eso que este de aquí no hace nada útil. En próximos capítulos veremos como manejar colores en la pantalla, reservar memoria y si puedo, como obtener datos del teclado.<br><br>Si alguien no quiere copiar todo lo que hay aquí, tengo un repositorio en GitHub (más elaborado) con el sistema operativo <strong>NextDivel</strong>. Si quieres compilar NextDivel solo tienes que tener git y cmake:<br><br><pre class="lang:default decode:true"><br>git clone https://github.com/AdrianArroyoCalle/next-divel <br>cd next-divel <br>mkdir build &amp;&amp; cd build <br>cmake .. <br>make <br>make DESTDIR=next install <br>chmod +x iso.sh <br>./iso.sh <br>qemu-system-i386 nextdivel.iso<br></pre><br><br>Os animo a colaborar en NextDivel si tienes tiempo y ganas de crear un sistema operativo. Quizá incluso superior a Linux… el tiempo lo dirá.]]></description>
                <comments>https://blog.adrianistan.eu/emulando-linus-torvalds-crea-propio-sistema-operativo-desde-0-i</comments>
                <pubDate>Thu, 26 Nov 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Hercólubus o planeta rojo</title>
                <link>https://blog.adrianistan.eu/hercolubus-planeta-rojo</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/hercolubus-planeta-rojo</guid>
                <description><![CDATA[<em><strong>Hercólubus o planeta rojo</strong></em>, de V. M. Rabolú.<br><br><img class="alignnone size-large wp-image-349" src="https://files.adrianistan.eu/Hercolubus-768x1024.jpg" alt="Hercolubus" width="768" height="1024" /><br><br>El peor libro que he leído en mucho tiempo. Llamarlo libro es faltar el respeto. Ideas descabelladas desmentidas por la ciencia con pruebas hace ya muchos años (no como el autor del libro que lo sabe todo por conocimiento místico). Técnicamente comete fallos gramaticales y no hay una cohesión entre los capítulos, pasando a hablar de temas que no tienen nada que ver.<br><br>En definitiva, si queréis pasároslo bien leyendo estupideces, leeros este libro. Yo no he podido parar de reír.]]></description>
                <comments>https://blog.adrianistan.eu/hercolubus-planeta-rojo</comments>
                <pubDate>Sat, 21 Nov 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Redox, el sistema operativo escrito en Rust</title>
                <link>https://blog.adrianistan.eu/redox-sistema-operativo-escrito-rust</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/redox-sistema-operativo-escrito-rust</guid>
                <description><![CDATA[Hoy voy a hablar de <a href="http://www.redox-os.org">Redox</a>, un sistema operativo relativamente nuevo, escrito totalmente en <a href="https://blog.adrianistan.eu/rust-101-tutorial-rust-espanol/">Rust</a>. Redox sigue la filosofía UNIX y la mayor parte del sistema es accesible a través del sistema de archivos. En Redox esta función la cumplen las URL. Además, es un sistema operativo seguro, una de las principales características de Rust. En Redox además todas las aplicaciones corren en modo sandbox.<br><h2 id="compilarredox">Compilar Redox</h2><br>Redox se distribuye únicamente a través del código fuente. En el futuro habrá imágenes ISO. Funciona en x86 de 32 bits y se está trabajando en el soporte x86_64 de 64 bits. En Debian/Ubuntu hay que seguir estas instrucciones.<br><pre><code>git clone http://github.com/redox-os/redox<br> cd redox <br>cd setup <br>./ubuntu.sh <br>./binary.sh <br>cd .. <br>make all </code></pre><br>Una vez haya terminado podemos ejecutar Redox en una máquina virtual. Yo voy a usar QEMU.<br><pre><code>sudo apt install qemu-system-x86 qemu-kvm <br>make qemu <br># make qemu_no_kvm </code></pre><br><h2 id="unvistazorpido">Un vistazo rápido</h2><br><img class="alignnone size-large wp-image-97" src="https://files.adrianistan.eu/Redox1-1024x712.png" alt="Redox1" width="840" height="584" /> <img class="alignnone size-large wp-image-98" src="https://files.adrianistan.eu/Redox2-1024x578.png" alt="Redox2" width="840" height="474" /> <img class="alignnone size-large wp-image-99" src="https://files.adrianistan.eu/Redox3-1024x768.png" alt="Redox3" width="840" height="630" /><br><br>Entramos directamente al escritorio gráfico, no ha hecho falta seleccionar nada. Redox es un sistema operativo diseñado con la interfaz ya en mente, no como los sistemas UNIX donde el sistema gráfico viene de terceras partes (X11, Quartz, DirectFB, Wayland, Mir, ...).<br><h2 id="aplicaciones">Aplicaciones</h2><br>Redox dispone de dos editores, el editor básico (que como vemos, el archivo que abre por defecto es none:/, el concepto de todo es una URL es básico en Redox) y Sodium, un editor más avanzado (tipo Vi o Emacs). Tenemos un explorador de archivos, un terminal de Lua, un terminal de comandos, un visor de imágenes y una aplicación de prueba de SDL. Han sido portados DOSBox, zlib, libpng, libiconv y FreeCiv a Redox. GCC, newlib y binutils están muy cerca de funcionar nativamente pero todavía hay algunos problemas.<br><h2 id="componentes">Componentes</h2><br><strong>Concepto de URL</strong>. En Redox todo debe ser una URL. Los registros se almacenan en log://, los daemons usan la interfaz bus://, /dev/null aquí es none://.<br><br><strong>fired</strong> es el sistema de arranque (SysV, systemd o Upstart en Linux), escrito totalmente en Rust. Usa ficheros Toml para la configuración, recurre a la paralelización pero solo tiene como dependencia el kernel Redox y no hace más cosa que el sistema de arranque (esto es un mensaje indirecto contra systemd).<br><br><strong>ZFS</strong>. El sistema de archivos principal será el magnífico <a href="https://wiki.archlinux.org/index.php/ZFS">ZFS</a> de Sun/Oracle. Todavía está en desarrollo pero el trabajo se está concentrando exclusivamente en ZFS.<br><br><strong>Oxide</strong>. Oxide es el gestor de paquetes de Redox. Todavía muy verde.<br><br><strong>Lua</strong>. Lua es el lenguaje usado para realizar scripts en Redox.<br><br><strong>Ion</strong>. Un shell más compatible con UNIX que Lua. Inspirado en fish y zsh.<br><br><strong>Bohr</strong>. El sistema gráfico de Redox.<br><br><strong>Orbital</strong>. El sistema de ventanas de Redox.]]></description>
                <comments>https://blog.adrianistan.eu/redox-sistema-operativo-escrito-rust</comments>
                <pubDate>Sun, 01 Nov 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Acortar enlaces en Node.js</title>
                <link>https://blog.adrianistan.eu/acortar-enlaces-node-js</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/acortar-enlaces-node-js</guid>
                <description><![CDATA[En alguna ocasión puede resultar necesario acortar enlaces desde nuestra aplicación en <a href="http://nodejs.org">Node.js</a>. Además, muchos acortadores añaden <strong>anuncios intersticiales</strong> de los que podemos sacar un <strong>dinero</strong>. Algunos ejemplos de acortadores que comparten ganancias son:<br><ul><br> 	<li><a href="http://adf.ly/?id=4869054">Adf.ly</a></li><br> 	<li><a href="http://bc.vc/?r=96749">Bc.vc</a></li><br> 	<li><a href="http://ouo.io/ref/kZrfrYdn">Ouo.io</a></li><br> 	<li><a href="https://coinurl.com/index.php?ref=aarroyo">CoinURL.com</a></li><br> 	<li><a href="http://shink.in/r/62630">Shink.in</a></li><br> 	<li><a href="https://shorte.st/es/ref/b4113e532f">Shorte.st</a></li><br> 	<li><a href="http://linkshrink.net/ref=f6A6T">LinkShrink.net</a></li><br></ul><br>Para facilitar el manejo de estos servicios y generar ingresos de manera sencilla he diseñado paquetes para todos esos servicios. Están disponibles en el registro de <a href="http://npmjs.com">npm</a> y todos usan una API similar.<br><h2 id="ejemploprctico">Ejemplo práctico</h2><br>Para el ejemplo voy a usar el paquete de Adf.ly, por ser quizá el proveedor de este tipo de enlaces más conocido.<br><br>Lo primero es instalar el paquete que provee acceso a Adf.ly:<br><pre><code>npm install adf.ly --save </code></pre><br>Ahora tenemos que cargar el módulo donde lo vayamos a usar. Aquí tenemos que escribir nuestra clave de API. Si lo dejais vacío seguirá funcionano, pero no ganareis nada, ¡los ingresos irán para mí!<br><br><pre class="lang:js decode:true"><br>var adfly=require(&quot;adf.ly&quot;)(&quot;TU_CLAVE_DE_API&quot;);<br>o<br>var adfly=require(&quot;adf.ly&quot;)();<br></pre><br><br>Para transformar un enlace en normal en uno acortado simplemente se usa el método <code>short</code>.<br><br><pre class="lang:js decode:true"><br>adfly.short(&quot;http://nexcono.appspot.com&quot;,function(url){<br>	console.log(&quot;Enlace acortado: &quot;+url);<br>    // Podemos usar la URL en algún motor de plantillas como Jade o EJS o donde queramos<br>});<br></pre><br><br><img class="alignnone size-full wp-image-10" src="https://files.adrianistan.eu/adfly-npm.png" alt="adfly-npm" width="990" height="478" /><br><br>En el paquete de Shink.in hay una particularidad. Shink.in permite acortar enlace en modo adulto. Esta opción está desactivada por defecto pero si quereis usarla solo hay que indicar <code>true</code> como tercer parámetro<br><br><pre class="lang:js decode:true"><br>shinkin.short(&quot;http://nexcono.appspot.com&quot;,procesarURL,true);<br>// El enlace se acorta en Modo Adulto<br></pre><br><br><h2 id="listadodepaquetes">Listado de paquetes</h2><br>El listado completo de paquetes que he creado es este. Todos tienen una API similar.<br><ul><br> 	<li><a href="https://www.npmjs.com/package/adf.ly">adf.ly</a></li><br> 	<li><a href="https://www.npmjs.com/package/bc.vc">bc.vc</a></li><br> 	<li><a href="https://www.npmjs.com/package/cur.lv">cur.lv</a></li><br> 	<li><a href="https://www.npmjs.com/package/ouo.io">ouo.io</a></li><br> 	<li><a href="https://www.npmjs.com/package/linkshrink.net">linkshrink.net</a></li><br> 	<li><a href="https://www.npmjs.com/package/sh.st">sh.st</a></li><br> 	<li><a href="https://www.npmjs.com/package/shink.in">shink.in</a></li><br></ul>]]></description>
                <comments>https://blog.adrianistan.eu/acortar-enlaces-node-js</comments>
                <pubDate>Sat, 31 Oct 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Usar GNU Parallel para aumentar el rendimiento de tus scripts</title>
                <link>https://blog.adrianistan.eu/usar-gnu-parallel-aumentar-rendimiento-tus-scripts</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/usar-gnu-parallel-aumentar-rendimiento-tus-scripts</guid>
                <description><![CDATA[La computación ha avanzado. Ha aumentado la potencia de cálculo. Y no lo ha hecho subiendo la velocidad del reloj del procesador, pues no queremos tener minitostadoras. En vez de eso se ha escogido el camino de <em>paralelizar</em>. Todo lo que sea susceptible de ser paralelizado deberá ser paralelizado. Desafortunadamente la programación en paralelo es compleja y requiere una planificación mucho más larga. ¡Pero no desistamos! ¡Podemos usar GNU Parallel para paralelizar algunas tareas que llevan tiempo pero son independientes las unas de las otras! <strong>¡Usemos <a href="http://www.gnu.org/software/parallel/">GNU Parallel</a> en nuestros scripts!</strong><br><br><img class="alignnone size-large wp-image-43" src="https://files.adrianistan.eu/GNUParallel-1024x576.png" alt="GNUParallel" width="840" height="473" /><br><br>Ejemplos prácticos:<br>- Convertir una biblioteca de MP3 en OGG (con ffmpeg)<br>- Normalizar el audio (con sox)<br>- Optimizar las imágenes de un sitio web (con OptiPNG, jpegoptim, etc)<br><h2 id="instalargnuparallel">Instalar GNU Parallel</h2><br>En Debian/Ubuntu:<br><pre><code>sudo apt install parallel<br></code></pre><br>En Fedora:<br><pre><code>dnf install parallel<br></code></pre><br>En openSUSE:<br><pre><code>zypper install gnu_parallel<br></code></pre><br>En Arch Linux:<br><pre><code>pacman -S parallel<br></code></pre><br>En NetBSD/SmartOS:<br><pre><code>pkgin install parallel<br></code></pre><br><h2 id="convertirunabibliotecademp3enoggynormalizarelaudio">Convertir una biblioteca de MP3 en OGG y normalizar el audio</h2><br>Esta era la tarea que tenía que realizar. Primero realicé una versión sencilla, que funcionaba utilizando un solo core del procesador.<br><br><pre class="lang:default decode:true"><br>#!/bin/bash<br>shopt -s globstar<br>for i in **/*.mp3; do<br>	BASENAME=&quot;${i%.mp3}&quot;<br>	ffmpeg -i &quot;${BASENAME}.mp3&quot; &quot;${BASENAME}.tmp.ogg&quot;<br>	sox --show-progress --norm &quot;${BASENAME}.tmp.ogg&quot; &quot;${BASENAME}.ogg&quot;<br>	rm &quot;${BASENAME}.tmp.ogg&quot;<br>done<br></pre><br><br>Con este script conseguimos el objetivo que nos habíamos propuesto, pero podemos optimizar el rendimiento. Usando GNU Parallel:<br><br><pre class="lang:default decode:true"><br>#!/bin/bash<br><br># Guardar como &quot;normalize.sh&quot;<br><br>if [ &quot;$2&quot; = &quot;1&quot;  ]; then<br>	BASENAME=&quot;${1%.mp3}&quot;<br>	ffmpeg -i &quot;${BASENAME}.mp3&quot; &quot;${BASENAME}.tmp.ogg&quot;<br>	sox --show-progress --norm &quot;${BASENAME}.tmp.ogg&quot; &quot;${BASENAME}.ogg&quot;<br>	rm &quot;${BASENAME}.tmp.ogg&quot;<br>else<br>	find  . -name &quot;*.mp3&quot; | parallel ./normalize.sh &quot;{}&quot; 1<br>fi<br></pre><br><br>Con esta modificación GNU Parallel se encarga de poner en cola los trabajos de conversión y normalización y los reparte entre los cores disponibles del procesador. La gráfica explica claramente la diferencia de uso entre los dos scripts.<br><h6 id="versinbsica">Versión básica</h6><br><img class="alignnone size-large wp-image-123" src="https://files.adrianistan.eu/TradicionalCore-1024x576.png" alt="TradicionalCore" width="840" height="473" /><br><h6 id="versingnuparallel">Versión GNU Parallel</h6><br><img class="alignnone size-large wp-image-43" src="https://files.adrianistan.eu/GNUParallel-1024x576.png" alt="GNUParallel" width="840" height="473" /><br><h2 id="optimizarlasimgenesdeunsitioweb">Optimizar las imágenes de un sitio web</h2><br>Aquí viene otro ejemplo que usa GNU Parallel para realizar la tarea más rápidamente.<br><br><pre class="lang:default decode:true"><br>#!/bin/bash<br><br># Guardar como &quot;optimize-img.sh&quot;<br><br>if [ &quot;$2&quot; = &quot;1&quot;  ]; then<br>	BASENAME=&quot;${1%.png}&quot;<br>	optipng -o7 &quot;${BASENAME}.png&quot;<br>elif [ &quot;$2&quot; = &quot;2&quot;  ]; then<br>	BASENAME=&quot;${1%.jpg}&quot;<br>	jpegoptim &quot;${BASENAME}.jpg&quot;<br>else<br>	find  . -name &quot;*.png&quot; | parallel ./optimize-img.sh &quot;{}&quot; 1<br>	find  . -name &quot;*.jpg&quot; | parallel ./optimize-img.sh &quot;{}&quot; 2<br>fi<br></pre><br>Hay muchos más usos para GNU Parallel, solo tienes que usar tu imaginación. ¿Y tú? ¿Conocías GNU Parallel? ¿Qué opinas al respecto?]]></description>
                <comments>https://blog.adrianistan.eu/usar-gnu-parallel-aumentar-rendimiento-tus-scripts</comments>
                <pubDate>Tue, 20 Oct 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>La gran lista de BitCoins gratis</title>
                <link>https://blog.adrianistan.eu/la-gran-lista-bitcoins-gratis</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/la-gran-lista-bitcoins-gratis</guid>
                <description><![CDATA[<blockquote>Esta página se mantiene por motivos históricos pero no creo que alguno de los enlaces funcione ahora (Marzo 2018)</blockquote><br>¿Quieres algún BitCoin extra? En estas páginas encontrarás metódos gratuitos para aumentar los números de tu monedero BitCoin. Las páginas se encuentran agrupadas por categorías y en ninguna es necesario registrarse.<br><br><img class="alignnone size-large wp-image-340" src="https://files.adrianistan.eu/BitCoin-1024x614.jpg" alt="Bitcoin" width="840" height="504" /><br><h2 id="visitarpginas">Visitar páginas</h2><br><ul><br> 	<li><a href="http://www.bitvisitor.com/?ref=1A2j8CwiFEhQ4Uycsjhr3gQPbJxFk1LRmM">BitVisitor</a></li><br> 	<li><a href="http://bitter.io/?r=35334">Bitter.io</a></li><br> 	<li><a href="http://earnfreebitcoins.com/?ref=1A2j8CwiFEhQ4Uycsjhr3gQPbJxFk1LRmM">EarnFreeBitcoins</a></li><br> 	<li><a href="http://visitbit.com/?r=1A2j8CwiFEhQ4Uycsjhr3gQPbJxFk1LRmM">VisitBit</a></li><br></ul><br><h2 id="faucets">Faucets</h2><br><h3 id="3minutos">3 minutos</h3><br><ul><br> 	<li><a href="http://www.ninjafaucet.com/?r=1A2j8CwiFEhQ4Uycsjhr3gQPbJxFk1LRmM">Ninja Faucet</a></li><br></ul><br><h3 id="4minutos">4 minutos</h3><br><ul><br> 	<li><a href="http://givecoins.net/?r=1A2j8CwiFEhQ4Uycsjhr3gQPbJxFk1LRmM">GiveCoins</a></li><br></ul><br><h3 id="5minutos">5 minutos</h3><br><ul><br> 	<li><a href="http://moonbit.co.in/?ref=a378051cf781">MoonBitcoin</a></li><br> 	<li><a href="http://qoinfaucet.com/?id=1A2j8CwiFEhQ4Uycsjhr3gQPbJxFk1LRmM">QoinFaucet</a></li><br> 	<li><a href="http://minionsfaucet.com/?r=1A2j8CwiFEhQ4Uycsjhr3gQPbJxFk1LRmM">MinionsFaucet</a></li><br> 	<li><a href="http://rollingfaucet.com/?r=1A2j8CwiFEhQ4Uycsjhr3gQPbJxFk1LRmM">RollingFaucet</a></li><br> 	<li><a href="http://jrswab.com/justgetbit/?r=1A2j8CwiFEhQ4Uycsjhr3gQPbJxFk1LRmM">Just Get Bit</a></li><br> 	<li><a href="http://takefreebitcoin.com/?r=7276e2df8a">Take Free Bitcoins</a></li><br></ul><br><h3 id="10minutos">10 minutos</h3><br><ul><br> 	<li><a href="http://www.crownfaucet.com/?r=1A2j8CwiFEhQ4Uycsjhr3gQPbJxFk1LRmM">CrownFaucet</a></li><br> 	<li><a href="http://www.MillionSatoshi.com/?r=1A2j8CwiFEhQ4Uycsjhr3gQPbJxFk1LRmM">Million Satoshi</a></li><br> 	<li><a href="http://www.satoshicity.org/?r=80800">SatoshiCity</a></li><br> 	<li><a href="http://playbitco.in/?ref=1A2j8CwiFEhQ4Uycsjhr3gQPbJxFk1LRmM">PlayBitco.in</a></li><br></ul><br><h3 id="15minutos">15 minutos</h3><br><ul><br> 	<li><a href="http://bitcoinker.com/faucet?ref=a24320d392f0">Bitcoinker</a></li><br> 	<li><a href="http://coins4all.tk/?id=93026">Coins4all</a></li><br></ul><br><h3 id="20minutos">20 minutos</h3><br><ul><br> 	<li><a href="http://bitcoingenie.info/?r=1A2j8CwiFEhQ4Uycsjhr3gQPbJxFk1LRmM">Bitcoin Genie</a></li><br> 	<li><a href="http://claimbtc.com/?r=3ad332679f">Claim BTC</a></li><br></ul><br><h3 id="30minutos">30 minutos</h3><br><ul><br> 	<li><a href="http://btc-free.eu/?r=1A2j8CwiFEhQ4Uycsjhr3gQPbJxFk1LRmM">BTC-Free.eu</a></li><br> 	<li><a href="http://faucetworld.eu/?r=1A2j8CwiFEhQ4Uycsjhr3gQPbJxFk1LRmM">faucetWorld</a></li><br> 	<li><a href="http://www.greenlifebitco.in/?id=1A2j8CwiFEhQ4Uycsjhr3gQPbJxFk1LRmM">GreenLifeBitcoin</a></li><br> 	<li><a href="http://waterbitco.in/?ref=1A2j8CwiFEhQ4Uycsjhr3gQPbJxFk1LRmM">Water Bitcoin</a></li><br></ul><br><h3 id="45minutos">45 minutos</h3><br><ul><br> 	<li><a href="http://busbitco.in/?id=1A2j8CwiFEhQ4Uycsjhr3gQPbJxFk1LRmM">BusBitcoin</a></li><br> 	<li><a href="http://freebetco.in/?id=1A2j8CwiFEhQ4Uycsjhr3gQPbJxFk1LRmM">FreeBitco.in</a></li><br></ul><br><h3 id="1hora">1 hora</h3><br><ul><br> 	<li><a href="http://bitcoinzebra.com/?ref=3b06e052b1cb">BitCoin Zebra</a></li><br> 	<li><a href="http://moneyinpjs.com/free-coin/?r=1A2j8CwiFEhQ4Uycsjhr3gQPbJxFk1LRmM">MoneyInPJS.com</a></li><br> 	<li><a href="http://icebitco.in/?id=1A2j8CwiFEhQ4Uycsjhr3gQPbJxFk1LRmM">IceBitcoin</a></li><br> 	<li><a href="http://welovebtc.com/?ref=1A2j8CwiFEhQ4Uycsjhr3gQPbJxFk1LRmM">We Love Bitcoin</a></li><br> 	<li><a href="http://infaucet.com/?id=1A2j8CwiFEhQ4Uycsjhr3gQPbJxFk1LRmM">InFaucet</a></li><br> 	<li><a href="http://elenabitco.in/?id=1A2j8CwiFEhQ4Uycsjhr3gQPbJxFk1LRmM">Elena BitCoin</a></li><br> 	<li><a href="http://artbitco.in/?id=1A2j8CwiFEhQ4Uycsjhr3gQPbJxFk1LRmM">ArtBitcoin</a></li><br> 	<li><a href="http://myfreebitcoins.org/?r=1A2j8CwiFEhQ4Uycsjhr3gQPbJxFk1LRmM">MyFreeBitcoins</a></li><br> 	<li><a href="http://bitcoins4.me">Bitcoins4me</a></li><br> 	<li><a href="http://web.archive.org/web/20160119125057/http://redcoinsco.aws.af.cm:80/index.php">RedCoins</a></li><br> 	<li><a href="http://bitcoinshowers.com/?r=1A2j8CwiFEhQ4Uycsjhr3gQPbJxFk1LRmM">BitCoin Showers</a></li><br> 	<li><a href="http://bitcoinsbest.com/faucet/?r=1A2j8CwiFEhQ4Uycsjhr3gQPbJxFk1LRmM">Bitcoins Best</a></li><br> 	<li><a href="http://moneyinpjs.com/best-bitcoin/?r=1A2j8CwiFEhQ4Uycsjhr3gQPbJxFk1LRmM">Best Bitcoin Faucet</a></li><br> 	<li><a href="http://coinsteller.com/?r=1A2j8CwiFEhQ4Uycsjhr3gQPbJxFk1LRmM">CoinsTeller</a></li><br> 	<li><a href="http://freebtcgiveaway.com/?r=1A2j8CwiFEhQ4Uycsjhr3gQPbJxFk1LRmM">Free BTC Giveaway</a></li><br> 	<li><a href="http://www.insatoshi.com/?id=4830">InSatoshi</a></li><br></ul><br><h3 id="24horas">24 horas</h3><br><ul><br> 	<li><a href="http://coinreaper.com/bunnyrun/">Bunny Run</a></li><br> 	<li><a href="http://bitcoiner.net/">Bitcoiner</a></li><br></ul>]]></description>
                <comments>https://blog.adrianistan.eu/la-gran-lista-bitcoins-gratis</comments>
                <pubDate>Sun, 04 Oct 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>js13k y Miss City (postmortem)</title>
                <link>https://blog.adrianistan.eu/js13k-miss-city-postmortem</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/js13k-miss-city-postmortem</guid>
                <description><![CDATA[Desde el 13 de agosto hasta el 13 de septiembre ha tenido lugar la competición <a href="http://js13kgames.com">js13kGames</a> 2015. El objetivo es construir un juego en HTML5, en el plazo de un mes o menos que no ocupe más de <strong>13kb</strong>. EL fichero que no debe superar los 13kb debe ser un fichero ZIP con compresión estándar que tenga un fichero <code>index.html</code> desde el cual arrancará el juego y todo lo necesario para su funcionamiento estará también en el fichero ZIP. JavaScript, CSS, imágenes, sonido, fuentes, etc deberán estar en el fichero ZIP que no supere los 13kb. Está explicitamente prohibido cargar algún recurso del exterior como Google Web Fonts, CDNs de JavaScript, imágenes en otro servidor, etc. Además hay un tema para los juegos, que fue anunciado el 13 de agosto. El tema ha sido <strong>Reversed</strong>.<br><br><img class="alignnone size-full wp-image-54" src="https://files.adrianistan.eu/js13k.png" alt="js13k" width="151" height="151" /><br><h2 id="misscity">MissCity</h2><br><img class="alignnone size-full wp-image-74" src="https://files.adrianistan.eu/MissCityGameplay.png" alt="MissCityGameplay" width="800" height="600" /><br><br>El juego que he presentado se llama <strong>MissCity</strong>. El nombre viene de darle la vuelta a Sim de SimCity. Mis no existe, pero Miss sí, y es perdido. Así MissCity es <em>ciudad perdida</em>.<br><blockquote><em>Euralia es la ciudad perfecta. Nuestra compañía desea construir un centro comercial en un solar abandonado pero el actual alcalde desea constuir una biblioteca. Dentro de poco son elecciones. ¡Debemos ganar las elecciones! Para ello puedes usar nuestro dron y repartir diversos tipos de ataques publicitarios a la población</em></blockquote><br>Los controles son:<br><ul><br> 	<li>WASD para desplazarse (si estamos en móvil o tablet, se usa la inclinación del dispositivo)</li><br> 	<li>VBNM para los 4 diferentes tipos de ataque (si estamos en un dispositivo táctil, aparecen cuatro botones en pantalla que realizan el mismo efecto)</li><br></ul><br>Ganamos:<br><ul><br> 	<li>Si conseguimos suficientes votos entre el electorado</li><br></ul><br>Perdemos:<br><ul><br> 	<li>Si pasan dos minutos</li><br> 	<li>Si nos quedamos sin dinero (cada ataque publicitario cuesta una cantidad de dinero sin especificar)</li><br></ul><br><img class="alignnone size-large wp-image-75" src="https://files.adrianistan.eu/MissCityOpera-1024x578.png" alt="MissCityOpera" width="840" height="474" /><br><br>Vamos a ver el postmortem en profundidad<br><h2 id="cosasquefueronbien">Cosas que fueron bien</h2><br><h3 id="pathfinding">PathFinding</h3><br>Los habitantes de Euralia son inteligentes, no van de una casilla a otra porque sí sino que tienen una ruta que realizar. El origen y el destino sí se calculan aleatoriamente, pero la ruta no, se usa un algoritmo de <em>pathfinding</em>. Debía encontrar una librería sencilla, pequeña, pero que implementase el algoritmo de manera limpia. Finalmente elegí <a href="http://easystarjs.com">EasyStar.js</a> que es <strong>asíncrona</strong>, sin dependencias y entre sus características asegura que es <strong>pequeña</strong> (~5kb) lo cual comprimido en ZIP resulta menos de lo que esperaba. Usa licencia MIT así que perfecto. El único inconveniente que presenta es que la rejilla que usa es bidimensional y yo definí la ciudad y el sistema de renderizado con un array unidimensional que voy cortando al procesarlo. Así que el juego tiene que transformar el array unidimensional en otro bidimensional para que EasyStar lo procese correctamente. Al obtener los resultados, es necesario volver a transformarlos.<br><h3 id="recursosgrficos">Recursos gráficos</h3><br>Creía que mi juego iba a tener peores gráficos, sinceramente. Las imágenes en formato GIF ocupaban menos de lo que esperaba y pude incluir bastantes detalles. Al principio no usé imágenes, tiré de colores en CSS. Renderizar toda la ciudad fue muy sencillo. Esta no es la versión final por supuesto, pero no es muy diferente.<br><br><pre class="lang:js decode:true"><br>// city es el array unidimensional donde defino el mapa de la ciudad<br><br>var draw=city.map(function(val){<br>    switch(val){<br>      case 0: return &quot;rgba(91,196,124,1)&quot;;<br>      case 1: return &quot;rgb(76, 77, 76)&quot;;<br>      case 2: return &quot;rgb(84, 230, 54)&quot;;<br>      case 3: return &quot;rgb(37, 88, 219)&quot;;<br>      case 4: return &quot;rgb(223, 155, 23)&quot;;<br>    }<br>  });<br>  var x=0,y=0;<br>  draw.forEach(function(cell){<br>	ctx.fillStyle=cell;<br>	ctx.fillRect(x,y,box,box);<br>	x+=box;<br>	if((x+box)&gt;id(&quot;a&quot;).width){<br>	  x=0;<br>	  y+=box;<br>	}<br>  });<br></pre><br><h3 id="debugenfirefoxparaandroid">Debug en Firefox para Android</h3><br>Me lo esperaba peor y realmente con WebIDE, el cable USB y ADB es muy sencillo ver la consola de JavaScript de Firefox para Android en tu ordenador.<br><h2 id="problemas">Problemas</h2><br><img class="alignnone size-large wp-image-73" src="https://files.adrianistan.eu/MissCityFirefox-1024x578.png" alt="MissCityFirefox" width="840" height="474" /><br><h2 id="dichosasapisdepantallayorientacin">Dichosas APIs de pantalla y orientación</h2><br>En HTML5 siempre me torturo con los aspect ratio y demás temas relacionados con la pantalla. En HTML5 hay tantas pantallas diferentes que simplemente no sé por donde empezar. El método que he usado en este juego es diferente al usado en otras ocasiones y daría para una entrada de blog suelta. Pero también me gustaría decir que las APIs de gestión de pantalla (saber si estás en modo <em>landscape</em> o <em>portrait</em>) no funcionan entre navegadores todavía. Incluso tienen nombres incompatibles que surgen de distintas versiones del estándar. Es una cosa que las aplicaciones nativas de móviles saben hacer desde el día 1.<br><h2 id="easyzip">EasyZIP</h2><br>En MissCity he usado <a href="http://gulpjs.com">Gulp</a> como herramienta que se encarga de la automatización de todo (ya sabes <a href="http://blog.adrianistan.eu/2015/08/04/haz-scripts">¡haz scripts!</a>). Usé <strong>EasyZIP</strong> para generar el fichero ZIP y posteriormente comprobar que su tamaño seguía siendo inferior a los 13kb. Mi sorpresa vino cuando al subir el fichero ZIP provoqué un error en el servidor de js13kgames. Tuve que contactar con el administrador, hubo que borrar archivos que se habían extraído correctamente en el servidor aunque hubiese devuelto un error. La solución fue comprimirlo manualmente con <strong>File Roller</strong> y el tamaño del fichero aumentó (sin pasar los 13kb).<br><h2 id="apidegestindeteclado">API de gestión de teclado</h2><br>Firefox recomienda usar <a href="https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key">KeyboardEvent.key</a> para leer el teclado y marca como obsoleta la manera antigua, que era <a href="https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode">KeyboardEvent.keyCode</a>. Leyendo MDN uno piensa que usando KeyboardEvent.key es la solución sin más. Y efectivamente en Firefox funciona bien, pero en Chrome y Opera no. Y pudiendo usar <code>keyCode</code>, quién va a usar <code>key</code>. <code>keyCode</code> será obsoleto pero funciona en todos los navegadores. Finalmente implementé el teclado usando <code>key</code> y <code>keyCode</code> si no soportan <code>key</code>.<br><h2 id="juega">Juega</h2><br>Si has leído hasta aquí, es un buen momento para jugar a MissCity. Hay un premio por compartir en redes sociales, si quieres ayudarme ya sabes.<br><div style="text-align: center;"><a href="http://js13kgames.com/entries/miss-city">MissCity</a></div><br>Código fuente: <a href="http://github.com/AdrianArroyoCalle/miss-city">http://github.com/AdrianArroyoCalle/miss-city</a>]]></description>
                <comments>https://blog.adrianistan.eu/js13k-miss-city-postmortem</comments>
                <pubDate>Sun, 20 Sep 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>eurocookie-js</title>
                <link>https://blog.adrianistan.eu/eurocookie-js</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/eurocookie-js</guid>
                <description><![CDATA[Hace ya algún tiempo que la ley europea en materia de privacidad se ha venido aplicando en <a href="https://www.cia.gov/library/publications/the-world-factbook/geos/sp.html">España</a>. La ley define que no se pueden almacenar datos que identifiquen al usuario con fines estadísticos (o publicitarios) a menos que se pida un consentimiento al usuario y este lo acepte. Solo lo deben cumplir aquellas personas que tengan un beneficio económico con la web. En empresas hay que aplicarlo siempre. El almacenamiento más usado para este tipo de identifición han sido las cookies, de ahí el nombre popular de <strong>"ley de cookies"</strong>.<br><br><img class="alignnone size-full wp-image-125" src="https://files.adrianistan.eu/UnionEuropea.jpg" alt="UnionEuropea" width="620" height="387" /><br><h2 id="odiseaentrelascookies">Odisea entre las cookies</h2><br>Yo uso cookies. Las uso en este blog y en otros sitios. <a href="https://www.google.com/analytics">Google Analytics</a> requiere consentimiento, <a href="https://disqus.com/">Disqus</a> requiere consentimiento, <a href="https://www.google.com/adsense">Google AdSense</a> requiere consentimiento. <strong>Los widgets sociales</strong> de Twitter, Facebook, Google+, etc requieren consentimiento.<br><blockquote><em>¿Pero entonces todas las cookies necesitan consentimiento?</em></blockquote><br>No. Sólo las que identifican al usuario con fines estadísticos, que en el caso de los gigantes de Internet es siempre. Si usamos nuestras propias cookies y no las conservamos para posterior análisis no haría falta y no hay que pedir consentimiento.<br><br><img class="alignnone size-full wp-image-23" src="https://files.adrianistan.eu/Cookies.jpg" alt="Cookies" width="419" height="245" /><br><blockquote><em>Cookies son las más usadas, pero si usas WebStorage (localStorage) o almacenamiento de Flash también tendrás que cumplir</em></blockquote><br><h2 id="eurocookiejsalrescate">eurocookie-js al rescate</h2><br>Basado en un pequeño plugin hecho por Google bajo licencia Apache 2.0. Se trata de un pequeño fichero JavaScript que al cargarse pedirá el consentimiento (si no ha sido preguntado antes). Este consentimiento es <strong>molesto</strong>, forzando al usuario a aceptar si quiere leer cómodamente la web. Una vez acepta el consentimiento se carga todo el JavaScript que necesitaba consentimiento. Esto último es algo que <strong>muchos plugins de consentimiento de cookies no hacen</strong>. Realmente no sé si simplemente avisando se cumple la ley, bajo mi interpretación <strong>no</strong>. Y por eso este plugin. Además eurocookie-js está traducido a una gran cantidad de idiomas de la Unión Europea (tomadas directamente de las traducciones oficiales de Google para webmasters sobre esta ley). Vamos a ver como se usa.<br><h2 id="instalandoeurocookiejs">Instalando eurocookie-js</h2><br>Instalar eurocookie-js es más simple que el mecanismo de un botijo. Hay 3 maneras:<br><ul><br> 	<li>npm - <code>npm install eurocookie-js --save</code>, pensado para usarlo con browserify</li><br> 	<li>bower - <code>bower install eurocookie-js --save</code></li><br> 	<li>directamente - descarga el archivo <code>index.js</code> de GitHub: <a href="http://github.com/AdrianArroyoCalle/eurocookie-js">http://github.com/AdrianArroyoCalle/eurocookie-js</a></li><br></ul><br><img class="alignnone size-full wp-image-15" src="https://files.adrianistan.eu/Biscuit.jpg" alt="Biscuit" width="236" height="397" /><br><h2 id="usandoeurocookiejs">Usando eurocookie-js</h2><br><h3 id="identificarjavascriptquenecesitaconsentimiento">Identificar JavaScript que necesita consentimiento</h3><br>Primero, necesitamos identificar que JavaScript necesita consentimiento. Si usa una etiqueta <code>script</code> es fácil. Es importante declarar el tipo MIME del script como texto plano y le añadimos la clase cookie.<br><br><pre class="lang:default decode:true"><br><br>&lt;script class=&quot;cookie&quot; type=&quot;text/plain&quot;&gt;<br> // USEMOS COOKIES FELIZMENTE, ENVIEMOS DATOS A LA NSA <br>&lt;/script&gt;<br></pre><br><br>En muchos casos bastará, por ejemplo con Disqus o con Google Analytics, puesto que cargan asíncronamente los archivos. En otros casos donde usemos el atributo <code>src</code> de <code>script</code> tendremos que modificar el código que se nos provee por el siguiente.<br><br><pre class="lang:default decode:true"><br><br>&lt;script src=&quot;http://servidor.com/archivo.js&quot;&gt;&lt;/script&gt;&lt;!-- Será sustituido por --&gt;&lt;script class=&quot;cookie&quot; type=&quot;text/plain&quot;&gt;<br> var file = document.createElement('script'); file.type = 'text/javascript'; file.async = true; file.src = &quot;http://servidor.com/archivo.js&quot;; document.getElementsByTagName('head')[0].appendChild(file); <br>&lt;/script&gt;<br></pre><br><br>Aunque si el proveedor te requería usar <code>script src</code> posiblemente no soporte la carga asíncrona.<br><h3 id="activandoeurocookiejs">Activando eurocookie-js</h3><br>Ahora solo nos falta activar eurocookie-js. Es fácil. Al final, antes de cerrar <code>body</code> tenemos que añadir lo siguiente:<br><h5 id="siusamosboweroelmtodomanual">Si usamos Bower o el método manual</h5><br><br><pre class="lang:default decode:true"><br><br>&lt;script&gt;<br> euroCookie(&quot;http://enlace-a-politica-de-privacidad.com&quot;); <br>&lt;/script&gt;<br></pre><br><br><h5 id="siusamosnpmbrowserify">Si usamos npm + browserify</h5><br><br><pre class="lang:js decode:true"> <br>var ec=require(&quot;eurocookie-js&quot;); <br>ec.euroCookie(&quot;http://link-to-privacy-policy.com&quot;); <br></pre><br><br>El fichero generado por browserify podrá ser añadido directamente al HTML.<br><h2 id="msinformacin">Más información</h2><br>Como siempre, toda la información está en GitHub: <a href="http://github.com/AdrianArroyoCalle/eurocookie-js">http://github.com/AdrianArroyoCalle/eurocookie-js</a>]]></description>
                <comments>https://blog.adrianistan.eu/eurocookie-js</comments>
                <pubDate>Wed, 16 Sep 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Tutorial de WiX (Windows Installer MSI)</title>
                <link>https://blog.adrianistan.eu/tutorial-wix-windows-installer-msi</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/tutorial-wix-windows-installer-msi</guid>
                <description><![CDATA[Siguiendo la estela del <a href="https://blog.adrianistan.eu/2015/06/22/tutorial-de-cmake/">tutorial de CMake</a> hoy traigo el tutorial de WiX.<br><h2>¿Qué es WiX?</h2><br>WiX es un proyecto opensource destinado a producir instaladores de la plataforma Windows Installer. Windows Installer es la plataforma de instalaciones preferida por Microsoft desde que comenzó su desarrollo en 1999. Los archivos MSI son los instaladores genéricos que usa Windows Installer. Provee de un entorno más seguro para modificar el sistema al de un EXE tradicional por el hecho de que Windows Installer es declarativo, no imperativo. Windows Installer es transaccional, facilita el despliegue en entornos empresariales, tiene APIs (Windows Installer API y Windows Installer SDK), permite la localización de manera sencilla, la validación de instalaciones y la gestión de reinicios. Tiene los siguientes inconvenientes: es complejo, los accesos directos, su línea de comandos, su uso del registro y sus herramientas. Así pues con WiX podemos crear paquetes MSI de Windows Installer con unos ficheros XML donde definimos la instalación.<br><br><img class="alignnone size-full wp-image-129" src="https://files.adrianistan.eu/WindowsInstaller.png" alt="WindowsInstaller" width="839" height="585" /><br><br>También conviene diferenciar los diferentes tipos de archivos que soporta Windows Installer.<br><ul><br> 	<li>MSI | Instalador convencional</li><br> 	<li>MSM | Módulo de fusión</li><br> 	<li>MSP | Módulo de parche</li><br> 	<li>MST | Módulo de transformación</li><br></ul><br>No hay que confundir estos archivos con los MSU, también usados por Microsoft pero para Windows Update y cuya estructura es diferente.<br><h2>Instalando WiX</h2><br>Para instalar WiX podemos usar Chocolatey. Abrimos PowerShell como administrador y ejecutamos.<br><br><pre class="lang:default decode:true"><br>choco install -y wixtoolset<br></pre><br><br>Si queremos añadir las herramientas al PATH, en PowerShell.<br><br><pre class="lang:default decode:true"><br>$PATH = [Environment]::GetEnvironmentVariable(&quot;PATH&quot;)<br>$WIX_BIN = &quot;C:\Program Files (x86)\WiX Toolset v3.9\bin&quot;<br>[Environment]::SetEnvironmentVariable(&quot;PATH&quot;,&quot;$PATH;$WIX_BIN&quot;)<br></pre><br><br><h2>Herramientas</h2><br>WiX viene con un conjunto de herramientas diseñadas para trabajar con Windows Installer.<br><ul><br> 	<li>candle.exe - Compila los archivos .wxs y .wxi para generar archivos objeto .wixobj</li><br> 	<li>light.exe - Enlaza los objetos .wixobj y .wixlib produciendo el .msi</li><br> 	<li>lit.exe - Permite unir archivos .wixobj en una librería .wixlib</li><br> 	<li>dark.exe - Convierte un archivo ya compilado en código fuente WiX</li><br> 	<li>heat.exe - Podemos añadir archivos al fichero WiX en masa, sin especificar manualmente</li><br> 	<li>insignia.exe - Permite firmar archivos MSI con las mismas firmas que los CABs</li><br> 	<li>melt.exe - Convierte un archivo MSM a un fichero código fuente de WiX</li><br> 	<li>torch.exe - Extrae las diferencias entre objetos WiX para generar una transformación. Usado para generar parches.</li><br> 	<li>smoke.exe - Valida ficheros MSI o MSM</li><br> 	<li>pyro.exe - Genera un fichero de parches MSP</li><br> 	<li>WixCop.exe - Recomienda estándares en los ficheros WiX</li><br> 	<li>WixUnit.exe - Realiza una validación a los ficheros WiX</li><br> 	<li>lux.exe - Usado para tests unitarios</li><br> 	<li>nit.exe - Usado para tests unitarios</li><br></ul><br><blockquote>Nota: Vamos a necesitar generar GUIDs. En PowerShell podeis usar `[GUID]::NewGuid()` o `[GUID]::NewGuid().ToString()`. Si teneis instalado Visual Studio, también está disponible `uuidgen`</blockquote><br><h2>El fichero .wxs</h2><br>El fichero WXS es la base de WiX y es un fichero XML. Para el ejemplo voy a empaquetar <a href="http://bitbucket.org/AdrianArroyoCalle/lumtumo">Lumtumo</a>. Lumtumo contiene un ejecutable, varias DLL y varios archivos como imágenes y fuentes que están distribuidos por carpetas. He depositado la carpeta con los binarios y todos lo necesario en SourceDir.<br><br><pre class="lang:default decode:true"><br>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;<br>&lt;!-- Variables del preprocesador --&gt;<br>&lt;?define Platform = x86 ?&gt;<br>&lt;?if $(var.Platform) = x64 ?&gt;<br>&lt;?define ProductName = &quot;Lumtumo (64 bit)&quot; ?&gt;<br>&lt;?define Win64 = &quot;yes&quot; ?&gt;<br>&lt;?define PlatformProgramFilesFolder = &quot;ProgramFiles64Folder&quot; ?&gt;<br>&lt;?else ?&gt;<br>&lt;?define ProductName = &quot;Lumtumo&quot; ?&gt;<br>&lt;?define Win64 = &quot;no&quot; ?&gt;<br>&lt;?define PlatformProgramFilesFolder = &quot;ProgramFilesFolder&quot; ?&gt;<br>&lt;?endif ?&gt;<br>&lt;Wix xmlns=&quot;http://schemas.microsoft.com/wix/2006/wi&quot;&gt;<br>	&lt;!-- Códigos de idioma https://msdn.microsoft.com/en-us/library/Aa369771.aspx --&gt;<br>	&lt;Product Name=&quot;Lumtumo&quot; Manufacturer=&quot;Adrian Arroyo Calle&quot; Id=&quot;BAE7D874-3177-46A6-BB2D-043EFF8C59F2&quot; UpgradeCode=&quot;457861BD-A051-4150-8752-82907E7BAF19&quot; Language=&quot;1033&quot; Codepage=&quot;1252&quot; Version=&quot;1.0&quot;&gt;<br>   &lt;!-- Nunca, NUNCA, se debe cambiar el valor de UpgradeCode. Es el identificador que distingue a los productos entre versiones --&gt;<br>    &lt;Package Description=&quot;$(var.ProductName)&quot; Platform=&quot;$(var.Platform)&quot; Keywords=&quot;Game&quot; Id=&quot;*&quot; Compressed=&quot;yes&quot; InstallScope=&quot;perMachine&quot; InstallerVersion=&quot;300&quot; Languages=&quot;1033&quot; SummaryCodepage=&quot;1252&quot; Comments=&quot;Space invaders redefined&quot; Manufacturer=&quot;Adrian Arroyo Calle&quot;/&gt;<br>	  &lt;!-- Generamos el listado de ficheros a instalar con heat.exe dir ..\..\build\Release\ -dr LumtumoDir -cg LumtumoComponent -gg -g1 -sf -srd -out files.wxs --&gt;<br>    &lt;!-- Es necesario actualizar Shortcut/@Target cada vez que usemos Heat para conservar la referencia al ejecutable principal válida --&gt;<br>    &lt;!-- Instalar con msiexec /i lumtumo.msi , desinstalar con msiexec /x lumtumo.msi--&gt;<br>    <br>        &lt;MajorUpgrade DowngradeErrorMessage=&quot;A later version of Lumtumo is already installed. Setup will now exit.&quot;/&gt;<br>    &lt;!-- Gestiona las actualizaciones posteriores. Estos MSI de actualización deben tener igual UpgradeCode, distinto Id y una versión superior--&gt;<br><br>    &lt;Icon Id=&quot;icon.ico&quot; SourceFile=&quot;SourceDir/Lumtumo.exe&quot;/&gt;<br>    &lt;!-- El icono puede estar en un ejecutable --&gt;<br>    &lt;Property Id=&quot;ARPPRODUCTICON&quot; Value=&quot;icon.ico&quot; /&gt;<br>    &lt;!-- Este es el icono del panel de control --&gt;<br>    &lt;Property Id=&quot;ARPHELPLINK&quot; Value=&quot;http://adrianarroyocalle.github.io&quot; /&gt;<br>    &lt;!-- Enlace de ayuda para el panel de control. Hay más propiedades con enlaces para el panel de control--&gt;<br><br>    &lt;MediaTemplate CabinetTemplate=&quot;LUM{0}.cab&quot; CompressionLevel=&quot;high&quot; EmbedCab=&quot;yes&quot;/&gt;<br>    &lt;!-- WiX se encargará de generar los archivos Cabinet necesarios. Esto será cuando llamemos a light.exe --&gt;<br>    <br>    &lt;Feature Id=&quot;ProductFeature&quot; Title=&quot;Game&quot; Level=&quot;1&quot;&gt;<br>      &lt;ComponentGroupRef Id=&quot;LumtumoComponent&quot;/&gt;<br>      &lt;ComponentRef Id=&quot;ApplicationShortcut&quot; /&gt;<br>    &lt;/Feature&gt;<br><br>    &lt;!-- Solo hay una funcionalidad, el juego completo. Esta incluye todos los archivos de Lumtumo y además el acceso directo en el menú de Inicio. LumtumoComponent está definido en files.wxs --&gt;<br><br>    &lt;Directory Id='TARGETDIR' Name='SourceDir'&gt;<br>      &lt;Directory Id='$(var.PlatformProgramFilesFolder)'&gt;<br>        &lt;Directory Id=&quot;LumtumoDir&quot; Name='Lumtumo'&gt;<br>        &lt;/Directory&gt;<br>      &lt;/Directory&gt;<br>      &lt;Directory Id=&quot;ProgramMenuFolder&quot;&gt;<br>        &lt;Directory Id=&quot;ApplicationProgramsFolder&quot; Name=&quot;Lumtumo&quot;/&gt;<br>      &lt;/Directory&gt;<br>    &lt;/Directory&gt;<br>    &lt;!-- La estructura de carpetas básica. TARGETDIR debe ser el ROOT siempre. Además, ProgramFilesFolder (en este caso usamos el preprocesador antes) y ProgramMenuFolder ya están definidos por Windows. Así que realmente creamos LumtumoDir (que usa files.wxs) y ApplicationProgramsFolder (justo abajo trabajamos con esa carpeta). Ambas se crean con el nombre de Lumtumo en el sistema de archivos --&gt;<br><br>    &lt;DirectoryRef Id=&quot;ApplicationProgramsFolder&quot;&gt;<br>      &lt;Component Id=&quot;ApplicationShortcut&quot; Guid=&quot;03466DA8-7C3F-485E-85E6-D892E9F7FFE4&quot;&gt;<br>        &lt;Shortcut Id=&quot;ApplicationStartMenuShortcut&quot; Name=&quot;Lumtumo&quot; Description=&quot;Space Invaders redefined&quot; Target=&quot;[#fil7BBA1E2E293173E87EC3F765BF048B16]&quot; Icon=&quot;icon.ico&quot; WorkingDirectory=&quot;LumtumoDir&quot;/&gt;<br>        &lt;RemoveFolder Id=&quot;ApplicationProgramsFolder&quot; On=&quot;uninstall&quot;/&gt;<br>        &lt;!-- Nos aseguramos de borrar la carpeta con el acceso directo al desinstalar--&gt;<br>        &lt;RegistryValue Root=&quot;HKCU&quot; Key=&quot;Software\Microsoft\Lumtumo&quot; Name=&quot;installed&quot; Type=&quot;integer&quot; Value=&quot;1&quot; KeyPath=&quot;yes&quot;/&gt;<br>        &lt;!-- Con esta llave del registro apareceremos en el panel de control para ser desinstalados--&gt;<br>      &lt;/Component&gt;<br>    &lt;/DirectoryRef&gt;<br><br>    &lt;Condition Message='This application only runs on Windows NT'&gt;<br>      VersionNT<br>    &lt;/Condition&gt;<br>    &lt;!-- Un ejemplo de condiciones. En este caso pedimos que Windows tenga el núcleo NT. Se pueden especificar rangos de versiones de Windows e incluso las ediciones. Las condiciones se pueden aplicar a componentes específicos también--&gt;<br>    <br>  &lt;/Product&gt;<br>&lt;/Wix&gt;<br></pre><br><br>Ahora se genera el archivo MSI<br><br><pre class="lang:default decode:true"><br>heat.exe dir SourceDir -dr LumtumoDir -cg LumtumoComponent -gg -g1 -sf -srd -out files.wxs<br># Editamos el archivo Lumtumo.wxs para actualizar la referencia al ejecutable principal en el acceso directo<br>candle.exe files.wxs<br>candle.exe Lumtumo.wxs<br>light.exe files.wixobj Lumtumo.wixobj -out lumtumo.msi<br></pre><br><br>Este ejemplo básico está muy bien, instala todo, añade un icono en el menú de inicio (también se puede poner en el escritorio pero no me gusta), se integra con el panel de control para hacer una desinstalación limpia e incluso con modificar un valor podemos generar el paquete para 64 bits. Sin embargo faltan cosas. No se puede especificar la carpeta de instalación, de hecho, el MSI no muestra ninguna opción al usuario al instalarse. Para introducir GUI en WiX tenemos que usar la librería WiXUI.<br><h2>WixUI</h2><br>Para usar WixUI tenemos que cambiar el comando que usamos para llamar a light<br><br><pre class="lang:default decode:true"><br>light.exe -ext WixUIExtension files.wixobj Lumtumo.wixobj -out lumtumo.msi<br></pre><br>Con esto ya podemos usar la librería WixUI. La manera más básica de añadir una GUI es introducir la GUI Minimal. Al final de la etiqueta Product podemos insertar<br><pre class="lang:default decode:true"><br>    &lt;WixVariable Id=&quot;WixUILicenseRtf&quot; Value=&quot;LICENSE.rtf&quot; /&gt;<br>    &lt;UIRef Id=&quot;WixUI_Minimal&quot; /&gt;<br>    &lt;UIRef Id=&quot;WixUI_ErrorProgressText&quot; /&gt;<br></pre><br><br>Y LICENSE.rtf en un archivo que tenemos junto a los ficheros WXS.<br><br>Si queremos un buen resultado visualmente hablando toca diseñar dos ficheros BMP.<br><pre class="lang:default decode:true"><br>&lt;WixVariable Id=&quot;WixUIBannerBmp&quot; Value=&quot;path\banner.bmp&quot; /&gt; &lt;!-- 493x58 --&gt;<br>&lt;WixVariable Id=&quot;WixUIDialogBmp&quot; Value=&quot;path\dialog.bmp&quot; /&gt; &lt;!-- 493x312 --&gt;<br></pre><br><br>Aquí uso la interfaz Minimal que lo único que hace es pedir aceptar la licencia e instalarse tal como habíamos visto antes. Hay más interfaces: WixUI_Mondo, WixUI_FeatureTree, WixUI_InstallDir y WixUI_Advanced. Todas ellas permiten más ajustes que WixUI_Minimal. También podemos crear ventanas personalizadas pero en mi opinión es algo a evitar. Las instalaciones deben ser lo más simples posibles y dejar a la aplicación en sí toda la configuración que necesite. Respecto a cambiar el directorio de instalación, no soy partidario pero si quereis la cuestión es sustituir LumtumoDir por INSTALLDIR (en hate.exe y en el fichero Lumtumo.wxs) y en la etiqueta Feature añadir `Display="expand" ConfigurableDirectory="INSTALLDIR"`<br><br><h2>Bundle y Bootstrap</h2><br>Un archivo MSI está bien pero quizá nos gusté más un EXE que además instale las dependencias.<br><br><pre class="lang:default decode:true"><br>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;<br>&lt;Wix xmlns=&quot;http://schemas.microsoft.com/wix/2006/wi&quot;&gt;<br>  &lt;Bundle Name=&quot;Lumtumo&quot; Version=&quot;1.0&quot; Manufacturer=&quot;Adrián Arroyo Calle&quot; UpgradeCode=&quot;AC44218D-B783-4DF9-B441-5AE54394DAA9&quot; AboutUrl=&quot;http://adrianarroyocalle.github.io&quot;&gt;<br>    &lt;!-- &lt;BootstrapperApplicationRef Id=&quot;WixStandardBootstrapperApplication.RtfLicense&quot; /&gt; --&gt;<br>    &lt;BootstrapperApplicationRef Id=&quot;WixStandardBootstrapperApplication.HyperlinkLicense&quot;&gt;<br>      &lt;bal:WixStandardBootstrapperApplication LicenseUrl=&quot;&quot; xmlns:bal=&quot;http://schemas.microsoft.com/wix/BalExtension&quot;/&gt;<br>    &lt;/BootstrapperApplicationRef&gt;<br><br>    &lt;Chain&gt;<br>      &lt;ExePackage Id=&quot;Dependency1&quot; SourceFile=&quot;Dependency_package_1.exe&quot; /&gt;<br>      &lt;ExePackage Id=&quot;Dependency2&quot; SourceFile=&quot;Dependency_package_2.exe&quot; /&gt;<br><br>      &lt;RollbackBoundary Id=&quot;RollBack&quot; /&gt;<br><br>      &lt;MsiPackage Id=&quot;MainPackage&quot; SourceFile=&quot;lumtumo.msi&quot; Vital=&quot;yes&quot; /&gt;<br>    &lt;/Chain&gt;<br>  &lt;/Bundle&gt;<br>&lt;/Wix&gt;<br></pre><br><br>Y compilamos<br><br><pre class="lang:default decode:true"><br>candle.exe setup.wxs<br>light.exe -ext WixBalExtension setup.wxs<br></pre><br><br>Y se genera el archivo `setup.exe`. Por defecto el archivo MSI se instala en modo silencioso por lo que la GUI pasa a ser la de Bootstrap.<br><br><h2>Más sintaxis WiX</h2><br><h3>Registrar un tipo de archivo</h3><br><pre class="lang:default decode:true"><br>&lt;ProgId Id='ACME.xyzfile' Description='Acme Foobar data file' Icon=&quot;icon.ico&quot;&gt;<br>    &lt;Extension Id='xyz' ContentType='application/xyz'&gt;<br>        &lt;Verb Id='open' Command='Open' TargetFile='[#ID_DE_EJECUTABLE]' Argument='&quot;%1&quot;' /&gt;<br>    &lt;/Extension&gt;<br>&lt;/ProgId&gt;<br></pre><br><h3>Escribir configuración en archivo INI</h3><br><pre class="lang:default decode:true"><br>&lt;IniFile Id=&quot;WriteIntoIniFile&quot; Action=&quot;addLine&quot; Key=&quot;InstallDir&quot; Name=&quot;Foobar.ini&quot; Section=&quot;Paths&quot; Value=&quot;[INSTALLDIR]&quot; /&gt;<br></pre><br><h3>Escribir en el registro</h3><br><pre class="lang:default decode:true"><br>&lt;RegistryKey Id='FoobarRegInstallDir' Root='HKLM' Key='Software\Acme\Foobar 1.0' Action='createAndRemoveOnUninstall'&gt;<br>    &lt;RegistryValue Type='string' Name='InstallDir' Value='[INSTALLDIR]'/&gt;<br>    &lt;RegistryValue Type='integer' Name='Flag' Value='0'/&gt;<br>&lt;/RegistryKey&gt;<br></pre><br><h3>Borrar archivos extra en la desinstalación</h3><br><pre class="lang:default decode:true"><br>&lt;RemoveFile Id='LogFile' On='uninstall' Name='Foobar10User.log' /&gt;<br>&lt;RemoveFolder Id='LogFolder' On='uninstall' /&gt;<br></pre>]]></description>
                <comments>https://blog.adrianistan.eu/tutorial-wix-windows-installer-msi</comments>
                <pubDate>Thu, 03 Sep 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Lista de sponsors para juegos HTML5</title>
                <link>https://blog.adrianistan.eu/lista-sponsors-juegos-html5</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/lista-sponsors-juegos-html5</guid>
                <description><![CDATA[Los sponsors son los encargados de atraer tráfico a tus juegos. Aquí esta la lista de sponsors que aceptan HTML5.<br><h2 id="mercadosdejuegos">Mercados de juegos</h2><br><ul><br> 	<li><a href="http://fgl.com">FGL</a></li><br> 	<li><a href="http://marketjs.com">MarketJS</a></li><br> 	<li><a href="http://boostermedia.com">BoosterMedia</a></li><br> 	<li><a href="http://orangegames.com/">Orange Games</a></li><br> 	<li><a href="http://www.nazara.com">Nazara</a></li><br> 	<li><a href="http://www.softgames.de">Soft Games</a></li><br> 	<li><a href="http://www.spilgames.com">Spil Games</a></li><br> 	<li><a href="http://clay.io">Clay.io</a></li><br> 	<li><a href="http://www.spreadmygame.com/">SpreadMyGame</a></li><br> 	<li><a href="http://www.gamepix.com">GamePix</a></li><br></ul><br><h2 id="portalesdejuegos">Portales de juegos</h2><br><ul><br> 	<li><a href="http://multiversos.herokuapp.com">Multiversos</a></li><br> 	<li><a href="http://kongregate.com">Kongregate</a></li><br> 	<li><a href="http://developers.facebook.com">Facebook</a></li><br> 	<li><a href="http://games.com">AOL Inc.</a></li><br> 	<li><a href="http://www.minijuegos.com">MiniJuegos</a></li><br> 	<li><a href="http://www.mocospace.com">MocoSpace</a></li><br> 	<li><a href="http://www.onemorelevel.com">One More Level</a></li><br> 	<li><a href="http://pakap.com">Pakap</a></li><br> 	<li><a href="http://clay.io">Clay.io</a></li><br> 	<li><a href="https://developer.mobage.com/">Mobage / DeNA </a></li><br> 	<li><a href="https://www.youdagames.com/en/developers/">Youda Games</a></li><br> 	<li><a href="http://html5games.com/">HTML5Games</a></li><br> 	<li><a href="http://m.jeux.com">Jeux.com</a></li><br> 	<li><a href="http://m.clickjogos.uol.com.br">ClickJogos</a></li><br> 	<li><a href="http://m.roundgames.com">RoundGames</a></li><br> 	<li><a href="http://www.kanogames.com">KanoGames</a></li><br> 	<li><a href="http://m.hopy.com">Hopy</a></li><br></ul><br><img class="alignnone size-full wp-image-56" src="https://files.adrianistan.eu/Kongregate.jpg" alt="Kongregate" width="225" height="246" /><br><h2 id="tiendastradicionales">Tiendas tradicionales</h2><br>Algunas permiten cobrar, otras son 100% gratis y deben ser monetizadas por anuncios o micropagos.<br><ul><br> 	<li><a href="https://chrome.google.com/webstore">Chrome Web Store</a></li><br> 	<li><a href="http://marketplace.firefox.com">Firefox Marketplace</a></li><br> 	<li><a href="http://apple.com">App Store</a></li><br> 	<li><a href="http://play.google.com">Google Play</a></li><br> 	<li><a href="https://www.amazon.es/gp/feature.html?ie=UTF8&amp;docId=1000644893">Amazon AppStore</a></li><br> 	<li><a href="http://pokki.com">Pokki</a></li><br> 	<li><a href="http://f-droid.org">F-Droid</a></li><br> 	<li><a href="http://m.aptoide.com">Aptoide</a></li><br> 	<li><a href="http://pcappstore.baidu.com">Baidu App Store</a></li><br> 	<li><a href="http://app.mi.com">Xiaomi App Store</a></li><br> 	<li><a href="http://www.wandoujia.com">Wandoujia</a></li><br> 	<li><a href="http://android.myapp.com">Tencent App Gem</a></li><br> 	<li><a href="http://softonic.com">Softonic</a></li><br> 	<li><a href="http://developer.ubuntu.com">Ubuntu Software Center</a></li><br> 	<li><a href="http://seller.tizenstore.com">Tizen Store</a></li><br> 	<li><a>Tienda Windows</a></li><br> 	<li><a href="http://apple.com">Mac App Store</a></li><br></ul><br><h2 id="tiendasespecializadas">Tiendas especializadas</h2><br><ul><br> 	<li><a href="http://d.cn">D.cn Games Center</a></li><br> 	<li><a href="http://itch.io">Itch.io</a></li><br> 	<li><a href="http://steamcommunity.com/greenlight/">Steam</a></li><br> 	<li><a href="http://www.gamestreamer.com/">GameStreamer</a></li><br> 	<li><a href="http://desura.com">Desura</a></li><br> 	<li><a href="http://gog.com">GOG</a></li><br> 	<li><a href="http://www.getgamesgo.com/">Get Games Go</a></li><br> 	<li><a href="http://www.greenmangaming.com">Green Man Gaming</a></li><br> 	<li><a href="http://partners.gamehouse.com/">Game House</a></li><br> 	<li><a href="http://www.beamdog.com/publisher">BeamDog</a></li><br> 	<li><a href="https://developer.impulsedriven.com/">GameStop</a></li><br> 	<li><a href="http://www.awomo.com/page/publishers_developers">Awomo</a></li><br> 	<li><a href="http://www.gamersgate.com/info/about-us">GamersGate</a></li><br> 	<li><a href="https://indiegamestand.com/developerSignUp.php">IndieGameStand</a></li><br> 	<li><a href="https://www.humblebundle.com/developer">Humble Bundle</a></li><br> 	<li><a href="https://uk.gamesplanet.com/">GamesPlanet</a></li><br> 	<li><a href="http://showmethegames.com">Show me the Games</a></li><br> 	<li><a href="http://playism-games.com">PLAYISM</a></li><br> 	<li><a href="http://www.gameolith.ltd.uk/">Gameolith</a></li><br> 	<li><a href="http://es.kawagames.com">Kawagames</a></li><br> 	<li><a href="https://developer.amazon.com/appsandservices/community/post/TxCTD48Y4EP7S3/Amazon-Loves-Indie-Introducing-the-Indie-Games-Store-for-PC-Mac-and-browser-base">Amazon Indie Games Store</a></li><br></ul><br><img class="alignnone size-full wp-image-38" src="https://files.adrianistan.eu/firefox-os.png" alt="firefox-os" width="558" height="359" /><br><h2 id="cmsdejuegos">CMS de juegos</h2><br><ul><br> 	<li><a href="http://gamezboost.com/">GameZBoost</a></li><br></ul><br><h2 id="streaming">Streaming</h2><br><ul><br> 	<li><a href="http://www.gametap.com/">GameTap</a></li><br></ul><br>Si conoces alguno más, dímelo. Te lo agradeceré.]]></description>
                <comments>https://blog.adrianistan.eu/lista-sponsors-juegos-html5</comments>
                <pubDate>Sat, 15 Aug 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Probando OpenIndiana</title>
                <link>https://blog.adrianistan.eu/probando-openindiana</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/probando-openindiana</guid>
                <description><![CDATA[Me he decidido a instalar <a href="http://openindiana.org">OpenIndiana</a>. Para esto he usado la última nightly disponible fechada a 30 de marzo de 2015.<br><br><img class="alignnone size-large wp-image-91" src="https://files.adrianistan.eu/OpenIndianaInstall-1024x576.png" alt="OpenIndianaInstall" width="840" height="473" /><br><br><strong>OpenIndiana</strong> entra por defecto en GNOME 2 en modo Live. Desde allí podemos lanzar el instalador para instalarlo en el disco.<br><h2 id="instalandopaquetes">Instalando paquetes</h2><br>OpenIndiana usa <strong>IPS</strong> como gestor de paquetes. Para poder instalar paquetes debemos usar los siguientes comandos<br><pre><code class="sh">sudo su <br>pkg install git <br>pkg install gcc-48 </code></pre><br>Para buscar paquetes<br><pre><code>pkg search -pr NOMBRE_PAQUETE </code></pre><br>IPS puede estar desactualizado. Afortunadamente, ya que SmartOS y OpenIndiana comparten el kernel illumos es posible usar los paquetes de SmartOS. Para ello hay que instalar <strong>pkgin</strong>. Añadimos al PATH las rutas /opt/local/sbin y /opt/local/bin. Usamos el archivo <strong>.profile</strong>.<br><pre><code>PATH=/opt/local/sbin:/opt/local/bin:$PATH <br>export PATH </code></pre><br>Después usamos el instalador<br><pre><code>curl http://pkgsrc.joyent.com/packages/SmartOS/bootstrap/bootstrap-2015Q2-x86_64.tar.gz | gtar -zxpf - -C / </code></pre><br>Una vez hecho esto podemos actualizar la base de datos de pkgin<br><pre><code>pkgin -y update </code></pre><br>Y podemos instalar normalmente<br><pre><code>pkgin in mercurial <br>pkgin in wxGTK30 </code></pre><br>De todos modos yo no instalaría ninguna aplicación GUI vía pkgin.<br><br><img class="alignnone size-large wp-image-90" src="https://files.adrianistan.eu/OpenIndiana-1024x576.png" alt="OpenIndiana" width="840" height="473" />]]></description>
                <comments>https://blog.adrianistan.eu/probando-openindiana</comments>
                <pubDate>Fri, 14 Aug 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>¡Haz scripts!</title>
                <link>https://blog.adrianistan.eu/haz-scripts</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/haz-scripts</guid>
                <description><![CDATA[¡Haz scripts! ¡Escribe scripts! ¡Programa scripts! ¡Ejecuta scripts! ¡Automatiza! Escribir scripts tiene muchas ventajas. Escribir scripts es la mejor manera de ser productivo, mantener el entorno actualizado y evitar el error humano. ¡Automatiza!<br><br><img title="" src="http://imgs.xkcd.com/comics/automation.png" alt="Automation" /><br><h2 id="seesmsproductivo">Se es más productivo</h2><br>Escribir scripts es la mejor manera de ser productivo. Perdemos parte de nuestro tiempo inicialmente para posteriormente recuperarlo en el futuro. Escribir un script es una inversión.<br><h2 id="estansiempreactualizados">Estan siempre actualizados</h2><br>Puede que hoy sepas el procedimiento exacto a realizar para una determinada tarea pero en el futuro puede que no lo sepas. El script siempre está actualizado, pues en cuanto deja de funcionar se debe reparar para seguir usándose. La documentación por ejemplo<br><br><img class="alignnone size-large wp-image-14" src="https://files.adrianistan.eu/bash-1024x768.png" alt="bash" width="840" height="630" /><br><h2 id="menoserrores">Menos errores</h2><br>Tener un script reduce la posibilidad de error humano. Piensa que cada vez que hacemos algo de memoria podemos fallar. ¡Y puede que no sepamos cuando ha pasado eso!<br><h2 id="fcilesdecompartir">Fáciles de compartir</h2><br>Hoy día es muy fácil compartir scripts con el mundo. Cualquier programa de 'paste' funciona y sirve. <a href="http://pastebin.com">Pastebin</a>, <a href="http://bitbin.it">BitBin.it</a>, <a href="http://paste.desdelinux.net">DesdeLinux Paste</a>, <a href="http://gist.github.com">GitHub Gist</a>, <a href="https://bitbucket.org/snippets/">BitBucket Snippets</a>, <a href="http://pastebin.mozilla.org">Mozilla Paste</a>, <a href="http://paste.ubuntu.com">Ubuntu Paste</a>, <a href="http://cryptobin.org">CryptoBin</a>, <a href="http://pasted.co">Pasted</a>, ...<br><br>Algunos incluso te pagan por las visitas<br><h2 id="sontusherramientas">Son TUS herramientas</h2><br>Eres programador. Usas herramientas diseñadas por otros programadores. ¿Por qué no hacer tú alguna de tus herramientas? Agilizan tu entorno de trabajo como solo tu sabes. El verdadero maestro conoce sus herramientas al dedillo, si las diseñas todo ya tendrás mucho trecho hecho.<br><br><img class="alignnone size-full wp-image-95" src="https://files.adrianistan.eu/powershell.png" alt="powershell" width="670" height="300" /><br><h2 id="aquesperas">¿A qué esperas?</h2><br>Creo que se ha quedado demostrado que quiero que hagas scripts; que automatices. Da igual el lenguaje que uses, pero usa tus propios scripts. Puedes usar Perl, Python, JavaScript, Ruby, Tcl, Bash, Fish, PowerShell, Lua y muchos más.]]></description>
                <comments>https://blog.adrianistan.eu/haz-scripts</comments>
                <pubDate>Tue, 04 Aug 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>¿Qué hacer ante la falta de espacio en Android?</title>
                <link>https://blog.adrianistan.eu/ante-la-falta-espacio-android</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/ante-la-falta-espacio-android</guid>
                <description><![CDATA[Me pide mucha gente que les ayude a liberar espacio de su teléfono. Así que voy a juntar un poco todo lo que conozco. En algunos pasos voy a asumir que tenemos el teléfono con <strong>root</strong>.<br><br><img class="alignnone size-full wp-image-12" src="https://files.adrianistan.eu/Android.jpg" alt="Android" width="384" height="640" /><br><h2 id="0pasarimgenesvdeoywhatsappsalasd">0. Pasar imágenes, vídeo y WhatsApps a la SD</h2><br>Pongo 0 porque esto lo deberías haber hecho nada más recibir el teléfono. Si el teléfono no soporta SD, borrar cosas y si se consideran importantes pasar al ordenador.<br><h2 id="1borrarlacachdelasaplicaciones">1. Borrar la caché de las aplicaciones</h2><br>Se puede hacer desde la interfaz de ajustes de Android o desde Link2SD.<br><h2 id="2pasaraplicacionesalatarjetasd">2. Pasar aplicaciones a la tarjeta SD</h2><br>Usa Link2SD. Tiene un modo básico con el script app2sd y otro más avanzado que funciona con particiones.<br><h2 id="3borraraplicacionesinnecesariasdeusuario">3. Borrar aplicaciones innecesarias de usuario</h2><br>Usa Link2SD o en los ajustes de Android.<br><h2 id="4borraraplicacionesinnecesariasdelsistema">4. Borrar aplicaciones innecesarias del sistema</h2><br>Usa Link2SD. Usa esto con cuidado. Me encuentro que mucha gente puede borrar sin problemas las Play cosas (menos Play Store) y las aplicaciones de la operadora. También puedes:<br><pre><code>mount -o remount,rw /system rm /system/app/APLICACION.apk mount -o remount,ro /system </code></pre><br><h2 id="5limpialadalivkcache">5. Limpia la Dalivk Cache</h2><br>Usa Link2SD. Si no lo puedes usar<br><pre><code>rm /data/dalvik-cache/* </code></pre><br><h2 id="6borrardatosdealgunasaplicaciones">6. Borrar datos de algunas aplicaciones</h2><br>Ciertas aplicaciones usan datos a lo tonto. Mejor borrar y empezar de nuevo. Sé cuidadoso.<br><h2 id="7pasarsdmaid">7. Pasar SD Maid</h2><br>Si lo tienes instalado perfecto. Si no, sigue más adelante.<br><h2>8. Usar FilesGo</h2><br><a href="https://play.google.com/store/apps/details?id=com.google.android.apps.nbu.files&amp;hl=es">FilesGo</a> es una aplicación de Google que encuentra archivos innecesarios en el teléfono y propone su borrado. Muy útil.<br><h2>9. Borra la caché de Telegram</h2><br>En Telegram -&gt; Ajustes -&gt;Datos y almacenamiento -&gt; Uso del almacenamiento -&gt; Borrar caché. Es totalmente seguro y todo se puede volver a descargar.<br><h2>10. Usa Google Photos</h2><br>Google Photos hace un backup de las fotos y permite borrar las fotos antiguas del teléfono que ya tengan copia de seguridad en la nube.<br><h2>11. Borra copias de seguridad de WhatsApp</h2><br>WhatsApp almacena copias de seguridad por la noche. Los ficheros en Whatsapp/Databases que contienen fechas se pueden borrar. Cuidado de no borrar el que no tiene fecha.<br><h2>12. Borra contenido de grupos de WhatsApp</h2><br>En Ajustes-&gt; Datos y almacenamiento -&gt; Uso de almacenamiento -&gt; Selecciona el grupo que quieras borrar. Aquí a diferencia de Telegram, los mensajes NO se pueden recuperar.<br><h2 id="8cambiarelporcentajedealerta">13. Cambiar el porcentaje de alerta</h2><br>En la terminal o usando adb shell.<br><pre><code>sqlite3 /data/data/com.android.providers.settings/databases/settings.db <br>insert into secure (name, value) VALUES('sys_storage_threshold_percentage','5'); <br>insert into gservices (name, value) VALUES('sys_storage_threshold_percentage','5'); <br>.quit </code></pre><br>¿Y si no tengo SQlite 3 instalado en mi teléfono? Algo totalmente comprensible, en cuyo caso deberás descargar una versión ya compilada (también puedes compilarla tú con el Android NDK). En <a href="http://forum.xda-developers.com/showthread.php?t=2730422">este hilo de XDA Developers</a> lo puedes encontrar actualizado.<br><h2 id="9optimizarbasesdedatos">14. Optimizar bases de datos</h2><br><pre><code>for i in $(find /data -iname "*.db"); do sqlite3 $i 'VACUUM;'; done </code></pre><br>quizá esto también valga<br><pre><code>find /data -iname "*.db" -exec sqlite3 {} "VACUUM;" \; </code></pre><br><h2 id="10borrararchivosgeneradosporandroid">15. Borrar archivos generados por Android</h2><br><pre><code>rm /data/tombstones/* <br>rm /data/system/dropbox/* <br>rm /data/system/usagestats/* </code></pre><br><h2 id="11reseteodefbrica">16. Reseteo de fábrica</h2><br>Solo en caso excepcional deberías plantearte empezar de nuevo. En muchos dispositivos se accede al recovery por Volumen Bajo + Boton Power. Desde allí buscamos la opción de Factory Reset o directamente instalamos otra ROM más ligera.<br><h2 id="ms">Más</h2><br>Si alguien conoce más métodos para reducir el espacio usado en Android me lo puede comentar.]]></description>
                <comments>https://blog.adrianistan.eu/ante-la-falta-espacio-android</comments>
                <pubDate>Sun, 02 Aug 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Rust Essentials, reseña del libro</title>
                <link>https://blog.adrianistan.eu/rust-essentials</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/rust-essentials</guid>
                <description><![CDATA[<blockquote>Dicen que a las personas importantes les pide la gente su opinión. Así que no entiendo porque tengo ahora que dar opiniones...</blockquote><br>Hoy voy a hablar del libro <a href="https://www.packtpub.com/application-development/rust-essentials">Rust Essentials</a> de <a href="https://www.linkedin.com/pub/ivo-balbaert/0/9b3/949">Ivo Balbaert</a>. En primer lugar quiero agradecer a la editorial <a href="https://www.packtpub.com">Packt Publishing</a> por haber contado conmigo a la hora de valorar este libro para que todos vosotros conozcais algo más acerca de él.<br><br><img class="alignnone size-full wp-image-101" src="https://files.adrianistan.eu/RustEssentials.jpg" alt="RustEssentials" width="500" height="617" /><br><br>Rust Essentials es un libro de introducción al lenguaje de programación Rust, lenguaje del que ya he hablado <a href="https://blog.adrianistan.eu/2015/05/22/la-gestion-de-la-memoria-en-rust/">anteriormente</a>. El libro está en inglés y asume que no conoces nada o muy poquito de Rust pero sí que has programado con anterioridad. Así pues, el libro no explica conceptos de programación tales como funciones o variables sino que directamente expone sus peculiaridades. Es recomendable haber programado C para entender algunas partes del libro al 100%.<br><br>El libro se estructura en 9 capítulos, a saber:<br><ul><br> 	<li>Starting with Rust</li><br> 	<li>Using variables and types</li><br> 	<li>Using functions and control structures</li><br> 	<li>Structuring data and matching patterns</li><br> 	<li>Generalizing code with high-order functions and parametrization</li><br> 	<li>Pointers and memory safety</li><br> 	<li>Organizing code and macros</li><br> 	<li>Concurrency and parallelism</li><br> 	<li>Programming at the boundaries</li><br></ul><br>En estos temas se tratan desde cosas tan triviales como los comentarios (que no lo son, pues según explica el libro, puedes hacer comentarios de RustDoc, que serán compilados como documentación en HTML y tienen marcado Markdown) hasta la gestión multihilo de Rust, para aprovechar uno de los 3 apartados en los que se enfoca Rust: la concurrencia.<br><br>Veremos la magia de Rust, respetando la convención de estilo (esto es importante, no vaya a pasar como con JavaScript) y las características que hacen de Rust un gran lenguaje de programación. El libro contiene ejercicios e incluso analizarás porque en determinados lugares obtenemo un error de compilación.<br><blockquote>Hacen falta un tiempo para que dejes de ver al compilador de Rust como un protestón sin sentido y lo empieces a ver como tu mejor amigo en la programación</blockquote><br>El libro también se adentra a explicar las partes de programación funcional de Rust, no sin antes explicar las closures y las funciones de primer orden. Más tarde nos adentramos en las traits, que posibilitan la programación orientada a objetos pero no como se plantea desde C++, C# o Java. En Rust, es mucho más flexible y unido a las funciones genéricas podemos evitar la repetición del código en un 100%. <strong>DRY</strong> (don't repeat yourself). El capítulo 6 es interesante y quizá algo denso para alguien que venga de lenguajes donde la gestión de memoria es administrada por una máquina virtual o intérprete. No es difícil, pero hay que saber las diferencias. Rust tiene muchos tipos de punteros y he de decir que este libro los explica mejor que mi antiguo libro de C de Anaya.<br><br><img class="alignnone size-large wp-image-100" src="https://files.adrianistan.eu/Rust-1024x576.jpg" alt="Rust" width="840" height="473" /><br><br>Más tarde se ve el sistema de módulos y la creación de macros en Rust. Las macros en Rust son muy potentes, más que en C donde también son bastante usadas. El capítulo 8 se dedica por completo a los hilos y la gestión de datos entre distintos hilos. El capítulo 9 nos explica cosas interesantes pero que no tienen mucha densidad y no se merecen un capítulo propio como la intercomunicación entre C y Rust o instrucciones en ensamblador directamente en el código.<br><br><strong>Me gusta</strong> que tenga ejercicios para practicar (las soluciones están en GitHub), que use las herramientas disponibles de Cargo, que explique porque un determinado código no compilará, que hable de como desarrollar tests unitarios y de que explore todas las características del lenguaje de manera incremental, muchas veces modificando ejemplos anteriores.<br><br><strong>No me gusta</strong> que quizá sea un libro muy rápido que presupone algunos conceptos y que casi no explora la librería estándar mas que para hablar de ciertas traits muy útiles y la gestión de hilos. No habla en ningún momento de como leer archivos por ejemplo aunque en el anexo menciona librerías para leer distintos tipos de archivos.<br><br><strong>En definitiva</strong> es un libro que puede estar bien para todos aquellos con experiencia programando y quieran aprender un nuevo lenguaje, lleno de peculiaridades diseñadas para trabajar en: velocidad, seguridad y concurrencia. No se lo recomendaría a alguien que no hubiese programado nunca.<br><br>Actualmente existen libros mejores en el mercado y además, en Adrianistán puedes aprender con <a href="https://blog.adrianistan.eu/rust-101-tutorial-rust-espanol/">Rust 101</a>, mi tutorial de Rust.]]></description>
                <comments>https://blog.adrianistan.eu/rust-essentials</comments>
                <pubDate>Fri, 17 Jul 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>NexCono, tu sitio viral preferido</title>
                <link>https://blog.adrianistan.eu/nexcono-sitio-viral-preferido</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/nexcono-sitio-viral-preferido</guid>
                <description><![CDATA[NexCono es uno de mis últimos proyectos. Se trata de un sitio de noticias fácilmente viralizables (eso creo yo) pero bajo mi propia línea editorial. Todo el contenido es propio. Dispone de aplicación para <a href="http://marketplace.firefox.com/app/nexcono">Firefox OS</a> y Ubuntu Phone.<br><br><img class="alignnone size-full wp-image-79" src="https://files.adrianistan.eu/NexCono.png" alt="NexCono" width="600" height="220" /><br><br>NexCono se ha dividido en las siguientes secciones:<br><ul><br> 	<li><a href="http://nexcono.appspot.com/#misterio">Misterio</a></li><br> 	<li><a href="http://nexcono.appspot.com/#humor">Humor</a></li><br> 	<li><a href="http://nexcono.appspot.com/#preguntas">Preguntas</a></li><br> 	<li><a href="http://nexcono.appspot.com/#filosofia">Filosofía</a></li><br> 	<li><a href="http://nexcono.appspot.com/#matematicas">Matemáticas</a></li><br> 	<li><a href="http://nexcono.appspot.com/#musica">Música</a></li><br> 	<li><a href="http://nexcono.appspot.com/#como">¿Cómo?</a></li><br> 	<li><a href="http://nexcono.appspot.com/#ciencia">Ciencia</a></li><br> 	<li><a href="http://nexcono.appspot.com/#historia">Historia</a></li><br> 	<li><a href="http://nexcono.appspot.com/#tests">Tests</a></li><br></ul><br>Y podemos encontrar artículos muy interesantes y variados como los <a href="http://nexcono.appspot.com/historia/pensamientos-de-thomas-edison-en-paris/">Pensamientos de Thomas Edison en París</a> o una demostración interactiva del <a href="http://nexcono.appspot.com/matematicas/que-es-el-teorema-del-seno/">teorema del seno</a><br><h2 id="participacindelosusuarios">Participación de los usuarios</h2><br>Para NexCono es muy importante la participación de los usuarios. Todas las páginas tienen comentarios de Disqus. Además de tener un formulario para <a href="http://nexcono.appspot.com/proponer-un-tema">proponer temas</a>. NexCono dispone de un newsletter para recibir las novedades del sitio directamente en tu buzón de correo. Las secciones Tests y Preguntas están diseñadas especialmente para fomentar la participación<br><h2 id="monetizacin">Monetización</h2><br>NexCono ha sido diseñado para tener el menor coste posible de infraestructura. De hecho, ahora mismo no estoy pagando nada. Uso AppEngine de Google como hosting pero solo como almacenamiento estático pues el sitio está construido en Jekyll. Mucha gente me hubiese recomendado WordPress, pero decidí quedarme con Jekyll por los costes y porque es una plataforma que ya domino. Los ingresos son por la publicidad. Estoy probando muchas combinaciones de anuncios: Adpv, AdPrimary, PropellerAds, ExoClick, YesAdvertising, Anonymous Ads, etc aunque todavía no tengo muy claro con quien quedarme.<br><h2 id="mipregunta">Mi pregunta</h2><br>Os pido a vosotros, los lectores de esta entrada que entreis a <a href="http://nexcono.appspot.com">NexCono</a> y me digais lo que debería mejorar en cualquier apartado (diseño (la fuente, la disposición), contenido (más largo, más corto, otros temas, más imágenes), categorías, etc) para así poco a poco ir mejorando ese portal.]]></description>
                <comments>https://blog.adrianistan.eu/nexcono-sitio-viral-preferido</comments>
                <pubDate>Mon, 13 Jul 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Easter egg en Linux: la syscall reboot</title>
                <link>https://blog.adrianistan.eu/easter-egg-linux-la-syscall-reboot</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/easter-egg-linux-la-syscall-reboot</guid>
                <description><![CDATA[Ayer me encontré con un curioso easter egg en el interior de Linux que no conocía. Se trata de la syscall <code>reboot</code> usada para reiniciar el ordenador. Primero veamos lo que nos dice el manual sobre reboot<br><pre><code>man 2 reboot <br>#o también en GNOME <br>yelp 'man:reboot(2)' </code></pre><br><img class="alignnone size-large wp-image-69" src="https://files.adrianistan.eu/man-reboot-1024x578.png" alt="man-reboot" width="840" height="474" /><br><br>Tiene algo interesante. Los dos primeros parámetros para llamar a reboot se llaman <code>magic</code> y <code>magic2</code> y no son muy importantes realmente porque reboot también admite una versión sin esos parámetros.<br><br>Si leemos algo más podemos ver que <code>magic</code> debe ser igual al valor de <code>LINUX_REBOOT_MAGIC1</code>, que es 0xfee1dead (<em>feel dead ?</em>) y que para <code>magic2</code> se admiten varios posibles valores<br><ul><br> 	<li><code>LINUX_REBOOT_MAGIC2</code> que es 672274793</li><br> 	<li><code>LINUX_REBOOT_MAGIC2A</code> que es 85072278</li><br> 	<li><code>LINUX_REBOOT_MAGIC2B</code> que es 369367448</li><br> 	<li><code>LINUX_REBOOT_MAGIC2C</code> que es 537993216</li><br></ul><br>¿Por qué la constante primera está en hexadecimal y el resto no? ¿Qué pasa si pasamos los posibles valores de <code>magic2</code> a hexadecimal?<br><blockquote>672274793 = 0x28121969<br><br>85072278 = 0x05121996<br><br>369367448 = 0x16041998<br><br>537993216 = 0x20112000</blockquote><br>¿A qué se parece? ¿Serán fechas? Veamos la primera. 28 de diciembre de 1969. ¿Qué ocurrió? ¡Pues que nació Linus Torvalds ese día! Y el resto de fechas son las fechas de nacimiento de sus hijas. Así que ya sabes, cada vez que reinicias en Linux estás usando fechas de nacimiento de la familia Torvalds.<br><br><img class="alignnone size-large wp-image-122" src="https://files.adrianistan.eu/Torvalds-1024x683.jpg" alt="Torvalds" width="840" height="560" /><br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/easter-egg-linux-la-syscall-reboot</comments>
                <pubDate>Sat, 11 Jul 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Un repaso por las aplicaciones de Haiku</title>
                <link>https://blog.adrianistan.eu/repaso-las-aplicaciones-haiku</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/repaso-las-aplicaciones-haiku</guid>
                <description><![CDATA[<strong>Haiku</strong> dispone de un catálogo pequeño pero interesante de aplicaciones que merecen la pena ser vistas. Voy a probar estas aplicaciones en una versión nightly recién descargada <a href="http://download.haiku-os.org">del sitio de Haiku</a> (hrev49344)<br>Solo voy a usar los paquetes binarios. En <a href="http://github.com/haikuports/haikuports">Haikuports</a> hay todavía más software, pero requiere compilación y puede estar desactualizado.<br><h2>HaikuDepot</h2><br>Se trata de la tienda de aplicaciones de Haiku. Similar a <strong>Ubuntu Software Center</strong>. Desde aquí podemos instalar cientos de aplicaciones ya compiladas para Haiku. Además permite comentar los progranas con una valoración.<br>HaikuDepot dispone de <a href="http://depot.haiku-os.org">una versión web</a><br><br><img class="alignnone size-large wp-image-147" src="https://files.adrianistan.eu/HaikuDepot-1024x768.png" alt="HaikuDepot" width="840" height="630" /><br><br><img class="alignnone size-large wp-image-148" src="https://files.adrianistan.eu/HaikuDepot2-1024x768.png" alt="HaikuDepot2" width="840" height="630" /><br><h5>Instalar</h5><br>HaikuDepot ya viene instalado en Haiku<br><h2>WonderBrush</h2><br><a href="http://yellowbites.com/wonderbrush.html">WonderBrush</a> se trata del editor gráfico de Haiku para imágenes de mapa de bits como PNG, JPEG o BMP.<br><br>La ventana principal puede contener cualquier número de documentos llamados Canvas. Un canvas tiene asociado un nombre y una resolución por píxel además de muchas otras propiedades. También referencia a dos archivos, uno como formato de exportación y otro como formato nativo.<br>Cada canvas puede tener cualquier número de capas, actualmente representadas como una lista. Cada capa representa una imagen del tamaño del canvas. Dependiendo del método de fusión de capas, estas son unidas para formar la imagen final.<br><br><img class="alignnone size-large wp-image-164" src="https://files.adrianistan.eu/WonderBrush-1024x768.png" alt="WonderBrush" width="840" height="630" /> <img class="alignnone size-large wp-image-165" src="https://files.adrianistan.eu/WonderBrush2-1024x768.png" alt="WonderBrush2" width="840" height="630" /><br><h5>Instalar</h5><br>Lo puedes encontrar en <a href="https://depot.haiku-os.org/#/pkg/wonderbrush/2/1/2/-/4/x86_gcc2?bcguid=bc338-FECF">HaikuDepot</a> o<br><br><pre class="lang:default decode:true"><br>pkgman install wonderbrush<br></pre><br><br><h2>Icon-O-Matic</h2><br>Icon-O-Matic es la aplicación de gráficos <strong>vectoriales</strong> nativa de Haiku. Trabaja con el formato <strong>HVIF</strong> (Haiku Vector Icon Format) y está pensada para diseñar iconos principalmente. También exporta a PNG y SVG.<br><br><img class="alignnone size-large wp-image-149" src="https://files.adrianistan.eu/IconOMatic-1024x768.png" alt="IconOMatic" width="840" height="630" /><br><h5>Instalar</h5><br>Ya viene instalado por defecto en Haiku<br><h2>WebPositive</h2><br>WebPositive es el navegador web por defecto en Haiku. Usa una versión especial de <strong>WebKit</strong> adaptada a Haiku y algo retrasada en funcionalidad.<br><br><img class="alignnone size-large wp-image-50" src="https://files.adrianistan.eu/haiku-webpositive-1024x768.png" alt="haiku-webpositive" width="840" height="630" /><br><br><img class="alignnone size-large wp-image-155" src="https://files.adrianistan.eu/MultiversosHaiku-1024x768.png" alt="MultiversosHaiku" width="840" height="630" /><br><h5>Instalar</h5><br>WebPositive ya viene instalado<br><h2>PoorMan</h2><br>PoorMan es un ligero servidor HTTP totalmente gráfico.<br><br><img class="alignnone size-large wp-image-161" src="https://files.adrianistan.eu/PoorMan-1024x768.png" alt="PoorMan" width="840" height="630" /><br><h5>Instalar</h5><br>PoorMan ya viene instalado por defecto en Haiku<br><h2>Vision</h2><br><a href="http://vision.sourceforge.net">Vision</a> es un cliente IRC<br><br><img class="alignnone size-large wp-image-163" src="https://files.adrianistan.eu/Vision-1024x768.png" alt="Vision" width="840" height="630" /><br><h5>Instalar</h5><br>Ya viene instalado por defecto en Haiku<br><h2>People</h2><br>People es el gestor de contactos de Haiku<br><br><img class="alignnone size-large wp-image-160" src="https://files.adrianistan.eu/People-1024x768.png" alt="People" width="840" height="630" /><br><h5>Instalar</h5><br>Ya viene instalado por defecto en Haiku<br><h2 id="album">Album</h2><br>Una aplicación para tratar los metadatos de las imágenes<br><br><img class="alignnone size-large wp-image-131" src="https://files.adrianistan.eu/Album-1024x768.png" alt="Album" width="840" height="630" /><br><h5 id="instalar">Instalar</h5><br>En HaikuDepot o <code>pkgman install album</code><br><h2 id="armyknife">ArmyKnife</h2><br>Una aplicación para tratar los metadatos de las canciones<br><br><img class="alignnone size-large wp-image-132" src="https://files.adrianistan.eu/ArmyKnife-1024x768.png" alt="ArmyKnife" width="840" height="630" /><br><h5 id="instalar">Instalar</h5><br>En HaikuDepot o <code>pkgman install armyknife</code><br><h2 id="beae">BeAE</h2><br>BeAE es una gran aplicación junto a <strong>Wonderbrush</strong> e <strong>Icon-O-Matic</strong> para la edición multimedia. En este caso podremos editar audio de manera muy intuitiva. En su momento fue una aplicación de pago para BeOS.<br><br><img class="alignnone size-large wp-image-133" src="https://files.adrianistan.eu/BeAE-1024x768.png" alt="BeAE" width="840" height="630" /><br><h5 id="instalar">Instalar</h5><br>En HaikuDepot o <code>pkgman install beae</code><br><h2 id="bepdf">BePDF</h2><br>El visor de documentos PDF de Haiku<br><br><img class="alignnone size-large wp-image-136" src="https://files.adrianistan.eu/BePDF-1024x768.png" alt="BePDF" width="840" height="630" /><br><h5 id="instalar">Instalar</h5><br>En HaikuDepot o <code>pkgman install bepdf</code><br><h2 id="beshare">BeShare</h2><br>Haiku también dispone de un sistema de transferencia de archivos P2P propio. Se trata de <strong>BeShare</strong> y esta es la implementación referencia. BeShare como no podía ser menos, tiene un magnífico soporte para búsquedas.<br><br><img class="alignnone size-large wp-image-138" src="https://files.adrianistan.eu/BeShare-1024x768.png" alt="BeShare" width="840" height="630" /><br><h5 id="instalar">Instalar</h5><br>En HaikuDepot o <code>pkgman install beshare_x86</code>. Para permitir la conexión entre distintos sistemas operativos a las redes BeShare se creó <a href="https://github.com/bvarner/javashare">JavaShare</a><br><h2 id="bezilla">BeZilla</h2><br>El port de Mozilla Firefox para Haiku. Está muy desactualizado y no lo recomiendo.<br><br><img class="alignnone size-large wp-image-139" src="https://files.adrianistan.eu/BeZilla-1024x768.png" alt="BeZilla" width="840" height="630" /><br><h5 id="instalar">Instalar</h5><br>En HaikuDepot o <code>pkgman install bezilla</code><br><h2 id="mailnews">MailNews</h2><br>El port de Mozilla Thunderbird para Haiku. Está igual de desactualizado que BeZilla pero al tratarse de correo electrónico puedes tener menos problemas al usarlo.<br><br><img class="alignnone size-large wp-image-152" src="https://files.adrianistan.eu/MailNews-1024x768.png" alt="MailNews" width="840" height="630" /> <img class="alignnone size-large wp-image-153" src="https://files.adrianistan.eu/MailNews-2-1024x768.png" alt="MailNews-2" width="840" height="630" /><br><h5 id="instalar">Instalar</h5><br>En HaikuDepot o <code>pkgman install mailnews</code><br><h2 id="beam">Beam</h2><br>Se trata de un cliente de correo electrónico plenamente integrado en Haiku<br><br><img class="alignnone size-large wp-image-134" src="https://files.adrianistan.eu/Beam-1024x768.png" alt="Beam" width="840" height="630" /><br><h5 id="instalar">Instalar</h5><br>En HaikuDepot o <code>pkgman install beam</code><br><h2 id="blogpositive">BlogPositive</h2><br>Una aplicación para escribir en blogs de WordPress y otros proveedores sin tener que usar el navegador<br><br><img class="alignnone size-large wp-image-140" src="https://files.adrianistan.eu/BlogPositive-1024x768.png" alt="BlogPositive" width="840" height="630" /><br><h5 id="instalar">Instalar</h5><br>En HaikuDepot o <code>pkgman install blogpositive</code><br><h2 id="capitalbe">CapitalBe</h2><br>Una aplicación de contabilidad sencilla y fácil de empezar a utilizar. <strong>GnuCash</strong>, que es el que uso en Linux, es mucho más complejo de empezar a usar.<br><br><img class="alignnone size-large wp-image-142" src="https://files.adrianistan.eu/CapitalBe-1024x768.png" alt="CapitalBe" width="840" height="630" /> <img class="alignnone size-large wp-image-143" src="https://files.adrianistan.eu/CapitalBe2-1024x768.png" alt="CapitalBe2" width="840" height="630" /><br><h5 id="instalar">Instalar</h5><br>En HaikuDepot o <code>pkgman install capitalbe</code><br><h2 id="sumit">Sum-It</h2><br>Una sencilla hoja de cálcula para realizar operaciones no muy complejas. La mejor hoja de cálcula es la de GoBe Productive, pero sigue siendo un producto privado que no se puede adquirir.<br><br><img class="alignnone size-large wp-image-162" src="https://files.adrianistan.eu/SumIt-1024x768.png" alt="SumIt" width="840" height="630" /><br><h5 id="instalar">Instalar</h5><br>En HaikuDepot o <code>pkgman install sum_it</code><br><h2 id="caya">Caya</h2><br>Caya es una aplicación que unifica la mensajería instantánea el estilo de Pidgin. Soporta AIM, Google Talk, Jabber, Facebook, MSN y Yahoo.<br><br><img class="alignnone size-large wp-image-144" src="https://files.adrianistan.eu/Caya-1024x768.png" alt="Caya" width="840" height="630" /><br><h5 id="instalar">Instalar</h5><br>En HaikuDepot o <code>pkgman install caya</code><br><h2 id="librecad">LibreCAD</h2><br><a href="http://librecad.org">LibreCAD</a> es una aplicación de CAD 2D escrita en Qt.<br><br><img class="alignnone size-large wp-image-150" src="https://files.adrianistan.eu/LibreCAD-1024x768.png" alt="LibreCAD" width="840" height="630" /><br><h5 id="instalar">Instalar</h5><br>En HaikuDepot o <code>pkgman install librecad_x86</code><br><h2 id="pe">Pe</h2><br>Pe es el editor de texto más usado en Haiku. Tiene resaltado de sintaxis y soporte para extensiones.<br><br><img class="alignnone size-large wp-image-158" src="https://files.adrianistan.eu/Pe-1024x768.png" alt="Pe" width="840" height="630" /> <img class="alignnone size-large wp-image-159" src="https://files.adrianistan.eu/Pe-2-1024x768.png" alt="Pe-2" width="840" height="630" /><br><h5 id="instalar">Instalar</h5><br>Pe viene instalado por defecto en Haiku<br><h2 id="netsurf">NetSurf</h2><br><a href="http://www.netsurf-browser.org">NetSurf</a> es un navegador ligero que no implementa JavaScript. Tiene una arquitectura interna muy limpia. Todo está contenido y modularizado. Está diseñado pensando en Haiku, RISC OS y otros sistemas <em>desconocidos</em>, aunque en Linux también funciona.<br><br><img class="alignnone size-large wp-image-156" src="https://files.adrianistan.eu/NetSurf-1024x768.png" alt="NetSurf" width="840" height="630" /><br><h5 id="instalar">Instalar</h5><br>En HaikuDepot o <code>pkgman install netsurf</code><br><h2 id="bepodder">BePodder</h2><br>BePodder es una aplicación para escuchar tus podcasts favoritos por RSS<br><br><img class="alignnone size-large wp-image-137" src="https://files.adrianistan.eu/BePodder-1024x768.png" alt="BePodder" width="840" height="630" /><br><h5 id="instalar">Instalar</h5><br>En HaikuDepot o <code>pkgman install bepodder</code><br><h2 id="abook">A-Book</h2><br>Una aplicación de calendario con recordatorios<br><h5 id="instalar">Instalar</h5><br>En HaikuDepot o <code>pkgman install a_book</code><br><h2 id="burnitnow">BurnItNow</h2><br>Una aplicación gráfica y fácil de usar para grabar CDs de todo tipo (datos, audio, rescate, ...)<br><br><img class="alignnone size-large wp-image-141" src="https://files.adrianistan.eu/BurnItNow-1024x768.png" alt="BurnItNow" width="840" height="630" /><br><h5 id="instalar">Instalar</h5><br>En HaikuDepot o <code>pkgman install burnitnow_x86</code><br><h2 id="clockwerk">Clockwerk</h2><br>Ya hemos hablado de Wonderbrush, Icon-O-Matic y BeAE. Le toca el turno a <strong>Clockwerk</strong>, el editor de vídeo no linear open source que trabaja en Haiku.<br><br><img class="alignnone size-large wp-image-145" src="https://files.adrianistan.eu/Clockwerk-1024x768.png" alt="Clockwerk" width="840" height="630" /><br><h5 id="instalar">Instalar</h5><br>En HaikuDepot o <code>pkgman install clockwerk</code><br><h2 id="lmms">LMMS</h2><br>LMMS es una completa suite de edición de audio pensada para Linux pero que funciona también en Haiku.<br><br><img class="alignnone size-large wp-image-151" src="https://files.adrianistan.eu/LMMS-1024x768.png" alt="LMMS" width="840" height="630" /><br><h5 id="instalar">Instalar</h5><br>En HaikuDepot o <code>pkgman install lmms_x86</code><br><h2 id="milkytracker">MilkyTracker</h2><br>MilkyTracker es un programa para componer música de estilo 8 bits o tune.<br><br><img class="alignnone size-large wp-image-154" src="https://files.adrianistan.eu/MilkyTracker-1024x768.png" alt="MilkyTracker" width="840" height="630" /><br><h5 id="instalar">Instalar</h5><br>En HaikuDepot o <code>pkgman install milkytracker</code><br><h2 id="paladin">Paladin</h2><br>Paladin nace con la idea de ser el IDE de referencia en Haiku. Trae plantillas y ejemplos y se integra con el resto de Haiku para no reduplicar esfuerzos. Por ejemplo, el editor de texto es Pe.<br><br><img class="alignnone size-large wp-image-157" src="https://files.adrianistan.eu/Paladin-1024x768.png" alt="Paladin" width="840" height="630" /><br><h5 id="instalar">Instalar</h5><br>En HaikuDepot o <code>pkgman install paladin</code><br><h2 id="qemu">QEMU</h2><br>QEMU es un contenedor de máquinas virtuales open source que permite emular arquitecturas diferentes a la de nuestro ordenador. Con QEMU podemos ejecutar Linux dentro de Haiku.<br><h5 id="instalar">Instalar</h5><br>En HaikuDepot o <code>pkgman install qemu_x86</code><br><h2 id="yabide">Yab IDE</h2><br>Yab es un entorno que permite programar aplicaciones para Haiku en <strong>BASIC</strong>. Yab IDE es el IDE para este entorno espefífico.<br><br><img class="alignnone size-large wp-image-166" src="https://files.adrianistan.eu/Yab-1024x768.png" alt="Yab" width="840" height="630" /> <img class="alignnone size-large wp-image-167" src="https://files.adrianistan.eu/Yab2-1024x768.png" alt="Yab2" width="840" height="630" /><br><h5 id="instalar">Instalar</h5><br>En HaikuDepot o <code>pkgman install yab_ide</code><br><h2 id="juegos">Juegos</h2><br>En este apartado voy a mencionar algunos juegos disponibles para Haiku. Todos se pueden encontrar en HaikuDepot<br><br><img class="alignnone size-large wp-image-135" src="https://files.adrianistan.eu/BeMines-1024x768.png" alt="BeMines" width="840" height="630" /><br><ul><br> 	<li>BeLife - <code>pkgman install belife</code></li><br> 	<li>BeMines - <code>pkgman install bemines</code></li><br> 	<li>Critical Mass - <code>pkgman install criticalmass</code></li><br> 	<li>DOSBox - <code>pkgman install dosbox_x86</code></li><br> 	<li>Flare - <code>pkgman install flare_x86</code></li><br> 	<li>FreedroidRPG - <code>pkgman install freedroidrpg_x86</code></li><br> 	<li>LBreakout2 - <code>pkgman install lbreakout2</code></li><br> 	<li>LMarbles - <code>pkgman install lmarbles</code></li><br> 	<li>LTris - <code>pkgman install ltris</code></li><br> 	<li>OpenTTD - <code>pkgman install openttd_x86</code></li><br> 	<li>Pipepanic - <code>pkgman install pipepanic</code></li><br> 	<li>Road Fighter - <code>pkgman install roadfighter</code></li><br> 	<li>Rocks'n'diamonds - <code>pkgman install rocksndiamonds_x86</code></li><br> 	<li>SDL Lopan - <code>pkgman install sdllopan</code></li><br> 	<li>Slime Volley - <code>pkgman install slime_volley</code></li><br> 	<li>Super Transball 2 - <code>pkgman install super_transball</code></li><br> 	<li>XRick - <code>pkgman install xrick</code></li><br></ul>]]></description>
                <comments>https://blog.adrianistan.eu/repaso-las-aplicaciones-haiku</comments>
                <pubDate>Wed, 01 Jul 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Tutorial de CMake</title>
                <link>https://blog.adrianistan.eu/tutorial-de-cmake</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/tutorial-de-cmake</guid>
                <description><![CDATA[Llevo varios años usando de forma habitual CMake. Sin embargo me doy cuenta que alguien que quiera empezar a usar este sistema va a encontrarse con documentación confusa.<br><blockquote>1º Regla de CMake. La documentación puede ser confusa</blockquote><br><h2>¿Qué es CMake?</h2><br>CMake se trata de una herramienta multiplataforma para generar instrucciones de compilación del código. No sustituye a las herramientas de compilación como Make o MSBuild, sino que nos proporciona un único lenguaje que será transformado a las instrucciones del sistema operativo donde nos encontremos. Sería un sustituto de Autotools.<br><br><img class="alignnone size-large wp-image-310" src="https://files.adrianistan.eu/cmake-dia-1024x425.png" alt="cmake-dia" width="840" height="349" /><br><br>Las ventajas son que no tenemos que tener varios archivos para gestionar la compilación. Usando CMake podemos generar el resto. Actualmente CMake (3.2.3) soporta:<br><ul><br> 	<li>Unix Make</li><br> 	<li>Ninja</li><br> 	<li>CodeBlocks</li><br> 	<li>Eclipse CDT</li><br> 	<li>KDevelop</li><br> 	<li>Sublime Text 2</li><br> 	<li>Borland Make</li><br> 	<li>MSYS Make</li><br> 	<li>MinGW Make</li><br> 	<li>NMake</li><br> 	<li>NMake JOM</li><br> 	<li>Watcom WMake</li><br> 	<li>Kate</li><br> 	<li>CodeLite</li><br> 	<li>Xcode</li><br> 	<li>Visual Studio (desde el 6 hasta 2013)</li><br></ul><br><h2>Usando CMake</h2><br>En CMake las configuraciones estan centralizadas por defecto en un archivo llamado CMakeLists.txt. Este se encuentra en la carpeta central del proyecto. Normalmente con CMake los proyectos se construyen en una carpeta diferente de la que tenemos el código fuente. Es corriente crear una carpeta <em>build</em> en lo alto del proyecto. Así si tenemos un proyecto con CMake ya descomprimido haríamos lo siguiente.<br><pre class="lang:default decode:true">mkdir build<br>cd build<br>cmake ..<br># make o ninja o nmake o lo que toque<br></pre><br>También puedes usar la aplicación gráfica. Muy cómoda cuando debamos modificar las configuraciones.<br><br><img class="alignnone size-large wp-image-21" src="https://files.adrianistan.eu/cmake-gui-1024x578.png" alt="cmake-gui" width="840" height="474" /><br><br>Podemos ajustar las variables de CMake desde la interfaz de usuario, usando el modo interactivo de la línea de comandos (`cmake .. -i`) o usando flags cuando llamamos a CMake (`cmake .. -DCMAKE_CXX_FLAGS=-std=c++11`)<br><h2>El archivo CMakeLists.txt</h2><br>Ya estamos listos para crear nuestro primer archivo de configuración de CMake.<br><br><img class="alignnone size-large wp-image-311" src="https://files.adrianistan.eu/proyecto-1024x788.png" alt="proyecto" width="840" height="646" /><br><br>Vamos a ir viendo distintas versiones del archivo donde voy a ir añadiendo diferentes tareas. Estate atento a los comentarios de los archivos<br><h4>Compilar como programa main.cpp</h4><br><pre class="lang:default decode:true">PROJECT(MiProyecto)<br>CMAKE_MINIMUM_REQUIRED(VERSION 2.8)<br># Indicamos la versión mínima que necesitamos de CMake<br><br>SET(MiProyecto_SRC "src/main.cpp")<br># Creamos la variable MiProyecto_SRC y le asignamos el valor "src/main.cpp" que es la ubicación de nuestro archivo.<br># Por defecto las variables son listas o arrays<br># Si tenemos dos archivos sería SET(MiProyecto_SRC "src/main.cpp"<br>"src/segundo.cpp")<br># Se permite multilínea<br><br>ADD_EXECUTABLE(MiProyecto ${MiProyecto_SRC})<br><br># Se creará un ejecutable llamado MiProyecto en Linux o MiProyecto.exe en Windows.<br># Se hace referencia a las variables con ${NOMBRE_VARIABLE}.<br></pre><br>Y ya está.<br><h2>Trabajar con opciones y condicionales</h2><br>CMake permite ajustar muchas opciones como hemos visto con el asistente gráfico de CMake. Sin embargo no todas las variables se muestran ahí. Solo son modificables las que nosotros marquemos explícitamente. Se usa OPTION()<br><pre class="lang:default decode:true">PROJECT(MiProyecto)<br>CMAKE_MINIMUM_REQUIRED(VERSION 2.8)<br><br>SET(MiProyecto_SRC "src/main.cpp")<br><br>OPTION(EXPERIMENTAL_FEATURE "Activar característica experimental" OFF)<br># OPTION(NOMBRE_VARIABLE DESCRIPCION_LEGIBLE VALOR_POR_DEFECTO)<br># ON/OFF es la pareja de valores booleanos en CMake. TRUE/FALSE también es correcto<br><br>IF(EXPERIMENTAL_FEATURE) # El condicional más básico<br>LIST(APPEND MiProyecto_SRC "experimental_feature.cpp")<br># Añadimos un elemento a la lista<br># También se puede hacer con<br># SET(MiProyecto_SRC ${MiProyecto_SRC} "experimental_feature.cpp")<br>ENDIF()<br><br>ADD_EXECUTABLE(MiProyecto ${MiProyecto_SRC})<br></pre><br><h2>Usar librería estática</h2><br><pre class="lang:default decode:true">PROJECT(MiProyecto C CXX)<br># Podemos marcar opcionalmente los lenguajes para que CMake busque los compiladores<br>CMAKE_MINIMUM_REQUIRED(VERSION 2.8)<br><br>SET(MiProyecto_SRC "src/main.cpp")<br>SET(Lib_SRC "lib/lib.cpp")<br><br>ADD_LIBRARY(Lib STATIC ${Lib_SRC})<br># El comando es exactamente igual que ADD_EXECUTABLE, pero marcamos si STATIC o SHARED<br>ADD_EXECUTABLE(MiProyecto ${MiProyecto_SRC})<br>TARGET_LINK_LIBRARIES(MiProyecto ${Lib})<br># Necesitamos "unir" la librería con nuestro ejecutable<br># Si necesitamos una librería tal cual usamos su nombre<br># TARGET_LINK_LIBRARIES(MiProyecto pthread)<br># Se pueden hacer las llamadas que se quiera a TARGET_LINK_LIBRARIES<br></pre><br><h2>Usar librería dinámica</h2><br><pre class="lang:default decode:true">PROJECT(MiProyecto)<br>CMAKE_MINIMUM_REQUIRED(VERSION 2.8)<br><br>SET(MiProyecto_SRC "src/main.cpp")<br>SET(Lib_SRC "lib/lib.cpp")<br><br>ADD_LIBRARY(Lib SHARED ${Lib_SRC})<br>ADD_EXECUTABLE(MiProyecto ${MiProyecto_SRC})<br>TARGET_LINK_LIBRARIES(MiProyecto ${MiProyecto_SRC})<br></pre><br><h2>Seleccionar archivos de forma masiva</h2><br>Usar SET para los archivos es muy fácil de entender, pero es posible que no queramos mantener una lista explícita del código fuente.<br><pre class="lang:default decode:true">PROJECT(MiProyecto)<br>CMAKE_MINIMUM_REQUIRED(VERSION 2.8)<br><br>FILE(GLOB MiProyecto_SRC "src/*.cpp")<br># FILE GLOB selecciona todos los archivos que cumplan la característica y los almacena en MiProyecto_SRC<br># GLOB no es recursivo. Si lo necesitas, usa GLOB_RECURSE<br><br>ADD_EXECUTABLE(MiProyecto ${MiProyecto_SRC})<br></pre><br>Esto tiene un inconveniente y es que CMake no detecta automáticamente si hay nuevos archivos que cumplen la característica, por lo que hay que forzar la recarga.<br><h2>Copiar, crear, eliminar y descargar archivos</h2><br><pre class="lang:default decode:true">PROJECT(MiProyecto)<br>CMAKE_MINIMUM_REQUIRED(VERSION 2.8)<br><br>FILE(GLOB MiProyecto_SRC "src/*.cpp")<br><br># Copiar archivos<br>FILE(COPY "MiArchivo.cpp" DESTINATION mi-carpeta)<br># COPY usa como destino siempre una carpeta<br># Se puede crear con FILE(MAKE_DIRECTORY mi-carpeta)<br><br># Crear archivos<br><br>FILE(WRITE "Generado.txt" "Este archivo ha sido generado por CMake\nLos archivos son: ${MiProyecto_SRC}")<br><br># Borrar archivos<br><br>FILE(REMOVE "Generado.txt")<br># No es recursivo, REMOVE_RECURSE sí lo es<br><br># Descargar archivos<br><br>FILE(DOWNLOAD http://mi-servidor.com/archivo.tar.gz archivo.tar.gz)<br># Podemos mostrar el progreso<br># FILE(DOWNLOAD http://mi-servidor.com/archivo.tar.gz archivo.tar.gz SHOW_PROGRESS)<br># Comprobar la suma MD5<br># FILE(DOWNLOAD http://mi-servidor.com/archivo.tar.gz archivo.tar.gz EXPECTED_MD5 LaSumaMD5)<br># Usar SSL<br># FILE(DOWNLOAD http://mi-servidor.com/archivo.tar.gz archivo.tar.gz TLS_VERIFY ON)<br># Guardar la información en un archivo de log<br># FILE(DOWNLOAD http://mi-servidor.com/archivo.tar.gz archivo.tar.gz LOG descarga.log)<br><br># Calcular suma de control<br><br>FILE(SHA256 archivo.tar.gz VARIABLE_CON_EL_HASH)<br><br>ADD_EXECUTABLE(MiProyecto ${MiProyecto_SRC})<br></pre><br><h2>Incluir archivos de cabecera</h2><br>A veces es necesario incluir archivos de cabecera en localizaciones no estándar<br><pre class="lang:default decode:true">PROJECT(MiProyecto)<br>CMAKE_MINIMUM_REQUIRED(VERSION 2.8)<br><br>SET(MiProyecto_SRC<br>"src/main.cpp"<br>"src/algo_mas.cpp")<br><br>INCLUDE_DIRECTORIES("src/includes")<br># Se añade el directorio a la ruta de búsqueda del compilador de turno<br><br>ADD_EXECUTABLE(MiProyecto ${MiProyecto_SRC})<br></pre><br><h2>Plugins de CMake</h2><br>CMake es extensible a través de módulos. La instalación por defecto de CMake trae unos cuantos módulos, no obstante, podemos añadir módulos solo para nuestro proyecto. Los módulos tienen extensión .cmake. Normalmente se dejan en una carpeta llamada `cmake`.<br><pre class="lang:default decode:true">PROJECT(MiProyecto)<br>CMAKE_MINIMUM_REQUIRED(VERSION 2.8)<br><br>LIST(APPEND CMAKE_PLUGIN_PATH "cmake")<br># Simplemente añadimos un nuevo lugar a buscar. Veremos como se usan los módulos más adelante<br><br>ADD_EXECUTABLE(MiProyecto_SRC "src/main.cpp")<br></pre><br><h2>Mostrar información y generar errores</h2><br>En ciertas situaciones querremos que no se pueda compilar el proyecto. MESSAGE es la solución.<br><pre class="lang:default decode:true">PROJECT(MiProyecto)<br>CMAKE_MINIMUM_REQUIRED(VERSION 2.8)<br><br>MESSAGE("Información relevante")<br>MESSAGE(STATUS "Información sin relevancia")<br>MESSAGE(WARNING "Alerta, continúa la configuración y generación")<br>MESSAGE(SEND_ERROR "Error, continúa la configuración pero no generará")<br>MESSAGE(FATAL_ERROR "Error grave, detiene la configuración")<br><br>ADD_EXECUTABLE(MiProyecto "src/main.cpp")<br></pre><br><h2>Condicionales avanzados</h2><br><pre class="lang:default decode:true">PROJECT(MiProyecto)<br>CMAKE_MINIMUM_REQUIRED(VERSION 2.8)<br><br>## Con variables booleanas, es decir, ON/OFF, TRUE/FALSE<br><br>IF(NOMBRE_VARIABLE)<br>MESSAGE("Algo es cierto")<br>ENDIF()<br><br>IF(NOT NOMBRE_VARIABLE)<br>MESSAGE("Algo no es cierto")<br>ENDIF()<br><br># La estructura completa es algo así<br><br>IF(CONDICION)<br><br>ELSEIF(CONDICION_2)<br><br>ELSE()<br><br>ENDIF()<br><br># Se pueden aplicar operadores lógicos<br><br>IF(CONDICION AND CONDICION_2)<br><br>IF(CONDICION OR CONDICION_2)<br><br># Con números y texto<br><br>IF(VAR_1 LESS VAR_2) # VAR_1 &amp;lt; VAR_2 IF(VAR_1 GREATER VAR_2) # VAR_1 &amp;gt; VAR_2<br><br>IF(VAR_1 EQUAL VAR_2) # VAR_1 === VAR_2<br><br>IF(VAR_1 MATCHES REGEX) # Se comprueba la expresión regular<br><br># Además, CMake provee operadores para trabajar directamente con archivos, comandos y ejecutables<br><br>IF(DEFINED VAR_1) # ¿Está definida VAR_1?<br><br>IF(COMMAND CMD_1) # ¿CMD_1 es un comando de CMake?<br><br>IF(POLICY POL_1) # ¿La directiva POL_1 está activada?<br><br>IF(TARGET MiProyecto) # ¿Está definido el ejecutable MiProyecto?<br><br>IF(EXISTS src/main.cpp) # ¿Existe el archivo src/main.cpp?<br><br>IF(src/main.cpp IS_NEWER_THAN src/old/main.cpp) # ¿Es src/main.cpp más nuevo que src/old/main.cpp?<br><br>IF(IS_DIRECTORY src/includes) # ¿src/includes es un archivo o una carpeta?<br><br></pre><br><h2>Bucles</h2><br><pre class="lang:default decode:true">PROJECT(MiProyecto)<br>CMAKE_MINIMUM_REQUIRED(VERSION 2.8)<br><br>SET(MiProyecto_SRC<br>"src/main.cpp"<br>"src/list.cpp"<br>"src/algomas.cpp")<br><br>FOREACH(Archivo_SRC IN MiProyecto_SRC)<br>MESSAGE(STATUS "Procesando archivo ${Archivo_SRC}")<br>ENDFOREACH()<br></pre><br><h2>Submódulos</h2><br>CMake usa un único archivo, pero quizá nos conviene repartir la configuración de CMake por varias carpetas entre zonas diferenciadas.<br><pre class="lang:default decode:true">PROJECT(MiProyecto)<br>CMAKE_MINIMUM_REQUIRED(VERSION 2.8)<br><br>ADD_SUBDIRECTORY(lib)<br>ADD_SUBDIRECTORY(src)<br><br># src y lib tienen un CMakeLists.txt cada uno<br></pre><br><h2>Librerías externas</h2><br>Una de las características más interesantes de CMake es que es capaz de encontrar librerías externas que necesite nuestro programa. Esta característica se implementa con plugins de CMake. Aquí voy a necesitar wxWidgets.<br><pre class="lang:default decode:true">PROJECT(MiProyecto)<br>CMAKE_MINIMUM_REQUIRED(VERSION 2.8)<br><br>FIND_PACKAGE(wxWidgets)<br># El plugin debe llamarse FindPackagewxWidgets.cmake, este esta incluido en la distribución estándar de CMake<br># En grandes librerías como wxWidgets, podemos pedir solo ciertos componentes<br># FIND_PACKAGE(wxWidgets COMPONENTS core gl html base net)<br># Podemos hacer que CMake no continúe si no encuentra la librería<br># FIND_PACKAGE(wxWidgets REQUIRED)<br># Si todo va bien, tenemos las variables wxWidgets_FOUND, wxWidgets_LIBRARIES y wxWidgets_INCLUDE_DIR<br><br>INCLUDE_DIRECTORIES(${wxWidgets_INCLUDE_DIR})<br>TARGET_LINK_LIBRARIES(MiProyecto ${wxWidgets_LIBRARIES})<br></pre><br><h2>Definiciones</h2><br>Podemos añadir directivas del preprocesador de C++ con CMake<br><pre class="lang:default decode:true">PROJECT(MiProyecto)<br>CMAKE_MINIMUM_REQUIRED(VERSION 2.8)<br><br>ADD_DEFINITIONS(-DPREMIUM_SUPPORT)<br># Ahora #ifdef PREMIUM_SUPPORT en el código evaluará como cierto<br><br>ADD_EXECUTABLE(MiProyecto "src/main.cpp")<br></pre><br><h2>Dependencias</h2><br>Se pueden crear árboles de dependencias en CMake<br><pre class="lang:default decode:true">PROJECT(MiProyecto)<br>CMAKE_MINIMUM_REQUIRED(VERSION 2.8)<br><br>ADD_EXECUTABLE(MiProyecto "src/main.cpp")<br>ADD_EXECUTABLE(NecesitaMiProyecto "src/otro.cpp")<br><br>ADD_DEPENDENCY(NecesitaMiProyecto MiProyecto)<br># NecesitaMiProyecto ahora depende de MiProyecto<br></pre><br><h2>Usando Qt</h2><br>Ejemplo práctico usando CMake y Qt5 que es capaz de usar QML. Soporta archivos QRC de recursos. Requiere los plugins de Qt5.<br><pre class="lang:default decode:true">PROJECT(ProyectoQt)<br>CMAKE_MINIMUM_REQUIRED(VERSION 2.8)<br><br>SET(CMAKE_AUTOMOC ON)<br>SET(CMAKE_INCLUDE_CURRENT_DIR ON)<br><br>FILE(GLOB ProyectoQt_SRC "src/*.cpp")<br><br>FIND_PACKAGE(Qt5Core REQUIRED)<br>FIND_PACKAGE(Qt5Widgets REQUIRED)<br>FIND_PACKAGE(Qt5Qml REQUIRED)<br>FIND_PACKAGE(Qt5Quick REQUIRED)<br><br>qt5_add_resources(Res_SRC "src/res.qrc")<br><br>ADD_EXECUTABLE(ProyectoQt ${ProyectoQt_SRC} ${Res_SRC})<br><br>qt5_use_modules(ProyectoQt Widgets Qml Quick)<br></pre><br><h2>Usando Java</h2><br>CMake soporta Java, aunque no maneja dependencias como Maven o Gradle.<br><pre class="lang:default decode:true">PROJECT(ProyectoJava)<br>CMAKE_MINIMUM_REQUIRED(VERSION 2.8)<br><br>FIND_PACKAGE(Java REQUIRED)<br>INCLUDE(UseJava)<br><br>SET(CMAKE_JAVA_COMPILE_FLAGS "-source" "1.6" "-target" "1.6")<br><br>FILE(GLOB JAVA_SRC "src/*.java")<br>SET(DEPS_JAR "deps/appengine.jar")<br><br>add_jar(ProyectoJava ${JAVA_SRC} INCLUDE_JARS ${DEPS_JAR} ENTRY_POINT "PuntoDeEntrada")<br><br></pre><br><h2>Usar C++11</h2><br>A partir de CMake 3.1, podemos definir el estándar de C y C++ que vamos a usar<br><pre class="lang:default decode:true">SET_PROPERTY(TARGET Ejecutable PROPERTY CXX_STANDARD 11) # Para C++11. Solo afecta al target Ejecutable<br><br>SET(CMAKE_CXX_STANDARD 11) # Para C++11. Afecta globalmente al proyecto.<br><br></pre><br><h2>Comandos personalizados, Doxygen</h2><br>En CMake podemos crear comandos personalizados. Por ejemplo, generar documentación con Doxygen<br><pre class="lang:default decode:true">PROJECT(Doxy)<br>CMAKE_MINIMUM_REQUIRED(VERSION 2.8)<br><br>ADD_CUSTOM_TARGET(doxygen doxygen ${PROJECT_SOURCE_DIR}/Doxyfile DEPENDS MiProyectoEjecutable WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMENT "Generando documentación" VERBATIM )<br><br># Ahora puedes usar "make doxygen"<br># Como es un TARGET cualquiera de CMake, puedes usar ADD_DEPENDENCY<br># También puedes usar el plugin FindDoxygen para más portabilidad<br></pre><br><h2>Archivos de configuración</h2><br>En Autotools es común usar un archivo con configuraciones en tiempo de compilación. Normalmente se trata de una cabecera con soporte para plantillas. En CMake se puede hacer.<br><br>config.hpp.in<br><pre class="lang:c++ decode:true">#ifndef CONFIG_HPP<br>#define CONFIG_HPP<br><br>#cmakedefine PREMIUM_SUPPORT<br><br>/* Si PREMIUM_SUPPORT está definido en CMakeLists.txt, se definirá aquí */<br><br>#define AUTHOR @AUTHOR@<br><br>/* Se definirá AUTHOR con el valor que tenga CMakeLists.txt de la variable AUTHOR */<br><br>#endif<br></pre><br><pre class="lang:default decode:true">PROJECT(MiProyecto)<br>CMAKE_MINIMUM_REQUIRED(VERSION 2.8)<br><br>SET(AUTHOR "\"Adrian Arroyo Calle\"")<br><br>CONFIGURE_FILE(src/config.hpp.in src/config.hpp)<br></pre><br><h2>Instalar</h2><br>CMake permite instalar también los programas<br><pre class="lang:default decode:true">PROJECT(MiProyecto)<br>CMAKE_MINIMUM_REQUIRED(VERSION 2.8)<br><br>ADD_EXECUTABLE(MiProyecto "src/main.cpp")<br><br>INSTALL(TARGETS MiProyecto DESTINATION bin/)<br># Instala un ejecutable o librería en la carpeta seleccionada. Tenemos que tener en cuenta los prefijos, que son configurables en CMake.<br># Si un programa en Linux suele ir en /usr/local/bin, debemos usar bin, pues /usr/local será añadido por CMake automáticamente<br>INSTALL(FILES ${ListaDeArchivos} DESTINATION .)<br># Archivos normales<br>INSTALL(DIRECTORY mi-carpeta DESTINATION .)<br># Copia la carpeta entera, conservando el nombre<br># Se permiten expresiones regulares y wildcards<br># INSTALL(DIRECTORY mi-carpeta DESTINATION . FILES_MATCHING PATTERN "*.png")<br><br>INSTALL(SCRIPT install-script.cmake)<br># Un archivo de CMake que se ejecutará en la instalación<br><br>INCLUDE(InstallRequiredSystemLibraries)<br># Importante si usas Windows y Visual Studio<br><br># Y con esto se puede usar 'make install'<br></pre><br><h2>CPack</h2><br>Pero `make install` es un poco incómodo. No se puede distribuir fácilmente. Aquí CMake presenta CPack, que genara instaladores. Yo soy reacio a usarlos pues son de mala calidad pero soporta:<br><ul><br> 	<li>ZIP</li><br> 	<li>TAR.GZ</li><br> 	<li>TAR.BZ2</li><br> 	<li>TZ</li><br> 	<li>STGZ - Genera un script de Bash que ejecutará la descompresión y hará la instalación</li><br> 	<li>NSIS</li><br> 	<li>DragNDrop</li><br> 	<li>PackageMaker</li><br> 	<li>OSXX11</li><br> 	<li>Bundle</li><br> 	<li>Cygwin BZ2</li><br> 	<li>DEB</li><br> 	<li>RPM</li><br></ul><br>CPack necesita que usemos el comando `cpack` en vez de `cmake`<br><pre class="lang:default decode:true">PROJECT(MiProyecto)<br>CMAKE_MINIMUM_REQUIRED(VERSION 2.8)<br><br>ADD_EXECUTABLE(MiProyecto "src/main.cpp")<br>INSTALL(TARGETS MiProyecto DESTINATION bin)<br><br>INCLUDE(CPack)<br># Esto servirá para ZIP, TAR.GZ, TAR.BZ2, STGZ y TZ<br># Para el resto deberás configurar manualmente unas cuantas variables necesarias<br># http://www.cmake.org/Wiki/CMake:CPackPackageGenerators<br></pre><br><h2>Usando ensamblador</h2><br>CMake soporta correctamente GNU ASM. Nasm requiere más trabajo.<br><pre class="lang:default decode:true">PROJECT(gnu-asm ASM C)<br>CMAKE_MINIMUM_REQUIRED(VERSION 2.8)<br><br>ENABLE_LANGUAGE(ASM-ATT)<br><br>FILE(GLOB ASM_SOURCES "*.asm")<br>FILE(GLOB C_SOURCES "*.c")<br><br>ADD_LIBRARY(asm STATIC ${ASM_SOURCES})<br>ADD_EXECUTABLE(gnu-asm ${C_SOURCES})<br>TARGET_LINK_LIBRARIES(gnu-asm asm)<br></pre><br><h2>Algunas variables interesantes</h2><br>|CMAKE_CURRENT_SOURCE_DIR|La ruta completa a la carpeta donde se encuentra CMakeLists.txt|<br>|CMAKE_MODULE_PATH|Las rutas para buscar plugins de CMake|<br>|PROJECT_BINARY_DIR|La carpeta que se está usando para guardar los resultados de la compilación|<br>|CMAKE_INCLUDE_PATH|Las carpetas de búsqueda de cabeceras|<br>|CMAKE_VERSION|Versión de CMake|<br>|CMAKE_SYSTEM|El nombre del sistema|<br>|CMAKE_SYSTEM_NAME|El sistema operativo|<br>|CMAKE_SYSTEM_PROCESSOR|El procesador|<br>|CMAKE_GENERATOR|El generador usado en ese momento|<br>|UNIX|Si estamos en Linux, OS X, BSD o Solaris será cierto|<br>|WIN32|Si estamos en Windows|<br>|APPLE|En OS X|<br>|MINGW| Usando MinGW|<br>|MSYS| Usando MSYS|<br>|BORLAND| Usando Borland|<br>|CYGWIN| Usando Cygwin|<br>|WATCOM| Usando OpenWatcom|<br>|MSVC| Usando Visual Studio|<br>|MSVC10| Usando Visual Studio 10|<br>|CMAKE_C_COMPILER_ID| El identificador de compilador de C|<br>|CMAKE_CXX_COMPILER_ID| El identificador de compilador de C++|<br>|CMAKE_COMPILER_IS_GNUCC| El compilador de C es una variante de GNU GCC|<br>|CMAKE_COMPILER_IS_GNUCXX| El compilador de C++ es una variante de GNU G++|<br>|CMAKE_BUILD_TYPE| La configuración Debug/Release que estamos usando|<br>|CMAKE_C_COMPILER| La ruta al compilador de C|<br>|CMAKE_C_FLAGS| La configuración del compilador de C|<br>|CMAKE_C_FLAGS_DEBUG| La configuración del compilador de C solo si estamos en la configuración Debug|<br>|CMAKE_C_FLAGS_RELEASE| La configuración del compilador de C solo si estamos en la configuración Release|<br>|CMAKE_SHARED_LINKER_FLAGS| La configuración del compilador para librerías compartidas|<br>|BUILD_SHARED_LIBS| Por defecto en ADD_LIBRARY, las librerías son compartidas. Podemos cambiar esto|<br><br>Muchas más en la <a href="http://www.cmake.org/Wiki/CMake_Useful_Variables">wiki de CMake</a><br><h2>RPath</h2><br>El RPath es importante en los sistemas UNIX. Se trata de cargar librerías dinámicas que no están en directorios estándar.<br><pre class="lang:default decode:true">SET(CMAKE_SKIP_BUILD_RPATH FALSE)<br>SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)<br>SET(CMAKE_INSTALL_RPATH "$ORIGIN")<br>SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)<br></pre><br>Esto hará que los ejecutables construidos en UNIX puedan cargar librerías desde la carpeta donde se encuentran. Al estilo Windows.]]></description>
                <comments>https://blog.adrianistan.eu/tutorial-de-cmake</comments>
                <pubDate>Mon, 22 Jun 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Literatura Adrianistaní, un libro ligero de relatos cortos</title>
                <link>https://blog.adrianistan.eu/literatura-adrianistani-libro-ligero-relatos-cortos</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/literatura-adrianistani-libro-ligero-relatos-cortos</guid>
                <description><![CDATA[Finalmente después de convencer a Raúl Izquierdo y después de maquetar los relatos ya está disponible <strong><a href="http://adrianistan.eu/literatura-adrianistani/">Literatura Adrianistaní</a></strong><em> para comprar. Se trata de un libro de recopilación de algunos relatos cortos que teníamos por ahí.</em><br><br><img class="alignnone" src="http://adrianistan.eu/literatura-adrianistani/LiteraturaAdrianistani-medium.jpg" width="800" height="1049" /><br><br>Se trata de 11 relatos, algunos mucho más largo que otros. De ellos 3 son de Raúl y el resto son míos. La idea de este libro surgió después de perder un concurso literario donde había un generoso premio económico. Entonces decidimos vender nuestros propios relatos, muchos presentados a otros concursos, simplemente para ver que éramos capaces.<br><h2>Prefacio</h2><br><blockquote>Este libro es una colección de pequeños relatos que hemos ido componiendo en nuestro paso por la faz de la Tierra. Un pequeño trocito de nosotros directo a vosotros. - <em>Adrián Arroyo Calle</em></blockquote><br>&nbsp;<br><blockquote>En este pequeño libro están escritos los mejores relatos, compuestos de la mejor creatividad, y en ellos, hacemos lo posible por transportaros a la más inimaginable realidad para que al menos por un ratito os hagamos sentir irreales, y a la vez igual de dichosos que nosotros nos sentimos al presentar esta colección. Disfrutadlo. <em>Raúl Izquierdo Buznego</em></blockquote><br><h2>Comprar</h2><br>He subido el libro a un par de tiendas, ninguna de las copias tiene DRM y en la mayoría de tiendas está en formato EPUB. Si lo deseas, puedes enviarme un correo para realizar la transacción sin intermediarios.<br><br><a href="http://adrianistan.eu/literatura-adrianistani">Comprar Literatura Adrianistaní</a>]]></description>
                <comments>https://blog.adrianistan.eu/literatura-adrianistani-libro-ligero-relatos-cortos</comments>
                <pubDate>Sun, 14 Jun 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Proyecto Ceres</title>
                <link>https://blog.adrianistan.eu/proyecto-ceres</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/proyecto-ceres</guid>
                <description><![CDATA[El círculo de Deming es una estrategia de mejora continua de la calidad. También tenemos DMAIC y Seis Sigma. Parece por tanto que la mejora continua de la calidad es importante dentro de cualquier proceso productivo.<br><br><img class="alignnone size-full wp-image-26" src="https://files.adrianistan.eu/DMAIC.jpg" alt="DMAIC" width="784" height="556" /><br><br>Parte de mi software más exitoso de cara al público han sido las extensiones que he ido desarrollando para Firefox. Ayudan a la gente y además gano un dinerillo. Recibes feedback fácilmente e incluso me han propuesto ideas de desarrollo de nuevos complementos. Es algo agradable si sale bien, pero no he tenido tiempo últimamente para diseñar nuevas extensiones o mejorar las existentes. Así que algunas extensiones dejaron de ser tan funcionales y decidí que requerían pasar por un proceso de mejora continua de calidad. El Proyecto Ceres.<br><br><img class="alignnone size-full wp-image-27" src="https://files.adrianistan.eu/DMAIC-2.png" alt="DMAIC-2" width="450" height="317" /><br><br>Este proyecto busca actualizar las extensiones para una mejor experiencia de uso. El registro de cambios es voluminoso, aunque en muchos casos la solución adoptada para un complemento es válida para otro.<br><h2>Lista de cambios</h2><br><ul><br> 	<li><a href="https://addons.mozilla.org/es/firefox/addon/divtranslate/?src=userprofile">DivTranslate</a><br><ul><br> 	<li>La API de ScaleMT había dejado de funcionar. Ahora se usa APY.</li><br></ul><br></li><br> 	<li><a href="https://addons.mozilla.org/es/firefox/addon/mozcleaner/?src=userprofile">mozCleaner</a><br><ul><br> 	<li>Errores de escritura</li><br></ul><br></li><br> 	<li><a href="https://addons.mozilla.org/es/firefox/addon/the-super-clock/?src=userprofile">The Super Clock</a><br><ul><br> 	<li>Pequeño fallo que hacía que horas con un cero delante no ocupasen lo mismo que las demás. Por ejemplo, 21:04 era representado como 21:4.</li><br></ul><br></li><br> 	<li><a href="https://addons.mozilla.org/es/firefox/addon/google-share/?src=userprofile">Google+ Share</a><br><ul><br> 	<li>Arreglar un fallo molesto que hacía que desapareciese el icono en algunas ventanas. Gracias a ismirth por decírmelo. Se actualizó a Australis.</li><br> 	<li>Traducción de Google+ Share a diversos idiomas</li><br> 	<li>Gestión analítica</li><br></ul><br></li><br> 	<li><a href="https://addons.mozilla.org/es/firefox/addon/no-m%C3%A1s-900/?src=userprofile">No más 900</a><br><ul><br> 	<li>Nuevo complemento</li><br></ul><br></li><br> 	<li><a href="https://addons.mozilla.org/es/firefox/addon/google-share-android/?src=userprofile">Google+ Share for Android</a><br><ul><br> 	<li>Actualizar rutas a los módulos de Addon SDK</li><br> 	<li>No interrumpir en las selecciones de texto. Se ha cambiado el lugar donde aparece el botón para no quitar la opción de seleccionar texto de Firefox.</li><br></ul><br></li><br> 	<li><a href="https://addons.mozilla.org/es/firefox/addon/google-adsense-earnings/?src=userprofile">Google AdSense Earnings</a><br><ul><br> 	<li>Nuevo complemento</li><br> 	<li>Módulo en npm de identificación con Google via OAuth2</li><br></ul><br></li><br> 	<li><a href="https://addons.mozilla.org/es/firefox/addon/bcvc-shortener/?src=userprofile">Bc.vc shortener</a><br><ul><br> 	<li>Nuevo complemento</li><br></ul><br></li><br> 	<li><a href="https://addons.mozilla.org/es/firefox/addon/divhttp/?src=userprofile">DivHTTP</a><br><ul><br> 	<li>Traducciones</li><br> 	<li>Ahora viene con su propia copia de httpd.js, puesto que ha sido retirado de Firefox</li><br></ul><br></li><br> 	<li><a href="https://addons.mozilla.org/es/firefox/addon/divel-notepad/?src=userprofile">Divel Notepad</a><br><ul><br> 	<li>Traducciones</li><br></ul><br></li><br></ul><br>El objetivo con esto es elevar la calificación que tengo en AMO por encima de las 3 estrellas sobre 5 actuales que tengo. He recibido malas críticas en algunos de estos complementos que me gustaría intentar remediar.]]></description>
                <comments>https://blog.adrianistan.eu/proyecto-ceres</comments>
                <pubDate>Thu, 04 Jun 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>La gestión de la memoria en Rust</title>
                <link>https://blog.adrianistan.eu/la-gestion-de-la-memoria-en-rust</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/la-gestion-de-la-memoria-en-rust</guid>
                <description><![CDATA[Finalmente <a href="http://blog.rust-lang.org/2015/05/15/Rust-1.0.html">ha sido publicada la versión 1.0 de Rust</a>. El lenguaje diseñado por Mozilla basado en 3 principios:<br><ul><br> 	<li>Seguridad</li><br> 	<li>Concurrencia</li><br> 	<li>Rendimiento</li><br></ul><br>Hoy voy a hablar del primer principio, la razón principal para querer ser un sustituto de C++. Porque C++ está bien, pero puedes liarla mucho si no sabes lo que haces.<br><br><img class="alignnone size-large wp-image-100" src="https://files.adrianistan.eu/Rust-1024x576.jpg" alt="Rust" width="840" height="473" /><br><h2>El concepto de dueño</h2><br>En Rust todo tiene un dueño. No puede haber más de uno ni ninguno, debe ser uno exactamente.<br><pre class="lang:rust decode:true">fn main(){<br>	let A = 5;<br>    // El dueño de A es main()<br><br>}<br></pre><br>Hasta aquí todo es sencillo. Ahora pasaremos la variable A a otra función.<br><pre class="lang:rust decode:true">fn sumar(a: i32, b: i32) -&gt; i32{<br>	a+b<br>}<br>fn main(){<br>	let A = 5;<br>	let suma = sumar(A,4);<br>	println!("{}",suma);<br>}<br></pre><br>El programa compila y nos da el resultado, que es 9. En los lenguajes de bajo nivel las variables pueden usar memoria del stack o del heap. Un pequeño repaso sobre sus diferencias.<br><h6>Stack</h6><br><ul><br> 	<li>Se reserva su espacio en RAM cuando el programa arranca</li><br> 	<li>Son más rápidas de acceder</li><br> 	<li>No se les puede cambiar el tamaño</li><br> 	<li>Son más seguras</li><br></ul><br><h6>Heap</h6><br><ul><br> 	<li>Se debe reservar manualmente la RAM cuando queramos</li><br> 	<li>Son más lentas de acceder</li><br> 	<li>Pueden cambiar su tamaño en tiempo real</li><br> 	<li>Son menos seguras. Pueden dar lugar a fugas de memoria.</li><br></ul><br>En este último caso, la variable A cuyo dueño es main() le pasa la propiedad temporalmente a sumar(). La propiedad se devuelve a main() rápidamente y esta garantizado que así suceda. El compilador lo permite. Veamos ahora un ejemplo más complejo.<br><br>Veamos ahora un código más complejo<br><pre class="lang:rust decode:true">struct Config{<br>	debug_mode: bool<br>}<br><br>struct App{<br>	config: Config<br>}<br><br>fn main(){<br>	let config=Config{debug_mode: true};<br>	<br>	let app=App{config: config};<br>	<br>	println!("OK");<br>}<br></pre><br>Por supuesto el código compila pero este de aquí abajo no y solo he cambiado una línea.<br><pre class="lang:rust decode:true">struct Config{<br>	debug_mode: bool<br>}<br><br>struct App{<br>	config: Config<br>}<br><br>fn main(){<br>	let config=Config{debug_mode: true};<br>	<br>	let app=App{config: config};<br>	let backup=App{config: config}; // He añadido esta línea<br>	<br>	println!("OK");<br>}<br></pre><br>La razón es que cuando creamos la estructura de App por primera vez le prestamos config a la estructura app. Así la función main no le puede pasar la propiedad a backup porque ya se la prestó a app.<br><h2>Préstamos</h2><br>Para solucionar este problema Rust usa los préstamos. Así la propiedad de config seguirá siendo main() pero lo podrán usar las estructuras app y backup. Para usar referencias usamos el símbolo <em>&amp;</em>.<br><pre class="lang:rust decode:true">struct Config{<br>	debug_mode: bool<br>}<br><br>struct App{<br>	config: &amp;Config<br>}<br><br>fn main(){<br>	let config=Config{debug_mode: true};<br>	<br>	let app=App{config: &amp;config};<br>	let backup=App{config: &amp;config};<br>	<br>	println!("OK");<br>}<br></pre><br>La estrucura ahora acepta &amp;Config en vez de Config. Es de decir usa una referencia en vez de un valor. Sin embargo esto no compilará. El compilador normalmente deduce si es posible hacer una referencia a algo no existente, un fallo común en C++. En caso de tener dudas no compilará. Rust es bastante inteligente pero no es mágico. En el caso de la estructura App, es necesario indicar que la propiedad config vivirá el mismo tiempo que la estructura.<br><pre class="lang:rust decode:true">struct Config{<br>	debug_mode: bool<br>}<br><br>struct App&lt;'a&gt;{<br>	config: &amp;'a Config<br>}<br><br>fn main(){<br>	let config=Config{debug_mode: true};<br><br>	let app=App{config: &amp;config};<br>	let backup=App{config: &amp;config};<br>	println!("OK");<br>}<br></pre><br>He usado la anotación de tiempo llamada <em>a</em>. Puedes poner cualquier nombre pero <em>a</em> es muy corto.<br><h2>Implementaciones y préstamos</h2><br>Voy a introducir un concepto de Rust que son las implementaciones. Para haceros una idea rápida, serían como clases de C++, pero solo alojan funciones.<br><pre class="lang:rust decode:true">impl&lt;'a&gt; App&lt;'a&gt;{<br>	fn isDebugMode(&amp;self) -&gt; (){<br>		println!("DEBUG MODE: {}",self.config.debug_mode);<br>	}<br>	<br>	fn delete(self) -&gt; (){<br>		<br>	}<br>}<br></pre><br>He creado dos funciones para implementar App. Son idénticas salvo por un pequeño detalle, una toma el valor self (como this en C++) por referencia y la otra toma el valor directamente.<br><pre class="lang:rust decode:true">	let app=App{config: &amp;config};<br>	app.isDebugMode();<br>	app.delete();<br></pre><br>Compila y funciona. Cambiemos el orden.<br><pre class="lang:rust decode:true">    let app=App{config: &amp;config};<br>    app.delete();<br>    app.isDebugMode();<br></pre><br>Ya no compila. La razón es que cuando llamamos a delete() estamos prestando app entera. Ahora delete() es la dueña de app y cuando salimos de la función eliminamos app porque si su dueña ha muerto, app también debe morir (no es tan sangriento como pensais). Rust lo detecta y delete() será la última función que podemos llamar de app. Por cierto si os preguntais como funcionan las implementaciones en Rust (que no son clases), este código haría lo mismo llamando a funciones estáticas. Quizá así veais mejor como se pasa el concepto de dueños y préstamos.<br><pre class="lang:rust decode:true">	let app=App{config: &amp;Config};<br>    App::isDebugMode(&amp;app);<br>    App::delete(app);<br></pre><br><h2>Diversión con punteros en el heap</h2><br>Todo estas variables eran del stack que siempre es la manera más sencilla de operar. Vamos ahora a ver como funcionaría esto con punteros. Los punteros operan como variables en el stack que hacen referencia a partes de la memoria que están en el heap. En Rust podemos operar con punteros con máxima seguridad pues todo lo aplicable a variables en el stack sigue siendo válido. Solo hay un dueño y podemos hacer referencias, aunque quizá necesitemos marcar el tiempo de vida manualmente.<br><pre class="lang:rust decode:true">	let puntero: Box&lt;i32&gt;=Box::new(42);<br></pre><br>Ahora el valor 42 estará en el heap y con puntero podremos acceder a él. Sin embargo como es lógico, no podemos operar directamente con él.<br><pre class="lang:rust decode:true">	puntero+1 //No funciona<br></pre><br>Para operar el valor directamente tenemos que derreferenciarlo. Se usa *<br><pre class="lang:rust decode:true">	*puntero+1 // Sí funciona, y será 43<br></pre><br>Así que esta operación sería correcto. Nótese el uso de <strong>mut</strong> para permitir editar el valor. En Rust por defecto las variables no son mutables. Ese privilegio tiene que ser declarado por adelantado.<br><pre class="lang:rust decode:true">	let mut puntero: Box&lt;i32&gt;=Box::new(41);<br>    *puntero+=1;<br>  	println!("La respuesta es: {}",*puntero);<br></pre><br>Como curiosidad mencionar que la macro println! (en Rust si algo termina con <strong>!</strong> es una macro) acepta puntero o *puntero indistintamente ya que se da cuenta si es necesario derreferenciar o no.<br><h2>El problema final</h2><br>¿Qué pasaría si copiamos un puntero en otro? Pues como un valor en el heap solo puede tener un dueño, la propiedad será del último puntero.<br><pre class="lang:rust decode:true">	let mut puntero: Box&lt;i32&gt;=Box::new(41);<br>    *puntero+=1;<br>    let puntero_inmutable=puntero;<br>    println!("La respuesta es: {}",puntero); // Esta línea no compilará pues el acceso a la respuesta última del universo ahora es propiedad de puntero_inmutable<br>    println!("La respuesta, ahora sí, es: {}",puntero_inmutable);<br></pre><br>Como curiosidad, este es un curioso método para bloquear en un determinado momento el acceso de escritura a nuestro puntero aunque es fácil volver a obtener el acceso a escritura con un nuevo cambio de dueño.<br><h2>Conclusiones</h2><br>Podemos ver que es un lenguaje que presta mucha atención a la seguridad. C++ es mucho más liberal en ese sentido y Mozilla cree que es un problema a la hora de desarrollar grandes aplicaciones. ¿Qué te ha parecido? Si tienes alguna duda no titubees y pregunta.]]></description>
                <comments>https://blog.adrianistan.eu/la-gestion-de-la-memoria-en-rust</comments>
                <pubDate>Fri, 22 May 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Crea tu propio lenguaje de programación</title>
                <link>https://blog.adrianistan.eu/crea-tu-propio-lenguaje-de-programacion</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/crea-tu-propio-lenguaje-de-programacion</guid>
                <description><![CDATA[Esta entrada la escribí en <a href="http://blog.desdelinux.net/crea-tu-propio-lenguaje-de-programacion/">DesdeLinux</a> hace ya un tiempo y la quiero conservar aquí. La entrada no es la original sino que la he modificado para que siga vigente - <strong>MAYO DE 2015</strong><br><br><img class="alignnone size-full wp-image-63" src="https://files.adrianistan.eu/LenguajesProgramacion.jpg" alt="LenguajesProgramacion" width="600" height="250" /><br><br>Después de escribir el primer artículo sobre cómo crear tu propio sistema operativo, alguien me dijo que si podía hacer un artículo sobre cómo crear un lenguaje de programación. Al principio no hice mucho caso, pero ahora y por otros caminos he aprendido bastante más sobre la creación de los lenguajes de programación. Así pues, vamos a hacer un lenguaje de programación básico, fácilmente empotrable en otros programas y que funcione con una máquina virtual que también diseñaremos. Hoy nos toca hacer la máquina virtual más básica.<br><br>Probablemente te preguntes: “¿una máquina virtual? ¿Pero eso no es muy difícil y además ralentiza los programas?” Al contrario, una máquina virtual simple es muy sencilla y relativamente rápida. He elegido Rust como lenguaje para la máquina virtual. Pero, ¿qué es <a href="http://rust-lang.org">Rust</a>?<br><blockquote><a href="http://rust-lang.org">Rust</a> es un lenguaje de programación que está enfocado en la seguridad en las ejecuciones, así que utilizándolo será prácticamente imposible que alguien consiga cerrar la máquina virtual. Es un lenguaje compilado en desarrollo creado por Mozilla. Servo, el sustituto de Gecko, se está desarrollando en él. Todavía puede cambiar su sintaxis pero el código que voy a usar va a mantenerse hasta la primera versión estable.</blockquote><br>Rust se instala en Linux de manera sencilla. Antes se podía usar un PPA pero ahora el script de RustUp es muy bueno y se encarga de todo.<br><pre class="lang:default decode:true">curl -s https://static.rust-lang.org/rustup.sh | sudo sh<br></pre><br><h3>¿Cómo funciona una máquina virtual?</h3><br>Si sabes como funciona el mundo en ensamblador es exactamente igual, con el stack o la pila. Si no, te lo explico. Imaginémonos el siguiente código:<br><pre class="lang:default decode:true">print 2+3<br></pre><br>El ordenador no entiende lo que significa 2+3, ni tampoco sabe qué orden hay que seguir. Los ordenadores funcionan con pilas o stacks en los que se van acumulando datos y se van sacando continuamente. Ese código en nuestra máquina virtual debería ser algo parecido a esto:<br><pre class="lang:default decode:true">PUSH 2<br>PUSH 3<br>ADD<br>PRINT<br></pre><br>Básicamente, pondríamos el 2 en la pila en lo alto, el 3 también. ADD sacaría (es decir, lo elimina de la pila y obtiene su valor) los 2 últimos elementos de la pila y añadiría el resultado en lo alto de la pila. PRINT cogería el último elemento de la pila y lo usaría para mostrárnoslo. Ahora hagamos eso en Rust.<br><blockquote>Ahora es cuando deberías descargarte el código fuente que está en GitHub. Voy a empezar por el archivo <em>vm.rs</em></blockquote><br>Primeramente deberemos definir un lenguaje para el Bytecode, podríamos usar uno ya existente como el de Java o el CLR de .NET/Mono, pero vamos a crear nosotros uno más básico.<br><pre class="lang:rust decode:true ">#[deriving(FromPrimitive)]<br>enum Instruction {<br>  INTEGER = 0x00,<br>  STRING = 0x01,<br>  ADD = 0x02,<br>  SHOWINTEGER = 0x0A,<br>  SHOWVERSION = 0x0E,<br>  EXITVM = 0x0F<br>}</pre><br>&nbsp;<br><br>Usamos notación hexadecimal para cada instrucción. Para hacer la traducción entre el código hexadecimal y la instrucción voy a usar la librería (crate en el argot de Rust) de enum_primitive. Antes se podía usar #[derive(FromPrimitive)] pero en Rust 1.0 no está disponible.<br><br>Ahora debemos hacer una función que ejecute cada una de esas instrucciones. Para ello debemos leer un byte y compararlo con las instrucciones que tenemos en la enumeración. Si se encuentra alguna que exista se debe ejecutar su acción.<br><pre class="lang:rust decode:true ">pub fn interpreter(&amp;mut self,bytecode: &amp;'static str) -&gt; (){<br>  for execbyte in bytecode.chars() {<br>    self.execute(execbyte as u8);<br>  }<br>}</pre><br>&nbsp;<br><br>Eso hacemos para leer cada byte individualmente y para ejecutarlas:<br><pre class="lang:rust decode:true ">fn execute(&amp;mut self, execbyte: u8) -&gt; () {<br>  if self.push {<br>    self.push(execbyte);<br>    self.push=false;<br>  }else{<br>    let op: Option&lt;Instruction&gt; = FromPrimitive::from_u8(execbyte);<br>    match op{<br>      None =&gt; {<br>        println!("Unknown instruction, skipping...");<br>      },<br>      Some(bc) =&gt; {<br>        match bc{<br>          INTEGER =&gt; {<br>            self.push=true;<br>          },<br>          ADD =&gt; {<br>            let a=self.pop() as int;<br>            let b=self.pop() as int;<br>            let c=a+b;<br>            self.push(c as u8);<br>          },<br>          SHOWINTEGER =&gt; {<br>            println!("Integer value {}",self.pop() as int);<br>          },<br>          SHOWVERSION =&gt; {<br>            println!("PerinVM v0.1.0");<br>          },<br>          EXITVM =&gt; {<br>            println!("Exit VM");<br>          },<br>          STRING =&gt; {<br>            println!("Unsupported instruction 'STRING' ");<br>          }<br>        }<br>      }<br>    }<br>  }<br>}</pre><br>&nbsp;<br><br>Como ven, diferenciamos si antes se nos dio la orden de PUSH (nuestra orden INTEGER), el siguiente byte será llevado completamente a la pila. Ahí estamos usando dos funciones que no les he enseñado, self.pop() y self.push(), que se encargan obviamente de manejar el stack.<br><pre class="lang:rust decode:true ">fn push(&amp;mut self, value: u8) -&gt; (){<br>  self.stack.push(value);<br>}<br>fn pop(&amp;mut self) -&gt; u8{<br>  let a: Option&lt;u8&gt;=self.stack.pop();<br>  match a{<br>    None =&gt; {<br>      println!("Failed to pop");<br>      0<br>    },<br>    Some(result) =&gt; {<br>      result<br>    }<br>  }<br>}</pre><br>&nbsp;<br><br>No son muy complejas, pero la función de pop tiene mecanismos de detección de errores. De hecho, en Rust, si quitásemos esos mecanismos nos daría un error de compilación. Ahora simplemente debemos llamar en un programa a Perin (nuestra máquina virtual) y que ejecute un bytecode.<br><pre class="lang:rust decode:true ">mod vm;<br><br>fn main(){<br>  let bytecode = "\x0E\x00\x02\x00\x03\x02\x0A";<br>  let mut perin = vm::PerinVM::new();<br>  perin.interpreter(bytecode);<br>}</pre><br>&nbsp;<br><br>Ese bytecode puede ser leído de un fichero, pero aquí para simplificar lo he almacenado en una variable. Si lo ejecutamos nos dará el resultado esperado:<br><pre class="lang:default decode:true">Perin v0.1<br>Perin VM executes FlopFlip bytecode<br>Starting PerinVM instance<br>PerinVM v0.1.0<br>Integer value 5<br></pre><br>Todo el código está disponible en GitHub bajo la Apache License 2.0: <a href="https://github.com/AdrianArroyoCalle/perin">https://github.com/AdrianArroyoCalle/perin</a>. Para compilar deben tener Cargo instalado y poner:<br><pre class="lang:default decode:true">cargo run<br></pre>]]></description>
                <comments>https://blog.adrianistan.eu/crea-tu-propio-lenguaje-de-programacion</comments>
                <pubDate>Wed, 13 May 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Aprobar consiste en entrar en el juego</title>
                <link>https://blog.adrianistan.eu/aprobar-consiste-en-entrar-en-el-juego</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/aprobar-consiste-en-entrar-en-el-juego</guid>
                <description><![CDATA[<img src="https://files.adrianistan.eu/AprobarConsisteEnEntrarEnElJuego.jpg" alt="AprobarConsisteEnEntrarEnElJuego" width="480" height="672" class="alignnone size-full wp-image-13" /><br><br>Interesante reflexión]]></description>
                <comments>https://blog.adrianistan.eu/aprobar-consiste-en-entrar-en-el-juego</comments>
                <pubDate>Wed, 08 Apr 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Fliuva, o como crear un servicio de analíticas</title>
                <link>https://blog.adrianistan.eu/fliuva-o-como-crear-un-servicio-de-analiticas</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/fliuva-o-como-crear-un-servicio-de-analiticas</guid>
                <description><![CDATA[Ya hemos visto los <a href="https://blog.adrianistan.eu/2015/04/05/la-informacion-es-poder/">motivos</a> que tengo para diseñar un servicio de analíticas desde 0. Ahora veremos como lo he hecho.<br><img class="alignnone size-large wp-image-39" src="https://files.adrianistan.eu/Fliuva-1024x496.png" alt="Fliuva" width="840" height="407" /><br><br>No he tocado ningún CSS.<br><h2>La base de datos</h2><br>En un servicio de analíticas lo más importante son los datos, así que debemos de definir como almacenaremos la información. En principio Fliuva iba a estar centrado en analizar una única aplicación. Más tarde he pensado en un soporte multiaplicaciones pero no lo voy a desarrollar de momento. Usaría un espacio de nombres delante de todas las tablas y las peticiones GET.<br><blockquote>Usuarios = Sesiones</blockquote><br>En ciertos servicios de analíticas los usuarios y las sesiones son dos conceptos diferentes. En Fliuva sin embargo, y por razones de simplificar la estructura, ambos conceptos son idénticos y hablaremos de ellos como sesiones.<br><h3>Tablas</h3><br>Será necesario tener una tabla de eventos.<br><h4>Events</h4><br><ul><br> 	<li>CATEGORY: Categoría del evento</li><br> 	<li>SUBCATEGORY: Subcategoría del evento</li><br> 	<li>NAME: Nombre del evento</li><br> 	<li>DESCRIPTION: Descripción del evento</li><br> 	<li>DATA: Datos del evento</li><br> 	<li>ID: Identificador</li><br> 	<li>TIME: Hora en que se produjo</li><br> 	<li>SESSION: Sesión a la que pertenece el evento</li><br></ul><br><pre class="lang:tsql decode:true ">CREATE TABLE IF NOT EXISTS EVENTS(ID INT NOT NULL AUTO_INCREMENT, CATEGORY TEXT, SUBCATEGORY TEXT, NAME TEXT, DESCRIPTION TEXT, DATA TEXT, TIME DATETIME, SESSION TEXT, PRIMARY KEY (`ID`) )</pre><br>&nbsp;<br><br>Y ya con esta tabla tendremos para almacenar muchos datos. Los campos CATEGORY, SUBCATEGORY, NAME, DESCRIPTION y DATA sirven unicamente para organizar eventos y subeventos en categorías. Los nombres de los campos son triviales. DESCRIPTION no guarda realmente la descripción sino que podemos definir otro subevento. Con 5 campos para categorizar eventos superamos a Google Analytics que tiene CATEGORY, ACTION, LABEL y VALUE. Además VALUE debe ser numérico mientras que en Fliuva todos son de tipo texto (en la práctica, cualquier cosa).<br><h3>Código de seguimiento</h3><br>¿Cómo introducir datos en la base de datos desde nuestra aplicación? Con una pequeña llamada GET a /collect. Yo la he definido en un fichero llamado collect.js<br><pre class="lang:js decode:true">var mysql=require("mysql");<br><br>// GET /collect<br><br>module.exports=function(req,res){<br>var connection=mysql.createConnection({<br>    host: process.env.OPENSHIFT_MYSQL_DB_HOST,<br>    port: process.env.OPENSHIFT_MYSQL_DB_PORT,<br>    user: process.env.OPENSHIFT_MYSQL_DB_USER,<br>    password: process.env.OPENSHIFT_MYSQL_DB_PASSWORD,<br>    database: "fliuva"<br>});<br>connection.connect(function(err){<br>    if(err){<br>        res.send(501,"MySQL connection error\n"+err);<br>    }<br>    connection.query("CREATE TABLE IF NOT EXISTS EVENTS(ID INT NOT NULL AUTO_INCREMENT,"+<br>        "CATEGORY TEXT, SUBCATEGORY TEXT, NAME TEXT, DESCRIPTION TEXT, DATA TEXT, "+<br>        "TIME DATETIME, SESSION TEXT,"+<br>        "PRIMARY KEY (`ID`) )",function(err,results,fields){<br>            if(err){<br>                res.send(501,"MySQL table creation error\n"+err);<br>            }<br>            connection.query("INSERT INTO EVENTS SET ?",{<br>                SESSION: req.query.SESSION,<br>                TIME: new Date(),<br>                CATEGORY: req.query.CATEGORY || "",<br>                SUBCATEGORY: req.query.SUBCATEGORY || "",<br>                NAME: req.query.NAME || "",<br>                DESCRIPTION: req.query.DESCRIPTION || "",<br>                DATA: req.query.DATA || ""<br>            },function(err,results,fields){<br>                if(err){<br>                    res.send(501,"Query error\n"+err);<br>                }<br>                res.send("OK");<br>            });<br>       });<br>});<br>}<br><br></pre><br>Y se llama muy fácilmente<br><pre class="lang:default decode:true">GET /collect?CATEGORY=&lt;category&gt;&amp;SUBCATEGORY=&lt;subcategory&gt;&amp;NAME=&lt;name&gt;&amp;DESCRIPTION=&lt;description&gt;&amp;DATA=&lt;data&gt;&amp;SESSION=&lt;session&gt;<br><br></pre><br>Si estamos en HTML5, necesitaremos una librería de cliente para poder realizar las llamadas<br><pre class="lang:js decode:true">function login(){<br>    var xhr=new XMLHttpRequest();<br>    xhr.open("GET","/uuid");<br>    xhr.addEventListener("load",function(){<br>        sessionStorage.__fliuvaSession=xhr.responseText;<br>    });<br>    xhr.send();<br>}<br><br>function sendEvent(category,subcategory,name,description,data){<br>    var xhr=new XMLHttpRequest();<br>    var url="/collect"+<br>    "?CATEGORY="+category+<br>    "&amp;SUBCATEGORY="+subcategory+<br>    "&amp;NAME"+name+<br>    "&amp;DESCRIPTION"+description+<br>    "&amp;DATA"+data+<br>    "&amp;SESSION"+sessionStorage.__fliuvaSession;<br>    xhr.open("GET",url);<br>    xhr.send();<br>}<br><br></pre><br>Así, simplemente hay que llamar a sendEvent. Llamar a <em>login</em> no es necesario siempre que rellenes el valor de <em>sessionStorage.__fliuvaSession </em>correctamente.<br><h2>Análisis de datos</h2><br>Ahora debemos analizar los datos. Primero debemos obtener los valores a analizar. La llamada a /get devuelve un fichero JSON con la información completa. En un futuro lo ideal sería espeficicar intervalos de fechas.<br><pre class="lang:js decode:true">//GET /get<br>var mysql=require("mysql");<br><br>module.exports=function(req,res){<br>var connection=mysql.createConnection({<br>    host: process.env.OPENSHIFT_MYSQL_DB_HOST,<br>    port: process.env.OPENSHIFT_MYSQL_DB_PORT,<br>    user: process.env.OPENSHIFT_MYSQL_DB_USER,<br>    password: process.env.OPENSHIFT_MYSQL_DB_PASSWORD,<br>    database: "fliuva"<br>});<br>connection.query("SELECT * FROM EVENTS",function(err,results,fields){<br>    res.send(JSON.stringify(results));<br>});<br>}<br><br></pre><br>Y en el cliente tratamos los datos. Aquí es donde debemos nosotros mismos crear las estadísticas según las métricas que hayamos definido. Yo solo voy a poner una métrica universal, usuarios por día.<br><pre class="lang:js decode:true">/* Visualization App */<br>window.addEventListener("load",function(){<br>    usersPerDay();<br>    sessionsTable();<br>});<br><br>function id(idx){<br>    return document.getElementById(idx);<br>}<br><br>function uniqBy(a, key) {<br>    var seen = {};<br>    return a.filter(function(item) {<br>        var k = key(item);<br>        return seen.hasOwnProperty(k) ? false : (seen[k] = true);<br>    })<br>}<br>function ISODateString(d){<br>    function pad(n){return n&lt;10 ? '0'+n : n}<br>    return d.getUTCFullYear()+'-'<br>    + pad(d.getUTCMonth()+1)+'-'<br>    + pad(d.getUTCDate());<br>}<br><br>/* Users-per-day */<br>function usersPerDay(){<br>    var xhr=new XMLHttpRequest();<br>    xhr.overrideMimeType("application/json");<br>    xhr.open("GET","/get");<br>    xhr.addEventListener("load",function(){<br>        var json=JSON.parse(xhr.responseText);<br>        var dataset=new vis.DataSet();<br>        /*var data=json.filter(function(){<br><br>        });*/<br>        var array=uniqBy(json,function(item){<br>            return item.SESSION;<br>        }); // Eventos de sesiones repetidas eliminados (mismos usuarios). Ahora tenemos sesiones únicas y tiempos distintos<br>        for(var i=0;i&lt;array.length;i++)<br>        {<br>            var time=new Date(array[i].TIME);<br>            var date=ISODateString(time); //time.toISOString().substring(0,time.toISOString().indexOf("T"));<br><br>            var y;<br>            if(dataset.get(date)==null)<br>                y=1;<br>            else<br>                y=dataset.get(date).y+1;<br><br>            console.log(date);<br>            dataset.update({x: date, id: date, y: y});<br>        }<br>        var options = {<br>            catmullRom: false<br>        };<br>        var graph2d = new vis.Graph2d(id("users-per-day"), dataset, options);<br>    });<br>    xhr.send();<br>}<br><br>/* Table for sessions */<br><br>function sessionsTable(){<br>	var table=document.getElementById("sessions");<br>	var xhr=new XMLHttpRequest();<br>	xhr.open("GET","/get");<br>	xhr.addEventListener("load",function(){<br>		var json=JSON.parse(xhr.responseText);<br>		var data=uniqBy(json,function(item){<br>			return item.SESSION;<br>		});<br>		for(var i=0;i&lt;data.length;i++)<br>		{<br>			var item=data[i];<br>			var tr=document.createElement("tr");<br>			var time=document.createElement("td");<br>			time.textContent=item.TIME;<br>			var session=document.createElement("td");<br>			var link=document.createElement("a");<br>			link.href="/session/"+item.SESSION;<br>			link.textContent=item.SESSION;<br>			session.appendChild(link);<br>			tr.appendChild(time);<br>			tr.appendChild(session);<br>			table.appendChild(tr);<br>		}<br>	});<br>	xhr.send();<br>}<br><br></pre><br>Que se corresponde a este pequeño HTML<br><pre class="lang:default decode:true">&lt;!DOCTYPE html&gt;<br>&lt;html&gt;<br>	&lt;head&gt;<br>		&lt;title&gt;Fliuva - Página principal&lt;/title&gt;<br>		&lt;meta charset="utf-8"/&gt;<br>		&lt;script src="bower_components/vis/dist/vis.min.js" type="text/javascript"&gt;&lt;/script&gt;<br>		&lt;link href="bower_components/vis/dist/vis.min.css" rel="stylesheet" media="all" type="text/css"&gt;<br>		&lt;script src="/app.js"&gt;&lt;/script&gt;<br>	&lt;/head&gt;<br>	&lt;body&gt;<br>		&lt;h1&gt;Fliuva&lt;/h1&gt;<br>		&lt;section&gt;<br>			&lt;h3&gt;Usuarios por día&lt;/h3&gt;<br>			&lt;div id="users-per-day"&gt;&lt;/div&gt;<br>		&lt;/section&gt;<br>		&lt;section&gt;<br>			&lt;table id="sessions"&gt;<br>				&lt;tr&gt;<br>					&lt;th&gt;Tiempo&lt;/th&gt;<br>					&lt;th&gt;Sesión&lt;/th&gt;<br>				&lt;/tr&gt;<br>			&lt;/table&gt;<br>		&lt;/section&gt;<br>	&lt;/body&gt;<br>&lt;/html&gt;<br></pre><br>Visualizar cada sesión por separado es posible con la llamada a /session/NOMBRE_DE_SESION<br><pre class="lang:js decode:true">var mysql=require("mysql");<br><br>module.exports=function(req,res){<br>	var session=req.params.session;<br>	var connection=mysql.createConnection({<br>		host: process.env.OPENSHIFT_MYSQL_DB_HOST,<br>		port: process.env.OPENSHIFT_MYSQL_DB_PORT,<br>		user: process.env.OPENSHIFT_MYSQL_DB_USER,<br>		password: process.env.OPENSHIFT_MYSQL_DB_PASSWORD,<br>		database: "fliuva"<br>	});<br>	connection.query("SELECT * FROM EVENTS WHERE SESSION = ?",[session],function(err,results){<br>		if(err)<br>			res.send(502,"Error: "+err);<br>		res.render("session.jade",{events: results});<br>	});<br>}<br><br></pre><br>Y para un rápido procesamiento he decidido usar Jade con JavaScript en el servidor. Y entonces session.jade queda<br><pre class="lang:default decode:true">doctype html<br>html<br>	head<br>		title Vista de sesión<br>		meta(charset="utf-8")<br>	body<br>		h1 Vista de sesión<br>		table<br>			tr<br>				th Categoría<br>				th Subcategoría<br>				th Nombre<br>				th Descripción<br>				th Datos<br>				th Tiempo<br>			each event in events<br>				tr<br>					td= event.CATEGORY<br>					td= event.SUBCATEGORY<br>					td= event.NAME<br>					td= event.DESCRIPTION<br>					td= event.DATA<br>					td= event.TIME<br><br></pre><br><h2>Juntando piezas</h2><br>Por último, la aplicación se tiene que iniciar en algún lado. Server.js contiene el arranque<br><pre class="lang:js decode:true">var express=require("express");<br>var mysql=require("mysql");<br>var collect=require("./collect");<br>var getdata=require("./getdata");<br>var session=require("./session");<br>var uuid=require("node-uuid");<br><br>var app=express();<br><br>app.set("views",__dirname + "/jade");<br>app.set("view engine","jade");<br><br>app.get("/collect",collect);<br><br>app.get("/get",getdata);<br><br>app.get("/uuid",function(req,res){<br>	res.send(uuid.v4());<br>});<br><br>app.get("/session/:session",session);<br><br>app.use(express.static("www"));<br><br>var ip=process.env.OPENSHIFT_NODEJS_IP || process.env.OPENSHIFT_INTERNAL_IP || "127.0.0.1";<br>var port=process.env.OPENSHIFT_NODEJS_PORT || process.env.OPENSHIFT_INTERNAL_PORT || 8080;<br><br>var server=app.listen(port,ip);<br><br></pre><br>Y así en un pis pas hemos hecho una aplicación de seguimiento y analíticas en JavaScript. Ahora toca empezar a diseñar estadísticas con los datos que tenemos a nuestra disposición y por supuesto cuando tengamos los datos a obrar en consecuencia.]]></description>
                <comments>https://blog.adrianistan.eu/fliuva-o-como-crear-un-servicio-de-analiticas</comments>
                <pubDate>Mon, 06 Apr 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>La información es poder</title>
                <link>https://blog.adrianistan.eu/la-informacion-es-poder</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/la-informacion-es-poder</guid>
                <description><![CDATA[La información es poder. La frase suele atribuirse a Francis Bacon y creo que en estos tiempos cada vez se vuelve más cierta. Términos como Big Data, políticas de privacidad y similares son términos para hablar del gran poder que nos ofrece la información, si la sabemos interpretar y usar en consecuencia.<br><h2>Obteniendo la información</h2><br>Esto venía para explicar que finalmente y después de pensarlo un rato he decidido crear mi propio sistema de analíticas para Secta Sectarium. Las analíticas pueden ofrecerme valiosa información pero no encontré ningún servicio que me gustase. Muchos sistemas de analíticas están centrados en blogs (como <a href="http://google.com/analytics">Google Analytics</a> o <a href="http://newrelic.com">New Relic Browser</a>) o en aplicaciones móviles (Google Analytics, <a href="https://developer.yahoo.com/analytics/">Flurry</a>). ¿Había algún servicio dedicado solo a juegos? Sí, <a href="http://www.gameanalytics.com/">GameAnalytics</a> es específico pero no tiene API para HTML5 (y las APIs REST no se pueden llamar entre dominios en HTML5, CORS se llama la idea). Google Analytics se puede modificar lo suficiente para funcionar pero ya que tenía que trabajarmelo he preferido crear mi propia solución.<br><br><img class="alignnone size-full wp-image-61" src="https://files.adrianistan.eu/LaGenteSeInventaEstadisticas.jpg" alt="LaGenteSeInventaEstadisticas" width="650" height="586" /><br><h2>Fliuva</h2><br>Así que he decidido gastar una gear de <a href="http://openshift.com">OpenShift</a> para una aplicación Node.js 0.12 y MySQL 5.5. Entre SQL y NoSQL he elegido SQL porque para introducir datos de eventos que luego, posteriormente, van a ser tratados, SQL da un mejor rendimiento y el esquema de tabla es más común. Las analíticas las podré ver desde la propia aplicación, que usa <a href="http://visjs.org">Vis.js</a> para la visualización.<br><br><img class="alignnone size-full wp-image-62" src="https://files.adrianistan.eu/LaPersonaMasPoderosa.jpg" alt="LaPersonaMasPoderosa" width="650" height="532" /><br><br>En próximas entradas veremos como se puede crear Fliuva y que métricas son más importantes.]]></description>
                <comments>https://blog.adrianistan.eu/la-informacion-es-poder</comments>
                <pubDate>Sun, 05 Apr 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Crea tu primer paquete para Haiku</title>
                <link>https://blog.adrianistan.eu/crea-tu-primer-paquete-para-haiku</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/crea-tu-primer-paquete-para-haiku</guid>
                <description><![CDATA[Hoy vamos a crear nuestro propio paquete para Haiku. Se trata de mi juego SuperFreeCell que diseñé para el Google Code-In 2014.<br><br><img class="alignnone size-large wp-image-108" src="https://files.adrianistan.eu/SuperFreeCell-1-1024x768.png" alt="SuperFreeCell-1" width="840" height="630" /><br><h2>Nuestra propia rama de desarrollo</h2><br>Si queremos publicar nuestros cambios a Haikuports, debemos hacernos una cuenta en BitBucket y hacer un fork del repositorio. Esto se hace desde <a href="http://github.com/haikuports/haikuports">http://github.com/haikuports/haikuports</a> y dándole al botón de fork o dividir. En mi caso creé el repositorio https://bitbucket.org/AdrianArroyoCalle/haikuports-superfreecell. Añadimos el repositorio a nuestra lista de orígenes en Git.<br><br><br><pre class="lang:default decode:true"><br><br>git remote add superfreecell https://bitbucket.org/AdrianArroyoCalle/haikuports-superfreecell<br><br></pre><br><br><h2>Ubicando el juego en Haikuports</h2><br>Nada más empezar tenemos que encontrar la categoría a la que pertenecerá el paquete. Cada carpeta dentro de la carpeta haikuports representa una categoría, que siguen el esquema de Gentoo. En mi caso creo que lo conveniente es "haiku-games". Dentro de esta carpeta creamos una con el nombre de nuestro paquete y allí almacenaremos la información sobre nuestro paquete. Estas carpetas deben tener al menos un archivo .recipe y pueden incluir las carpetas licenses y patches con licencias y parches adicionales respectivamente. En mi caso no usaremos ninguna de estas dos carpetas, así que creamos el archivo superfreecell-0.1.0.recipe . Es importante esta estructura para encontrar la versión fácilmente.<br><h2>El archivo .recipe</h2><br>El archivo .recipe contiene la información necesaria de metadatos e instrucciones para compilar e instalar el programa en cuestión.<br><br><br><pre class="lang:default decode:true"><br><br>SUMMARY=&amp;quot;Descripción de menos de 70 caracteres&amp;quot;<br>DESCRIPTION=&amp;quot;<br>Descripción extensa del programa usando \<br>para separar entre renglones que no deben superar \<br>los 80 caracteres<br>&amp;quot;<br>HOMEPAGE=&amp;quot;http://pagina-de-inicio.org.es&amp;quot;<br>SOURCE_URI=&amp;quot;http://una-pagina.com/con-el-archivo-del-codigo-fuente.tar.gz&amp;quot; # Se admiten muchas variaciones aquí. Podemos usar git://, hg://, svn://, bzr://, ftp://, http:// y los formatos de compresión tar.gz, tar.bz2, zip. Se admiten combinaciones de protocolos.<br>LICENSE=&amp;quot;MIT&amp;quot;<br>COPYRIGHT=&amp;quot;Año y autor&amp;quot;<br>REVISION=&amp;quot;1&amp;quot; # Siendo la misma versión del programa, revisiones del propio empaquetado<br>ARCHITECTURES=&amp;quot;?x86 x86_gcc2 !x86_64&amp;quot; # Arquitecturas compatibles, siendo x86_gcc2 estable, x86 sin probar (untested) pero que debería ir y x86_64 incompatible<br>if [ $effectiveTargetArchitecture != x86_gcc2 ]; then<br>ARCHITECTURES=&amp;quot;$ARCHITECTURES x86_gcc2&amp;quot;<br>fi<br>SECONDARY_ARCHITECTURES=&amp;quot;x86&amp;quot; # Arquitecturas secundarias<br>PROVIDES=&amp;quot;<br>miaplicacion$secondaryArchSuffix = $portVersion # Todos los paquetes se proveen a sí mismos<br>app:miaplicacion$secondaryArchSuffix = $portVersion # además es una aplicación de Haiku accesible desde los menús<br>&amp;quot;<br><br>REQUIRES=&amp;quot;<br>haiku$secondaryArchSuffix # Bastante claro. Aquí vendrían librerías en tiempo de ejecución<br>&amp;quot;<br><br>BUILD_REQUIRES=&amp;quot;<br>haiku_devel$secondaryArchSuffix # Actualmente todos los Haiku tienen haiku_devel pero por si las moscas. Aquí vendrían librerías de desarrollo<br>&amp;quot;<br><br>BUILD_PREREQUIRES=&amp;quot;<br>cmd:gcc$secondaryArchSuffix # Aquí vendrían las herramientas de línea de comandos. El prefijo cmd: indica que se llama desde la línea de comandos<br>cmd:ld$secondaryArchSuffix<br>cmd:make # Hay herramientas que da igual en que arquitectura estén para funcionar correctamente<br>&amp;quot;<br><br>SOURCE_DIR=&amp;quot;LA_CARPETA_CON_EL_CODIGO_FUENTE&amp;quot;<br><br>PATCH()<br>{<br># Aquí se ponen los parches que se puedan aplicar son sed<br>}<br>BUILD()<br>{<br># Las instrucciones de configuración y compilación. Si usamos autotools<br>runConfigure ./configure<br>make<br># Si usamos CMake<br>cmake .<br>make<br><br>}<br>INSTALL()<br>{<br># Los comandos para instalar la aplicación. Hay que tener cuidado con los directorios especiales de Haiku, que no son POSIX<br># Si usamos autotools y CMake<br>make install<br><br># También podemos copiar manualmente<br>mkdir -p $includeDir<br>cp include/libreria.h $includeDir/<br># Hay unas cuantas variables de carpetas que podemos usar<br><br>addAppDeskbarSymlink $appsDir/MiApplicacion<br># Es muy posible que el make install no instale los enlaces para mostrarse en el lanzador de aplicaciones de Haiku<br>}<br><br></pre><br><br><br>Y este sería el caso más básico de receta que podemos hacer. Si empaquetamos librerías la cosa se complica un poco más ya que tenemos que distinguir la parte de ejecución de la parte de desarrollo y entonces tendremos secciones como PROVIDES_devel que es especifico al paquete de desarrollo. Hay otra manera de aplicar parches que es con patchsets. Nosotros editamos la aplicación hasta que funcione y Haikuporter nos generará un archivo con los cambios que hay que aplicar a las fuentes. Es el mejor método para software un poco más complejo.<br><br><img class="alignnone size-large wp-image-110" src="https://files.adrianistan.eu/SuperFreeCell-Recipe-1024x768.png" alt="SuperFreeCell-Recipe" width="840" height="630" /><br><h2>Publicar cambios</h2><br>Una vez hayamos comprobado que funciona, lo subimos a nuestra copia de Git.<br><br><br><pre class="lang:default decode:true"><br><br>git add haiku-games/superfreecell<br>git commit -m &amp;quot;SuperFreeCell 0.1.0&amp;quot;<br>git push superfreecell master<br><br></pre><br><br><br>Y desde BitBucket hacemos una pull request o solicitud de integración]]></description>
                <comments>https://blog.adrianistan.eu/crea-tu-primer-paquete-para-haiku</comments>
                <pubDate>Fri, 03 Apr 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Instala programas en Haiku con HaikuPorts y HaikuPorter</title>
                <link>https://blog.adrianistan.eu/instala-programas-en-haiku-con-haikuports-y-haikuporter</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/instala-programas-en-haiku-con-haikuports-y-haikuporter</guid>
                <description><![CDATA[Haiku introdujo recientemente su nuevo sistema de paquetería. Este dispone de varios métodos para obtener los programas. El método más sencillo es HaikuDepot, la aplicación gráfica con paquetes ya compilados listos para descargar e instalar con un click. Sin embargo hay mucho más software disponible en el árbol de recetas de Haiku, conocido como HaikuPorts, que usa un programa propio para su gestión llamado HaikuPorter. HaikuPorter no gestiona la instalación, sino la creación de los paquetes desde la fuentes originales.<br><br><img class="alignnone size-large wp-image-46" src="https://files.adrianistan.eu/haiku-depot-1024x768.png" alt="haiku-depot" width="840" height="630" /><br><h2>Haikuports y Haikuporter</h2><br>Haikuports es una colección se software en forma de recetas que dicen como se deben de compilar los programas pero no los almacena. Un sistema similar al de Gentoo y FreeBSD. Haikuports usa Haikuporter para construir los paquetes así que debemos instalar antes de nada Haikuports y Haikuporter.<br><h3>Instalando Haikuporter</h3><br>Instalar Haikuporter requiere que abras la terminal y obtengamos su código fuente<br><br><br><pre class="lang:default decode:true"><br><br>git clone https://bitbucket.org/haikuports/haikuporter<br><br></pre><br><br><br>Ahora debemos configurarlo con nuestros datos<br><br><br><pre class="lang:default decode:true"><br><br>cd haikuporter<br>cp haikuports-sample.conf /boot/home/config/settings/haikuports.conf<br>ln -s /boot/home/haikuporter/haikuporter /boot/home/config/non-packaged/bin/<br>lpe /boot/home/config/settings/haikuports.conf<br><br></pre><br><br><br>Tendremos que editar un archivo. Os pongo como tengo el mío<br><br><br><pre class="lang:default decode:true"><br><br>TREE_PATH=&quot;/boot/home/haikuports&quot;<br>PACKAGER=&quot;Adrián Arroyo Calle &lt;micorreo@gmail.com&gt;&quot;<br>ALLOW_UNTESTED=&quot;yes&quot;<br>ALLOW_UNSAFE_SOURCES=&quot;yes&quot;<br>TARGET_ARCHITECTURE=&quot;x86_gcc2&quot;<br>SECONDARY_TARGET_ARCHITECTURES=&quot;x86&quot;<br><br></pre><br><br><br>Aunque en vuestro caso podeis poner "no" en ALLOW_UNTESTED y ALLOW_UNSAFE_SOURCES.<br><h3>Instalando Haikuports</h3><br>Volvemos a nuestra carpeta y obtenemos el código<br><br><br><pre class="lang:default decode:true"><br><br>cd ~<br>git clone https://bitbucket.org/haikuports/haikuports.git --depth=10<br><br></pre><br><br><h2>Usando Haikuporter y Haikuports</h2><br>Ya estamos listo para construir cualquier paquete con Haikuporter, no solo los nuestros. Con esto podemos acceder a gran cantidad de software. El uso básico de haikuporter es<br><br><br><pre class="lang:default decode:true"><br><br>haikuporter NOMBRE_DEL_PAQUETE<br><br></pre><br><br><br>Aunque si las dependencias nos abruman podemos saltarnoslo<br><br><br><pre class="lang:default decode:true"><br><br>haikuporter NOMBRE_DEL_PAQUETE --no-dependencies<br><br></pre><br><br><br>Los paquetes no se instalan automáticamente, se guardan en /boot/home/haikuports/packages y para instalarlos los debemos copiar a /boot/home/config/packages. También podemos compilar con GCC4 en vez de GCC2 si el programa lo soporta. Hay que añadir _x86 al final. Comprobemos que todo funciona con un paquete al azar<br><br><br><pre class="lang:default decode:true"><br><br>haikuporter cmake_haiku_x86<br><br></pre><br><br><br>Tardará en actualizar la base de datos y pueden que nos salten errores de arquitecturas pero no hay que preocuparse. En mi caso, Haikuporter quería instalar paquetes del sistema ya que había versiones nuevas en haikuports. Sin embargo, como se que iba a tardar mucho cancelé y ejecuté<br><br><br><pre class="lang:default decode:true"><br><br>haikuporter cmake_haiku_x86 --no-dependencies<br><br></pre><br><br><br>Convendría ahora instalar todo lo compilado<br><br><br><pre class="lang:default decode:true"><br><br>cp /boot/home/haikuports/packages/*.hpkg /boot/home/config/packages/<br><br></pre><br>]]></description>
                <comments>https://blog.adrianistan.eu/instala-programas-en-haiku-con-haikuports-y-haikuporter</comments>
                <pubDate>Thu, 02 Apr 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Secta Sectarium, documentación e investigación</title>
                <link>https://blog.adrianistan.eu/secta-sectarium-documentacion-e-investigacion</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/secta-sectarium-documentacion-e-investigacion</guid>
                <description><![CDATA[El diseño de Secta Sectarium require de mucha documentación y trabajo de investigación previo para conocar los fundamentos básicos, no caer en errores contradictorios y ofrecer una evaluación más crítica del juego.<br><h2>La idea</h2><br>Siempre me he sentido atraído por las sectas y todos sus extraños ritos fundamentos en nada. Y quizá por ello me gustan más las sectas que no copian descaradamente ideas de religiones ya establecidas dándoles otra interpretación.<br><br><img class="alignnone size-large wp-image-104" src="https://files.adrianistan.eu/SectaTriptico-767x1024.jpg" alt="SectaTriptico" width="767" height="1024" /><br><br>Últimamente he tenido más información acerca de las sectas y cuanta más información obtenía era necesario hacer algo que expresase todo este misterioso mundo.<br><br><img class="alignnone size-large wp-image-102" src="https://files.adrianistan.eu/ScientologyDVD-767x1024.jpg" alt="ScientologyDVD" width="767" height="1024" /><br><br><img class="alignnone size-large wp-image-103" src="https://files.adrianistan.eu/ScientologyDVDInterior-1024x767.jpg" alt="ScientologyDVDInterior" width="840" height="629" /><br><br>Por ejemplo este es un DVD que he recibido de Scientology (la Iglesia de la Cienciología) conocida por tener entre sus miembros a personalidades de Hollywood. Esta secta fue fundada por Lafayette Ronnald Hubbard, un escritor de ciencia ficción estadounidense que escribió Dianética y dio comienzo a la revolución. L. Ron Hubbard vivió una vida intensa relacionada con los viajes y el ejército. Finalmente se suicidó y dejó una fortuna de 600 millones de dólares.<br><br><img class="alignnone size-full wp-image-67" src="https://files.adrianistan.eu/L-Ron-Hubbard.jpg" alt="L-Ron-Hubbard" width="532" height="380" /><br><h2>¿Qué es una secta?</h2><br>El diccionario de la RAE define secta como:<br><ol><br> 	<li>Conjunto de seguidores de una parcialidad religiosa o ideológica.</li><br> 	<li>Doctrina religiosa o ideológica que se diferencia e independiza de otra.</li><br> 	<li>Conjunto de creyentes en una doctrina particular o de fieles de una religión que el hablante considera falsa.</li><br></ol><br>Estas definiciones no me gustan mucho puesto que son demasiado abiertas a cualquier cosa y no introduce el significado que para muchos significa. Para mí, la diferencia entre religión y secta tiene que ver claramente con el control que ejerce sobre el individuo. Así pues a la pregunta <em><strong>¿Es la Iglesia Católica secta? </strong></em>debo de decir un rotundo <strong>no</strong>. La Iglesia Católica no ejerce prácticamente ningún control sobre sus miembros. Es cierto que hay sacramentos como el bautismo y el matrimonio pero no son comparables con los que una secta en condiciones debería hacer, controlar las cuentas bancarias de sus miembros por ejemplo.<br><div style="width: 100%; padding-top: 64%; position: relative; border-bottom: 1px solid #aaa; display: inline-block; background: rgba(255,255,255,0.9);"><br><br><iframe style="width: 100%; height: 90%; position: absolute; left: 0; top: 0; overflow: hidden;" src="https://www.rtve.es/drmn/embed/video/2141407" name="Los otros creyentes" width="300" height="150" frameborder="0" scrolling="no"></iframe><br><div style="position: absolute; bottom: 0; left: 0; font-family: arial,helvetica,sans-serif; font-size: 12px; line-height: 1.833; display: inline-block; padding: 5px 0 5px 10px;"><span style="float: left; margin-right: 10px;"><img style="height: 20px; width: auto; background: transparent; padding: 0; margin: 0;" src="http://img.irtve.es/css/rtve.commons/rtve.header.footer/i/logoRTVEes.png" /></span> <a style="color: #333; font-weight: bold;" title="Los otros creyentes" href="http://www.rtve.es/alacarta/videos/comando-actualidad/comando-actualidad-otros-creyentes/2141407/"><strong>Los otros creyentes</strong></a></div><br></div><br>Las religiones son el mayor producto cultural de la humanidad, el más perfecto y muchas veces el más infravalorado. Una religión es una filosofía de vida.<br><h2>Funcionamiento de una secta</h2><br>Las sectas pueden tener muchos tipos de funcionamiento. Principalmente son fundadas por un líder carismático que ha tenido alguna <em>revelación</em>. Para conseguir atraer fieles se usan diversas técnicas. En el tríptico que poseo se dan 24 técnicas de manipulación que no voy a escribir pero las podeis leer aquí<br><br><img class="alignnone size-large wp-image-124" src="https://files.adrianistan.eu/TripticoInterior-1024x767.jpg" alt="TripticoInterior" width="840" height="629" /><br><br>Una buena manera de iniciar a alguien en una secta sería en una conferencia sobre un tema intrigante y misterioso (he visto carteles de sectas que hablaban de electrones y sus enseñanzas metafísicas), un DVD o un bonito libro de reflexión.<br><br><img class="alignnone size-large wp-image-64" src="https://files.adrianistan.eu/LibroSecta-1024x767.jpg" alt="LibroSecta" width="840" height="629" /><br><h2>Arquitectura</h2><br>Ya centrándonos más en el juego de Secta Sectarium surge la duda arquitectónica. No todas las sectas tienen campos de trabajo, aunque en el juego tendrán que tener para dar más interés al asunto. Un estilo arquitectónico que se adapta muy bien a estas cosas es el panóptico. Se trata de que todas las personas estén en todo momento vigiladas en su interior. Estos diseños han sido llevados con éxito a cárceles, escuelas y manicomios desde el siglo XIX. El libro Vigilar y Castigar aportará más información al respecto. La Isla de Hashima en Japón, aporta un diseño interesante para sectas marinas. Llamaremos a este concepto <em>Isla Malvada</em><br><br><img class="alignnone size-full wp-image-51" src="https://files.adrianistan.eu/Hashima.jpg" alt="Hashima" width="800" height="531" /><br><h2>Más inspiraciones</h2><br>Los Simpson, una de mis series favoritas tiene un episodio divertidísimo sobre los mvimientarios; una secta que se afinca en Springfield. El episodio se llama  <em>The Joy of the Sect</em> o <em>La alegría de la secta</em> en español.<br><br><img class="alignnone size-full wp-image-111" src="https://files.adrianistan.eu/TheJoyOfTheSect.png" alt="TheJoyOfTheSect" width="499" height="382" /><br><br>La secta del banco Triodos Bank. Sí, ese banco ético fantástico se dedica a financiar su propia secta llamada la antroposofía, la cual también está basada en las <em>geniales</em> escuelas Waldorf que sacan tan <em>buena</em> calificación en PISA. Este tema es complejo y hay muy buena documentación en español en el blog de El Retorno de los Charlatanes.<br><ul><br> 	<li><a href="http://charlatanes.blogspot.com.es/2012/08/antroposofia-la-secta-y-su-banco.html">Antroposofía: la secta y su banco (Triodos)</a></li><br> 	<li><a href="http://charlatanes.blogspot.com.es/2013/10/la-censura-de-banca-triodos.html">La censura de Banca Triodos</a></li><br></ul><br>También podríamos hablar como inspiraciones las inofensivas agrupaciones como<br><ul><br> 	<li><a href="http://mormon.org">Mormones</a></li><br> 	<li><a href="http://jw.org">Testigos de Jehová</a></li><br> 	<li><a href="http://mensa.org">Mensa</a></li><br> 	<li><a href="http://www.opusdei.es">Opus Dei</a></li><br></ul><br>Otras fuentes que merece la pena ser mencionadas<br><ul><br> 	<li>Manuscritos de Qumrán</li><br> 	<li><a href="http://www.yorokobu.es/secta/">Guía práctica para montar una secta</a></li><br> 	<li>El magnífico musical Jesucristo Superstar</li><br></ul><br><h2>El Soneto de la Secta</h2><br>Admiraba yo a mi líder genial<br>una persona sencilla, compleja<br>que su nombre se te queda en la ceja<br>no puede haber nadie más fenomenal<br><br>Iremos a un planeta divertido<br>donde animales música cantan<br>y nuestros miedos humanos se espantan<br>y nadie vuelve a hablar del temido<br><br>Nuestro canto es de alabanza suprema<br>somos guiados por la fe, la razón<br>hablando tranquilamente del tema<br><br>Progresamos rápido cual neutrón<br>¿Quieres venir? Coge la barca, rema<br>Aquí te esperamos con ilusión<br><br>-- Adrián Arroyo Calle<br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/secta-sectarium-documentacion-e-investigacion</comments>
                <pubDate>Sun, 08 Mar 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Secta Sectarium, JavaScript vs Rust</title>
                <link>https://blog.adrianistan.eu/secta-sectarium-javascript-vs-rust</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/secta-sectarium-javascript-vs-rust</guid>
                <description><![CDATA[Acabo de decidirme por el próximo proyecto grande que voy a realizar, se trata de Secta Sectarium, un juego estilo tycoon sobre controlar una secta. Voy a intentar reflejar en este blog todo lo que pueda sobre el desarrollo.<br><h2>Lenguaje de programación</h2><br>Todavía no os voy a contar las mecánicas principales del juego así que vamos a ir directamente al tema técnico. Tenía serias dudas de cual usar. Me rondaban la cabeza C++, <a href="http://rust-lang.org">Rust</a> y JavaScript.<br><h4>C++</h4><br><ul><br> 	<li>Pros<br><ul><br> 	<li>Alto rendimiento</li><br> 	<li>Soporte multiplataforma (incluso para Haiku)</li><br></ul><br></li><br> 	<li>Contras<br><ul><br> 	<li>Tareas sencillas tienen que ser implementadas</li><br> 	<li>Largos tiempos de compilación si usas librerías</li><br> 	<li>Tampoco podía decidir si SDL o SFML</li><br></ul><br></li><br></ul><br><h4>Rust</h4><br><ul><br> 	<li>Pros<br><ul><br> 	<li>Alto rendimiento</li><br> 	<li>Librerías fáciles de usar</li><br> 	<li>Un lenguaje nuevo, con el que conviene acostumbrarse</li><br></ul><br></li><br> 	<li>Contras<br><ul><br> 	<li>Mal soporte en Windows</li><br> 	<li>Poco soporte</li><br> 	<li>Todavía no ha salido la versión 1.0</li><br></ul><br></li><br></ul><br><h4>JavaScript</h4><br><ul><br> 	<li>Pros<br><ul><br> 	<li>Soporte multiplataforma</li><br> 	<li>Gran cantidad de librerías para todo</li><br> 	<li>Lo conozco</li><br></ul><br></li><br> 	<li>Contras<br><ul><br> 	<li>Bajo rendimiento</li><br> 	<li>Es fácil cometer errores simples</li><br></ul><br></li><br></ul><br>Al final me decanté por JavaScript (espero usar TypeScript de verdad) porque aunque el rendimiento puede ser peor, es un amigo (y enemigo a la vez, la historia es larga) conocido. Tiene un soporte para publicar juegos real (en contraposición a Rust) y es bastante rápido desarrollar.<br><br>Así que la elección de JavaScript es definitiva y de repente me surgen dos dudas:<br><ul><br> 	<li>¿Es mejor dibujar un mundo 2.5D en WebGL o en Canvas?</li><br> 	<li>¿Grunt o Gulp?</li><br></ul><br><h2>WebGL o Canvas</h2><br>Inicialmente pensaba hacerlo en Canvas, una API similar a la que esperaba usar en <a href="http://sfml-dev.org">SFML</a> o <a href="http://libsdl.org">SDL</a> con C++ o Rust. Fue cuando estaba viendo el issue tracker de un motor de 2.5D en JavaScript cuando comentaban que <em>sería mejor usar WebGL por rendimiento</em>. Y me surgió la duda. Sé que Canvas 2D es acelerado por hardware en Firefox pero ¿es algo en lo que se puede confiable? y aún siendo confiable ¿es peor el rendimiento de Canvas que WebGL?<br><br>Estuve investigando y resulta que WebGL es más rápido que Canvas en operaciones 2D <em>si lo usas bien</em>. Interesante concepto. En principio Secta Sectarium no va a ser muy exigente gráficamente así que acepto el desafío ya que soy algo familiar con OpenGL ES 2.0 y WebGL.<br><h2>Grunt o Gulp</h2><br>Luego me surgió una duda relacionada con la construcción del proyecto. Había usado <a href="http://gruntjs.com">Grunt</a> anteriormente pero el <a href="https://blog.adrianistan.eu/2014/10/26/esqueleto-de-juegos-npm/">esqueleto de juegos npm</a> me quitaba las ganas de seguir usando Grunt. Era poco claro con tantas configuraciones. Sabía que existía Gulp, así que heché un vistazo y me han quedado buenas impresiones respecto a claridad. Así que uso Gulp.<br><h2>Seguiré informando</h2><br>Seguiré informando sobre Secta Sectarium y su desarrollo. Próximamente os enseñaré el proceso de documentación.]]></description>
                <comments>https://blog.adrianistan.eu/secta-sectarium-javascript-vs-rust</comments>
                <pubDate>Fri, 06 Mar 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Introducción a D-Bus</title>
                <link>https://blog.adrianistan.eu/introduccion-a-d-bus</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/introduccion-a-d-bus</guid>
                <description><![CDATA[<blockquote>Esta entrada la he realizado originalmente para el blog <a href="http://blog.desdelinux.net">DesdeLinux</a></blockquote><br><img class="alignnone size-full wp-image-24" src="https://files.adrianistan.eu/dbus.png" alt="dbus" width="744" height="352" /><br><br>Si llevas algún tiempo en Linux quizás te hayas llegado a preguntar que es <a href="http://dbus.freedesktop.org">D-Bus</a>. D-Bus es un componente incorporado no hace mucho a las distribuciones de escritorio en Linux que previsiblemente jugará un papel muy importante para la programación en Linux.<br><h2>¿Qué es D-Bus?</h2><br>D-Bus es un sistema de comunicación entre aplicaciones de muy diverso origen. Con este sistema podremos llamar incluso a aplicaciones privativas (si estas implementan D-Bus). No juega el mismo papel que una librería pues una librería no es un programa independiente y la librería forma parte de tu ejecutable. La idea de D-Bus está inspirada en los objectos OLE, <a href="http://es.wikipedia.org/wiki/Component_Object_Model">COM</a> y ActiveX de <a href="http://windows.com">Windows</a>. Los objetos COM de Windows ofrecen una manera sencilla de llamar a cualquier programa desde otro programa llegando incluso a poder incrustarse visualmente uno dentro de otro sin necesidad de usar el mismo lenguaje de programación. D-Bus no llega tan lejos pero ofrece esa comunicación de la que UNIX carecía.<br><h2>¿Por qué es importante D-Bus?</h2><br>D-Bus es importante dada la gran diversidad de lenguajes que pueden funcionar en Linux y la gran diversidad también de librerías. Pongamos un ejemplo práctico. Yo quiero mandar una notificación al sistema notify-osd de Ubuntu desde mi aplicación en Node.js. Primero tendría que ver que librería ofrece esa funcionalidad en el sistema, libnotify en este caso, y después debería hacer unos bindings para poder llamar la librería programada en C desde JavaScript. Además imaginemos que queremos hacer funcionar nuestra aplicación con un escritorio que no usa libnotify para las notificaciones.<br><h2>Usando D-Bus</h2><br>Entonces hemos decidido que vamos a usar D-Bus para crear la notificación de nuestra aplicación en JavaScript.<br><pre class="lang:js decode:true ">var dbus=require("dbus-native");<br><br>var bus=dbus.sessionBus();<br>bus.getService("org.freedesktop.Notifications").getInterface("/org/freedesktop/Notifications","org.freedesktop.Notifications",function(err,notify){<br>  <br>  notify.Notify("Probando D-Bus",0,"/usr/share/pixmaps/firefox.png","Título","Cuerpo de la notificación",[],[],5,function(err,id){<br>    console.log("Notificación enviada");<br>  });<br>});</pre><br>&nbsp;<br><br>Hay 2 tipos de buses en D-Bus, un D-Bus único al sistema y un D-Bus para cada sesión de usuario. Luego en D-Bus tenemos servicios que son "los nombres de los proveedores D-Bus", algo así como las aplicaciones D-Bus. Después en una estructura como de carpeta están los objetos que puede tener ese servicio o instancias y finalmente la interfaz es la manera de interactuar con los objetos de ese servicio. En este caso es muy redundante pues el servidor de notificaciones es muy simple.<br><h2>¿Quién usa D-Bus?</h2><br><ul><br> 	<li>com.Skype.API</li><br> 	<li>com.canonical.Unity</li><br> 	<li>org.freedesktop.PolicyKit1</li><br> 	<li>org.gnome.Nautilus</li><br> 	<li>org.debian.apt</li><br> 	<li>com.ubuntu.Upstart</li><br></ul><br>Para averiguar todos los servicios de D-Bus que tienes instalados puedes usar <a href="http://wiki.gnome.org/action/show/Apps/DFeet">D-Feet</a>]]></description>
                <comments>https://blog.adrianistan.eu/introduccion-a-d-bus</comments>
                <pubDate>Sat, 28 Feb 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Usando Node con XULRunner y Gecko</title>
                <link>https://blog.adrianistan.eu/usando-node-con-xulrunner-y-gecko</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/usando-node-con-xulrunner-y-gecko</guid>
                <description><![CDATA[Las aplicaciones basadas en <a href="https://es.wikipedia.org/wiki/HTML5">HTML5</a> para el escritorio han ido en aumento. Soluciones como node-webkit, recientemente renombrado a <a href="http://nwjs.io/">NW.js</a>, han crecido en popularidad. Juegos como <a href="http://www.greenheartgames.com/app/game-dev-tycoon/">Game Dev Tycoon</a> o aplicaciones como <a href="http://atraci.github.io/Atraci-website/">Atraci</a> o <a href="https://popcorntime.io/">Popcorn Time</a> son solo unos ejemplos de apliaciones que usan node-webkit. Sin embargo las personas que preferimos tecnología de Mozilla nos encontrabas con que no había nada que sirviese.<br><h2><a href="http://github.com/AdrianArroyoCalle/node-xulrunner">node-xulrunner</a></h2><br>Así que tenía que hacer algo. De momento es más que nada una prueba de concepto sin nada reseñable y con miles de bugs potenciales. Lo he diseñado para ser llamado desde una API de CommonJS. Esto permitirá tener un cliente de línea de comandos de manera sencilla, pero de momento no lo voy a hacer. Os enseñaré a usarlo con otro método<br><h2>Nuestra aplicación HTML5, Node.js, XULRunner</h2><br>El primer paso será clonar el proyecto node-xulrunner de GitHub.<br><br><br><pre class="lang:default decode:true"><br><br>git clone http://github.com/AdrianArroyoCalle/node-xulrunner html5-app<br>cd html5-app<br>npm install<br><br></pre><br><br><br>La estructura del proyecto ya es una aplicación empaquetable, de manera que solo tendremos que modificar ciertos ficheros. Necesitaremos editar el archivo <em>test.js</em> para indicar ciertas preferencias sobre la aplicación. Algunos parámetros interesantes son <em>os</em> que puede ser: win32, mac, linux-i686 o linux-x86_64 y <em>version</em> que debe coincidir con una versión de XUL Runner disponible y publicada. El resto de opciones son autoexplicativas cuando las veais. En mi caso <em>test.js</em> queda así:<br><br><br><pre class="lang:js decode:true"><br><br>var xul=require(&quot;./index.js&quot;);<br><br>var options={<br>os: &quot;linux-i686&quot;,<br>version: &quot;35.0.1&quot;,<br>name: &quot;HTML5 App&quot;,<br>vendor: &quot;Adrián Arroyo&quot;,<br>appVersion: &quot;0.1.0&quot;,<br>buildId: &quot;00000001&quot;,<br>id: &quot;test@node-xulrunner&quot;,<br>copyright: &quot;2015 Adrián Arroyo Calle&quot;,<br>width: 800,<br>height: 800<br>};<br><br>xul.packageApp(options);<br><br></pre><br><br><br>Ahora debemos crear nuestra aplicación propiamente dicha. Se encuentra bajo el directorio app/ y concretamente el fichero <em>main.js</em> será el ejecutado nada más arrancar la aplicación. <em>main.js</em> puede usar todas las APIs de Node. De hecho si usamos npm en esa carpeta funcionaría correctamente. En mi caso:<br><br><br><pre class="lang:js decode:true"><br><br>var http = require('http');<br>http.createServer(function (req, res) {<br>res.writeHead(200, {'Content-Type': 'text/plain'});<br>res.end('Hello World\n');<br>}).listen(4200, '127.0.0.1');<br>console.log('Server running at http://127.0.0.1:4200/');<br><br></pre><br><br><br>Y ahora los pasos mágicos. Vamos a la terminal y ejecutamos test.js con Node.<br><br><br><pre class="lang:default decode:true"><br><br>node test.js<br><br></pre><br><br><br>Nos saldrá una advertencia y empezará a descargar XULRunner y Node.js. Este ejecución no creará ninguna aplicación. Para, ahora sí, crear la aplicación debemos llamar otra vez a test.js<br><br><br><pre class="lang:default decode:true"><br><br>node test.js<br><br></pre><br><br><br>Y en la carpeta build/ tendremos la aplicación lista para ser probada. Simplemente tendremos que ejecutar <em>app</em><br><br><img class="alignnone size-large wp-image-52" src="https://files.adrianistan.eu/HTML5App-XULRunner-1024x578.png" alt="HTML5App-XULRunner" width="840" height="474" />]]></description>
                <comments>https://blog.adrianistan.eu/usando-node-con-xulrunner-y-gecko</comments>
                <pubDate>Tue, 24 Feb 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Convertir a Haiku en rolling release</title>
                <link>https://blog.adrianistan.eu/convertir-a-haiku-en-rolling-release</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/convertir-a-haiku-en-rolling-release</guid>
                <description><![CDATA[Las distros como <a href="http://archlinux.org">Arch Linux</a> han hecho famoso el concepto de <em>rolling release</em>. ¿No sabes lo qué es? Consiste en el software que está en constante actualización y elimina el concepto de versión, al menos como se pensaba de él. Por ejemplo en <a href="http://ubuntu.com">Ubuntu</a> que no es rolling release el sistema se actualiza pero llegará un momento que para dar un cambio mayor deberemos cambiar a otra versión (de 12.04 a 14.04 por ejemplo). Arch Linux es rolling release y no tiene versiones, siempre que actualizas estás en la última versión.<br><h2>Rolling release en Haiku ¿era algo esperado?</h2><br>Seguramente la gente que conoce Haiku no piense que sea un sistema que aspirase a ser rolling release. Yo tampoco y de hecho no es rolling release oficialmente, sigue teniendo versiones y nightlies pero ahora y gracias al nuevo sistema de paquetería ha sido posible convertir Haiku en rolling release.<br><h2>Los pasos</h2><br>Los pasos a seguir son sencillos pero deben hacerse por línea de comandos. Así que abre la aplicación Terminal y escribe:<br><br><br><pre class="lang:default decode:true"><br><br>pkgman list-repos<br><br></pre><br><br><br>Esto mostrará los repositorios que haya en el sistema. Tendrás que tener 1 por lo menos llamado HaikuPorts. Lo vamos a eliminar, pues está fijado a una versión y entraría en conflicto con los repositorios de rolling release.<br><br><br><pre class="lang:default decode:true"><br><br>pkgman drop-repo HaikuPorts<br><br></pre><br><br><br>Y ahora añadimos el repositorio HaikuPorts de rolling release<br><br><br><pre class="lang:default decode:true"><br><br>pkgman add-repo http://packages.haiku-os.org/haikuports/master/repo/x86_gcc2/current<br><br></pre><br><br><br>Ahora ya tendremos las últimas aplicaciones, sin embargo el sistema más básico (el kernel y el navegador) no se actualizarán. Debemos de añadir otro repositorio<br><br><br><pre class="lang:default decode:true"><br><br>pkgman add-repo http://packages.haiku-os.org/haiku/master/x86_gcc2/current<br><br></pre><br><br><br>Y ya está. Ahora podemos actualizar el sistema entero (requiere reinicio para usar el nuevo kernel).<br><br><img class="alignnone size-full wp-image-126" src="https://files.adrianistan.eu/Updating.png" alt="Updating" width="590" height="453" /><br><br><br><pre class="lang:default decode:true"><br><br>pkgman update<br><br></pre><br><br><h2>Otros repositorios</h2><br>Todavía no ha salido la Beta 1 y ya hay repositorios externos para pkgman. Se añaden de igual manera que los anteriores.<br><ul><br> 	<li>Guest One's bin ports: http://haiku.uwolke.ru/repo/binaries-x86_gcc2</li><br> 	<li>Guest One's Java ports: http://haiku.uwolke.ru/repo/java-ports</li><br> 	<li>Clasqm: http://clasquin-johnson.co.za/michel/repo</li><br> 	<li>BeSly: http://software.besly.de/repo</li><br> 	<li>FatElk: http://coquillemartialarts.com/fatelk/repo</li><br></ul><br><img class="alignnone size-full wp-image-66" src="https://files.adrianistan.eu/ListRepos.png" alt="ListRepos" width="590" height="453" />]]></description>
                <comments>https://blog.adrianistan.eu/convertir-a-haiku-en-rolling-release</comments>
                <pubDate>Sat, 31 Jan 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>La experiencia GCI 2014 con Haiku</title>
                <link>https://blog.adrianistan.eu/la-experiencia-gci-2014-con-haiku</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/la-experiencia-gci-2014-con-haiku</guid>
                <description><![CDATA[El día 1 de diciembre comenzaba el <a href="http://www.google-melange.com/gci/homepage/google/gci2014">Google Code-In 2014</a> y hasta el 19 de enero pude trabajar en la organización que había elegido, en este caso Haiku, como el año anterior.<br><h2>HaikuToDo</h2><br><img class="alignnone size-large wp-image-48" src="https://files.adrianistan.eu/HaikuToDo-1-1024x768.png" alt="HaikuToDo-1" width="840" height="630" /><br><br>La tarea consistía en reusar cierto código de una aplicación de tareas para crear un frontend en Haiku usando la BeAPI. Sin embargo ese código no funcionaba. No tenía sistema de compilación y más tarde descubrí que carecía de punto de entrada y ciertas headers no coincidían con la implementación. Así que lo hice de 0. En primera instancia usé SQlite para almacenar las tareas. Sin embargo me pidieron que mejorara mucho más la interfaz. Eso hice y en el camino tuvo que inventarme el sistema de categorías. La siguiente tarea consistía en reemplazar <a href="http://sqlite.org">SQlite</a> por algo específico de Haiku y BeOS. Hablo de BeFS y su sistema de Queries. Gracias a C++ solo tuve que crear un objeto que implementase las operaciones de escritura con BeFS y pude mantener el de SQlite (hay que activarlo en tiempo de compilación). La siguiente tarea consistía en añadirle el soporte online. Valía cualquiera pero elegí Google Tasks porque ya tenía cuenta. Toda la gestión de HTTP se debía realizar por las APIs nativas de Haiku. Estas son APIs que BeOS no llegó a incorporar nunca. Además el procesado de JSON corría a cargo de las APIs de Haiku que al igual que las de HTTP están indocumentadas. Obtener la información de Google puede parecer lioso pero una vez lo entiendes es bastante sencillo. Para la autenticación, HaikuToDo abre WebPositive para que te registres con Google y recibes un código. Ese código lo pones en la aplicación y se descargarán tus tareas de Google. El soporte actual es de solo lectura. <a href="http://github.com/AdrianArroyoCalle/haiku-todo">La aplicación tiene licencia MIT y se puede descargar</a>.<br><br><img class="alignnone size-large wp-image-49" src="https://files.adrianistan.eu/HaikuToDo-2-1024x768.png" alt="HaikuToDo-2" width="840" height="630" /><br><h2>SuperFreeCell</h2><br><img class="alignnone size-large wp-image-108" src="https://files.adrianistan.eu/SuperFreeCell-1-1024x768.png" alt="SuperFreeCell-1" width="840" height="630" /><br><br>¿Quién no ha jugado al solitario de Windows? ¿Y al Carta Blanca también conocido como <a href="https://es.wikipedia.org/wiki/FreeCell">FreeCell</a>? Seguro que alguno más pero también muchos. La tarea consistía en hacer el clon de FreeCell para Haiku. Por supuesto usando la BeAPI, en concreto la gran flexibilidad de <a href="http://api.haiku-os.org/classBView.html">BView</a> para estas tareas. Para ello tomé como referencia BeSpider, el clon del solitario spider en Haiku. Luego comprendí que no entendía mucho el código y lo empecé a hacer a mi manera. Se me ocurrió hacer las cartas un objeto que se tenía que dibujar. Error. La interación con el resto del mundo se hecha en falta. Luego pensé en que heredasen BView. Error. El número de glitches gráficos era digno de mencionar. Finalmente las cartas pasaron a ser estructuras simplemente e hice toda la lógica en la BView del tablero. El resultado era mucho mejor. Cambié el algoritmo de ordenación de cartas (barajar, si alguien no se enterá) para ganar en velocidad. Antes usé uno con bucles muy simple pero lentísimo. Otros detalles fue que use DragMessage para mover las cartas y que las cartas son PNGs de BeSpider integrados dentro del ejecutable (una cosa que siempre he hechado en falta a Linux). Obviamente al hacer un clon tuve que jugar mucho a otras implementaciones de FreeCell. Jugué a la de Windows 7, <a href="https://wiki.gnome.org/action/show/Apps/Aisleriot?action=show&amp;redirect=Aisleriot">Ubuntu 14.04</a> y <a href="https://play.google.com/store/apps/details?id=com.mobilityware.freecell">Android</a>. Todo sea por SuperFreeCell. <a href="http://github.com/AdrianArroyoCalle/SuperFreeCell">La aplicación tiene licencia MIT y se puede descargar</a><br><br><img class="alignnone size-large wp-image-109" src="https://files.adrianistan.eu/SuperFreeCell-2-1024x768.png" alt="SuperFreeCell-2" width="840" height="630" /><br><h2>Haiku EGL</h2><br><a href="http://en.wikipedia.org/wiki/EGL">EGL</a> es una API del <a href="http://khronos.org">Khronos Group</a> para definir <em>superficies</em> sobre las que operar con otras APIs de Khronos Group, entre ellas OpenGL y OpenVG. EGL se ha usado sobre todo en móviles pero el salto a escritorio es inminente (Wayland y Mir dependen de EGL para funcionar). La API es teóricamente multiplataforma aunque en la práctica hay que modificar el código si usamos las X11 o Wayland o en este caso Haiku. La tarea consiste en portar EGL de manera que funcione en Haiku como wrapper de BGLView. La tarea requería modificar código de Mesa 10. Tuve muchos problemas en esta tarea: documentación nula, características exclusivas de C99 (y que no están en C++), desconocimiento de parte del sistema gráfico tanto en Linux como en Haiku, desconocimiento de SCons, etc. Esto me llevó a implementar EGL en un principio como un driver DRM2. Pero luego descubrí que DRM2 solo funciona en Linux. Así que tuve que escribir un driver desde 0. Este crea una configuración placebo y realiza operaciones básicas como crear una ventana y hacer el intercambio de búferes.<br><h2>Haikuports</h2><br>El resto de tareas consistían en portar software normal y corriente a Haiku. He portado con más o menos dificultad:<br><ul><br> 	<li><a href="http://openscenegraph.org">OpenSceneGraph</a></li><br> 	<li><a href="http://bitbucket.org/SpartanJ/eepp">EntropiaEngine++</a></li><br> 	<li><a href="http://github.com/libuv/libuv">libuv</a></li><br></ul><br>Quería haber portado Node.js pero dependía de libuv que no estaba portado. Después comprobé que nuestra versión del motor V8 estaba desactualizada para Node.js así que no tuve tiempo para terminarla.]]></description>
                <comments>https://blog.adrianistan.eu/la-experiencia-gci-2014-con-haiku</comments>
                <pubDate>Fri, 23 Jan 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Post Mortem de Torre Utopía</title>
                <link>https://blog.adrianistan.eu/post-mortem-de-torre-utopia</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/post-mortem-de-torre-utopia</guid>
                <description><![CDATA[Torre Utopía fue un juego diseñado con el objetivo de ser creado en menos de un mes usando solo mis ratos libres. Para facilitar el trabajo había diseñado un conjunto de librerías y scripts en lo que denomino <em>esqueleto</em>. Este esqueleto es open-source y lo podeis encontrar en <a href="http://github.com/AdrianArroyoCalle/skeleton-npm-game">GitHub</a>.<br><h2>Desarrollo</h2><br><img class="alignnone size-full wp-image-32" src="https://files.adrianistan.eu/elevator-action.png" alt="elevator-action" width="256" height="224" /><br><br>Torre Utopía esta fuertemente inspirado en <a href="http://es.wikipedia.org/wiki/Elevator_Action">Elevator Action</a>. Inicialmente iba a ser un clon del juego pero según avanzó el desarrollo del juego vi partes que no eran fáciles de implementar y que requerirían un rediseño. Estoy hablando sobre todo del scrolling. Torre Utopía inicialmente fue pensado para ser programado en TypeScript que es como el esqueleto lo admite por defecto. Sin embargo terminé usando muy poco las características de TypeScript. Creo que porque no tengo soltura suficiente todavía en TypeScript como para usarlo correctamente. Aun así la inclusión fue positiva en cierto punto porque el compilador era capaz de avisarme de variables no definidas, un error que en JavaScript corriente es tedioso de solucionar pues debes mirar la consola del navegador.<br><br>Un acierto, creo yo, fue usar gráficos pre-generados para ir probando las mecánicas. Esto facilita mucho desde el principio saber como va a quedar cada cosa. Sin embargo, este arte pregenerado no fue sustituido al final enteramente dando una mala imagen al espectador. Realmente se cambiarón muchos gráficos pero la falta de tiempo hizo que tuviesen poco nivel de detalle. Una cosa que no me gustó fue que el personaje se moviera mirándote a ti. Y me cabrea más cuando pienso que hacerlo bien es muy sencillo de implementar.<br><br>La música era repetitiva y sin copyright pero quizá debería haber un botón para acabar con el dolor de oídos que genera escuchar una y otra vez lo mismo durante varias horas. Aunque el tema del audio lo implementé casi al final. Sin embargo no fue difícil alterar el resto del código para que al subir el ascensor sonase (aunque es posible que con algo de retardo).<br><br>La fuente usada quería que fuese estilo pixel para que encajase mejor; fue más difícil de lo que esperaba. Al final encontré una que encajaba con el estilo que quería dar y era gratuita.<br><br><img class="alignnone size-full wp-image-121" src="https://files.adrianistan.eu/TorreUtopia.png" alt="TorreUtopia" width="876" height="660" /><br><h2>Distribución</h2><br>Una vez di por terminado el juego empezó la distribución. Estaba un poco impaciente porque era la primera vez que iba a publicar en tantos sitios a la vez. Sin embargo el tema de crearse cuentas en tantos sitios hizo que me decantase solo por 5 distribuidoras. Estas han sido:<br><ul><br> 	<li><a href="http://www.kongregate.com/games/aarroyoc/torre-utop-a">Kongregate</a></li><br> 	<li><a href="http://fgl.com">FGL</a></li><br> 	<li>MarketJS</li><br> 	<li><a href="http://clay.io/game/torreutopia">Clay.io</a></li><br> 	<li><a href="https://marketplace.firefox.com/app/torre-utopía">Firefox Marketplace</a></li><br></ul><br><h4>Kongregate</h4><br><img class="alignnone size-full wp-image-56" src="https://files.adrianistan.eu/Kongregate.jpg" alt="Kongregate" width="225" height="246" /><br><br>Simple y directo. Me publicaron el juego en poco tiempo y recibí comentarios de usuarios en el juego. Cuando miré por primera vez había generado 4 céntimos de euro. Lo más molesto de Kongregate es el tema fiscal. Ellos insisten en que lo cumplimentes todo aunque no es necesario. Esto es porque se pueden ahorrar impuestos (y tú ganar más) si lo cumplimentas. Para los que no lo conozcan en Kongregate ganas dinero por la publicidad que se ve en Kongregate cuando gente juega a tu juego. Es un portal de juegos pero con mucho tráfico.<br><h4>FGL</h4><br>Originalmente diseñado para juegos Flash se ha adaptado y ofrece ahora también para HTML5 y Unity. FGL tiene muchas opciones de distribución y para un principiante puede ser confuso. Por suerte, FGL tiene ayudas en los márgenes muy útiles. En FGL ganamos dinero si nos compran el juego para publicarlo en portales de juegos. Todavía no he recibido ninguna oferta (tampoco espero, es mi primera vez) y se encargan de hacerte una valoración con nota en diferentes aspectos. En mi corta experiencia con FGL puedo decir que los administradores ayudan mucho (me enviaron correos varias veces para comunicarme fallos y como podría mejorar ciertas cosas)<br><h4>MarketjS</h4><br>MarketJS es como FGL pero centrado exclusivamente en HTML5. Mal. Creí que por estar centrados en HTML5 (y obviamente más nuevos en el mundillo) tendrían algo más de simpatía. No sé si será la plataforma (mucho más verde en todos los aspectos) pero no recibí respuesta al publicar el juego. Semanas más tarde les envié un correo personalmente. Su respuesta fue que cambiase la imagen de la descripción. La cambié. Volví a publicar. Y ya está. Ninguna respuesta.<br><h4>Clay.io</h4><br>Ya les conocía y el proceso es el habitual, algo mejor que de costumbre y todo. En Clay.io ganamos dinero por anuncios como Kongregate, pero también nos pueden comprar el juego como FGL y tiene un tercer modo que es dejar a los portales de juegos el juego tal cual y por los anuncios de Clay.io recibiremos dinero como si fuera en el propio sitio. Puse un anuncio de vídeo al principio y nada más. Y todavía no he ganado nada pero es más sospechoso si pensamos que Clay.io ha añadido mi juego a más portales con el tercer sistema como html5games.club<br><h4>Firefox Marketplace</h4><br><img class="alignnone size-full wp-image-38" src="https://files.adrianistan.eu/firefox-os.png" alt="firefox-os" width="558" height="359" /><br><br>Una tienda al estilo Google Play o App Store pero para Firefox OS. Fue aprobado y está disponible pero en esta versión no he añadido anuncios. Comentar que en Firefox Marketplace obtienes los certificados PEGI, ESRB y parecidos de manera gratuita, aunque solo valen para Firefox OS. Las certificaciones en principio te las dan según rellenas unas preguntas sobre el juego. Además hacen pruebas aleatorias cada institución y por ello pueden cambiarte la calificación. En mi caso ESRB me daba T y PEGI me daba +7. Pero en diferentes análisis (llegaron en diferentes meses) me dijeron que al estar los gráficos muy pixelados la violencia que contiene el juego es mínima, por ello actualmente tengo en ESRB la E y en PEGI +3.<br><h2>Recepción</h2><br>Vamos a ver las críticas del juego a día de hoy:<br><ul><br> 	<li>Firefox Marketplace: <em>Es como wrecking cree pero más chafa 4/5</em></li><br> 	<li>FGL: <em>Definitely not my type of game, but could be fun. You need to think about touch controls (how will one play it on a mobile browser) and also improve graphics, like game over screen for example.</em></li><br> 	<li>FGL: <em>I like the concept for the game! Although the graphics needs some serious improvment but I think that the game could be fun with new graphics and some polish!</em></li><br> 	<li>FGL, la review oficial:<br><ul><br> 	<li>Intuitiveness:    5    Needs Improvement</li><br> 	<li>Fun:    6    Average</li><br> 	<li>Graphics:    5    Needs Improvement</li><br> 	<li>Sound:    6    Average</li><br> 	<li>Quality:    6    Average</li><br> 	<li>Overall:    6    Average</li><br> 	<li>Comments: <em>Instructions should be included in game. No-one will understand what's going on without them.Graphics are poor. They're programmer's art, not professional finished product. They're basic and very static(lack of animations). Need to be changed. Music is little low-quality. Also, there should be mute button on every screen. It's very frustrating for players if they can't mute music, especially if it's annoying. There should be always a way back to main menu(Home button or something like that). Policemans are staying in the same place as player and they can't shoot him if you don't move. There should be some text on main menu saying "Press anything to play".</em></li><br></ul><br></li><br> 	<li>Firefox Marketplace, un comentario oficial: <em>The game doesn't cover large screens.</em></li><br> 	<li>Kongregate: <em>Nice game, i recomend adding a menu, and make AI on the enemies.</em></li><br></ul><br><h2>Conclusión</h2><br>Hay que mejorar el esqueleto npm, el audio, los sprites (aunque seguiré usando gráficos pre-diseñados al principio). También creo que debe haber un menú, un sistema de anuncios integrados fácilmente desactivables via parámetros GET, integración con Google Analytics, ajustes para quitar el sonido, etc. Espero mejorar todos estos (y más aspectos que seguro que me olvido) para futuros juegos.]]></description>
                <comments>https://blog.adrianistan.eu/post-mortem-de-torre-utopia</comments>
                <pubDate>Thu, 22 Jan 2015 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Prosperidad</title>
                <link>https://blog.adrianistan.eu/prosperidad</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/prosperidad</guid>
                <description><![CDATA[Finaliza el 2014. Ha sido un buen año. Con altos y bajos como todos. En esta entrada voy a intentar comentar el año en general para ver que conclusiones podemos sacar, unos valores que se suman a la experiencia acumulada y con los que poco a poco nos vamos construyendo una mejor realidad.<br><h2>Hechos</h2><br>Empecemos por los hechos que hemos acumulado<br><h3>¡Empieza la monetización!</h3><br>En 2013 ya había recibido algún dinero por <a href="http://paypal.com">PayPal</a> pero 2014 ha sido el año de la monetización. El auge de BitCoin también contribuyó a ello y en consecuencia ahora veis bonitos anuncios de AdSense. No ha sido fácil llegar a AdSense por unos problemas que tenía con Google Sites y su API propia de AdSense. También he probado <a href="https://coinurl.com/index.php?ref=aarroyo">CoinURL.com</a>, <a href="http://adpv.com">Adpv.com</a> y <a href="https://a-ads.com?partner=184258">Anonymous Ads</a>. Las donaciones via PayPal también han crecido y tuve la oportunidad de probar un servicio llamado <a href="http://tran.sl">Tran.sl</a> en el que te pagan por traducir textos.<br><h3>¡Vamos a Turquía!</h3><br>En 2014 nuestro equipo de Orientación ganó el Campeonato de España de Orientación de Centros Escolares. Esto quiere decir que en 2015 competiremos en Turquía en el Campeonato del Mundo de Centros Escolares.<br><br><iframe style="border: 0;" src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d6669711.459056255!2d29.493102625579283!3d35.28870751153888!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x14c3b7a4820a22e7%3A0x973532648469d291!2s07980+Kemer%2C+Turqu%C3%ADa!5e0!3m2!1ses!2ses!4v1419599739726" width="600" height="450" frameborder="0"></iframe><br><h3>Competiciones</h3><br>Este año participé en varios concursos/competiciones a parte del Campeonato de España de Orientación. A destacar la Olimpiada Matemática (donde pasé a la fase provincial), el <a href="http://google-melange.com">Google Code-In 2013</a> (y 2014 que esta siendo ahora mismo) y el programa <a href="http://developer.ubuntu.com/pioneers/#com.ubuntu.developer.adrian-arroyocalle">Ubuntu Pioneers</a> (el cual fue una gran sorpresa).<br><h3>Aprendizaje continuo</h3><br>Este 2014 he aprendido nuevas tecnologías que creo que podrán tener alguna utilidad. Compré un Arduino UNO, use <a href="http://openshift.com">OpenShift</a> y <a href="http://heroku.com">Heroku</a> con <a href="http://nodejs.org">Node.js</a>, también eche un vistazo a <a href="http://www.typescriptlang.org/">TypeScript</a> e hice alguna cosita en <a href="http://www.rust-lang.org/">Rust</a>. Mejoré mi destreza en <a href="http://www.blender.org/">Blender</a> y pude hacer un pequeño juego completo en <a href="http://ogre3d.org/">Ogre3D</a> (<a href="https://blog.adrianistan.eu/2014/09/25/lumtumo-ya-es-open-source/">Lumtumo</a>). También fue el año de GameJS para hacer <a href="http://adrianistan.eu/norzarea/player.html">Norzarea</a>.<br><br><img class="alignnone size-full wp-image-89" src="https://files.adrianistan.eu/Norzarea.png" alt="Norzarea" width="801" height="402" /><br><h3>MEME Manía</h3><br>La MEMEManía alcanzó un gran esplendor. Tanto tiempo perdido haciendo memes... Pero alguno hay bastante graciosete que aun hoy me hacen reír.<br><br><img class="alignnone size-full wp-image-28" src="https://files.adrianistan.eu/Dogma42.jpg" alt="Dogma42" width="499" height="499" /><br><h2>Cultura</h2><br><h3>Blogs</h3><br>En 2013 cerró Google Reader y me pasé a Feedly. La verdad es poco a poco acumulaba algún que otro sitio interesante pero este año el crecimiento ha sido exponencial. Voy a recomendaros algunos:<br><ul><br> 	<li><a href="http://elpinguinotolkiano.wordpress.com">El Pingüino Tolkiano</a></li><br> 	<li><a href="http://diegocg.blogspot.com.es/">D'Oh!</a></li><br> 	<li><a href="http://disruptiveludens.wordpress.com/">Disruptive Sketchbook 2.0</a></li><br> 	<li><a href="http://grumpygamer.com">Grumpy Gamer</a></li><br> 	<li><a href="https://hacks.mozilla.org/">Mozilla Hacks</a></li><br> 	<li><a href="https://blog.mozilla.org/addons/">Mozilla Add-ons blog</a></li><br> 	<li><a href="http://0fps.net/">0 FPS</a></li><br> 	<li><a href="http://this-week-in-rust.org/">This Week in Rust</a></li><br> 	<li><a href="http://reactosnews.blogspot.com.es/">El Blog de ReactOS</a></li><br> 	<li><a href="http://www.stevestreeting.com/">SteveStreeting.com</a></li><br> 	<li><a href="http://skatox.com/blog">El weblog de Skatox</a></li><br> 	<li><a href="http://journal.stuffwithstuff.com/">journal.stuffwithstuff.com</a></li><br> 	<li><a href="http://otrasmusicasotrosmundos.blogspot.com.es/">Otras Músicas. Otros mundos.</a></li><br></ul><br><h3>Libros</h3><br>Ahora toca recomendar libros. Creo que podemos empezar por un buen clásico: De Ratones y Hombres de John Steinbeck. Una lectura fácil y aparentemente inocente pero con mucho trasfondo.<br><br>Si avanzamos más podemos mencionar <a href="http://www.amazon.es/gp/product/847223844X/ref=as_li_ss_tl?ie=UTF8&amp;camp=3626&amp;creative=24822&amp;creativeASIN=847223844X&amp;linkCode=as2&amp;tag=adrarrcal-21" rel="nofollow">El Quark y el Jaguar</a><img style="border: none !important; margin: 0px !important;" src="http://ir-es.amazon-adsystem.com/e/ir?t=adrarrcal-21&amp;l=as2&amp;o=30&amp;a=847223844X" alt="" width="1" height="1" border="0" /> de Murray Gell-Man, un gran libro sobre física cuántica, pero que no os asuste el tema, el libro en general es bastante ameno.<br><br><img class="alignnone size-full wp-image-33" src="https://files.adrianistan.eu/ElQuarkYElJaguar.jpg" alt="ElQuarkYElJaguar" width="134" height="200" /><br><br>Si quieres hacer pasar un mal rato a vuestros enemigos recomendadle Paz en la Guerra de Miguel de Unamuno. Lo siento, pero es pesadísimo de leer. Sé que Unamuno está considerado un gran autor de la generación del 98 pero con esta obra no sé que pensar de él.<br><br><img class="alignnone size-full wp-image-72" src="https://files.adrianistan.eu/MiguelDeUnamuno.jpg" alt="MiguelDeUnamuno" width="623" height="720" /><br><br>Un buen libro que seguro os dará algo que pensar es <a href="http://www.ciudadseva.com/textos/cuentos/esp/juanma/lucanor/lucanor.htm">El Conde Lucanor</a> de Juan Manual<br><br>¿Un libro de motivación? Quizá The Game Jam Survival Guide de Christer Kaitila, es cortito pero te pone las pilas en cuanto a empezar a hacer juegos.<br><h3>Videojuegos</h3><br><ul><br> 	<li>Grim Fandango</li><br> 	<li>Trópico 3</li><br> 	<li>Half-Life 2</li><br> 	<li>Portal</li><br></ul><br><img class="alignnone size-full wp-image-44" src="https://files.adrianistan.eu/GrimFandango.jpg" alt="GrimFandango" width="256" height="341" /><br><br>Y como siempre, los clásicos de siempre:<br><ul><br> 	<li>SimCity 4</li><br> 	<li><a href="http://openttd.org">OpenTTD</a></li><br> 	<li>RollerCoaster Tycoon 2</li><br> 	<li><a href="http://simutrans.com">Simutrans</a></li><br></ul><br><h2>Objetivos</h2><br>Vamos a repasar los objetivos principales. Mi agenda se desbordó hace tiempo de cosas por hacer y ahora mismo tengo claro una cosa. Es mejor hacer pocos proyectos, hacerlos bien, que el público pueda disfrutar de ello que muchos proyectos en estado dudoso y sin utilidad práctica real.<br><br>Así pues he intentado reducir los proyectos al mínimo y aunque parezcan vagos aquí, en mi cabeza son algo más tangibles:<br><ul><br> 	<li>Publicar más en el blog (típica mentira que se cuenta al estilo de voy a ir al gimnasio o este es el año de Linux en el escritorio)</li><br> 	<li>Desarrollar más juegos para el 2015. El desafío #OneGameAMonth promete 12 en un año, pero ya os digo yo que con ciertos meses no espereis nada.</li><br> 	<li>Colaborar más con Haiku y FreeRCT.</li><br> 	<li>Ir finalizando proyectos antiguos abiertos. Esto sin duda va a ser lo más difícil de cumplir, lo dejamos como algo "a intentar".</li><br> 	<li>Actualizar los complementos de Firefox y crear alguno nuevo. Ya han venido múltiples personas contactando conmigo con ideas, algunas de ellas interesantes.</li><br> 	<li>Avanzar paso a paso hacia la excelencia. Para esto es importante el Círculo de Deeming y el feedback que recibo del público.</li><br></ul><br><h2>Conclusión</h2><br>Creo que podemos sacar unos valores que debemos almacenar en nuestros cerebros. Unas frases célebres de esas que tanto gustan a la gente nos hablará<br><ul><br> 	<li><em>Si todos piensan igual, entonces nadie está pensando</em><br><ul><br> 	<li>George S. Patton o Winston Churchill, lo cierto es que no he encontrado el autor exacto pero tengo medianamente claro que fue alguien de la primera mitad del siglo XX. Esta frase se ha convertido en mi máxima y creo que no está de más recordarla</li><br></ul><br></li><br> 	<li><em>La vida es como montar en bicicleta. Para mantener el equilibro hay que seguir pedaleando</em><br><ul><br> 	<li>Atribuida a Albert Einstein, es una frase que refleja que no debemos quedarnos parados, siempre tenemos que tener metas, objetivos que alcanzar</li><br></ul><br></li><br> 	<li><em>Nunca discutas con un idiota, te rebajará a su nivel y te ganará por su experiencia</em><br><ul><br> 	<li>Dicha por Mark Twain. Conviene recordarla muy a menudo en estos tiempos.</li><br></ul><br></li><br> 	<li><em>Una mentira repetida adecuadamente mil veces se convierte en una verdad</em><br><ul><br> 	<li>De Joseph Goebbels. Tiene usos positivos y negativos, aunque...</li><br></ul><br></li><br> 	<li><em>No hay buenos o malos, solo hay puntos de vista</em><br><ul><br> 	<li>No sé si lo dijo alguien, pero esto también es importante</li><br></ul><br></li><br> 	<li><em>Piensa globalmente, actúa localmente</em><br><ul><br> 	<li>El mundo no es tu ciudad y tu país. El mundo es mucho más grande, si no pensamos en esa gente nos estamos perdiendo el gran pastel. Y luego decimos que el pastel es una mentira.</li><br></ul><br></li><br> 	<li><em>Publicar es una característica</em></li><br> 	<li><em>Ponte objetivos cortos, nunca parés y alcanzarás grandes metas</em></li><br></ul><br><h2>Finalizando</h2><br>Os deseo a todos vosotros un feliz año 2015 que sea todavía mejor que 2014<br><br>&nbsp;]]></description>
                <comments>https://blog.adrianistan.eu/prosperidad</comments>
                <pubDate>Fri, 26 Dec 2014 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>¿Qué nos espera en Haiku Beta?</title>
                <link>https://blog.adrianistan.eu/que-nos-espera-en-haiku-beta</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/que-nos-espera-en-haiku-beta</guid>
                <description><![CDATA[No sé si conoceis <a href="http://haiku-os.org">Haiku</a>. Se trata de un sistema operativo libre (bajo licencia MIT) que intenta ser un clon libre de BeOS siguiendo su filosofía. Podríamos compararlo a <a href="http://kernel.org">Linux</a> respecto a UNIX o <a href="http://reactos.org">ReactOS</a> respecto a Windows NT. El caso es que no comparte nada con otros sistemas operativos disponibles en el mercado y eso lo hace muy interesante. La historia de Haiku y BeOS es muy interesante y podeis encontrar mucha información al respecto que no voy a replicar. Sin embargo el desarrollo de Haiku siempre ha sido muy lento y ahora mismo estamos en los albores de la Beta 1 de Haiku. Después de 4 alphas bastante estables se van a atrever a lanzar la primera beta. ¿Por qué ahora? Vamos a ver las novedades que traerá Haiku para saber porque se ha decidido este importante paso.<br><br><img class="alignnone size-large wp-image-45" src="https://files.adrianistan.eu/haiku-basic-1024x768.png" alt="haiku-basic" width="840" height="630" /><br><h2>Compilador actualizado</h2><br>Haiku ya dispone de una vía rápida para obtener un compilador con las últimas características que se esperan de él. La versión que está instalada en mi máquina virtual tiene <a href="http://gcc.gnu.org">GCC</a> 4.8.3 (publicado el 22 de mayo de 2014). Sin embargo no será el compilador por defecto. Durante mucho tiempo en Haiku solo se podía usar GCC 2.95 y va a seguir siendo el compilador por defecto. Las razones es que el código generado por este compilador son compatibles con el último BeOS. Actualmente en Haiku se ofrecen 4 descargas para x86: una usando solo GCC 2, otra usando solo GCC 2, una híbrida usando por defecto GCC 2 y otra híbrida usando por defecto GCC 4. Recomiendo la híbrida usando por defecto GCC 2.<br><br><img class="alignnone size-large wp-image-47" src="https://files.adrianistan.eu/haiku-gcc-1024x768.png" alt="haiku-gcc" width="840" height="630" /><br><h2>Nuevas arquitecturas</h2><br>Relacionado con lo anterior Haiku ha podido ser portado a arquitecturas diferentes como PPC, ARM, m68k y x86_64. Decir que la versión ARM es todavía muy prematura aunque se han hecho grandes avances en ello.<br><h2>Navegador web actualizado</h2><br>Haiku antes disponía de <a href="http://getfirefox.com">Firefox</a>. Un día Firefox decidió usar Cairo como librería de renderizado. Esto supone un aumento de velocidad para Firefox en aquel momento pero rompe el port existente con BeOS/Haiku/ZETA. Cairo al igual que <a href="http://gtk.org">GTK</a> nunca han estado soportados de manera oficial en BeOS y con GTK2 el port que había dejó de funcionar. Desde entonces Haiku ha sufrido la carencia de navegadores decentes. Algo más grave si tenemos en cuenta la importancia de HTML5. Así pues ahora mismo en 2014 tenemos los siguientes navegadores gráficos actualizados funcionando sobre Haiku:<br><ul><br> 	<li><a href="http://qupzilla.com">Qupzilla</a></li><br> 	<li><a href="http://netsurf-browser.org">NetSurf</a></li><br> 	<li>WebPositive</li><br></ul><br>Pero también tendríamos versiones antiguas de:<br><ul><br> 	<li>Firefox (BeZilla)</li><br> 	<li>Opera</li><br> 	<li>NetPositive</li><br></ul><br>Qupzilla usa WebKit y Qt, además es bastante inestable. Y su interfaz no es la típica de Haiku. Por eso no es muy usado por la comunidad. NetSurf es bastante estable ya que es una de las plataformas a las que está enfocado, pero NetSurf es un navegador muy pequeño comparado con otros. Usa su propio motor y es muy ligero pero muy poco compatible. De hecho no soporta JavaScript. Nos queda WebPositive, el navegador oficial de Haiku. Usa WebKit pero usando una interfaz nativa de Haiku y con llamadas propias al sistema. Está implementado con la API BWebView para que otras aplicaciones lo puedan usar. Es el navegador más avanzado con un soporte decente para HTML5 y JavaScript. Le sigue faltando WebGL y alguna que otra API pero el soporte de SVG, Canvas 2D, Audio y muchas otras características es bastante decente. Mencionar también que Haiku soporta <a href="http://openjdk.java.net/projects/haiku-port/">OpenJDK</a> 7 como un port que funciona decentemente.<br><br><img class="alignnone size-large wp-image-50" src="https://files.adrianistan.eu/haiku-webpositive-1024x768.png" alt="haiku-webpositive" width="840" height="630" /><br><h2>Sistema de paquetería</h2><br>Quizá la característica más importante para Haiku ha sido el sistema de paquetería. Un gran sistema que se podría calificar como de lo mejores en mucho tiempo. Esto no quiere decir que Haiku no tuviese sistema de paquetería antes, realmente tenía uno muy simple pero que servía, eran los OptionalPackages. Sin embargo el concepto de sistema de paquetería ha sido rediseñado para Haiku y el resultado es bastante bueno. Lo primero que vemos son dos programas dedicados a gestionar la paquetería para los usuarios, el primero es <strong>pkgman</strong>, el de línea de comandos y el segundo es <strong>HaikuDepot</strong>, para realizar todas las operaciones desde la línea de comandos. Esta es una cosa que me gusta de Haiku, cuidan tanto la línea de comandos como la interfaz gráfica y quieren que tengas las mismas características en cualquier entorno. Respecto a los paquetes, usan una extensión .HPKG y para instalarlos los tendremos que copiar en nuestra carpeta de usuario (actualmente es /boot/home/config/packages) y ya está.<br><blockquote>¿Sólo hay que copiar los paquetes? ¿ya está?</blockquote><br>Sí, solo hay que copiar. La magia reside en el <a href="https://dev.haiku-os.org/wiki/PackageManagement/Infrastructure">PackageFS</a>, un sistema de archivos que surge de la lectura del HPKG y montando los ficheros en sus respectivos lugares en modo lectura. Para ello hay un demonio vigilando la carpeta y si recibe cambios los descomprime y los monta en el sistema de ficheros. Esto influye a que ahora solo la carpeta /boot/home pueda ser escrita simple y llanamente porque el resto de carpetas (/boot/system es la única que veo) están montadas en modo solo lectura de los paquetes del sistema. Estos estan localizados en /boot/system/packages. En esta carpeta también podremos escrbir y borrar pero teniendo en cuenta que el HPKG con el kernel también está ahí conviene tener cuidado con la carpeta. Y la cosa se hace más graciosa cuando comprobamos que cada paquete tiene cargada su instancia del sistema operativo y sus librerías (/boot/system/package-links). La verdad es que las instalaciones son muy rápidas y funciona por lo general. <a href="https://blog.adrianistan.eu/2015/04/03/crea-tu-primer-paquete-para-haiku/">Si quieres saber como crear paquetes para Haiku, revisa este post</a>. También crearemos un repositorio y veremos como hacer Haiku un sistema operativo 100% rolling release usando los HPKG.<br><br><img class="alignnone size-large wp-image-46" src="https://files.adrianistan.eu/haiku-depot-1024x768.png" alt="haiku-depot" width="840" height="630" /><br><h2>Conclusión</h2><br>Me habré dejado cosas seguro y además la compatibilidad con aplicaciones de terceros ha aumentado. Haiku se acerca a su primera beta y las cosas se ponen muy pero que muy interesantes.]]></description>
                <comments>https://blog.adrianistan.eu/que-nos-espera-en-haiku-beta</comments>
                <pubDate>Mon, 17 Nov 2014 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Esqueleto de juegos NPM</title>
                <link>https://blog.adrianistan.eu/esqueleto-de-juegos-npm</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/esqueleto-de-juegos-npm</guid>
                <description><![CDATA[Quería haber empezado este mes con el desafío <a href="http://www.onegameamonth.com">One Game a Month</a> pero mis herramientas no estaban listas tal y como recomendaba el libro <a href="http://www.mcfunkypants.com/jam">The Game Jam Survival Guide</a> de <a href="http://www.mcfunkypants.com">Christer Kaitila</a>. Así que este mes lo dediqué a hacer herramientas para las juegos. Concretamente he desarrollado dos sets de herramientas (esqueletos a partir de ahora). Uno es de <a href="https://developer.mozilla.org/es/docs/Glossary/JavaScript">JavaScript</a>/<a href="http://www.typescriptlang.org/">TypeScript</a> y el otro de C++. Quería haber hecho uno de Rust, pero hasta que no salga la 1.0 y lo pueda probar en Windows en condiciones no habrá esqueleto de Rust. Así pues hecha la introducción, en este post voy a hablar del esqueleto de JavaScript/TypeScript que es el que hice primero.<br><h2>Descripción del esqueleto</h2><br>El esqueleto ha sido diseñado alrededor de <a href="http://gruntjs.com">Grunt</a> para su fácil manejo. Me encanta ver como Grunt puede con todo lo que le echen y más (lo único que le falta es un plugin nativo de C++ al estilo de Gradle). Actualmente el esqueleto usa TypeScript para el código. Este maneja las definiciones con <a href="https://www.npmjs.org/package/tsd">TSD</a>, se compila y finalmente se le  pasa por <a href="http://browserify.org/">Browserify</a> para convertirlo en un único fichero. A parte no hay ficheros <a href="http://www.w3.org/">HTML</a>, sino que son plantillas <a href="http://jade-lang.com/">Jade</a>; la razón no es muy clara pero prefería tener Jade antes que HTML básico. Quizá me sentía inspirado y pensé que le daba un toque más <a href="http://nodejs.org">node.js</a>. Y el CSS tampoco se usa directamente, sino que debemos pasar por el compilador de <a href="http://lesscss.org/">LESS</a> (aquí la ventaja es más clara que con Jade/HTML). Adicionalmente podemos generar documentación si estuviese documentado el código con <a href="https://github.com/TypeStrong/typedoc">TypeDoc</a> y generamos una página del manual de UNIX con markedman. Posteriormente en etapas finales del juego podemos generar iconos de todos los tamaños imaginables (Apple iOS iPhone sin retina, con retina, iPad, iPad mini,... incluso para el navegador Coast). Además se minifican las imágenes con <a href="http://optipng.sourceforge.net/">OptiPNG</a> y sus equivalentes para JPG y GIF. Para acabar se añade una tag al repositorio Git y se publica en el repositorio, pero en el branch gh-pages. También he diseñado otra ruta para el empaquetamiento en local de la aplicación. Usando <a href="https://github.com/rogerwang/node-webkit">NodeWebkit</a> y un plugin para hacer un paquete <a href="http://debian.org">Debian</a> (tengo pendiente el RPM) y comprimiendo el resultado en un ZIP listo para el <a href="http://marketplace.firefox.com">Firefox Marketplace</a>. Además, sin estar automatizados hay código para <a href="http://ubuntu.com">Ubuntu Touch</a>, <a href="http://android.com">Android</a> y <a href="https://developers.google.com/chrome/apps/docs/">Chrome OS</a>.<br><h2>¿Cómo se usa?</h2><br>Buena pregunta. Primero debemos clonar el repositorio Git y tenemos que tener en cuenta que a partir de ahora podemos modificar todo con fin de que se adapte a nuestras necesidades. Entonces clonamos con:<br><br><br><pre class="lang:default decode:true"><br>git clone https://github.com/AdrianArroyoCalle/skeleton-npm-game TU_JUEGO<br></pre><br><br><br>Ahora debemos instalar las dependencias. Te recomiendo que vayas a por un café o algo ya que el sistema de dependencias empotradas de NPM hace que el tamaño de la descarga se nos vaya de las manos con ciertas dependencias duplicadas y reduplicadas. Así que entra en la carpeta creada llamada TU_JUEGO y pon:<br><br><br><pre class="lang:default decode:true"><br>npm install<br></pre><br><br><br>Adicionalmente si no tienes Grunt instalado, instálalo con:<br><br><br><pre class="lang:default decode:true"><br>npm -g grunt-cli<br></pre><br><br><br>Es posible que según tu configuración lo debas ejecutar como root o no.<br>Una vez descargado solo nos quedaría empezar el desarrollo. Podemos hacer una pequeña prueba de que todo se haya instalado con:<br><br><br><pre class="lang:default decode:true"><br>grunt test<br></pre><br><br><h2>Librerías incluidas</h2><br>He incluido las siguientes librarías y me gustaría añadir unas cuantas más pronto:<br><ul><br> 	<li>Babylon.js</li><br> 	<li>three.js</li><br> 	<li>GameJS</li><br> 	<li>Box2d.js</li><br> 	<li>Cannon</li><br> 	<li>meSpeak</li><br> 	<li>Bongo.js</li><br> 	<li>Hammer.js</li><br> 	<li>Canvace</li><br> 	<li>i18next</li><br></ul><br>Y sin duda me gustaría añadir alguna más, pero con estas creo que se pueden dar muchas combinaciones.He intentado recopilar la mayor cantidad de sitios donde publicar los juego una vez terminados.<br><h2>Y una cosa más...</h2><br>La lista ya es bastante grande pero no están probados todas las empresas allí expuestas. Además hay que tener ciudado ya que hay tiendas que son incompatibles entre ellas.]]></description>
                <comments>https://blog.adrianistan.eu/esqueleto-de-juegos-npm</comments>
                <pubDate>Sun, 26 Oct 2014 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>SmartOS, una introducción</title>
                <link>https://blog.adrianistan.eu/smartos-una-introduccion</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/smartos-una-introduccion</guid>
                <description><![CDATA[Hoy vamos a probar <a href="http://smartos.org">SmartOS</a>. SmartOS es el sistema operativo de Joyent para aplicaciones cloud. Usa un kernel <a href="http://wiki.illumos.org/display/illumos/illumos+Home">illumos</a> (fork de OpenSolaris cuando Oracle cerró el proyecto, a nivel técnico comparte mucho con Solaris 10) y usa extensivamente características únicas de Solaris. Además, es el único illumos que tiene KVM, una característica de Linux para permitir virtualización de alto rendimiento.<br><h2>Descargando SmartOS</h2><br>Lo primero que tenemos que hacer para probar SmartOS es descargarlo. Lo podemos descargar desde <a href="http://smartos.org">SmartOS.org</a>. Allí encontraremos un enlace a la wiki donde está alojada la descarga. En SmartOS no se habla de versiones, hay versiones nuevas cada 2 semanas y se identifican las descargas con las fechas. La descarga está disponible en varios formatos. El formato recomendado es USB, pero para este post vamos a descargar la imagen de VMware. La descarga no es muy grande (~250 MB) y cuando lo tengamos lo descomprimimos en alguna carpeta. Abrimos <a href="https://my.vmware.com/web/vmware/free">VMware Player </a>(o VMware Fusion o VMware Workstation, el que te guste más) y desde allí abrimos la máquina virtual. Nos preguntará que si la hemos movido o copiado. Decimos que la hemos copiado. Ahora tenemos que arrancarla. Antes de nada comprueba que tu ordenador puede ejecutar máquinas virtuales de 64 bits, ya que SmartOS solo es de 64 bits. Una vez lo hemos arrancado nos aparecerá una imagen de GRUB. Seleccionamos la primero opción <strong>Live 64-bit (text)</strong>. Si tienes experiencia instalando sistemas operativos verás que aquí no hay ninguna entrada para instalar. Esto es porque SmartOS no se instala, se configura.<br><h2>Configurando SmartOS</h2><br>Ahora nos saldrá un asistente con una serie de preguntas. Respondemos a ellas. En el tema de la conexión yo elegí DHCP y tenía configurada la máquina virtual para conectarse usando NAT. En el apartado de discos destacar que solo podemos seleccionar el último. Si elegimos el primer disco que se nos ofrece estaremos sobreescribiendo la imagen Live de SmartOS y la suiente vez no arrancará. Cuando hayan terminado las preguntas se reiniciará y en GRUB seleccionamos la misma opción. Ahora veremos un curioso login con el símbolo de Joyent. En este momento el sistema ya está configurado para empezar a trabajar dentro de él. Podemos usar SSH o la terminal del ordenador.<br><h2>Creando la SmartMachine</h2><br>Ahora vamos a crear una SmartMachine. ¿Qué es una SmartMachine? Es una zona dentro de SmartOS autocontenida que no puede interactuar con el exterior. Es una manera de virtualización que ofrece SmartOS (la otra es <a href="http://es.wikipedia.org/wiki/Kernel-based_Virtual_Machine">KVM</a>). En las SmartMachines será el único lugar donde podremos instalar software (realmente sí se puede instalar fuera de una zone con un instalador manual, pero no es recomendable). Bien, ahora debemos actualizar la lista de imágenes base para las SmartMachines.<br><pre class="lang:default decode:true">imgadm update<br></pre><br>Ahora buscamos la que más se adecue a lo que buscamos.<br><pre class="lang:default decode:true">imgadm avail<br></pre><br>En mi caso buscaba algo que tuviese ya Node.js e hice esto.<br><pre class="lang:default decode:true">imgadm avail | grep node<br></pre><br>Y la descargamos con<br><pre class="lang:default decode:true">imgadm import UUID_DE_LA_IMAGEN<br></pre><br>Es importante ver que en SmartOS el uso de los UUID está muy presente como veremos<br><br>Ahora que ya tenemos la base vamos a definir nuestra SmartMachine mejor. Así pues creamos un fichero JSON tal que así. Yo lo he decidido guardar en /tmp/node-zone.json<br><pre class="lang:js decode:true">{<br>"brand" : "joyent",<br>"dataset_uuid": "NUESTRO_UUID",<br>"resolvers" : ["8.8.8.8"],<br>"nics" : [<br>"nic_tag" : "dhcp",<br>"ip" : "dhcp"<br>],<br>"autoboot" : true,<br>"ram" : 512<br>}<br></pre><br>Y en hay muchísimas más opciones. Puedes verlas todas en el manual de vmadm<br><pre class="lang:default decode:true">man vmadm<br></pre><br>Ahora creamos una nueva zone basada en nuestra imagen base con:<br><pre class="lang:default decode:true">vmadm create -f /tmp/node-zone.json<br></pre><br>Ahora podemos encontrar las zonas que estan activas:<br><pre class="lang:default decode:true">zoneadm list -civ<br></pre><br>Como vemos hay dos zonas activas, una es la global, en la que estamos trabajando. La otra es la que acabamos de crear y que tiene un nuevo UUID asignado. Mencionar que las zonas las podemos encender y apagar con vmadm también:<br><pre class="lang:default decode:true">vmadm stop UUID &amp;&amp; vmadm start UUID<br></pre><br>Bien, para entrar a las zonas podemos hacerlo con zlogin<br><pre class="lang:default decode:true">zlogin UUID<br></pre><br>y estaremos dentro de una zona. En mi caso se puede comprobar que ya tengo Node.js instalado con<br><pre class="lang:default decode:true">node -v &amp;&amp; npm -v<br></pre><br><h2>Dentro de la SmartMachine</h2><br>Ahora podemos instalar paquetes para la zona con pkgin. Por ejemplo digamos que quiero instalar <a href="http://cmake.org">CMake</a>.<br><pre class="lang:default decode:true">pkgin update &amp;&amp; pkgin install cmake<br></pre><br>Ahora debemos ajustar todo para la aplicación que deseemos probar. Una buena manera para empezar sería clonar un repositorio Git en la carpeta dentro del usuario de la carpeta /home. Luego instalaríamos todo con<br><pre class="lang:default decode:true">npm install<br></pre><br>Ahora crearemos el servicio. Creamos un fichero XML parecido a este:<br><pre class="lang:default decode:true">&lt;?xml version="1.0"?&gt;<br>&lt;!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1"&gt;<br>&lt;service_bundle type="manifest" name="sonzono"&gt;<br>&lt;service name="site/sonzono" type="service" version="1"&gt;<br><br>&lt;create_default_instance enabled="true"/&gt;<br><br>&lt;single_instance/&gt;<br><br>&lt;dependency name="network" grouping="require_all" restart_on="refresh" type="service"&gt;<br>&lt;service_fmri value="svc:/milestone/network:default"/&gt;<br>&lt;/dependency&gt;<br><br>&lt;dependency name="filesystem" grouping="require_all" restart_on="refresh" type="service"&gt;<br>&lt;service_fmri value="svc:/system/filesystem/local"/&gt;<br>&lt;/dependency&gt;<br><br>&lt;method_context working_directory="/home/admin/sonzono"&gt;<br>&lt;method_credential user="admin" group="staff" privileges='basic,net_privaddr'  /&gt;<br>&lt;method_environment&gt;<br>&lt;envvar name="PATH" value="/home/admin/local/bin:/usr/local/bin:/usr/bin:/usr/sbin:/bin"/&gt;<br>&lt;envvar name="HOME" value="/home/admin"/&gt;<br>&lt;/method_environment&gt;<br>&lt;/method_context&gt;<br><br>&lt;exec_method<br>type="method"<br>name="start"<br>exec="/opt/local/bin/node /home/admin/sonzono/app.js"<br>timeout_seconds="60"/&gt;<br><br>&lt;exec_method<br>type="method"<br>name="stop"<br>exec=":kill"<br>timeout_seconds="60"/&gt;<br><br>&lt;property_group name="startd" type="framework"&gt;<br>&lt;propval name="duration" type="astring" value="child"/&gt;<br>&lt;propval name="ignore_error" type="astring" value="core,signal"/&gt;<br>&lt;/property_group&gt;<br><br>&lt;property_group name="application" type="application"&gt;<br><br>&lt;/property_group&gt;<br><br><br>&lt;stability value="Evolving"/&gt;<br><br>&lt;template&gt;<br>&lt;common_name&gt;<br>&lt;loctext xml:lang="C"&gt;node.js sonzono&lt;/loctext&gt;<br>&lt;/common_name&gt;<br>&lt;/template&gt;<br>&lt;/service&gt;<br>&lt;/service_bundle&gt;<br></pre><br>Lo guardamos dentro de la configuración con.<br><pre class="lang:default decode:true">svccfg import sonzono.xml<br></pre><br>Y ya podemos activar el servicio<br><pre class="lang:default decode:true">svcadm enable sonzono<br></pre><br>Podemos ver los logs de todos los servicios en /var/svc/log/<br><br>Y con esto creo que ya teneis suficiente información acerca de SmartOS como para ir jugueteando con él. Quiero mencionar que SmartOS es un sistema operativo con muy poco soporte pero que poco a poco se va haciendo más importante.<br><h2>Referencias</h2><br><a href="https://github.com/isaacs/joyent-node-on-smart-example">https://github.com/isaacs/joyent-node-on-smart-example</a><br><br><a href="http://docs.instantservers.telefonica.com/display/isc2/Developing+a+Node.js+Application">http://docs.instantservers.telefonica.com/display/isc2/Developing+a+Node.js+Application</a><br><br><a href="http://wiki.smartos.org/display/DOC/How+to+create+a+zone+%28+OS+virtualized+machine+%29+in+SmartOS">http://wiki.smartos.org/display/DOC/How+to+create+a+zone+%28+OS+virtualized+machine+%29+in+SmartOS</a><br><br><a href="http://www.machine-unix.com/beginning-with-smartos/">http://www.machine-unix.com/beginning-with-smartos/</a>]]></description>
                <comments>https://blog.adrianistan.eu/smartos-una-introduccion</comments>
                <pubDate>Mon, 13 Oct 2014 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>La mística relación entre el 9N y Firefox</title>
                <link>https://blog.adrianistan.eu/la-mistica-relacion-entre-el-9n-y-firefox</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/la-mistica-relacion-entre-el-9n-y-firefox</guid>
                <description><![CDATA[Si habeis leído el título pudiera parecer que he empezado a tomar drogas o alguna sustancia estupefaciente variada. Sin embargo nada más lejos de la realidad, la mística relación entre el 9N (la consulta catalana) y <a href="http://getfirefox.com">Firefox</a> se abrió ante mis ojos cuando vi esta valoración de mi complemento en el sitio de complementos de Firefox.<br><br><img class="alignnone size-full wp-image-8" src="https://files.adrianistan.eu/9N-Firefox.png" alt="9N-Firefox" width="720" height="126" /><br><blockquote>He intentado por tres golpes instalar este complemento, pero, como ya es fuerza normal, si no eres de MADRID ("Villa y Corte") no funciona. Puedes entrar a "Opciones", pero el identificador de municipio NO ADMITE un primer número que sea el "0". Por lo tanto, Barcelona, que es id 08019 , no funciona. SIEMPRE IGUAL. 9N. <em>(Traducción hecha por <a href="http://www.apertium.org">Apertium</a>)</em></blockquote><br>Os pongo en situación. El comentario en cuestión se refiere al complemento <a href="https://addons.mozilla.org/es/firefox/addon/el-tiempo-de-espa%C3%B1a/">El Tiempo en España</a> y la valoración anterior (solo había una antes) le daba 5 estrellas sobre 5. ¿Qué ha pasado? Resulta que cuando hice el complemento (es bastante viejo) use el control numérico que ofrece <a href="https://developer.mozilla.org/es/docs/XUL">XUL</a> y almacené la preferencia como un entero. Esto funcionaba con todos los códigos que había probado pero fallaba con los que empezaban con 0. ¿Por qué? La respuesta es muy sencilla y es que al estar en un control numérico al introducir un número a la izquierda este se nos quita tan rápidamente como introdujamos la siguiente cifra. El código de Barcelona empieza por 0 y claro, en esa ciudad no funcionaba el complemento. En eso le doy la razón al autor de la valoración. Pero de ahí a relacionar el que no funcione con un compot contra los catalanes por parte de Madrid me parece excesivo y un sin sentido. ¿Cómo alguien puede relacionar un bug con un complot político? (encima de un complemento que es open source)]]></description>
                <comments>https://blog.adrianistan.eu/la-mistica-relacion-entre-el-9n-y-firefox</comments>
                <pubDate>Wed, 08 Oct 2014 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Lumtumo ya es open source</title>
                <link>https://blog.adrianistan.eu/lumtumo-ya-es-open-source</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/lumtumo-ya-es-open-source</guid>
                <description><![CDATA[Si miramos la fecha del calendario descubriremos que cuando he publicado este artículo era el día 25 de septiembre. Como todos los que me conocen saben normalmente publico siempre algo el día 25 de septiembre, ya que es un manera de celebrar mi cumpleaños. Durante un tiempo publicaba <a href="http://adrianistan.eu/azpazeta-promotional-website">Azpazeta</a>, este año sin embargo voy a publicar <a href="http://bitbucket.org/AdrianArroyoCalle/lumtumo">Lumtumo</a>. Básicamente comentar que Azpazeta no está en desarrollo y sí otro tipo de juegos como Lumtumo que según puedo observar en BitBucket el último commit es de abril de este año. Realmente Lumtumo ya había sido publicado y se podía adquirir previo pago. Sin embargo, a partir de ahora Lumtumo tendrá una licencia open-source auténtica. Concretamente va a tener la <a href="http://www.gnu.org/licenses/gpl-2.0.html">GNU GPLv2</a> y seguirá alojado en <a href="http://bitbucket.org">BitBucket</a>, aunque ahora el repositorio será abierto.<br><br><img class="alignnone size-full wp-image-68" src="https://files.adrianistan.eu/Lumtumo.png" alt="Lumtumo" width="803" height="600" /><br><blockquote>En Lumtumo tenemos que ir destruyendo las naves enemigos que se opongan a nosotros. Para ellos podremos lanzar misiles y tendremos un tiempo para acabar con todos antes de que lleguen refuerzos. ¿Serás capaz de mantener a las naves alienígenas fuera de nuestro planeta?</blockquote><br>Así pues ya puedes descargar el código fuente original: <a href="http://bitbucket.org/AdrianArroyoCalle/lumtumo">http://bitbucket.org/AdrianArroyoCalle/lumtumo</a>]]></description>
                <comments>https://blog.adrianistan.eu/lumtumo-ya-es-open-source</comments>
                <pubDate>Thu, 25 Sep 2014 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>La Odisea de una aplicación comercial hecha en Ogre y CEGUI</title>
                <link>https://blog.adrianistan.eu/la-odisea-de-una-aplicacion-comercial-hecha-en-ogre-y-cegui</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/la-odisea-de-una-aplicacion-comercial-hecha-en-ogre-y-cegui</guid>
                <description><![CDATA[Últimamente he estado concentrado en una tarea que considero extenuante, por la falta de información, y por la abundancia de respuestas que no aportan nada al caso. El dilema en cuestión es la distribución de binarios en Linux de una aplicación privativa, concretamente un juego hecho en <a href="http://ogre3d.org">OGRE</a> y <a href="http://cegui.org.uk">CEGUI</a>. Y la verdad es que la odisea es larga. Así que he decidido comparar todas las opciones, sus pros y sus contras.<br><br><img class="alignnone size-large wp-image-218" src="https://files.adrianistan.eu/OGRE-1024x768.png" alt="OGRE" width="840" height="630" /><br><h2>Opciones</h2><br>Las opciones que he valorado para comparar son:<br><ul><br> 	<li>Paquetería nativa DEB, RPM y TAR.XZ</li><br> 	<li>Distribuir un TAR.GZ binario</li><br> 	<li><em>Estatificar</em> (palabro inventado) el binario</li><br> 	<li>Paquetería multi-distro, Autopackage, Listaller</li><br></ul><br><h2>Paquetería nativa DEB, RPM y TAR.XZ</h2><br>Esta opción, es a priori la más conveniente. El usuario dispondrá de un bonito paquete nativo (que se podrá actualizar) y se le gestionarán las dependencias, por ello ahorrará espacio en disco. Además tendrá una integración real con el sistema, lo podrá desinstalar desde donde acostumbra a desinstalar y el juego le aparecerá en los menús. Sin embargo los problemas salen enseguida, por una parte tienes que centrarte en ofrecer varios sistemas de distribución. Por otra parte, los paquetes nativos suelen no llevarse muy bien con otros paquetes de otras arquitecturas, ya que en algunos casos se nos pedirá instalar prácticamente el sistema de 32 bits encima del de 64, cuando realmente no es necesario. Además el tema de las dependencias es un arma de doble filo, pues en el caso de OGRE y CEGUI, o no hay, o están desfasadas. Así pues nuestro paquete puede quedarse inservible pronto. Y una peculiaridad de OGRE, resulta que tenemos que definir en plugins.cfg la localización de los plugins que usemos (normalmente con definir el de OpenGL ya sirve), pero la localización de esos plugins puede estar dispersa entre sistemas y arquitecturas. Con lo que deberíamos modificar el fichero plugins.cfg en cada sistema y arquitectura.<br><h2>Distribuir un TAR.GZ binario</h2><br>Visto lo anterior, parece ser más cómodo distribuir un TAR.GZ binario. Muchas aplicaciones como <a href="http://java.com">Oracle Java</a> o <a href="http://getfirefox.com">Mozilla Firefox</a> son distribuidas desde el sitio oficial usando este sistema. Sin embargo, y aunque pueda parecer una buena idea, tiene algún que otro inconveniente. En primer lugar debemos intentar usar librerías estáticas. Si las librerías que usamos tienen esa opción, correcto. Puede que esas librerías dependan en otras que no puedan ser estáticas, y aunque lo fueran, añadirían más carga al ejecutable y complejidad en la compilación. Si dependemos de librerías compartidas las podemos poner en una carpeta que tengamos dentro del TAR.GZ y usar RPATH con $ORIGIN o LD\_LIBRARY\_PATH. El caso es que hay ciertas herramientas que nos pueden servir para ello. Por ejemplo <a href="http://web.archive.org/web/20141013184013/http://codevarium.gameka.com.br:80/deploying-cc-linux-applications-supporting-both-32-bit-and-64-bit/"><strong>cpld</strong></a> nos puede servir para copiar todas las dependencias. El problema es que el proceso no puede ser automatizado completamente ya que el script no puede diferenciar una dependencia base de Linux de una que realmente debamos distribuir. Así que despúes de usar la herramienta nos tocará volver a revisar los ficheros que ha copiado ya que habrá copiado libc, libm, libpthread, libX11 y otras que posiblemente no necesitemos. Pero si no las copiamos podríamos tener problemas en sistemas de 64 bits. Una solución efectiva consiste en hacer un pequeño script que cargue las librerías según el sistema en el que estemos, habiendo usado el script en un sistema de 32 bits y en otro de 64 bits. Esto puede parecer razonable, pero tanto OGRE como CEGUI usan una arquitectura basada en plugins. Estos plugins no pueden ser analizados por ninguna herramienta y deberemos saber manualmente que plugins tendremos que pasar a  el comprimido. Por supuesto debemos separar las dos arquitecturas. En OGRE es fácil pasar los plugins ya que por defecto se usa el fichero plugins.cfg para leer los plugins, pero CEGUI no tiene esa característica (o yo no la he encontrado). Sin embargo es una alternativa muy sólida.<br><h2><em>Estatificar</em></h2><br>Una opción que encontré mientras buscaba una solución se puede llamarla como estatificar. Es un método poco común que coge las dependencias compartidas y las empaqueta en un único binario. No se debe confundir con compilación estática ya que <em>estatificar</em> requiere un binario ya compilado con dependencias. Únicamente hay dos aplicaciones que hacen esta función, y las dos son del mismo creador, aunque difieren en el funcionamiento de base. Por un lado <a href="http://statifier.sourceforge.net/">Statifier</a>, open source y gratuita, trabaja a un modo más rudimentario; por otro lado, Magic Ermine, trabaja con un sistema más perfeccionado, es de pago pero tiene una demo gratuita para probar. Los resultados no eran muy buenos, Statifier modificó el binario de manera que ya no arrancaba, por otro lado Magic Ermine funcionaba correctamente, pero el plugin de OpenGL de OGRE usa libGL.so, y como es cargada dinámicamente, <a href="http://www.magicermine.com">Magic Ermine</a> no sabe que tenía que empaquetarla y como consecuencia falla con un bonito error.<br><h2>Paquetería multidistro</h2><br>Autopackage, Listaller, 0install, ... Prometen mucho pero no funcionan bien. Autopackage ha sido descontinuado en favor de Listaller, Listaller se tiene que integrar con AppStream y PackageKit, pero esos mismos componentes no están finalizados, <a href="https://bugs.launchpad.net/listaller/+bug/1361686">Listaller falla más que una escopeta de feria en la práctica</a> y la documentación es pésima. Realmente espero que mejoren ya que prometen mucho, pero no se cuando será realmente usable. 0install trabaja de otra manera y al menos en mi caso, fallaba y no podía siquiera empaquetar una simple aplicación. Esta opción quizá en otro momento, pero ahora no.<br><h2>Conclusión</h2><br>Hemos visto 4 opciones. Dos de ellas no funcionaban bien en la práctica (estatificar y la paquetería multidistro), las otras dos eran tediosas, pero si se estudia al detalle puede funcionar bien. Así pues podemos elegir entre la paquetería nativa y el TAR.GZ binario con scripts de dependencias. Así pues cada cuál elija la que más le guste.]]></description>
                <comments>https://blog.adrianistan.eu/la-odisea-de-una-aplicacion-comercial-hecha-en-ogre-y-cegui</comments>
                <pubDate>Sat, 30 Aug 2014 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Probando Jekyll</title>
                <link>https://blog.adrianistan.eu/probando-jekyll</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/probando-jekyll</guid>
                <description><![CDATA[Aquí estamos probando el nuevo sistema de blog usando Jekyll, siguiendo los pasos de <a href="http://web.archive.org/web/20160526214350/http://joshualande.com/jekyll-github-pages-poole">otro blog</a>]]></description>
                <comments>https://blog.adrianistan.eu/probando-jekyll</comments>
                <pubDate>Tue, 05 Aug 2014 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>GAJSE, una carta de presentación</title>
                <link>https://blog.adrianistan.eu/gajse-una-carta-de-presentacion</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/gajse-una-carta-de-presentacion</guid>
                <description><![CDATA[Si me seguís en Google+ sabreis que hace poco he anunciado públicamente la existencia de GAJSE. Hoy os voy a hablar un poco más sobre GAJSE y todo lo relacionado. En primer lugar comentar la idea, GAJSE nace con el objetivo de cubrir el nicho de las aventuras gráficas en 3D en el navegador. Busqué y no encontré nada parecido, solamente había motores 2D para el navegador. Así pues, y después de jugar a Grim Fandango, el proyecto comenzó. He estado aprendiendo node y npm por el camino, de modo que todavía GAJSE no es un proyecto 100% node, pero espero que lo sea rápidamente. En GAJSE he buscado facilidad a la vez que flexibilidad. De momento GAJSE no hace mucho por sí solo, pero espero que eso mejore. Todo puesto en su lugar para que en un determinado momento se puedan generar los juegos desde un editor gráfico sin tocar una pizca de código. Más adelante les enseñaré como usar GAJSE de forma productiva pero espero que esto sirva de carta de presentación a GAJSE aquí, en mi blog.]]></description>
                <comments>https://blog.adrianistan.eu/gajse-una-carta-de-presentacion</comments>
                <pubDate>Mon, 28 Jul 2014 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Mis predicciones para el futuro (en lenguajes de programación) I</title>
                <link>https://blog.adrianistan.eu/mis-predicciones-para-el-futuro-en-lenguajes-de-programacion-i</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/mis-predicciones-para-el-futuro-en-lenguajes-de-programacion-i</guid>
                <description><![CDATA[Recientemente he estado probando nuevos lenguajes de programación, he investigado un poco en su historia y he pensado en hacer unas predicciones para el futuro de los lenguajes más populares.<br><br><b>Python</b><br>Python va a subir su popularidad con a corto plazo debido a su implantación en la educación. Si se consigue la transición a Python 3 que la gente lleva esperando tanto tiempo podría estabilizarse pero si no tendríamos en el problema de compatiblidad con grandes aplicaciones en Python 2. Quizá interese a largo plazo un nuevo JIT, de manera que este aumente su velocidad. Por estos motivos y teniendo en cuenta su sintaxis <i>no de C</i> creo que a largo plazo podría bajar considerablemente su popularidad.<br><br><b>Perl</b><br>Sencillamente cada vez va a bajar más. Es un lenguaje que para la mayoría resulta demasiado complejo teniendo otras alternativas para el scripting como Python, Bash o Node. Perl se seguirá usando en las tareas de compilación debido a la gran dependencia de las autotools pero no creo que evolucione mucho más de lo que es. Habrá que ver como reacciona la gente con Perl 6. Mencionar también que los sitios usando CGI van poco a poco a ir desapareciendo. De hecho el módulo de CGI está desrecomendado dentro de Perl 5.22 .<br><br><b>Node y JavaScript</b><br>Le veo un gran futuro a esta plataforma, al igual que a las bases de datos al estilo de MongoDB y al gran gestor de paquetes NPM. Su rapidez y el concepto de asíncrono van a ser sus grandes bazas. Me parece que va a ser un gran contrincante en el mundo web y también para el scripting, pudiendo sustituir eventualmente a Python y Perl en esas tareas. En el lado del cliente su uso es obligatorio pero va a crecer mucho con las tecnologías HTML5.<br><br><b>PHP</b><br>Se estabilizará bastante. Dentro de lo que cabe, es rápido para la poca memoria que gasta en comparación con otros<b> </b>lenguajes (Python, Java,...). Se va a seguir usando bastante sobre todo debido al gran público que conoce PHP. Además muchas empresas seguirán usando PHP antes que arriesgar con otros lenguajes más novedosos.<br><br><b>Java</b><br>Perderá adeptos. El modelo actual de Java es a mi parecer demasiado estricto y complejo y además cuenta con limitaciones de soporte en escritorio y perdida de velocidad en la web. Opino que si bien Java es de los lenguajes más populares, va a perder comunidad, que se va a desplazar a otros lenguajes más cómodos. Pero será muy gradual debido a la misma razón que PHP, muchas empresas seguirán usando Java un tiempo<br><b> </b><br><b>Ruby</b><br>Ruby va a ser más importante sobre todo en scripting local, y quizá algo menos en web. La razón es que Ruby no aporta atractivas ventajas para las empresas al cambiar de PHP o Java a Ruby. Por eso creo que va a crecer más en scripting local, aunque<b> </b>posiblemente las startups usen Ruby para sus proyectos.<br><br><b>C y C++</b><br>Grandes proyectos seguirán usando C y C++ y muy dificilmente cambien. Los nuevos proyectos quizá se sientan menos atraídos por C++ teniendo Rust y Go, pero aguantará mucho.<br><b> </b><br><b>C#</b><br>Crecerá. En cuanto la plataforma .NET<b> </b>se abra más a diferentes sistemas, el crecimiento será exponencial. Y ese es el rumbo que se está tomando con iniciativas como Xamarin o ASP.NET vNext, que han comunicado que será open-source y funcionará sobre Mono en Mac OS X y Linux.<br><br><b>Rust</b><br>Creo que este lenguaje va a dar mucho que hablar, no hoy, pero sí en un futuro a medio plazo. Rust aporta ventajas al venerado C++ y conserva su velocidad. Creo que Rust va a ocupar un lugar en los videojuegos, creo que en algún tiempo veremos algún Triple A hecho en Rust como lenguaje principal.<br><br>Seguiré con mis predicciones en otro post]]></description>
                <comments>https://blog.adrianistan.eu/mis-predicciones-para-el-futuro-en-lenguajes-de-programacion-i</comments>
                <pubDate>Wed, 02 Jul 2014 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>La Semana en Adrianistán (IV)</title>
                <link>https://blog.adrianistan.eu/la-semana-en-adrianistan-iv</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/la-semana-en-adrianistan-iv</guid>
                <description><![CDATA[Con un día de retraso llega una nueva entrada de La Semana en Adrianistán. Empecemos:<br /><ul><li>GAJSE ha añadido soporte para eventos cuando el usuario choque con un objeto.</li><li>DivCity ha mejorado en lo relativo al display de las <i>Cells </i>e incluye un nuevo concepto para modificar las Cells, las Tools.</li><li>DivPacker, un nuevo proyecto, que espero que sea simple y divertido</li><li>Tengo nueva página oficial. Mi nueva página pasa a estar disponible en http://adrianarroyocalle.github.io Basada en una temática de una ciudad, es muy colorida.</li></ul><br>]]></description>
                <comments>https://blog.adrianistan.eu/la-semana-en-adrianistan-iv</comments>
                <pubDate>Tue, 17 Jun 2014 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>La Semana en Adrianistán III</title>
                <link>https://blog.adrianistan.eu/la-semana-en-adrianistan-iii</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/la-semana-en-adrianistan-iii</guid>
                <description><![CDATA[Esta semana me ha dejado poco tiempo, pero hemos podido avanzar en algunos aspectos. Primero, he de decir que he creado una máquina virtual en C++ que ejecuta el bytecode. La he llamado FlopFlip, en contraposición al típico FlipFlop. De momento solo soporta un set de instrucciones básico. En segundo lugar he mejorado un poco el webapp de La Maquinaria de Videojuegos. Todavía no hace nada útil pero ya soporta sesiones y puedo generar CSS desde LESS. Para ello he preferido usar Node.js porque me parece que se ajustaba bastante mejor a Java EE. Voy a usar Express, un framework magnífico por su sencillez y MongoDB para los datos. Por otro lado GAJSE ha recibido una nueva versión en NPM que incluye soporte para preguntas y respuestas en el ScriptedText. Me estoy planteando la pregunta de si debo hacer una interfaz al estilo de FileText que cargue los fichero JavaScript, pero soy un poco reacio. Primero por el poco control que tendría si lo hago con el mismo mecanismo que las escenas y segundo porque los eval() y los new Function() están desactivados en algunos entornos como las Chrome Web Apps.De momento la única implementación de Text va a ser ScriptedText y puede que lo mantenga hasta la salida de la 1.0.0.<br>]]></description>
                <comments>https://blog.adrianistan.eu/la-semana-en-adrianistan-iii</comments>
                <pubDate>Mon, 09 Jun 2014 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>La semana en Adrianistán II</title>
                <link>https://blog.adrianistan.eu/la-semana-en-adrianistan-ii</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/la-semana-en-adrianistan-ii</guid>
                <description><![CDATA[Esta semana ha sido movidita. Primero he recibido un Arduino UNO. Por supuesto he mirado un montón de cosas como por ejemplo esta:<br /><div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-Y-cRHno_Iow/U4ybYJtZdCI/AAAAAAAACO4/A1fu1wGGv9E/s1600/VID_20140531_125653.mp4" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-Y-cRHno_Iow/U4ybYJtZdCI/AAAAAAAACO4/A1fu1wGGv9E/s1600/VID_20140531_125653.mp4" height="240" width="320" /></a></div>Un sencillo juego que nos muestra al principio un número de 0 a 9 durante un segundo. Después empiezan a pasar los números en orden y tenemos que pulsar el botón cuando estemos en el número del principio. Si acertamos, lucecita verde, si no lucecita roja. Además he incluido sonido con el zumbador.<br /><br />También se han producido avances en DivCity que ahora luce un nuevo sistema de clases para los edificios. Poco visible por el momento, pero bastante código potencialmente útil. Además se ha añadido un curioso script que mejorará la distribución del TAR.GZ binario en Linux. Como medida de seguridad, ahora DivCity guarda todo el código de las librerías en las que depende. Ahora mismo estoy trabajando en el sistema para detectar los edificios seleccionados con el ratón.<br>]]></description>
                <comments>https://blog.adrianistan.eu/la-semana-en-adrianistan-ii</comments>
                <pubDate>Mon, 02 Jun 2014 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>La semana en Adrianistán I</title>
                <link>https://blog.adrianistan.eu/la-semana-en-adrianistan-i</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/la-semana-en-adrianistan-i</guid>
                <description><![CDATA[Esta nueva sección que voy a llamar "La semana en Adrianistán". Comenzamos con la semana en Adrianistán I.<br />El proyecto GAJSE se ha llevado la mayor parte de mis esfuerzos. He añadido a GAJSE las siguientes funcionalidades:<br /><ul><li>&nbsp;Audio posicional. Basándome en Geometría Analítica básica y el Gain Node de la API WebAudio he conseguido crear un audio posicional que depende de donde estes situado. Funciona bastante bien.</li><li>Sistema de iluminación. No ha supuesto mucho esfuerzo pues simplemente llamamos a Three.js con los mismos parámetros</li><li>Sistema básico de personajes. Ya se permite la creación de personajes en una escena, pero faltan muchas cosas. Ha sido necesario para implementar lo siguiente:</li><li>Guiones hablados. Ahora ya se pueden hacer conversaciones basadas en <i>ScriptedText.</i> En este método las conversaciones son un callback de una función. Me ha parecido que era la más fácil de implementar y funciona bastante bien. Sin embargo para ello he tenido que modificar el módulo de mensajes y hacer uno basado en un Stack de mensajes (debido a que JavaScript es asíncrono). Todavía no se permiten elecciones basadas en el texto, pero trabajaré en ello.</li></ul>Y esta ha sido la primera semana en Adrianistán. La próxima semana contaré más novedades. <br /><ul></ul><br>]]></description>
                <comments>https://blog.adrianistan.eu/la-semana-en-adrianistan-i</comments>
                <pubDate>Mon, 26 May 2014 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Introducción a la orientación</title>
                <link>https://blog.adrianistan.eu/introduccion-a-la-orientacion</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/introduccion-a-la-orientacion</guid>
                <description><![CDATA[La Orientación es un deporte que practico bastante a menudo y me gustaría enseñaros un poco más sobre este deporte desconocido.<br><h2>¿Qué es la Orientación?</h2><br>La Orientación (o deporte de orientación) es un deporte en el que varias personas deben llegar cuanto antes a  la meta pasando por una serie de puntos marcados. Estos puntos no están unidos entre sí y para llegar a ellos se te proporciona un mapa y una brújula. Los puntos por los que se debe pasar se llaman balizas y son como en la foto inferior.<br><div class="separator" style="clear: both; text-align: center;"></div><br><div class="separator" style="clear: both; text-align: center;"><a style="margin-left: 1em; margin-right: 1em;" href="http://1.bp.blogspot.com/-Q8nmdM13P5g/Uq35_uwPDlI/AAAAAAAABr0/iu-PABDlFf0/s1600/baliza.jpg"><img src="http://1.bp.blogspot.com/-Q8nmdM13P5g/Uq35_uwPDlI/AAAAAAAABr0/iu-PABDlFf0/s400/baliza.jpg" width="400" height="300" border="0" /></a></div><br>Una baliza suele ser de color naranja y lleva un número. Este número es el identificador de la baliza. Arriba tenemos dos sistemas diferentes de comprobar que has pasado por allí, una pinza roja que taladra un dibujo sobre el papel, y una baliza SportIdent, un sistema más moderno y usado que permite guardar tiempos y parciales. El número de balizas en una carrerar puede variar, pero suele haber más de 10. El mapa que nos dan en la salida (normalmente la carrear es cronometrada, se sale escalonadamente) está dibujado siguiendo unas normas. Aquí tenemos un trozo del tramo de una baliza en un mapa:<br><div class="separator" style="clear: both; text-align: center;"><a style="margin-left: 1em; margin-right: 1em;" href="http://1.bp.blogspot.com/-CqcJitI8z48/Uq36AcYOswI/AAAAAAAABr4/9m83Po8HLKI/s1600/mapa.jpg"><img src="http://1.bp.blogspot.com/-CqcJitI8z48/Uq36AcYOswI/AAAAAAAABr4/9m83Po8HLKI/s400/mapa.jpg" width="400" height="175" border="0" /></a></div><br>El mapa que se nos da viene todo el terreno (bosque, pradera, ciudad, etc) por el que se corre. Este recorrido nos indica el camino para la baliza 10. He aquí una pequeña tabla de colores para empezar:<br><table><br><tbody><br><tr><br><th>Color</th><br><th>Significado</th><br></tr><br><tr><br><td>Blanco</td><br><td>Bosque de penetrabilidad media</td><br></tr><br><tr><br><td>Verde</td><br><td>Bosque denso (hay niveles)</td><br></tr><br><tr><br><td>Naranja</td><br><td>Claro, no hay bosque</td><br></tr><br><tr><br><td>Líneas marrones</td><br><td>Nos indica la altura de una zona, son las curvas de nivel</td><br></tr><br><tr><br><td>Azul</td><br><td>Agua</td><br></tr><br><tr><br><td>Negro</td><br><td>Piedra o artificial</td><br></tr><br><tr><br><td>Marrón</td><br><td>Tierra</td><br></tr><br></tbody><br></table><br>Estos colores tiene a su vez símbolos más concretos (la V es hoyo, puede ser negra o marrón; el "peine" es un cortado, puede ser negro o marrón).<br><h2>Suena bien, ¿dónde puedo ir?</h2><br>Para empezar deberías a ir a un club de orientación, allí te enseñarán y te llevarán a mapas reales, primero en categorías pequeñas, después mejorarás. Hay clubes por todo el mundo, pero es cierto que si vivieses en Finlandia conocerías más este deporte.]]></description>
                <comments>https://blog.adrianistan.eu/introduccion-a-la-orientacion</comments>
                <pubDate>Sun, 15 Dec 2013 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Territorios españoles olvidados</title>
                <link>https://blog.adrianistan.eu/territorios-espanoles-olvidados</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/territorios-espanoles-olvidados</guid>
                <description><![CDATA[España, como todo el mundo debería saber, llegó a controlar en el S. XVI la mayoría de territorios entonces conocidos (Pen. Ibérica, Nápoles, Baleares, Sicilia, Canarias, Marruecos, Alemania, América del Sur, Filipinas, Centro América y Norte América). De este pasado imperial y colonial quedan todavía algunos territorios de los que no se conoce mucho en España. En general los territorios españoles son:<br /><ul><li>Parte de la Península Ibérica</li><li>Islas próximas a la parte de la Península Ibérica correspondiente.</li><li>Ceuta</li><li>Melilla</li><li>Archipiélago de Baleares</li><li>Archipiélago de Canarias</li><li>Islas Chafarinas (Isla del Congreso, Isla de Isabel II e Isla del Rey)</li><li>Isla de Alborán (e Isla de las Nubes)</li><li>Isla de Perejil</li><li>Islas Alhucemas (Isla de Tierra, Isla de Mar y Peñón de Alhucemas)</li><li>Peñón de Vélez de la Gomera</li><li>Isla de Guedes</li><li>Isla de los Pescadores</li><li>Isla de Ceas</li><li>Isla de Coroas</li><li>Llívias</li><li>Islas Salvajes</li><li>Isla de los Faisanes </li></ul>Es importante conservar estos territorios, sobre todo los que pequeños ya que son los más fáciles de atacar o dejar olvidadas. No incluyo en esta lista las islas próximas a la costa de la península pues a no ser una excepción todas deben pertenecer a España (Islas Cíes, etc). Tampoco incluyo las concretas de los archipiélagos de Baleares y Canarias ya que al ser grandes contienen muchos peñones e islotes similares.<br>]]></description>
                <comments>https://blog.adrianistan.eu/territorios-espanoles-olvidados</comments>
                <pubDate>Thu, 10 Oct 2013 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Esperanto, Interlengua y cía.</title>
                <link>https://blog.adrianistan.eu/esperanto-interlengua-y-cia</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/esperanto-interlengua-y-cia</guid>
                <description><![CDATA[Últimamente se viene hablando mucho de la importancia de conocer idiomas para poder encontrar trabajar (u obtener uno mejor). Sin embargo yo estoy viendo que todos estos problemas podrían haberse solucionado si en un ejemplo de generosidad los habitantes del planeta se hubiesen puesto de acuerdo en decidir una lengua internacional (como por ejemplo ocurre con los números o el SI de medidas). En el tema de las lenguas internacionales podrían elegirse de 3 tipos:<br /><ol><li>Una lengua viva</li><li>Una lengua artificial</li><li>Una lengua muerta</li></ol>Analicemos punto por punto las opciones que presenta cada grupo.<br /><br /><u>Una lengua viva</u><br />Puede que esto suene la opción más práctica y viable pero es la que desataría más rencillas entre países. Cada país querría que su lengua, la que ya saben, sea impuesta sobre las demás y esas luchas no llevarían a ninguna parte. Si tenemos que dar ejemplos concretos, el inglés últimamente goza de esa superioridad, pero no nos engañemos, solo es por el dominio de los EEUU. Hace tiempo este mismo puesto lo ocupó el francés y mucho antes el español, el latín y el griego. En el futuro quizá el idioma más importante sea el ruso, el chino o el árabe. ¿Quién sabe? Sin embargo ningún país querría poner un idioma actual como idioma internacional. <br /><br /><u>Una lengua artificial</u><br />Los idiomas artificiales fueron diseñados para, entre otros propósitos, usarse internacionalmente. Son idiomas muy bien diseñados en los que prima su facilidad de aprendizaje y su falta de excepciones. Su principal problema es la falta de cultura y de hablantes. Entre estos idiomas encontramos el esperanto e interlengua. Ambos son idiomas muy interesantes.<br /><br /><u>Una lengua muerta</u><br />Una lengua muerta puede parecer una buena opción pues es conocida por muchos estudiosos de la materia y no beneficiaría a ningún país. El mayor rechazo estaría en que versiones de la lengua muerta habría que implantar. En este grupo puede entrar el latín y el griego.<br>]]></description>
                <comments>https://blog.adrianistan.eu/esperanto-interlengua-y-cia</comments>
                <pubDate>Mon, 07 Oct 2013 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Algunos proyectos open-source con los que colaboro</title>
                <link>https://blog.adrianistan.eu/algunos-proyectos-open-source-con-los-que-colaboro</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/algunos-proyectos-open-source-con-los-que-colaboro</guid>
                <description><![CDATA[La filosofía open-source se basa en colaborar unos con otros para así lograr un software altamente vigilado, y por ende, estable y eficiente. Hay mucha gente que tiene a alabar los proyectos open-source, sin embargo hay un paso superior a esto, y es colaborar directamente con el proyecto. He aquí una lista de los proyectos con los que colaboro regularmente:<br /><ul><li>Haiku: El sistema operativo libre no basado en Linux ni UNIX sino en BeOS</li><li>FreeRCT: Un juego libre que intenta ser el clon de libre del famoso juego Rollercoaster Tycoon 2</li><li>Mozilla Firefox: No creo que tenga que explicarlo, aunque quizá este sea el proyecto open-source donde más gente colabora.</li></ul>Por supuesto también hay más proyectos pero en los que colaboro no tan regularmente y dicho esto me despido. <br>]]></description>
                <comments>https://blog.adrianistan.eu/algunos-proyectos-open-source-con-los-que-colaboro</comments>
                <pubDate>Mon, 07 Oct 2013 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Inicio del nuevo curso. Novedades interesantes</title>
                <link>https://blog.adrianistan.eu/inicio-del-nuevo-curso-novedades-interesantes</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/inicio-del-nuevo-curso-novedades-interesantes</guid>
                <description><![CDATA[Comienza el nuevo curso y para este curso hay novedades importantes en lo que respecta a mí, ya que desde ahora soy miembro del Consejo Escolar en concepto de sustitución de una plaza vacante. También hay nuevos proyectos en lo educativo, ya que intentaré centrarme este año mucho más en hardware. También quiero anunciar que intentaré escribir lo máximo posible y que probablemente haya una novela (o dos) por entregas en el blog. Me gustaría concluir con las siguientes secciones que crearé (sin contar las novelas):<br /><ul><li>La Reflexión del día - Hablaré sobre un tema dando mi opinión</li><li>Kuriosidad con K - Hablaré sobre una curiosidad que me haya llamado la atención</li><li>Programación - Contaré como implementar algo en algún lenguaje de programación que me haya costado</li><li>Crítica - Hablaré sobre una película, libro o videojuego que haya podido disfrutar (o aborrecer) y daré mi opinión</li></ul>Además no se quedarán fuera otros artículos que me parezcan interesantes pero que no tengan una categoría. Y dicho esto me despido de vosotros para desearos un feliz día (si lo quereis lo tomais y si no, pues que se le va a hacer) y un hasta luego.<br>]]></description>
                <comments>https://blog.adrianistan.eu/inicio-del-nuevo-curso-novedades-interesantes</comments>
                <pubDate>Thu, 03 Oct 2013 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Azpazeta 2.0 Juno. Disponible a partir de hoy.</title>
                <link>https://blog.adrianistan.eu/azpazeta-2-0-juno-disponible-a-partir-de-hoy</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/azpazeta-2-0-juno-disponible-a-partir-de-hoy</guid>
                <description><![CDATA[Hoy es un día muy feliz para mí, primero porque es mi cumpleaños y segundo porque sale oficialmente la nueva versión de Azpazeta, la 2.0 JUNO. Vamos a repasar las novedades que trae y lo probaremos.<br><h2>Diseñado desde cero</h2><br>Este es el titular para Azpazeta 2.0 Juno que al contrario de lo que el número de versión sugiere es un juego completamente nuevo. Se ha diseñado teniendo en cuenta <b>modularidad</b> y que sea una plataforma para añadir rápidamente nuevas características; en este aspecto Azpazeta original fallaba bastante con un código relativamente desordenado. Otra característica importante es el <b>rendimiento</b> de Azpazeta que ha mejorado de una manera vertiginosa. Antes con la implementación de gráficos original, el muñeco se movía lento. Ahora y gracias a su renderizado por hardware OpenGL y GLSL es mucho más rápido. No tenemos tests pero la diferencia es abismal. Otra novedad importante es el sistema de <b>mapas</b> y <b>scripts</b> que unidos sirven para crear aventuras increíbles en el universo Azpazeta. Relacionado con lo anterior tenemos la <b>tienda online de mapas</b>, un servicio online gratuito que provee de mapas para descargar desde Azpazeta. Otro detalle importante es el <b>formato de partidas</b> que sufre un resiseño (no muy grande) para ofrecer mejores características. Quizá un detalle que pase desapercibido al principio es la <b>ventana de opciones</b> que incluye muchas opciones (algunas sin uso actual) que siempre se agradece. Un detalla también importante internamente pero difícil de ver es la división del juego en <b>Cliente-Servidor</b> lo que proveerá una jugabilidad más amplia en el futuro, aunque según se está viendo, puede ser causa de problemas al intentar jugar a Azpazeta, sobre todo en plataformas Windows.<br><h2>Y lo que llegará...</h2><br>Azpazeta 2.0 viene sin mapas, os recomiendo pasaros por Azpazeta Market para descargaros unos cuantos, si tienes una idea para hacer un mapa, coméntanosla, nosotros te asesoraremos de como hacerlo mejor y como subirlo a Azpazeta Market gratuitamente. Si quieres ayudar en la plataforma, también eres bien recibido, hay muchas tareas que implementar. Por último comentar que ya se han definido las novedades que traerá Azpazeta 2.1 LINK, entre ellas: mayor estabilidad, sonido y audio, implementación de policía y tiendas y pequeños mini-concursos. A esto se sumarán seguramente más novedades pequeñas igualmente agradecidas.<br><h2>A disfrutar</h2><br>Bueno y llegado el momento, los links de descarga. Azpazeta lo podreis encontrar en muchas webs, pero yo os recomiendo ir a la que hemos creado para el lanzamiento.<br><div style="text-align: center;"><a href="http://adrianistan.eu/azpazeta-promotional-website/">Web oficial de Azpazeta</a></div>]]></description>
                <comments>https://blog.adrianistan.eu/azpazeta-2-0-juno-disponible-a-partir-de-hoy</comments>
                <pubDate>Wed, 25 Sep 2013 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>PortAudio + libsndfile. Reproducir Ogg Vorbis, FLAC y WAV en C++</title>
                <link>https://blog.adrianistan.eu/portaudio-libsndfile-reproducir-ogg-vorbis-flac-y-wav-en-c</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/portaudio-libsndfile-reproducir-ogg-vorbis-flac-y-wav-en-c</guid>
                <description><![CDATA[Escribo este post ya que cuando quise buscar información sobre el tema no encontré nada en castellano y en inglés poca información. Así pues escribo el que yo considero el mejor método para reproducir audio Ogg Vorbis, FLAC y WAV en C++.<br>Lo primero es descargar e instalar las librerías, ambas son multiplataforma. En Windows irás a la página oficial y seguirás las instrucciones:<br><ul><br> 	<li>PortAudio: <a href="http://portaudio.com/">http://portaudio.com/</a></li><br> 	<li>libsndfile: <a href="http://www.mega-nerd.com/libsndfile/">http://www.mega-nerd.com/libsndfile/</a></li><br></ul><br>A su vez si tenemos que compilar libsndfile también necesitaremos libogg y libvorbis para activar la reproducción de Ogg Vorbis o las librerías de FLAC para activar FLAC en la librería. El soporte para WAV es nativo y no hace falta nada.<br>En Linux podemos ahorrarnos tiempo instalando los paquetes binarios. En Debian/Ubuntu:<br><blockquote class="tr_bq">sudo apt-get install portaudio19-dev libsndfile1-dev</blockquote><br>Ahora creamos un fichero de C. Atención, aunque C++ sea compatible con el código de C, este código de C tiene problemas en compiladores de C++. Así pues el código de aquí abajo da error con g++ pero funciona correctamente y sin advertencias en gcc. Si quieres insertarlo en un programa de C++, sigue creando este fichero como fichero de C y en el header encapsula la función definida en un "extern C":<br><pre class="lang:c++ decode:true ">extern "C" {   <br>	miFuncionAudio();<br>}<br></pre><br>Ahora en el ejemplo voy a usar una función main para no liarnos y usar solo C, pero eso serán los pasos a seguir si tienes C++.  Creamos el fichero con extensión .c y escribimos la función main(). Ahora abriremos el archivo de audio con la librería encargada de decodificar el audio, libsndfile. Después inicializamos PortAudio y obtenemos el dispositivo de audio por defecto (en el caso de mi Ubuntu, es ALSA). Configuramos el dispositivo con los datos que tenemos del fichero. Abrimos un stream de PortAudio que leerá el archivo. Esta función necesita dos funciones de callbak que se crean más arriba. La principal tiene como objetivo leer el fichero más y la otra hacer algo cuando termine el fichero (se puede dejar vacía). En la de leer el fichero tenemos que tener en cuenta si el equipo es Mono o Estéreo. Yo no he llegado a saber como detectarlo, así que hago la multiplicación por 2 para el estéreo. Luego en el flujo principal iniciamos el stream, sonará, esperamos 10 segundos, paramos y cerramos el stream. Finalmente cerramos el fichero de audio y deshabilitamos PortAudio. Fácil y sencillo. Ahora el código:<br><pre class="lang:c++ decode:true">#include "portaudio.h"<br>#include "sndfile.h"<br><br>SF_INFO sfinfo;<br>PaStreamParameters out_param;<br>PaStream * stream;<br>PaError err;<br>SNDFILE * file;<br>static int<br>output_cb(const void * input, void * output, unsigned long frames_per_buffer,<br>        const PaStreamCallbackTimeInfo *time_info, PaStreamCallbackFlags flags, void * data)<br>{<br>	SNDFILE * filex = data;<br>	/* Here you need to multiply per 2 for stereo and per 1 for mono*/<br>	sf_read_short(filex, output, frames_per_buffer*2);<br>	return paContinue;<br>}<br>static void<br>end_cb(void * data)<br>{<br>	 Pa_StopStream(stream);<br>	 Pa_CloseStream(stream);<br>	 sf_close(file);<br>	 Pa_Terminate();<br>}  <br>#define error_check(err) \     do {\         if (err) { \             fprintf(stderr, "line %d ", __LINE__); \             fprintf(stderr, "error number: %d\n", err); \             fprintf(stderr, "\n\t%s\n\n", Pa_GetErrorText(err)); \             return err; \         } \     } while (0)  <br>int main(int argc, char ** argv)<br>{<br>	 file = sf_open(argv[1], SFM_READ, &amp;amp;sfinfo);<br>	 printf("%d frames %d samplerate %d channels\n", (int)sfinfo.frames,<br>	 sfinfo.samplerate, sfinfo.channels);<br>	 /* init portaudio */<br>	 err = Pa_Initialize();<br>	 error_check(err);<br>	 /* we are using the default device */<br>	 out_param.device = Pa_GetDefaultOutputDevice();<br>	 if (out_param.device == paNoDevice)<br>	 {<br>		fprintf(stderr, "Haven't found an audio device!\n");<br>		return -1;<br>	 }<br>	 /* stero or mono */<br>	 out_param.channelCount = sfinfo.channels;<br>	 out_param.sampleFormat = paInt16;<br>	 out_param.suggestedLatency = Pa_GetDeviceInfo(out_param.device)-&amp;gt;defaultLowOutputLatency;<br>	 out_param.hostApiSpecificStreamInfo = NULL;<br>	 err = Pa_OpenStream(&amp;amp;stream, NULL, &amp;amp;out_param, sfinfo.samplerate,<br>	 paFramesPerBufferUnspecified, paClipOff,output_cb, file);<br>	 error_check(err);<br>	 err = Pa_SetStreamFinishedCallback(stream, &amp;amp;end_cb);<br>	 error_check(err);<br>	 err = Pa_StartStream(stream);<br>	 error_check(err);<br>	 printf("Play for 10 seconds.\n");<br>	 Pa_Sleep(10000);<br>	 err = Pa_StopStream(stream);<br>	 error_check(err);<br>	 err = Pa_CloseStream(stream);<br>	 error_check(err);<br>	 sf_close(file);<br>	 Pa_Terminate();<br>	 return 0;<br>}<br></pre><br>Y para terminar un diagrama de salidas de audio nativas soportadas por PortAudio:<br><div class="separator" style="clear: both; text-align: center;"><a style="margin-left: 1em; margin-right: 1em;" href="http://1.bp.blogspot.com/-ictC8XlwEWY/UibpnGlIMRI/AAAAAAAABhM/7fHR1fLC-Lg/s1600/portaudio-external-architecture-diagram.png"><img src="http://1.bp.blogspot.com/-ictC8XlwEWY/UibpnGlIMRI/AAAAAAAABhM/7fHR1fLC-Lg/s1600/portaudio-external-architecture-diagram.png" width="640" height="240" border="0" /></a></div>]]></description>
                <comments>https://blog.adrianistan.eu/portaudio-libsndfile-reproducir-ogg-vorbis-flac-y-wav-en-c</comments>
                <pubDate>Wed, 04 Sep 2013 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Preparando el nuevo curso</title>
                <link>https://blog.adrianistan.eu/preparando-el-nuevo-curso</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/preparando-el-nuevo-curso</guid>
                <description><![CDATA[Acaba Agosto, llega Septiembre y algo ocurre; es el inicio del curso. ¿Qué decir? Para mí el año no empieza en Enero sino en Septiembre. Además el día 25 de ese mes será mi cumpleaños... ¿Qué nos deparaá el nuevo curso? Entre mis proyectos está lanzar Azpazeta 2.0 (ya está hecho) y hacerle algunas actualizaciones, DivCity en 3D podría salir bien, Bloco avanza a buen ritmo (el mejor de todos), DivHotel avanza fijo, los complementos de Firefox recibirán alguna que otra actualización, redactaré el guión de una película o una novela, manejaré mis BitCoins y puede que alguna sorpresa más. me gustaría también empezar a escribir más a menudo (1 vez a la semana mínimo) y hablar más sobre muchas más cosas. Y vosotros ¿qué proyectos teneis para el nuevo curso?<br><br>PD: Supuestamente este año me toca ser miembro del Consejo Escolar el instituto, veremos como sale todo... y de regalo el trailer de Azpazeta que ya se puede descargar si sabeis mirar:<br><div class="separator" style="clear: both; text-align: center;"><object class="BLOGGER-youtube-video" width="320" height="266" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://i1.ytimg.com/vi/xC69N3M1JRY/0.jpg"><param name="movie" value="http://www.youtube.com/v/xC69N3M1JRY?version=3&amp;f=user_uploads&amp;c=google-webdrive-0&amp;app=youtube_gdata" /><param name="bgcolor" value="#FFFFFF" /><param name="allowFullScreen" value="true" /><embed width="320" height="266" src="http://www.youtube.com/v/xC69N3M1JRY?version=3&amp;f=user_uploads&amp;c=google-webdrive-0&amp;app=youtube_gdata" type="application/x-shockwave-flash" allowfullscreen="allowfullscreen" /></object></div>]]></description>
                <comments>https://blog.adrianistan.eu/preparando-el-nuevo-curso</comments>
                <pubDate>Mon, 26 Aug 2013 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Calculadora de 4 Bits</title>
                <link>https://blog.adrianistan.eu/calculadora-de-4-bits</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/calculadora-de-4-bits</guid>
                <description><![CDATA[Un día que no sabía que hacer con un circuito de una bombilla y su pila de 4,5 V se me ocurrió hacer una calculadora binaria (sabía que decimal sería difícil). Antes de nada necesitaba diseñar los planos del diseño para saber que iba a necesitar y como lo tenía que hacer. No sabía por donde empezar así que busque en Google algo de información. Nada. Busqué entonces tutoriales de Redstone del juego Minecraft con el que se pueden hacer calculadoras. Al final eran diseños muy complejos y largos, pero no debía de ser tan difícil pensaba. Empezé a diseñar en el Crocodile Clips varios prototipos, pero llegaba a liarme mucho con si debía poner 1 o 2 o 3 puertas lógicas, etc. Al final di de casualidad con una página en la que expliacaban como montar una calculadora de 4 bits. Vi los planos y los adapté a un circuito completo. Es una calculadora binaria y con el límite en 15 (16-1) como máximo resultado. No es muy útil, pero es fácilmente extensible en potencias de base 2. Así pues os dejo los diseños de mi calculadora a ver si algún día la construyo:<br><div class="separator" style="clear: both; text-align: center;"><a style="margin-left: 1em; margin-right: 1em;" href="http://1.bp.blogspot.com/-kH0JmeMNNL0/UhtGY2E8lmI/AAAAAAAAAx0/R1hwTJPKSqU/s1600/Calculadora4Bits.PNG"><img src="http://1.bp.blogspot.com/-kH0JmeMNNL0/UhtGY2E8lmI/AAAAAAAAAx0/R1hwTJPKSqU/s1600/Calculadora4Bits.PNG" border="0" /></a></div><br><div class="separator" style="clear: both; text-align: center;"></div><br>El funcionamiento es algo retorcido (faltan conversores de decimal a binario y viceversa). Primero ponemos los sumandos activando los interruptores de los sumandos que queremos sumar. Podemos activar 1,1,2,2,4,4,8 y 8. Pero hay que tener cuidado si sumamos más de 15, pues habrá un overflow un el circuito en cuestión será físicamente dañado. Una vez ajustados los interruptores presionamos el botón al lado de la pila de 2V y veremos el resultado en los LEDs. Para obtener el resultado deberemos sumar un valor diferente por cada LED encendido. En el siguiente orden: 1,2,4 y 8. Así podemos obtener hasta 15. Digo que este modelo es fácilmente extensible, pues podemos añadir más módulos como el de los 2 y los 4. Así aumentaremos la capacidad de procesamiento. Por cierto, dentro de poco vereis más novedades sobre mi videojuego.<br><br>Planos originales: <a href="http://www.instructables.com/id/4-Bit-Binary-Adder-Mini-Calculator/step5/Building-It-On-A-Breadboard/">http://www.instructables.com/id/4-Bit-Binary-Adder-Mini-Calculator/step5/Building-It-On-A-Breadboard/</a>]]></description>
                <comments>https://blog.adrianistan.eu/calculadora-de-4-bits</comments>
                <pubDate>Tue, 28 May 2013 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>La Fuente Q y el problema sinóptico</title>
                <link>https://blog.adrianistan.eu/la-fuente-q-y-el-problema-sinoptico</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/la-fuente-q-y-el-problema-sinoptico</guid>
                <description><![CDATA[Hoy vamos a hablar sobre el <a href="https://es.wikipedia.org/wiki/Problema_sin%C3%B3ptico">problema sinóptico</a>. Quizá a muchos no os suene pero es un tema importante en la teología (habéis leído bien, la ciencia de las religiones).<br><h2>¿Qué es el Problema Sinóptico?</h2><br>Como muchos sabéis en la religión católica hay 4 evangelios oficiales (correctamente llamados canónicos). Además existen los evangelios apócrifos que son aquellos que por su contenido no están en la Biblia. El problema sinóptico ocurre cuando 3 de los 4 evangelios son muy parecidos en algunas partes (usando los mismos párrafos algunas veces). Estos evangelios son los de Marcos, Mateo y Lucas. Esto tiene una explicación lógica pues el de Marcos fue primero y los de Mateo y Lucas se inspiraron en él. Se le llama la Fuente M. Sin embargo hay partes coincidentes entre Mateo y Lucas que no aparecen en Marcos (y son bastante abundantes). Se sabe que ni Mateo conoció a Lucas ni Lucas a Mateo. Este es el problema sinóptico que ocurre además con algunos evangelios apócrifos.<br><h2>La Teoría de las dos fuentes</h2><br>Hay una teoría muy aceptada que dice que tanto Marcos, como Lucas tuvieron acceso a una fuente hoy en día desconocida, esta fuente se la llama la <a href="https://es.wikipedia.org/wiki/Fuente_Q">Fuente Q</a> (del alemán "queller", fuente). La Fuente Q explicaría las similitudes entre los diversos evangelios<br><h2>La Fuente Q</h2><br>Mientras la Fuente M se la conoce como la una lista de hechos y milagros de Jesús, la Fuente Q sería una recopilación de dichos de Jesús. Las bienaventuranzas y el Padre Nuestro solo aparecen por ejemplo en los evangelios con Fuente Q. Actualmente no se conoce ninguna referencia a la Fuente Q en la antigüedad por lo que ha sido criticada por expertos que simplemente dicen que no existe. Sin embargo, la teoría de las dos fuentes que explica la existencia de la Fuente Q es la más sencilla y como nos dice la navaja de Ocam (ya explicaré), lo más probable es que esta teoría sea cierta.]]></description>
                <comments>https://blog.adrianistan.eu/la-fuente-q-y-el-problema-sinoptico</comments>
                <pubDate>Thu, 16 May 2013 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Intercambio con Francia</title>
                <link>https://blog.adrianistan.eu/intercambio-con-francia</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/intercambio-con-francia</guid>
                <description><![CDATA[Recientemente he tenido un intercambio con Francia entre alumnos que estudian español en Francia y alumnos que estudian francés en España. He de decir que es una muy buena experiencia en la que se aprende mucho del idioma y de las costumbres de un país. Lo primero decir que es una experiencia cansada pero a la vez divertida y os la recomiendo a todos. Mi intercambio se realizaba con el pueblo de St. Brevin Les Pins, un pueblo cerca de Nantes.]]></description>
                <comments>https://blog.adrianistan.eu/intercambio-con-francia</comments>
                <pubDate>Fri, 19 Apr 2013 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>OpenGL y su gran futuro</title>
                <link>https://blog.adrianistan.eu/opengl-y-su-gran-futuro</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/opengl-y-su-gran-futuro</guid>
                <description><![CDATA[OpenGL es para quien no lo sabe o no está bien informado del todo una API gráfica de bajo nivel que representa el estándar para gráficos 3D. Esta API fue diseñada originalmente por Silicon Graphics Inc. (SGI en adelante) que se dedicaba a fabricar workstations. La idea surgió para tener un método más sencillo para representar gráficos 3D y a SGI le interesaba que fuera abierto porque así más programas habría y más hardware vendería. Fue presentado para Windows NT y en un principio Microsoft quería participar en OpenGL. OpenGL estaría disponible a partir de Windows 95 permitiendo las aplicaciones con OpenGL si la tarjeta gráfica lo soportaba. Entonces Microsoft presentó DirectX, un conjunto de tecnologías para desarrollar juegos en Windows y entre ellas los gráficos 3D. Microsoft defendió OpenGL para programas tipo CAD y DirectX para videojuegos. En un intento de unificar las APIs se creo Fahrenheit; sin embargo esta API nunca vio la luz. Empezó la separación de las APIs<br><h2>GPU, Windows XP y Khronos</h2><br>Por el año 2000 salieron las primeras GPU y por entonces también salió Windows XP. También se formó el Khronos Group. Empecemos por Windows XP donde el soporte claro de Microsoft era DirectX. Con el gran lanzamiento de DirectX 9 una API que ya era superior a OpenGL se desarrollaron muchos juegos. OpenGL no evolucionaba y además solía ser más lento ya que si el fabricante no proporcionaba drivers de OpenGL (algo no obligatorio) este funcionaba por software en vez de por hardware. Entonces SGI y otras empresas fundan el Khronos Group que incorpora entre sus objetivos velar por mejorar OpenGL y otras tecnologías.<br><h2>Windows Vista, DirectX 10 y el iPhone</h2><br>La siguiente versión de DirectX 10 avanzaba y solo sería compatible con Windows Vista. Todo el mundo sabe que Windows Vista fracasó y por tanto DirectX 10 perdía fuelle y que hicieron los de OpenGL... Reescribir partes de la API para que fuese igual. Así pues OpenGL no lo aprovechó. Sin embargo las cosas cambian y ese día llegó cuando se lanzó el iPhone. No soy partidario de Apple pero el iPhone y todos sus gráficos funcionaban con OpenGL, concretamente con OpenGL ES(ES significa sistemas empotrados). OpenGL volvía a resurgir. Android también nació y también usó OpenGL ES. OpenGL volvía a ser importante.<br><h2>OpenGL ES 2, la revolución</h2><br>Entonces se vió que OpenGL ES 1 era un poco malo y lento y para ello el Khronos Group definió una nueva API completamente diferente, sin retrocompatibilidad y centrada en la potencia y simpleza. Nació OpenGL ES 2, la versión de OpenGL más innovadora del momento. Eliminaba partes típicas de la API y las reemplazaba por otros sistemas. Destacan los shaders tanto de vértices como de fragmento. OpenGL ES 2 además tiene muy pocas funciones por tanto la simpleza manda sin dejar de ser potente y profesional. OpenGL ES 2 se diseña como un subconjunto de OpenGL por tanto OpenGL también necesita una renovación. Será OpenGL 3 y 4 las grandes precursoras del cambio que dejan atrás rotaciones de matrices por vertex shaders. La ventaja de los shaders es que se compilan para la GPU y dejan más rendimiento para la CPU.<br><h2>WebGL y OpenGL ES 3</h2><br>Las especificaciones de HTML5 ya comtemplaban la etiqueta canvas que permite dibujar gráficos. Primero se introdujo el 2D, más tarde se pensó en el 3D y así surgió WebGL, una API gráfica para HTML/JavaScript. Se basa en OpenGL ES 2 y por ello hace uso extensivo de matrices de vértices y shaders. También surge OpenGL ES 3 que añade mejoras a OpenGL ES 2, sin embargo actualmente no ha sido muy adoptado debido a que es bastante reciente. Actualmente OpenGL es una gran API gráfica libre y multiplataforma que funciona sobre Linux, Mac, Windows (un poco mal, observad el proyecto ANGLE), Haiku, Android, iOS, Firefox OS, BlackBerry OS, Ubuntu Phone, PlayStation 3 y más. OpenGL tiene un gran futuro sobre todo cuando se implementen las otras especificaciones del Khronos Group como OpenCL, WebCL, OpenMAX, OpenWF, OpenML, OpenVG, COLLADA y más APIs que no son tan comunes pero que se integran muy bien con OpenGL.]]></description>
                <comments>https://blog.adrianistan.eu/opengl-y-su-gran-futuro</comments>
                <pubDate>Thu, 14 Mar 2013 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Los números aleatorios</title>
                <link>https://blog.adrianistan.eu/los-numeros-aleatorios</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/los-numeros-aleatorios</guid>
                <description><![CDATA[La gente a veces te pregunta un número aleatorio para así elegir algo de entre un conjunto. Si piensas un número en ese momento pensarás que es aleatorio y lo dejarás pasar. Sin embargo vamos a entrar en una reflexión acerca de los números aleatorios y si de verdad existen. Primero vamos a hablar sobre el efecto de la aleatoriedad en el cerebro humano.<br><h2>Aleatoriedad en el cerebro humano</h2><br>Este punto es interesante y es importante aclarar primero para evitar futuros errores. El cerebro no piensa bien aleatoriamente. Si tuviésemos una máquina de números aleatorios y los oyesemos veríamos que algunos se repetirían o creeríamos ver patrones. Esto es una falsa sensación de no-aleatoriedad. Esto ocurrió con el iPod, cuando la gente seleccionaba Reproducción aleatoria veían que algunas canciones se repetían y la gente se quejó de falta de aleatoriedad. Al final se modificó el algoritmo para que fuese menos aleatorio pero no los repitiese y la gente pensó que era más aleatorio<br><h2>Generar números aleatorios</h2><br>Estará pensando como generar números aleatorios con una máquina. Si usted ha pensado poco dirá que un algoritmo, si ha pensado más verá que necesitaremos valores de entrada diferentes para ese algoritmo y no se puede hacer aleatoriedad. Realmente hay 2 métodos pseudo-aleatorios. El primero se basa en mediciones de radiación, es normal que varíe bastante pero es muy lento, el segundo ampliamente usado en la computación es el tiempo. Cada milisegundo es un número que con unas cuentas puede ser muy diferente al del milisegundo anterior. Así se hace aleatoriedad en los ordenadores actualmente. Esto tiene un inconveniente y es que si viésemos todo eso algún día se repetiría y se verían patrones por tanto es imposible generar números realmente aleatorios. Existe investigación en el campo de la física cuántica y los ordenadores cuánticos pero todavía no ha dado sus frutos<br><h2>¿Es el universo aleatorio?</h2><br>Podríamos pensar que sí, pero hemos dicho que nos ha resultado imposible hacerlo con ordenadores (ordenadores que son capaces de simular ciertas tareas del universo). Así pues, ¿existen los números aleatorios? Cuando nosotros pensamos un número ¿estamos obedeciendo un patrón?. Es posible pero también hay que pensar en el rango de ese patrón ya que no hay un límite de números en el universo porque los números son infinitos. Si los números son infinitos ¿como serían los números aleatorios reales? ¿Habría algún tope?<br><h2>Conclusión</h2><br>Los números aleatorios no existen en los ordenadores y es posible que tampoco en el universo. Pero esto plantea grandes dudas sobre nuestro futuro y si lo podremos predecir usando patrones. El tiempo dirá]]></description>
                <comments>https://blog.adrianistan.eu/los-numeros-aleatorios</comments>
                <pubDate>Mon, 11 Mar 2013 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Hablando sobre Google Code-in 2012 y proyectos</title>
                <link>https://blog.adrianistan.eu/hablando-sobre-google-code-in-2012-y-proyectos</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/hablando-sobre-google-code-in-2012-y-proyectos</guid>
                <description><![CDATA[Ya han anunciado ganadores del concurso Google Code-in 2012. No estoy elegido aunque tiene lógica porque los ganadores los eligen las organizaciones y yo no he trabajado 2 tasks con la misma organización. Visto esto he de decir que he aprendido mucho en diversos temas y me servirá mucho en el futuro. Actualmente quiero mejorar mis juegos actuales (Monopoly, Azpazeta, DivCity, El Juego de las Galaxias, Bloco), colaborar en algún concurso (Olimpiada Informática) y colaborar con algún proyecto de software libre (Simutrans, OpenTTD, Hedgewars). Además tengo que poner en funcionamiento las DivAccounts y Divel Updater y si queda algo de tiempo mejorar otros programas como Narciso, Divel Calendar, Templvs, Tú eres el protagonista, Divel Quiz, Agenda de Correos, etc. No sé si me dará tiempo pero al menos podré decir que lo intenté. Os animo a colaborar si quereis ayudarme en los proyectos de mayor envergadura.]]></description>
                <comments>https://blog.adrianistan.eu/hablando-sobre-google-code-in-2012-y-proyectos</comments>
                <pubDate>Wed, 06 Feb 2013 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Los navegadores del 2012</title>
                <link>https://blog.adrianistan.eu/los-navegadores-del-2012</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/los-navegadores-del-2012</guid>
                <description><![CDATA[<i>Este artículo está escrito originalmente para el IES Zorrilla donde colaboro de vez en cuando</i><br><br><strong>Internet Explorer -- Microsoft -- HTML: Trident -- JavaScript: Chakra</strong><br>Internet Explorer es uno de los navegadores más importantes. Su éxito radica en que desde su primera versión, ha estado incluido en Windows (y este tenía el 98% de los ordenadores). Internet Explorer ha desarrollado su propia tecnología web con los controles ActiveX, Visual Basic Script y etiquetas HTML extrañas. Por eso tiene fama de extraño y apartado debido a que no cumple el estándar web y todas las webs se tienen que modificar para funcionar con él. Además no es multiplataforma por tanto sería imposible ver webs de Internet Explorer con Mac OSX o con Linux.<br><img title="Sonrisa" src="http://ieszorrilla.centros.educa.jcyl.es//lib/javascript/tinymce/jscripts/tiny_mce/plugins/emotions/img/smiley-smile.gif" alt="Sonrisa" border="0" />Buena integración con Windows<br><img title="Triste" src="http://ieszorrilla.centros.educa.jcyl.es//lib/javascript/tinymce/jscripts/tiny_mce/plugins/emotions/img/smiley-frown.gif" alt="Triste" border="0" />Monopoliza la web<br><strong>Mozilla Firefox -- Mozilla Foundation -- HTML: Gecko -- JavaScript: SpiderMonkey</strong><br>Mozilla Firefox es la continuación de el navegador Netscape. Funciona en Windows, Mac OSX, Linux, Android y tiene ports para sistemas como NetBSD, Solaris o Haiku. Además se está desarrollando un sistema operativo de smartphone basado en Firefox, Firefox OS, donde todo es una página web. Mozilla Firefox es famoso por su respeto a los estándares web y a su filosofía de mantener la privacidad del usuario. Además a sido pionera en tecnologías ya implantadas y cuenta con una capacidad de personalización increíble gracias a extensiones (tiene tienda oficial con todo gratis), compementos y temas. Actualmente se encuentran desarrollando las aplicaciones de Firefox que servirán para Firefox, Firefox para Android y para Firefox OS. Tiene fama de que gasta mucho, pero actualmente gasta muchísimo menos que antes y esta a igual nivel que Chrome<br><img title="Sonrisa" src="http://ieszorrilla.centros.educa.jcyl.es//lib/javascript/tinymce/jscripts/tiny_mce/plugins/emotions/img/smiley-smile.gif" alt="Sonrisa" border="0" />Siempre funcionará con estándares.<br><img title="Triste" src="http://ieszorrilla.centros.educa.jcyl.es//lib/javascript/tinymce/jscripts/tiny_mce/plugins/emotions/img/smiley-frown.gif" alt="Triste" border="0" />Puede tener fallos en tecnologías propietarias<br><strong>Google Chrome -- Google -- HTML: WebKit -- JavaScript: V8</strong><br>El navegador de Google ha crecido muy rápido. Está disponible de manera oficial en Windows, Mac OSX y Linux. Además el navegador Chromium que es Chrome pero con código abierto (como Firefox). Tiene un motor de JavaScript muy rápido y usa muchos hilos. Esto provoca que parezca que consuma poco aunque no es así. Además hace que páginas muy pequeñas y muy sencillas tarden lo mismo que las grandes. Su capacidad de personalización es muy inferior pero cuenta con la Chrome Web Store. que proporciona apps.<br><img title="Sonrisa" src="http://ieszorrilla.centros.educa.jcyl.es//lib/javascript/tinymce/jscripts/tiny_mce/plugins/emotions/img/smiley-smile.gif" alt="Sonrisa" border="0" />Muy rápido en JavaScript<br><img title="Triste" src="http://ieszorrilla.centros.educa.jcyl.es//lib/javascript/tinymce/jscripts/tiny_mce/plugins/emotions/img/smiley-frown.gif" alt="Triste" border="0" />Poca seguridad y privacidad<br><strong>Opera -- Opera -- HTML: Presto -- JavaScript: Carakan</strong><br>Un desconocido en España pero muy usado en otros países. También usa estándares es también usado en móviles y es el navegador de la Wii y la Nintendo DSi. Poca capacidad de personalización, pero líder en algunas cosas.<br><img title="Sonrisa" src="http://ieszorrilla.centros.educa.jcyl.es//lib/javascript/tinymce/jscripts/tiny_mce/plugins/emotions/img/smiley-smile.gif" alt="Sonrisa" border="0" />Alternativa a los otros navegadores<br><img title="Triste" src="http://ieszorrilla.centros.educa.jcyl.es//lib/javascript/tinymce/jscripts/tiny_mce/plugins/emotions/img/smiley-frown.gif" alt="Triste" border="0" />Se usa muy poco<br><strong>Safari -- Apple -- Motor: WebKit -- JavaScript: Nitro</strong><br>Desarrollado por Apple se lleva muy bien con Mac OSX e iOS. Usa el mismo motor de Chrome (realmente lo creo Safari). En Windows tiene resultados pésimos y no hay versión de Linux. Se lleva bien-mal con los estándares porque algunas cosas acepta y otras no.<br><img title="Sonrisa" src="http://ieszorrilla.centros.educa.jcyl.es//lib/javascript/tinymce/jscripts/tiny_mce/plugins/emotions/img/smiley-smile.gif" alt="Sonrisa" border="0" />El mejor navegador para Mac OSX<br><img title="Triste" src="http://ieszorrilla.centros.educa.jcyl.es//lib/javascript/tinymce/jscripts/tiny_mce/plugins/emotions/img/smiley-frown.gif" alt="Triste" border="0" />Muy cerrado y algo retrasado en estándares<br><strong>El resto</strong><br>Estos son los 5 navegadores principales, ahora bien, existen muchos más navegadores, sobre todo derivados de estos 5 y alguno independiente.<br><strong>Konqueror -- KHTML -- KJS</strong><br><strong>Epiphany -- WebKit y Gecko -- ?</strong><br><strong>Midori -- WebKit -- ?</strong><br><strong>Maxthlon -- Trident y WebKit -- ?</strong><br>Así pues decídete por el que creas conveniente, yo uso Mozilla Firefox ¿y tú?<strong><br></strong>]]></description>
                <comments>https://blog.adrianistan.eu/los-navegadores-del-2012</comments>
                <pubDate>Sat, 29 Dec 2012 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Consejo Escolar</title>
                <link>https://blog.adrianistan.eu/consejo-escolar</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/consejo-escolar</guid>
                <description><![CDATA[Bueno antes de nada me presento en estas primeras entradas del blog, soy Adrián Arroyo Calle y soy de Valladolid, España. Hoy voy ha hablaros sobre el consejo escolar. Pero vamos a ver qué es el Consejo Escolar, según Wikipedia el consejo escolar es: <b> </b><br><blockquote class="tr_bq"><b>Consejo escolar</b> es el nombre que en distintos <a class="mw-redirect" title="Sistemas educativos" href="http://es.wikipedia.org/wiki/Sistemas_educativos">sistemas educativos</a> se da a una institución colegiada cuyas funciones se extienden al control de la gestión de los <a class="mw-redirect" title="Centros escolares" href="http://es.wikipedia.org/wiki/Centros_escolares">centros escolares</a>, teniendo su mayor nivel decisiorio en cuestiones no estrictamente docentes.</blockquote><br>En mi instituto se celebraban elecciones este año y decidí presentarme. Fui a secretaría y allí, con bastante desorden consiguieron que pudiese participar. Más tarde vi los candidatos, estos eran 3 (conmigo). No les conocía, pero enseguida conocí al primero, vino a ver las listas al mismo tiempo que yo. El otro no le conocí y sigo sin conocerle. Doy la noticia y hablo con profesores y alumnos (una especie de campaña) y llega el día de la verdad, día 19 de Noviembre. A primera hora entregan papeletas por las clases (sin el papel, solo el sobre por una falta de organización tremenda). A mi clase nos toca votar sobre las 11:45. Se pueden elegir 2 candidatos (pues hay dos plazas libres) y somos 3. La mayoría de la gente vota a 2 personas y todos a los que conozco me votan. Hoy ya han sido contados los votos y veo una cosa bastante...296 votos lo que quiere decir que no solo no soy elegido sino que el que no conozco (y nadie por lo visto) ¡me ha ganado! La verdad es que es bastante igualado pero no me he quedado satisfecho con el resultado aunque sea suplente. Dentro de 2 años me volveré a presentar...]]></description>
                <comments>https://blog.adrianistan.eu/consejo-escolar</comments>
                <pubDate>Tue, 20 Nov 2012 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>Presentación</title>
                <link>https://blog.adrianistan.eu/presentacion</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/presentacion</guid>
                <description><![CDATA[Hola, soy Adrián y este es mi blog. En realidad es el segundo pues tenía otro en hazblog.com que no usaba. Espero que este sí que lo use. Quizá me conozcas personalmente o quizás me conozcas de internet. En internet tengo cuenta de Google+, Tuenti, YouTube y Launchpad. Espero que mis posts te interesen. Algunos serán de opinión y de crítica, mientras que otros serán informativos y casi tutoriales.]]></description>
                <comments>https://blog.adrianistan.eu/presentacion</comments>
                <pubDate>Thu, 25 Oct 2012 00:00:00 +0000</pubDate>
            </item>
        
            <item>
                <title>FAQs</title>
                <link>https://blog.adrianistan.eu/faqs</link>
                <guid isPermaLink="true">https://blog.adrianistan.eu/faqs</guid>
                <description><![CDATA[<p>En esta secci&oacute;n respondo a las preguntas frecuentes (y no tan frecuentes) que surgen respecto al blog.</p>
<h2>&iquest;Qu&eacute; es esto?</h2>
<p>&iexcl;Adrianist&aacute;n! Se trata del blog en espa&ntilde;ol donde me dedico a hablar de cosas poco interesantes pero que me entretienen. Principalmente de programaci&oacute;n, pero tambi&eacute;n hay algo de filosof&iacute;a, f&iacute;sica y <em>Rep&uacute;blica de Adrianist&aacute;n</em>.</p>
<h2>&iquest;Qui&eacute;n eres?</h2>
<p>Soy Adri&aacute;n Arroyo Calle, soy de Valladolid (Castilla). Actualmente estudio Ingenier&iacute;a Inform&aacute;tica en la Universidad de Valladolid. Creo que el mundo es impresionante y me gustar&iacute;a conocer todo sobre &eacute;l. Me gusta mucho viajar, conocer datos est&uacute;pidos</p>
<h2>&iquest;Cu&aacute;l es tu lenguaje de programaci&oacute;n favorito?</h2>
<p>La verdad es que no tengo un lenguaje favorito, aunque ahora mismo el lenguaje en el que soy m&aacute;s productivo es JavaScript. Tambi&eacute;n he programado en Java, Python, Rust, C, C#, SQL, C++ (hace tiempo) y siempre trato de ir aprendiendo cosas nuevas (Haskell, Prolog).</p>
<h2>&iquest;D&oacute;nde trabajas?</h2>
<p>Actualmente estudio <a href="http://inf.uva.es">Ingenier&iacute;a Inform&aacute;tica en la UVa</a>. Tambi&eacute;n soy miembro de la asociaci&oacute;n <a href="http://bestvalladolid.org">BEST Valladolid</a>.</p>
<h2>&iquest;Tienes m&aacute;s blogs?</h2>
<p>S&iacute;, tambi&eacute;n escribo en Phaser.js Hispano, aunque no escribo tanto all&iacute;</p>
<h2>&iquest;Qu&eacute; m&uacute;sica te gusta?</h2>
<p>La verdad es que me gusta la m&uacute;sica en general, sin demasiadas distinciones. Sin embargo, si tengo que elegir me quedar&iacute;a con mis bandas sonoras: Ennio Morricone, Vangelis, Alexandre Desplat,&nbsp; Yann Tiersen, etc (John Williams no). Tambi&eacute;n me gusta bastante Mike Oldfield y el g&eacute;nero del Latin Jazz. Pero en mi biblioteca musical hay de todo: Nino Bravo, Nina Simone, Nach, Paco Pil, Philip Glass, Pink Floyd, Bach, Johnny Cash, Sole&aacute; Morente, ... Muchas veces depende del momento.</p>
<h2>&iquest;Pel&iacute;cula preferida?</h2>
<p>Algunas de mis pel&iacute;culas favoritas son: <em>El Sentido de la Vida</em> de los Monty Python, <em>La Naranja Mec&aacute;nica</em>, <em>Intocable</em>, <em>The Royal Tenenbaums</em>, <em>&iexcl;Bienvenido Mr. Marshall!</em> y <em>Qu&eacute; bello es vivir</em>. Tambi&eacute;n fui j<a href="https://blog.adrianistan.eu/2017/11/12/cronica-la-62-seminci/">urado joven del festival de cine de Valladolid</a>, la SEMINCI.</p>
<h2>&iquest;Vim o Emacs?</h2>
<p>Vim claramente (&iquest;o acaso ten&eacute;is 20 dedos en cada mano?). Aunque suelo programar en Visual Studio Code.</p>
<h2>&iquest;Windows o Linux?</h2>
<p>Suelo trabajar con Linux (concretamente con Debian) porque me gusta m&aacute;s, pero he de reconocer que Windows no es tan mal sistema operativo.</p>
<h2>&iquest;Qu&eacute; escritorio usas en Linux?</h2>
<p>GNOME 3, con algunas extensiones por defecto. Lo considero uno de los escritorios m&aacute;s productivos que existen actualmente y con buen dise&ntilde;o.</p>
<h2>&iquest;Espacios o tabuladores?</h2>
<p>Tabuladores que en el editor se convierten a espacios</p>
<h2>&iquest;En qui&eacute;n te inspiras?</h2>
<p>Aunque intento seguir mi propio camino, he de decir que hay ciertos personajes que me han inspirado durante mi vida. Richard Feynman, Bertrand Russell, mis compa&ntilde;eros de BEST Valladolid, ...</p>
<h2>&iquest;Puedo publicar en Adrianist&aacute;n?</h2>
<p>S&iacute;, claro, cualquiera puede aportar. Si eres una empresa, tendr&aacute;s que pagar. C<a href="https://blog.adrianistan.eu/contacto/">ontacta conmigo para m&aacute;s detalles.</a></p>
<h2>&iquest;Puedo anunciarme en Adrianist&aacute;n?</h2>
<p>S&iacute; por supuesto. <a href="https://blog.adrianistan.eu/contacto/">Contacta conmigo</a> y seg&uacute;n el tama&ntilde;o/tipo del anuncio veremos el precio.</p>
<h2>&iquest;Puedes trabajar para m&iacute;?</h2>
<p>Actualmente estudio y actualmente no estoy buscando trabajo</p>
<h2>&iquest;Qu&eacute; fue de Divel?</h2>
<p>Fue como empec&eacute; con la programaci&oacute;n, mi <em>empresa</em>. Actualmente ya no uso m&aacute;s ese pseud&oacute;nimo para publicar mis programas.</p>
<h2>&iquest;Qu&eacute; pas&oacute; con NextDivel?</h2>
<p>El objetivo de NextDivel era ver hasta donde pod&iacute;a llegar programando un sistema operativo de cero. Ahora que he estudiado m&aacute;s el funcionamiento de los sistemas operativos solo puedo decir que NextDivel hace demasiadas pocas cosas y las que hace, no las hace bien del todo.</p>
<h2>&iquest;Cada cu&aacute;nto publicas?</h2>
<p>En Adrianist&aacute;n no hay un horario fijo de publicaciones. A veces hay publicaciones muy seguidas unas de otras y luego un largo tiempo sin ellas. Realmente todo depende de si tengo algo interesante que contar y de si tengo tiempo y ganas para ello.</p>
<h2>&iquest;Cu&aacute;ndo vas a hacer un post sobre X?</h2>
<p>Cuando sea interesante para m&iacute; poder contaros algo sobre X. No suelo seguir demasiado las modas tecnol&oacute;gicas, as&iacute; que el blog estar&aacute; condenado siempre a recibir menos visitas que otros.</p>
<h2>&iquest;Podemos quedar y charlar?</h2>
<p>S&iacute;, claro. Vivo en Valladolid y normalmente puedo quedar. Una meta que me puse hace tiempo era la de conocer cada d&iacute;a a una persona distinta, algo que nunca ha ocurrido, pero por algo se les llama metas. Si hablas ingl&eacute;s bien, mejor, as&iacute; puedo practicar.</p>
<h2>&iexcl;Tengo m&aacute;s preguntas!</h2>
<p>&iexcl;Bien! Usa el <a href="https://blog.adrianistan.eu/contacto/">formulario de contacto</a> y hablamos.</p>]]></description>
                <comments>https://blog.adrianistan.eu/faqs</comments>
                <pubDate>Sat, 01 Jan 2000 00:00:00 +0000</pubDate>
            </item>
        
    </channel>
</rss>