domingo, 27 de septiembre de 2015

Examen POO Junio 2015 prMaternidad (Segunda Parte)

Aquí os traigo la segunda parte. Lo siento por haber sido un vago... Al final ha sido uno de mis alumnos de éste verano quien ha hecho el código conmigo y me lo ha mandado. Gracias Álvaro ;)

Os recuerdo que la primera parte y el enunciado a descargar se encuentran aquí.

De todas formas os pego la segunda parte del enunciado:
(2 ptos.) La clase MaternidadLimiteBebeHabitacion se comporta como la clase Maternidad, con la diferencia de que va a detectar aquellas habitaciones que tienen un número de bebés que supera el valor máximo definido en la variable llamada maximo (int). Para cada habitación que supere el máximo permitido se guardará un mensaje informando de tal situación en la variable cambios (List). Con el propósito de saber la ocupación actual de cada habitación esta clase cuenta con una variable ocupacion (Map) que asocia cada habitación con el número de bebés actualmente asignados a ella. La variable ocupación se va actualizando cada vez que un nuevo bebé es registrado en el sistema y será consultada para ver si se supera el mencionado límite. 
a. Los constructores MaternidadLimiteBebeHabitacion(int maximo) y MaternidadLimiteBebeHabitacion(int maximo, String nombreFichero) deben crear todas las estructuras de datos: en el primer caso se crean vacías; en el segundo caso se añade toda la información del fichero cuyo nombre se pasa como segundo parámetro. El formato de dicho fichero es el mismo que el utilizado para la clase Maternidad. 
b. Redefinir addMadreBebes(Persona madre, Collection bebes). La nueva versión el método debe añadir la información de pacientes a la estructura de datos paciente sin tener en cuenta el límite de bebés. En caso de que se detecte que ello hace que alguna habitación supere dicho límite, se deberá añadir un mensaje informativo donde conste el número de habitación al listado de cambios (cambios) que han de hacerse. Se debe tener en cuenta que la incubadora (habitación con código 0) no estará sujeta a dicho límite. 
c. Redefinir String toString() para que a la información sobre las madres y bebés a las que atiende el servicio de maternidad mostrada en la clase Maternidad, añada todos los mensajes contenidas en la lista de cambios.

5) (1.5 ptos.) La clase ControladorMaternidad controla e interactúa con el modelo (clases Maternidad y Persona) y la vista (se proporcionan en el campus virtual la interfaz VistaMaternidad y la clase PanelMaternidad). El constructor debe habilitar la parte de inicialización de la vista (introducción del fichero de pacientes, y el botón de inicio) y mostrar un mensaje en la parte baja de la misma indicando al usuario que introduzca el nombre del fichero y pulse el botón iniciar. El resto de la vista estará deshabilitado. La pulsación del botón “Inicio” hará que se cree un objeto de la clase Maternidad (pasándole el nombre del fichero introducido), se deshabilite la zona de inicialización y se habilite el resto de la vista. Cada vez que el usuario pulse algunos de los demás botones se procederá a realizar la acción correspondiente. El botón “Guardar” guarda la información sobre pacientes en el fichero indicado en el campo de texto a su derecha. En caso de que dicho campo contenga la cadena vacía, la información se imprime en el área de texto del panel. El botón “Buscar Madre” hará que se lea el código del bebé del campo de texto a su derecha y se muestre en el área de texto la habitación en que se encuentra su madre. El botón “Media” muestra en el área de texto el resultado de calcular la media de bebés. Tras cada operación se debe mostrar un mensaje de confirmación o error en la parte baja de la vista. 
Las clases Main y MainGUI se proporcionan en el campus virtual, para que se pueda probar el funcionamiento de las distintas clases a implementar.
Aquí os dejo las clases que deberían porporcionaos: VistaMaternidad PanelMaternidad
Y aquí os dejo ya las soluciones:
Empezamos con la clase MaternidadLimiteBebeHabitacion

package prMaternidad;

import java.io.FileNotFoundException;
import java.util.Collection;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeSet;

public class MaternidadLimiteBebesHabitacion extends Maternidad {
 private int maximo;
 private List<String> cambios;
 private SortedMap<Integer, Integer> ocupacion;//no he encontrado nada que me pida usarlo pero abajo
 //esta señalado donde lo utilizaria si fuese necesario

 public MaternidadLimiteBebesHabitacion(int maximo) {
  super();
  this.maximo = maximo;
 }

 public MaternidadLimiteBebesHabitacion(int maximo, String nombreFichero) throws FileNotFoundException {
  super(nombreFichero);
  this.maximo = maximo;

 }

 public void addMadreBebes(Persona madre, Collection<Persona> bebes) {
  int cont = 1;
  if (!pacientes.containsKey(madre))
   pacientes.put(madre, new TreeSet<Persona>());
  if(!ocupacion.containsKey(madre.getHabitacion())){
   ocupacion.put(madre.getHabitacion(),0);
  }

  for (Persona b : bebes) {
   pacientes.get(madre).add(b);
   if (b.getHabitacion() != 0) {
    cont++;
   }
   ocupacion.put(madre.getHabitacion(), cont+ocupacion.get(madre.getHabitacion()));
   //en cada habitacion a traves de ocupacion lo añadiria aqui
   if (ocupacion.get(madre.getHabitacion()) >= maximo) {
    cambios.add("habitacion " + madre.getHabitacion()
      + "se encuentra llena");
   }
   
  }
 }

 public String toString() {
  String std = "";
  for (Persona madre : pacientes.keySet()) {
   std = std + madre;
  //para poder concatenar un string con el toString de una clase, el string ha de estar inicializado
   //aunque sea un string vacio como en este caso
   for (Persona hijo : pacientes.get(madre)) {
    std = std + "#" + hijo;
   }
   
   
   std = std + "\n";

  }
  for(int i=0;i<cambios.size();i++){
  std=std+cambios.get(i)+"\n ";//meto la lista de las habitaciones que superan el limite
  //de pacientes en el string
  }
  return std;
 }

}

Y acabamos con la clase ControladorMaternidad
package prMaternidad;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.FileNotFoundException;

public class ControladorMaternidad implements ActionListener {
 private Maternidad model;
 private VistaMaternidad vistamaternidad;

 public ControladorMaternidad(VistaMaternidad vm, Maternidad m) {
  this.vistamaternidad = vm;
  this.model = m;

  vistamaternidad.habilitarInit(true);
 }

 public void actionPerformed(ActionEvent ae) {
  String command = ae.getActionCommand();

  if (command.equals(VistaMaternidad.BUSCAR)) {
   try{
   int c = vistamaternidad.codigoBebe();
   try {
    int habit = model.encontrarMadre(c);
    vistamaternidad
      .entradaHistorial("La habitacion de la madre del bebe con codigo: "
        + c + "es: " + habit);
   } catch (MaternidadException e) {
    vistamaternidad.error("No se encuentra a la madre del bebe.");
   }
   }catch (NumberFormatException e){
    vistamaternidad.error("Hay un error con el codigo del bebe.");
   }
  } else if (command.equals(VistaMaternidad.GUARDAR)) {
   try {
    String ruta = vistamaternidad.fichSalida();
    model.escribirFichero(ruta);
   } catch (FileNotFoundException e) {

    vistamaternidad.entradaHistorial(model.toString());
   }
  } else if (command.equals(VistaMaternidad.INICIO)) {

   vistamaternidad.habilitarInit(true);
   try {
    model = new Maternidad(vistamaternidad.fichEntrada());
    vistamaternidad.habilitarInit(false);
    vistamaternidad.mensaje("Fichero cargado.");
   } catch (FileNotFoundException e) {
    vistamaternidad.error("Fichero no encontrado.");
   }

  } else if (command.equals(VistaMaternidad.MEDIA)) {
   double media = model.mediaBebes();
   vistamaternidad
     .entradaHistorial("La media de los bebes por cada madre en el hospital es: "
       + media);
  }

 }
}


Es trivial ver que ésto es más un trabajo duro que complejo... Por eso mismo me he demorado tanto en hacerlo, me daba demasiada pereza :P

Espero que os sea tan simple como a mí, pero sino ya sabéis que podeis preguntar las dudas aquí abajo;)

Saludos;)

domingo, 26 de julio de 2015

Examen POO Junio 2015 prMaternidad (Primera Parte)

Buenas gente. Ya estoy de nuevo por aquí, que recientemente uno de mis alumnos me ha pasado éste examen y yo, aburrido, he decidido hacerlo.
Os traigo los primeros 6.5 puntos porque estoy vago. Ya acabaré el resto otra tarde :)

El enunciado podéis descargarlo de aquí. De todos modos os lo pego por aquí por si queréis leerlo ;)

Se desea desarrollar una aplicación para la gestión de pacientes a los que atiende el servicio de maternidad de un hospital. Para ello, se creará un proyecto prMaternidad con las clases siguientes:
1) (0.25 ptos.) La clase MaternidadException se utilizará para tratar las diferentes situaciones excepcionales. Se trata de una excepción no comprobada. En general, se lanzará esta excepción si los parámetros de entrada de los métodos no contienen valores adecuados.
2) (2.25 ptos.) La clase Persona mantendrá información sobre un paciente ingresado en el área de maternidad del hospital. En concreto, su nombre (String), un código (int) que se considerará único y la habitación (int) asignada a ese paciente. La clase dispondrá de constructores y métodos necesarios para:
a. Construir un objeto de la clase dados los valores para las tres variables descritas anteriormente. Si el código proporcionado no es un número positivo mayor que cero o la habitación dada es negativa se debe lanzar una excepción de tipo MaternidadException informando de tal situación.
b. Obtener el nombre (String getNombre()), el código (int getCodigo()) y el número de habitación (int getHabitacion()).
c. La representación de una persona (String toString()) viene dada por su nombre, código y número de habitación separados por el carácter ‘:’.
Ej: García, Ana:1002:132
d. Dos objetos de la clase Persona son iguales si coinciden sus códigos, que recordemos que son únicos.
e. Los objetos de la clase Persona se ordenan de forma natural por código.
f. Se proporcionará una ordenación alternativa, que organice los objetos primero por nombre, ignorando mayúsculas y minúsculas, y, en caso de igualdad, por código. Esta ordenación deberá definirse en una clase aparte, llamada OrdAlt.
NOTAS PARA LA REALIZACIÓN DEL EJERCICIO:
 El ejercicio se almacenará en el directorio C:\POO. En caso de que no exista deberá crearse, y si ya existiese, deberá borrarse todo su contenido antes de comenzar.
 Al inicio del contenido de cada fichero deberá indicarse el nombre del alumno, titulación, grupo y código del equipo que está utilizando.
 La evaluación tendrá en cuenta la claridad de los algoritmos, del código y la correcta elección de las estructuras de datos, así como los criterios de diseño que favorezcan la reutilización.
 Los diferentes apartados tienen una determinada puntuación. Si un apartado no se sabe hacer, el alumno no debe pararse en él indefinidamente. Puede abordar otros.
 Está permitido:
o Consultar el API.
 No está permitido:
o Utilizar otra documentación electrónica o impresa.
o Intercambiar documentación con otros compañeros.
o Utilizar soportes de almacenamiento.
 Una vez terminado el ejercicio subir un fichero comprimido sólo con los ficheros .java que hayáis realizado a la tarea creada en el campus virtual para ello.
3) (4 ptos.) La clase Maternidad almacenará la información de los pacientes del servicio de maternidad en una estructura pacientes de tipo SortedMap<Persona, SortedSet <Persona>>, de tal manera que cada madre ingresada esté asociada al conjunto de bebés a los que ha dado a luz. La madre y los bebés suelen estar en la misma habitación, pero hay veces en qué ello no ocurre así. Por ejemplo, si alguno de los bebés debe ir a la incubadora (habitación con código 0).
La clase debe proporcionar los siguientes constructores y métodos públicos:
a. El constructor Maternidad() crea la estructura de datos. Se debe tener en cuenta que queremos ordenar las madres por nombre (orden alternativo mencionado anteriormente). Los bebés asociados a cada madre se ordenarán por código (orden natural).
El constructor Maternidad(String)recibe un nombre de fichero y, tras crear la estructura de datos, añade toda la información que contiene el fichero (ver apartados siguientes).
b. Los métodos addPacientesFichero(String) y addPacientes(Scanner) añaden información sobre pacientes a la estructura de datos. El primero lee la información de un fichero. El segundo desde el flujo de entrada proporcionado. Se supondrá que la información sobre cada madre y sus bebés se encuentra en una línea de texto, donde el carácter ‘#’ separa la información de distintas personas. Para cada persona, sus datos particulares se separan con el carácter ‘:’. Por ejemplo, la siguiente línea indica que la persona Ana García es madre de dos niños, Pedro y Nuria.
García,Ana:1002:132#López,Pedro:1034:132#López,Nuria:1036:0
Cualquier error de formato detectado (falta algún dato personal, o bien el dato no es del tipo correcto), provocará el lanzamiento de una excepción del tipo MaternidadException.
Se proporciona el fichero de datos pacientes.txt para probar el correcto funcionamiento de la solución implementada.
c. El método addMadreBebes(Persona madre, Collection<Persona> bebes) registra la madre y sus bebes en el servicio de maternidad. Nótese que la colección bebes puede estar vacía, lo que indica que dicha madre aún no ha dado a luz. Si la madre ya estuviese registrada se añadirán los bebés indicados por el segundo parámetro al conjunto de bebés que ya tenía previamente.
d. Se ha de redefinir el método String toString(). La representación de los objetos de la clase muestra toda la información contenida en la estructura de datos. El formato que se ha de utilizar es el que aparece en el fichero pacientes.txt. Es decir, en cada línea la información de una madre y sus bebés.
e. Los métodos escribirFichero(String) y escribir(PrintWriter) guardan la información de la estructura de datos en el fichero o en el flujo de salida dado respectivamente.
f. El método double mediaBebes() calcula el número medio de bebés por madre que están registrados en el sistema. Aquellas madres que no tienen hijos asociados no se tienen en cuenta en el cómputo.
g. El método int encontrarMadre(int codigoBebe) devuelve el número de habitación de aquella persona que es madre del bebé cuyo código se pasa como parámetro. En caso de no encontrarse la madre, se debe lanzar una excepción de tipo MaternidadException informando de tal situación.
Igualmente os aconsejo que lo descarguéis. Se verá mejor que aquí.
Aquí os dejo también las clases terminadas, pero no os limiteis a verlas. Intentad hacerlo vosotros y luego comparáis ;)
MaternidadException, Persona, OrdAlt, Maternidad.
Aquí os dejo pegado el código ;)
MaternidadException:
public class MaternidadException extends Exception {
    public MaternidadException() {
        super();
    }
    public MaternidadException(String e) {
        super(e);
    }
}

Persona:
public class Persona {
    private String nombre;
    private int codigo;
    private int habitacion;
    
    public Persona(String nombre, int codigo, int habitacion) throws MaternidadException{
        if(codigo <1 || habitacion<0)
            throw new MaternidadException("Codigo o habitacion incorrectos.");
        this.nombre = nombre;
        this.codigo = codigo;
        this.habitacion = habitacion;
    }
    
    public String getNombre() {
        return this.nombre;
    }
    public int getCodigo() {
        return this.codigo;
    }
    public int getHabitacion() {
        return this.habitacion;
    }
    
    public String toString() {
        return this.nombre+":"+this.codigo+":"+this.habitacion;
    }
    
    public boolean equals(Persona p) {
        return this.codigo==p.getCodigo();
    }
    public int compareTo(Persona p) {
        int cod = p.getCodigo();
        if(this.codigo<cod)
            return -1;
        else if(this.codigo>cod)
            return 1;
        else
            return 0;
    }
    
}

OrdAlt:
public class OrdAlt extends Persona{
    public OrdAlt(String nombre, int codigo, int habitacion) throws MaternidadException{
        super(nombre, codigo, habitacion);
    }
    
    public int compareTo(OrdAlt p) {
        String nombre1 = this.getNombre().toLowerCase();
        String nombre2 = p.getNombre().toLowerCase();
        int c = nombre1.compareTo(nombre2);
        
        if(c!=0)
            return c;
        
        /* Copiado tal cual de Persona*/
        int cod = p.getCodigo();
        if(this.getCodigo()<cod)
            return -1;
        else if(this.getCodigo()>cod)
            return 1;
        else
            return 0;
        
    }
}

Maternidad:
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.Scanner;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author NikNitro
 */
public class Maternidad {
    private final int incubadora = 0;
    private SortedMap<Persona, SortedSet<Persona>> pacientes;
    
    public Maternidad() {
        pacientes = new TreeMap<>();
    }
    public Maternidad(String std) {
        pacientes = new TreeMap<>();
        addPacientesFichero(std);
        
    }
    
    public void addPacientesFichero(String std){
        File f = new File(std);
        try {
            Scanner sc = new Scanner(f);
            sc.useDelimiter("\n");
            addPacientes(sc);
        } catch (FileNotFoundException ex) {
            Logger.getLogger(Maternidad.class.getName()).log(Level.SEVERE, null, ex);
        } catch (MaternidadException e ) {
            System.err.printf("Existe algún error con el archivo dado");
        } 
    }
    
    public void addPacientes(Scanner sc) throws MaternidadException{
        String linea, persona, nombre;
        int codigo, habitacion;
        boolean esMadre = true;
        OrdAlt madre= null;
        Persona bebe;
        Collection<Persona> bebes = null;

        while(sc.hasNext()) {   //Vemos cada línea
            linea =sc.next();
            Scanner separador = new Scanner(linea);
            separador.useDelimiter("#");
            
            while(separador.hasNext()) {    //Vemos cada persona en una línea
                try{
                    persona = separador.next();
                    Scanner dosPuntos = new Scanner(persona);   //Vemos los datos de cada persona
                    dosPuntos.useDelimiter(":");
                    nombre = dosPuntos.next();
                    codigo = Integer.parseInt(dosPuntos.next());
                    habitacion = Integer.parseInt(dosPuntos.next());
                    if(esMadre) {
                        madre = new OrdAlt(nombre, codigo, habitacion);
                        esMadre = false;
                        bebes = new TreeSet<>();
                    } else {
                        bebe = new Persona(nombre, codigo, habitacion);
                        bebes.add(bebe);
                        
                    }
                } catch(NumberFormatException | MaternidadException e) {
                    throw new MaternidadException();
                }
            }
            //Aquí ya hemos rellenado a la madre y el treeSet con una línea.
            addMadreBebes(madre, bebes);
            esMadre = true;
        }
    }
    
    public void addMadreBebes(Persona madre, Collection<Persona> bebes) {
        if(pacientes.containsKey(madre))
            
                //Es la forma nueva de java 8. La antigua sería "for(Persona b : bebes)"
            bebes.stream().forEach((b) -> {
                pacientes.get(madre).add(b);
            });
        else {
            SortedSet bebe = new TreeSet<>();
            bebes.stream().forEach((b) -> {
               bebe.add(b);
            });
            pacientes.put(madre, bebe);
        }
    }
    
    @Override
    public String toString() {
        String std="";
        for (Persona p : pacientes.keySet()) {
            std = std + p + "#";
            for(Persona b : pacientes.get(p)) {
                std = std + b + "#";
            }
            std = std+"\n";
        }
        return std;
    }
    
    public void escribirFichero(String s) {
        File f = new File(s);
        PrintWriter p;
        try {
            p = new PrintWriter(f);
            escribir(p);
        } catch (FileNotFoundException ex) {
            Logger.getLogger(Maternidad.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    public void escribir(PrintWriter p) {
        p.append(this.toString());
    }
    
    public double mediaBebes() {
        double numMadres = 0, numBebes = 0;
        for(Persona p : pacientes.keySet()) {
            SortedSet bebes = pacientes.get(p);
            if(bebes.size()>0) {
                numMadres++;
                numBebes += bebes.size();
            }
                
        }
        return numBebes/numMadres;
    }
    
    public int encontrarMadre(int codigoBebe) throws MaternidadException{
        for(Persona m : pacientes.keySet()) 
            for(Persona b : pacientes.get(m)) {
                 if(b.getCodigo()== codigoBebe)
                     return m.getHabitacion();
            }
            
        throw new MaternidadException("Madre no encontrada");
        
    }
}

Os aviso de que el código aún no está probado, por lo que si veis cualquier cosilla que de error o que no cuadre, por favor avisadme para que lo arregle.

Saludos y gracias por visitarme;)

sábado, 21 de marzo de 2015

MIPS: Como calcular la siguiente fila a una dada en la pirámide de Tartaglia.

Buenas gente. Hace una semana se me ocurrió plantear éste ejercicio para mis alumnos de Computadores. Ya lo hemos hecho bien y os lo traigo aquí.
Decir que lo hemos desarrollado en forma de función para poder utilizarlo más adelante en otras funciones más complejas.
La función toma en $3 la dirección donde comienza el array, en $7 el tamaño del array y en $2 la dirección de memoria donde guardaremos el resultado (sería algo así como pasar un parámetro por referencia en C o C++).

He de decir, por último, que para que funcione bien con el primer elemento no hay que pasarle un [1], sino un [1, 1]. Ésto lo hemos hecho por temas de simplicidad, pero si hiciera falta cambiarlo, sería muy simple.

Dicho ésto, os dejo aquí el código ;)

.data
array: .word 1 3 3 1
tam: .word 4
res: .word -1

.text

 lw $7, tam($0) #Tamaño
 la $3,array
 la $2,res
 jal Tar
 li $2,10
 syscall
 
Tar: add $8,$0,$2 #Indice Mem. Res
 add $5,$3,$0 #Índice Mem. Array
 addi $6,,$0,1  #Constante 1
 sw $6,0($8) #res[0]=1
 addi $10,$0,1  #i del For 
loop: slt $4,$10,$7
 beq $4,$0,FinFor
 lw $12,0($5) #Array +$8 
 addi $5,$5,4 
 addi $8,$8,4  #Sumamos 4 al Índice
 lw $13,0($5) #Array +$8
 add $21,$12,$13 #Guarda en $21 la suma $12+$13
 sw $21,0($8) #Guarda en $21 en res $8
 addi $10,$10,1 #i++
 j loop  #Bucle
FinFor: addi $8,$8,4  #Suma 4 a lo que hay en $8
 sw $6,0($8) 
 jr $31

No es complicado, ¿verdad? Si no lo entendéis del todo escribidme y subo el código que se usaría en C++ o Java con la misma función, para que lo entendáis mejor.

Saludos;)

domingo, 8 de marzo de 2015

HTML POST PHP Tablas de multiplicar

Hola gente :)
He estado haciendo un par de pruebas con PHP y quería compartirlas. Aviso desde ya que son cosas muy básicas.

Para montar mi lugar de trabajo he instalado XAMPP, que es una aplicación gratuita que te permite montar en tu máquina un servidor con PHP, MySQL y un montón más de funcionalidades que espero usar :)

Para programar he usado NetBeans, que aunque traga muchos recursos, mi ordenador lo soporta de sobra y me ha gustado el funcionamiento que tiene.

Pues he empezado haciendo un formulario simple a más no poder en HTML para pasarle un número al PHP. Éste PHP se encargará de tomar el número y calcular la tabla de multiplicar del 1 al 10 del mismo número.
Luego probé (con un for) a que imprimiera todas las tablas entre 1 y 20. De ésta forma practicamos tanto la recogida de datos (por medio del método POST) y los bucles y el uso de variables.

Si veis que os sale feo o que hay etiquetas que no conocéis en el HTML, es porque estoy usando Bootstrap. Si queréis saber más sobre ello, preguntad ;)

Aquí el código (demasiado simple) del index.php (también puede ser index.html pero por comodidad les he puesto a todos la misma extensión):

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Tablas de multiplicar</title>
    </head>
    <body>
        
        <form method="POST" name="tabla" role="form" action="tablas.php" enctype="multipart/form-data">
            <div class="form-group">
                <label for="exampleInputNombre">Introduzca un número</label>
                <input name="number" type="text" class="form-control" id="numero" placeholder="Escribe un numero">
                <button type="submit" class="btn btn-default">Enviar</button>
            </div>
        </form>
    </body>
</html>
La web resultante tendría únicamente un formulario tal que así:

Como veis, la complejidad es nula. Vamos ahora con el "tablas.php":

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Tablas de multiplicar</title>
        <link href="css/bootstrap.min.css" rel="stylesheet">
        <script src="https://code.jquery.com/jquery.js"></script>
        <script src="js/bootstrap.min.js"></script>
    </head>
    
    <body>
        <!-- Botón para volver atrás -->
        <div class="row">
            <div class="col-md-12 well"><a href="index.php"><button class="btn btn-default">Atrás</button></a> </div>
        </div>
        <!-- Tabla del número elegido-->
        <table  class="table table-hover table-bordered ">
        <?php
        $num = $_POST["number"];
        echo ("<tr>");
        // Cabecera 
        for ($i = 1; $i < 11; $i++) {            
            echo ("<th>");
            echo "$i";
            echo ("</th>");
        }
        
        echo ("</tr>");
        
        // Resultados 
        echo ("<tr>");

        for ($i = 1; $i < 11; $i++) {            
            echo ("<td>");
            $aux = $num*$i;
            echo "$aux";
            echo ("</td>");
        }
        
        
        echo ("</tr>");
        
        
        ?>
            
            <!-- Tabla genérica, creada al ejecutar el php -->
        </table>
        <br><br>
        <p class="active"> 
            Ahora una tabla más genérica:
        </p>
        <br>
        <table  class="table table-hover table-bordered ">
        <?php
            
            echo ("<tr>");

            for ($i = 1; $i < 11; $i++) {            
                echo ("<th>");
                echo "$i";
                echo ("</th>");
            }

            echo ("</tr>");

            for($j=1; $j<21; $j++) {

            for ($i = 1; $i < 11; $i++) {            
                echo ("<td>");
                $aux = $j*$i;
                echo "$aux";
                echo ("</td>");
            }


            echo ("</tr>");
            }

        ?>
        </table>
    </body>
</html>

Espero que no lo veáis demasiado complejo. Si queréis imágenes de cómo queda o tenéis cualquier duda/sugerencia, no dudéis en escribirnos. Estaremos encantados de responderos.

Saludos y hasta otra ;)

viernes, 6 de marzo de 2015

Validación de formulario HTML5 con JavaScript

Os dejo un pequeño y simple formulario hecho mediante HTML5 con la correspondiente comprobación de sus campos mediante JavaScript.
Lo que se pretende hacer es que cuando el usuario pulse el botón de envío del formulario,se ejecute el código JavaScript que debe comprobar:

  1. que los campos nombre y apellidos no estén vacíos
  2. que la dirección de correo electrónico esté bien formada (aparece @ en un carácter que no es el primero, y tiene al menos un punto tras la arroba) 
  3. que la contraseña tiene al menos 8 caracteres y
  4. que la contraseña con su repetición.
Si no es así se muestra una alerta al usuario indicando el problema y se cancela el envío, limpiando los campos de contraseña. Si todo es correcto se procede con el envío.

Os dejo primero el código html (puede que haya algún error con el tema de formato por los < > y las etiquetas debido al copia y pega).

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <script src="codigo.js" type="text/javascript"></script> <!-- Para incluir el fichero JavaScript-->
  <title>Formulario</title> 
  <meta name="author" content="Aprendiendo a programar entre todos">
 </head>
 <body>
  <form method="post" action="mailto:aprendiendoaprogramarentretodo@gmail.com" name="formulario" enctype="text/plain" onsubmit="return validar();">
   <table>
    <tr>
     <td>Nombre</td>
     <td><input type="text" size="25" name="nombre"></td>
    </tr>
    
    <tr>
     <td>Apellidos</td>
     <td><input type="text" size="25" name="apellidos"></td>
    </tr>
    
    <tr>
     <td>Correo electrónico</td>
     <td><input type="text" size="25" name="email"></td>
    </tr>
    
    <tr>
     <td>Contraseña</td>
     <td><input type="password" size="25" name="pass"></td>
    </tr>
    
    <tr>
     <td>Repita contraseña</td>
     <td><input type="password" size="25" name="rpass"></td>
    </tr>

                                <tr>
                                        <td>Sexo (opcional) </td>
                                        <td>
                                             <input type="radio" name="sexo" value="mujer"> Mujer
                                                <input type="radio" name="sexo" value="hombre">Hombre<br>
                                        </td>
                                         
                                </tr>
                                <tr>
                                        <td><input type="checkbox" name="sesion" value="v_sesion">Deseo recibir ofertas</td>
                                        <td> </td>
                                </tr>                                                                

    
    <tr>
     <td><input type="reset" value="Reset"></td>
     <td><input type="submit" value="Enviar"></td>
    </tr>
    
   </table>
   
  </form>

 </body>
</html>


Y aquí el código JavaScript que valida los campos del formulario de la manera indicada anteriormente, comprobando de forma muy clara para que lo podáis ver bien cada una de las restricciones que se indican en el enunciado.
function resetPass (){
    document.formulario.pass.value = "";
    document.formulario.rpass.value = "";
    return false;
}

function validar (){
    var nombre = document.formulario.nombre.value;
    var apellidos = document.formulario.apellidos.value;
    var email = document.formulario.email.value;
    var pass =  document.formulario.pass.value;
    var rpass =  document.formulario.rpass.value;
    if(nombre === "" || apellidos === ""){
        alert("Error: nombre incompleto");
        return resetPass();
    }else if (email.indexOf('@') < 1){
        alert("Error en email (debe aparecer @)");
        return resetPass();
    }else if(email.lastIndexOf(".")  < email.indexOf('@') ){
        alert("Error en email (debe aparecer un .)");
        return resetPass();
    }else if(pass.length < 8){
        alert("Contraseña demasiado corta");
        return resetPass();     
    }else if(pass !== rpass){
       alert("Contraseñas no coinciden");
        return resetPass();
    }else{
       return true; 
    }
}

Está claro que es un ejemplo muy sencillo y realizado de forma muy básica, pero por algo se empieza ;)

jueves, 5 de marzo de 2015

LinkedArray en C

Hola gente, cuánto tiempo!! :)
Lo siento por mi desaparición pero estoy demasiado perdido entre estudiar y trabajar... Además estoy aprendiendo front-end en condiciones y.., le faltan horas a mi día. Creo que me voy a comprar un reloj de esos de 40 horas a ver...

Os traigo un código que les he puesto a mis alumnos de la academia. El enunciado es simple y tal que así:
Crea en C (con cabecera y código separados) una estructura de Array en Nodos, en la que cada Nodo tendrá
como atributos un entero (valor), un entero (tam) y un puntero al siguiente Nodo. Además debes crear las
siguientes funciones:
Crear(Primero* primero, int n) Crea todos los nodos inicializándolos a cero.
Destruir(Primero* primero) Borra todos los nodos, liberando el espacio de disco.
Obtener(Primero primero, int index, int *ok, int *valor) Devuelve en valor el valor de ese trozo de array. En
ok devuelve un 1 si se ha podido y un 0 en caso contrario.
Cambiar(Primero* primero, int index, int valor) Cambia el valor de la posición index por el nuevo valor
Mostrar(Primero primero) Imprime el array.

Aquí tenéis la cabecera:
#ifndef MAIN_H
#define MAIN_H

typedef struct Array* Primero;
struct Array {
    int valor;
    int tam;
    Primero sig;
};

void Crear(Primero* primero, int n);
void Destruir(Primero* primero);
void Obtener(Primero primero, int index, int *ok, int *valor);
void Cambiar(Primero* primero, int index, int valor);
void Mostrar(Primero primero);


#endif /* MAIN_H */


Y aquí os dejo el código:

#include <stdio.h>
#include <stdlib.h>
#include "main.h"

/*
 * 
 */

void Crear(Primero* primero, int n) {
     *primero = (Primero)malloc(sizeof(struct Array));
     (*primero)->tam = n;
     (*primero)->valor = 0;
     (*primero)->sig = NULL;
     Primero ant = *primero;
     int i;
     for(i=1; i<n; i++) {
         Primero nuevo;
         nuevo = (Primero)malloc(sizeof(struct Array));
         nuevo->valor=0;
         nuevo->tam = (*primero)->tam;
         nuevo->sig = NULL;
         ant->sig = nuevo;
         ant = ant->sig;
     }
     
}
void Destruir(Primero* primero) {
    Primero ptr;
    while (*primero != NULL) {
        ptr=(*primero)->sig;
        free((void *)*primero);
        *primero=ptr;
    }
}
void Obtener(Primero primero, int index, int *ok, int *valor) {
    
    if((*valor)<primero->tam) {
    int i;
    Primero aux = primero;
    for(i = 0; i < index; i++) {
        aux = aux->sig;
    }
    (*valor)=aux->valor;
    (*ok) = 1;
    } else {
        (*ok) = 0;
    }
}
void Cambiar(Primero* primero, int index, int valor) {
    int i;
    Primero aux = *primero;
    for(i = 0; i < valor; i++) {
        aux = aux->sig;
    }
    aux->valor = valor;
    
}
void Mostrar(Primero primero) {
    int ultimo = primero->tam;
    int i;
    printf("{");
    for(i=0; i < ultimo-1; i++) {
        printf("%d, ", primero->valor);
        primero = primero->sig;
    }
    printf("%d}", primero->valor);
    fflush(stdout);
}

int main() {
    printf("Vamos a crear un array\n");
    fflush(stdout);
    Primero p;
    Crear(&p, 5);
 //   int ok;
 //   int valor;
//    Obtener(p, 3, &ok, &valor);
//    printf("%d", valor);
 //   fflush(stdout);
   Cambiar(&p, 2, 1);
   Cambiar(&p, 3, 4);
    Mostrar(p);
    Destruir(&p);
    return (EXIT_SUCCESS);
}

Pues eso gente, si necesitáis ayuda con algo pedidla por aquí (no me metáis más de 2 faltas de ortografía por palabra por favor) y espero que os sigan sirviendo todas éstas cosillas ;)

Saludos

lunes, 9 de febrero de 2015

Ejercicios de ensamblador en MIPS

¿Qué pasa gente? Demasiado tiempo sin publicar nada...
Hoy traigo unos ejercicios en MIPS de unos alumnos de Electrónica, Robótica y Mecatrónica. Os los voy poniendo:

El primero dice asi:
Diseña un programa ensamblador que defina el vector de enteros de dos elementos V=(10,20) en la memoria de datos a partir de la dirección 0x10000000 y almacene su suma a partir de la dirección donde acaba el vector.
.data 0x10000000
vector: .word 10, 20 # Vector de elementos
res: .word 0  # Donde guardaremos el resultado
.text

 addi $8, $0, 4   # Guardo un 4 para pedir la segunda palabra del vector.
 lw $9, vector($0)
 lw $10, vector($8)
 
 add $11, $9, $10  # Sumamos en un temporal el resultado
 sw $11, res($0)   # Guardamos en memoria el resultado.
 
 li $2, 10
 syscall
Por aquí el segundo:
Diseña un programa ensamblador que divida los enteros 18,-1215 almacenados a partir de la dirección 0x10000000 entre el número 5 y que a partir de la dirección 0x10010000 almacene el cociente de dichas divisiones.

.data 0x10000000
entero1: .word 18
entero2: .word -1215
.data 0x10010000
res1: 0
res2: 0

.text
 addi $5, $0, 5
 # Primero un numero y luego el otro.
 lw $8, entero1($0)
 div $9, $8, $5
 sw $9, res1($0)
 
 # Ahora el segundo.
 lw $8, entero2($0)
 div $9, $8, $5
 sw $9, res2($0)
 
 li $2, 10
 syscall
 

El tercero lo he hecho usando una función AND y un número que en binario es todo unos excepto los bits que quiero poner a 0
Pon a cero los bits 3,7,9 del entero 0xabcd12bd almacenado en memoria a partir de la dirección 0x10000000, sin modificar el resto.
.data 0x10000000
valor: .word 0xabcd12bd
.text 
 addi $8, $0, 0xDD7FFFFF   
 lw $9, valor($0)
 and $10, $8, $9
 sw $10, valor($0)
 
 li $2, 10
 syscall

Y el cuarto ejercicio (es el sexto en la relación, pero los anteriores tampoco tenían mucha dificultad) dice tal que así:
Diseña un programa en ensamblador que defina un vector de enteros, V, inicializado según los siguientes valores (V=[2, -4, -6]). Y obtenga un vector de booleanos, tal que cada elemento será 1 si el correspondiente elemento en el vector de enteros es mayor o igual que cero y 0 en caso contrario.

.data 0x10000000
vector: .word 2, -4, -6
tam: .word 3
res: .word -1, -1, -1
.text 
 addi $2, $0, 1   # Constante 1 
 add $6, $0, $0  # Indice del array
 add $7, $0, $0  # Contador
 lw $8, tam($0)  # Tamaño a llegar
loop: 
 lw $9, vector($6) #Elemento del vector
 slt $10, $9, $0  # Si el segundo es menor que el tercero, el primero vale 1
 beq $10, $0, esPositivo
 # Si sigue por aquí es negativo
 sw $0, res($6)
 j vuelta
 
esPositivo:
 sw $2, res($6)
 j vuelta 
 
vuelta: addi $7, $7, 1
 addi $6, $6, 4
 bne $7, $8 loop
 
 li $2, 10
 syscall
 

Eso es todo. Espero que os sirvan. Si tenéis alguna duda comentadla.

Saludos;)

jueves, 31 de julio de 2014

Diferencia entre iteración y recursión en ensamblador (MIPS)

Buenas gente. Últimamente os tenemos demasiado abandonados. Mea culpa. Estamos los dos de viajes a todos lados (me fuí a Valencia a la DreamHack) y a la vuelta empecé a trabajar en una academia. No tengo tiempo para nada. Es totalmente estresante. Trabajo de mañana y de tarde pero bueno.., hay que comer no?

Hoy os traigo un code simple en MIPS tanto en forma iterativa como recursiva para que veais las diferencias entre ambos así como el uso de la pila. Debo decir que (en éste codigo concreto) la versión iterativa es más eficiente que la recursiva, pero no siempre es así.
El código lo único que hace es coger y multiplicar los registros $4 y $5 y guardar el resultado en $2, pero lo hace simando $5 veces $4.
Dicho ésto, aquí vuestro código iterativo:

Y aquí vuestro código recursivo:


Espero que veais la diferencia. Hay que añadir que en todos los códigos recursivos (ya que hay que usar la pila) es necesario hacer push y pop, donde push siempre es restar un múltiplo de 4 a $29 ($sp) y guardar los valores con sw en esa memoria; y pop es la operación inversa.

Espero que os haya quedado claro y que nos perdoneis por no ser puntuales :P
Un saludo y gracias ;)

viernes, 18 de julio de 2014

Ejercicios C++: Strings (I)

Aquí unos ejercicios en los que usamos strings y todo lo aprendido anteriormente sobre Registros y arrays.

Diseñar un algoritmo (y desarrollar los subalgoritmos C++ correspondientes y el main para probarlo) que permita traducir una cadena numérica al número natural que representa:
Entrada: 7805 (hay que leer el dato como un string)
Salida: 7805 (hay que mostrar el dato como un unsigned)
#include <iostream>
#include <string>


using namespace std;

void leerCadena(string & cadena){
 cout << "Introduce cadena numérica: ";
 cin >> cadena;
}

unsigned potenciaDiez(unsigned exp){
 unsigned resultado=1;
 for(unsigned i = 1; i <= exp; i++){
 resultado = resultado * 10;
 }
 return resultado;
}

void transformar (string cadena, unsigned& num){
 num = 0;
 unsigned n;
 for(unsigned i= 0 ; i < cadena.size(); i++){
  n = (int(cadena[i]))-(int('0'));
  num = num + n * potenciaDiez(cadena.size()-1-i);
 }
}

int main() {
 string cadena;
 unsigned num;
 leerCadena(cadena);
 transformar(cadena, num);
 cout << "El número con el que se corresponde es el: "<< num;
 return 0;
}


Una palabra w es un anagrama de la palabra v, si podemos obtener w cambiando el orden de las letras de v. Por ejemplo, VACA es un anagrama de CAVA.
Diseñar un algoritmo (y desarrollar los subalgoritmos C++ correspondientes y el main para probarlo) que lea un texto y determine de cuántas palabras es anagrama la primera que aparece dentro de dicho texto. Hay que tener en cuenta que:

  • El texto contiene un número indefinido de palabras.
  • El texto termina con la palabra FIN.
  • Cada palabra tiene un número indefinido pero limitado de caracteres (todos alfabético mayúsculas).
  • El carácter separador de palabras es el espacio en blanco.

#include <iostream>
#include <string>
using namespace std;


void esAnagrama(string palabra, string texto){
 unsigned contador = 0;
 bool loes = false;
 string aux = texto;
 if(palabra.size() == texto.size()){

  for(unsigned i= 0; i < palabra.size(); i++){

   unsigned j= 0;
   loes = false;


   while(j < texto.size() && loes== false){

     if(palabra[i]== aux[j]){
      loes = true;
      contador++;
      aux[j]= ' ';
     }

    j++;
   }
  }

 }

 if(contador == palabra.size()){
  cout << texto << " es anagrama de "<< palabra << endl;
 }else{
  cout << texto << " no es anagrama de "<< palabra << endl;
 }
}


void introducirTexto(){
 string palabra, texto;
 cout << "Introduce texto: "<< endl;
 getline(cin, palabra, ' ');
 //getline(cin, texto, ' ');
 cin >> texto;
 while(texto != "FIN"){
  esAnagrama(palabra, texto);
  cin >> texto;
 }
}


int main() {
 introducirTexto();
 return 0;
}


Supongamos que deseamos evaluar a un determinado número de alumnos siguiendo el criterio de que aprobará una determinada evaluación aquel que supere o iguale la nota media de la clase en dicha evaluación.
Diseñar un algoritmo (y desarrollar los subalgoritmos C++ correspondientes y el main para probarlo) que lea por teclado un número de alumnos, N_alumnos (como máximo podrá tomar el valor de MAX_ALUMNOS=20), y las notas de tres evaluaciones para cada alumno, N_EVALUACIONES=3, y como resultado emita un informe indicando para cada alumno el resultado de cada evaluación (Aprobado o Suspenso).
#include <iostream>
using namespace std;

const unsigned MAX_ALUMNOS=20;
const unsigned N_EVALUACIONES=3;

typedef double notasAlumno[N_EVALUACIONES];

struct TAlumnos{
 string nombre;
 notasAlumno notas;
};

typedef TAlumnos ArrayAlumnos[MAX_ALUMNOS];

unsigned leerNalumnos(){
 unsigned N_alumnos;
 do{
  cout << "Introduce el número de alumnos (<= "<<MAX_ALUMNOS<<"): ";
  cin >> N_alumnos;
 }while(N_alumnos > MAX_ALUMNOS);
 return N_alumnos;
}

void leerAlumnos(unsigned N_alumnos, ArrayAlumnos& alumnos){
 for(unsigned i=0; i < N_alumnos; i++){

  cout << "Introduce el nombre y 3 notas: ";
   getline(cin, alumnos[i].nombre, ' ');
   for(unsigned j = 0; j < N_EVALUACIONES; j++){
    cin >> alumnos[i].notas[j];
   }
 }
}

double notaMedia (unsigned N_alumnos, const ArrayAlumnos& alumnos, unsigned evaluacion){
 double suma = 0;
 for(unsigned i= 0; i < N_alumnos; i++){
  suma = suma + alumnos[i].notas[evaluacion-1];

 }
 return suma/N_alumnos;
}






void notasAlumnos(unsigned N_alumnos, const ArrayAlumnos& alumnos){
 cout << "Alumno \t Nota 1 \t Nota 2 \t Nota 3" << endl;
 cout << "-------------------------------------------------------";
 for(unsigned i=0; i < N_alumnos; i++){

  cout << alumnos[i].nombre << "\t";

  for(unsigned ev = 1; ev <= N_EVALUACIONES; ev++){
   double media = notaMedia(N_alumnos, alumnos, ev);
   //cout << media << endl;
   if(alumnos[i].notas[ev-1] >= media){
    cout << "Aprobado \t";
   }else{
    cout << "Suspenso \t";
   }

  }

  cout << endl;


 }
}

int main() {
 
 unsigned N_alumnos = leerNalumnos();

 ArrayAlumnos alumnos;
 leerAlumnos(N_alumnos, alumnos);
 notasAlumnos(N_alumnos, alumnos);

 return 0;
}
Diseñar un algoritmo (desarrollando los subalgoritmos C++ correspondientes y el main para probarlo) que se comporte como una calculadora que pida repetidamente un operador de conjuntos y dos operandos que sean conjuntos de letras minúsculas y que escriba el resultado de la operación. Las operaciones se expresan como caracteres, siendo válidas las siguientes:
+ Unión de conjuntos
- Diferencia de conjuntos
* Intersección de conjuntos
El proceso se repetirá hasta que se introduzca como código de operación el carácter ‘&’. Los operadores y el resultado se expresan como cadenas de caracteres. Ejemplo:
Operación = *
Operando1 = azufre
Operando2 = zafio
Resultado = afz
Operación = -
Operando1 = abril
Operando2 = arco
Resultado = bil
Operación = &
FIN
#include <iostream>
#include <string>
using namespace std;

string restaString(string op1, string op2){
 string res;
 bool esta;
 for(unsigned i=0; i < op1.size(); i++){
  esta = false;
  unsigned j = 0;
  while(esta == false && j < op2.size()){
   if(op1[i] == op2[j]){
    esta = true;
   }

   j++;
  }

  if(!esta){
   res = res + op1[i];
  }

 }
 return res;
}

string interseccion(string op1, string op2){
 string res;
  bool esta;
  for(unsigned i=0; i < op1.size(); i++){
   esta = false;
   unsigned j = 0;
   while(esta == false && j < op2.size()){
    if(op1[i] == op2[j]){
     esta = true;
    }

    j++;
   }

   if(esta){
    res = res + op1[i];
   }

  }
  return res;
}

string realizarOperacion(char operacion, string op1, string op2){
 string resultado;
 switch (operacion){
 case '+': resultado = op1 + op2;;
  break;
 case '-': resultado = restaString(op1,op2);
  break;
 case '*': resultado = interseccion(op1, op2);
  break;
 }

 return resultado;
}


void introducirDatos(){
 char operacion;
 string op1, op2;




 do{
 cout << "Operación: ";
 cin >> operacion;
 } while(operacion != '+' && operacion != '-' && operacion != '*' && operacion != '&');

 while(operacion != '&'){
 cout << "Operando 1: ";
 cin >> op1;
 cout << "Operando 2: ";
 cin >> op2;


 string resultado = realizarOperacion(operacion, op1, op2);
 cout << "Resultado: "<< resultado << endl;

 do{
  cout << "Operación: ";
  cin >> operacion;
  } while(operacion != '+' && operacion != '-' && operacion != '*' && operacion != '&');

 if(operacion == '&'){
  cout << "FIN";
 }
 }


}




int main() {
 cout << "Operaciones con strings" << endl; 
 introducirDatos();
 return 0;
}

viernes, 11 de julio de 2014

Mi primera App con Android. BPCalculator.

Buenas gente, ¿qué tal? Acabo de depurar la aplicación que hice antes de ayer y ya os puedo mostrar de qué va. Os cuento... Resulta que por ser de la UMA, tengo una tarjeta para la BP mediante la cual me descuentan 5 céntimos por litro, pero me los dan en mano. Pues como hay veces que llevo 4,60 y pocos euros y cosas así, me decidí a hacer una calculadora con la que poder ver lo que me iba a costar y cuánto me iban a echar. Ésta es la version 1.0. Quiero poder hacer que busque directamente el precio, pero eso será más adelante cuando aprendamos a conectar la aplicación con la web. El proyecto en git lo tenéis por aquí.

Antes de nada os aconsejaría que en vez de usar las máquinas virtuales de Android uséis vuestro teléfono, que irá mil veces mejor (ayer me dijeron que en el eclipse de Linux, las máquinas van perfectas; pero aún no lo he podido probar). Si no sabéis cómo conectar el móvil, preguntad ;)

Aquí tenéis una imagen del aspecto de la aplicación:


Ésto lo podéis hacer editando el activity.xml (es uno de los dos archivos que se os abren). De todos modos, aquí os dejo el mío:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.NikNitro.bpcalculator.Calculator" >

    <GridLayout
        android:id="@+id/gridLayout1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:layout_marginTop="25dp"
        android:alignmentMode="alignBounds"
        android:columnCount="2"
        android:useDefaultMargins="true" >

        <TextView
            android:id="@+id/textView1"
            android:layout_width="102dp"
            android:layout_column="0"
            android:layout_gravity="left|top"
            android:layout_row="0"
            android:text="Precio"
            android:textAppearance="?android:attr/textAppearanceLarge" />

        <TextView
            android:id="@+id/textView2"
            android:layout_column="1"
            android:layout_gravity="center"
            android:layout_row="0"
            android:text="Cantidad(€)"
            android:textAppearance="?android:attr/textAppearanceLarge" />

        <EditText
            android:id="@+id/Cantidad"
            android:layout_width="106dp"
            android:layout_column="0"
            android:layout_gravity="left|center_vertical"
            android:layout_row="1"
            android:ems="10"
            android:inputType="numberDecimal" />

        <EditText
            android:id="@+id/Precio"
            android:layout_width="121dp"
            android:layout_column="1"
            android:layout_gravity="center"
            android:layout_row="1"
            android:ems="10"
            android:inputType="numberDecimal" />

    </GridLayout>

    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/gridLayout1"
        android:layout_below="@+id/gridLayout1"
        android:layout_marginTop="26dp"
        android:text="Aceptar" />

    <GridLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/button"
        android:layout_below="@+id/button"
        android:layout_marginTop="75dp"
        android:columnCount="2"
        android:useDefaultMargins="true" >

        <TextView
            android:id="@+id/textView3"
            android:layout_width="126dp"
            android:layout_column="0"
            android:layout_gravity="right|top"
            android:layout_row="0"
            android:text="Vas a Pagar"
            android:textAppearance="?android:attr/textAppearanceLarge" />

        <TextView
            android:id="@+id/textView4"
            android:layout_width="124dp"
            android:layout_gravity="left"
            android:text="Vas a Tener"
            android:textAppearance="?android:attr/textAppearanceLarge" />

        <TextView
            android:id="@+id/Pago"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="left"
            android:textAppearance="?android:attr/textAppearanceLarge" />

        <TextView
            android:id="@+id/Compra"
            android:layout_gravity="left"
            android:textAppearance="?android:attr/textAppearanceLarge" />

    </GridLayout>

</RelativeLayout>
Como veis no es nada difícil de comprender (no digo nada de hacerlo, solo de entenderlo :P). Bueno, aquí os dejo ahora el código de la aplicación (el cual es más sencillo aún).

package com.NikNitro.bpcalculator;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class Calculator extends Activity implements OnClickListener{
 //Botón
 private Button boton;
 //Texto Editable por el usuario
 private EditText litros, precio;
 //Texto SÓLO legible por el usuario
 private TextView compra, preciofinal;
 // Variables enteras
 private double dlitros, dprecio, dcompra, dpreciofinal;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  //Éste método se ejecuta el primero al iniciar la aplicación.
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_calculator);
  
  initialize();
 }

 private void initialize() {
  // TODO Auto-generated method stub
  boton = (Button)findViewById(R.id.button);
  //Añado al botón el controlador.
  boton.setOnClickListener(this);
  //Así es como se inicializan los objetos para que queden
  // linkeados con el activity.
  litros = (EditText)findViewById(R.id.Cantidad);
  precio = (EditText)findViewById(R.id.Precio);
  compra = (TextView)findViewById(R.id.Compra);
  preciofinal = (TextView)findViewById(R.id.Pago);
  
  
 }

 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  // Inflate the menu; this adds items to the action bar if it is present.
  getMenuInflater().inflate(R.menu.calculator, menu);
  return true;
 }

 @Override
 public boolean onOptionsItemSelected(MenuItem item) {
  // Handle action bar item clicks here. The action bar will
  // automatically handle clicks on the Home/Up button, so long
  // as you specify a parent activity in AndroidManifest.xml.
  int id = item.getItemId();
  if (id == R.id.action_settings) {
   return true;
  }
  return super.onOptionsItemSelected(item);
 }

 @Override
 public void onClick(View v) {
  // Al hacer click en algún botón, se llama aquí.
  //Si tuvieramos más de un botón tendríamos que ver a cual se refiere. Para ello usamos la v que nos pasan.
  calcular();
  mostrarToast("Cálculo completo");
 }

 private void mostrarToast(String string) {
  //Muestra un mensaje
  Toast texto1 = Toast.makeText(getBaseContext(), string, Toast.LENGTH_SHORT);
  texto1.show();
  
 }

 private void calcular() {
  try {
   dlitros = Double.parseDouble(litros.getText().toString());
   dprecio = Double.parseDouble(precio.getText().toString());
   double prec = Math.rint(dprecio*100/dlitros)/100;
   compra.setText(prec+"");
   double desc = prec*0.05;
   preciofinal.setText((dprecio-desc)+"");
  } catch (Exception e) {
   mostrarToast("Rellene todos los campos correctamente, por favor");
  }
  
 }
 
 
}

Jeje, bueno, espero que no os queden dudas y (si os quedan) las preguntéis, pues estaré encantado de responderos. Un saludo y hasta la próxima ;)

miércoles, 9 de julio de 2014

Arduino: Control de un motor con velocidad variable.

Qué pasa gente? Aquí os traigo una nueva curiosidad si estáis empezando en Arduino. Si queréis hacer algo con motores, lo primero que notaréis es que el motor corre a una velocidad exagerada. Hoy aprenderemos a controlarla con un sencillo programita. Para ello utilizaremos los pines PWD (los que tienen un signo ~ junto al número) pues éstos nos permiten variar la velocidad. Antes de nada os dejo un video del proyecto, para que veais cómo va:

El esquema que usaremos será el siguiente:

Conexiones.
Por si no se ve bien, el amarillo va a la salida de 5V, el negro al GND y el verde a la salida ~3.
Ahora os dejo el código:

int motor=3;     //Declara Pin del motor

void setup() {
  Serial.begin(9600); 
  Serial.println("Eliga de 0 a 9 la velocidad de giro del motor");
}


void loop() {

  // Si hay algun valor en la Consola Serial
  if (Serial.available()){

    //Variable donde se guarda el caracter enviado desde teclado
    char a = Serial.read();

    // Si el caracter ingresado esta entre 0 y 9
    if (a>='0' && a<='9'){ 

      //Variable para escalar el valor ingresado a rango de PWM
      int velocidad = map(a,'0','9',0,255);
      //Escritura de PWM al motor
      analogWrite(motor,velocidad);
      //Mensaje para el usuario
      Serial.print("El motor esta girando a la velocidad ");
      Serial.println(a);

    } else { 

      Serial.print("Velocidad invalida");
      Serial.println(a);

    }
  }
}

Como veis no es nada demasiado complicado. Una vez hecho tenéis que abrir la consola desde el programa de Arduino (Herramientas->Monitor Serial ó Control+Mayus+M).

Eso es todo. Espero que os hayais enterado bien y hasta la próxima.
Saludos;)

lunes, 7 de julio de 2014

Empezando con Android. Instalación.

Buenas gente. Llevo todo el año con un par de proyectillos en mente así que éste finde me decidí a empezar a darle caña a Android... Hasta hoy no he podido programar, por lo que espero dejaros medianamente claro cómo hay que instalarlo.

Comencé con Android Studio, pero me daba bastantes fallos (Muchos de ellos al buscar en Google solo encontrabas solución para otras versiones). Tiene mucho potencial pero aún está en BETA (versión 0.8.1).

Al fin (tras cargarme, Eclipse, Java...) empecé de 0. Instalé JRE, JDK, Instalé Eclipse (me decía que no podía abrir, pero busqué y abrí el archivo javaw.exe y justo después me dejaba abrir Eclipse. No me ha vuelto a dar fallos).

Ahora que tenemos Eclipse, nos descargamos e instalamos el SDK de Android de aquí (http://developer.android.com/sdk/index.html) dándole a abajo donde pone "GET THE SDK FOR AN EXISTING IDE". Instalamos donde nos dé la gana (en mi caso C:/Android) y abrimos Eclipse. Ahora nos vamos a Help->Install New Software y le damos a Add. Como nombre ponemos lo que queramos (en mi caso ADT) y en la URL ponemos la siguiente: https://dl-ssl.google.com/android/eclipse/ 
Típico Next, Next... Finish. Se instala todo. Reiniciamos Eclipse y VOI LÁ. Objetivo completado.
Para crear proyectos en Android nos vamos a New->Other-> Android -> Android Application Project.

A partir de ahora iré subiendo mis proyectos. Ahora mismo estoy con una especie de "calculadora" para el descuento de la gasolina; es muy simple, pero en cuanto esté acabada la subiré.

Saludos a todos y hasta otra;)

viernes, 4 de julio de 2014

Liga de Pádel. Modelo de Entidad Relación.

Buenas gente. Como ahora empezaré a dar (entre otras) clases de Bases de Datos, voy a ir subiendo ejercicios resueltos. Os dejo aquí el enunciado del mismo:


Se desea diseñar una base de datos para almacenar la información relacionada con la edición de ligas de pádel. Como es bien sabido, el pádel es un deporte similar al tenis el que siempre participan 4 jugadores en cada partido. Cada liga tiene su código y su nombre que son únicos, un organizador y un premio final. Cada liga se celebra como sumo una vez cada año, en lo que se denomina ediciones, cada una con sus propias normas. 
Cada partido de pádel se celebra en un club (aunque es posible que al principio el lugar sea desconocido y no se rellene este dato hasta que los jugadores celebren el encuentro). Por cada partido hay que rellenar la fecha de celebración, la pista donde se juega y el resultado final. La liga tiene convenios con una serie de clubs de los que hay que almacenar un código, un nombre (obligatorio) una dirección y un teléfono de contacto. En un club sólo se puede jugar un partido en una pista en una determinada fecha (en nuestro sistema gestor de bases de datos el tipo fecha contiene también la hora). 
De cada jugador hay que almacenar un código, nombre y teléfono móvil (obligatorios). El móvil debe ser único por jugador. Además, se ha habilitado una página web en la que los jugadores pueden inscribirse como usuarios. Un usuario es un jugador del que hay que almacenar el alias, la password de entrada y la dirección de correo electrónico. Es obligatorio que el organizador de cada liga esté inscrito como usuario. Cada usuario sólo puede organizar una liga. 
Hay que conocer qué jugadores están inscritos en cada una de las ediciones. Éstas, a su vez, se pueden descomponer en varias fases, cada una con su propia fecha de inicio, nombre (fase regular, playoff, finales, etc.) y número de partidos de los que consta, número de partidos que se han jugado realmente y cuáles son. Además, una vez terminada cada edición hay que saber la pareja de jugadores que ha resultado campeona de esa edición. 
Nuestro cliente nos comenta que últimamente se está poniendo de moda el pádel individual, el cual lo juegan 2 jugadores en lugar de 4 y que se están planteando organizar ligas de este estilo. Para ello, bastaría con almacenar el puesto que cada jugador ocupa en cada partido de modo que variará entre 1 y 4 para los partidos de ligas normales y entre 1 y 2 para las ligas de pádel individual.

Bueno, vamos a ello. En una primera lectura de la especificación podemos identificar fácilmente las
entidades (objetos de interés). Éstos serán seres distinguibles del mundo real; pudiendo ser reales o abstractos y vienen representados por un nombre o sustantivo.
Normalmente las entidades suelen ser algo sobre lo que la organización necesita información.
Identificamos las Entidades como:
Liga, Edición, Club, Partido, Jugador, Usuario y Fase. Ahora vamos a crearlas con sus atributos.

Hay que tener en cuenta que un atributo nunca puede tener 'propiedades', pues en ese caso se trataría de otra entidad más. Además por la propiedad de redundancia, no debemos tener atributos que podamos calcular (por ejemplo, si tenemos una fecha de nacimiento, no pongamos la edad puesto que podría darse el caso de que una de las dos estuviera mal y no cuadraran las cuentas).

Ahora debemos fijarnos en qué atributos pueden ser únicos y elegir una clave para cada Entidad. Una clave es un atributo único que representa inequívocamente a su entidad (Por ejemplo, en la entidad Español, la clave debería ser DNI, pues es único).

Marcaremos con un * los valores obligatorios, con una U los únicos y con un # las claves. Una entidad puede tener varias claves (#1, #2...) o una clave compuesta (marcaremos los atributos con el mismo #) como en el caso de la Entidad Partido.

Ahora nos queda definir las relaciones. En ése momento nos daremos cuenta de que todos los Usuarios son Jugadores, por lo que Usuario es una Subentidad de Jugador. Además nos damos cuenta de que Fase es una entidad débil de Edición y ésta, a su vez, de Liga. Una entidad débil es aquella la cual sus atributos sólo la identifican de forma parcial (para saber de que fase estamos hablando necesitamos saber a qué edición pertenece y a qué liga).

Al fin, nuestro esquema quedaría de la siguiente manera:

Espero que si le echáis sus 5 minutos lo veáis todo claro, en otro caso me hallaré encantado de resolver vuestras dudas. Un saludo a todos y hasta la próxima ;)

miércoles, 2 de julio de 2014

Servidor de Echo en C++ para Linux.

Buenas gente. Ésta es la última práctica que se pidió para redes. Montar un servidor de Echo en C++ para Ubuntu. Me ha sido super trabajoso conseguirlo porque a mi ordenador le ha costado vida y media tirar de la máquina virtual (para compilar) y del Visual Studio para programar. Bueno, el servidor debe seguir el siguiente esquema:

Como veis parece ser seguir una estructura, pero el averiguar de donde sacar los datos nos costó (tanto a Cristina como a mi) demasiado tiempo. Seguramente haya códigos mejores que el mio, pues no soy perfecto. Pero espero que os sirva de ayuda. Aquí los teneis:

Código del Cliente:
#include <iostream>
#include <sys/socket.h>    //socket
#include<stdio.h> //printf
#include<netinet/in.h> 
#include<arpa/inet.h>  //inet_addr
#include<netdb.h>
#include<string.h>    //strlen
#include <stdlib.h>  // para atoi


using namespace std;





int main(int argc, char *argv[]) {
 int sck, rtn;
// const int PUERTO = 5050;
 const int PUERTO = atoi(argv[argc-1]);
 cout << argv[1] << ":" << PUERTO << "\n\n";

 struct sockaddr_in server;
 char bufferIn[2000], bufferOut[2000], bufferAux[2000];
 //creamos socket
 sck = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
 if (sck == -1) {
  cout << "No se puede crear el socket";
 }
 puts("Socket creado");

 server.sin_family = PF_INET;
 server.sin_port = htons(PUERTO);
 server.sin_addr.s_addr = inet_addr(argv[1]);
// server.sin_addr.s_addr = inet_addr("127.0.0.1");

 //Conectando al servidor
 if (connect(sck, (struct sockaddr*) (&server), sizeof(server)) < 0) {
  perror("Conexion fallida. error");
  return 1;
 }
 puts("Conectado\n");
 
 //Obtenemos la string a enviar, la separamos en caracteres y la enviamos.
 bool noAcaba = true;
 while (noAcaba) {
  cout << "Conectado al servidor " << inet_ntoa(server.sin_addr) << ":" << PUERTO << ".\n";
  cout << "Que desea enviar?\n";
  scanf("%s", bufferOut);
  if (!strcmp(bufferOut, "FIN")) {
   noAcaba = false; 
   if (write(sck, bufferOut, strlen(bufferOut)) < 0) {
    puts("envio fallido");
    return 1;
   }
  } else {
   strcat(bufferOut, "\n");

   if (write(sck, bufferOut, strlen(bufferOut)) < 0) {
    puts("envio fallido");
    return 1;
   }
   rtn = read(sck, bufferIn, 2000);
   
   cout << "\nDevuelto " ; 
   for (int i = 0; i < strlen(bufferOut); i++) {
    cout << bufferIn[i];
   }
   cout << "\n";
   strcpy(bufferIn, "\0");
   strcpy(bufferOut, "\0");
  }
  

 }
 cout << "Desconectado Correctamente.\n";
 

 return 0;
}

Código del Servidor:
// EchoServer.cpp: define el punto de entrada de la aplicación de consola.
//

#include <iostream>
#include <sys/socket.h> 
#include <unistd.h>  //write
#include <stdio.h>  //para perror
#include <stdlib.h>  // para atoi

//Otros includes:
#include<netinet/in.h> 
#include<arpa/inet.h> //inet_addr
#include<netdb.h>
#include<string.h>  //strlen

using namespace std;




int main(int argc, char *argv[]) {

 int listen_socket, cliente, rtn;
 struct sockaddr_in server, clientinfo;
// const int PUERTO = 5050;
 const int PUERTO = atoi(argv[1]);

 //SOCKET
 listen_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); 
 if (listen_socket == -1) {
  perror("No se puede crear el socket");
  return 1;
 }
 cout << "Socket creado\n";

 //preparar la estructura sockaddr_in
 server.sin_family = AF_INET;
 server.sin_addr.s_addr = INADDR_ANY;
 server.sin_port = htons(PUERTO);
 
 //BIND
 rtn = bind(listen_socket, (struct sockaddr*)&server, sizeof(server));
 if (rtn < 0) {
  perror("Error en el bind");
  return 1;
 }
 puts("Bind hecho");
 
 //LISTEN
 rtn = listen(listen_socket, 1); //1 es el maximo de conexiones a la vez
 cout << "Esperando conexiones entrantes en el puerto "<< PUERTO << "\n";
 /*if (rtn < 0) {
  perror("Error en el listen");
  return 1;
 }*/

 while (true) {

 sockaddr client;
 socklen_t c = sizeof(client);
 //ACCEPT
 cliente = accept(listen_socket, &client, &c);
 if (cliente < 0) {
  perror("Error en el accept");
  return 1;
 }
 cout << "Conexion aceptada en el puerto " << PUERTO << "\n";
 fflush(stdout);

 //READ
 char buffer[2000], bufferAux[2000];
//  cout << "Al menos intenta leer \n";
 bool noAcaba = true;
 while ((rtn = read(cliente, buffer, 2000)) > 0 && noAcaba) {
   cout << "Conectado en el puerto " << PUERTO << ". Esperando accion.\n";
//   cout << "RECIBIDO: " << buffer << "\n";
   if (!strcmp(buffer, "FIN")) {
    noAcaba = false;
   }
   else {
    fflush(stdout);
 //   cout << "Al menos lee \n";
 //   strcat(buffer, "  Servidor");
    for (int i = 0; i < rtn; i++) {
     buffer[i] = toupper(buffer[i]);
    }
    fflush(stdout);
    if (write(cliente, buffer, strlen(buffer)) < 0) {
     perror("Envio fallido");
     return 1;
    }
//    cout << "ENVIADO: " << buffer;
    fflush(stdout);
    //Reiniciarlo
    strcpy(buffer, "\0");
   }

   
  }
  if (rtn == 0) {
   cout << "Recibido FIN. \nCerrando la conexión con el cliente\n";
   cout<<"Cliente desconectado. Esperando conexion en el puerto "<< PUERTO << "\n";
   fflush(stdout);
  }
  else if (rtn == -1)
  {
   perror("Error en Read");
  }
  cout << "Esperando otro cliente.\n \n";

 }
 
 
 return 0;
}

Como podéis ver son algo extensos y programados de forma estructurada. De todos modos aquí teneis el proyecto en Git donde podréis ver los cambios así como otras dos posibilidades de crearlo llamadas "client2" y "server2".

Espero que os haya gustado. El contenido de éste post tiene relación con el "intento de chat" que aún está por acabar, pues ése empezó siendo un servidor de echo.

Saludos y hasta la próxima;)

domingo, 29 de junio de 2014

Crear un sistema de videovigilancia con Raspberry, Webcam y Dropbox (Parte II)

Buenas gente. ¿Acabando los exámenes, no? Os traigo la segunda parte del sistema de vigilancia. Esto también lo podéis usar para tener una copia de seguridad 'autoactualizable' de vuestra carpeta de dropbox en la Raspberry Pi y viceversa.

Si te perdistes la primera parte, aquí la tienes.

Lo primero que vamos a hacer es descargarnos una pequña aplicación de Dropbox creada por Andrea Fabrizi. Para ello nos vamos a la shell (o entramos via ssh como hago yo) y escribimos lo siguiente:
cd /usr/local/bin/

sudo wget https://raw.github.com/andreafabrizi/Dropbox-Uploader/master/dropbox_uploader.sh

sudo chmod +x dropbox_uploader.sh

Esto descargará la aplicación en /usr/local/bin/ y le dará los permisos necesarios. Si ejecutaramos dropbox_uploader.sh (escribiendolo tal cual) nos saldrían las opciones necesarias, pero aún no podemos ejecutarlo. Necesitamos explicarle con que cuenta debe comunicarse. Para ello nos vamos a https://www.dropbox.com/developers/apps y creamos una nueva app "Dropbox API app". Le decimos que trabajaremos con "files and datastores" y que necesitamos acceder también a archivos de dropbox (por si nos hiciera falta) y para lo cual hay que pulsar en el No. Especificamos como archivos fotos (y el video es opcional) y le damos un nombre a la app. No cerreis la ventana que os sale. Ahora si teneis que ejecutar el script instalado anteriormente. Os pedira una "app key" y una "app secret" las cuales son las que os dice la ultima ventana de dropbox. Si os pregunta si darle acceso full, aceptad.
Si ahora escribierais en la consola:
dropbox_uploader.sh list

Os devolvería todas las carpetas y archivos que tenéis en la raíz.

Bueno, como usaremos un solo comando para subir (en este caso) los archivos de motion a dropbox, vamos a usar un alias en vez de un script (por hacerlo más liviano).
Un alias en Linux es una palabra que tu reservas y que significará uno o varios comandos seguidos. Por ejemplo, si pusieramos como alias a='cd /' cada vez que escribieramos 'a' y pulsaramos enter nos llevaría a la carpeta raíz del disco. Bueno, nuestro comando será el siguiente:

/usr/local/bin/./dropbox_uploader.sh upload /tmp/motion/* Motion/$(date +%Y%m%d)


El cual lo que hace es ejecutar dropbox_uploader.sh con la opcion upload, la carpeta de origen de motion y como destino una carpeta en Dropbox llamada Motion (si no existe, la crea) y dentro de ella una carpeta con la fecha actual. Para crear el alias editaremos un archivo llamado bashrc. Para ello ejecutamos:

sudo nano /home/pi/,bashrc


y bajamos abajo del todo. Allí escribimos :

alias subidaMotion='/usr/local/bin/./dropbox_uploader.sh upload /tmp/motion/* Motion/$(date +%Y%m%d)'


Cerramos con Control+X, nos pregunta si guardar, escribimos 'y' y dejamos el nombre tal y como está. Deberemos reiniciar para que surta efecto el cambio. A partir de ese momento cada vez que escribais subidaMotion os copiará todas las imagenes de /tmp/motion a la carpeta de dropbox; la cual depende de la fecha.

Bueno gente, eso es todo. Ya solo os queda toquetear. Quizás en un futuro próximo me de por hacer un script que cada 5 minutos mirase el contenido de la carpeta /tmp/motion y si fuera distinto de vacío subiera automáticamente. También cabe la posibilidad de hacer que nos enviara un correo o nos avisara vía Twitter. Son cosas sencillas que iré poniendo en cuanto tenga tiempo.

Saludos y espero que os haya gustado;)

viernes, 27 de junio de 2014

Examen final Programación de Sistemas y Concurrencia. Junio 2012.

Os dejo mi solución al ejercicio de C del examen final de Programación de Sistemas y Concurrencia de la UMA de 2012.

Se desea implementar una lista enlazada circular para representar la lista de procesos que están disponibles para ejecución. El puntero externo apuntará al proceso que le corresponde la ejecución (ver figura).  

Definir la estructura de datos e implementar las siguientes operaciones: 
void Crear (LProc *lista) 
Crea una lista de procesos vacía. 
void AñadirProceso (LProc *lista, int idproc) 
Añade el proceso con identificador idproc a la lista de procesos disponibles para ejecución. Este proceso se añade como nodo anterior al nodo al que apunta ejecución. Si la lista está vacía el puntero externo apuntará a l único nodo. Dada la figura anterior, si queremos añadir el proceso 5, la lista quedaría como aparece en la figura: 


void MostrarLista( LProc lista) 
Muestra la lista de los procesos que están disponibles para la ejecución. 
void EjecutarProceso(LProc *lista) 
Simula la ejecución del proceso apuntado por ejecución, eliminándolo de la lista de procesos.  Así, partiendo de la figura anterior, la lista quedaría :


ListaCircular.h
#ifndef LISTACIRCULAR_H_
#define LISTACIRCULAR_H_


typedef struct Lista LProc;

struct Lista{
 struct TNodo * ejecucion;
};
struct TNodo{
 int valor;
 struct TNodo * sig;
};

void Crear (LProc *lista);
//Crea una lista de procesos vacía.
void AnadirProceso (LProc *lista, int idproc);
void MostrarLista( LProc lista);
//Muestra la lista de los procesos que están disponibles para la ejecución.
void EjecutarProceso(LProc *lista);

#endif /* LISTACIRCULAR_H_ */

ListaCircular.c
#include <stdio.h>
#include <stdlib.h>
#include "ListaCircular.h"

void Crear (LProc *lista){
 //Crea una lista de procesos vacía.
 (*lista).ejecucion = NULL;
}
void AnadirProceso (LProc *lista, int idproc){

 struct TNodo* nuevo = malloc(sizeof(struct TNodo));
 struct TNodo * auxiliar = (*lista).ejecucion;
 struct TNodo * anterior = NULL;
 nuevo -> valor = idproc;
 int insertado = 0;
 if((*lista).ejecucion == NULL){ //lista vacía
  nuevo ->sig = nuevo;
  (*lista).ejecucion = nuevo;
  insertado = 1;
 }
 while(!insertado ){
  if(auxiliar -> sig == (*lista).ejecucion){ // se añade detras de auxiliar
   nuevo -> sig = (*lista).ejecucion;
   auxiliar -> sig = nuevo;
   insertado = 1;
  }else{
   anterior = auxiliar;
   auxiliar = auxiliar -> sig;
  }
 }

}
void MostrarLista( LProc lista){
 //Muestra la lista de los procesos que están disponibles para la ejecución.
 struct TNodo * aux = lista.ejecucion;
 if(lista.ejecucion == NULL){
  printf("No hay procesos disponibles para la ejecución. \n");
 }else{
  printf("Procesos disponibles para la ejecución: \t");


   printf(" %d -> ",lista.ejecucion ->valor );
   lista.ejecucion = lista.ejecucion -> sig;
   while(lista.ejecucion != aux){
    printf(" %d -> ",lista.ejecucion ->valor );
    lista.ejecucion = lista.ejecucion -> sig;
   }
   printf("\n");
 }

}
void EjecutarProceso(LProc *lista){
 /*
  * Simula la ejecución del proceso apuntado por ejecución, eliminándolo de la lista de procesos.
Así, partiendo de la figura anterior, la lista quedaría :
  */
 struct TNodo * aux;
 struct TNodo * auxiliar = (*lista).ejecucion ;

 while(auxiliar -> sig != (*lista).ejecucion){
   auxiliar = auxiliar -> sig;
 }
 aux = (*lista).ejecucion ->sig;
 printf("Se ejecuta el proceso: %d \n", (*lista).ejecucion -> valor);
 free((*lista).ejecucion);
 (*lista).ejecucion = aux;
 auxiliar -> sig = (*lista).ejecucion;
}

int main(void) {
 LProc lista;
 Crear(&lista);
 MostrarLista(lista);
 AnadirProceso(&lista, 4);
 AnadirProceso(&lista, 6);
 AnadirProceso(&lista, 1);
 AnadirProceso(&lista, 8);
 AnadirProceso(&lista, 3);
 AnadirProceso(&lista, 5);
 MostrarLista(lista);
 EjecutarProceso(&lista);
 MostrarLista(lista);

 return EXIT_SUCCESS;
}


miércoles, 25 de junio de 2014

Ejercicios C++: Registros y Arrays (III)

Hace ya tiempo que no escribía una entrada de ejercicios de C++, así que aquí os dejo la tercera que vemos sobre registros y arrays, pues es lo más complicado que hemos visto hasta ahora de este lenguaje. En las (dos) próximas entradas de C++ trabajaremos con strings.


Se dispone de un array de 80 números enteros en el que al menos hay dos números que son iguales y dos que son distintos. Obtenga una función que tomando como parámetro dicho array, devuelva un elemento del array que sea mayor que el mínimo de éste.
int minimo (const TArray& array){
 int min = array[0];
 for(unsigned i = 1; i < TAMANO; i++){
  if(array[i] < min){
   min = array[i];
  }
 }
 return min;
}

int ejercicio (const TArray& array){
 int res;
 bool encontrado = false;
 unsigned i= 0;
 minimo = minimo(array);
 while(i < TAMANO && !encontrado){
  if(array[i] > minimo){
   encontrado = true;
   res = array[i];
  }
  i++;
 }

 return res;
}

Diseña un algoritmo que permita invertir el contenido de un array.
#include <iostream>
using namespace std;

const unsigned TAMANO = 10;

typedef int TArray[TAMANO];


void leerArray(TArray& array){
 for(unsigned i=0; i < TAMANO; i++){
  //cout << "v["<<i<<"]->";
  cin >> array[i];
 }
}

void invertir(const TArray& array, TArray& inv){
 unsigned j = TAMANO -1;
 for(unsigned i = 0; i < TAMANO; i++){
  inv[i] = array[j];
  j--;
 }
}



void mostrarArray(const TArray& unarray){
 for(unsigned i =0; i < TAMANO; i++){
  cout << "["<<unarray[i]<<"]";
 }
}
int main() {
 TArray array, inv;
 leerArray(array);
 invertir(array, inv);
 mostrarArray(inv);
 return 0;
}


Escriba un programa que efectúe la conversión de un número natural en base 10 a otra determinada base, sabiendo que el resultado no sobrepasará los 50 dígitos. El usuario introducirá primero el número en base 10 y después la base a la que convertirlo (el programa debe asegurarse de que la base no sea ni menor de 2 ni mayor de 9).
#include <iostream>
using namespace std;

const unsigned MAXIMO = 50;

typedef unsigned TArray[MAXIMO];

void leerDatos(unsigned &num, unsigned& base){
 cout << "Introduce el número a convertir: ";
 cin >> num;
 do{
  cout << "Introduce la base: ";
  cin >> base;
 }while(base < 2 || base > 9);
}

void convertir(unsigned num, unsigned base, TArray array, unsigned&  i){
 i=0;
 while(num != 0){
  array[i] = num%base;
  num = unsigned(num/base);
  i++;
 }
}
void invertir(const TArray& array, TArray& inv, unsigned contador){
 unsigned j = contador -1;
 for(unsigned i = 0; i < contador; i++){
  inv[i] = array[j];
  j--;
 }
}

void mostrarSolucion(const TArray& array, unsigned contador){
 cout << "El número ya convertido es ";
 for(unsigned i=0; i < contador  ; i++){
  cout << array[i];
 }
}

int main() {
 unsigned num, base, i;
 TArray primero, solucion;
 leerDatos(num, base);
 convertir(num, base, primero, i);
 invertir(primero, solucion, i);
 mostrarSolucion(solucion, i);
 return 0;
}

Dados los siguientes tipos (para una constante MAX cualquiera):

typedef char Componentes[MAX];

struct Vector {

Componentes datos; // array de caracteres

unsigned tam; // numero de celdas ocupadas

};


a) La moda de un array de caracteres es el carácter del array que se repite más frecuentemente. Si varios caracteres se repiten con la misma frecuencia máxima, entonces no hay moda. Escribe un procedimiento con tres parámetros. El primero es de entrada para recibir un registro de tipo Vector que contiene el array datos con tam caracteres. El segundo parámetro es de salida e indicará si se ha encontrado la moda en el array o no. El tercer parámetro es de salida y será el carácter que representa la moda (si es que existe).

#include <iostream>
using namespace std;

const unsigned MAX =50;

typedef char Componentes[MAX];
struct Vector {
 Componentes datos; // array de caracteres
 unsigned tam; // numero de celdas ocupadas
};

void apartadoA(const Vector& vector, bool& existeModa, char& moda){
 int apariciones;
 int mayor=0;
 char m;
 existeModa = false;
 for(unsigned i=0; i < vector.tam; i++){
  apariciones = 1;
  for(unsigned j=i+1; j< vector.tam;j++){
   if(vector.datos[i] == vector.datos[j]){
    apariciones++;
   }
  }
  if(apariciones != mayor){
   if(mayor < apariciones){
    mayor = apariciones;
       m = vector.datos[i];
       existeModa = true;
   }
  }else{
   existeModa = false;
  }
 }
 if(existeModa){
  moda = m;
 }
}

void leerVector(Vector& vector){
 vector.tam = 0;
 char a;
 cin >> a;
 while(a != '.'){
  vector.datos[vector.tam]= a;
  vector.tam++;
  cin >> a;
 }
}

int main() {
 Vector vector;
 bool existeModa;
 char moda;
 leerVector(vector);
 apartadoA(vector, existeModa, moda);
 if(existeModa){
  cout << "La moda es '"<< moda<<"'";
 }else{
  cout << "No existe la moda.";
 }
 return 0;
}

b) Diseña una función booleana que dados dos registros de tipo Vector como parámetros, devuelva TRUE si son iguales y FALSE en otro caso. Dos vectores son iguales si contienen los mismos elementos y en el mismo orden relativo, suponiendo que el primer elemento sigue al último.
#include <iostream>
using namespace std;

const unsigned MAX=50;
typedef char Componentes[MAX];
struct Vector {
 Componentes datos; // array de caracteres
 unsigned tam; // numero de celdas ocupadas
};

bool vectoresIguales(Vector& vector1, Vector& vector2){
 bool iguales = false;
 if(vector1.tam == vector2.tam){

 iguales = true;
 unsigned i=0;

 bool encontrado=false;
 unsigned j=0;
  while(j < vector2.tam && !encontrado){
   if(vector1.datos[0] == vector2.datos[j]){
    encontrado = true;
   }else{
    j++;
   }
  }

 while(i < vector1.tam && iguales){
  iguales = false;
   if(vector1.datos[i] == vector2.datos[j]){
    i++;
    if(j == vector2.tam -1){
     j = 0;
    }else{
     j++;
    }
    iguales = true;
   }

 }

 }
 return iguales;

}


void leerVector(Vector& vector){
 vector.tam = 0;
 char a;
 cout << "Introduce cadena: ";
 cin >> a;
 while(a != '.'){
  vector.datos[vector.tam]= a;
  vector.tam++;
  cin >> a;
 }
}


int main() {
 Vector v1, v2;
 leerVector(v1);
 leerVector(v2);
 if(vectoresIguales(v1,v2)){
  cout << "Los vectores son iguales.";
 }else{
  cout << "No son iguales";
 }

 return 0;
}

c) Diseña un procedimiento que tome como parámetros de entrada dos vectores con los arrays ordenados y devuelva (con un parámetro de salida) el vector resultado de realizar la mezcla ordenada de los dos arrays contenidos en los vectores de entrada.
#include <iostream>
using namespace std;

const unsigned MAX =50;

typedef char Componentes[MAX];
struct Vector {
 Componentes datos; // array de caracteres
 unsigned tam; // numero de celdas ocupadas
};

void apartadoC(const Vector& v1, const Vector& v2, Vector& res){
 unsigned i=0;
 unsigned j=0;
 unsigned cnt =0;
 res.tam = v1.tam+v2.tam;
 while(cnt < res.tam && i < v1.tam && j < v2.tam){
  if(v1.datos[i] < v2.datos[j]){
   res.datos[cnt] = v1.datos[i];
   i++;
  }else{
   res.datos[cnt] = v2.datos[j];
   j++;
  }
  cnt++;
 }
 if(i == v1.tam){
  while(cnt < res.tam){
   res.datos[cnt] = v2.datos[j];
   j++;
   cnt++;
  }
 }else if (j == v2.tam){
  while(cnt < res.tam){
     res.datos[cnt] = v1.datos[i];
     i++;
     cnt++;
  }
 }
}

void leerVector(Vector& vector){
 vector.tam = 0;
 char a;
 cout << "Introduce los datos: ";
 cin >> a;
 while(a != '.'){
  vector.datos[vector.tam]= a;
  vector.tam++;
  cin >> a;
 }
}

void mostrarArray(const Vector& res){
 for(unsigned i=0; i < res.tam; i++){
  cout <<"["<<res.datos[i]<<"]";
 }
}


int main() {
 Vector v1, v2, res;
 leerVector(v1);
 leerVector(v2);
 apartadoC(v1,v2, res);
 mostrarArray(res);
 return 0;
}

Diseña un algoritmo para calcular la suma de los elementos de la diagonal principal de una
matriz cuadrada.
include <iostream>
using namespace std;

typedef double TFilas[100];
typedef TFilas TMat[100];

struct TMatriz{
 unsigned dim;
 TMat matriz;
};

void leerDimension(TMatriz& mat){
 cout << "Introducir número de filas y columnas: ";
 cin >> mat.dim;
}

void leerMatriz(TMatriz& mat){
 cout << "Introduce los valores de la matriz."<< endl;
 for(unsigned f =0; f < mat.dim; f++){
  for(unsigned c =0; c < mat.dim; c++){
   cout << "Matriz["<<f+1<<"]["<<c+1<<"]->";
   cin >> mat.matriz[f][c];
  }
 }
}

void mostrarMatriz(const TMatriz& mat){
 for(unsigned f =0; f < mat.dim; f++){
  for(unsigned c =0; c < mat.dim; c++){
   cout << mat.matriz[f][c]<<" ";
  }
  cout << endl;
 }
}

double sumaDiagonal(const TMatriz& mat){
 double suma=0;
 for(unsigned f =0; f < mat.dim; f++){
  for(unsigned c =0; c < mat.dim; c++){
    if(c==f){
     suma = suma + mat.matriz[f][c];
    }
  }
 }
 return suma;
}

int main() {
 cout << "Suma de la diagonal de una matriz." << endl; 
 TMatriz matriz;
 leerDimension(matriz);
 leerMatriz(matriz);
 mostrarMatriz(matriz);
 cout << "La suma de la diagonal es "<< sumaDiagonal(matriz);

 return 0;
}


Una matriz tiene un punto silla en una de sus componentes, si ese componente es el mayor valor de su columna y el menor de su fila. Diseña un algoritmo que recogiendo de teclado los componentes de una matriz cuadrada de enteros de hasta un máximo de 10x10, muestre en la pantalla las coordenadas de todos sus puntos silla.
##include <iostream>
using namespace std;

typedef double TFilas[10];
typedef TFilas TMat[10];

struct TMatriz{
 unsigned dim;
 TMat matriz;
};

void leerDimension(TMatriz& mat){
 do{
 cout << "Introducir número de filas y columnas: ";
 cin >> mat.dim;
 }while(mat.dim > 10);
}

void leerMatriz(TMatriz& mat){
 cout << "Introduce los valores de la matriz."<< endl;
 for(unsigned f =0; f < mat.dim; f++){
  for(unsigned c =0; c < mat.dim; c++){
   cout << "Matriz["<<f+1<<"]["<<c+1<<"]->";
   cin >> mat.matriz[f][c];
  }
 }
}

void mostrarMatriz(const TMatriz& mat){
 for(unsigned f =0; f < mat.dim; f++){
  for(unsigned c =0; c < mat.dim; c++){
   cout << mat.matriz[f][c]<<" ";
  }
  cout << endl;
 }
}

double mayorColumna(const TMatriz& mat, unsigned columna){
 double mayor = mat.matriz[0][columna];
 for(unsigned f=1; f < mat.dim; f++){
  if(mayor < mat.matriz[f][columna]){
   mayor =  mat.matriz[f][columna];
  }
 }
 return mayor;
}

double menorFila(const TMatriz& mat, unsigned fila){
 double menor = mat.matriz[fila][0];
  for(unsigned c=1; c< mat.dim; c++){
   if(menor > mat.matriz[fila][c]){
    menor =  mat.matriz[fila][c];
   }
  }
  return menor;
}


void buscarPuntos(const TMatriz& mat){
 for(unsigned f=0; f < mat.dim; f++){
  for(unsigned c=0; c < mat.dim; c++){
   if(mat.matriz[f][c] == menorFila(mat, f) && menorFila(mat, f)==mayorColumna(mat, c)){
    cout << "El elemento de la fila "<< f+1 << " y la columna "<< c+1 << " es un punto de silla."<< endl;
   }
  }
 }
}
int main() {
 cout << "Buscar punto de silla." << endl; // prints !!!Hello World!!!
 TMatriz matriz;
  leerDimension(matriz);
  leerMatriz(matriz);
  mostrarMatriz(matriz);
  buscarPuntos(matriz);
 return 0;
}

Un tablero n-goro es un tablero con n x (n+1) casillas. Una propiedad interesante es que se pueden visitar todas sus casillas haciendo el siguiente recorrido por diagonales. Empezamos por la casilla (1,1) y recorremos la diagonal principal hacia la derecha y hacia abajo hasta llegar a la casilla (n,n). La siguiente casilla a visitar sería la (n+1,n+1) que no existe porque nos saldríamos del tablero por abajo. En estos casos siempre se pasa a la casilla equivalente en al primera fila, es decir, la (1,n+1). Ahora seguimos moviéndonos hacia la derecha y hacia abajo. Pero la siguiente casilla sería la (2,n+2) que no existe porque nos hemos salido por la derecha. En estos casos se continúa por la casilla equivalente de la primera columna, es decir, la (2,1). De nuevo nos movemos hacia la derecha y hacia abajo, hasta alcanzar la casilla (n,n-1). La siguiente casilla sería la (n+1,n), pero como nos saldríamos por abajo pasamos a la casilla equivalente de la primera fila (1,n). Si se continúa con este proceso se termina visitando todas las casillas del tablero.
Diseñe un procedimiento que dada una constante N devuelve como parámetro de salida un
tablero N-goro con sus casillas rellenas con el número correspondiente al momento en que se
visitan.
#include <iostream>
using namespace std;

typedef double TFilas[10];
typedef TFilas TMat[10];

struct TMatriz{
 unsigned nco, nfi;
 TMat matriz;
};

void leerDimension(TMatriz& mat){
 cout << "Introducir número de filas: ";
 cin >> mat.nfi;
 mat.nco= mat.nfi+1;
}
void inicializar(TMatriz& mat){
 for(unsigned i=0; i < mat.nfi; i++){
  for(unsigned j= 0; j < mat.nco; j++){
   mat.matriz[i][j]= 0;
  }
 }
}

void escribirMatriz(TMatriz& mat){
 unsigned fila = 0;
 unsigned columna =0;
 for(unsigned i = 1; i <= mat.nfi*mat.nco; i++){
  if(mat.matriz[fila][columna]== 0){
   mat.matriz[fila][columna]= i;

   fila++;
   columna++;

   if(fila >= mat.nfi){
   fila = 0;
   }
   if(columna >= mat.nco){
   columna =0;
   }
  }
 }

}


void mostrarMatriz(const TMatriz& mat){
 for(unsigned f =0; f < mat.nfi; f++){
  for(unsigned c =0; c < mat.nco; c++){
   cout << mat.matriz[f][c]<<"  ";
  }
  cout << endl;
 }
}


int main() {
 
 TMatriz matriz;
 leerDimension(matriz);
 inicializar(matriz);
 escribirMatriz(matriz);
 mostrarMatriz(matriz);
 return 0;
}