Parse::Eyapp provee el método Parse::Eyapp::Node->hnew
el cual facilita la construcción de DAGs. El método hnew
trabaja siguiendo lo que en programación funcional se denomina
hashed consing. En Lisp la función de asignación
de memoria es cons y la forma habitual de compartición
es a través de una tabla hash.
Hash consing es una técnica para compartir datos que son estructuralmente
iguales. De esta forma se ahorra memoria.
La técnica esta relacionada con - puede considerarse una aplicación concreta
de - la técnica conocida con
el nombre de memoization
[4].
Si alguno de los subárboles
(o el árbol) fueron creados anteriormente no serán creados
de nuevo sino que hnew
retorna una referencia
al ya existente. Esto hace mas rápida la comprobación de tipos:
en vez de comprobar que los dos árboles son estructuralmente
equivalentes basta con comprobar que las referencias son iguales.
Asi la comprobación de la equivalencia de tipos pasa a requerir
tiempo constante.
Observe los siguientes comandos:
DB<2> x $a = Parse::Eyapp::Node->hnew('F(X_3(A_3(A_5(INT)), CHAR, A_5(INT)),CHAR)') 0 F=HASH(0x85f6a20) 'children' => ARRAY(0x85e92e4) |- 0 X_3=HASH(0x83f55fc) | 'children' => ARRAY(0x83f5608) | |- 0 A_3=HASH(0x85a0488) | | 'children' => ARRAY(0x859fad4) | | 0 A_5=HASH(0x85e5d3c) | | 'children' => ARRAY(0x83f4120) | | 0 INT=HASH(0x83f5200) | | 'children' => ARRAY(0x852ccb4) | | empty array | |- 1 CHAR=HASH(0x8513564) | | 'children' => ARRAY(0x852cad4) | | empty array | `- 2 A_5=HASH(0x85e5d3c) | -> REUSED_ADDRESS `- 1 CHAR=HASH(0x8513564) -> REUSED_ADDRESS DB<3> x $a->str 0 'F(X_3(A_3(A_5(INT)),CHAR,A_5(INT)),CHAR)'
La segunda aparición de A_5(INT)
aparece etiquetada como reusada (tercer hijo de
X_3
). Lo mismo ocurre con la segunda aparición de CHAR
.
El valor asignado a los atributos no influye en la memoización de hnew
: si los
términos tienen la misma forma se devolverán los mismos árboles:
pl@nereida:~/src/perl/testing$ perl -MParse::Eyapp::Node -wde 0 main::(-e:1): 0 DB<1> @x = Parse::Eyapp::Node->hnew('A(B,C(D))', sub { $_->{n} = $i++ for @_ }) DB<2> @y = Parse::Eyapp::Node->hnew('C(D)', sub { $_->{t} = 10-$j++ for @_ }) DB<3> x @x 0 A=HASH(0x850935c) 'children' => ARRAY(0x8509530) 0 B=HASH(0x85095a8) 'children' => ARRAY(0x8326800) empty array 'n' => 1 1 C=HASH(0x8509410) 'children' => ARRAY(0x85094dc) 0 D=HASH(0x85094b8) 'children' => ARRAY(0x85095f0) empty array 'n' => 3 't' => 9 'n' => 2 't' => 10 'n' => 0 1 B=HASH(0x85095a8) -> REUSED_ADDRESS 2 C=HASH(0x8509410) -> REUSED_ADDRESS 3 D=HASH(0x85094b8) -> REUSED_ADDRESS DB<4> x @y 0 C=HASH(0x8509410) 'children' => ARRAY(0x85094dc) 0 D=HASH(0x85094b8) 'children' => ARRAY(0x85095f0) empty array 'n' => 3 't' => 9 'n' => 2 't' => 10 1 D=HASH(0x85094b8) -> REUSED_ADDRESS
Sigue el código de la función
build_function_scope
:
pl@nereida:~/doc/casiano/PLBOOK/PLBOOK/code/Simple-Types/lib/Simple$ \ sed -ne '/sub bu.*scop/,/^}/p' Types.eyp | cat -n 1 sub build_function_scope { 2 my ($funcDef, $returntype) = @_; 3 4 my $function_name = $funcDef->{function_name}[0]; 5 my @parameters = @{$funcDef->{parameters}}; 6 my $lst = $funcDef->{symboltable}; 7 my $numargs = scalar(@parameters); 8 9 #compute type 10 my $partype = ""; 11 my @types = map { $lst->{$_}{type} } @parameters; 12 $partype .= join ",", @types if @types; 13 14 my $type = "F(X_$numargs($partype),$returntype)"; 15 16 #insert it in the hash of types 17 $type{$type} = Parse::Eyapp::Node->hnew($type); 18 $funcDef->{type} = $type; 19 $funcDef->{t} = $type{$type}; 20 21 #insert it in the global symbol table 22 die "Duplicated declaration of $function_name at line $funcDef->{function_name}[1]\n" 23 if exists($st{$function_name}); 24 $st{$function_name}->{type} = $type; 25 $st{$function_name}->{line} = $funcDef->{function_name}[1]; 26 27 return $funcDef; 28 }Obsérvese que para acelerar los accesos al atributo tipo dotamos a los nodos del atributo
t
que
permite acceder directamente al DAG representando el tipo.