My KDE life started

1 12 2009

KDE Community

KDE is the community and the Open Source/Free Software project that I always wanted to be part of. I am a KDE user for about 3 years and enjoy using KDE applications everyday. I use Kate for web development, can’t live without Amarok, Dolphin, Kaffeine, digiKam, Kopete, Konversation and Konsole obviously.

Unfortunately, I haven’t had enough time to contribute to KDE in a real way due to other responsibilities. I just blogged a little about KDE, shared Kubuntu and OpenSuse CDs with some friends and adapted a small script to add Peruvian radios in Amarok. No more.

But, I am following the KDE development since the 4.0 times reading the dot.kde.org and planet.kde.org and all articles around the web about KDE development. I think is time to give KDE a real contribution. Why?

Simple, I love the KDE community. These people have fun creating things and software, really smart, creative, friendly and with great passion for what they do. That is really encouraging for me.

In my case, I want to develop and promote KDE. I think is better to start contributing to an existing project to see how all this works, have a good understanding of the KDE platform and then write your own application.

I have some knowledge of C++, learned the principles of QT and checked the KDE examples at Techbase. Also, I set up my KDE development environment. So, I felt ready to start and took a look at many KDE apps/projects and see where I can get involved. Finally, I decided to start with KDE games. I enjoy playing KSudoku, Palapelli and KPat. (Didn’t try the others yet).

Well, I picked up a game from playground called Peg-E, an implementation of the game Peg solitaire (also known as Hi-Q). The game consists of jumping over pieces in order to remove them from the board. The goal is to remove all pegs but one. Peg solitaire at Wikipedia:

Playground is the section in the KDE source code repository where live KDE applications in alpha version. Many of those applications compile, but they lack of stability, international support, artwork and are not ready to use for the masses.

This game was abandoned for about 8 months. I compiled, become addicted and started hacking on it. I had to study libkdegames which is a library used for games in KDE. The first thing I did was to add difficulty levels, a timer, highscores and follow the KDE coding style. After that, I sent the patches to the developer (Graeme Gott) and requested him the maintainership of the game. The developer answered me that KDE is not a good fit for him, and accepted me to be the maintainer (Thanks a lot Graeme), but I have to change the name of the game because he has a pure QT version called Peg-E and users can be confused. The game name probably will be KPeg.

Then, I applied for my KDE svn account attaching the patches and happily, after some hours, my svn account was created and I made my first commit. Now, if everything goes fine this game will be part of KDE games for the KDE SC 4.5 release. I’ll work hard on it and sure I’ll learn much more about programming and games development.

I am impressed about the interesting algorithms and math behind the Peg Solitaire. That’s why I choose it. The game looks relatively simple, but it is very difficult to leave only one peg. There are many competitions around the world and papers about how fast and efficient you can be to solve a Peg solitaire. Well, I created my TODO list and currently reading some technical articles about the game.

That’s how my KDE life started and now I am happy of being part of the KDE community. Of course, I will write all my progress about the development of the game and my new adventures in the KDE devland.

Wait, wait, wait I forgot to tell you something. There is another KDE user in my family: Celeste, my daughter. I showed her the game KTurbeling and she loved it. I promised her to write a Lazy Town theme. She is a real fan and crazy about that program.

I am preparing more posts about KDE and have a surprise for you: Peruvian KDE users/developers.

So, Stay tuned.





Peruvian Radios in Amarok

5 08 2009

Estimados KDEeros peruanos y usuarios de Linux en general:

Acabo de publicar un script que agrega algunas radios online de nuestro querido Perú en el excelente reproductor Amarok versión 2.0+. Lo pueden descargar desde aquí

Para instalar el script se van al Menú Tools->Script Manager->Install Script seleccionan el script que descargaron, presionan OK y listo.

Otra forma es en vez de hacer click en “Install script” presionan “Get More Scripts”, y hacen click en “Install” a lado de “Peruvian Radio Stations” y listo. Cierran y vuelven a abrir Amarok y tendrán una pantalla como esta:

Peruvian Radio Stations in Amarok

Peruvian Radio Stations in Amarok

Hacen click en Peruvian Radio Stations y les aparecerá:

Peruvian Radio Stations in Amarok

Peruvian Radio Stations in Amarok

He agregado todas las radios que pude encontrar en la web. Disfruten escuchando la música de su preferencia, así como noticias, deportes, etc. Normalmente cuando cambias de una radio a otra, demora de 1 a 5 segundos.

Las siguientes radios fueron testeadas una y otra vez y funcionan correctamente:

Lima:
Callao 1400 KHZ AM
Capital 96.7 FM
Corazon 96.7 FM
CPN Radio 90.5 FM
Doble Nueve 99.1 FM
Felicidad 88.9 FM
La Unión 103.3 FM
Miraflores 96.1 FM
Moda 97.3 FM
Panamericana 101.1 FM
Planeta 107.7 FM
Ritmo Romántica 93.1 FM
RPP 89.7 FM
San Borja 91.1 FM
Secretos al corazón 93.1 FM
Studio92 92.5 FM
Telestereo 88 FM

Arequipa:
La Mega 94.3 FM
Las Vegas 99.3 FM
Melodia 104.3 FM
Panorama 96.5 FM
Super Stereo 105.5 FM
Yaravi 106.3 FM

Tacna:
Power 98.1 FM
Super Stereo 103.5 FM
Uno 93.7 FM

Cusco:
TV Color 98.1 FM
Willkamayu 940 AM

Trujillo:
La Grande 96.1 FM
Libertad Mundo 1160 Kcs AM

Ica:
Sonica 92.1 FM

Este pequeño script está basado en el script de Radio France con ciertas modificaciones. Gracias al desarrollador y a este tutorial que encontré en la web de Kubuntu que me permitió entender mejor como funcionan los scripts en Amarok..

Si desean que una radio sea agregada, por favor, simplemente envíen un mail a ronnycontacto(at)gmail.com indicando la stream url de la radio o simplemente la dirección del sitio web y gustosamente agregaré la radio en la brevedad posible..





Congratulations KDE team for Caizen (KDE 4.3)

4 08 2009

kde 4.3 (Caisen) was released today. Please read the announcement, see the improvements, applications, the platform and give this desktop a try and you will see the difference.

Congratulations and thanks to all the KDE team for the hard work..

KDE 4.3

KDE 4.3

I love KDE and now that I am doing more c++ and QT I hope to be part of the team for 4.4.





Upgrade to Django 1.1

3 08 2009

Developing or considering Django to create your cool 2.0 web applications/sites?

Please, try the new version of Django (1.1) which comes with interesting new features.

If you already have Django installed, you must remove the old version directory before upgrading. The path to the directory depends of your system, but it is usually located at:

/usr/local/lib/python$version/dist-packages/
/usr/local/lib/python$version/site-packages/
or
/usr/lib/python$version/dist-packages/
/usr/lib/python$version/site-packages/

One of the new features I recently tried is the possibility to create admin actions that allow for bulk updates to many objects at once.

Example:

Imagine you have a field called status which is a boolean field and you want to change the status of many elements at the same time. It’s annoying to select an object one by one to change the status. Here come “admin actions”, they are really useful.

In this short example, I want to make some objects published/unpublished. I will define two methods in my admin.py to achieve this.

def published(modeladmin, request, queryset):
    queryset.update(status=1)
published.short_description = 'Published stores'

def unpublished(modeladmin, request, queryset):
    queryset.update(status=0)
unpublished.short_description = 'Unpublished stores'

As you can see, in this case, the actions take an argument: queryset, which contains the action to perform and the fields that will be affected.

Then, you add these actions to your model admin:

class StoreAdmin(admin.ModelAdmin):
  ...
  actions = [published,unpublished]

And your admin interface will look like this:

Django admin actions

Django admin actions

Now you can select Published/Unpublished and press “Go” to test the actions.

I selected some elements to make them unpublished and in a second I got the following:

After performing admin actions in Django

After performing admin actions in Django

Simple right?. KISS is always important. But, there are some things to consider before finishing:

Those actions(published, unpublished) are placed outside the class ModelAdmin. (StoreAdmin in this example). But, we also can define these actions as methods of the ModelAdmin.

The advantage is that defining actions as methods gives the action a more direct access to the ModelAdmin (Performance) and the possibility to call any of the methods provided by the admin.

So, I am going to define the actions as methods of StoreAdmin and rename modeladmin parameter to self.

And finally I put the strings ‘published’,'unpublished’ in actions to tell the ModelAdmin to see those actions as methods instead of direct references to functions:

class StoreAdmin(admin.ModelAdmin):
  ...
  actions = ['published','unpublished']

  def published(self, request, queryset):
    queryset.update(status=1)
  published.short_description = 'Published stores'

  def unpublished(self, request, queryset):
    queryset.update(status=0)
  unpublished.short_description = 'Unpublished stores'

And that’s it. If you want to know more about admin actions or implement advanced action techniques, take a look at the excellent Django documentation.

Another feature I tried in Django 1.1 was the option: list_editable which allow edit and save multiple rows at once from the change list page. This option has the same format that list_display, but you have two restrictions:

  • Any field in list_editable must be in list_display.
  • The same field can’t be listed in both list_editable and list_display_links
  • class StoreAdmin(admin.ModelAdmin):
      ...
      list_display = ('customer', 'state','city','phone','status')
      list_editable = ['phone']
    

    A screenshot of the list_editable option for the field phone of my Store model.

    Django - list_editable option

    Django - list_editable option

    Well, I’ll continue exploring the new features in Django 1.1. If you have something to share, don’ t hesitate to post a comment or blog about that.





    Búsqueda lineal y búsqueda binaria

    9 07 2009

    Los algoritmos de búsqueda lineal y binaria son 2 de los algoritmos más usados para encontrar elementos en una estructura de datos.

    La búsqueda lineal probablemente es sencilla de implementar e intuitiva. Básicamente consiste en buscar de manera secuencial un elemento, es decir, preguntar si el elemento buscado es igual al primero, segundo, tercero y así sucesivamente hasta encontrar el deseado. Entonces este algoritmo tiene una complejidad de O(n).

    La búsqueda binaria al igual que otros algoritmos como el quicksort utiliza la técnica divide y vencerás. Uno de los requisitos antes de ejecutar la búsqueda binaria, es que el conjunto de elementos debe de estar ordenado. Supongamos que tenemos el siguiente array.

    57 53 21 37 17 36 22 3 44 97 89 26 31 47 8 17

    Debemos ordenarlo
    3 8 17 17 21 22 26 31 36 37 44 47 53 57 89 97

    ¿Como funciona la búsqueda binaria?
    Necesitamos una seria de datos para realizar la búsqueda: El elemento en la posición inicio, fin, medio. Y porsupuesto el tamaño del vector y elemento que queremos buscar.

    inicio = 0;
    fin = tam – 1;
    medio = (inicio + fin) / 2;

    Preguntamos si el elemento buscado es igual al elemento que se encuentra en la posicion medio del array, si es afirmativo ya encontramos el elemento y no hay nada más que hacer.

    if(buscado == array[medio]){
      cout << "Se encuentra en la posicion " << medio + 1 << endl;
      return array[medio];
    }
    

    Si no es así. Preguntamos si el elemento que se encuentra en la posición medio es mayor al elemento buscado, si es afirmativo y como el array está ordenado, quiere decir que elemento buscado es menor al del medio. Entonces ahora solo buscaríamos en esa división del array,
    por lo tanto, el fin ahora sería el elemento anterior al medio.

    if(array[medio] > buscado){
      fin = medio - 1;
    }
    

    De lo contrario quiere decir que elemento buscado es mayor al del medio, entonces debemos buscar en la otra división del array, por lo tanto el inicio sería el elemento posterior al medio.

    else {
      inicio = medio + 1;
    }
    

    Con cualquiera de estas 2 operaciones descartamos la otra mitad del array y por lo tanto reducimos notablemente el número de iteraciones.
    Por lo tanto, en cada iteración, la búsqueda se reduce a la mitad del array. Realizamos esto en un bucle mientras el inicio sea menor o igual al fin.

    El algoritmo de búsqueda binaria tiene una complejidad de O(log n).

    Veamos la implementación de la búsqueda lineal y binaria en C++.

    Nota: Para realizar la búsqueda binaria, estoy incluyendo el archivo que contiene el algoritmo de ordenamiento quicksort, para no volver a escribirlo y así reutilizar código.
    #include “quicksort.h”

    Aquí tienen el código del algoritmo y una breve explicación. Retiren el main para utilizarlo en otros programas.

    #include <iostream>
    #include "quicksort.h"
    
    using namespace std;
    
    int busqueda_lineal(int *array,int buscado,int tam)
    {
        for(int i=0;i<tam;i++){
            if(buscado==array[i]){
              cout << "Se encuentra en la posicion " << i+1 << endl;
            }
       }
    }
    
    int busqueda_binaria(int *array, int buscado, int tam)
    {
        int medio, inicio, fin;
        inicio = 0;
        fin = tam-1;
    
        while(inicio <= fin){
          medio = (inicio + fin) / 2;
    
          if(buscado == array[medio]){
            cout << "Se encuentra en la posicion " << medio + 1 << endl;
            return array[medio];
          } else{
              if(array[medio] > buscado){
                fin = medio - 1;
              } else {
                inicio = medio + 1;
              }
          }
        }
        return -1;
    }
    
    void imprimir(int *array,int tam)
    {
       for(int i=0;i<tam;i++){
            cout << array[i] << "  ";
       }
       cout << endl << endl;
    }
    
    int main()
    {
        int tam;
        int buscado;
        cout << "Ingresa el tamanyo del array" << endl;
        cin >> tam;
    
        int array[tam];
    
        srand(time(NULL));
        for(int i=0;i<tam;i++){
            array[i] = rand() % 100;
        }
    
        cout << endl;
        cout << "Array al inicio " << endl;
        imprimir(array,tam);
    
        cout << "Ingresa el elemento a buscar (Busqueda lineal) ";
        cin >>buscado;
        busqueda_lineal(array,buscado,tam);
    
        cout << "Array ordenado " << endl;
        quicksort(array,0,tam);
        imprimir(array,tam);
    
        cout << "Ingresa el elemento a buscar (Busqueda binaria) ";
        cin >>buscado;
        busqueda_binaria(array,buscado,tam);
    
        return 0;
    }
    

    Ejemplo de la salida del programa:

    Ingresa el tamanyo del array
    16
    
    Array al inicio
    57  53  21  37  17  36  22  3  44  97  89  26  31  47  8  17
    
    Ingresa el elemento a buscar (Busqueda lineal) 36
    El elemento se encuentra en la posicion 6
    Array ordenado
    3  8  17  17  21  22  26  31  36  37  44  47  53  57  89  97
    
    Ingresa el elemento a buscar (Busqueda binaria) 89
    El elemento se encuentra en la posicion 15
    




    Listas enlazadas – Clase Lista,Nodo en c++

    4 07 2009

    Una lista es una estructura de datos que nos permite agrupar elementos de una manera organizada. Las listas al igual que los algoritmos son importantísimas en la computación y críticas en muchos programas informáticos.

    Las listas están compuestas por nodos, estos nodos tienen un dato o valor y un puntero a otro(s) nodo(s).

    Existen varios tipos de listas: Simplemente enlazada, doblemente enlazada, circular simplemente enlazada, circular doblemente enlazada.

    Vamos a revisar las listas enlazadas simples, por ser el punto de partida y fundamentales para poder entender las otras.

    Una lista enlazada tiene un conjunto de nodos, los cuales almacenan 2 tipos de información: El dato que contienen y un puntero al siguiente nodo en la lista. El último nodo de la lista tiene como siguiente nodo el valor NULL. Entonces las listas enlazadas simples solo pueden ser recorridas en una dirección, apuntando al nodo siguiente, mas no a un nodo anterior.

    Aquí una ejemplo de un lista enlazada simple.

    En cristiano:
    55-> 60-> 31-> 5-> 4-> 51-> 9-> 27-> 68-> 62-> NULL
    
    Internamente:
    Nodo-> Dato: 55 Direcion: 0x3d2c00 Siguiente: 0x3d2c80
    Nodo-> Dato: 60 Direcion: 0x3d2c80 Siguiente: 0x3d2c90
    Nodo-> Dato: 31 Direcion: 0x3d2c90 Siguiente: 0x3d2ca0
    Nodo-> Dato: 5  Direcion: 0x3d2ca0 Siguiente: 0x3d2cb0
    Nodo-> Dato: 4  Direcion: 0x3d2cb0 Siguiente: 0x3d2cc0
    Nodo-> Dato: 51 Direcion: 0x3d2cc0 Siguiente: 0x3d3ab8
    Nodo-> Dato: 9  Direcion: 0x3d3ab8 Siguiente: 0x3d3ac8
    Nodo-> Dato: 27 Direcion: 0x3d3ac8 Siguiente: 0x3d3ad8
    Nodo-> Dato: 68 Direcion: 0x3d3ad8 Siguiente: 0x3d3ae8
    Nodo-> Dato: 62 Direcion: 0x3d3ae8 Siguiente: 0
    

    Obviamente, internamente no existen las palabras nodo, dato,dirección y siguiente, es solo una representación.

    Como una lista es una estructura de datos dinámica, el tamaño de la misma puede cambiar durante la ejecución del programa.

    Como vimos en post anteriores, se puede generar memoria dinámicamente para un array, pero un array es una estructura estática pues su tamaño tiene un limite y así creáramos array dinámicos hay que redimensionar el tamaño si es necesario, lo cual ya implica un costo de volver a generar memoria dinámica.

    Entonces podemos ver una ventaja de la listas sobre los arrays: No tener que redimensionar la estructura y poder agregar elemento tras elemento indefinidamente.

    Cuando uno ya ha trabajado con arrays (vectores y matrices) y empieza a estudiar las listas, se da cuenta que una restricción de las listas es el acceso a los elementos. En un vector podíamos hacer algo como v[50] y nos estábamos refiriendo al índice 50 del vector v. A esto se le conoce como acceso aleatorio.

    En el caso de las listas el acceso es secuencial, es decir, para acceder a un elemento del conjunto debemos de recorrer uno por uno los elementos hasta llegar al solicitado. Rápidamente se puede concluir que el tiempo de acceso a los elementos de un array es muchísimo más rápido que en una lista. Esta es una gran desventaja de las listas, por lo que buscar elementos por índice sería muy costoso. Esto no quiere decir que trabajar con arrays sea mejor que con listas. Las listas son muy flexibles y para muchos casos son imprescindibles.

    Bueno, aquí va la primera práctica que hice sobre listas enlazadas. Implementación de una clase Lista, clase Nodo y los siguientes métodos:

    • Añadir un elemento al inicio.
    • Añadir un elemento al final
    • Añadir un elemento de manera ordenada
    • Llenar la lista por teclado
    • Llenar la lista aleatoriamente
    • Imprimir la lista
    • Buscar un elemento
    • Eliminar un elemento por dato
    • Eliminar un elemento por posicion o índice
    • Eliminar toda la lista
    • Invertir una lista
    • Ordernar una lista
    • Cargar una lista desde archivo
    • Guardar la lista en un archivo
    • Concatenar una lista a otra
    • Intersección entre 2 listas

    Podrán ver la variable *temp en casi todos los métodos , este es el puntero de tipo Nodo que me va a permitir moverme a través de la lista y que inicialmente es igual a la cabeza (head). Mientras exista algo en la lista, voy avanzado el puntero para que apunte al siguiente. Esto se consigue en casi todos los casos con un while.

    While(temp)
    temp = temp->gte

    Otra operación común en los métodos es preguntar si inicialmente la lista está vacía, es decir, si la cabeza no contiene algo o es igual a Null.

    if(!head) o if(head==NULL)

    Apliqué mis limitados conocimientos de templates para tener una lista genérica y así pueda funcionar con varios tipos de datos y de verdad funciona.

    Ahí la definición e implementación de la clase, lista, clase nodo y el main para ver el funcionamiento. Cualquier crítica, sugerencia o comentarios son bienvenidos siempre.

    Nodo.h

    #ifndef NODO_H
    #define NODO_H
    #include <iostream>
    
    using namespace std;
    template <class T>
    class Nodo
    {
      public:
        T dato;
        Nodo *sgte;
        Nodo();
        Nodo(T);
        ~Nodo();
    
        void print();
        void eliminar();
    };
    
    #endif // NODO_H
    

    Nodo.cpp

    #include "Nodo.h"
    
    //constructor por defecto
    template<typename T>
    Nodo<T>::Nodo()
    {
      dato = NULL;
      sgte = NULL;
    }
    
    //constructor por parametro
    template<typename T>
    Nodo<T>::Nodo(T dato_)
    {
      dato = dato_;
      sgte = NULL;
    }
    
    //imprimir un nodo
    template<typename T>
    void Nodo<T>::print()
    {
      //cout << "Nodo-> " << "Dato: " << dato << " Direcion: " << this << " Siguiente: " << sgte << endl;
      cout << dato << "-> ";
    }
    
    //eliminar todos los nodos
    template<typename T>
    void Nodo<T>::eliminar()
    {
      if(sgte)
        sgte->eliminar();
      delete this;
    }
    
    template<typename T>
    Nodo<T>::~Nodo(){}
    

    Lista.h

    #ifndef LISTA_H
    #define LISTA_H
    
    #include <iostream>
    #include "time.h"
    #include <fstream>
    #include <string>
    #include "Nodo.h"
    #include "Nodo.cpp"
    
    using namespace std;
    
    template <class T>
    class Lista
    {
      private:
        int dim,num_nodos;
        T line;
        string archivo;
    
      public:
        T ele;
        Nodo<T> *head;
        Lista();
        ~Lista();
    
        void llenar_teclado(int);
        void llenar_aleatorio(int);
        void print();
        void add_end(T);
        void add_head(T);
        void add_sort(T);
        void search(T);
        void del_dato(T);
        void del_pos(int);
        void del_all();
    
        void invertir();
        void sort();
        void load(string);
        void save(string);
    
        void concat(Lista);
        void interseccion(Lista);
    };
    
    #endif // LISTA_H
    

    Lista.cpp

    #include "Lista.h"
    
    using namespace std;
    
    //constructor por defecto
    template<typename T>
    Lista<T>::Lista()
    {
      num_nodos = 0;
      head = NULL;
    }
    
    //llenar la lista por teclado
    template<typename T>
    void Lista<T>::llenar_teclado(int dim)
    {
      for(int i=0;i<dim;i++){
        cout << "Ingresa el elemento " << i + 1 << endl;
        cin >> ele;
        add_end(ele);
      }
    }
    
    //llenar la lista aleatoriamente para enteros
    template<typename T>
    void Lista<T>::llenar_aleatorio(int dim)
    {
      srand(time(NULL));
      for(int i=0;i<dim;i++){
        add_end(rand() % 100);
      }
    }
    
    //imprimir la lista
    template<typename T>
    void Lista<T>::print()
    {
      Nodo<T> *temp = head;
      if(!head){
        cout << "La lista esta vacia " << endl;
      } else{
        while(temp){
          temp->print();
          if(!temp->sgte) cout << "NULL";
          temp = temp->sgte;
        }
      }
      cout << endl << endl;
    }
    
    //insertar al final
    template<typename T>
    void Lista<T>::add_end(T dato_)
    {
      Nodo<T> *temp = head;
      Nodo<T> *nuevo = new Nodo<T> (dato_);
    
      if(!head){
          head = nuevo;
      } else{
        while(temp->sgte !=NULL){
          temp = temp->sgte;
        }
        temp->sgte = nuevo;
      }
      num_nodos++;
    }
    
    //insertar al inicio
    template<typename T>
    void Lista<T>::add_head(T dato_)
    {
      Nodo<T> *temp = head;
      Nodo<T> *nuevo = new Nodo<T> (dato_);
    
      if(!head){
        head = nuevo;
      } else{
        nuevo->sgte = head;
        head = nuevo;
        while(temp){
          temp = temp->sgte;
        }
      }
      num_nodos++;
    }
    
    //insertar de manera ordenada
    template<typename T>
    void Lista<T>::add_sort(T dato_)
    {
      Nodo<T> *nuevo = new Nodo<T> (dato_);
      Nodo<T> *temp = head;
    
      if(!head){
        head = nuevo;
      } else{
        if(head->dato > dato_){
          nuevo->sgte = head;
          head = nuevo;
        } else{
          while((temp->sgte != NULL) && (temp->sgte->dato < dato_)){
            temp = temp->sgte;
          }
          nuevo->sgte = temp->sgte;
          temp->sgte = nuevo;
        }
      }
      num_nodos++;
    }
    
    //buscar el dato de un nodo
    template<typename T>
    void Lista<T>::search(T dato_)
    {
      Nodo<T> *temp = head;
      int cont = 1;
      int cont2=0;
      while(temp){
        if(temp->dato == dato_){
    	  cout << "El dato se encuentra en la posicion: " << cont << " (Para seres humanos)" << endl;
    	  cont2++;
        }
    	temp = temp->sgte;
    	cont++;
      }
      if(cont2 == 0){
        cout << "No existe el dato " << endl;
      }
      cout << endl << endl;
    }
    
    //eliminar por dato del nodo
    template<typename T>
    void Lista<T>::del_dato(T dato_)
    {
      Nodo<T> *temp = head;
      Nodo<T> *temp1 = head->sgte;
    
      int cont=0;
    
      if(head->dato == dato_){
        head = temp->sgte;
      } else {
        while(temp1){
          if(temp1->dato == dato_){
    	    Nodo<T> *aux = temp1;
            temp->sgte = temp1->sgte;
            delete aux;
            cont++;
            num_nodos--;
          }
    	  temp = temp->sgte;
    	  temp1 = temp1->sgte;
        }
      }
      if(cont == 0){
        cout << "No existe el dato " << endl;
      }
    }
    
    //eliminar por posicion del nodo
    template<typename T>
    void Lista<T>::del_pos(int pos)
    {
      Nodo<T> *temp = head;
      Nodo<T> *temp1 = temp->sgte;
    
      if(pos < 1 || pos > num_nodos){
        cout << "Fuera de rango " << endl;
      } else if(pos==1){
        head = temp->sgte;
      } else{
        for(int i=2;i<=pos;i++){
          if(i==pos){
            Nodo<T> *aux = temp1;
            temp->sgte = temp1->sgte;
            delete aux;
            num_nodos--;
          }
          temp = temp->sgte;
          temp1 = temp1->sgte;
        }
      }
    }
    
    //eliminar todos los nodos
    template<typename T>
    void Lista<T>::del_all()
    {
      head->eliminar();
      head = 0;
    }
    
    //invertir la lista
    template<typename T>
    void Lista<T>::invertir()
    {
      Nodo<T> *temp = head;
      Nodo<T> *prev = NULL;
      Nodo<T> *next = NULL;
    
      while(temp){
        next = temp->sgte;
        temp->sgte = prev;
        prev = temp;
        temp = next;
      }
      head = prev;
    }
    
    //ordenar de manera ascendente
    template<typename T>
    void Lista<T>::sort()
    {
      T aux2;
      Nodo<T> *aux = head;
      Nodo<T> *temp = aux;
    
      while(aux){
        temp=aux;
        while(temp->sgte){
          temp=temp->sgte;
    
          if(aux->dato>temp->dato){
            aux2=aux->dato;
            aux->dato=temp->dato;
            temp->dato=aux2;
          }
        }
        aux=aux->sgte;
      }
    }
    
    //cargar una lista de un archivo
    template<typename T>
    void Lista<T>::load(string archivo)
    {
      ifstream in;
      in.open(archivo.c_str());
    
      if(!in.is_open()){
        cout << "No se puede abrir el archivo: " << archivo << endl << endl;
      } else{
        while(in >> line){
          add_end(line);
        }
      }
      in.close();
    }
    
    //guardar una lista en un archivo
    template<typename T>
    void Lista<T>::save(string archivo)
    {
      Nodo<T> *temp = head;
      ofstream out;
      out.open(archivo.c_str());
    
      if(!out.is_open()){
        cout << "No se puede guardar el archivo " << endl;
      } else{
        while(temp){
          out << temp->dato;
          out << " ";
          temp = temp->sgte;
        }
      }
      out.close();
    }
    
    //concatenar a otra lista
    template<typename T>
    void Lista<T>::concat(Lista Lista2)
    {
      Nodo<T> *temp2 = Lista2.head;
    
      while(temp2){
        add_end(temp2->dato);
        temp2 = temp2->sgte;
      }
    }
    
    //numeros que coinciden en 2 listas
    template<typename T>
    void Lista<T>::interseccion(Lista Lista2)
    {
      Nodo<T> *temp = head;
      Nodo<T> *temp2 = Lista2.head;//cabeza de la segunda lista
      Lista lista_interseccion;//creo otra lista
    
      int num_nodos2 = Lista2.num_nodos;//nodos de la segunda lista
      int num_inter=0;//numero de coincidencias
    
      //creo 2 vectores dinamicos
      T *v1 = new T[num_nodos];
      T *v2 = new T[num_nodos2];
    
      //lleno los vectores v1 y v2 con los datos de la lista original y segunda lista respectivamente
      int i=0;
      while(temp){
        v1[i] = temp->dato;
        temp = temp->sgte;
        i++;
      }
    
      int j=0;
      while(temp2){
        v2[j] = temp2->dato;
        temp2 = temp2->sgte;
        j++;
      }
    
      //ordeno los vectores
      insert_sort(v1,num_nodos);
      insert_sort(v2,num_nodos2);
    
      int v1_i= 0;//indice del 1er vector (v1)
      int v2_i= 0;//indice del 2do vector (v2)
    
      //mientras no haya terminado de recorrer ambas listas
      while (v1_i < num_nodos && v2_i < num_nodos2) {
        if(v1[v1_i] == v2[v2_i]){//cuando los datos de ambas sean iguales
          lista_interseccion.add_end(v1[v1_i]);//utilizo mi metodo add_end para llenar la lista de intersecciones
          v1_i++;
          v2_i++;
          num_inter++;
        } else if(v1[v1_i] < v2[v2_i]){
          v1_i++;
        } else{
          v2_i++;
        }
      }
    
      //Solo si hay alguna interseccion imprimo la nueva lista creada
      if(num_inter > 0) {
        cout << "Existen " << num_inter << " intersecciones " << endl;
        lista_interseccion.print();
      } else {
        cout << "No hay interseccion en ambas listas" << endl;
      }
    }
    
    //usado por el metodo interseccion
    template<typename T>
    void insert_sort(T a[],int tam)
    {
      T temp;
      for(int i=0;i<tam;i++){
        for(int j=i-1;j >=0 && a[j+1] < a[j];j--){
          temp = a[j+1];
          a[j+1] = a[j];
          a[j] = temp;
        }
      }
    }
    
    template<typename T>
    Lista<T>::~Lista(){}
    

    main.cpp

    #include <iostream>
    #include "Lista.h"
    #include "Lista.cpp" //por errores de linking de tipo "undefined reference to" (estudiando)
    
    using namespace std;
    int main()
    {
      Lista<int> l1;
      Lista<int> l2;
    
      int dim,pos;
      string archivo;
    
      cout << "Ingresa la dimension de la lista " << endl;
      cin >>dim;
    
      l1.llenar_aleatorio(dim);//llenar_teclado para otros tipos
    
      cout << "Lista A al inicio " << endl;
      l1.print();
    
      cout << "Agrega un elemento por la cabeza" << endl;
      cin >>l1.ele;
      l1.add_head(l1.ele);
      l1.print();
    
      cout << "Lista invertida " << endl;
      l1.invertir();
      l1.print();
    
      cout << "Lista ordenada " << endl;
      l1.sort();
      l1.print();
    
      cout << "Agrega un elemento (Sera ordenado)" << endl;
      cin >>l1.ele;
      l1.add_sort(l1.ele);
      l1.print();
    
      cout << "Busca un elemento" << endl;
      cin >>l1.ele;
      l1.search(l1.ele);
    
      cout << "Elimina un elemento por dato" << endl;
      cin >>l1.ele;
      l1.del_dato(l1.ele);
      l1.print();
    
      cout << "Elimina un elemento por posicion" << endl;
      cin >>pos;
      l1.del_pos(pos);
      l1.print();
    
      cout << "Cargar una lista desde archivo - Ingresa el nombre" << endl;
      cin >> archivo;//debe estar en el mismo directorio que este programa
      l2.load(archivo);
      cout << "Lista B" << endl;
      l2.print();
    
      cout << "Guardar la lista en un archivo - Ingresa el nombre" << endl;
      cin >> archivo;//ingresa un nombre cualquiera *.*
      l2.save(archivo);
    
      cout << "Interseccion entre las listas A y B " << endl;
      l1.interseccion(l2);
    
      cout << "Listas A y B concatenadas " << endl;
      l1.concat(l2);
      l1.print();
    
      l1.del_all();
      l1.print();
    
      return 0;
    }
    

    Un ejemplo de la salida del programa:

    Ingresa la dimension de la lista
    10
    Lista A al inicio
    55-> 60-> 31-> 5-> 4-> 51-> 9-> 27-> 68-> 62-> NULL
    
    Agrega un elemento por la cabeza
    18
    18-> 55-> 60-> 31-> 5-> 4-> 51-> 9-> 27-> 68-> 62-> NULL
    
    Lista invertida
    62-> 68-> 27-> 9-> 51-> 4-> 5-> 31-> 60-> 55-> 18-> NULL
    
    Lista ordenada
    4-> 5-> 9-> 18-> 27-> 31-> 51-> 55-> 60-> 62-> 68-> NULL
    
    Agrega un elemento (Sera ordenado)
    45
    4-> 5-> 9-> 18-> 27-> 31-> 45-> 51-> 55-> 60-> 62-> 68-> NULL
    
    Busca un elemento
    51
    El dato se encuentra en la posicion: 8 (Para seres humanos)
    
    Elimina un elemento por dato
    60
    4-> 5-> 9-> 18-> 27-> 31-> 45-> 51-> 55-> 62-> 68-> NULL
    
    Elimina un elemento por posicion:
    11
    4-> 5-> 9-> 18-> 27-> 31-> 45-> 51-> 55-> 62-> NULL
    
    Cargar una lista desde archivo - Ingresa el nombre
    prueba.txt // mi archivito prueba.txt tenia estos datos
    Lista B
    8-> 59-> 23-> 15-> 94-> 63-> 9-> 17-> 62-> 33-> NULL
    
    Guardar la lista en un archivo - Ingresa el nombre
    otralista.txt // abre tu archivo y mira el contenido
    Interseccion entre las listas A y B
    Existen 2 intersecciones
    9-> 62-> NULL
    
    Listas A y B concatenadas
    4-> 5-> 9-> 18-> 27-> 31-> 45-> 51-> 55-> 62-> 8-> 59-> 23-> 15-> 94-> 63-> 9->17-> 62-> 33-> NULL
    
    La lista esta vacia.
    

    Hasta pronto





    Operaciones con matrices – Clase Matriz en c++

    4 07 2009

    El siguiente es el primer ejercicio que hice con matrices. Quizás alguno de estos ejercicios te puedan servir como una base. Puedes leer el post anterior sobre vectores, matrices y punteros si aún no haz trabajo con ellos.

    Recomiendo que intentes y practiques mucho antes de copiar y pegar. Si te sientes estancado en algún problema recién trata de buscar una guía para llegar a la solución.

    Esta clase matriz tendrá 3 atributos: Matriz,número de filas y número de columnas. Tiene un constructor por defecto,constructor copiador y un constructor por parámetro que recibe el número de filas y columnas.

    Las siguientes funciones fueron implementadas:

    • Generar una matriz dinámicamente
    • LLenar la matriz desde teclado:
    • Llenar la matriz aleatoriamente:
    • Imprimir la matriz
    • Hallar el mayor elemento.
    • Hallar el menor elemento.
    • Hallar la moda.
    • Intercambiar filas.
    • Intercambiar columnas.
    • Sumar otra matriz: Sumar 2 objetos de tipo matriz retornar otra matriz. Ejem: c = a + b.
    • Restar otra matriz: Igual que la suma. Ejem: c= a – b
    • Multiplicar por otra matriz: Ejem: c = a * b. El nro de filas de a debe ser igual al nro de columnas de b
    • Multiplicar por un escalar: Ingreso un número y todos los elementos de la matriz se multiplican por ese número.
    • Hallar matriz transpuesta: matr[m][n] su transpuesta es matr[n][m]. Se obtiene cambiando filas por columnas. Donde los elementos de la fila m ahora pertenecen a la columna m de la transpuesta.
    • Verificar si es simétrica: Una matriz es simétrica si es cuadrada(filas=columnas) y cuando su transpuesta es igual a su matriz original.
    • Verificar si es identidad: Es identidad si tiene todos sus elementos nulos excepto los de la diagonal principal que son iguales a 1.

    NOTA: He aplicado un poco de templates para manejar la matriz con varios tipos de datos (int, float, char,double) y sobrecarga de operadores para la suma, resta y multiplicación de matrices.

    Recien estoy aprendiendo a usar estas 2 características importantes del lenguaje (templates y sobrecarga de operadores) por lo tanto, estos temas serán tratados a profundidad el proximo año.

    En breve:

    Un template es una manera de que funciones, clases métodos puedan ser usados con varios tipos de datos. Imagínense crear una lista de datos y tener que crear funciones insertar, eliminar, buscar, concatenar, etc para cada tipo de dato. Si los métodos y la clase tienen la misma lógica para que reescribir código si podemos reutilizar.

    La sobrecarga de operadores es una manera de realizar las mismas operaciones que solemos hacer con tipos de datos comunes con tipos abstractos de datos. Por ejemplo, la sobrecarga me permitió sumar 2 objetos de tipo Matriz y almacenar el resultado en otro objeto Matriz, del modo c = a + b

    Al grano:

    Definición de la clase Matriz (La cabecera o header):

    matriz.h

    #ifndef MATRIZ_H
    #define MATRIZ_H
    
    #include <iostream>
    #include <time.h>
    
    using namespace std;
    
    template <class T>
    class Matriz
    {
      private:
        T mayor_,menor_,moda_,ele,**matr1;
        int dim_matriz;
      public:
        int fils,cols;
        T escalar;
    
        Matriz();
        Matriz(int,int);
        Matriz(const Matriz &m);//constructor copia
    
        void llenar_teclado();
        void llenar_aleatorio();
        void imprimir();
    
        T mayor();
        T menor();
        T moda();
    
        void inter_filas(int,int);
        void inter_cols(int,int);
    
        bool esSimetrica();
        bool esIdentidad();
        void multi_escalar(T);
        void transpuesta();
    
        Matriz<T> operator+ (const Matriz &matr2);
        Matriz<T> operator- (const Matriz &matr2);
        Matriz<T> operator* (const Matriz &matr2);
    
        void eliminar();
    
        ~Matriz();
        protected:
    };
    
    #endif // MATRIZ_H
    

    Implemetanción de la clase:

    matriz.cpp

    #include "Matriz.h"
    
    //Constructor por defecto
    template<typename T>
    Matriz<T>::Matriz()
    {
      fils=4;
      cols=4;
    }
    
    //Constructor copia
    template<typename T>
    Matriz<T>::Matriz(const Matriz &m)
    {
      *this = m;
    }
    
    //Constructor por parametro
    template<typename T>
    Matriz<T>::Matriz(int fils_ , int cols_)
    {
      fils = fils_;
      cols = cols_;
      matr1 = new T*[fils];
      for(int i=0;i<fils;i++){
          matr1[i] = new T[cols];
      }
    }
    
    //Llenar aleatoriamente una matriz
    template<typename T>
    void Matriz<T>::llenar_aleatorio()
    {
      //srand(time(NULL));
      for(int i=0;i<fils;i++){
        for(int j=0;j<cols;j++){
          matr1[i][j] = rand() % 30;
        }
      }
      mayor_= matr1[0][0];
      menor_= matr1[0][0];
      srand(time(NULL));
    }
    
    //Llenar una matriz desde teclado
    template<typename T>
    void Matriz<T>::llenar_teclado()
    {
      for(int i=0;i<fils;i++){
        cout << "Fila " << i+1 << endl;
        for(int j=0;j<cols;j++){
          cout << "Ingresa el elemento " << j+1 << endl;
          cin >>ele;
          matr1[i][j] = ele;
        }
        cout << endl;
      }
      mayor_= matr1[0][0];
      menor_= matr1[0][0];
    }
    
    //Imprimir matriz
    template<typename T>
    void Matriz<T>::imprimir()
    {
      for(int i=0;i<fils;i++){
        for(int j=0;j<cols;j++){
          cout << matr1[i][j] << " ";
        }
        cout << endl << endl;
      }
      cout << endl << endl;
    }
    
    //Obtener el mayor de la matriz
    template<typename T>
    T Matriz<T>::mayor()
    {
      for(int i=0;i<fils;i++){
        for(int j=0;j<cols;j++){
          if(matr1[i][j] > mayor_){
            mayor_ = matr1[i][j];
          }
        }
      }
      return mayor_;
    }
    
    //Obtener el menor de la matriz
    template<typename T>
    T Matriz<T>::menor()
    {
      for(int i=0;i<fils;i++){
        for(int j=0;j<cols;j++){
          if(matr1[i][j] < menor_){
            menor_ = matr1[i][j];
          }
        }
      }
      return menor_;
    }
    
    //Obtener la moda de la matriz
    template<typename T>
    T Matriz<T>::moda()
    {
      //creo una matriz auxiliar
      Matriz maux(fils,cols);
    
      //lleno la matriz con ceros
      for(int i=0;i<fils;i++){
        for(int j=0;j<cols;j++){
          maux.matr1[i][j] = 0;
        }
      }
    
      dim_matriz = fils * cols;
      int y=0;//para retener una fila n veces
      int z=0;//para retener una columna n veces
    
      //empiezo a comparar cada elemento n veces
      for(int x=0;x<dim_matriz;x++){
        for (int i = 0; i < fils; i++){
          for (int j = 0; j < cols; j++){
            if(matr1[y][z] == matr1[i][j]){
              maux.matr1[i][j]++;//incremento en 1 el valor de la matriz aux en esa posicion
            }
          }
        }
        //pasar a la siguiente columna despues de n comparaciones
        z++;
        if(z==cols){//empiezo a comparar con la siguiente fila
          z=0;//empiezo nuevamente en la 1era columna
          y++;//paso a la siguiente fila
        }
      }
    
      //obtengo el valor mayor de la matriz
      mayor_ = maux.mayor();
    
      if(mayor_==1){//si ningun valor se ha repetido mas de una vez no hay moda
        return -1;
      } else {
        for(int i=0; i<fils;i++){
          for(int j=0;j<cols;j++){
            if(maux.matr1[i][j] == mayor_) {
              moda_ = matr1[i][j];
            }
          }
        }
      }
      return moda_;
    }
    
    //Intercambiar dos filas en una matriz
    template<typename T>
    void Matriz<T>::inter_filas(int fil1, int fil2)
    {
      if(fil1 > fils || fil2 > fils){
        cout << "Por favor, hasta cuando " << endl;
      } else {
        T temp;
        fil1--;
        fil2--;
    
        for(int i=0;i<cols;i++){
          temp = matr1[fil1][i];
          matr1[fil1][i] =  matr1[fil2][i];
          matr1[fil2][i] = temp;
        }
        cout << "Se intercambio la fila " << fil1 + 1 << " por la " << fil2 + 1 << endl;
        imprimir();
      }
    }
    
    //Intercambiar dos columnas en una matriz
    template<typename T>
    void Matriz<T>::inter_cols(int col1, int col2)
    {
      if(col1 > cols || col2 > cols){
        cout << "Por favor, hasta cuando " << endl;
      } else{
        T temp;
        col1--;
        col2--;
    
        for(int i=0;i<fils;i++){
          temp = matr1[i][col1];
          matr1[i][col1] =  matr1[i][col2];
          matr1[i][col2] = temp;
        }
        cout << "Se intercambio la columna " << col1 + 1 << " por la " << col2 + 1 << endl;
        imprimir();
      }
    }
    
    //Verificar si una matriz es simetrica
    template<typename T>
    bool Matriz<T>::esSimetrica()
    {
      if(fils!=cols) {
        return false;
      }
    
      for(int i=0;i<fils;i++){
        for(int j=0;j<cols;j++){
          if (matr1[i][j]!=matr1[j][i]){
            return false;
           }
        }
      }
      return true;
    }
    
    //Verificar si una matriz es identidad
    template<typename T>
    bool Matriz<T>::esIdentidad()
    {
      if(fils!=cols) {
        return false;
      }
    
      for(int i=0;i<fils;i++){
        for(int j=0;j<cols;j++){
          if (i == j) {
            if (matr1[i][j] != 1)
              return false;
          } else {
            if (matr1[i][j] != 0)
              return false;
          }
        }
      }
      return true;
    }
    
    //Multiplicar a una matriz por un escalar
    template<typename T>
    void Matriz<T>::multi_escalar(T escalar)
    {
      for(int i=0;i<fils;i++){
        for(int j=0;j<cols;j++){
          matr1[i][j] = matr1[i][j] * escalar;
        }
      }
      cout << "Se multiplico a la matriz por el escalar " << escalar << endl;
      imprimir();
    }
    
    //Obtener la transpuesta de una matriz
    template<typename T>
    void Matriz<T>::transpuesta()
    {
      Matriz matresult(cols,fils);
      for(int i=0;i<cols;i++){
        for(int j=0;j<fils;j++){
          matresult.matr1[i][j]= matr1[j][i];
        }
      }
      matresult.imprimir();
    }
    
    //Suma de matrices con sobrecarga de operadores
    template<typename T>
    Matriz<T> Matriz<T>::operator+ (const Matriz &matr2)
    {
      Matriz matresult(fils,cols);
      for (int i=0;i<fils;i++){
        for (int j=0;j<cols;j++){
    	  matresult.matr1[i][j] = matr1[i][j] + matr2.matr1[i][j];
        }
      }
      return matresult;
    }
    
    //Resta de matrices con sobrecarga de operadores
    template<typename T>
    Matriz<T> Matriz<T>::operator- (const Matriz &matr2)
    {
      Matriz matresult(fils,cols);
      for (int i=0;i<fils;i++){
        for (int j=0;j<cols;j++){
          matresult.matr1[i][j] = matr1[i][j] - matr2.matr1[i][j];
        }
      }
      return matresult;
    }
    
    //Multiplicacion de matrices con sobrecarga de operadores
    template<typename T>
    Matriz<T> Matriz<T>::operator* (const Matriz &matr2)
    {
      Matriz matresult(fils,matr2.cols);
      T total;
      for (int i=0;i<fils;i++){
        for (int j=0;j<matr2.cols;j++){
          for(int k=0;k<cols;k++){
              total += (matr1[i][k] * matr2.matr1[k][j]);
              //para mostrar lo que pasa
              /*cout << "(" << matr1[i][k] << " * " << matr2.matr1[k][j] << ")";
              if(k<cols-1){
                  cout << " + ";
              } else {
                  cout << " = " << total;//despues del ultimo muestro la suma
              }*/
          }
          //cout << endl;
          matresult.matr1[i][j] = total;
          total = 0;//para limpiar el total sumado arriba
        }
        cout << endl;
      }
      return matresult;
    }
    
    template<typename T>
    void Matriz<T>::eliminar()
    {
      for(int i=0;i<fils;i++){
        delete[] matr1[i];
      }
      delete[] matr1;
    }
    
    template<typename T>
    Matriz<T>::~Matriz(){}
    

    main.cpp

    #include <iostream>
    #include "Matriz.h"
    #include "Matriz.cpp"
    
    using namespace std;
    
    int main()
    {
        srand(time(NULL));//para no generar los mismos numeros aleatorios
        int fils,cols,fil1,col1,fil2,col2;
    
        cout << "Ingresa nro de filas " << endl;
        cin >> fils;
        cout << "Ingresa nro de columnas " << endl;
        cin >> cols;
        cout << endl;
    
        Matriz<int> a(fils,cols);
        Matriz<int> b(fils,cols);
        Matriz<int> c(fils,cols);//matriz para almacenar el resultado de las operaciones
    
        a.llenar_aleatorio();//existe el metodo llenar_teclado() para otros tipos
        b.llenar_aleatorio();
    
        cout << "Operaciones con matrices " << endl;
        cout << "Matriz A " << endl;
        a.imprimir();
    
        cout << "Matriz B " << endl;
        b.imprimir();
    
        cout << "Matriz A + B " << endl;
        c = a + b;
        c.imprimir();
    
        cout << "Matriz A - B " << endl;
        c = a - b;
        c.imprimir();
    
        cout << "Matriz A * B " << endl;
        c = a * b;
        c.imprimir();
    
        cout << "Operaciones basicas con la matriz A" << endl;
        cout << "Matriz A " << endl;
        a.imprimir();
    
        cout << "El mayor de la matriz es: " << a.mayor() << endl;
        cout << "El menor de la matriz es: " << a.menor() << endl;
        cout << "La moda de la matriz es: " << a.moda() << endl;
        cout << (a.esSimetrica() ? "" : "No") << " Es simetrica." << endl;
        cout << (a.esIdentidad() ? "" : "No") << " Es identidad." << endl;
        cout << endl;
    
        cout << "Ingresa el escalar " << endl;
        cin >>a.escalar;
        a.multi_escalar(a.escalar);
    
        cout << "Intercambio: Ingresa dos filas del 1 al " << fils << endl;
        cin >>fil1;
        cin >>fil2;
        a.inter_filas(fil1,fil2);
    
        cout << "Intercambio: Ingresa dos columnas del 1 al " << cols << endl;
        cin >>col1;
        cin >>col2;
        a.inter_cols(col1,col2);
    
        cout << "Transpuesta de A " << endl;
        a.transpuesta();
    
        a.eliminar();
        b.eliminar();
        c.eliminar();
    
        return 0;
    }
    

    Particularmente, todos los ejercicios no son nada difíciles de resolver, pero yo me estanqué en la moda y creo que fue el problema que me hizo pensar un rato.

    Lo resolví, pero sinceramente no me siento bien con la solución. No me gustó porque tiene una complejidad de O(n2) y con una matriz muy grande puede tarda mucho debido al número de recorridos y comparaciones.

    La solución consistió en:

    • Crear otra matriz auxiliar con el mismo nro de filas columas y llenarla con ceros.
    • Recorrer la matriz original e ir comparando el primer elemento con todos los otros elementos de la matriz y contar sus repeticiones y así sucesivamente con los demás elementos.
    • Si encuentro una repetición, incremento en uno el valor en esa posición de la matriz auxiliar.
    • Luego hallar el mayor elemento de la matriz auxiliar.
    • Y finalmente recorrer la matriz auxiliar, cuando algún elemento de esta matriz sea igual al mayor elemento, quiere decir, que ese fue el elemento que más se repitió en la matriz original. Por lo tanto la moda se encuentra en esa posición.
    Matriz original
    17 16 10 26
    10  3 9 19
    29 7  1 6
    6  20 0 10
    
    Matriz auxiliar al inicio
    0 0 0 0
    0 0 0 0
    0 0 0 0
    0 0 0 0
    
    Matriz auxiliar despues de las comparaciones
    con el nro de repeticiones de los valores
    1 1 3 1
    3 1 1 1
    1 1 1 2
    2 1 1 3
    

    La moda de la matriz es: 10,
    3 es el valor maximo que se repite en la matriz
    Y en las posiciones en que se encuentra 3 se encuentra 10 en la matriz original.

    Un tip que me dió el profe es reducir el nro de comparaciones creando una matriz booleana e ir poniendo unos y ceros a los números que ya comparé para luego no volver a comparar con los números que comparé anteriormente. Voy a seguir resolviendo el problema,si alguien tiene otra solución y puede explicarla fantástico.

    Si yu





    Vectores, Matrices y Punteros en c++

    4 07 2009

    Estimados lectores y fans, les pido disculpas por este humilde y sencillo post, se que este mini-tutorial super básico es una humillación a su inteligencia y profesionalismo, pero comprendan que esto va dedicado a personas que como yo recién empiezan con sus primeros pininos en C++. Espero de verdad le sea útil a alguien.

    Escribir todo esto, me costó una noche entera y mucho café. Parte de los conceptos aquí los aprendí en clases de la U y mucho también lo aprendí por cuenta propia. Así que agradecimientos al teacher Patricio del cual aprendí muchísimo. Una vez más a ustedes y a los tantos miles de seguidores que me leen a diario las disculpas del caso.

    Vamos ir avanzando de a pocos así que no se preocupen y desesperen porfavor, paciencia. La idea es que esta sección crezca con su ayuda, feedback y motivación. Luego vamos a ver ejemplos con objetos, clases, sobrecarga de operadores, métodos de búsqueda, màs ordenación, punteros, listas, pilas, colas, templates… por ahora sigo practicando y haciendo simples cosas con este lenguaje. Los códigos los compilé con g++, el compilador de GNU.

    VECTORES
    Un vector, también llamado array(arreglo) unidimensional, es una estructura de datos que permite agrupar elementos del mismo tipo y almacenarlos en un solo bloque de memoria juntos, uno despues de otro. A este grupo de elementos se les identifica por un mismo nombre y la posición en la que se encuentran. La primera posición del array es la posición 0.

    Podríamos agrupar en un array una serie de elementos de tipo enteros, flotantes, caracteres, objetos, etc. Crear un vector en c++ es sencillo, seguimos la siguiente sintaxix: Tipo nombre[tamanyo];

    Ejm:

    int a[5];//Vector de 5 enteros
    float b[5];//vector de 5 flotantes
    Producto product[5];//vector de 5 objetos de tipo Producto
    

    Podríamos también inicializar el vector en la declaración:

    int a[] = {5,15,20,25,30};
    float b[] = {10.5,20.5,30.5,12.5,50.5}
    Producto product[] = {celular,calculadora,camara,ipod,usb}

    Como hay 5 elementos en cada array, automáticamente se le asignará 5 espacios de memoria a cada vector.Pero si trato de crear el vector de la forma int a[]; el compilador mostrará un error, porque no indiqué el tamaño del vector ni tampoco inicializé sus elementos.

    Asigno valores a los elementos de un vector indicando su posición:

    int a[4] = 30; // le asigno el valor 30 a la posición 4 del vector, es decir, al 5to elemento.
    product[2].setPrecio(300) // le asigno un precio de 300 al producto en la posicion 2, o sea al tercer elemento. 

    Obviamente el método setPrecio() debe de estar implementado.

    Para llenar, recorrer e imprimir un vector podemos utilizar un bucle for:

    #include <iostream>
    using namespace std;
    
    int main()
    {
      int dim;
      cout << "Ingresa la dimension del vector" << endl;
      cin >> dim; // Supongamos que ingrese 10
      int vector[dim]; // mi vector es de tamanyo 10
    
      for(int i=0;i < dim;i++){
        vector[i] = i * 10;
        cout << vector[i] << " ";
      }
    
      return 0;
    }

    La salida del programa mostrará: 0 10 20 30 40 50 60 70 80 90

    Fàcil verdad? Bien ahora creen 2 o más vectores y empiecen a hacer funciones básicas como sumar, restar, buscar, ordenar, moda, etc que ayudan mucho a ir desarrollando la lógica. No vale copiar y pegar, mejor es practicar, practicar y practicar.

    Aquí una función simple para sumar 2 vectores a y b y poner el resultado en un tercer vector c

    #include <iostream>
    using namespace std;
    
    void sumar(int a[], int b[], int c[],int dim){
        for (int i=0; i<dim; i++) {
            c[i]=a[i] + b[i];
        }
    }
    
    void imprimir(int v[],int dim)
    {
       for(int i=0;i<dim;i++){
            cout << v[i] << " ";
       }
       cout << endl << endl;
    }
    
    int main()
    {
        int dim;
        cout << "Ingresa la dimension" << endl;
        cin >> dim;
    
        int a[dim];
        int b[dim];
        int c[dim];
    
        for(int i=0;i<dim;i++){
            a[i] = i * 10;
            b[i] = i * 5;
        }
    
        cout << "Vector A " << endl;
        imprimir(a,dim);
    
        cout << "Vector B " << endl;
        imprimir(b,dim);
    
        sumar(a,b,c,dim);
        cout << "Vector C " << endl;
    
        imprimir(c,dim);
        return 0;
    }

    Este programa me botaría (si ingreso una dimensión de 10):

    Vector A
    0 10 20 30 40 50 60 70 80 90

    VECTOR B
    0 5 10 15 20 25 30 35 40 45

    VECTOR C
    0 15 30 45 60 75 90 105 120 135

    Entonces para tomar en cuenta:

    Todo vector debe tener definido un tipo de dato.
    Todo vector necesita de una dimensión o tamanyo.
    El código de arriba se puede mejorar muchísimo con objetos y clases, este es solo un pequeño ejemplo.

    MATRICES
    Una matriz es un vector de vectores o un también llamado array bidimensional. La manera de declarar una matriz es c++ es similar a un vector:

    int matriz[fils][cols];

    int es el tipo de dato, matriz es el nombre del todo el conjunto de datos y debo de especificar el numero de filas y columnas. Las matrices también pueden ser de distintos tipos de datos como char, float, double,etc.

    Las matrices en c++ se almacenan al igual que los vectores en posiciones consecutivas de memoria. Usualmente uno se hace la idea que una matriz es como un tablero. Pero internamente el manejo es como su definicion lo indica, un vector de vectores, es decir, los vectores estan uno detras del otro juntos.

    La forma de acceder a los elementos de la matriz es utilizando su nombre e indicando los 2 subindices que van en los corchetes. Si Coloco int matriz[2][3]=10; //estoy asignando al cuarto elemento de la tercera fila el valor 10. No olvidar que tanto filas como columnas se enumeran a partir de 0.

    Bueno y para recorrer una matriz podemos usar igualmente un bucle. En este caso 2 for

    for(int i=0;i<fils;i++){
      for(int j=0;j<cols;j++){
        matriz[i][j] = i % j;
      }
    }

    PUNTEROS
    El valor de todas las variales que manejamos en nuestros programas se almacenan en memoria y tienen una dirección. Un puntero es una variable especial que apunta a la dirección de memoria de una variable. El puntero tiene a su vez su propia dirección. Todas estas direcciones tienen un formato hexadecimal.

    Los punteros son herramientas muy poderosas con muchas utilidades y enormes ventajas como veremos más adelante. A grandes rasgos, un puntero me permite desplazarme en la memoria, apuntar, redireccionar a ciertas variables, funciones, métodos, objetos sin necesidad de mover grandes bloques de datos, lo cual nos ahorra muchísimo el consumo de memoria en los programas.

    Un puntero se debe declarar de acuerdo al tipo de dato al que apunta. Ejem:

    int *var; //Un puntero llamado var que podra apuntar a cualquier variable de tipo entero.
    char *u;//puntero de tipo char
    Persona *per;//puntero de tipo persona

    Para determinar,asignar la dirección de una variable en c++, se usa el operador & y para obtener el contenido de un puntero utilizamos el operador * Ejem:

    int a;//entero
    int *b;//puntero a entero
    a = 20;//a tiene 20
    b=&a;//asigno la direccion de a al puntero b
    
    cout << b << endl; // imprira la direccion de memoria de a;
    cout << *b;// imprimira 20, osea el contenido de a

    Ahora analicemos las siguientes instrucciones y veamos como las variables van cambiando de valor en tiempo de ejecución:

    #include <iostream>
    using namespace std;
    int main () {
        int a=10;
        int b=20;
        int *p,*p2;//punteros de tipo entero
    
        cout << "ANTES" << endl;
        cout << "Variable a = " << a << endl;
    
        cout << "Direccion de a = " << &a << endl << endl;
    
        cout << "Variable b = " << b << endl;
        cout << "Direccion de b = " << &b << endl << endl;
    
        cout << "Contenido de p (BASURA)= " << *p << endl;//tiene basura al principio, podria inicializar con *p=0
        cout << "Direccion de p = " << &p << endl << endl;
    
        cout << "DESPUES" << endl;
        a++;//incremento a
        p= &a; //p ahora tiene a la direccion de a
    
        cout << "Contenido de p =  " << *p << endl;
    
        p = &b;//p ahora tiene la direccion de b
        *p +=20; // le sumo 20 al contenido de p, es decir, estoy incrementando el valor de b
    
        cout << "Variable a = " << a << endl;
        cout << "Variable b = " << b << endl << endl;
    
        p=&a;//p ahora tiene la direccion de a
        *p = a * 5;//contenido de p es igual al contenido de a * 5
    
        cout << "Contenido de p = " << *p << endl;
        cout << "Variable a = " << a << endl << endl;
    
        cout << "Contenido de p2 (BASURA) = " << *p2 << endl;//tiene basura al principio, podria inicializar con *p2=0
        cout << "Direccion de p2 = " << &p2 << endl << endl;
    
        p2 = p;//el contenido de p es asignado al contenido de p2
        *p2 +=15;//incremento 15 al contenido de p2
    
        cout << "Contenido de p2 = " << *p2 << endl;//igual al contenido de p
        p++;//p apunta a otra direccion de memoria,se desplaza 4 bytes en memoria
        cout << "Contenido de p (BASURA) = " << *p << endl;//el contenido de esa nueva direccion
    
        return 0;
    }
    

    La salida del programa:
    ANTES

    Variable a = 10
    Direccion de a = 0×22ff74

    Variable b = 2
    Direccion de b = 0×22ff70

    Contenido de p (BASURA)= -1017291943
    Direccion de p = 0×22ff6c

    DESPUES
    Contenido de p = 11
    Variable a = 11

    Variable b = 40
    Contenido de p = 55
    Variable a = 55

    Contenido de p2 (BASURA) = 2293680
    Direccion de p2 = 0×22ff68
    Contenido de p2 = 70

    Contenido de p (BASURA) = 2293680

    El contenido de p y p2 al principio es basura porque no tienen ningun valor asignado aun. Podriamos asignar el valor NULL a un puntero para luego posteriormente en algun problema que se me presente saber el estado del puntero y saber si contiene algo o no, así:

    int *p= NULL;

    ARITMÉTICA DE PUNTEROS

    En las ultimas sentencias del programa anterior:

    p++;
    cout << *p;

    Pueden visualizar que estoy incrementando el puntero p en 1. Esto quiere decir que el puntero se desplazara 4 bytes en memoria (en este caso por ser entero) y entonces apuntara a otra direccion. Por eso es que el nuevo contenido de p es basura o bueno el contenido de lo que tiene esa nueva direccion a la que apunta.

    Supongamos que definir un entero y puntero de tipo char:

    char c;
    char *d;
    
    d= &c;//asigno la direccion de c a d
    c='u';//asigno el valor u a mi variable c
    c--;//desplazo una posicion a c
    cout << *d;//

    No Imprimira ‘u’ porque fijense que desplazé c en sentido negativo 1 byte (los char ocupan a 1 byte). Es decir, que si d estaba apuntado a una direccion como por ejemplo 0×22ff99, despues del c– estara apuntando a algo como 0×22ff98

    Para tomar en cuenta cosas que no puedo hacer con punteros:

    int a=15;
    int *p;
    
    double *q;
    void *r;
    
    p = a; //No puedo hacer esto porque estoy asignando una variable a un puntero y un puntero es una direccion.
    p = &50; // 50 es un valor constante en este caso y no una variable,por lo tanto no tiene direccion
    p = &(a+1); //una expresion no tiene direccion
    p = 30;//igual que el primer error, 30 es un entero.
    &a = p;//no puedo cambiar la direccion de una variable
    p = q;//p es puntero de tipo entero y q de tipo double

    Un puntero de tipo void, es un puntero al cual le podemos asignar cualquier tipo de puntero. Por lo tanto si podriamos hacer esto:

    r = p;

    VECTORES Y PUNTEROS
    Cuando declaramos un vector int v[10];El nombre del vector, o sea v, es un puntero al primer elemento del vector, es decir a v[0].Entonces como un vector es un puntero al primer elemento del mismo, también podríamos hacer aritmética de punteros con el vector.

    (v+1) ;//apuntara a v[1];
    *(v+5);//me refiero al contenido de v[5]
    
    //Y también a los punteros les puedo poner índices:
    
    int *p; //puntero de tipo entero
    p = &v[0];//p apunta a la direccion del vector v[0] o tambien a v. p = v
    p[8] = 80; //le asigno el valor 80 al puntero en la posicion 8, es decir a v[8]
    

    VECTORES DINÁMICOS

    Lo que vimos en el inicio de este post, son vectores estáticos, puesto que tienen una cantidad fija de memoria asignada y tamaño definido que no podemos modificarlo. Sin embargo, un vector podría tener una cantidad variable de datos, a este se le llama un vector dinámico.

    Para usar vectores dinámicos necesitamos gestionar memoria dinámica. Si bien es cierto que es trae enormes ventajas, el hacer un mal uso de la memoria dinámica nos podría traer problemas desastrozos. Por eso es importante que que cuando creemos vectores dinámicos también liberemos la memoria utilizada. Obviamente eliminaremos la memoria utilizada cuando ya no necesitamos más usar, en este caso, un determinadao vector.

    El operador new sirve para reservar memoria dinámica.
    El operador delete se usa para liberar la memoria dinámica reservada con new.
    Para liberar memoria de un array dinámico usamos delete[]

    El espacio de memoria que hemos reservado con new tendrá vida hasta que finalize la ejecución del programa o cuando liberemos ese espacio con delete. Siempre es recomendable liberar memoria para posteriormente no tener problemas con excesivo consumo de memoria.

    Un simple ejem:

    #include <iostream>
    using namespace std;
    
    int main()
    {
        int *pv;
        int dim;
    
        cout << "Ingresa el tamanyo del vector" << endl;
        cin >>dim;
        pv = new int[dim];
    
        for(int i=0;i<dim;i++){
            pv[i] = i * i;
            cout << pv[i] << " ";
        }
    
        delete[] pv;
        return 0;
    }

    MATRICES Y PUNTEROS

    Supongamos que se declaró una matriz int m[5][5]

    Como dijimos anteriormente, el nombre o identificador de un vector es un puntero al primer elemento del vector. En el caso de matrices el nombre de la matriz, en este ejemplo v, es un puntero que apunta al primer elemento del primer vector de la matriz. Entonces m es un doble puntero.m es igual a &m[0] que es igual a la direccion de &m[0][0].

    Si declaramos un puntero int *pm y luego igualamos pm = m, p ahora puede desplazarse por los valores de m.

    *p;//contenido de m[0],el cual apunta al primer elemento de ese vector, es decir, m[0][0]

    También puedo referirme a los contenidos con aritmética de punteros

    *(p+1);//Desplazo una posicion a p,se refiere al contenido de m[1],el cual apunta al primer elemento de ese vector, es decir, m[1][0]
    *(*(p+1)+1);//desplazo una posición en el vector principal y este a su vez se desplaza una posicion en ese vector. es decir,me refiero al contenido de m[1][1];
    
    p[2][4] = 20;//asigno el valor 20 a la posicion 2,4 de la matriz
    *(*(p+2)+4) = 20 // es lo mismo que la asignacion anterior
    *(pm[2]+4) = 20  // tambien los mismo
    
    //En conclusión:
    p[i][j] = *(*(p+i)+j) = *(pm[i]+j)
    

    MATRICES DINÁMICAS

    Para crear una matriz dinámica debemos de crear un doble puntero int **pm y samos al igual que los vectores el operador new para reservar memoria y delete para liberar.

    Primero tenemos que crear el vector que contendrá a otros vectores especificando el numero de vectores que tendra este vector principal. Ejem:

    pm = new int* [fils];//creo el vector de punteros principal
    for(int i=0;i<fils;i++){
      pm[i] = new int [cols];//para crear los vectores dentro del vector principal
    }
    

    Ahora sí vamos al grano y veamos un simple programa que crea una matriz dinámica, asigna valores, muestra el contenido de cada uno de los elementos los elementos así como sus direcciones de memoria.

    También mostramos la matriz usando aritmética de punteros,ahí va:

    #include <iostream>
    using namespace std;
    
    int main()
    {
      int **pm;//puntero a una matriz
      int fils,cols;
    
      cout << "Ingresa el nro de filas: ";
      cin >>fils;
    
      cout << endl;
      cout << "Ingresa el nro de columnas: ";
      cin >>cols;
    
      pm = new int* [fils];
      for(int i=0;i<fils;i++){
        pm[i] = new int [cols];
      }
    
      cout << "Elementos de la Matriz con sus direcciones" << endl;
      for(int i=0;i<fils;i++){
        for(int j=0;j<cols;j++){
          pm[i][j] = i + j;
          cout << pm[i][j] << "--> ";
          cout << &pm[i][j] << " ";
        }
        cout << endl;
      }
      cout << endl;
    
      cout << "La matriz con aritmetica de punteros" << endl;
      for(int i=0;i<fils;i++){
        for(int j=0;j<cols;j++){
          *(*(pm+i)+j) = i + j;//aritmetica de punteros
          cout << *(*(pm+i)+j) << "-->";
          cout << &pm[i][j] << " ";
        }
        cout << endl;
      }
    
      for(int i=0;i<fils;i++){
        delete[] pm[i];//elimino cada vector de la matriz
      }
      delete[] pm;//elimino el vector principal
      return 0;
    }
    

    La salida del programa:

    Ingresa el nro de filas: 6
    Ingresa el nro de columnas: 4

    //Elementos de la Matriz con sus direcciones
    0–> 0×3d2c90 1–> 0×3d2c94 2–> 0×3d2c98 3–> 0×3d2c9c
    1–> 0×3d2ca8 2–> 0×3d2cac 3–> 0×3d2cb0 4–> 0×3d2cb4
    2–> 0×3d2cc0 3–> 0×3d2cc4 4–> 0×3d2cc8 5–> 0×3d2ccc
    3–> 0×3d3ab8 4–> 0×3d3abc 5–> 0×3d3ac0 6–> 0×3d3ac4
    4–> 0×3d3ad0 5–> 0×3d3ad4 6–> 0×3d3ad8 7–> 0×3d3adc
    5–> 0×3d3ae8 6–> 0×3d3aec 7–> 0×3d3af0 8–> 0×3d3af4
    
    //La matriz con aritmetica de punteros
    0–> 0×3d2c90 1–> 0×3d2c94 2–> 0×3d2c98 3–> 0×3d2c9c
    1–> 0×3d2ca8 2–> 0×3d2cac 3–> 0×3d2cb0 4–> 0×3d2cb4
    2–> 0×3d2cc0 3–> 0×3d2cc4 4–> 0×3d2cc8 5–> 0×3d2ccc
    3–> 0×3d3ab8 4–> 0×3d3abc 5–> 0×3d3ac0 6–> 0×3d3ac4
    4–> 0×3d3ad0 5–> 0×3d3ad4 6–> 0×3d3ad8 7–> 0×3d3adc
    

    En mi caso esa son las direcciones de memoria. Bueno ya que hemos revisado conceptos básicos a continuación veamos la Clase Matriz, su definición, algunos métodos y su implementación.





    Tail recursion

    19 05 2009

    La recursividad es una técnica de programación que consiste en que una serie de instrucciones se repiten como una subtarea de la tarea principal, es decir, las funciones, procesos o rutinas se llaman a sí mismos cada vez que lo requieran y se ejecutan repetidas veces hasta que se satisface una condición específica.

    Sin embargo, la recursividad en algunos casos, tiene un costo computacional alto, debido a las constantes llamadas a la misma función, rutina y muchas veces estas llamadas consumen demasiada memoria.

    En algunos algoritmos recursivos, se puede implementar un caso de recursividad especial llamado Tail Recursion (recursividad por cola), la cual es una técnica para optimizar la recursividad eliminando las constantes llamadas recursivas. Tail recursion es cuando la llamada recursiva es la última instrucción de la función.

    Sin embargo, las funciones tail recursive deben cumplir la condición que en la parte que realiza la llamada a la función, no debe existir ninguna otra sentencia.

    Una ventaja de la recursividad por cola es que podemos evitar la sobrecarga de cada llamada a la función y nos evitamos el gasto de memoria de pila. Con una función tail recursive se puede evitar lo que se conoce como stack overflow, que ocurre cuando la pila de llamadas (call stack) consume mucha memoria.

    Veamos esta técnica con un ejemplo muy conocido que es encontrar el factorial de un número.

    Esta simple función obtiene el factorial de un número de la manera recursiva convencional: La explicación es sencilla, llamamos a la misma función fact hasta que n sea igual a 1, en ese momento la recursividad se detiene.

    int fact_recursivo(int n)
    {
      if(n == 1)
        return n;
      else
        return n * fact_recursivo(n-1);
    }
    
    int main() {
      int num=0;
      cout << "Ingresa un nro " << endl;   cin >> num;
    
      cout << "Su factorial es " << fact_recursivo(num);
      return 0;
    }
    

    Digamos que ingresé 15, si compilamos el programa la función me daría 2004310016, lo cual es correcto, pero hay que tener en cuenta todas las llamadas recursivas que hizo la función fact_recursivo, es decir, la función tiene que calcular el factorial de 14 y este el fact de 13, y este el fact de 12… sucesivamente hasta 1.  Tenemos un crecimiento del número de llamadas recursivas.

    Ahora vamos a realizar la misma función usando tail recursion. En este caso no se necesita guardar un marco de pila para cada llamada recursiva, y el algoritmo se comporta como si fuera iterativo.

    Para conseguir esto usamos un parámetro adicional que actúa como acumulador.

    Esta sería la función factorial con tail recursion:

    //function factorial con tail recursion
    int fact_tail_sum(int n, int sum) {
      if(n == 1) {
        return sum;
      } else {
        return fact_tail_sum(n - 1, sum*n);
      }
    }
    
    //para mantener la sintaxis original de como se llama a la función factorial
    int fact_tail(int n) {
      return fact_tail_sum(n, 1);
    }
    
    int main() {
      int num=0;
      cout << "Ingresa un nro " << endl;   cin >> num;
      cout << "Su factorial es " << fact_tail(num);
      return 0;
    }
    

    El resultado igualmente sería 2004310016, pero el costo computacional baja drásticamente debido a que cuando se hace la llamada return fact_tail_sum(n – 1, sum*n) ambos parámetros son inmediatamente resueltos. Con esta llamada podemos calcular sin esperar una llamada a una función recursiva para que nos devuelva un valor.

    En este caso el compilador reduce drásticamente el uso de la pila y la cantidad de información que debe ser almacenada, el valor de n y sum es independiente del número de llamadas recursivas.

    Una función recursiva normal se puede convertir a tail recursive usando en la función original un parámetro adicional, usado para ir guardando un resultado de tal manera que la llamada recursiva ya no tiene una operación pendiente.

    También se usa una función adicional para mantener la sintaxis de como llamamos normalmente a la función. En el caso del factorial, para seguir llamando a la función de la forma fact(n).
    En conclusión:

    • Una llamada es tail recursive (recursiva por cola) si no tiene que hacer nada más después de la llamada de retorno.
    • Tail recursion es cuando la llamada recursiva es la última instrucción en la función.
    • Usar tail recursion es muy ventajoso, porque la cantidad de información que debe ser almacenada durante el cálculo es independiente del número de llamadas recursivas.
    • El compilador trata una función tail recursive como si fuera una función iterativa.





    Quicksort en C++

    19 05 2009
    Quicksort es un algoritmo de ordenación  considerado entre los más rápidos y eficientes. Fue diseñado en los años 60s por C. A. R. Hoare un científico en computación. El algoritmo usa la técnica divide y vencerás que básicamente se basa en dividir un problema en subproblemas y luego juntar las respuestas de estos subproblemas para obtener la solución al problema central.

    Me pareció una buena manera de practicar programación y C++ y hacer una explicación que permite entender este algoritmo , bueno al menos eso intenté.  Hace año y medio también publiqué unos ejemplos de ordenación en Haskell.

    Voy a explicar el funcionamiento del quicksort brevemente y después veremos su implementación en C++.

    Se tiene una array de n elementos, tomamos un valor del array como pibote(usualmente el primero), separamos los elementos menor a este pibote a laizquierda y los mayores a la derecha, es decir, dividimos el array en 2 subarrays.

    Con estos subarrays se repite el mismo proceso de forma recursiva hasta que estos tengan más de 1 elemento. Por lo tanto la función quicksort quedaría de la siguiente manera:

    void quicksort(int *array, int inicio, int fin)
    {
      int pivote;
      if (inicio < fin) {
        pivote = dividir(array, inicio, fin );
        quicksort( array, inicio, pivote - 1 ); //ordeno la lista de los menores
        quicksort( array, pivote + 1, fin ); //ordeno la lista de los mayores
      }
    }
    

    La magia está en la función dividir que es la que voy a explicar a
    continuación:

    Empezamos creando o generando un array de n elementos, por ejemplo yo use la función rand() de c++ para generar aleatorios del 1 al 15, mi arreglo quedó así:

    a[] = {8, 1, 5, 14, 4, 15, 12, 6, 2, 11, 10, 7, 9};

    izq                                                                              der

    0 1 2 3 4 5 6 7 8 9 10 11 12
    8 1 5 14 4 15 12 6 2 11 10 7 9

    Tomamos como pibote el 8 y usamos 2 índices que me indiquen la posición del array:

    Uno que vaya de izquierda a derecha buscando los elementos mayores al pibote. a[izq]

    Y un índice que busque de derecha a izquierda los elementos menores al pibote.  a[der]

    El índice izquierdo irá aumentando en 1 mientras el array en la posición
    izquierda sea menor o igual al pibote:

    while ((izq < der) && (array[izq] <= pibote)){
      izq++;
    }
    

    El índice derecho irá reduciéndose en 1 mientras el array en la posición
    derecha sea mayor al pibote.

    while (array[der] > pibote){
      der--;
    }
    

    Si al final de estas 2 operaciones, el índice izquierdo es menor al derecho se intercambian las posiciones a[izq] con a[der], usando una variable temporal:

    En este caso, en la primer recorrido el índice izquierdo encuentra al 14 (mayor al pibote) y el índice derecho al 7 (menor al pibote), y
    se intercambian los índices:

    8 1 5 14 4 15 12 6 2 11 10 7 9
    8 1 5 7 4 15 12 6 2 11 10 14 9

    Segundo recorrido: El índice izquierdo encuentra al 15 (mayor al
    pibote) y el índice derecho al 2 (menor al pibote), se intercambian:

    8 1 5 7 4 15 12 6 2 11 10 14 9
    8 1 5 7 4 2 12 6 15 11 10 14 9

    Tercer recorrido: El índice izquierdo encuentra al 12 (mayor al
    pibote) y el índice derecho al 6 (menor al pibote), se intercambian:

    8 1 5 7 4 2 12 6 15 11 10 14 9
    8 1 5 7 4 2 6 12 15 11 10 14 9

    Cuando los índices se juntan o se cruzan ponemos el pibote en el lugar que le corresponde en el array:

    temp = array[der];
    array[der] = array[inicio]; // el pibote está en el inicio del array
    array[inicio] = temp;
    

    Se intercambian el 8 con el 6 y el array quedaría así:

    6 1 5 7 4 2 8 12 15 11 10 14 9

    Ahora la función quicksort se llamaría 2 veces recursivamente para los 2 subarray que tenemos:

    quicksort(a,0,5) // el pibote está en la posición 6
    quicksort(a,7,12) // el pibote está en la posición 6
    

    Se repite el mismo proceso con este primer subarray quicksort(a, 0, 5)

    El pibote es 6, se encuentra por la izquierda al 7 y por la derecha al 2. Se intercambian y como se cruzaron los índices movemos el pibote a su lugar:

    6 1 5 7 4 2
    6 1 5 2 4 7
    4 1 5 2 6 7

    Otra vez tenemos 2 subarrays. Entonces se vuelve a llamar la función

    quicksort(a,0,3) // la posición del pibote es 4
    quicksort(a,5,5) // No se ejecuta nada, el inicio no es menor al final
    

    El mismo proceso. Se ejecutar quicksort (a, 0, 3). El pibote ahora es 4, se encuentra por el índice izquierdo al 5 y por el derecho a 2.  Se intercambian y como se juntaron los índices se mueve el pivote a su lugar.

    4 1 5 2
    4 1 2 5
    2 1 4 5
    quicksort (a,0,1) // la posición del pibote es 2
    quicksort (a,3, 3) // No se ejecuta nada, el inicio no es menor al final
    

    Cuando se ejecuta quicksort (a, 0, 1) se intercambia los índices otra vez.

    2 1
    1 2

    Ahora se llamaría a quicksort(a, 0,0), se divide en 2 elementos el subarray
    y no hay nada más que hacer por que solo tienen 1 elemento.

    Ahora se ejecuta el mismo proceso con el segundo subarray del array original. Quicksort (a, 7,12)

    Se encuentra el 15 y el 9, se intercambian y como luego se juntan los índices, se coloca el pibote a su lugar:

    12 15 11 10 14 9
    12 9 11 10 14 15
    10 9 11 12 14 15

    Tenemos otros 2 subarrays y se vuelve a llamar la función quicksort

    quicksort(a,7,10) // posición del pibote es 10 y la 1era 7
    quicksort(a,11,12) //posición del pibote es 10 y la ultima 12
    

    Se intercambian los índices y nos quedan otros 2 subarrays de 1 solo elemento entonces no se ejecuta nada.

    10 9 11
    9 10 11

    Con el anterior subarray (14, 15) se llama a quicksort(a, 11,12).

    14 15

    Se divide en 2 elementos este subarray y no hay nada más que hacer por que los array que contienen al 14 y al 15 solo tienen 1 elemento.

    De manera que el árbol recursivo de ordenación queda más o menos así:

    Quick Sort

    Quick Sort

    Complejidad computacional del Quicksort:

    En el mejor de los casos tiene un costo de O(n*log (n)). Que es cuando el pibote siempre queda al medio del arreglo.

    quicksort

    Quicksort Mejor caso

    En el peor de los casos tiene un costo de O(n^2). Cuando el pibote siempre se inclina hacia a un lado, es decir, genera una array de sólo 1 elemento y una segunda con el resto de elementos.

    quicksort

    Quicksort peor caso

    En el caso promedio también tiene un costo de O(n*log (n)). Se produce cuando el pibote se inclina más hacia un lado y los 2 subarrays tienen distinto tamaño de elementos.

    quicksort

    Quicksort caso promedio

    Para calcular el tiempo de ejecución estoy usando la función clock() que determina el tiempo usado por el procesador. En este caso defino 3 variables ini, final y total.

    ini=clock(); // Antes del quicksort:
    final = clock(); //Después que se ejecuta el quicksort
    total =((double)(final – ini)) /
    CLOCKS_PER_SEC; // El valor retornado por clock()
    debe ser dividido por el valor de la macro CLOCKS_PER_SEC
    

    He leído en varios sitios de c++ que la función clock no tiene mucha precisión. Si alguien sabe un mejor método para calcular el tiempo de ejecución, algún comentario, sugerencia o aporte para optimizar al algoritmo, bienvenido.

    Queda pendiente para otro post calcular con exactitud tiempos de ejecución e implementar el código del número de comparaciones e intercambios.

    Bueno, aquí está la implementación del algoritmo en C++ disfrutenlo.

    #include <iostream>
    #include <time.h>
    #include <stdio.h>
    
    using namespace std;
    
    //funcion para dividir el array y hacer los intercambios
    int dividir (int *array, int inicio, int fin){
      int izq;
      int der;
      int pibote;
      int temp;
    
      pibote = array[inicio];
      izq = inicio;
      der = fin;
    
      //Mientras no se cruzen los índices
      while (izq < der){
        while (array[der] > pibote){
    	  der--;
        }
    
    	while ((izq < der) && (array[izq] <= pibote)){
          izq++;
        }
    
        // Si todavia no se cruzan los indices seguimos intercambiando
    	if(izq < der){
          temp= array[izq];
          array[izq] = array[der];
          array[der] = temp;
        }
      }
    
      //Los indices ya se han cruzado, ponemos el pivote en el lugar que le corresponde
      temp = array[der];
      array[der] = array[inicio];
      array[inicio] = temp;
    
      //La nueva posición del pivote
      return der;
    }
    
    //funcion recursiva para hacer el ordenamiento
    void quicksort( int *array, int inicio, int fin)
    {
      int pivote;
      if(inicio < fin){
        pivote = dividir(array, inicio, fin );
        quicksort( array, inicio, pivote - 1 );//ordeno la lista de los menores
        quicksort( array, pivote + 1, fin );//ordeno la lista de los mayores
      }
    }
    
    int main()
    {
      int const max = 100;
      int tamanyo;
      clock_t ini, final;
      double total;
      ini=clock();
    
      cout << "Ingresa tamanyo " << endl;
      cin >> tamanyo;
    
      int a[tamanyo];
      srand(time(0));//para que el rand no genere siempre los mismos numeros, utilizando la hora del sistema
    
      for (int i = 0; i < tamanyo; i++){
        a[i] = rand() % max;//rand para generar enteros aleatorios
      }
    
      cout << "Array antes de ordenarse: " << endl;
      for (int i=0; i < tamanyo; i++){
        cout << a[i] << " ";
      }
    
      cout << endl << endl;
    
      quicksort(a, 0, tamanyo - 1 );
    
      final=clock();
      total=((double)(final - ini)) / CLOCKS_PER_SEC;
      printf("Tiempo de ejecucion : %lf segundos \n",total);
    
      cout << "Array ordenado " << endl;
    
      for (int i=0; i < tamanyo; i++ ){
        cout << a[i] << "-";
      }
      cout << endl << endl;
    
      return 0;
    }
    




    Install KDE 4.2 in debian

    2 03 2009

    This is how looked my desktop some minutes after the kde 4.2 installation.

    KDE 4 Runner, Leave note, Twitter, RSS, NowPlay, Dictionary, Clock -> Widgets in KDE 4.2Y

    You need Debian unstable (Sid) and enable the experimental branches to install kde 4.2 version.Have in mind that this Debian branches come with lots of bugs and sometimes your system doesn’t respond as you like. Well, going to the whole point.

    Add this to your /etc/apt/sources.list

    deb http://ftp.de.debian.org/debian/ sid main
    deb-src http://ftp.de.debian.org/debian/ sid main

    Enable the experimental branch by adding this to your /etc/apt/sources.list

    deb http://ftp.de.debian.org/debian/ experimental main
    deb-src http://ftp.de.debian.org/debian/ experimental main

    Update your system:

    sudo apt-get update

    Install the KDE packages from experimental with the command:

    sudo aptitude -t experimental install kde4-minimal

    kde4-minimal includes:
    kdebase-runtime: Essential runtime components
    kdebase-workspace: Desktop environment
    kdebase: Core applications

    If you want a complete installation, run:

    sudo aptitude -t experimental install kde4

    This command will install all packages of KDE 4.2 and could take some time. But, I recommend you install just the packages you need like: kdepim, kdemultimedia, kdenetwork, kdeplasma-addon, etc. Look at the Debian/KDE website for more information.

    ¡Enjoy your KDE Desktop!

    Here are some screnshots of my desktop when playing with KDE 4.2.

    Calendar, Folder view, Picture widgests in Plasma

    Calendar, Folder view, Picture widgests in Plasma

    Blue Curl Picture

    Blue Curl Picture

    Iceweasel

    Iceweasel

    Dolphin

    Dolphin

    Kickoff Menu, Twitter widget, Folder View

    Kickoff Menu, Twitter widget, Folder View

    Konsole, RSS, Runner, Dictionary

    Konsole, RSS, Runner, Dictionary





    KDE 4.2 “The answer”

    2 03 2009

    kde 4First of all: I always prefer and love KDE for his elegance, customization, easy-usage, great applications and now because is really  interesting for programming. I use KDE in my everyday work: Konqueror (now Dolphin), konsole, k3b, Amarok, kate, Konversation, Kopete and other ones.

    When KDE 4.0 was published many people criticize it very hard: A lot of bugs, different look and concept of desktop, different panels, new applications menu, widgets, etc, etc were some features that made most KDE users unhappy. However, KDE developers always said in the announcement page, blogs and mailing lists that KDE 4 is a work in progress and they will be working, coding hard to release new versions of KDE (4.1, 4.2… ) with a lot of improvements.

    KDE 4.1 was much better than KDE 4.0(many bugs killed and improvements,more stability, applications,speed), but KDE 4.2, in my opinion, is a version, that give us a stable desktop  with cool, interesting, features not only for users but also for developers. This KDE version was named: “The answer” because it minimize negative, destructive. comments and people who didn’t believe in the KDE community and team. I consider these people don’t know appreciate the innovation and great effort.

    Congratulations to the all KDE Team for the hard-work they made. I am really impressed about how can you customize your desktop, add new features and the KDE 4 development way.

    In this video you can see the KDE 4 presentation in Google Campus where Aaron Seigo a famous KDE hacker member of the core-team talk about the KDE 4 technology and how KDE innovation can make current desktops obsolete.

    Nepomuk, Decibel, Plasma, Oxygen, Solid, Phonon, Akonadi are some parts of KDE 4 that introduce new behaviours and will make a big change in KDE. I am particularly interested in Plasma and Nepomuk for development and deciding in which project participate for this Google Summer of Code. But I need more reading and practicing about how these work. If you  make a Gsoc proposal without researching, reading, practicing and talking to mentors you probably will fail.  In this page, you can see the initial process to start your KDE 4 development environment.

    In general KDE 4  has as main goals: Improve the desktop user experience, have better applications and development platform and make our desktop a central place to be more productive. That is my impression and for now on I will continue researching and folowing the KDE development process.





    Blogging once again

    28 02 2009

    Wow.  Almost 4 months since my last post, sometimes it’s too hard to update my blog. I’m married, have a beautiful baby, study and work and believe me that is a little bit difficult to manage.

    However, I continue exploring and learning new technologies about my passion: Open Source/Free Software.

    Here are some things that happened in the last months.

    I started learning Python and Django and I have to admit that since the first moment I tried them that I like it very much. On the other hand, I got tired about how slow rails is and the fucking error messages whenever you upgrade some gems and plugins. Conclusion: I switched to Python-Django for web development. Now I am re-writing an application, written in Rails, with Django.

    My boss acquired a dedicated server hosting to run a lot of web services for his company use and proposed me to manage it. Now I am maintaining the server and it was fun to learn SSH and Fedora commands (I use Debian). SSH takes part of my everyday freelance work.

    Last week, I finished the development of an e-commerce site for an American company called Misti International. I used the Magento e-commerce platform to develop it. Which at the beginning seems beautiful with a lot of cool features, but you need some patience to understand it deeply, because has a complex structure with too many folder and files and it is kind of difficult to customize it. But, I worked hard on this site and my client is really happy about it. I strongly recommend to learn PHP5 and take a look at the Zend Framework to learn more about the Magento code.

    I installed KDE 4.2 in my Debian machine. (Here is a quick tutorial ). The only thing I want to say is: THANKS to all the KDE team for give us this great technology that is so well created and designed. But I will talk about KDE in other posts, because I am really interested in developing for KDE.

    Google Summer of Code 2009 was announced and I am going to apply this year again.  I already got in contact with a mentor to work in his proposal and received good feedback about it.

    That’s it. Well , I forgot to mention that with Maritza and Celeste (wife and daughter) visited many tourist places here in Arequipa and had great moments. I love spend and enjoy time with my 2 girls.





    Debian GNU/Linux 5.0 – Lenny released

    14 02 2009

    Esta es la nota de publicación de esta nueva versión de este gran sistema operativo..

    El Proyecto Debian se complace en anunciar la publicación oficial de la versión 5.0 de Debian GNU/Linux, nombre en clave lenny, tras 22 meses de desarrollo constante. Debian GNU/Linux es un sistema operativo libre que soporta un total de doce arquitecturas de procesador e incluye los entornos de escritorio KDE, GNOME, Xfce y LXDE. También ofrece compatibilidad con el estándar FHS v2.3 y software desarrollado para la versión 3.2 de LSB.

    (para descargar Lenny ahora mismo, visita la página de descargas de Debian)

    Debian GNU/Linux se ejecuta en ordenadores que van desde agendas hasta supercomputadoras, pasando por prácticamente cualquier sistema intermedio. Se da soporte a un total de doce arquitecturas: Sun SPARC (sparc), HP Alpha (alpha), Motorola/IBM PowerPC (powerpc), Intel IA-32 (i386), IA-64 (ia64), HP PA-RISC (hppa), MIPS (mips, mipsel), ARM (arm, armel), IBM S/390 (s390), y AMD64 de AMD y EM64T de Intel (amd64).





    ¡Grande BarCampLima!

    10 11 2008

    Que linda experiencia, lindo día, no sé como describirlo, pero el 1er BarCamp Lima para mi fue simplemente de puta madre. Un evento que definitivamente debe repetirse, me encantó los pequeños debates que se armaban mientras alguien exponía, la informalidad, la colaboración, el espíritu open source, charlas interesantísimas, aprendí muchísimo sobre todo temas de los cuales tenía muy poco conocimiento como por ejemplo Computación en la nube (Cloud Computing) y también temas que me apasionan como programación en la web.

    Y claro también hice unos cuantos amigos e intercambié un par de ideas con varios de los geeks presentes.

    Aquí está el video de mi charla, motivando a la gente para que participe en el Google Summer of Code.

    Google Summer of Code – Barcamp Lima

    Felicitaciones a todos los organizadores, colaboradores, participantes y a todos los que hicieron de esto un gran día.

    ¡Salud por el BarCampLima!





    BarCamp Lima 2008

    2 11 2008

    Si señores, se viene el 1er BarCamp Lima este 8 de noviembre, el cual no me pienso perder por ninún motivo. Ya estoy haciendo mis maletas. Así que me voy al barcamp a encontrarme con toda esa gente geek. Creo que estoy en racha de viajes, proyectos, me encanta esta vida. Será Dios?. No creo.

    Un BarCamp es una reunión abierta, libre y flexible, que tiene como finalidad compartir conocimiento e intercambiar experiencias, y sobre todo buscar la participación de todos los asistentes a fin de interactuar en una verdadera comunidad. En un Barcamp todos participan, todos pueden dar una charla sobre su tema de preferencia. En un barcamp normalmente se habla sobre tecnología, internet. Más info en la wikipedia.

    En el caso del barcamp Lima se tocarán temas como web 2.0, negocios en internet, open source. Contará con la presencia de bloggers, desarrolladores, diseñadores, hackers, geeks, fans de linux y del software libre.

    Por mi parte, voy a dar con una charla sobre el Google Summer of Code – Gran Oportunidad para comprometerse realmente con el Open Source (Mentores y estudiantes).  Voy a colaborar llevando a una de mis mejores amigas: mi cámara, ah y pizarra y plumones. Y porsupuesto que pienso compartir mis pequeñas experiencias, anécdotas  en proyectos web, uso de Linux y también seguramente aprenderé bastante de los demás. Eso es lo bueno de este tipo de charlas: El feedback.

    Pueden ver el sitio oficial del evento y la wiki. Entre los auspiciadores está HP del Perú que brindará el local que tiene un aforo para 70 personas, así que hay que ir temprano muchachos. Por lo que tengo entendido las inscripciones ya están cerradas.

    Felicito a los organizadores, 2 grandes comunidades nacionales la Asociación Nacional De Webmasters Del Perú y el el PLUG por su dedicación, esfuerzo y organizar un evento así en tiempo récord.

    Así que nos vemos en el BarCamp Lima y a disfrutar del evento.





    What I learned from Gsoc?

    30 10 2008

    Google Summer of Code was a wonderful experience for me, I’ll never forget it. I finished my project and that gave me a lot of satisfaction. Getting opinions and feedback from the open source community was really special. In general, this is what I learned:

    Community:

    Improve my communication skills: Doing a project that involves other people means that you have to be clear in your words, be brief but efficient, and most important, make people understand what you are trying to say.

    English:

    I wrote a lot in English. (My project, IRC meetings, asking in the list, answering questions, etc). Although, English is not my first language I enjoyed having to be clear with my mentor, my organization and the Google group of summer of coders.

    Technology

    Ruby and Ruby on Rails: This Ruby user’s guide helped me a lot. I haven’t finished it, but I learned a lot about this great language (regular expressions, strings, arrays, iterators, control structures, OOP, classes, methods). And I also do a lot of programing with the ruby on rails framework. I learned how to manage and modified some plugins.

    Actually working with views is not my favorite part of a project. However, CSS work at the end of the project was really fun. What CSS make is impressive.

    Solr: I never ever have worked with a search server. Solr is just amazing – Full-Text Search Capabilities- . t I learned how to integrate Solr and the act_as_solr plugin to my Rails application.

    Subversion: I learned how to work with this collaboration tool which I consider make you more productive. However, I had to deal with so many error messages, strange problems, I need to learn more commands apart from the common ones.

    Vulnerability and Patch concepts.

    I believe that be an expert in these fields takes his time. I never did a security system and didn’t work with a security team before. But I learned key concepts related to patches and vulnerabilities that helped me a lot to write the code. For example:

    • Vulns classification (Location, Attack Type , Impact, Solution, Exploits).
    • Vulns technical description and how to test a vulnerability.
    • Patch severity (Critical, Severe,important, Minor, Pointless )
    • Security Products: Nikto, Snort and Nessus.
    • What CVE means.
    • How to associate a vuln-patch with a Vendor/Product/Version

    Many lessons learned in these months let me think what I did wrong and what I did well. I feel that  I’m not always as productive as I might like to, my effort changes with the tasks I’m doing.

    Once I read some Linus interview in which he said that if you are completely present in a situation and totally focused on something then that something *becomes* interesting, whatever it may be.

    So, I think that making your job interesting and fun and get really concentrated on the problem are the keys to your project success.

    I would like to participate for Gsoc 2009 again.

    ¡Happy Hacking!





    La teoría del todo de Hawking.

    29 10 2008

    Estoy contento, hace una semana compré mi espectacular libro “La teoría del todo” cuyo autor es uno de los científicos, en mi opinión, más grandes de todos los tiempos: Stephen Hawking.

    Siempre he tenido dudas y mucho interés en leer sobre el origen, evolución del universo y del hombre. Leí varios artículos acerca de esto, pero siempre me quedaba con interrogantes por la diversidad de teorías. Sin embargo, creo que Hawking es uno de los investigadores científicos, que más ha hablado y escrito acerca del universo en los últimos años, por eso tenía una deuda conmigo mismo: Leer un libro del tío Hawking.

    Me parece atrevido decir que Dios creo al universo y no tener algún sustento de ello. Bueno y yo que soy agnóstico con mucha más razón no puedo afirmar ni negar dicha teoría. Por eso los invito a leer este libro.

    Estoy en el capítulo 5: El origen y el destino del universo. La lectura no ha sido muy difícil, aunque sinceramente ciertos párrafos los he leído un par de veces para entenderlos mejor porque contenían un vocabulario complejo. Quizá si fuera físico y no programador, sería mucho más sencillo.

    Lo interesante del libro es que Hawking hace un resumen de ideas antiguas del universoy también habla de teorías de la gravedad y relatividad de Einstein y Newton, habla del big bang  los agujeros negros, y nuestra época actual, bueno eso es hasta donde leí.

    Voy a hacer un breve resumen del libro cuando acabe de leerlo. Se los recomiendo muchísimo, sumamente interesante.





    ¿Porqué perdemos el tiempo?

    21 10 2008

    Es impresionante como los medios de comunicación le dan semejante relevancia a la captura de un personaje de televisión. Que por más famoso o popular que sea, es solo eso, un personaje.

    Amo a mi país, y me da tristeza ver como la gente se llena la boca, todos los días, hablando de temas que no tienen absolutamente relación alguna con los problemas reales por los que atraviesa el país: Pobreza, Corrupción, educación, desempleo, etc.

    ¿Porqué perdemos el tiempo?, porqué no invertir ese tiempo en cosas más productivas. porqué no en vez de ver televisión y hablar de Magaly Medina o su programa mediocre, pensamos en aportar ese granito de arena para hacer un Perú mejor o hablar de proyectos educativos, de generación de empleos.

    Porque no existen medios de comunicación peruanos que hablen de esos 3 puntos vitales hoy en día de los que tanto habla Oppenheimer: Ciencia, Tecnología y Educación. Cuando tengo la remota oportunidad de ver algo en TV o leer algún periódico siempre mantengo la esperanza viva de encontrar algo que eduque, algo interesante, pero siempre me quedo con las manos vacías y siempre me pregunto ¿porqué perdemos el tiempo?.

    Cada vez estoy más convencido que la TV es una basura. Y si no fuera por el Discovery, Animal Planet, National Geography, History Channel y afines sería aún más basura.

    No perdamos el tiempo, en lugar de ello, dediquemos tiempo a nuestras familias, mejor dedicarse a estudiar , trabajar, escribir o leer algo, en fin realizar algo educativo o que nos divierta. En fin, tantas cosas más productivas podríamos hacer.

    Bueno es solo una opinión, ojalá a alguien le sirva o ayude algo.





    De vuelta al GYM

    1 10 2008

    Hip, hip urra. Después de un duro año sin tiempo para nada, regresé al gym. Ya era tiempo de un relax, salud mental y mucho ejercicio. Me da mucha satisfacción, aunque levantarse a las 6:30am todos los días si me trasnocho no va a ser fácil, pero le voy a poner huevos. Y lo bueno que tendré todos los días la companía de mi hermanita Nadia.

    El plan a corto plazo es octubre, noviembre y diciembre, bien trabajados, para llegar en formar al verano 2009.  Una buena alimentación, creatina para las fuerzas y energías, dormir al menos 6 horas diarias, un breve descanso en la tarde, investigaré  respecto a que es lo mejor para mí.

    Antes del ingreso

    No pongo los nombres de las máquinas porque no me las se todas. Mi rutina es básica, en sí, para todos los ejercicios estoy haciendo 4 series de 12 c/u. Casi todo fue recomendación del chato. (El instructor)

    Lunes: Pecho y espalda  + Abdominales y pantorrillas.

    Martes: Biceps y Triceps + Abdominales y pantorrillas.

    Miércoles: Hombros y piernas + Abdominales y pantorrillas.

    Continúa sucesivamente para el jueves, viernes y sábado. Porfavor si alguien tiene un consejo, sugerencia opinión será bienvenido.

    A la salida





    Rails 2.2 will be thread safe

    19 08 2008

    Reading the rubyonrails blog I read that Rails 2.2 will be thread safe and that this hard work comes from a Google Summer of Coder. Josh Peek who will join the rails core.

    This is a great advance for the framework, because many people were looking for merb for this rails weak, but this improvement will attract new developers to join the rails community.

    Congratulations Josh.





    Finishing my project for the Gsoc

    18 08 2008

    These are the latest changes on the project. Before that, it is important to know all the work made before and the information we can submit for a patch.

    Basic Information:
    Dates(Disclosure, Creation)
    Description
    Classification
    Severity
    Ratings

    Association with:
    Products
    Vulnerabilities
    Files
    Documentation Links
    Credits (Authors, Companies)

    User Interaction:
    Comments:
    Stats (views, percent complete).

    Updates:

    * Allow users select patches based on the watchlist.
    * Patches were incorporated into the solr search engine.Main fields were indexed(id, title,short and technical description,dates,vendors and products). For the moment, the search for patches is separated from the search for vulnerabilities.
    * An advanced search for patches (For severity, for classification, for products)
    * When adding vulnerabilities to a patch, users can auto complete vulns based on the their identifiers..
    * I made some CSS work changing the style for the show page on the patches, that way users can distinguish immediately an information page for a patch from a vulnerability.
    * However I will need one or maybe two more days to finish all my presentation styles and user alerts when a patch is released and goes into the portal.

    I am really glad to see that everything is coming fine and all work made in these months is having sense for me.





    Passed GSoC Mid-Term Evaluation

    14 07 2008

    I am really happy. I passed the Google Summer of Code 2008 mid-term evaluation! This means that I get to finish my project patch management portal. I want to thank Dave, my mentor for give me the opportunity to continue working on the project.

    This is a quick report of all the work made until this point.

    May 26 – June 1: Tables used on the project were created and integrated into the OSVDB schema. Use cases were defined.

    June 1-18:

    I created an MVC to manage patches. So the base_patches_controller.rb allow us to manage all the actions related to patch submission. I was testing the code and submitting my first patches and all their relevant information and things are going quite well.

    So, principal functions were created. You can submit general information about a patch, also associate it with a vendor/product/version, associate it with an author, vulnerability and add a patch rating.

    These are the principal actions (methods). However, more actions will be created this week, specially for ratings and vulnerabilities.

    General: create edit update destroy
    Ratings: addpatchrating updatepatchrating
    Products: addvendor showversions
    Credits: adduthor update_author deleteauth
    Vulnerabilities addvuln deletevuln

    I also created and MVC for base_patch_rating_levels. So, it is possible to CRUD patch_rating_levels.

    I just worked in models creating maping objects (Has many, belongs_to, has_and_belongs_to), but didn’t work on validations yet. I expect to work in views at the end of this week. (This is HTML and CSS work).

    June 18-30: We have the core of Patch Management Portal written. Patches are linked to vulnerabilities and shown in the home page.

    -Main views for patches were created.
    -Optimization in the database tables.(indexes)
    -Active and fragmet caching in some pages and methods.And a patch sweeper created.
    -Moderator nav modified to submit a patch and to C,R,U,D rating levels with Active scaffold.
    -Home page modified to show “Latest OSVDB Patches” (similar functionality to vulns) with Printer,normal and popup views.

    -Validations added to models.

    July 01-07: Working on file submission.

    This is really important because users can submit a file for everybody to download. So, it is more easy for mostly users to apply the patch just by reading the short-technical description and downloading the file(code).
    my my
    July 13: File Submission finished. Users can add/delete files related to patches.

    You can see my work in progress here in the project wiki and also take a look at my mid-term evaluation. Later on I will be publishing the source code.

    I expect to finish the project successfully and be part of the development team.





    Rdoc – Generating documentation for ruby and rails

    3 07 2008

    Rdoc is a program for creating documentation for ruby source code. Rdoc generates HTML documentation, using syntactic information from the source and text in comment blocks.

    You can check documentation about ruby libraries if you have ruby property installed by running the ri command. For example, if we want to know what the capitalize method does, just type in your terminal:

    ri String.capitalize
    
    ---------------------------------------------------String#capitalize
    str.capitalize    => new_str
    --------------------------------------------------------------------
    Returns a copy of str with the first character converted to
    uppercase and the remainder to lowercase
    "hello".capitalize  #=> "Hello"
    "HELLO".capitalize  #=> "Hello"
    "123ABC".capitalize  #=> "123abc"

    Rdoc is very useful to look for information about programs, methods and examples.

    If you want to see the rails api documentation type in your terminal: gem server and then go to your browser at the url http://localhost:8808

    You will get an HTML page “RubyGems Documentation Index” with a summary of all ruby gems installed on your system.

    Click on the gem you want to see the documentation and you will see an HTML page with 3 columns: (Files, Classes, Methods) and everything documented in detail.

    If you installed rails with rubygems you can access the rails api.
    Also the rake app:doc command creates the HTML documentation for your Rails project and stores this documentation in the doc/app directory.

    Run the file doc/app/index.html and you will see of the documentation of your rails project.





    Query analyzer for Rails

    3 07 2008

    I found an interesting and very useful plugin for rails called Query analyzer that allows us check for tables not optimized in our database. It really helps a lot to optimize tables and queries and put indexes on our conditions columns and primary/foreign keys. Sometimes, we forget to do things so simple like this. Here is a great explanation of what the plugin does.

    How to use it?

    Just install the plugin with:

    script/plugin install http://svn.nfectio.us/plugins/query_analyzer

    and then check your logs. You can see your console as well. Once you have the plugin installed you will see all queries your application does and how they are currently managed.

    For example:

    I have a table called form_help_divs. It is a table to show some messages in forms around my application. I usually make a call to the database to get a form_help_div record. The query generated is like this:

    SELECT * FROM `form_help_divs` WHERE (`form_help_divs`.`name` = 'patch_vuln') LIMIT 1

    So in theory. The query should check for only one row, but that is not true because the name column doesn’t have an index. And how I realized that?. I just checked my log and saw this:

    Analyzing FormHelpDiv Load
    
    select_type | key_len | type | Extra       | id | possible_keys | rows | table          | ref | key
    ---------------------------------------------------------------------------------------------------
    SIMPLE      |         | ALL  | Using where | 1  |               | 33   | form_help_divs |     |

    As you see the query is looking for all records (in this case 33) in the database searching for the name ‘patch_vuln’. That is not good guys. Let us optimize our queries by adding and index to that table. We can add it by hand in our mysql interface, but is better to create migrations.

    class AddFormHelpDivIndexes < ActiveRecord::Migration
    def self.up
    add_index :form_help_divs, :name
    end
    
    def self.down
    remove_index :form_help_divs, :name
    end
    end

    Type rake db:migrate in your console and your migration will be executed.Now run your application again and you will see the difference in your logs and console. This is the result:

    FormHelpDiv Load (0.001585)   SELECT * FROM `form_help_divs` WHERE (`form_help_divs`.`name` = 'patch_vuln') LIMIT 1
    
    Analyzing FormHelpDiv Load
    
    select_type | key_len | type | Extra     | id | possible_keys | rows |table  | ref   | key
    -------------------------------------------------------------------------------------------------
    SIMPLE   |768|ref|Using where|1|name,index_form_help_divs_on_name|1|form_help_divs | const | name

    ¡What a big difference!. Now the query is looking just for one row in the database and that will increase the query speed significantly.

    There are other ways to make our applications running faster with rails. Adding indexes is one of those ways and let us gain a lot of optimization specially if we are working with big applications and tables with so many records.

    I strongly recommend you to optimize tables and use the query analyzer.





    Rake

    30 05 2008

    Rake is a ruby program that builds other ruby programs. Each time you execute rake, it knows how to build those programs by reading a file called Rakefile which has a set of tasks. Those tasks allows us to do some project needs in a very easy and efficient way.

    When you generate a rails project you automatically get a Rakefile and it is in the root of your project.

    You can see all rake tasks and their descriptions by running a simple command in your main directory:

    rake –tasks

    And this should be shown:

    rake backgroundrb:remove # Remove backgroundrb from your rails …
    rake backgroundrb:restart # Restart backgroundrb server (default…
    rake backgroundrb:setup # Setup backgroundrb in your rails app…
    rake backgroundrb:start # Start backgroundrb server (default v…
    rake backgroundrb:stop # Stop backgroundrb server (default va…
    rake db:abort_if_pending_migrations # Raises an error if there are pending…
    rake db:charset # Retrieves the charset for the curren…
    rake db:collation # Retrieves the collation for the curr…
    rake db:create # Create the database defined in confi…
    rake db:create:all # Create all the local databases defin…
    rake db:drop # Drops the database for the current R…
    rake db:drop:all # Drops all the local databases define…
    rake db:fixtures:identify # Search for a fixture given a LABEL o…
    rake db:fixtures:load # Load fixtures into the current envir…
    rake db:migrate # Migrate the database through scripts…
    rake db:migrate:redo # Rollbacks the database one migration…
    rake db:migrate:reset # Resets your database using your migr…
    rake db:reset # Drops and recreates the database fro…
    rake db:rollback # Rolls the schema back to the previou…
    rake db:schema:dump # Create a db/schema.rb file that can …
    rake db:schema:load # Load a schema.rb file into the database
    rake db:sessions:clear # Clear the sessions table
    rake db:sessions:create # Creates a sessions migration for use…
    rake db:structure:dump # Dump the database structure to a SQL…
    rake db:test:clone # Recreate the test database from the …
    rake db:test:clone_structure # Recreate the test databases from the…
    rake db:test:prepare # Prepare the test database and load t…
    rake db:test:purge # Empty the test database
    rake db:version # Retrieves the current schema version…
    rake doc:app # Build the app HTML Files
    rake doc:clobber_app # Remove rdoc products
    rake doc:clobber_plugins # Remove plugin documentation
    rake doc:clobber_rails # Remove rdoc products
    rake doc:plugins # Generate documentation for all insta…
    rake doc:rails # Build the rails HTML Files
    rake doc:reapp # Force a rebuild of the RDOC files
    rake doc:rerails # Force a rebuild of the RDOC files
    rake log:clear # Truncates all *.log files in log/ to…
    rake notes # Enumerate all annotations
    rake notes:fixme # Enumerate all FIXME annotations
    rake notes:optimize # Enumerate all OPTIMIZE annotations
    rake notes:todo # Enumerate all TODO annotations
    rake rails:freeze:edge # Lock to latest Edge Rails or a speci…
    rake rails:freeze:gems # Lock this application to the current…
    rake rails:unfreeze # Unlock this application from freeze …
    rake rails:update # Update both configs, scripts and pub…
    rake rails:update:configs # Update config/boot.rb from your curr…
    rake rails:update:javascripts # Update your javascripts from your cu…
    rake rails:update:scripts # Add new scripts to the application s…
    rake remove_simple_captcha_files # Remove unuseful captcha images and s…
    rake routes # Print out all defined routes in matc…
    rake secret # Generate a crytographically secure s…
    rake solr:destroy_index # Remove Solr index
    rake solr:start # Starts Solr.
    rake solr:stop # Stops Solr.
    rake stats # Report code statistics (KLOCs, etc) …
    rake test # Test all units and functionals
    rake test:functionals # Run tests for functionalsdb:test:pre…
    rake test:integration # Run tests for integrationdb:test:pre…
    rake test:plugins # Run tests for pluginsenvironment / R…
    rake test:recent # Run tests for recentdb:test:prepare …
    rake test:uncommitted # Run tests for uncommitteddb:test:pre…
    rake test:units # Run tests for unitsdb:test:prepare /…
    rake tmp:cache:clear # Clears all files and directories in …
    rake tmp:clear # Clear session, cache, and socket fil…
    rake tmp:create # Creates tmp directories for sessions…
    rake tmp:pids:clear # Clears all files in tmp/pids
    rake tmp:sessions:clear # Clears all files in tmp/sessions
    rake tmp:sockets:clear # Clears all files in tmp/sockets
    rake uml:schema # Generate an XMI db/schema.xml file d…

    Those rake tasks will be used many times when developing your rails application.
    For example there is an interesting task rake stats which generates detailed statistics about your application code and provides a dashboard of information. This is what I get:

    +----------------------+-------+-------+---------+---------+-----+-------+
    | Name                 | Lines |   LOC | Classes | Methods | M/C | LOC/M |
    +----------------------+-------+-------+---------+---------+-----+-------+
    | Controllers          |  6080 |  5241 |      54 |     369 |   6 |    12 |
    | Helpers              |   552 |   501 |       0 |      25 |   0 |    18 |
    | Models               |  3855 |  3274 |     157 |     326 |   2 |     8 |
    | Libraries            |  3068 |  2806 |      18 |      71 |   3 |    37 |
    | APIs                 |     9 |     9 |       1 |       0 |   0 |     0 |
    | Components           |     0 |     0 |       0 |       0 |   0 |     0 |
    | Integration tests    |     0 |     0 |       0 |       0 |   0 |     0 |
    | Functional tests     |  1261 |   942 |      97 |     200 |   2 |     2 |
    | Unit tests           |  1246 |   884 |     117 |     129 |   1 |     4 |
    +----------------------+-------+-------+---------+---------+-----+-------+
    | Total                | 16071 | 13657 |     444 |    1120 |   2 |    10 |
    +----------------------+-------+-------+---------+---------+-----+-------+
    Code LOC: 11831     Test LOC: 1826     Code to Test Ratio: 1:0.2

    Many other rake commands are very useful for rails development.





    Summer of Code started

    26 05 2008

    Hey. Today Summer of Code started and I feel pretty well at this point.

    I had a good chat with my mentor David Shettler (Leader software developer of the OSVDB team). I consider him a complete professional and better mentor because of his support, humility and his advices.

    I remember perfectly the first words he told me today: “Oh Ronny. Fun starts today”.

    We discuss many things , mainly about the current OSVDB database schema wich is really big. This let me get a better understanding of the project and making an initial design of my work and use cases.

    I got subversion access and I am now looking the source code and running this great software project (OSVDB 2.0). This is a real web application written in ruby on rails used to manage vulnerabilities and all the osvdb website. It is just amazing.

    I really enjoyed this day and think this will be a great experience for me and other summer of coders. I am going to inform of my work progress in the OSVDB gsoc 2008 wiki.

    Good luck to everyone. Enjoy the summer.





    Hpricot installation problem

    5 05 2008

    Hpricot is a fast, flexible HTML parser written in C.Hpricot can be handy for reading broken XML files, since many of the same techniques can be used. If a quote is missing, Hpricot tries to figure it out.

    I was really having problems to install hpricot, the newest version is 0.6. Whenever I executed

    $ sudo gem install hpricot
    Select which gem to install for your platform (arm-linux)
    1. hpricot 0.6 (mswin32)
    2. hpricot 0.6 (jruby)
    3. hpricot 0.6 (ruby)
    4. hpricot 0.5 (ruby)
    5. hpricot 0.5 (mswin32)
    6. Skip this gem
    7. Cancel installation
    > 3
    
    I received the following message:
    
    Building native extensions.  This could take a while...
    ERROR:  While executing gem ... (Gem::Installer::ExtensionBuildError)
    ERROR: Failed to build gem native extension.
    
    I Typed this to see what is going on:
    ruby extconf.rb install hpricot
    
    and the error continued.
    
    extconf.rb:1:in `require': no such file to load -- mkmf (LoadError)
    from extconf.rb:1
    
    Gem files will remain installed in /var/lib/gems/1.8/gems/hpricot-0.6 for inspection.
    Results logged to /var/lib/gems/1.8/gems/hpricot-0.6/ext/hpricot_scan/gem_make.out
    $
    

    Googling for a while I found a good solution in the hpricot project wiki. The main problem is that I had ruby 1.8 and 1.9 installed, Ruby 1.8.5 was the version I was using, but I didn’t have development libraries installed. So, gem couldn’t install correctly hpricot.

    You can check your version for ruby, rails, gem and everything else by typing -m after the program name.

    $ ruby -v
    ruby 1.8.5 (2006-08-25) [i486-linux]
    $ gem -v
    1.1.1
    

    The solution:
    There is a file we need called mkmf.rb that has connection with ruby libraries, so let’s search it.

    $ auto-apt search mkmf.rb
    usr/lib/ruby/1.9/mkmf.rb        devel/ruby1.9-dev
    
    As you can see, we don't have it in ruby1.8 directory, so let us install ruby1.8 development libraries.
    
    $ sudo apt-get install ruby1.8-dev
    
    and now, we have the mkmf.rb in ruby1.8
    
    $ auto-apt search mkmf.rb
    usr/lib/ruby/1.9/mkmf.rb        devel/ruby1.9-dev
    usr/lib/ruby/1.8/mkmf.rb        devel/ruby1.8-dev
    

    There is no documentation in the wiki, but I assume that you have to install another gem called mechanize which has as a dependency hpricot. By doing:

    $ sudo gem install mechanize
    Install required dependency hpricot? [Yn]
    Select which gem to install for your platform (arm-linux)
    1. hpricot 0.6 (mswin32)
    2. hpricot 0.6 (jruby)
    3. hpricot 0.6 (ruby)
    4. hpricot 0.6 (jruby)
    5. hpricot 0.6 (ruby)
    6. hpricot 0.6 (mswin32)
    7. Skip this gem
    8. Cancel installation
    > 3
    Building native extensions.  This could take a while...
    Successfully installed mechanize-0.6.9
    Successfully installed hpricot-0.6
    Installing ri documentation for mechanize-0.6.9...
    Installing ri documentation for hpricot-0.6...
    Installing RDoc documentation for mechanize-0.6.9...
    Installing RDoc documentation for hpricot-0.6...
    

    you will get hpricot successfully installed and now you can use this great gem.





    Wellcome Google Summer of Code

    21 04 2008
    wget http://code.google.com/soc/2008/soc.gz
    tar xvzf soc.gz
    cd soc
    ./configure && make && make install
    
    #!/usr/bin/ruby
    
    class SummerofCode
      attr_accessor :words
    
      def initialize(words = "Happy")
        @words = words
      end
    
      def wellcome
        if @words.nil?
          puts "..."
        elsif @words.respond_to?("each")
    
          @words.each do |word|
            puts " #{word}"
          end
        else
          puts " #{@words}"
        end
      end
    
    end
    
    if __FILE__ == $0
      soc = SummerofCode.new
      soc.wellcome
    
      soc.words = "Thanks OSVDB"
      soc.wellcome
    
      soc.words = ["Wellcome", "Google", "Summer of Code","2008"]
      soc.wellcome
    
    end
    
    ruby soc.rb
    
    Happy
    Thanks OSVDB
    Wellcome
    Google
    Summer of Code
    2008
    ...
    ...
    
    rails patch_management_portal

    These last two months were really exciting:
    Researching, reading, writing, talking with mentors, reviewing and reviewing my applications, hanging out in the Gsoc IRC channel, nervous, anxious, refreshing the Gsoc page every minute and finally waiting for the final word. ¡Great experience! But, this just starts.

    THANKS TO GOOGLE, OSVDB, and folks from the open source community who encouraged, adviced and guided me to participate in this Summer of Code.

    I am going to work and code hard to make the best Open Source “Patch Management Software” and give it free (Like in Freedom) to the Information Security World and of course to the Open Source Vulnerability Database.

    I dedicate all this effort to my wonderful country Peru and to the women who trust 100% at me: My four wifes: Maria, Nadia, Maritza and Celeste.

    WE CAN DO IT

    Patch Management Portal at Google





    Migrations

    15 12 2007

    Rails has a great feature called migrations which allow the developer to have control over the database schema by using ruby code and avoiding use the conventional SQL language. But, more important is that the developer can apply certain changes to move a database from one state to another, add-remove columns,indexes, tables etc. By using migrations you can get different versions of your database structure.

    How it works?

    Migrations are files found in the db/migrate directory and have a sequence number in the filename.

    For example, each time you want to add a migration you can generate it with:

    ./script/generate migration CreateCompanies
    
    exists  db/migrate
    create  db/migrate/001_create_companies.rb
    Loaded suite ./script/generate
    Started
    Finished in 0.002254 seconds.
    0 tests, 0 assertions, 0 failures, 0 errors
    
    If you see the file, this was generated for you.
    
    class CreateCompanies < ActiveRecord::Migration
    def self.up
    
    end
    
    def self.down
    
    end
    end
    
    and now you can modify it to create a table, the syntax is the folowing:
    
    class CreateCompanies < ActiveRecord::Migration
    def self.up
    create_table :companies do |t|
    t.string :name
    t.text :description
    t.datetime :created_on
    
    t.timestamps
    end
    end
    
    def self.down
    
    drop_table :companies
    
    end
    end

    I think the code is explained implicity.It is similar to an sql creation, but this time you have to add the datatype first and then the column name.

    Possible Column types:
    :string, :text, :integer, :float, :datetime, :timestamp, :time, :date, :binary, :boolean

    Column details:
    :limit: Maximum long characters in the column (for types :string, :text, :binary or :integer)
    :default: Specify the default value of the column.
    :null: Enable or disable the value NULL in a column.

    To get this table created, just you have to run the command: rake db:migrate and the companies table will be created.

    Imagine you want to create another table users, generate it with:

    ./script/generate migration CreateUsers
    
    exists  db/migrate
    create  db/migrate/002_create_users.rb
    Loaded suite ./script/generate
    Started
    Finished in 0.002254 seconds.
    0 tests, 0 assertions, 0 failures, 0 errors
    
    Modify the file 002_create_users.rb
    
    class CreateUsers < ActiveRecord::Migration
    def self.up
    create_table :users do |t|
    t.string   :login
    t.string   :email
    t.string      :password,   :limit => 20
    t.datetime :created_at
    t.datetime :updated_at
    t.integer  :status
    t.datetime :last_login
    
    t.timestamps
    end
    end
    
    def self.down
    drop_table :users
    end
    end

    and if you run again rake db:migrate table users will be created in your database.

    You are probably wondering how rails knows next migration should be named 002 and when running rake db:migrate which migration has to be executed.

    Whenever doing these operations rails checks in your database a table called schema_info, which has the latest version of your database schema. So, that way rails knows which migration number to apply or generate.

    Try to run again rake db:migrate and you will receive an error message

    Mysql::Error: Table ‘users’ already exists:

    telling you that table users already exists. This is because this migration was already executed and in your schema_info table the version is 2(the same as the number of your migration).

    Now you can realize what the pattern is.

    Also you can execute an specific migration by running
    rake db:migrate version= version_number

    version_number is the version you want to be executed.

    When developing your rails application you will see that migrations are very useful because you are database-independent, can change to one version or another and undo changes easily.

    I hope this brief explanation could be helpful to someone.





    Algoritmos de ordenación – Haskell

    11 12 2007

    Los algoritmos de ordenación, así como los de búsqueda, también son muy estudiados en el área de ciencia de la computación. Básicamente, el trabajo de un algoritmo de ordenación es establecer una relación de orden en una secuencia de elementos. La necesidad de ordenar archivos, datos, etc es una tarea que realizamos muy a menudo en nuestras vidas, por ello la importancia de investigar la eficiencia y complejidad de estos algoritmos.

    A continuación vamos a ver algunos algoritmos de ordenación y su implementación en Haskell:

    Insert Sort: Se trata de un algoritmo en el que va haciendo comparaciones del primer elemento del vector con el resto y se inserta ese elemento cuando encuentre su ubicación adecuada Es prácticamente un intercambio de elementos hasta que el vector se encuentre ordenado. ESte algoritmo tiene una complejidad en el mejor de los casos de Ω (n) y en el peor de los casos de O(n²). Veamos su implementación y explicación en haskell.

    Primero creamos una función que me inserte un elemento dado en una lista en su correcta ubicación, esta función compara el elemento con la cabeza de la lista y si es menor lo inserta delante, caso contrario sigue recorriendo la lista recursivamente. Así:

    insert::Ord a => a -> [a] ->[a]
    insert e [] = [e]
    insert e (x:xs)
    | e<=x = e:x:xs
    | otherwise = x: insert e xs

    Main> insert 5 [1,9,4,7,8,43,34,65,89]
    [1,5,9,4,7,8,43,34,65,89]

    Como ya tenemos la función que hace ese trabajo, entonces la llamaremos desde otra función para que inserte la cabeza de la lista, en la cola de la misma. Entonces quedaría así:

    insertSort::Ord a =>[a] ->[a]
    insertSort [] = []
    insertSort (x:xs) = insert x (insertSort xs)

    Main> insertSort [45,23,78,96,22,7,6,14]
    [6,7,14,22,23,45,78,96]

    El algoritmo insertSort no es tan eficiente comparado a otros, pero se recomienda usarlo con vectores pequeños y cuando es necesario una solución rápida, dada su facilidad de programación.

    Quick Sort: Algoritmo que utiliza la técnica divide y vencerás ,es uno de los algoritmos más conocidos y eficientes por su rapidez. Consiste en tomar un elemento pibote y a partir de él generar 2 sublistas una de los menores y una de los mayores a él. Y con cada una de estas sublistas hacer lo mismo, es decir es un algoritmo recursivo. Tiene una complejidad en el mejor de los casos de Ω (n log n) y en el peor de los casos de O(n²). Veamo como es en haskell:

    quickSort::Ord a=>[a]->[a]
    quickSort [] = []
    quickSort (x:xs) = quickSort(menores) ++ [x] ++ quickSort(mayores)
    where
    menores = [y | y <-xs, y < x]
    mayores = [z | z <-xs, z >= x]

    Main> quickSort ['p','d','g','r','h','s','x','w','l','i','t','y','a','z','e']
    “adeghilprstwxyz”

    En este caso tomaríamos a p(cabeza de la lista como nuestro pibote y crearíamos 2 sublistas una de los menores de ‘p’ y otra de sus mayores e iríamos llamando a la función recursivamente para que hagan la misma operación con estas sublistas. Cuando se ha dividido hasta más no poder, entonces se empieza a concatenar los elementos menores con su pibote y con los mayores.

    Merge Sort: Este también es un algoritmo que usa la técnica dividde y vencerás.Consiste en dividir el vector dado en 2 vectores, ordenar estos vectores aparte y luego mezclar el resultado de las 2 ordenaciones obteniendo así un nuevo vector ordenado.

    Tiene una complejidad en el mejor de los casos de Ω (n) y en el peor de los casos de O(n log n). Veamo como es en haskell: Lo primeros que haremos es crear una función que concatener 2 listas ordenadas y el resultado también sea ordenado, llamaremos a esta función Merge, Veamos:

    merge::Ord a =>[a] ->[a] ->[a]
    merge [] l2 = l2
    merge l1 [] = l1
    merge (x:xs) (y:ys)
    | x<=y = x : merge xs (y:ys)
    | True = y : merge (x:xs) ys

    Main> merge [1,3,5,7,9] [2,4,6,8]
    [1,2,3,4,5,6,7,8,9]

    La función Merge lo que hace es ir comparando la cabeza de las 2 lista. En caso que la cabeza de la x (primera lista) sea menor a la cabeza de y (la segunda lista), entonces concatenamos x con la llamada recurisva a su cola y la lista y. caso contrario concatenamos y con la llamada recursiva a la lista x con la cola de y.

    Ahora llamaremos a la función Merge en nuestro algoritmo mergeSort, veamos:

    mergeSort::Ord a =>[a] ->[a]
    mergeSort [] = []
    mergeSort l  = merge (mergeSort izq) (mergeSort der)
    where
    mitad = (div (length l) 2)
    izq = take mitad l
    der = drop mitad l

    Obtenemos cual es la mitad de la lista para obtener 2 divisiones con igual número de elementos, para ello simplemente dividimos la longitud de la lista (length) en 2. Las funciones take y drop se utilizan para tomar y quitar elementos de una lista, las veremos más adelante. Pero aquí el objetivo es tener 2 listas y después con la función Merge ordenarlas.

    Main> mergeSort [1,3,5,7,9,2,4,6,8]
    [1,2,3,4,5,6,7,8,9]

    Bueno más adelante seguiré tratando algunos temas que me fascinan de Haskell y más algoritmos.





    Algoritmos de búsqueda – Haskell

    10 12 2007

    En la ciencias de la Computación, y la clasificación de algoritmos, los algoritmos de búsqueda son imprescindibles y son una de las operaciones más importantes en el procesamiento de la información ya que continuamente nos vemos en la necesidad de buscar información.

    Un algoritmo de búsqueda consiste en buscar en un elemento en una estructura de datos (listas , arrays, matrices, etc) y devolvernos un valor booleano en el caso de la existencia o no del elemento. Los algoritmos de búsqueda tienen 2 objetivos primordiales:

    • Determinar si el elemento buscado se encuentra o no en la estructura.
    • En el caso que se encuentre el elemento, devolver la posición en la que se encuentra.

    Ahora veremos la implementación en Haskell de 2 algoritmos de búsqueda muy conocidos:

    Búsqueda Secuencial.- Este es un tipo de algoritmo en el cual se explora consecutivamente todos los elementos de un conjunto. La comprobacion se realiza desde el primer elemento hasta el último, en el caso que se encuentre el elemento buscado el algoritmo se detiene. Este algoritmo tiene una complejidad en el mejor de los casos de Ω(1) y en el peor de los casos O(n).

    En código haskell:

    busSec::Ord a=>[a]->a->Bool
    busSec [] _ = False
    busSec (x:xs) ele
    | x==ele = True
    | True = busSec xs ele

    Main> busSec [48,59,63,12,45,8,5,36,25,120] 41
    False
    Main> busSec [48,59,63,12,45,8,5,36,25,120] 25
    True

    Como lo dije anteriormente con Ord indicamos que ‘a’ puede ser un dato de cualquier tipo. El caso base es si la lista está vacía o no indicamos el elemento a buscar, en estos caso el algoritmo retorna False.

    Sino se da el caso base, entonces separamos cabeza y cola y se empieza a preguntar, si el elemento es igual a la cabeza devuelve True, caso contrario que siga buscando recursivamente en la cola hasta encontrarlo. Sencillo no?.

    Búsqueda Binaria: Este es un tipo de algoritmo en el cual se parte de la premisa de que una determinada lista de elementos se encuentra ordenada y además de ello se conoce su número de elementos. Este algoritmo tiene una complejidad en el mejor de los casos de Ω(1) y en el peor de los casos O(log n).

    En código haskell:

    busBin::Ord a=>[a]->a->Int->Int->Bool
    busBin [] _ _ _ = error (“please,check your code”)
    busBin vector ele ini fin
    | ini > fin = False
    | ele == vector!!medio = True
    | ele > vector!!medio = busBin vector ele (medio+1) fin
    | True = busBin vector ele ini (medio-1)
    where
    medio = (div(ini+fin) 2)

    Main> busBin ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'] ‘f’ 1 25
    True
    Main> busBin ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'] ‘ñ’ 1 25
    False

    Explicaré como funciona:

    1. Es necesario pasarle 4 parámetros a la función busBin: El vector (conjunto de elementos), el elemento a buscar, el inicio y fin del vector, en este caso del abecedario, 1 y 25 respectivamente.
    2. A nosotros nos interesa la posición del elemento que se encuentra en el medio del vector. Para a partir de ello empezar a buscar a su izquierda o a su derecha o detenerse en el caso que el elemento a buscar sea igual al del medio.
    3. El elemento del medio lo calculamos con :(inicio + fin) /2.
    4. El algoritmo pregunta si el elemento a buscar es el del medio, si es asi, simplemente retorna true y se detiene.
    5. Si no, preguntamos si el elemento a buscar es mayor al elemento del medio, entonces volvemos a llamar a la función recursivamente y tendriamos como parámetros: vector ele (medio+1) fin, es decir se buscaría en el vector desde un elemento después del medio hacia la derecha.
    6. En caso contrario, la función tendria como parámetros: vector ele ini (medio-1) , es decir se buscaría desde el inicio hasta un elemento antes del medio.

    Esta ha sido una pequeña implementación de 2 algoritmos de búsqueda en haskell.





    Funciones útiles en listas – Haskell

    8 12 2007
    • !!: Retorna el elemento ubicado en la posición n, empezando desde cero.
    • head: Retorna el primer elemento de la lista.
    • last: Retorna el último elemento de la lista.
    • tail: Retorna todos los elementos menos el primero.
    • init: Retorna todos los elementos menos el último.
    • length: Retorna el número de elementos de la lista.
    • take: Retorna los primeros n elementos de la lista.
    • drop: Retorna los elementos de la lista, excepto los n primeros.
    • takeWhile : Más potente que take pues puede retornar ciertos tipos de datos indicados.
    • dropWhile: Más potente que drop pues puede retornar ciertos tipos de datos indicados.
    • reverse: Invierte una lista.
    • concat: Toma ciertos elementos o listas y las retorna en una sola lista.
    • words: Retorna una lista de strings de acuerdo a los espacios en blanco de un string.
    • unwords: Retorna un string de una lista de strings.
    • elem: Retorna si un elemento esta o no en la lista
    • notElem: Lo opuesto a elem.

    Ejemplos:

    Main> ["maritza","celeste","nadia","maria","julia"]!!1
    “celeste”

    Main> head [11,1,1985,22,8,2007]
    11

    Main> last [11,1,1985,22,8,2007]
    2007

    Main> tail [11,1,1985,22,8,2007]
    [1,1985,22,8,2007]

    Main> init [11,1,1985,22,8,2007]
    [11,1,1985,22,8]

    Main> take 2 [11,1,1985,22,8,2007]
    [11,1]

    Main> length [11,1,1985,22,8,2007]
    6

    Main> drop 2 [11,1,1985,22,8,2007]
    [1985,22,8,2007]

    Main> takeWhile (<=15) [1..30]
    [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]

    Main> dropWhile (<=15) [1..30]
    [16,17,18,19,20,21,22,23,24,25,26,27,28,29,30]

    Main> reverse [11,1,1985,22,8,2007]
    [2007,8,22,1985,1,11]

    Main> concat ["open","source","solutions"]
    “opensourcesolutions”

    Main> words ” I like to use Debian GNU/Linux”
    ["I","like","to","use","Debian","GNU/Linux"]

    Main> unwords ["I","like","to","use","Debian","GNU/Linux"]
    “I like to use Debian GNU/Linux”

    Main> elem ‘t’ ['a','f','r','h','t']
    True

    Main> notElem ‘t’ ['a','f','r','h','t']
    False





    Listas en Haskell

    6 12 2007

    Una lista es una estructura de datos que representa un conjunto de datos de un mismo tipo, es muy usada e importante en el lenguaje haskell.

    Su declaración es muy simple, ejem:

    [Int]: Representa una lista de enteros [4,5,9,25,60 ]

    [Char]: Representa una lista de chars ['l','i','n','u','x']

    [Bool]: Representa una lista de valores booleanos [True, False, True]

    [String]: Representa una lista de strings ["buenas","noches"]

    [Float]: Representa una lista de flotantes [2.5,5.8,4.3,7.1 ]

    También podríamos definir una lista de listas así: [[Int]], pero eso lo veremos más adelant con el tema de matrices. Una lista vacía se representa [] y algo importante para el manejo de listas es separar el primer elemento (cabeza), del resto(cola) de la sgt manera(x:xs) . X representa la cabeza y xs la cola.

    Como Haskell hace mucho uso de la recursividad entonces, debemos tener en cuenta cuando deterne la operación recursiva y para ello necesitamos un caso base.

    Vamos a ver algunos ejemplos:

    1.- Sumar los elementos de una lista.En este caso, el caso base es que la lista se encuentre vacía y devuelve 0, mientras tanto que siga sumando los elementos con la operación recursiva.

    sumar::[Int]->Int
    sumar [ ] = 0
    sumar (x:xs) = x + sumar(xs)

    Main> sumar [5,4,7,8]
    24
    2.- Invertir una lista: El operador Ord me sirve para indicar que representa a cualquier tipo de dato.

    invertir::Ord a=>[a]->[a]
    invertir [ ] = [ ]
    invertir (x:xs) = (invertir xs)++[x]

    Main> invertir [5,4,7,8]
    [8,7,4,5]

    3.- Comparar si 2 listas son iguales:

    igualLista:: Eq a => [a]->[a]->Bool
    igualLista l1 l2 = l1 == l2

    Main> igualLista ["Hola","Mundo"] ["Mundo","Hola"]
    False

    4.- Comprobar si una lista esta ordenada: En este caso ‘y’ representa al 2do elemento de la lista.

    lista_ordenada::Ord a=>[a]->Bool
    lista_ordenada [] = True
    lista_ordenada [_] = True
    lista_ordenada (x:y:xs) = (x<=y) && lista_ordenada (y:xs)

    Main> lista_ordenada ['a','b','c','d']
    True

    5.- Mostrar el elemento que se encuentra en cierta ubicacion:Como en un array el 1er elemento esta en la ubicacion 0.

    mostrar_ubicacion::Ord a=>[a]->Int->a
    mostrar_ubicacion l n = l!!n

    Main> mostrar_ubicacion [15,25,26,28] 2
    26

    6.- Mayor elemento de una lista:

    mayor::[Int]->Int
    mayor (x:xs)
    | x > mayor(xs) = x
    | otherwise = mayor(xs)

    Main> mayor [78,24,56,93,21,237,46,74,125]
    237

    Listas por comprensión: Son listas que no usan cabeza y cola, sino ciertos argumentos para definir los elementos que pertenecen a ella, de esta manera resolvemos problemas de una manera muy elegante y potente. Ejm: Le decimos a haskell que queremos una lista de elementos x, tales que x este en el rango de 1 a 12.

    Main> [x | x <- [1 .. 12]]
    [1,2,3,4,5,6,7,8,9,10,11,12]

    Apliquemos esto a las operaciones con listas.

    1.- Contar cuantos elementos pares hay en una lista. Estamos diciendo que x pertenece a la lista y ademas debe cumplir la condición de ser par. Como en varios lenguajes el length cuenta los elementos.

    contarpares::[Int]->Int
    contarpares []=0
    contarpares lista= length [x | x <- lista, mod x 2 ==0]

    Main> contarpares [5,4,7,8]
    2

    2.- Devolver los cuadrados de una lista:

    cuadrados::[Int]->[Int]
    cuadrados [ ] = [ ]
    cuadrados l = [x*x| x <- l]

    Main> cuadrados [1..10]
    [1,4,9,16,25,36,49,64,81,100]

    3.- Devolver una lista de  numeros primos de 1 a n: Para ello debemos crear nuestra funcion para saber si un numero es primo o no y despues la aplicamos a la lista por comprensión:

    divisible::Int->Int->Bool
    divisible x y = (mod x y) ==0

    divisibles::Int->[Int]
    divisibles x = [y | y <-[1..x],divisible x y]

    esPrimo::Int->Bool
    esPrimo n = length (divisibles n) <=2

    primos::Int->[Int]
    primos n = [x | x <-[1..n],esPrimo x]

    Main> primos 100
    [1,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97]

    Continuaré en el sgt post con problemas mas avanzados y otros temas en haskell.





    Haskell y la Programación Funcional

    23 11 2007

    Despues de tanto tiempo sin postear debido al trabajo y estudios. Mi blog ya estaba llenándose de polvo. Pues hoy quiero hablar brevemente de lo que estoy estudiando en la Universidad: Haskell y Programación Funcional (P.F)

    La P.F es un paradigma de programación que se basa en el uso de funciones matemáticas para resolver problemas. Algunos lenguajes de programación funcional son Lisp (El más popular aun usado por muchos programadores) , Scheme, AP, Haskell y Miranda.

    Entre las principales características de P.F tenemos:

    • Los bucles se modelan a través de la recursividad ya que no hay manera de incrementar o disminuir el valor de una variable,
    • Definición y evaluación de funciones y variables.
    • Y una cosa realmente interesante de la P.F es que las funciones son consideradas un tipo de dato primitivo. Qué significa esto?.

    Primeramente, definimos a un dato primitivo como aquel que puede ser el valor de una variable, argumento de una función o valor que devuelve una función. En otros paradigmas de programación los números, caracteres y cadenas son datos primitivos, sin embargo sus funciones y procedimientos no pueden ser considerados como tal. Entonces podríamos decir que en la PF.podríamos hacer esto:

    • Una función podría tomar como argumentos otras funciones
    • Devolver un procedimiento como resultado de una llamada a otro procedimiento.
    • Tener estructuras de datos que contengan procedimientos como elementos
    • Una variable puede tener como valor un procedimien

    No es esto una gran ventaja?. Solo sería cuestión que TÚ lo pruebes y juzgues por tu cuenta. En muchas univesidades e institutos del país aun se sigue enseñando paradigmas como el imperativo y orientación a objetos, pero es interesante probar nuevas cosas y apostar por el cambio.

    EL L.P que estamos aprendiendo este semestre en la U es Haskell, un lenguaje funcional puro de propósito general que no goza de mucha popularidad, pero ya tiene un prestigio bien ganado, por algunas características que lo hacen elegante, eficiente y potente para resolver problemas. Más adelante entraremos en detalles.
    Quisiera contar como experiencia personal que he programado en PHP desde hace casi 2 años a Ruby lo he investigado tan solo un par de meses y ahora puedo decir que con Haskell he pensado, razonado y hecho funcionar más el cerebro que con los anteriores. El nivel de abstracción, desarrollo de la lógica se producen a grandes niveles en Haskell.

    Ahora vamos al grano, queremos jugar con Haskell. Tenemos varias implementaciones del lenguaje, entre las principales tenemos el intérprete HUGS y el compilador GHC. He leído en algunas listas de correo y me han recomendado usar GHC, pero bueno empezemos con algo más liviano y menos sofisticado como es Hugs para empezar a probar.

    No perdamos tiempo compilando, y como Debian todo lo tiene y lo que no tiene se lo inventa solo bastaría un: sudo apt-get install hugs , Tecleamos en nuesto terminal hugs y tendremos una salida como esta:
    || || || || || || ||__ Hugs 98: Based on the Haskell 98 standard
    ||___|| ||__|| ||__|| __|| Copyright (c) 1994-2005
    ||—|| ___|| World Wide Web: http://haskell.org/hugs
    || || Report bugs to: hugs-bugs@haskell.org
    || || Version: 20050308 _________________________________________

    Haskell 98 mode: Restart with command line option -98 to enable extensions

    Type : ? for help
    Hugs.Base>

    Jejeje, sigamos , puedes editar tus programas en consola si deseas, nano, pico, vi,etc, bueno yo prefiero usar el gedit que te aclara muy bien el código. Crearemos nuestro archivo examples.hs y empezemos con un típico ejemplo, una función que me calcule el factorial de un número, tan simple como esto

    fact::Int->Int
    fact n
    | n ==0 = 1
    | otherwise = n * fact(n-1)

    Recibimos un entero y que nos devuelva otro entero y usamos la recursión para hacer el cálculo. Para probarlo en nuestra consola:

    ronny@mentelibre:~/haskell$ hugs examples.hs

    Main> fact 5

    120

    Ohhhhhh que maravilla, fue solo para probar. Ahora queremos una funcion que me halle a^b editamos nuestro examples.hs

    potencia::Int->Int->Int
    potencia a b
    | b ==0 = 1
    | a ==1 = 1
    | otherwise = a * potencia a (b-1)

    Main> :r //para actualizar nuestro archivo y luego

    Main> potencia 5 4

    625

    Ahora vamos a sumar todos los dígitos de determinado número:

    suma::Int->Int
    suma n

    | n < 10 = n
    | otherwise = suma(div n 10) + (mod n 10)

    Actualizamos con :r y luego

    Main> suma 123456789

    45

    Fueron ejemplos sencilllos de introducción, en los proximos posts emplearemos listas, tuplas, matrices, algoritmos de ordenación, algoritmos de búsqueda y otras cosillas que con Haskell son muy interesantes.