Spanish Developers Community

Page 1

Spanish Developers Community número 5 ­ octubre 2013


ÍNDICE

CONTENIDOS PRINCIPALES Acerca de 01. Cómo sacar el máximo partido a nuestra red PLC 02. Estructura de datos Conjunto 03. Utilizar código QR para acceder fácilmente a nuestra red wifi 04. Concepto de Interfaz en Java

Spanish

Developers Community es una iniciativa sin ánimo de lucro, dedicada al fomento y difusión del software basado en licencias GPL, Programación de lenguajes de programación, Sistemas Operativos Linux / Android e iOS así como diseño gráfico y redes sociales. Alcanzar nuestro objetivo de una función docente y con la intención de que estos conocimientos puedan aplicarse de una manera práctica en las actividades cotidianas de los estudiantes universitarios, empresarios y autónomos . Spanish developers community se basa en la colaboración desinteresada de profesionales y estudiantes que comparten sus saber­hacer, sus conocimientos teóricos y prácticos con la comunidad informática en Internet. Esta publicación digital se distribuye mensualmente bajo una licencia Creative Commons

Colabora ¿Quieres colaborar con Spanish Developers Community?. Tienes muchas formas de hacerlo:

Envía tu artículo Puedes enviarnos tu artículo a la dirección de correo electrónico:

spanishdevcommunity@gmail.com Colabora en nuestros proyectos Puedes ayudarnos en los proyectos de canal de youtube, blogs, página web, redes sociales y página web.

responsable del proyecto: Antonio García Prats coordinación y comunicación: Antonio García sitio web: www.spanishdeveloperscommunity.es facebook: www.facebook.com/spanishdeveloperscommunity.es twitter: @spanishdc

Contactar Puedes contactar conmigo para enviarnos cualquier duda que tengas en el correo electrónico:

antoniogarciaprats@gmail.com

Depósito Legal J 368­2013

diseño de portada: Antonio García 2

© 2013 - www.spanishdeveloperscommunity.es


Redes inhalambricas

Cómo sacar el máximo partido a nuestra red PLC

En éste artículo vamos a centrarnos en cómo podemos usar la tecnología en casa, qué debemos tener en cuenta al instalar los adaptadores y en trucos para sacarles el máximo partido.

Redes PLC: Casos de uso La principal situación en la que nos conviene usar PLC es cuando no podemos desplegar un cable de red en casa desde el router al equipo que deseamos conectar (por motivos físicos o por motivos estéticos), ni tampoco podemos o queremos usar tecnologías inalámbricas como WiFi. En estos © 2013 - www.spanishdeveloperscommunity.es

casos las redes PLC son la solución más sencilla. Para empezar necesitamos hacernos con un kit de adaptadores PLC, típicamente dos dispositivos que se conectan directamente a un enchufe eléctrico y que vienen con un puerto Ethernet al que conectaremos un cable de red. Casos Uso PLC El primero de los adaptadores estará situado cerca del router y se conectará mediante el cable de red directamente a uno de sus puertos libres. El segundo adaptador lo colocaremos en la habitación en la que tengamos el dispositivo que necesite

3


conectividad (por ejemplo un televisor) y también se venden adaptadores con mejores prestaciones (de los primeros modelos de 80 Mbps hemos pasado a 200 e incluso conectará con éste mediante el típico cable de red. 500 Mbps), puede que nos encontremos con situaciones en De este modo, podemos ir conectando varios adaptadores por las que llegamos a casa, enchufamos el equipo y la velocidad toda la casa para dar conectividad en cada habitación de obtenida es muy baja, o por lo menos no todo lo buena que forma rápida y sencilla. Y dichos adaptadores no tienen por debería. ¿Por qué? qué quedarse instalados ahí de por vida, sino que son fácilmente desmontables y transportables a otra habitación Existen múltiples causas, aunque las dos más comunes son la (basta con desenchufarlos de la pared) si necesitamos darles mala calidad del cableado eléctrico y las interferencias con otros electrodomésticos del hogar. un nuevo uso.

PLC individual

Kit PLC Belkin

Este sería el caso de uso más habitual, aunque existen en el mercado alternativas que permiten variaciones. Por ejemplo venden adaptadores que en vez de tener un solo puerto Ethernet tienen varios (los hay de hasta 4 puertos), de forma que podemos conectar en la “habitación destino” varios equipos al mismo adaptador.

En cuanto a la primera, señalar que si nuestra vivienda tiene un cableado muy antiguo, con un mal aislamiento, la velocidad obtenida puede llegar a disminuir considerablemente. A esto hay que sumarle que para que todo funcione correctamente es necesario que los dos adaptadores PLC se encuentren dentro del mismo circuito eléctrico de nuestra casa.

También venden adaptadores PLC que incorporan una interfaz WiFi, de forma que sirven para extender la red Es decir, deberán estar conectados a la misma fase y a ser inalámbrica de casa o bien para crear una en una habitación a posible sin pasar por elementos que perjudiquen la transmisión de la señal como por ejemplo los típicos la que no llega la cobertura del router. diferenciales que nos protegen de sobretensiones. Esto es Hay que destacar que en la mayoría de los casos y salvo fácilmente comprobable conectando los adaptadores PLC en funcionalidades especiales como la de cifrado de red o diferentes enchufes y bajando las palancas eléctricas hasta creación de redes inalámbricas el funcionamiento y la dar con un enlace “directo” entre ambos. configuración de estos adaptadores PLC son transparentes para el usuario. Basta con enchufar el equipo y listo, no hay De este modo deberíamos obtener la máxima velocidad de que andar metiendo datos de direcciones IP ni nada parecido. nuestros adaptadores. Sin embargo puede que nos encontremos con problemas de interferencias Cómo exprimir el potencial de nuestra red PLC electromagnéticas que perjudiquen la conexión. A pesar de esta sencilla instalación y de que cada vez nos Estas

4

interferencias

son

principalmente

causadas

por

© 2013 - www.spanishdeveloperscommunity.es


electrodomésticos que incorporan partes móviles como motores. Por ejemplo la lavadora, el lavavajillas e incluso ventiladores o sistemas de calefacción por aire. Siempre que podamos debemos tratar de evitar enchufar el adaptador PLC cerca de uno de estos aparatos, ya que cuanto más cerca mayor será su influencia y mayores las interferencias. También conviene que el adaptador PLC se enchufe él solo en la toma eléctrica. Es decir, nada de regletas ni los típicos “ladrones” que solemos usar en casa para conectar varios dispositivos a un mismo enchufe. PLC uso real En caso de que andemos escasos de tomas eléctricas en casa y necesitemos usar una de estas regletas, lo mejor es que nuestro adaptador PLC incorpore una toma de enchufe. De este modo conectaremos el adaptador directamente a la pared y la regleta o ladrón al PLC (en estos casos no conviene enchufar equipos ni electrodomésticos que necesiten mucha potencia, como por ejemplo receptores A/V de alto consumo, planchas, radiadores, etc.)

Finalmente, comentar que los adaptadores PLC no se llevan bien con los Sistemas de Alimentación Ininterrumpida (SAIs), ya que éstos suelen incorporar filtros de la señal eléctrica que reducen o destruyen por completo la señal de datos transportada por la red. Así que nunca deberíamos conectar un adaptador PLC a una SAI o regleta que lleve filtros de la señal eléctrica.

Un ejemplo práctico Para ver las diferencias de rendimiento y cómo influye el enchufe al que conectemos el adaptador PLC os voy a contar un ejemplo de la instalación que tengo en casa. El escenario de pruebas es el siguiente: una vivienda de unos 100 metros cuadrados en la que queremos conectar dos habitaciones separadas en línea recta unos 10 metros. En la © 2013 - www.spanishdeveloperscommunity.es

primera habitación (el salón) tenemos el router que nos da acceso a Internet y en la segunda queremos instalar un ordenador que también pueda conectarse a Internet. Nos decidimos por un kit de adaptadores PLC de la marca D­ Link modelo DHP­P307AV que en teoría son capaces de proporcionar hasta 200 Mbps. Vamos a verlo. Kit PLC D­link En primer lugar conectamos los adaptadores ellos solos a sendos enchufes eléctricos (uno en cada habitación) y con todos los electrodomésticos de la casa apagados (menos el frigorífico). Gracias a una aplicación proporcionada en el kit medimos la velocidad del enlace que se ha establecido y obtenemos un muy buen resultado que supera los 170 Mbps, casi cerca del máximo teórico. A continuación empezamos a encender electrodomésticos por toda la casa, como por ejemplo la lavadora, el lavavajillas, un radiador, un sistema de calefacción que funciona mediante un extractor de aire caliente, el televisor y el equipo de música del salón.

Volvemos a medir la capacidad del enlace y observamos que ha descendido a unos 153 Mbps, todavía bastante bueno aunque ya por debajo del límite teórico. PLC velocidades reales Por último probamos a conectar el adaptador en una regleta que lleva un sistema de protección contra sobretensiones junto con un ordenador, unos altavoces autoamplificados y un monitor. En esta ocasión la capacidad desciende hasta entre 70 y 80 Mbps, menos de la mitad del máximo teórico. Como vemos, el enchufe al que conectemos los adaptadores y los electrodomésticos que tengamos en uso influyen notablemente en el rendimiento de nuestra red PLC. Sin embargo, siguiendo los sencillos consejos que os he comentado y teniendo un poco de cuidado deberíamos ser capaces de poder exprimir nuestra red PLC al máximo.

5


Estructuras de datos

Estructura de datos Conjunto

Los conjuntos son una de las estructuras básicas de las matemáticas, y por tanto de la informática. No se va a entrar en la definición de conjuntos ni en sus propiedades. Se supondrá que el lector conoce algo de teoría de conjuntos. Con lo más básico es suficiente. En realidad las estructuras de datos que se han implementado hasta ahora no son más que elementos diferentes entre sí (en general) en los que se ha definido una relación. Por ejemplo, en las listas ordenadas o los árboles binarios de búsqueda se tiene una serie de elementos que están ordenados entre sí. Obviando las propiedades de las estructuras, se ve que forman un conjunto, y su cardinal es el número de elementos que contenga la estructura. En los conjuntos no existen elementos repetidos, y esto se respeta en las implementaciones que se ofrecen a continuación.

Listado 1 ­ módulo de cabecera ­ array de bits #ifndef SET_[tip]_TYPO #define SET_[tip]_TYPO unsigned char conjunto8; var conjunto8 : byte; typedef typedef unsigned long tconjunto; const tconjunto Universal = 0xFFFFFFFF; const tconjunto vacio = 0; tconjunto unircjto(tconjunto A, tconjunto B); tconjunto interseccion(tconjunto A, tconjunto B) tconjunto diferencia2(tconjunto A, tconjunto B) tconjunto difsim(tconjunto A, tconjunto B)

Ahora bien, en esta sección se van definir unas implementaciones que permitan aplicar el álgebra de conjuntos, ya sea unión, intersección, pertenencia, etc. Se realizan tres implementaciones: array de bits, arrays y listas enlazadas.

Representación mediante arrays de bits

6

int iguales(tconjunto A, tconjunto B) int subconjunto(tconjunto A, tconjunto B) int pertenece(tconjunto A, int x) tconjunto insertar(tconjunto A, int x) tconjunto borrar(tconjunto A, int x)

© 2013 - www.spanishdeveloperscommunity.es


Ciertamente, un bit no da más que para representar dos estados diferentes. Por supuesto, pueden ser atributos muy variados, por ejemplo, ser hombre o mujer, adulto o niño, Windows o Linux, etc. También sirve para indicar si un elemento está o no dentro de un conjunto.

A continuación se implementa un TAD sobre conjuntos en C mediante array de bits.

El array se utiliza para representar un conjunto de números naturales (u otro tipo de datos cuyos elementos se identifiquen por un número natural único mediante una correspondencia) entre 0 y N, siendo N la capacidad del array unidimensional (es decir, un vector); almacenará valores booleanos, esto es, 1 ó 0.

typedef unsigned long tconjunto;

­ Tipo de datos empleado:

El tipo long suele ocupar 32 bits, por tanto el rango será: [0..31]. ­ Definición de conjunto vacío y universal:

Por ejemplo, suponer el conjunto universal formado por los enteros entre 0 y 4: U = {0, 1, 2, 3, 4}, y el conjunto C = {1, 2}. Se representará de esta manera:

const tconjunto Universal = 0xFFFFFFFF; const tconjunto vacio = 0;

1 : indica que el elemento pertenece al conjunto. 0 : indica que el elemento no pertenece al conjunto.

Es decir, 32 bits puestos a 1 para el conjunto universal, 32 bits puestos a 0 para el conjunto vacío.

Ahora bien, se ha dicho que se va a emplear un array de bits. ¿Qué se quiere decir con esto? Que no se va a emplear un array o vector como tal, sino un tipo de datos definido por el lenguaje de programación, que suele ocupar entre 8 y 64 bits, y por tanto podrá incluir hasta 64 elementos en el conjunto. Por ejemplo, en C o Pascal se define un tipo que ocupa 8 bits:

­ Unión:

unsigned char conjunto8; var conjunto8 : byte;

Si todos los bits de conjunto8 están a 1 entonces se tiene el conjunto: U = {0, 1, 2, 3, 4, 5, 6, 7}, y su cardinal es 8. Si todos los bits están a 0 se tiene el conjunto vacío. El bit más significativo señalará al elemento de mayor valor, el bit menos significativo al de menor valor. Ejemplos (bit más significativo a la izquierda): 11111111 11110001 01010101 00000000

-> -> -> ->

U U U U

= = = =

{0, 1, 2, 3, 4, 5, 6, 7} {0, 4, 5, 6, 7} {0, 2, 4, 6} vacío

La razón para emplear los arrays de bits es que las operaciones sobre los conjuntos se realizan de manera muy rápida y sencilla, al menos con los computadores actuales, que tienen un tamaño de palabra múltiplo de 8. Por supuesto, la ocupación en memoria. está optimizada al máximo. El inconveniente es que el rango de representación es muy limitado. Por eso su aplicación es muy restringida, y depende fuertemente del compilador y el computador sobre el que se implementan, pero es increíblemente rápida. © 2013 - www.spanishdeveloperscommunity.es

Se realiza mediante la operación de OR inclusivo. Ejemplo (con 8 bits en lugar de 32): 11001100 -> A = {2,3,6,7} Or 10010100 -> B = {2,4,7} --------11011100 -> C = {2,3,4,6,7}

­ Intersección: Se realiza mediante la operación AND. Ejemplo: 11001100 -> A = {2,3,6,7} And 10010100 -> B = {2,4,7} --------10000100 -> C = {2,7}

­ Diferencia: Para obtener C = A­B se invierten todos los bits de B y se hace un AND entre A y B negado. Ejemplo: 10011101 -> A = {0,2,3,4,7} 10110010 -> B = {1,4,5,7} B negado: 01001101 -> B(negado) = {0,2,3,6} 10011101 And 01001101 --------00001101 -> C = {0,2,3}

7


­ Diferencia simétrica:

Se hace el test de signo: A' mod 2 = 0. x no pertenece a A.

­ Inserción y borrado: C = (A­B) Unión (B­A) Se realiza mediante la operación de OR exclusivo (XOR) o Para insertar un elemento x es necesario poner a 1 el bit aplicando las primitivas definidas anteriormente. Ejemplo: correspondiente. Una manera sencilla de hacerlo es mediante una suma. Hay que sumar un valor que se corresponda con el bit que 11110000 -> A = {4,5,6,7} se quiere establecer a 1. Para hacerlo se volverá a aplicar una Xor 00011110 -> B = {1,2,3,4} operación de desplazamiento, pero esta vez hacia la izquierda y --------aplicada sobre el número 1. Se desplazan x bits hacia la 11101110 -> C = {1,2,3,5,6,7} izquierda, suponiendo que el compilador llena con ceros por la derecha. ­ Igualdad de conjuntos: La implementación es directa, si todos los bits de A y B se Por ejemplo, partir de A = conjunto vacío: { }. Se quieren insertar los elementos 0,2,3 sobre A. corresponden entonces son iguales. Insertar 0: x = 0. p = 1, (00000001 en binario). Se desplaza p x (0) bits a la ­ Subconjuntos: izquierda, p' = 1, y se suma a A. Queda: A <­ A + p'. A = 1. Si un conjunto A es subconjunto (considerando que un conjunto cualquiera es subconjunto de si mismo) de otro B entonces Insertar 2: verifica esta relación: A intersección B = A. Notar que A es x = 2. p = 1. Se desplaza p x (2) bits a la izquierda, p' = 4 (000000100 en binario). A <­ A (1) + p' (4), A = 5 (000000101). subconjunto de A, pues A intersección A = A. Ejemplo: A = {1,2,3,4}, B = {0,1,2,3} C = A intersección B = {1,2,3}; C es distinto de A. ­ Pertenencia:

Insertar 3: x =3. p = 1. Se desplaza p x (3) bits a la izquierda, p' = 8. A <­ A (5) + p' (8), A = 13 (00001101). El borrado es exactamente lo mismo, pero hay que restar p en vez de sumar. Ejemplo: borrar 3 de A. A <­ A (13) ­ p' (8), A = 5 (00000101)

Determinar si un elemento pertenece a un conjunto requiere efectuar una operación de desplazamiento a nivel de bits y una Representación mediante array posterior comprobación del bit de signo resultante. Como Los elementos del conjunto se guardan uno a continuación de siempre, un ejemplo o dos lo aclaran: otro empleando una lista densa representada mediante un array. Sea x = 0 y A = {0,1,2,5}. Determinar si x pertecene a A. 00100111 ­> A. Primero se desplazan los bits de A tantas veces a la derecha como valga x, en el ejemplo no se desplazan; se obtiene A'. A continuación se aplica el test del bit de signo sobre A', que consiste en obtener el resto de la división entera entre dos. Si el resto es uno, entonces x pertenece a A. En caso contrario no pertenece a A. En el ejemplo: A' mod 2 = 1, luego x pertenece a A. Otro ejemplo: x = 3, A = {0,1,2,5}. Se desplazan los bits de A tres posiciones a la derecha: 00000100 ­> A'.

8

Ejemplo: Sea el conjunto C = {1, 2}. Se representará de esta manera: y su cardinal es 2. Esta representación no limita el rango de representación más que al tipo de datos empleado. Por supuesto, ya no puede definirse explícitamente el conjunto universal. Por razones de eficiencia a la hora de implementar las primitivas, las estructuras se pasan por referencia. Es un detalle importante, porque C garantiza que un array se pasa siempre por referencia, pero eso no es cierto si el array se pasa como parte de una estructura.

© 2013 - www.spanishdeveloperscommunity.es


­ Tipo de datos empleado: typedef int tTipo; typedef struct { tTipo elems[MAXELEM]; int cardinal; } tconjunto;

­ Definición de conjunto vacío: Un conjunto está vacío si su cardinal es cero. Para inicializar un conjunto a vacío basta con una instrucción: A­>cardinal = 0 No se implementan rutinas de control de errores ni su detección. Se produce un error cuando se tratan de añadir elementos y estos desbordan la capacidad del array. Nota importante: los elementos dentro del array no están ordenados entre sí. ­ Pertenencia: Para determinar si un elemento x pertenece al conjunto basta con recorrer el array hasta encontrarlo. Se devuelve True si se encuentra. ­ Inserción y borrado: Para insertar un elemento, primero debe comprobarse que no está, después se inserta en la última posición, esto es, la que señale el cardinal, que se incrementa en una unidad. Borrar es aparentemente más complicado. No se puede eliminar el elemento y dejar un hueco, puesto que en ese caso ya no se tiene una lista. Para eliminar este problema se sustituye el elemento borrado por el último de la lista. ­ Unión: Para hacer C = A Unión B, se introducen en C todos los elementos de A y todos los elementos de B que no pertenezcan a A. ­ Intersección: Para hacer C = A intersección B, se hace un recorrido sobre A (o B) y se insertan en C los elementos que estén en B (o A). © 2013 - www.spanishdeveloperscommunity.es

­ Diferencia: Para hacer C = A­B, se hace un recorrido sobre A (o B) y se insertan en C los elementos que no estén en B (o A). ­ Diferencia simétrica: Sea C = (A­B) Unión (B­A). Para obtener este resultado se puede aprovechar el código estudiado anteriormente. ­ Subconjuntos: Determinar si un conjunto A es subconjunto de B se reduce a comprobar si todo elemento de A es elemento de B. Se devuelve True si A es subconjunto de B. ­ Igualdad de conjuntos: Un conjunto A es igual a otro B si A es subconjunto de B y ambos tienen los mismos elementos. Se devuelve True si A es igual a B.

Representación mediante lista enlazada Esta representación es muy parecida a la implementación mediante un array, pero con alguna particularidad que la hace más interesante en algunos casos. Por supuesto, los tipos de datos de los elementos que se insertan son igualmente admisibles con listas como lo eran con arrays. Suponer que entre los elementos del conjunto se puede definir una relación de orden, es decir, que se puede determinar si un elemento es mayor que otro. En este caso se pueden insertar y borrar elementos del conjunto de forma que la lista que los mantiene esté ordenada. Esto puede resultar interesante en algunas aplicaciones. Sea |A| y |B| el cardinal de unos conjuntos cualesquiera A y B. Aplicando la suposición anterior las operaciónes de búsqueda, inserción y borrado se ejecutan en un tiempo O(|A|). Pero hay una gran ventaja, y es que las restantes operaciones se ejecutan en un tiempo O(|A|+|B|). ¿Cómo se consigue ésto? Aprovechando las propiedades de tener listas ordenadas basta con hacer un único recorrido sobre cada lista. Esto es posible implementando un algoritmo basado en el algoritmo de fusión de dos listas ordenadas, que obtiene una lista ordenada a partir de dos o más listas ordenadas con un único recorrido de cada lista. (Es recomendable ver primero los Algoritmos de ordenación de listas y entender el proceso de intercalación o fusión, pero NO es necesario estudiar el proceso recursivo ya que no tiene interés aquí). Las operaciones de unión,

9


Listado 2 ­ Estructura array de bits

Listado 3 ­ Estructura array

#include <stdlib.h>

#include <stdlib.h>

#include <stdio.h>

#include <stdio.h>

#include "set.h"

#include "set.h"

/*-------------------------------------------------

int pertenece(tconjunto *A, tTipo x)

tconjunto unircjto(tconjunto A, tconjunto B)

{ int i;

{ return (A | B); }

for (i = 0; i < A->cardinal; i++) if (A->elems[i] == x) return 1;

/*-------------------------------------------------

return 0;

tconjunto interseccion(tconjunto A, tconjunto B) { return (A & B); }

}

/*-------------------------------------------------

void insertar(tconjunto *A, tTipo x)

tconjunto diferencia2(tconjunto A, tconjunto B)

{ if (!pertenece(A, x))

{ return (A & ~B); }

A->elems[A->cardinal++] = x; /*-------------------------------------------------

}

tconjunto difsim(tconjunto A, tconjunto B) { return (A ^ B); }

void borrar(tconjunto *A, tTipo x) {

/*-------------------------------------------------

int i;

int iguales(tconjunto A, tconjunto B)

for (i = 0; i < A->cardinal; i++) if (A->elems[i] == x) {

{ return (A == B); }

A->elems[i] = A->elems[--A->cardinal]; /*-------------------------------------------------

return; }

int subconjunto(tconjunto A, tconjunto B) { return (iguales(interseccion(A,B),A)); }

}

/*-------------------------------------------------

void unircjto(tconjunto *A, tconjunto *B,

int pertenece(tconjunto A, int x)

tconjunto *C)

{ return ((A >> x) % 2); }

{ int i;

/*-------------------------------------------------

*C = *A;

tconjunto insertar(tconjunto A, int x)

for (i = 0; i < B->cardinal; i++) if (!pertenece(A, B->elems[i]))

{

insertar(C, B->elems[i]);

if (pertenece(A,x)) return A; else return (A + ((tconjunto)1 << x));

}

} void interseccion(tconjunto *A, tconjunto *B, /*-------------------------------------------------

tconjunto *C)

tconjunto borrar(tconjunto A, int x)

{ int i;

{ if (pertenece(A,x)) return A;

C->cardinal = 0;

else return (A - ((tconjunto)1 << x));

for (i = 0; i < A->cardinal; i++) if (pertenece(B, A->elems[i]))

}

insertar(C, A->elems[i]); } void diferencia(tconjunto *A, tconjunto *B,

10

© 2013 - www.spanishdeveloperscommunity.es


tconjunto *C) { int i; C->cardinal = 0; for (i = 0; i < A->cardinal; i++) if (!pertenece(B, A->elems[i])) insertar(C, A->elems[i]); }

tratarán conjuntos de números enteros. Los tipos de datos se declaran así: typedef struct lista { int elem; struct lista *sig; } lista;

void difsim(tconjunto *A, tconjunto *B, tconjunto *C) { int i; C->cardinal = 0; for (i = 0; i < A->cardinal; i++) if (!pertenece(B, A->elems[i])) insertar(C, A->elems[i]); for (i = 0; i < B->cardinal; i++) if (!pertenece(A, B->elems[i])) insertar(C, B->elems[i]); } int subconjunto(tconjunto *A, tconjunto *B) / { int i, esta; esta = 1; for (i = 0; i < A->cardinal; i++) if (!pertenece(B, A->elems[i])) return 0; return 1; } int iguales(tconjunto *A, tconjunto *B) { return (subconjunto(A,B) && A->cardinal == B>cardinal); }

intersección, etcétera, e incluiso el determinar si un conjunto es subconjunto de otro se efectúan haciendo pequeñas variaciones sobre el algoritmo de intercalación. Definición y tipo de datos empleado: Se empleará una lista enlazada con cabecera ficticia y centinela. La razón es que se realizarán inserciones y búsquedas sobre la lista que contiene los elementos del conjunto. Como se ha comentado anteriormente, los elementos de la lista estarán ordenados. Por tanto, para emplear esta representación los elementos deben ser ordenables. En el código propuesto, se

typedef struct tconjunto { lista *cabecera, *centinela; int cardinal; } tconjunto;

­ Creación del conjunto vacío: La creación de un nuevo conjunto (vacío) se realiza estableciendo a cero el número de elementos y reservando memoria para los elementos de cabecera y centinela de la lista. ­ Pertenencia: Para determinar si un elemento x pertenece al conjunto basta con recorrer la lista hasta encontrarlo o llegar al final de ésta. Se devuelve True si se encuentra antes del centinela. ­ Inserción y borrado: Para insertar un elemento primero debe comprobarse que no está, después se inserta ordenadamente en la lista, y se incrementa el cardinal en una unidad. Para borrar un elemento basta con localizarlo dentro de la lista y eliminarlo. ­ Unión: A partir de los conjuntos A y B se crea un nuevo conjunto C. Se supone que el conjunto C no ha sido inicializado antes. En cada paso se añade siempre un nuevo elemento. Por último se comprueba que no queden elementos sin copiar. ­ Intersección: C = A Intersección B, es el nuevo conjunto que se crea. Se añade un elemento cuando coincide en ambas listas a la vez (c1­>elem == c2­>elem).


Listado 4 ­ Estructura lista enlazada

nuevo->sig = actual; anterior->sig = nuevo;

#include <stdlib.h> cjto->cardinal++;

#include <stdio.h> }

#include "set.h" } void crearcjto(struct tconjunto *cjto)

void borrar(tconjunto *cjto, int x)

{ cjto->cabecera = (lista *) malloc(sizeof(lista));

{ lista *anterior, *actual;

cjto->centinela = (lista *)

/* 1.- busca */

malloc(sizeof(lista)); cjto->cabecera->sig = cjto->centinela;

anterior = cjto->cabecera;

cjto->centinela->sig = cjto->centinela; /*

actual = cjto->cabecera->sig; cjto->centinela->elem = x;

opcional, por convenio */

while (actual->elem < x) {

cjto->cardinal = 0;

anterior = actual;

}

actual = actual->sig; int pertenece(tconjunto cjto, int x)

}

{

/* 2.- borra si existe */ if (actual != cjto->centinela && actual-

lista *actual;

>elem == x) { actual = cjto.cabecera->sig;

anterior->sig = actual->sig;

cjto.centinela->elem = x;

free(actual); }

while (actual->elem != x) actual = actual->sig;

}

if (actual == cjto.centinela) return 0; else return 1;

void unioncjto(tconjunto A, tconjunto B, tconjunto *C) {

}

lista *c1, *c2, *c3, *aux;

void insertar(tconjunto *cjto, int x)

crearcjto(C);

{

c3 = C->cabecera; lista *anterior, *actual, *nuevo;

c1 = A.cabecera->sig; c2 = B.cabecera->sig; while (c1 != A.centinela && c2 !=

/* 1.- busca */

B.centinela) {

anterior = cjto->cabecera;

aux = (lista *) malloc(sizeof(lista));

actual = cjto->cabecera->sig;

if (c1->elem < c2->elem) {

cjto->centinela->elem = x;

aux->elem = c1->elem;

while (actual->elem < x) {

c1 = c1->sig;

anterior = actual;

}

actual = actual->sig;

else if (c1->elem > c2->elem) { aux->elem = c2->elem;

}

c2 = c2->sig; if (actual->elem != x || actual == cjto>centinela) {

} else { aux->elem = c1->elem; /* tambien vale:

/* 2.- crea */ nuevo = (lista *) malloc(sizeof(lista));

aux->elem = c2->elem */ c1 = c1->sig;

nuevo->elem = x;

c2 = c2->sig;

/* 3.- enlaza */ }

12

Š 2013 - www.spanishdeveloperscommunity.es


aux->sig = C->centinela;

­ Diferencia:

c3->sig = aux; c3 = aux; C->cardinal++; }

C = A­B. Se añade un nuevo elemento sólo cuando (c1­>elem < c2­>elem).

/* copia los elementos restantes si los

­ Diferencia simétrica:

hubiera */ if (c1 != A.centinela) { while (c1 != A.centinela) { aux = (lista *) malloc(sizeof(lista));

C = (A­B) Unión (B­A). Es decir, todos los elementos no comunes de ambos conjuntos. Se añaden elementos si (c1­ >elem != c2­>elem).

aux->elem = c1->elem; aux->sig = C->centinela;

­ Subconjuntos:

c3->sig = aux; c3 = aux; C->cardinal++; c1 = c1->sig; } } else if (c2 != B.centinela) { while (c2 != B.centinela) { aux = (lista *) malloc(sizeof(lista)); aux->elem = c2->elem; aux->sig = C->centinela; c3->sig = aux; c3 = aux; C->cardinal++;

Determinar si un conjunto A es subconjunto de B se reduce a comprobar si todo elemento de A es elemento de B. Se devuelve True si A es subconjunto de B. Observar que si (c1­>elem < c2­ >elem) entonces A ya no puede ser subconjunto de B, pues implica que dicho elemento no está en B, ya que c2 representa al menor de los elementos restantes del conjunto. Por último, observar la última condición: return (essub && c1 == A.centinela);. Es decir, quedan elementos de A que no han sido recorridos, pero B ya está totalmente recorrido, luego A no es subconjunto de B. Si se da el caso de que essub = true y c1 != A.centinela entonces se puede devolver un tercer valor que indique que B es subconjunto de A.

c2 = c2->sig; }

­ Igualdad de conjuntos:

} } void interseccion(tconjunto A, tconjunto B,

Un conjunto A es igual a otro B si ambos tienen los mismos elementos. Se devuelve True si A es igual a B. Se comprueba primero el cardinal de ambos conjuntos.

tconjunto *C)

Conclusión

{ lista *c1, *c2, *c3, *aux; crearcjto(C); c3 = C->cabecera; c1 = A.cabecera->sig; c2 = B.cabecera->sig;

Esta implementación tampoco limita el rango de representación de los elementos del conjunto, y por supuesto tampoco limita el tipo de datos, siempre y cuando se pueda deducir cuando un elemento es igual a otro o no.

while (c1 != A.centinela && c2 != B.centinela) { if (c1->elem < c2->elem) c1 = c1->sig; else if (c1->elem > c2->elem)

Dado un conjunto A y B, las operaciones de inserción, borrado y pertenencia se ejecutan en un tiempo de O(|A|). Las operaciones de unión, intersección, diferencia, diferencia simétrica, subconjunto e igualdad se ejecutan en un tiempo de O(|A|+|B|).

c2 = c2->sig; else { aux = (lista *) malloc(sizeof(lista)); aux->elem = c1->elem; /* tambien vale: aux->elem = c2->elem */

El espacio que ocupa un conjunto es de O(|A|), siendo |A| el cardinal del conjunto A. Por supuesto es proporcional al tamaño del conjunto implementado mediante array, multiplicado por una constante debido al espacio ocupado por los punteros.

aux->sig = C->centinela; c3->sig = aux; c3 = aux;

© 2013 - www.spanishdeveloperscommunity.es

13


aux = (lista *) malloc(sizeof(lista));

C->cardinal++;

if (c1->elem < c2->elem) {

c1 = c1->sig; c2 = c2->sig;

aux->elem = c1->elem; c1 = c1->sig; } else { aux->elem = c2->elem;

} c2 = c2->sig; }

} }

aux->sig = C->centinela; c3->sig = aux; c3 = aux;

void diferencia(tconjunto A, tconjunto B, tconjunto

C->cardinal++;

*C) }

{ else {

lista *c1, *c2, *c3, *aux;

c1 = c1->sig; c2 = c2->sig;

crearcjto(C); }

c3 = C->cabecera; c1 = A.cabecera->sig; c2 = B.cabecera->sig;

}

while (c1 != A.centinela && c2 != B.centinela) {

/* copia los elementos restantes si los

if (c1->elem < c2->elem) {

hubiera */ if (c1 != A.centinela) {

aux = (lista *) malloc(sizeof(lista));

while (c1 != A.centinela) {

aux->elem = c1->elem;

aux = (lista *)

aux->sig = C->centinela; c3->sig = aux; c3 = aux;

malloc(sizeof(lista));

C->cardinal++;

aux->elem = c1->elem;

c1 = c1->sig;

aux->sig = C->centinela; c3->sig = aux; c3 = aux;

}

C->cardinal++;

else if (c1->elem > c2->elem)

c1 = c1->sig;

c2 = c2->sig; }

else }

c1 = c1->sig,

else if (c2 != B.centinela) {

c2 = c2->sig;

while (c2 != B.centinela) {

}

aux = (lista *)

/* aniade lo que quede de A */ while (c1 != A.centinela) {

malloc(sizeof(lista)); aux->elem = c2->elem;

aux = (lista *)

aux->sig = C->centinela;

malloc(sizeof(lista)); aux->elem = c1->elem;

c3->sig = aux; c3 = aux;

aux->sig = C->centinela;

C->cardinal++; c2 = c2->sig;

c3->sig = aux; c3 = aux; }

C->cardinal++; }

c1 = c1->sig; }

} }

int subconjunto(tconjunto A, tconjunto B) void difsim(tconjunto A, tconjunto B, tconjunto *C)

int essub = 1;

{ lista *c1, *c2, *c3, *aux;

lista *c1, *c2;

crearcjto(C);

c1 = A.cabecera->sig; c2 = B.cabecera->sig;

c3 = C->cabecera;

while (c1 != A.centinela && c2 !=

c1 = A.cabecera->sig; c2 = B.cabecera->sig; while (c1 != A.centinela && c2 != B.centinela) { if (c1->elem != c2->elem) {

14

{

B.centinela && essub) { if (c1->elem < c2->elem) essub = 0;

Š 2013 - www.spanishdeveloperscommunity.es


else if (c1->elem > c2->elem)

Listado 5 ­ Programa de prueba

c2 = c2->sig; else { c1 = c1->sig;

int main(void) {

c2 = c2->sig;

tconjunto A, B, C, p1, p2, p3, p4, p5, p6;

} }

crearcjto(&A);

return (essub && c1 == A.centinela);

crearcjto(&B);

}

crearcjto(&C);

int iguales(tconjunto A, tconjunto B)

/* A = {2,3,5}

{

B = {1,2,3,4,5}

int igual;

C=

lista *c1, *c2;

{3,4,6}

*/ insertar(&A, 2); insertar(&A, 3); insertar(&A, 5);

igual = A.cardinal == B.cardinal;

insertar(&B, 1); insertar(&B, 2); insertar(&B, 3); insertar(&B, 4); insertar(&B, 5);

c1 = A.cabecera->sig; c2 = B.cabecera->sig;

insertar(&C, 3); insertar(&C, 4); insertar(&C, 6);

while (c1 != A.centinela && c2 != B.centinela && igual) {

if (pertenece(A, 5)) printf("5 pertenece a A");

if (c1->elem != c2->elem) igual = 0;

if (!pertenece(B, 6)) printf("\n6 no pertenece a B");

c1 = c1->sig; c2 = c2->sig;

/* p1 = {2,3,4,5,6} */

}

unioncjto(A,C,&p1);

return (igual);

/* p2 = {3} */

}

interseccion(A,C,&p2); /* p3 = {1,4} */ diferencia(B,A,&p3);

/* p4 = p6 = {2,4,5,6}, p5 = vacĂ­o */ difsim(C,A,&p4); difsim(A,C,&p6); difsim(B,B,&p5);

if (iguales(p4,p6)) printf("\np4 = p6"); if (subconjunto(B,B)) printf("\nB es subconjunto de B"); if (!subconjunto(B,C)) printf("\nB no es subconjunto de C"); if (subconjunto(A,B)) printf("\nA es subconjunto de B"); if (subconjunto(p5, A)) printf("\np5 es subconjunto de A");

return 0; }

Š 2013 - www.spanishdeveloperscommunity.es

15


Laboratorio práctico

Cómo utilizar un código QR para acceder fácilmente a una red wifi

Me pareció muy ingenioso y práctico este truco para dar acceso a la red WiFi sin tener que andar buscando el nombre de la red y, mejor aún, sin tener que recitar ni teclear la en muchas ocasiones larga y complicada contraseña.

con una aplicación como Barcode Scanner (gratuita) se podrá establecer la conexión con la red WiFi directamente, sin necesidad de acceder a la lista de redes para seleccionarla y —lo que es aún mejor— sin necesidad de teclear la contraseña. Aunque en otros dispositivos que no sean Android, no se podrá conectar automáticamente con la red, dado que el código QR contiene la información en forma de texto, puede verse y, sobre todo, se puede copiar y pegar la contraseña para que no sea necesario teclearla.

La idea era generar un código QR con la información de acceso a la red WiFi utilizando, por ejemplo, este generador de códigos QR. Hay que seleccionar en el menú que el código corresponde a una red WiFi, indicar el nombre de la red (SSID) y la contraseña, y el tipo de cifrado que utiliza la red, que suele ser WEP o WPA. Una vez generado el código QR se puede compartir, o Por ejemplo para Windows Phone 8 existe la app Code simplemente imprimir y pegar en el router, o en algún lugar Scanner (gratuita) y para iPhone la app Bakodo (gratuita). Ambas aplicaciones muestran los datos contenidos en el visible dentro de la zona de cobertura de la red. código QR, así que sólo hay que ver el nombre de la red, Al escanear el código QR desde un teléfono o tablet Android copiar la contraseña y pegarla en la configuración WiFi.

16

© 2013 - www.spanishdeveloperscommunity.es


Programación Java

Concepto de Interfaz en Java

Ya hemos dicho que interface en Java es una palabra que puede tener diferentes significados. Vamos a repasar algunos significados que ya hemos visto y a introducir otros nuevos. Es habitual que entre las personas que estudian Java haya dificultades para entender el concepto de interface. Esto es hasta cierto punto normal porque este término tiene varios usos. Ya hemos dicho que interface en Java es una palabra que puede tener diferentes significados. Vamos a repasar algunos significados que ya hemos visto y a introducir otros nuevos. a) Interface: parte visible y pública de una clase que describe qué hace y cómo usarla. La documentación de una clase en el API de Java vendría siendo su interface. b) Interface: parte visible y pública de un método que describe qué hace y cómo usarlo (signatura del método + instrucciones de uso). La documentación de un método en el API de Java vendría siendo su interface.

© 2013 - www.spanishdeveloperscommunity.es

c) Interfaz Gráfica de Usuario, interfaz de usuario o GUI (Graphical User Interface): es el entorno de objetos gráficos disponibles para un usuario en el marco de una aplicación o sistema operativo. El sistema operativo MS­Dos se basaba en intérpretes de comando (escritura de instrucciones por consola) pero Windows se basa en una interfaz gráfica de usuario (su entorno de escritorio), Linux en otra y Macintosh en otra. d) Herramientas para crear Interfaces gráficas de usuario en Java. Hacemos referencia principalmente a los paquetes (packages) del API de Java swing y awt (Abstract Windowing Toolkit). Las clases de estos paquetes permiten crear interfaces gráficas de usuario basadas en ventanas estilo “Windows” para nuestras aplicaciones. e) Interfaces de Java: son unas entidades abstractas conceptualmente por encima de las clases cuyo concepto vamos a introducir a continuación.

17



Turn static files into dynamic content formats.

Create a flipbook
Issuu converts static files into: digital portfolios, online yearbooks, online catalogs, digital photo albums and more. Sign up and create your flipbook.