VISITAS:

jueves, 22 de diciembre de 2016

¿Typescript en 30 minutos?

¿Es posible aprender Typescript en 30 minutos o menos? Depende, si ya conoces algo de Javascript y también algún lenguaje de programación orientado a objetos como Java o C#, seguro que sí. Si no, te costará un poco más, pero no mucho. Te lo voy a demostrar en este post. Abre una nueva pestaña en playground Pon el cronómetro en marcha y empieza a leer... Empezamos con el lenguaje de moda y, quizás, el lenguaje del futuro para la web.

Qué es Typescript

Typescript es un lenguaje de programación desarrollado y mantenido por Microsoft. La primera versión pública salió en Octubre de 2012 y la presentó Anders Hejlsberg, el creador de lenguajes tan conocidos como Turbo Pascal, Delphi y C#.
Typescript es un superconjunto de Javascript, que no se utiliza directamente como tal, sino que se convierte a Javascript, que ya se puede utilizar en cualquier navegador o en NodeJS. Cualquier código Javascript válido es también código Typescript válido. Soporta Javascript ES5, ES2015 y ES2016, e incluso está previsto el soporte ES2017.
Typescript proporciona chequeo de tipos, interfaces, clases y módulos, entre otros mecanismos propios de los lenguajes orientados a objetos.
Existe una web que proporciona los ahora tan de moda, playground para aprender y experimentar con el lenguaje Typescript.
Veamos un poco más despacio las nuevas características del lenguaje Typescript.

Tipos

Typescript soporta chequeo de tipos estático que permite detectar muchos errores en la fase de desarrollo.
Los chequeos de tipos se realizan en la fase de compilación (realmente conversión) de Typescript a Javascript.
Las anotaciones de tipos son opcionales. Si no se especifica un tipo, entonces no se efectuará ningún chequeo.
Los tipos soportados son: number, boolean, string y los arrays de estos tipos. También existe el tipo any para cualquier tipo, incluidas las estructuras de datos u objetos.
Ejemplo:

function multiply(a: number, b: number) : number {
    return a * b;
}

Funciones arrow

En Javascript podemos crear funciones anónimas de la siguiente forma:

setTimeout(function() {
    console.log("Timed out function");
}, 500);

Typescript permite una sintaxis, denominada arrow, para definir de una forma más sencilla las funciones anónimas:

setTimeout( () => { console.log("Timed out function"); }, 500);

Variables con ámbito de bloque

En Javascript, las variables declaradas con var tienen ámbito de la función donde se declaran. De hecho, aunque se declare la función en un punto intermedio de la función, Javascript traslada esa declaración al principio de la función. Esto puede llevar a situaciones extrañas, como por ejemplo:

var x = 3;
function show(param) {
    if (param == true) {
        return x;
    } else {
        var x = 4;
        return x;
    }
}
console.log(show(true));

Aunque podríamos pensar que el resultado en la consola es 3, realmente veremos "undefined". ¿Por qué? Porque el motor Javascript traslada la declaración var x al principio de la función. Es como si fuera:

var x = 3;
function show(param) {
    var x;
    if (param == true) {
        return x;
    } else {
        x = 4;
        return x;
    }
}
console.log(show(true));

Así se entiende porqué el valor retornado es undefined.

Typescript introduce la declaración de variables con let y con const.
let hace que la variable tenga ámbito de bloque y const es similar pero sólo se puede inicializar el valor y luego no se puede modificar.

Cambiemos el ejemplo anterior utilizando let y quedará mucho más claro:

let x = 3;
function show(param) {
    if (param == true) {
        return x;
    } else {
        let x = 4;
        return x;
    }
}
console.log(show(true));

En este caso, el valor mostrado en consola es 3, como era de esperar.

Se recomienda utilizar siempre let y const en lugar de var.

Patrones de cadenas

Muchas veces necesitamos concatenar cadenas con valores de variables:

let str : string = "( " + a + ", " + b +" )";

Con Typescript podemos conseguir el mismo efecto mediante patrones de cadenas, utilizando la comilla que va hacia la izquierda:

let str : string = `(  {a} , {b} )`;

Además, los patrones de cadenas en Typescript también permiten cadenas multilínea:

let str : string = `
    Esta cadena tiene
    múltiples líneas
    y así se guardará`;

Parámetros por defecto y opcionales

En Typescript podemos especificar valores por defecto para los parámetros de una función:

function add( a : number, b : number = 0): number  {
    return a + b;
}

add(3, 4);     // 7
add(3);        // 3

Los parámetros con valores por defecto se aplican cuando se pasa un valor undefined (no cuando se pasa null, y siempre son los últimos.
En Javascript todos los parámetros de una función son opcionales. Si un parámetro no se pasa al llamar a la función, la función recibe undefinedSin embargo, en Typescript se deben especificar los parámetros que son opcionales poniendo ? después del nombre del parámetro:

function add( a : number, b? : number): number  {
    if (b) {
        return a + b;
    } else {
        return a;
    }
}

add(3, 4);     // 7
add(3);        // 3

En Javascript se puede pasar un número indeterminado de argumentos a una función y ésta los recoge utilizando la variable llamada arguments. En Typescript esto se puede hacer con la siguiente sintaxis:

function add( a : number, ...other: number[] ) {
    let total = a;
    for (int i = 0; i < other.length; i++) {
        a += other[i];
    }
}

add(3, 4, 5, 6);    // 18

Interfaces

Los interfaces son una forma de definir contratos:

interface Graphic {
    toString() : string;
    getX() : number;
    getY() : number;
    setX(x:number) : void;
    setY(y:number) : void;
    draw() : void;
}

Este interfaz define las operaciones que tendrán los objetos gráficos.

Utilización de este interfaz:

function move(obj : Graphic, x : number, y : number) : void {
    obj.setX(x);
    obj.setY(y);
}

Para obligar a una clase a implementar la interfaz:

class Point implements Graphic {
    ...
}

Clases

La sintaxis para la definición de clases en Typescript es muy similar a la de los lenguajes orientados a objetos tradicionales:

class Rectangle {
    protected x : number;
    protected y : number;
    protected w : number;
    protected h : number;

    constructor(x: number, y: number, w: number, h: number) {
        this.x = x;
        this.y = y;
        this.w = w;
        this.h = h;
    }

    scale(factor: number): void {
        this.w = this.w * factor;
        this.h = this.h * factor;
    }
}

Podemos crear objetos de una clase utilizando new:

let r = new Rectangle(3, 2, 60, 50);
r.scale(1.3);

Se puede extender una clase mediante herencia:

class Square extends Rectangle {
    private name : string;
    constructor(x: number, y: number, s: number, name: string) {
        super(x, y, s, s);
        this.name = name;
    }
    getName() {
        return this.name;
    }
}

Los miembros de una clase (atributos y métodos) soportan los modificadores private, protected y public para definir el acceso. Por defecto, todos los miembros son public, y se puede acceder a ellos desde cualquier sitio. Los miembros protected sólo se pueden acceder desde la propia clase y desde sus clases derivadas. Por último, los miembros private sólo se pueden acceder desde la propia clase.

En Typescript se pueden definir métodos get y set para interceptar el acceso a atributos de una clase:

class Point {
    private _x : number;
    private _y : number;
    constructor(x: number, y: number) {
        this._x = x;
        this._y = y;
    }
    public get x() : number {
        console.log("getting x value");
        return this._x;
    }
    public set x(v: number) {
        console.log("setting x value");
        this._x = v;
    }
    public get y() : number {
        return this._y;
    }
    public set y(v: number) {
        this._y = v;
    }
}

let point = new Point(3, 5);
point.x = 9;
console.log(point.x);

En la consola de Javascript veremos lo siguiente:

setting x value
getting x value
9

Se pueden definir miembros estáticos en las clases (tanto atributos como métodos):

class Types {
    static VALUE_1: number = 1;
    static VALUE_2: number = 2;
    static VALUE_3: number = 3;
    static add(a: number, b: number): number {
        return a + b;
    }
}

Types.add(Types.VALUE_1, 5);   // 6

Por último, en Typescript podemos definir clases abstractas, las cuales no se pueden instanciar, pero podemos definir clases que heredan de ellas:

abstract class BaseClass {
    protected _name: string;
    abstract toString(): string;
    public set name(n: string) {
        _name = n;
    }
}

Módulos

Cuando un proyecto empieza a crecer, suele ser una buena idea dividir nuestro código en ficheros diferentes, también llamados módulos. En Typescript cada módulo lo pondremos en un fichero.
Si queremos exportar algo desde un módulo, ya sea un interfaz, una clase, una función, etc, utilizamos la palabra clave export:
 
Módulo para validación de strings:

// stringvalidator.ts
export interface StringValidator {
    isValid(s: string) : boolean;
}

Otro módulo para validación de código postal:

// zipvalidator.ts
export class ZipValidator implements StringValidator {
    isValid(n: string) : boolean {
        return .......
    }
}

Podemos hacer un módulo de validaciones que exporte a los otros dos:

// validators.ts
export * from "./stringvalidator";
export * from "./zipvalidator";

Para importar desde otro módulo:

import { ZipValidator } from "./validators"
let v = new ZipValidator();
v.isValid("28041");

Se puede importar cambiando el nombre (una especie de alias):

import { ZipValidator as Zip } from "./validators"

Finalmente, podemos importar todo el contenido de un módulo y meterlo en una variable:

import * as validators from "./validators"
let s = new validators.StringValidator();

Genéricos

Las clases genéricas permiten:

  • Mayor seguridad de tipos: un Array sólo puede contener cadenas
  • El compilador chequea los tipos de los genéricos
  • Son más rápidas que utilizar cualquier tipo libremente
  • Podemos escribir código que es aplicable a muchos tipos
Ejemplo:

class Box {
    private _value: T;
    set value(v: T) {
        this._value = v;
    }
    get value() {
        return this._value;
    }
}

let strBox = new Box();
strBox.value = "hola";


























lunes, 12 de diciembre de 2016

NativeScript y múltiples tamaños de pantalla

NativeScript: múltiples tamaños de pantalla

Hoy voy a escribir sobre NativeScript y sobre el soporte a múltiples tamaños de pantalla.

Cuando definimos la UI con NativeScript, situamos los objetos y los dimensionamos utilizando puntos (dp). Estos puntos no se corresponden directamente con pixels de la pantalla. Cuando el móvil o tablet ejecuta nuestro programa, convierte estos puntos en pixels reales de la pantalla (px).

Cada dispositivo tiene una densidad de pixels (pixels por pulgada, dpi) diferente. Hay dispositivos con 90 dpi y hay dispositivos con 320 dpi.

Se establece el siguiente criterio: suponemos una densidad de pixels de 160 dpi, y sobre esa densidad definimos los puntos. O sea, los puntos se corresponden exactamente con pixels en pantallas con 160 dpi. Pero en pantallas de otra densidad, los puntos hay que transformarlos en pixels (si tenemos más de 160 dpi, se pintarán varios pixels por cada punto, y si tenemos menos de 160 dpi, necesitaremos varios puntos para completar un pixel).

En un dispositivo con densidad dpi, la conversión a pixels del dispositivo se hace con la siguiente fórmula:

    px = dp * dpi / 160

Al programar, se posiciona y se dimensiona todo en puntos, y luego esto se convierte a pixels en cada dispositivo según la fórmula anterior.

Por ejemplo, si creamos un círculo en la posición (300, 200) con 100 puntos de radio, se dibujará de la siguiente forma:

    - en un dispositivo de 160 dpi: radio 100 pixels, posición (300, 200)
    - en un dispositivo de 320 dpi: radio 200 pixels, posición (600, 400)
    - en un dispositivo de 220 dpi: radio 137 pixels, posición (412, 275)


el tamaño y posición del círculo será exactamente igual en ambos dispositivos.

Esto resuelve el problema de que cada dispositivo tiene una densidad de pixels distinta, y los objetos gráficos tendrán el mismo tamaño en todos los dispositivos.

Sin embargo, esto no resuelve el problema de los distintos tamaños de pantalla. Tenemos pantallas desde 3.5" hasta 5.5" y más. Aunque el círculo del ejemplo anterior se mostraría en la misma posición y con igual tamaño en cualquier dispositivo, sin embargo, en función del tamaño físico de la pantalla, el círculo estaría en el centro, o hacia la izquierda o hacia la derecha.

Este problema se resuelve con los layouts que posicionan los objetos y los redimensionan en función del tamaño de la pantalla.

Se considera que un dispositivo es una tablet cuando su dimensión más pequeña es superior a 3,75", o sea, cuando hay más de 600 dp en su dimensión más pequeña.

    Si la dimensión más pequeña es mayor que 3,75" (600 dp) => es una tablet

NativeScript sigue el siguiente criterio de nombrado para los ficheros:

    filename.qualifier*.extension

Por ejemplo: main_page.android.xml

Los cualificadores pueden ser:

  • cualificadores de orientación: port, land
  • cualificadores de plataforma: android, ios
  • cualificadores de tamaño de pantalla: minWHxxx la dimensión más pequeña debe ser al menos xxx dp's, minWxxx el ancho debe ser al menos xxx dp's, minHxxx el alto debe ser al menos xxx dp's


Con este criterio, podemos definir diseños de pantalla y estilos específicos para una orientación, para un tamaño o para un sistema operativo.

miércoles, 3 de agosto de 2016

Redis sorted sets

Qué son los sorted sets

Una de las estructuras de datos más potentes que tiene redis son los sorted sets (en castellano, conjuntos ordenados).
Los sorted sets son similares a los sets, o sea, son una colección de elementos (strings) únicos (no repetidos). La diferencia es que cada miembro de una lista ordenada tiene un score (puntuación), un valor en punto flotante que se utiliza para mantener ordenados los elementos, desde el más pequeño al más grande.
En resumen, un sorted set es un conjunto de elementos con un valor, que se mantiene ordenado por el valor.
Los elementos de un sorted set son únicos, pero los valores se pueden repetir.

Las operaciones de añadir, actualizar y borrar elemento+valor en un sorted set tienen un orden de computación de log(N), donde N es el número de elementos que tiene el set. O sea, es computacionalmente muy eficiente.

Ejemplos de uso

Ejemplos que se pueden modelar con sorted sets:

  • lista de records:
    • ZADD para añadir un nuevo record
    • ZRANGE para obtener las mejores puntuaciones
    • ZSCORE para saber los puntos de un usuario
    • ZRANK para saber el ranking de un usuario
  • indexar otros datos de redis, por ej, datos de usuarios
    • En un sorted set se puede almacenar element=userid y score=edad
    • ZRANGEBYSCORE para sacar los usuarios en un rango de edades

Ordenación

Los elementos en un sorted set se mantienen (siempre) ordenados, no se ordenan cuando se realiza una petición.
La ordenación sigue las siguientes reglas, suponiendo dos elementos A y B:
  • Si A.score != B.score, entonces se ordenan por score
  • Si A.score == B.score, entonces se ordenan lexicográficamente, es decir, por el elemento (A, B)

Comandos

A continuación se explican los comandos usados más frecuentemente para sorted sets. Aunque hay algunos más que se pueden consultar en: comandos redis para sorted sets

ZADD set-name score element

Añade un elemento con score a un sorted-set

ZRANGE set-name start stop [withscores]

Devuelve los elementos en las posiciones entre start y stop (inclusive). Los elementos van ordenados de menos a mayor (si se desea el orden inverso, utilizar ZREVRANGE). El primer elemento de la lista es el 0. Opcionalmente, WITHSCORES, puede devolver también el score asocado a cada elemento retornado.

ZRANGEBYSCORE set-name min max [WITHSCORES] [LIMIT offset count]

Devuelve los elementos con un score comprendido entre min y max (inclusive) ordenados de menos a mayor score (si se desea el orden inverso, utilizar ZREVRANGEBYSCORE). Se pueden utilizar los valores -inf y inf para min y max. Opcionalmente, WITHSCORES, puede devolver también el score asocado a cada elemento retornado. Opcionalmente también, se puede limitar el número de valores retornados con LIMIT (offset sería 0 y count el número máximo de elementos a retornar).

ZREM set-name element

Elimina un elemento del sorted set.

ZSCORE set-name element

Devuelve el score de un elemento.

ZCARD set-name

Devuelve el número de elementos en un sorted set.

ZCOUNT set-name min max

Devuelve el número de elementos con scores entre min y max.


miércoles, 30 de marzo de 2016

Funcionamiento de Redis Sentinel

Características de sentinel

Sentinel da alta disponibilidad a redis, permitiendo a redis recuperarse de algunos fallos sin intervención humana.
Estas son las características de sentinel:

  • Monitorización: sentinel está continuamente monitorizando si los redis master y slave están arriba y funcionando.
  • Notificación: sentinel puede avisar a los administradores del sistema o a otros programas, de caídas de instancias redis.
  • Failover automático: si la instancia master de redis no está funcionando adecuadamente, sentinel puede iniciar un proceso de failover donde un slave es promocionado a master y los demás slaves se reconfiguran para utilizar el nuevo master
  • Proveedor de configuración: sentinel actúa como descubridor de la instancia master de redis. Los clientes se conectan a sentinel, y éste les indica a qué instancia de redis se tienen que conectar. Cuando se realiza un failover, sentinel informa a sus clientes del nuevo master.

Sentinel es un sistema distribuido

Sentinel funciona de forma distribuida, es decir, varias instancias de sentinel se comunican entre sí cooperando. Las ventajas de tener varios sentinel ejecutando conjuntamente son:
  • la detección de fallos se realiza cuando varias instancias sentinel acuerdan que la instancia master ya no está disponible. Esto disminuye la probabilidad de falsos positivos.
  • sentinel puede funcionar incluso aunque no todas las instancias de sentinel estén disponibles, haciendo al sistema más robusto.

Ejecutar sentinel

Sentinel se ejecuta de la siguiente forma:

    redis-sentinel sentinel-config-file

Es obligatorio especificar un fichero de configuración para ejecutar sentinel, ya que este fichero puede ser reescrito por sentinel para guardar el estado actual, y se utilizará después de una caída. Además, el proceso sentinel tiene que tener permisos de escritura en este fichero.
Las distintas instancias de sentinel se comunican entre sí por el puerto configurado en sentinel.

Criterios de despliegue de sentinel

  1. Se recomienda desplgar al menos 3 instancias de sentinel
  2. Las 3 instancias de sentinel deben ejecutar en máquinas independientes desde el punto de vista de fallos
  3. No se garantiza que todas las escrituras permanezcan después de la caída de un master, ya que redis utiliza un sistema asíncrono de replicación
  4. No todas las librerías cliente soportan sentinel (jedis sí lo soporta)

Configuración de sentinel

La configuración mínima de una instancia sentinel sería como la siguiente:
sentinel monitor  127.0.0.1 6379 2
sentinel down-after-milliseconds  60000
sentinel failover-timeout  180000
sentinel parallel-syncs  1 1
Se especifica el nombre de un grupo de instancias redis. Este nombre es el que se utilizará para el grupo. Se especifica también la IP de la instancias redis master. No es necesario especificar las instancias slaves de redis, ya que sentinel es capaz de descubrirlas (preguntando al master). Cuando sentinel autodetecta esclavos, los escribe en su fichero de configuración:

    sentinel known-slave my_redis_group 168.190.1.45 6379

También se reescribe el fichero de configuración cuando después de un failover, cambia el master y cada vez que se descubre un nuevo sentinel:

    sentinel known-sentinel my_redis_group 168.190.1.47 26379 xxxxxxx

Detección de master caído

El último argumento de sentinel monitor, un 2 en este ejemplo, se denomina quorum, y su significado es el siguiente:

  • quorum es el número mínimo de instancias sentinel que es necesario que acuerden que un master no está disponible.
  • quorum sólo se utiliza para detectar un fallo del master, no para iniciar un failover. Para realmente iniciar un failover, una de las instancias sentinel es elegida como líder, pero debe ser autorizada para iniciar el failover. Esto sólo ocurre con el voto de la mayoría de las instancias sentinel.
Se puede entender con un ejemplo. Tenemos 5 instancias de sentinel vigilando un master reds y el quorum es 2. Si dos instancias de sentinel detectan que el master se ha caído, entonces uno de los dos es elegido para intentar iniciar el proceso de failover. Pero es necesario que al menos 3 instancias estén de acuerdo en iniciar el failover para realmente llevarlo a cabo.
Ya que es necesario que la mayoría de los sentinel decidan iniciar un failover, se RECOMIENDA/OBLIGA que haya al menos 3 instancias de sentinel en 3 máquinas distintas.

Otras opciones de sentinel

Las otras opciones de sentinel tienen el siguiente formato:

    sentinel

  • down-after-milliseconds es el tiempo que una instancia de redis no es alcanzable (no responde a pings o responde con error) para que sentinel piense que está caída
  • parallel-syncs determina el número de slaves que pueden ser reconfigurados en paralelo para usar el nuevo master después de un failover

Pérdidas de datos en failover

Ya que redis replica asíncronamente (no espera la confirmación), siempre hay un riesgo de perder algún dato cuando se cae el master, ya que éste puede caerse justo después de una escritura y justo antes de replicar esa escritura en los slaves.
Otro caso que puede ocurrir es que el master no se caiga, pero se interrumpa su conexión de red con los slaves. Entonces, podría ocurrir que los sentinel pensaran que se ha caído el master (ya que no responde, aunque realmente no se ha caído) y promocionaran a otra instancia de redis como master. El problema es que si había clientes conectados al master anterior y que no han perdido la conexión con él, pueden seguir escribiendo datos en el antiguo master. Mientras tanto, el master nuevo también puede recibir nuevos datos. Los datos escritos en el master antiguo se perderán cuando la conexión se arregle, ya que éste va a convertirse en slave y va a sincronizarse con el nuevo master.
Este problema puede reducirse bastante utilizando la siguiente configuración:

    min-slaves-to-write 1
    min-slaves-max-lag 10

Con esta configuración, una instancia redis actuando como master, dejará de aceptar escrituras si no puede escribir en al menos un slave en un máximo de 10 segundos. De esta forma, el antiguo master dejará de funcionar en 10 segundos. La desventaja es que si se pierde la conexión con los slaves, la instancia master dejará de funcionar.












lunes, 24 de agosto de 2015

SWIFT (V) : Control de flujo

Sea cual sea el lenguaje de programación en el que estemos trabajando, el desarrollo de aplicaciones es siempre un ejercicio de lógica: tomar decisiones en función de ciertas condiciones y ejecutar múltiples veces cierta lógica sobre unos datos. Todo esto se suele denominar control de flujo, y swift tiene su propia sintaxis, no muy diferente por cierto a la de otros lenguajes.

Sentencia for

Swift ofrece dos tipos de for: condition-increment y for-in

Condition-increment for

La sintaxis es similar (aunque no idéntica) a otros lenguajes C-like:

    for var i = 0; i < 15; i++ {
        println(i)
    }

For-in for

Esta estructura de control permite iterar por los datos de una colección (las colecciones se explican más adelante) o por un rango numérico.
La sintaxis es la siguiente:

    for nombre in colección_o_rango {
        ....
    }

Por ejemplo:

    for i in 1...5 {
        println(i)
    }

Este tipo de for también sirve para iterar por los caracteres de un string:

    for c in "Esto es un ejemplo" {
    }

La variable no es obligatorio declararla. Esto puede ser útil cuando, por ejemplo, sólo se quieren contar los elementos de la colección. En este caso se pone el carácter _:

    for  _  in "Esto es una cadena" {
        counter++
    }

Sentencia while

El bucle for se utiliza cuando sabemos de antemano el número de repeticiones. Pero en otras ocasiones necesitamos repetir hasta que se cumpla una determinada condición y no sabemos de antemano el número de repeticiones que habrá que hacer.

Sintaxis de while:

    while condición {
    }

La condición tiene que se de tipo Bool, y se ejecuta hasta que sea false.

Sentencia do .. while

Este bucle es similar al bucle while, excepto que siempre se ejecuta al menos una vez.

    do {
    } while condición

Salir de un bucle: break

A veces es necesario salir del bucle cuando en su interior se detecta alguna condición que obliga a salir. Para ello se utiliza la sentencia break:

    while a > 10 {
        ....
        if otra_condicion {
            break
        }
        ....
    }

Sentencia continue

La sentencia continue hace que volvamos al principio del bucle, saltando todo lo que queda. Suele ser útil para no hacer nada cuando se cumple una determinada condición.

    while a > 10 {
        if personas[a] == "001" {
            continue
        }
        .....
    }

Sentencia if

La sentencia if (y todas sus variantes) es muy similar a la de otros lenguajes de programación:

    if condición_1 {
        .....
    } else if condición_2 {
        .....
    } else if condición_3 {
        .....
    } else {
        .....
    }

Sentencia switch

La sentencia switch es equivalente a if/else if/else, pero se utiliza cuando el número de casos a comprobar es grande:

    switch expresión {
        case valor1:
            .....  // no se pone break
        case valor2:
            .....  // no se pone break
        default:
            ....
    }

Se pueden combinar varios valores:

    switch expresión {
        case valor1, valor2, valor3:
            ..... 
        case valor4, valor5:
            ..... 
        default:
            ....
    }

También se pueden poner rangos:

    switch expresión {
        case valor1 ... valor2:
            ..... 
        default:
            ....
    }

Algo muy novedoso de la sentencia switch en swift es el uso de la cláusula where, que añade una condición adicional a evaluar:

    switch expresión {
        case valor1 where condición:
            ..... 
        default:
            ....
    }




SWIFT (VIII) : Arrays y Diccionarios

Los arrays y los diccionarios de swift son colecciones de otros objetos y son muy similares a los que ya existen en otros lenguajes de programación. La diferencia entre ambos es el modo de acceso a los objetos que contienen: en los arrays se suele acceder por índice y en los diccionarios se suele acceder por clave. Los diccionarios en otros lenguajes de programación se denominan mapas.

Colecciones mutables e inmutables

Antes de empezar a ver tanto arrays como diccionarios, es preciso indicar que estas colecciones en swift pueden ser mutables o inmutables. Una colección inmutable no se puede modificar una vez que se ha inicializado. Para hacer una colección inmutable, la asignamos a una constante (let constante = colección). Por otro lado, las colecciones mutables sí se pueden modificar después de haberse inicializado. Para hacer una colección mutable, se asigna a una variable (var variable = colección).

Arrays

Un array es una colección que guarda múltiples objetos de forma ordenada. Todos los valores que se guardan en un array tienen que ser del mismo tipo.

Inicialización de arrays

Un array puede inicializarse en la creación mediante una colección de valores (array literal):

    var array : [type] = [ value1, value2, value3 ]

Por ejemplo:

    var cadenas : [String] = [ "Hola", "Mundo" ]

Igualmente, podemos crear un array inmutable:

    let plantas : [String] = [ "helecho", "tomillo" ]

No es obligatorio declarar el tipo del array, se puede dejar que el compilador lo infiera (tanto en constantes como en variables):

    let plantas = [ "helecho", "tomillo" ]

También podemos crear un array vacío:

    var lista = [String]()

Existe una forma de crear un array con una serie de elementos repetidos:

    var nombres = [String](count: 12, repeatedValue: "nombre")

Por último, se puede crear un array con la concatenación de otros arrays:

    var lista = lista1 + lista2

Trabajando con arrays

Tamaño de un array:

    array.count

Comprobar si un array está vacío:

    array,isEmpty

Acceder (en lectura y en escritura) a los items de un array (el primer elemento es el índice 0):

    var a = array[3]
    array[3] = 4

Añadir items a un array

Se pueden añadir elementos a un array mediante la función append o mediante el operador +. Los nuevos items se añaden al final del array, aumentando su tamaño:

    array.append("Hola")
    array += [ "Pepe", "Mundo" ]

También se pueden insertar elementos en una posición concreta de un array, desplazando los demás elementos hacia la derecha:

    array.insert("Manzano", atIndex: 3)

Eliminar items de un array

Se puede eliminar un elemento de una posición concreta de un array, desplazando los demás elementos hacia la izquierda:

    array.removeAtIndex(2)

También se puede eliminar el último elemento de un array:

    array.removeLast()

Iterar por un array

Se puede iterar por un array mediante for:

    for arbol in listaArboles {
        println(arbol)
    }

Diccionarios

Los diccionarios en swift, al igual que en otros lenguajes de programación, permiten almacenar datos en forma de parejas clave-valor. Las claves son únicas y permiten acceder a los valores correspondientes. Los diccionarios son colecciones no ordenadas.
Actualmente, swift sólo permite que las claves sean de tipo String, Int, Double y Bool (supongo que más adelante se ampliarán estos tipos utilizando técnicas similares a las de Java).

Inicialización de diccionarios

Forma de crear e inicializar un diccionario:

    var diccionario : [tipo-key: tipo-valor] = [ key1: value1, key2 : value2 ]

Por ejemplo:

    var personas = [ "001":"José Luis", "005":"Pedro" ]

En este caso se podría haber especificado el tipo, pero se puede dejar que el compilador lo infiera:

    var personas : [String: String] = [ "001":"José Luis", "005":"Pedro" ]

Creación de un diccionario vacío:

    var personas = [String: String]()

Trabajando con diccionarios

Número de elementos en un diccionario:

    personas.count

Acceso a un elemento:

    personas["001"]

Escritura:

    personas["001"] = "Samuel"

Añadir elementos a un diccionario

Se puede añadir un elemento a un diccionario directamente, por su clave:

    personas["045"] = "John"

Eliminar elementos de un diccionario

Para eliminar un elemento de un diccionario, ponemos su valor a nil:

    personas["001"] = nil

Iterar por un diccionario

Al igual que ocurría con los arrays, se puede iterar por todos los elementos de un diccionario utilizando un bucle for:

    for (id, nombre) in personas {
        println("Id = \(id) es \(nombre)")
    }






jueves, 20 de agosto de 2015

SWIFT (IV) Operadores y expresiones

Swift tiene prácticamente los mismos operadores que casi todos los lenguajes C-like (C, C++, Java, Objective-C, Javascript, etc), así que este artículo no será muy novedoso para los programadores experimentados en alguno de estos lenguajes.

Operador de asignación

El operador de asignación simplemente asigna el resultado de una expresión (a la derecha) a una variable (a la izquierda):

    var x : Int?
    x = 10 + 45
    x = x! + 1

Operadores aritméticos

    -  si es unario cambiar el signo, si es binario resta
    * multiplicación
    / división
    + suma
    % módulo o resto

Operador de asignación compuesta

    x += 3

Es equivalente a:

    x = x + 3

Operadores de incremento y decremento

    x++;

Es equivalente a:

    x = x + 1

Y también:

    x--

Es equivalente a:

    x = x - 1

Operadores de comparación

Igualdad:    ==
Desigualdad:   !=
Comparaciones numéricas:   <, >, <=, >=

Operadores lógicos booleanos

NOT ! invierte el valor de un booleano
OR || retorna true si uno de sus operandos es true
AND && retorna true si sus dos operandos son true
XOR ^ retorna true si sus dos operandos son distintos (true, false o false,true)

Operadores de rango

Los operadores de rango se suelen emplear en bucles.
La sintaxis del operador de rango básico es:

    a ... b

Esto representa el rango de valores entre a y b, ambos incluidos. Por ejemplo, 3 ... 6 representa los números 3, 4, 5 y 6.
Si se desea no incluir el último número:

    a ..< b

En este caso, el número b no va incluido en el rango.

Operador ternario

Al igual que en muchos lenguajes C-like en swift existe también el operador ternario:

    condición ? expresión_true : expresión_false

El resultado de esta expresión depende del valor de la condición. Si la condición es true, entonces el resultado de la expresión será la expresión_true. Si la condición es false, entonces el resultado de la expresión será la expresión_false.

Operadores de bit

Operador unario NOT de bit, cambia los bits a 1 por 0 y los bits 0 por 1:

    var x : UInt8 = 3      // 00000011
    var y : UInt8 = ~x    // 11111100       

Operador binario AND de bit, realiza un AND a nivel de bit de los dos operandos:

    var x : UInt8 = 12        // 00001100
    var y : UInt8 = 5          // 00000101
    var z : UInt8 = x & y   // 00000100

Operador binario OR de bit, realiza un OR a nivel de bit de los dos operandos:

    var x : UInt8 = 12        // 00001100
    var y : UInt8 = 5          // 00000101
    var z : UInt8 = x | y     // 00001101

Operador binario XOR de bit, realiza un XOR a nivel de bit de los dos operandos:

    var x : UInt8 = 12        // 00001100
    var y : UInt8 = 5          // 00000101
    var z : UInt8 = x ^ y    // 00001001

Desplazamiento de bits a la izquierda, metiendo ceros por la derecha:

    var x : UInt8 = 12       // 00001100
    var z : UInt8 = x << 1 // 00011000

Desplazamiento de bits a la derecha, metiendo ceros por la izquierda:

    var x : UInt8 = 12       // 00001100
    var z : UInt8 = x >> 1 // 00000110