martes, junio 05, 2007

¿Cuánto son dos más dos?

... y ahora pretenderán que los que siempre respondimos "cuatro" nos alegramos de que finalmente no hayan sido cinco.
Cuando yo no trabajo, no cobro. Pretender que en España alguien dimita por un error, aunque sea por tamaño error, es ser muy inocente. Pero al menos que no cobre. Que no cobre ni un duro de salario, que vaya a La Moncloa en el metro de Aguirre y Gallardón, que se olvide de los helicópteros del ejército para ir a ver los toros o a su mujer haciendo los coros, hasta que todos aquellos con los que ha estado impúdicamente coqueteando durante estos meses estén donde siempre debían haber estado: en la cárcel, o entre las seis tablas de madera de un paralelepípedo oblongo.

15 Comments:

Anonymous Anónimo said...

Mejor en la carcel que en el paralelepípedo, digo yo.
Es que me parece un poco exagerado el comentario.

martes, junio 05, 2007 3:22:00 p. m.  
Anonymous sentoki said...

Es que por esa regla de tres, no había un político en el hemiciclo. En este país que algunos llaman España solo dimite el que lo dimiten. Y podemos gloriarnos de los políticos más infectos que pueblan la tierra, desde Godoy hasta nuestros días. Además somos tan cainitas que para todos puede haber hostias a manta.

martes, junio 05, 2007 4:13:00 p. m.  
Blogger Ian Marteens said...

Mejor en la carcel que en el paralelepípedo, digo yo.

Perdona, puede que tengas razón, pero la rabia me puede ahora mismo. Sobre todo, siento rabia porque alguien me ha dicho esta mañana que "bueno, ya podéis alegraros"... y eso no es que sea exagerado: es que es bastante miserable. Uno no se alegra porque al final quede demostrado que dos más dos son cuatro. Pero se sabía hasta donde iban a llegar las cosas. Se veía venir.

Es que por esa regla de tres, no había un político en el hemiciclo

Oye... no es mala idea. Quizás la desgracia que tenemos se deba a la existencia de "políticos profesionales".

martes, junio 05, 2007 7:01:00 p. m.  
Blogger Alfredo Novoa said...

Pretender que en España alguien dimita por un error, aunque sea por tamaño error, es ser muy inocente. Pero al menos que no cobre.

Esto es ser aun más inocente. Los que son "dimitidos" casi siempre siguen cobrando, y mucho.

miércoles, junio 06, 2007 11:58:00 p. m.  
Blogger Ian Marteens said...

:) Pagaría por no verle el careto a algunos...

Por cierto, mira qué curioso:

iterator InOrder: X;
begin
if Left <> nil then
yield Left.InOrder;
yield Value;
if Right <> nil then
yield Right.InOrder;
end;

Esto "no lo hagáis en casa": un iterador recursivo, ahora mismo, traga una memoria que ni te cuento. Pero a ver si notas algo extraño: el iterador pertenece, por supuesto, a una clase de nodos binarios. Si eso lo combinas con el nuevo operador "member access" de Chrome:

iterator InOrder: X;
begin
yield Left:InOrder;
yield Value;
yield Right:InOrder;
end;

Me explico: en Chrome, cuando se usan dos puntos para el acceso a miembros, se verifica primero si el handle de la expresión no es nulo. Si lo es, se "nulifica" el resto de la expresión, como si fuese SQL. A mí se me había ocurrido la idea... pero usando checked/unchecked. De todos modos, me da un poco de miedo las ambigüedades sintácticas que pueden surgir con los dos puntos.

En cualquier caso, mirando estos inventos me he encontrado con una optimización cojonuda para bucles foreach, que no la hace C# tampoco: los casos en que la variable de control sólo se usa una vez (para repetir un yield, p.ej.). En esos casos, no es necesario sacar el valor de Current, guardarlo en una variable temporal y luego volver a cargarlo (si la grabación y carga están una a continuación de la otra, el peephole optimizer ya las simplificaba).

Ah, y logré echar a andar el prefijo "tailcall" de CIL... pero ralentiza la ejecución, más que acelerarla. Supongo que será porque la "optimización" está diseñada más bien para ahorrar espacio. Lo que he hecho es pasar la detección de recursividad por la cola al peephole optimizer, y así sí que va bien. Incluso hago backpatching para quitar de la pila los parámetros que no se modifican en el paso recursivo: recorro el flujo de instrucciones en sentido inverso.

jueves, junio 07, 2007 10:55:00 a. m.  
Blogger Alfredo Novoa said...

Menudo cambio de tema :-)

Muy chulo lo de los iteradores recursivos. Una cosa que he echado de menos últimamente en C# es lo de poder crear varios iteradores para una clase. Había pensado en usar propiedades.

Algo así:

foreach (Node node in tree.InOrder) ...

Pero al final lo hago todo con "Visitors"

A mi no me gusta mucho la sintaxis de los dos puntos, pero no creo que de problemas.

En general los compiladores .NET de M$ no producen código CIL de muy buena calidad. No parece que se hayan currado mucho el optimizador.

miércoles, junio 13, 2007 10:29:00 a. m.  
Blogger Alfredo Novoa said...

Que chorrada he puesto con lo de los iteradores. En la documentación dice que puedes hacer eso mismo si quieres tener varios iteradores.

Con respecto a lo del tailcall a lo mejor esto te sirve para algo:

http://www.cookcomputing.com/blog/archives/000200.html

A lo mejor lo arreglan un año de estos.

miércoles, junio 13, 2007 12:50:00 p. m.  
Blogger Alfredo Novoa said...

Lo único raro que veo es que te ahorras el foreach cuando haces:

yield Left:InOrder;

Lo cuál está muy bien :-)

miércoles, junio 13, 2007 1:09:00 p. m.  
Blogger Ian Marteens said...

Con respecto a lo del tailcall

Interesante. De todos modos, mira que el artículo es del 2003: todavía no lo han arreglado. Mi implementación escribe sobre los parámetros (usa starg.s, que de otra manera tiene muy poco uso: es un opcode de 2 bytes, además). La versión "recursiva/con acumulador" de Factorial es 4 veces más rápida que la recursiva sin acumulador (para la que no hay tail recursion pues se multiplica después de la llamada):

method Factorial(N: Integer; A: Int64): Int64 =>
  if N <= 1 then A else Factorial(N - 1, N * A);

:) Que no cunda el pánico, se puede usar la sintaxis de siempre. Esto lo estoy probando para no hacer que la sintaxis de las "lambdas" de C# 3 parezcan caídas del cielo. El símbolo => se puede usar para definir cualquier recurso que devuelva algo y que tenga una sola instrucción (una asignación a Result). Puede ser un método o una propiedad de sólo lectura (si es read/write, la notación complica más que simplifica).

Es difícil justificarlo: lo puse experimentalmente, pero me ha gustado. La clave, creo, es que al tener menos bazofia alrededor, la intención se nota enseguida. La expresión if/then/else se incorpora sin problemas (es la ventaja del LALR) y creo que es mejor que el iff de Chrome.

Donde todavía tengo dudas es, precisamente, en la forma de traducir el "where" funcional:

method Normalized: Vector =>
  using L := Length do
    if L = 0 then Self else Self / L;

Aquí es una tontería (aunque permite definir con =>), pero la idea es que using (o la forma final que tome) pueda introducir también una lambda (hablando con propiedad). El problema es que where no es palabra reservada en C# (una chapuza lo de las keywords contextuales), y en un lenguaje imperativo es más natural (por tradición) declarar antes de usar. Además, mi using sirve también para declarar locales con alcance de bloque (deseable por ser más limpio), por lo que parece una extensión natural. De todos modos, no es definitivo.

miércoles, junio 13, 2007 10:49:00 p. m.  
Blogger Ian Marteens said...

yield Left:InOrder;

La idea la saqué de un artículo sobre Spec# (el lenguaje experimental con "contratos" de Microsoft). En realidad es una idea más general, para que programar un iterador recursivo no sea un desastre en velocidad. Lo que hacen es definir una segunda interfaz genérica, parecida a IEnumerable, e implementarla de manera que use una pila. De esa manera, aunque la declaración sea recursiva, la traducción no lo es, con lo que se gana en claridad... sin perder en velocidad. A tanto no he llegado, por supuesto, aunque es interesante, porque está claro que la nueva interfaz no tiene por qué estar predefinida.

En cualquier caso, la ventaja del yield sobrecargado (en el artículo de Microsoft utilizan dos instrucciones yield diferentes) es que da un paso hacia la composición funcional de iteradores. Imagina una función como el map (creo que de Miranda; en Haskell tiene otro nombre), que convierte una función en su equivalente aplicada a secuencias. Ahora mismo en C# habría que meter un bucle explícito. Lo de yield no resuelve el problema, pero es uno de los pequeños cambios que harían falta.

miércoles, junio 13, 2007 10:54:00 p. m.  
Blogger Alfredo Novoa said...

El símbolo => se puede usar para definir cualquier recurso que devuelva algo y que tenga una sola instrucción (una asignación a Result).

Yo uso la palabra clave return para eso mismo. Pero no es una palabra reservada. Intento tener el mínimo número posible de palabras reservadas.

No se por que te parece una chapuza que las palabras clave no tengan por que ser palabras reservadas.

Yo en lugar de using uso with, y dejo que se puedan definir varias cosas en la misma sentencia

with_expression
= WITH name_intro_commalist ':' expression
;
name_intro_commalist
= name_intro
| name_intro_commalist ',' name_intro
;
name_intro
= expression AS introduced_name
;

jueves, junio 14, 2007 12:22:00 a. m.  
Blogger Alfredo Novoa said...

Ya he vuelto a meter la gamba. En este caso return si que es palabra clave y palabra reservada, pero tengo muchos casos en los que no es así.

jueves, junio 14, 2007 12:30:00 a. m.  
Blogger Alfredo Novoa said...

method Normalized: Vector =>
using L := Length do
if L = 0 then Self else Self / L;

En mi lenguaje sería:

operator Normalize(V Vector) return with Length as L: if L = 0 then V else V / L;

jueves, junio 14, 2007 12:45:00 a. m.  
Blogger Ian Marteens said...

No se por que te parece una chapuza que las palabras clave no tengan por que ser palabras reservadas.

Tiene mejor pinta return que un símbolo (y los dos puntos antes de la expresión subordinada en el with son menos molestos). Pero he tirado por la otra vía por una razón principal: no cargar más palabras reservadas en la cuenta del lenguaje, y donde sea posible, reutilizar las ya existentes. Por eso, a la hora de escoger entre using y with, me quedé con using. Cuando tuve que diseñar los métodos de acceso para propiedades, en vez usar read/write me quedé con readonly. También using en lugar de uses. La mayor extravagancia en ese sentido es mi instrucción lock... que es realmente lock/then. Creo que en Chrome usaron locking. ¿Por qué seguir perrunamente a C#? Compatibilidad, por supuesto. Es verdad que tengo un prefijo @ para usar keywords como identificadores (ReadOnly es una propiedad de DataSet, por ejemplo). Y como vez, el problema en Freya se agrava (respecto a C#) porque es case insensitive.

Hay otras razones menores, que por sí misma no bastarían para justificar el estilo, pero que sumadas a la principal, pesan lo suyo. Por ejemplo, tuve que separar el and lógico del bitwise. Dos motivos: si no, las asignaciones compuestas, o serían demasiado engorrosas y bizarras, o tendrían una morfología no explicable desde el propio lenguaje (y necesitaba asignaciones compuestas, aunque no expresiones, porque de lo contrario, no existiría una construcción semántica similar, que permitiese evitar el cálculo por duplicado del "handle" del objetivo de la asignación). Y segundo motivo: así es más sencilla la definición de los operadores.

Los operadores sirven de ejemplo para otro motivo por el que he usado muchos de los símbolos crípticos de C/C#: Chrome tiró por la otra vía, y al igual que con Delphi.NET, te obliga a conocer el "nombre secreto" de cada operador. Por ejemplo, para redefinir la suma, tienes que declarar un "operator Add". Add es fácil, pero ¿tiene todo el mundo claro que Subtract en inglés no lleva "s" tras la "b"? ¿Y se dice "modulus" o "remainder"?

Otro ejemplo: shr/shl. ¿Es siempre evidente que la "r" va por "right" y que "right" es derecha? Por el contrario, creo que el significado de >> es evidente.

De todos modos, me alegra ver que hemos coincidido en varias decisiones de diseño: es muy probable que se trate de "jugadas obligadas".

viernes, junio 15, 2007 1:20:00 a. m.  
Blogger Alfredo Novoa said...

Yo intento no tener demasiadas palabras reservadas, pero no me corto casi nada a la hora de añadir nuevas palabras clave. La parte relacional del lenguaje ya tiene un montón de palabras clave, así que meter unas cuantas más no me preocupa.

Como es lógico, un analizador sintáctico debe de ser un módulo independiente y así se pueden crear todos los lenguajes que quieras siempre que se genere un árbol sintáctico con el mismo formato. Tengo previsto crear otra versión del lenguaje mucho más concisa y con muchos simbolitos unicode.

La distinción entre métodos y operadores es completamente artificial. En realidad son lo mismo. Yo le llamo "operadores" a todo, que es el término matemático.

Bizarro en español significa valiente, generoso o gallardo. Bizarre significa grotesco o extravagante.

sábado, junio 16, 2007 3:37:00 p. m.  

Publicar un comentario

<< Home