viernes, 21 de mayo de 2010

operador +=, cadenas, cadenas constante y el compilador en Java

Como se vio en la entrada anterior, el utilizar incorrectamente un operador, puede ser optimizado por el compilador o en su defecto, una instruccion (en apariencia) se puede convertir en un conjunto de instrucciones, que impacta negarivamente en el desempeño del sistema.

El operador += es un ejemplo de operador que nunca se debe utilizar, debido a como esta implementado, veamos este codigo.






1: package test;
2: 
3: public class StringTest {
4:         public static void main(String[] args) {
5:                 String x = "cadena1";
6:                 x += "cadena2";
7:                 System.out.println(x);
8:         }
9: }
10: 

Si de compilamos el codigo java resultante (el archivo .class) con el comando "javap -c", obtenemos lo siguiente:

Compiled from "StringTest.java"
   public class test.StringTest extends java.lang.Object{
   public test.StringTest();
   Code:
   0: aload_0
   1: invokespecial #8; //Method java/lang/Object."<init>":()V
   4: return

   public static void main(java.lang.String[]);
   Code:
   0: ldc #16; //String cadena1
   2: astore_1
   3: new #18; //class java/lang/StringBuilder
   6: dup
   7: aload_1
   8: invokestatic #20; //Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
   11: invokespecial #26; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
   14: ldc #29; //String cadena2
   16: invokevirtual #31; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   19: invokevirtual #35; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   22: astore_1
   23: getstatic #39; //Field java/lang/System.out:Ljava/io/PrintStream;
   26: aload_1
   27: invokevirtual #45; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   30: return
   }

El codigo equivalente, para generar el mismo resultado, pero utilizando explicitamente la clase StringBuilder y la funcion valueOf, es el siguiente:

1: package test;
2: 
3: public class StringTest2 {
4:         public static void main(String[] args) {
5:                 String x = "cadena1";
6:                 x = new StringBuilder(String.valueOf(x)).append("cadena2").toString();
7:                 System.out.println(x);
8:         }
9: }
10: 

Pero que ocurre cuando se mete la operacion += dentro de un loop? Como en el siguiente codigo:

01: package test;
02: 
03: public class StringTest3 {
04:         public static void main(String[] args) {
05:                 String x = "cadena1";
06:                 for (int i = 0; i < 10; i++) {
07:                         x += "cadena2";
08:                 }
09:         }
10: }
11: 

Al decompilar, se genera el siguiente codigo:

Compiled from "StringTest3.java"
   public class test.StringTest3 extends java.lang.Object{
   public test.StringTest3();
  Code:
   0: aload_0
   1: invokespecial #8; //Method java/lang/Object."<init>":()V
   4: return

public static void main(java.lang.String[]);
  Code:
   0: ldc #16; //String cadena1
   2: astore_1
   3: iconst_0
   4: istore_2
   5: goto 31
   8: new #18; //class java/lang/StringBuilder
   11: dup
   12: aload_1
   13: invokestatic #20; //Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
   16: invokespecial #26; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
   19: ldc #29; //String cadena2
   21: invokevirtual #31; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   24: invokevirtual #35; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   27: astore_1
   28: iinc 2, 1
   31: iload_2
   32: bipush 10
   34: if_icmplt 8
   37: return

}

Es decir, el codigo equivalente es el siguiente:

01: package test;
02: 
03: public class StringTest4 {
04:         public static void main(String[] args) {
05:                 String x = "cadena1";
06:                 for (int i = 0; i < 10; i++) {
07:                         x = new StringBuilder(String.valueOf(x)).append("cadena2").toString();
08:                 }
09:         }
10: }
11: 

Como se puede observar, en cada iteracion, se construye un nuevo objeto StringBuilder, con el resultado de ejecutar la funcion valueOf, que recibe como parametro la variable x, posterior mente, se agrega la cadena que "cadena2" y finalmente, se convierte en una cadena nueva, la cual se asigna a la variable x.

Derivado de esto es destacable notar que este codigo es ineficiente, en la mayoria de las situaciones, particularmente cuando se emplea dentro de loops.

Sin embargo, cuando se emplea en conjunto con el operador +, puede eficiente, pero solo si se conoce exactamente la cantidad de elemenos implicados en la operacion. Como en el siguiente ejemplo:

01: package test;
02: 
03: public class StringTest5 {
04:         public static void main(String[] args) {
05:                 String x = "cadena1";
06:                 String y = "auxiliar";
07:                 x += "cadena2" + y;
08:                 System.out.println(x);
09:         }
10: }
11: 

Al de compilar se obtiene lo siguiente:

Compiled from "StringTest5.java"
   public class test.StringTest5 extends java.lang.Object{
   public test.StringTest5();
   Code:
   0: aload_0
   1: invokespecial #8; //Method java/lang/Object."<init>":()V
   4: return

public static void main(java.lang.String[]);
   Code:
   0: ldc #16; //String cadena1
   2: astore_1
   3: ldc #18; //String auxiliar
   5: astore_2
   6: new #20; //class java/lang/StringBuilder
   9: dup
   10: aload_1
   11: invokestatic #22; //Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
   14: invokespecial #28; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
   17: ldc #31; //String cadena2
   19: invokevirtual #33; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   22: aload_2
   23: invokevirtual #33; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   26: invokevirtual #37; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   29: astore_1
   30: getstatic #41; //Field java/lang/System.out:Ljava/io/PrintStream;
   33: aload_1
   34: invokevirtual #47; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   37: return
}

El codigo equivalente, es el siguiente:

01: package test;
02: 
03: public class StringTest6 {
04:         public static void main(String[] args) {
05:                 String x = "cadena1";
06:                 String y = "auxiliar";
07:                 x = new StringBuilder(String.valueOf(x)).append("cadena2").append(y)
08:                                 .toString();
09:                 System.out.println(x);
10:         }
11: }
12: 

Lo cual es eficiente para en terminos de uso de los objetos creados.

martes, 18 de mayo de 2010

operador +, cadenas, cadenas constante y el compilador en Java

"Mucho ayuda el que no estorba"

El uso de cadenas, en un sistema es casi inevitable, pues las personas en general leen y escriben cadenas, independientemente de que estas representen fechas, numeros, palabras, etc.

La generacion de estas cadenas, repercute negativamente en el desempeño de una aplicacion o sistema, sobre todo, cuando se generan y utilizan de manera incorrecta o excesiva.

Un ejemplo es el operador + en el lenguaje de programacion, que cuando es correctamente empleado, puede no ser una carga excesiva, pero cuando se emplea incorrectamete o se mal interpreta su utilizacion, puede provocar que se trabaje de manera excesiva, consumiendo tiempo de procesamiento y memoria indiscriminadamente.

La mayoria de los lenguajes compilados, como Java, particularmente, los que no soportan sobrecarga de operadores por programacion, convierten las instrucciones que tienen el operador + cuando estan operando con cadenas, en objetos y/o funciones. Sin embargo, el compilador, puede tener la capacidad de manipular el codigo resultante, para minimizar el uso de los recursos (memoria y procesador)

En el siguiente codigo, se hace uso del operador +.




01: package test;
02: 
03: public class StringTest {
04:         public static void main(String[] args) {
05:                 String a = "cadena1";
06:                 String b = "cadena2";
07:                 String c = a + b;
08:                 System.out.println(c);
09:         }
10: }
11: 

Posteriormente ejecutamos los siguientes comandos:

   javac test\StringBuilderTest.java

   javap -c -classpath . test.StringTest

Obteniendo el siguiente codigo:

Compiled from "StringTest.java"
   public class test.StringTest extends java.lang.Object{
   public test.StringTest();
   Code:
   0: aload_0
   1: invokespecial #8; //Method java/lang/Object."<init>":()V
   4: return

   public static void main(java.lang.String[]);
   Code:
   0: ldc #16; //String cadena1
   2: astore_1
   3: ldc #18; //String cadena2
   5: astore_2
   6: new #20; //class java/lang/StringBuilder
   9: dup
   10: aload_1
   11: invokestatic #22; //Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
   14: invokespecial #28; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
   17: aload_2
   18: invokevirtual #31; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   21: invokevirtual #35; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   24: astore_3
   25: getstatic #39; //Field java/lang/System.out:Ljava/io/PrintStream;
   28: aload_3
   29: invokevirtual #45; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   32: return
   }

Como se puede observar, el compilador, tradujo la sentencia con el operador + en el uso de la funcion valueOf, la creacion de un objeto StringBuilder, el uso de la funcion append y finalmente toString, es decir algo como esto:



01: package test;
02: 
03: public class StringTest2 {
04:         public static void main(String[] args) {
05:                 String a = "cadena1";
06:                 String b = "cadena2";
07:                 String c = new StringBuilder(String.valueOf(a)).append(b).toString();
08:                 System.out.println(c);
09:         }
10: }
11: 

Nosotros conocemos que se estan empleando constantes, por lo que podemos substituir la variable por el valor.


1: package test;
2: 
3: public class StringTest3 {
4:         public static void main(String[] args) {
5:                 String c = "cadena1" + "cadena2";
6:                 System.out.println(c);
7:         }
8: }
9: 

Ejecutamos la compilacion (javac) y de compilacion (javap), obteniendo lo siguiente:

Compiled from "StringTest3.java"
   public class test.StringTest3 extends java.lang.Object{
   public test.StringTest3();
   Code:
   0: aload_0
   1: invokespecial #8; //Method java/lang/Object."<init>":()V
   4: return

   public static void main(java.lang.String[]);
   Code:
   0: ldc #16; //String cadena1cadena2
   2: astore_1
   3: getstatic #18; //Field java/lang/System.out:Ljava/io/PrintStream;
   6: aload_1
   7: invokevirtual #24; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   10: return
   }

Este es el codigo equivalente, el cual genera el mismo codigo compilado, que el codigo anterior.



1: package test;
2: 
3: public class StringTest4 {
4:         public static void main(String[] args) {
5:                 String c = "cadena1cadena2";
6:                 System.out.println(c);
7:         }
8: }
9: 

Como se puede notar, cuando se utilizan constantes, el operador + y el compilador, pueden optimizar el codigo, siempre y cuando la constante este "inline". De ahi, la frase inicial "mucho ayuda el que no estorba", en ocasiones hay que dejar que el compilador haga su trabajo.

viernes, 14 de mayo de 2010

binario a hexadecimal en C

Funcion que genera una cadena hexadecimal en base a una cadena binaria. Esta funcionalidad es util, para tranportar o almacenar informacion binaria y posteriormente recuperarla





01: #include <stdlib.h>
02: #include <string.h>
03: 
04: static const char *base="0123456789abcdef";
05: 
06: static const unsigned int ibase[]={
07:         0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
08:         0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
09:         0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
10:         0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
11:         0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
12:         2, 3, 4, 5, 6, 7, 8, 9, 0, 0,
13:         0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
14:         0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
15:         0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
16:         0, 0, 0, 0, 0, 0, 0, 10, 11, 12,
17:         13, 14, 15};
18: 
19: char *uchar2hex(const unsigned char *in, size_t len)
20: {
21:         char *out;
22:         int i;
23:         int l;
24: 
25:         l=sizeof(char)*len*2+1;
26:         out=malloc(l);
27:         out[l]='\0';
28:         for(i=0;i<len;i++)
29:         {
30:                 int p;
31:                 int t;
32: 
33:                 p=i*2;
34:                 t=in[i];
35:                 out[p]=base[(t>>4)&0x0F];
36:                 out[p+1]=base[t&0x0F];
37:         }
38:         return out;
39: }
40: 
41: unsigned char *hex2uchar(char *hexstr, size_t *len)
42: {
43:         unsigned char *out;
44:         int i;
45:         int l;
46: 
47:         l=strlen(hexstr)/2;
48:         out=malloc(sizeof(unsigned char)*l);
49:         for(i=0;i<l;i++)
50:         {
51:                 int p;
52: 
53:                 p=i*2;
54:                 out[i]=(unsigned char)((ibase[hexstr[p]&0xEF]<<4)|(ibase[hexstr[p+1]&0xEF]&0xFF));
55:         }
56:         *len=l;
57:         return out;
58: }
59: 

Explicacion:

04-04: Caracteres validos en hexadecimal, ordenados convenientemente
06-17: Arreglo que sirve para invertir el proceso, convirtiendo un caracter hexadeximal en binario
19-39: Funcion que codifica la informacion binaria en una cadena hexadecimal, la longitud de la informacion binaria, esta indicada por len.
41-58: Funcion que extrae la informacion binaria de una cadena hexadecimal, la longitud resultante se almacenara en la direccion de len.

Optimizacion:
  • Se construye el arreglo resultante con la logitud exacta necesaria
  • Se emplean arreglos, para minimizar el numero de operaciones
Notas:
  • Los valores regresados por estas funciones, se deben liberar con la funcion free, pues se aloja memoria con malloc 
  • En esta version, solo es posible usar minusculas

miércoles, 12 de mayo de 2010

Extraccion del nombre de una propiedad del getter respectivo en java

Extraccion del nombre de una propiedad del getter respectivo

Esta funcionalidad es requerida, para obtener dinamicamente el nombre de la propiedad asociada al methodo getter, sin embargo, puede complicarse, debido a que el estandard puede aceptar el prefijo is o el prefijo get






01: package test;
02: 
03: public class Test {
04:         public String getProperty(String methodName) {
05:                 assert methodName.startsWith("get") || methodName.startsWith("is");
06:                 int index = methodName.charAt(0) == 'i' ? 2 : 3;// cache
07:                 char c = Character.toLowerCase(methodName.charAt(index));// cache
08:                 int l = methodName.length();
09:                 if ((index == 3 && l > 4) || (index == 2 && l > 3)) {
10:                         return new StringBuilder(l-index).append(c).append(
11:                                         methodName.substring(index + 1)).toString();
12:                 } else {
13:                         return new String(new char[] { c });
14:                 }
15:         }
16: }
17: 

Explicacion:
06-06: Determinar donde inicia el nombre de la propiedad.
07-07: El primer caracter del nombre de la propiedad, lo convertimos a minuscula
09-14: Determinamos si el nombre de la propiedad, esta formado por 1 o mas letras.

Optimizacion:
  • Se realiza cache de variables, como el indice, para evitar calcularlo en cada ocasion
  • Cuando el nombre de la propiedad esta formado por un solo caracter se construye una cadena formada por ese caracter exclusivamente, en caso contrario, se forma la cadena correspondiente.
  • Cuando el nombre de la propiedad esta formado por mas de un caracter, se crea un StringBuilder de longitud exacta.

lunes, 10 de mayo de 2010

como llenar un elemento select de html

En ocasiones al momento de llenar un objeto select, dentro de una forma html. La manera de realizar este llenado, influye mucho en la velocidad de despliegue.




01: function fillSelect(obj_select, arr_data) {
02:         var opts = obj_select.options; //cache, direct access
03:         var l = arr_data.length;//cache
04:         opts.length = l;//resize
05:         for (i = 0; i > l; i++) { //fill
06:                 var opt = opts[i];//cache, direct access
07:                 var d = arr_data[i];//cache
08:                 opt.value = d.value;//value for current option
09:                 opt.text = d.label;//text for current option
10:         }
11: }

Explicacion:
01-01: Declaracion de la funcion fillSelect, la cual recibe el elemento select y el arreglo que contiene los nuevos datos del select.
04-04: Cambio en longitud de los elementos (options) que componen el select.
05-10: Llenado de las opciones a mostrar en el componente select. Sin crear objetos innecesarios.

Optimizacion:
  • Se trata de realizar un acceso directo a las propiedades o atributos de los objetos (lineas 02, 03, 06 y 07), utilizando variables locales en vez de referencias.
  • Se evita la construccion innecesaria de objetos option, dado que al cambiar la longitud del arreglo representado por la propiedad options del elemento select, se crean tantos objetos option, como grande sea este arreglo (linea 04). Derivado de esto, solo se le asignan los valores al iesimo option y no como comun mente se hace, creando un objeto option y asignandolo al iesimo elemento en el arreglo. Esto repercute en un uso minimo de memoria.

sábado, 8 de mayo de 2010

implementando la funcion compareTo de Comparable con Generics en java

En esta ocasion, utilizando el codigo de la entrada anterior utilizando herencia en esta clase, se implementa Comparable, para poder utilizar la funcionalidad de Collections.




01: package test;
02: 
03: public abstract class GenericsBeanComparable<T extends Comparable<T>> extends
04:                 GenericsBean<T> implements Comparable<GenericsBeanComparable<T>> {
05: 
06:         public int compareTo(GenericsBeanComparable o) {
07:                 return this.id.compareTo(o.id);
08:         }
09: 
10: }

Explicacion:
03-04: Se define el tipo que se va a emplear , notese que T es Comparable, dando la posibilidad de utilizar clases como Long, Integer, String, etc.
06-08: Se define como se comparara la unica propiedad que conocemos (id) con el id del otro objeto.

Optimizacion:
  • Como en la clase padre, al implementar esta clase, implicitamente, se puede utilizar la funcionalidad de clases como Collections.sort o Collections.binarySearch. Ademas de implementar la funcionalidad de hashCode y equals, heredadas de la clase padre.

Nota:
  • La funcionalidad por defecto, solo compara la propiedad id, por lo que seria recomendable sobreescribir el metodo, para poder comparar otras propiedades, en caso de ser necesario.
Ejemplo:



01: package test;
02: 
03: public class Person extends GenericsBeanComparable<Integer> {
04: 
05:         private String name;
06:         private Integer age;
07: 
08:         public String getName() {
09:                 return name;
10:         }
11: 
12:         public void setName(String name) {
13:                 this.name = name;
14:         }
15: 
16:         public Integer getAge() {
17:                 return age;
18:         }
19: 
20:         public void setAge(Integer age) {
21:                 this.age = age;
22:         }
23: 
24: }

En este ejemplo se define la clase persona, la cual, tiene definido un id de tipo Integer, junto con las funciones hashCode, equals y CompareTo. definidas anteriormente, pueden crear 2 objetos distintos y determinar si son o no iguales, incluso ingresarlos en una tabla de hash (Hashtable o HashMap). E incluso, si se define una Lista de objetos Person y utilizar las funciones sort y binarySearch de Collections.

    miércoles, 5 de mayo de 2010

    implementando las funciones hashCode y equals con generics en java

    En la entrada anterior, se mostro como implementar la funcion equals y hashCode, utilizando una clase especifica (String), sin embago, es posible implementar la funcionalidad anteriormente descrita, utilizando generics de java, funcionalidad introducida en la version 1.5 (5.0).




    01: package test;
    02: 
    03: public abstract class GenericsBean<T> {
    04: 
    05:         T id;
    06: 
    07:         @Override
    08:         public int hashCode() {
    09:                 return id.hashCode();
    10:         }
    11: 
    12:         @SuppressWarnings("unchecked")
    13:         @Override
    14:         public boolean equals(Object obj) {
    15:                 return this == obj
    16:                                 || (obj instanceof GenericsBean && equals0((GenericsBean<T>) obj));
    17:         }
    18: 
    19:         public boolean equals(GenericsBean other) {
    20:                 return this == other || equals0(other);
    21:         }
    22: 
    23:         protected boolean equals0(GenericsBean other) {
    24:                 return this.id.equals(other.id);
    25:         }
    26: 
    27:         public T getId() {
    28:                 return id;
    29:         }
    30: 
    31:         public void setId(T id) {
    32:                 this.id = id;
    33:         }
    34: 
    35: }
    36: 
    Explicacion:

    03-03: Se define la clase "parametrizada" que se empleara para definir el id del bean. Esta clase del id, puede ser incluso un id compuesto (un bean) y no solo un tipo nativo (Integer, Long, String, etc).
    07-10: Se define el hashCode, utiilzando el hashCode del objeto id. A esta funcion se le agrega la anotacion @SuppressWarnings("unchecked"), para que el compilador no muestre mensajes de alerta debido a la conversion de un tipo "Generico".
    19-21: Definicion de equals, para comparar contra un objeto cualquiera.
    23-25: Definicion de la funcion equals0, la cual utiliza el equals del id.

    Optimizacion:
    1. La utilizacion de este codigo, ayuda a no definir estas funciones en cada ocasion. La mayor desventaja, radica en que no es posible cambiarle el nombre al atributo id.
    Actualizacion:
    • Se suprime el private en el id, para permitir acceso "friend"

    implementando las funciones hashCode y equals en java

    Creando una clase bean, necesita implementar las funciones equals y hashCode, no es necesario recurrir a algoritmos complejos y cripticos. El ejemplo siguiente implementa las funciones equals y hashCode, haciendolas simples, aprovechando funciones existentes.




    01: package test;
    02: 
    03: public class Bean {
    04: 
    05:         private String key;
    06: 
    07:         @Override
    08:         public int hashCode() {
    09:                 return key.hashCode();
    10:         }
    11: 
    12:         protected boolean equals0(Bean bean) {
    13:                 return key.equals(bean.key);
    14:         }
    15: 
    16:         @Override
    17:         public boolean equals(Object obj) {
    18:                 return this == obj || (obj instanceof Bean && equals0((Bean) obj));
    19:         }
    20: 
    21:         public boolean equals(Bean bean) {
    22:                 return this == bean || equals0(bean);
    23:         }
    24: 
    25:         public String getKey() {
    26:                 return key;
    27:         }
    28: 
    29:         public void setKey(String key) {
    30:                 this.key = key;
    31:         }
    32: 
    33: }
    34: 



    En este ejemplo en particular, se utilizan las funciones existentes hashCode de la clase String y equals de la misma clase.

    Explicacion:

    07-10: Se emplea la funcion hashCode de la clase String, para generar el hashCode de la clase Bean.
    12-14: Se implementa una funcion equals0, utilizando la funcion equals de la clase String. Esta clase define la funcionalidad basica de la funcion equals de la clase Object.
    16-19: En la implementacion de la funcion equals, se emplea la funcion equals0 definida previamente y verificando el caso en que se trata del mismo objeto.
    21-23: Esta funcion se define, para verificar la igualdad entre objetos del mismo tipo
    25- 31: Definicion de getters y setters propios de la clase.

    Optimizacion:
    1. Uso de las funciones propias de String. Particularmente al definir la funcion hashCode. Esto mismo se puede implementar utilizando las funciones equivalentes de las clases Integer, Float, Double o cualquier otra que encapsule a un tipo nativo dentro del paquete java.lang
    2. Las funciones equals primero determinan si se trata del mismo objeto, antes de intentar comparar cualquier propiedad o atributo.
    3. Evitar la ejecucion del operador instanceof, en la medida de lo posible, al implementar 2 funciones equals, una generica, que recibe un parametro del tipo Object (linea 17) y una funcion especifica para objetos de la misma clase (linea 21). El mayor problema de estas funciones, radica en su resolucion, pues solo es posible optimizar en tiempo de ejecucion o por medio del uso de Reflection

    domingo, 25 de abril de 2010

    trim en javascript

    Despues de leer algunos documentos como este Faster JavaScript Trim me di a la tarea de generar una implementacion de esta funcion, la cual fuera lo mar eficiente y eficaz posible en la medida de mis posibilidades, evitando la dependencia de un navegador web en particular.

    funcion trim en javascript

    01: // spaces list
    02: var wl = [ ' ', '\n', '\r', '\t', '\f', '\u000B', '\u0085', '\u00A0', '\u1680',
    03:                 '\u180E', '\u2000', '\u2001', '\u2002', '\u2003', '\u2004', '\u2005',
    04:                 '\u2006', '\u2007', '\u2008', '\u2009', '\u200A', '\u200B', '\u2028',
    05:                 '\u2029', '\u202F', '\u205F', '\u3000', '\uFEFF' ];
    06: 
    07: function iltrim(sValue, l, wll) {
    08:         var i;
    09:         for (i = 0; i < l; i++) {
    10:                 var c = sValue[i];// cache
    11:                 var j;
    12:                 for (j = 0; j < wll; j++) { // search in spaces list
    13:                         if (c == wl[j]) {
    14:                                 break;
    15:                         }
    16:                 }
    17:                 if (j == wll) { // not a space?
    18:                         break;// not a space!
    19:                 }
    20:                 // found a space, continue with next
    21:         }
    22:         return i;
    23: }
    24: 
    25: function ltrim(sValue) {
    26:         var i = iltrim(sValue, sValue.length, wl.length);// index
    27:         return i > 0 ? sValue.substr(i) : sValue;// i need substr?
    28: }
    29: 
    30: function irtrim(sValue, l, wll) {
    31:         var i;
    32:         for (i = l - 1; i >= 0; i--) {
    33:                 var c = sValue[i];// cache
    34:                 var j;
    35:                 for (j = 0; j < wll; j++) { // search in spaces list
    36:                         if (c == wl[j]) {
    37:                                 break;
    38:                         }
    39:                 }
    40:                 if (j == wll) { // not a space?
    41:                         i += 1;// not a space!, need this char
    42:                         break;
    43:                 }
    44:                 // found a white; continue with prev
    45:         }
    46:         return i;
    47: }
    48: 
    49: function rtrim(sValue) {
    50:         var l = sValue.length;// cache
    51:         var r = irtrim(sValue, l, wl.length);// index
    52:         return r != l ? sValue.substr(0, r) : sValue;// i need substr?
    53: }
    54: 
    55: function trim(sValue) {
    56:         var l = sValue.length;// string length
    57:         var wll = wl.length;// cache
    58:         var r = irtrim(sValue, l, wll);// r index
    59:         // optimization (start in r not in 0) -- why? --
    60:         var i = iltrim(sValue, r, wll);// l index
    61:         return i > 0 || r != l ? sValue.substr(i, r - i) : sValue; // substr?
    62: }
    

    Explicacion:

    • 02-05: Se definen un arreglo que contiene todos los caracteres "blancos" que pude encontrar, definidos en unicode.
    • 07-23: funcion iltrim esta funcion obtiene el indice del primer caracter no blanco dentro de la cadena (de izquierda a derecha)
    • 25-28: funcion ltrim esta funcion realiza un trim por la izquierda.
    • 30-47: funcion irtrim esta funcion obtiene el indice del ultimo caracter no blanco dentro de la cadena (de izquierda a derecha)
    • 49-53: funcion rtrim esta funcion realiza un trim por la derecha.
    • 55-62: funcion trim esta funcion realiza un trim por la izquierda y por la derecha.
    Optimizacion:
    1. Se evita el uso de funciones, como indexOf (Esta funcion busca cadenas en una cadena. Pero estamos buscando caracteres en una cadena).
    2. Se evita el uso de banderas.
    3. Se evita crear objetos intermedios, creando funciones que buscan indices y posteriormente se decide si se genera una nueva cadena o no, utilizando substr. 
    4. Se crea "cache" de algunos valores, por ejemplo del caracter a comparar, o de las longitudes.
    5. Se evita el uso  de cadenas, cuando es posible utilizar un arreglo (dada la rapidez al acceder a un elemento, sin necesidad de invocar una funcion.

    domingo, 18 de abril de 2010

    Cambio de tema

    Despues de meditarlo, he llegado a la conclusion de la necesidad de hacer un blog de optimizacion, particularmente en idioma español.

    Saludos a todos los que siguen este blog