Archivos y carpetas ocultas en Mac OS X

Hasta la fecha, nunca me ha hecho falta ver carpetas ocultas desde le finder, pues suelo gestionar mis ficheros desde la consola de comandos. Hoy me he encontrado con un pequeño problema cuando he querido poder acceder a unos archivos generados por una aplicación bajo un directorio oculto y tras repasar las opciones de visualización del finder no he encontrado nada.

Tras buscar un poco por la red he encontrado un modo que pese a resultar un poco engorroso en el caso de que no queramos hacer permanente la visualización de los mismos, funciona correctamente.

Para ello tenemos que abrir un terminal e introducir

defaults write com.apple.finder AppleShowAllFiles TRUE

Tras ello tendremos que reiniciar el navegador de archivos, para ello pulsamo: ‘cmd+alt+esc’ y en la lista que aparece seleccionamos el finder y pulsamos sobre ‘forzar salida’.

Ahora ya podríamos ver todos los archivos y directorios ocultos del sistema.

El truco funciona igual si en lugar de TRUE se pone un ON.

defaults write com.apple.finder AppleShowAllFiles ON

Del mismo modo podemos recuperar la normalidad de nuestro sistema cambiando TRUE por FALSE u OFF.

defaults write com.apple.finder AppleShowAllFiles FALSE
defaults write com.apple.finder AppleShowAllFiles OFF

Espero que este pequeño howto os sea de utilidad.

Hasta pronto

Optimizando los IF al programar

Cualquier programador sabe que las sentencias condicionales son junto a los bucles unas de las estructuras más empleadas ya que nos permiten ejecutar un aparte del programa u otra dependiendo de diferentes factores. El caso de los IF resulta tan trivial, que pocos son los que se plantean que dependiendo de como se construyen pueden afectar al rendimiento de sus aplicaciones.

En el presente artículo vamos a centrarnos en el caso de Java, aunque es aplicable a la mayoría de los lenguajes de programación existentes.

Para comenzar veamos algunos conceptos muy simples de como se ejecutan las sentencias condicionales más famosas. Me gustaría aclarar que se  da por hecho que el lector conoce los conceptos más básicos de los IFs. Esta prueba podéis hacerla usando un debugger y separando las condiciones en diferentes lineas.

OR

El comportamiento de la condición OR, ejecuta todas las expresiones ha analizar hasta encontrar una que resulte cierta y procesa lo correspondiente.

if (TRUE || 1==2){
   //TODO
}

En este primer caso, nunca se llegaría a analizar la condición ‘1==2’ ya que una vez llegado al ‘TRUE’ se ejecutaría el código.

if (FALSE || 1==1){
   //TODO
}

En este otro caso, se comprobaría que la primera condición no es cierta por lo que se procedería a comprobar la siguiente.

De esta lección podemos aprender que hay que poner por delante aquellas expresiones que tengan una mayor probabilidad de ser ciertas o aquellas que consuman menos ciclos de procesador (calcular el módulo ‘%’ consume más que una igualación ‘==’), a fin de que se ejecuten la menor cantidad de comprobaciones.

AND

Las condiciones unidas por un AND se ejecutan siempre de principio a fin hasta encontrar una que no sea cierta. En el caso de que ninguna de las condiciones resulte FALSE, se ejecuta el código pertinente. En el momento en el que una resulte FALSE, se sale del programa.

En el siguiente bloque de código se muestran unas condiciones, las primeras unidas por AND  y las segundas mediante IFs anidados, su ejecución es la misma.

//Unión mediante AND
if (a ==b && b==c){
  //TODO
}

//Unión mediante anidación
if (a==b){
  if(b==c){
    //TODO
  }
}

Como en el caso de OR, se muestran unos ejemplos a fin de explicar con mayor claridad el funcionamiento del AND.

if (2==2 && 1==1){
   //TODO
}

En este primer ejemplo se comprobaría en primera instancia el ‘2==2’ y al resultar TRUE, se pasaría a analizar el ‘1==1’.

if (2==1 && 1==1){
   //TODO
}

En esta ocasión pese a que la segunda condición sea cierta, el programa nunca llega a comprobarla pues la primera ya ha resultado ser FALSE.

Como probablemente sabrás, para evitar ciertos problemas a la hora de programar en Java, hay que tener muy claro como se ejecutan las condiciones unidas por un AND, pero no voy a hacer referencia a ello pues no es el objetivo de esta entrada. Desde el punto de vista de la optimización, hay que tratar de poner por delante aquellas sentencias que tengan mayor probabilidad de ser FALSE y nuevamente aquellas que menos ciclos consuman de CPU. Si por ejemplo tenemos dos sentencias, una ‘a==b’ y otra que tenga cálculos más complejos como ‘a+15/b == c-18/d’, se ha de optar por poner antes la primera de ellas.

OTROS CONSEJOS

Hay otro consejo que resulta tan obvio que muchos no se dan cuenta, cuando tenemos un bucle en el que hay un IF con varias comprobaciones y una o varias de ellas no cambian, es recomendable hacer esa comparación fuera y almacenarla como variable boolean ya que es más rápido comparar ésta que toda la secuencia.

String a = "hola";
int cont =0;
while ( cont <100){
    if((a.compareToIgnoreCase("Hola")==0 && ....){
           //TODO
    }
    cont++;
}

Podría sustituirse por este otro código en el que el ‘compareToIgnoreCase’ solo se realiza una vez y no una vez en cada bucle.

String a = "hola";
boolean b=a.compareToIgnoreCase("Hola")==0;
int cont =0;
while ( cont <100){
    if((b<strong> </strong>&& ....){
        //TODO
     }
    cont++;
}

EJEMPLO PRÁCTICO

El programa que se muestra a continuación toma una lista de números, un rango y un número y clasifica todos los números de la lista en diferentes ArrayList dependiendo de si es un múltiplo del numero dado dentro del rango, si es múltiplo pero no está comprendido en el rango o si no es múltiplo.

Se trata de un caso muy simple, pero como podrá comprobarse, cuando se ejecuta repetidas veces, pueden notarse ligeras diferencias entre unos casos y otros, lo suficiente como para cuando tengamos sentencias condicionales dentro de bucles nos pensemos un par de veces el orden adecuado antes de escribir el código.

La clase Main

import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;

public class Launcher {
	private final static int ini=30;
	private final static int fin=500000;
	private final static int num=3;

	private final static int numTest=1000;

	public static void main(String[] args) {
		int cont;
		Launcher launcher = new Launcher();
		ArrayList list = new ArrayList();

		for(cont =1; cont<=999999;cont++){
 			list.add(cont);
		}

		//Modelo A
		System.out.println("MODELO A");
		launcher.impHora();

		MultiplosA pruebaA = new MultiplosA();
		for (cont=0;cont<numTest;cont++){
			pruebaA.getMultiplos(list, ini, fin, num);
		}

		launcher.impHora();

		//Modelo B
		System.out.println("MODELO B");
		launcher.impHora();

		MultiplosB pruebaB = new MultiplosB();
		for (cont=0;cont<numTest;cont++){
			pruebaB.getMultiplos(list, ini, fin, num);
		}
		launcher.impHora();

		//Modelo C
		System.out.println("MODELO C");
		launcher.impHora();

		MultiplosC pruebaC = new MultiplosC();
		for (cont=0;cont<numTest;cont++){
			pruebaC.getMultiplos(list, ini, fin, num);
		}
		launcher.impHora();
	}

	private void impHora(){
		Calendar calendario = new GregorianCalendar();
		int hora, minutos, segundos, ms;
		hora =calendario.get(Calendar.HOUR_OF_DAY);
		minutos = calendario.get(Calendar.MINUTE);
		segundos = calendario.get(Calendar.SECOND);
		ms = calendario.get(Calendar.MILLISECOND);
		System.out.println(hora + ":" + minutos + ":" + segundos+":"+ms);
	}
}

Este es un simple programa que genera una ArrayList con los primeros 999999 números. Posteriormente se pasa esta lista a diferentes clases que como veremos realizan la función anteriormente descrita. Junto a la lista de números se establece un margen inferior (ini) y uno superior (fin) junto con el número del que buscamos los múltiplos (num). Al inicio del programa puede observarse una tercera variable numTest la cual indica cuantas veces se ha de ejecutar cada una de las llamadas a la función.

Se ha añadido una función ‘impHora()’, encargada de mostrar la hora actual del equipo antes y después de haber ejecutado cada prueba.

La prueba se realiza varias veces a fin de obtener unos tiempos significativos para compararlos con mayor facilidad.

En los siguientes bloques de código se exponen las tres clases empleadas en el ‘MultiplosA’, ‘MultiplosB’ y ‘MiltiplosC’. Las clases implementan el mismo método ‘getMultiplos(…)’ al cual pasamos una lista de números en la que buscar, un número mínimo y máximo. El programa almacena en tres ArrayList diferentes:

  • multiplosEntre: Son los múltiplos del 3 comprendidos en el rango indicado.
  • multiplosOtros: Son los múltiplos del 3 que no se encuentran dentro del rango.
  • noMultiplos: El resto de los números.
Se aclara que estas variables se incluyen dentro de la función y no como globales a fin de ahorrar memoria al repetir la llamada a la función múltiples veces, evitando así dejar al equipo sin recursos.
La única diferencia entre los códigos es como están construidos los IFs.
import java.util.ArrayList;

public class MultiplosA {

	public  void getMultiplos(ArrayList list, int ini, int fin, int num){
		int cont=0;
		ArrayList multiplosEntre = new ArrayList();
		ArrayList multiplosOtros = new ArrayList();
		ArrayList noMultiplos = new ArrayList();

		while (cont < list.size()){
 			int a = list.get(cont);
 			if (a%3 == 0 && a>=ini && a<=fin){
 				multiplosEntre.add(a);
			}else if(a%3 == 0 &&( afin)){
				multiplosOtros.add(a);
			}else if (a%3!=0){
				noMultiplos.add(a);
			}

			cont++;
		}
	}
}

En este primer código se hace lo más lógico, lo que en un principio haríamos todos, se tienen en cuenta todos los casos así como todas las condiciones a cumplirse en cada caso. En el próximo bloque veremos que podemos mejorar en este caso.

import java.util.ArrayList;
public class MultiplosB {
	public  void getMultiplos(ArrayList list, int ini, int fin, int num){
		int cont=0;
		ArrayList multiplosEntre = new ArrayList();
		ArrayList multiplosOtros = new ArrayList();
		ArrayList noMultiplos = new ArrayList();

		while (cont < list.size()){
 			int a = list.get(cont);
 			if (a>=ini && a<=fin && a%3==0){
 				multiplosEntre.add(a);
			}else if (( afin)&&a%3==0){
				 multiplosOtros.add(a);
			}
			else if (a%3!=0){
				 noMultiplos.add(a);
			}
			cont++;
		}
	}
}

En este caso, el primer y último IF se han dejado como en el anterior, pero en el segundo se han cambiado el orden de los operandos, ya que la comprobación de ‘menor y mayor que’ son más rápidas que el cálculo del resto.

import java.util.ArrayList;
public class MultiplosC {
	public  void getMultiplos(ArrayList list, int ini, int fin, int num){
		int cont=0;
		ArrayList multiplosEntre = new ArrayList();
		ArrayList multiplosOtros = new ArrayList();
		ArrayList noMultiplos = new ArrayList();

                while (cont < list.size()){
 			int a = list.get(cont);
 			if (a>=ini && a<=fin && a%3 == 0){
 				multiplosEntre.add(a);
			}else if (a%3==0){
				multiplosOtros.add(a);
			}else{
				noMultiplos.add(a);
			}

			cont++;
		}
	}
}

Este es el caso más óptimo de los tres presentados, en el ‘ELSE IF’ marcado, se omite comprobar que  está fuera del rango establecido, ya que si es múltiplo del 3 y no está comprendido dentro del rango (comprobado dos lineas más arriba), se encuentra fuera del mismo. Del mismo modo el último ‘ELSE IF’ ha sido sustituido por un ‘ELSE’ ya que el resto de números no será un múltiplo del 3.

RESULTADOS DE LOS TEST

El código superior ha sido ejecutado en mi MacBook Pro de 13” de principios del 2011 con un Intel Core i5 a 2,3GHz y 4 GB de DDR3 a 1333MHz bajo Mac OS X Lion 10.7.1.

Se han realizado dos pruebas idénticas pero con valores para ‘numTest’ diferentes, en la primera de ellas con 1000 y la segunda con 5000. Los resultados obtenidos son los siguientes

TEST 1000
MODELO A -- 2min 23seg
15:48:44:903
15:51:17:934

MODELO B -- 2min 21seg
15:51:17:934
15:53:38:394

MODELO C -- 2min 20seg
15:53:38:394
15:55:58:411

TEST 5000 MODELO A -- 12mn 38seg
19:29:43:139
19:42:11:631

MODELO B -- 11min 54seg
19:42:11:631
19:54:5:535

MODELO C -- 11min 37seg
19:54:5:535
20:5:42:455

Se puede observar con este rudimentario procedimiento como del Modelo A al resto de pruebas, hay una diferencia sustancial, sobretodo cuando se repite el proceso 5000 veces. En programas simples que se ejecuten en máquinas potentes, puede parecer absurdo pensar en ello, pero resulta interesante cuando nuestros programas van dirigidos a terminales como teléfonos móviles con recursos limitados y operaciones de cálculo más complejas, no olvidemos que la operación más costosa en el código de prueba a sido calcular el resto de una división.

En mi pequeña experiencia programando para Android, he conseguido optimizar la ejecución de parte de mi código gracias a darle un par de vueltas a que hay que comprobar y que no y en que orden se han de ejecutar las cosas. Esto ha supuesto que algo que en un Nexus One, se ejecutaba en 10 segundos, se viera reducido a 5. Cuando hablamos de terminales más modestos como es el caso de un HTC Magic, en el que el código antiguo tardaba cerca de 25 segundos, es algo que se agradece.

Un saludo y hasta pronto.