Un Algebra de Generadores

El módulo Test::LectroTest::Generator provee generadores para los tipos de datos mas comunes. Provee además un algebra de combinadores de generadores que permite la construcción de generadores complejos.

$ perl -wde 0
  DB<1> use Test::LectroTest::Generator qw(:common Gen)
  DB<2> p 1<<31
2147483648
  DB<3> $tg = Int(range=>[0, 2_147_483_647], sized=>0)
  DB<4> x $tg
0  Test::LectroTest::Generator=HASH(0x84faa78)
   'generator' => CODE(0x84fa9a0)
      -> &Test::LectroTest::Generator::\
      __ANON__[/usr/local/share/perl/5.8.7/Test/LectroTest/Generator.pm:212]\
      in /usr/local/share/perl/5.8.7/Test/LectroTest/Generator.pm:210-212
Una expresión como Int(range=>[0, 2_147_483_647], sized=>0) construye un generador aleatorio de enteros.

Si el argumento sized fuera cierto (que es el valor por defecto) los enteros generados lo serían de acuerdo con una cierta 'política de tamaños'. El generador es un objeto que contiene un método generate . Aqui el argumento sized puesto a falso indica que los valores generados sólo están restringidos por el rango especificado.

  DB<3> x $tg->generate
0  1760077938
  DB<4> x scalar localtime $tg->generate
0  'Wed Jan 14 00:07:07 1998'

La subrutina Gen permite la construcción de nuevos generadores:

  DB<7> $ctg = Gen { scalar localtime $tg->generate( @_ ) }
  DB<8> print $ctg->generate()."\n" for 1..3
Thu Aug 15 15:34:05 2019
Sat Sep  7 04:11:15 1974
Sun Jun 19 14:30:14 1977
de esta forma hemos construido un generador aleatorio de fechas.

Existen múltiples generadores básicos y un conjunto de combinadores de generadores. Sigue un ejemplo que muestra la influencia del parámetro sizing guidance:

  DB<1> use Test::LectroTest::Generator qw(:common :combinators)
  DB<2> $int_gen = Int
  DB<3> print $int_gen->generate($_)." " for 1..100 # El arg es sizing guidance
1 1 0 0 -2 3 -3 8 -6 6 6 6 -9 -10 2 1 -9 -11 4 12 19 -11 16 -1 -22 12 -13 -20 -12 9 \
-21 -9 21 -27 5 -19 -20 28 -18 -31 -14 2 -40 -10 33 32 1 33 6 21 3 5 45 31 -28 20 -17 \
-9 58 48 -58 2 -40 27 6 -20 -41 30 59 40 -49 -60 -38 44 -44 -63 12 -76 45 41 65 \
63 -20 59 -5 62 0 65 63 34 71 32 59 -61 -7 -14 30 -71 -13 -58

Veamos algunos constructores de generadores básicos:

  DB<4> $flt_gen = Float( range=>[0,1] )
  DB<5> x  $flt_gen->generate
0  0.793049252432869
  DB<6> x  $flt_gen->generate
0  0.543474544861482
  DB<7> $bln_gen = Bool
  DB<8> x $bln_gen->generate
0  0
  DB<9> x $bln_gen->generate
0  1
  DB<10> x $bln_gen->generate
0  1
  DB<11> $chr_gen = Char( charset=>"a-z" )
  DB<12> x $chr_gen->generate
0  's'
  DB<13> x $chr_gen->generate
0  'u'
  DB<14> x $chr_gen->generate
0  'i'
  DB<15> $elm_gen = Elements("e1", "e2", "e3", "e4")
  DB<16> x $elm_gen->generate
0  'e2'
  DB<17> x $elm_gen->generate
0  'e4'
  DB<18> x $elm_gen->generate
0  'e2'

El combinador Frequency permite construir un generador a partir de una lista de generadores que produce los elementos con probabilidades proporcionales a las frecuencias asignadas:

  DB<19> $english_dist_vowel_gen = Frequency([8.167,Unit("a")], [12.702,Unit("e")], \
                          [6.996,Unit("i")], [ 7.507,Unit("o")],[2.758,Unit("u")] )
  DB<20> x $english_dist_vowel_gen->generate
0  'i'
  DB<21> x $english_dist_vowel_gen->generate
0  'e'
  DB<22> x $english_dist_vowel_gen->generate
0  'i'
0  'o'

Otro combinador es Paste :

  DB<23> $digit_gen  = Elements( 0..9 )
  DB<24> $ssn_gen = Paste(Paste(($digit_gen)x3),Paste(($digit_gen)x2),Paste(($digit_gen)x4),glue => "-")
  DB<25> x $ssn_gen->generate
0  '168-19-1333'
  DB<26> x $ssn_gen->generate
0  '348-35-8320'

Veamos el generador String :

  DB<27>  $gen = String( length=>[3,5], charset=>"A-Z", size => 100 )
  DB<28> x $gen->generate
0  'KBZNB'
  DB<29> x $gen->generate
0  'AAK'
  DB<30> x $gen->generate
0  'AZL'

Un ejemplo con List :

  DB<31> $ary_gen = List( Int(sized=>0), length => 5 )
  DB<32> x $ary_gen->generate
0  ARRAY(0x8563850)
   0  19089
   1  '-13489'
   2  10390
   3  5382
   4  981
  DB<33> x $ary_gen->generate
0  ARRAY(0x853f030)
   0  17062
   1  18558
   2  29329
   3  31931
   4  2464

También podemos construir generadores de hashes:

  DB<34> $gen = Hash( String( charset=>"A-Z", length=>3 ), Float( range=>[0.0, 100.0] ), length => 4)
  DB<35> x $gen->generate
0  HASH(0x8576a30)
   'FSW' => 0.998256374071719
   'KLR' => 0.0577333231717212
   'PEV' => 0.834037952293134
   'TNK' => 0.0146371360307889

El combinador OneOf aplica uno de varios generadores:

  DB<36> $gen = OneOf( Unit(0), List(Int,length=>3) )
  DB<37> x $gen->generate
0  ARRAY(0x856c454)
   0  '-1'
   1  0
   2  0
  DB<38> x $gen->generate
0  0

El combinador Each permite generar listas formados de aplicar cada uno de los generadores:

  DB<39> $gen = Each( Char(charset => "aeiou"), Int( range=>[0,10], sized => 0 ) )
  DB<40> x $gen->generate
0  ARRAY(0x857b770)
   0  'o'
   1  3
  DB<41> x $gen->generate
0  ARRAY(0x85771bc)
   0  'e'
   1  5
  DB<42> x $gen->generate
0  ARRAY(0x857b080)
   0  'i'
   1  3

El combinador Apply aplica el código dado al resultado producido opr los generadores especificados:

  DB<42> $gen = Apply( sub { $_[0] x $_[1] }, Char( charset=>'a-z'), Unit(4) )
  DB<43> x $gen->generate
0  'xxxx'
  DB<44> x $gen->generate
0  'hhhh'
  DB<45> x $gen->generate
0  'jjjj'

El siguiente ejemplo produce matrices 3x3:

  DB<46> $loloi_gen = List( List( Int(sized=>0), length => 3 ), length => 3)
  DB<47> x $loloi_gen->generate
0  ARRAY(0x857bd1c)
   0  ARRAY(0x8576a90)
      0  9648
      1  2796
      2  9589
   1  ARRAY(0x8576dcc)
      0  '-29523'
      1  '-21714'
      2  31931
   2  ARRAY(0x857658c)
      0  '-9477'
      1  '-2434'
      2  '-3794'

Ejercicio 5.21.1   Construya un generador que produzca problemas de la mochila 0-1 representados mediante una estructura de datos de la forma
[$Capacity, [$w_0, ..., $w_n], [$p_0, ..., $p_n]]

Casiano Rodríguez León
Licencia de Creative Commons
Principios de Programación Imperativa, Funcional y Orientada a Objetos Una Introducción en Perl/Una Introducción a Perl
por Casiano Rodríguez León is licensed under a Creative Commons Reconocimiento 3.0 Unported License.

Permissions beyond the scope of this license may be available at http://campusvirtual.ull.es/ocw/course/view.php?id=43.
2012-06-19