Subsecciones

Pistas

A continuación veamos algunos conocimientos que hacen falta para abordar un programa como este:

Lectura de la línea de comandos

[~/rubytesting/postfix]$ irb 
1.9.3-p392 :001 > ARGV = ["4 3 +", "2 *"]
(irb):1: warning: already initialized constant ARGV
 => ["4 3 +", "2 *"] 
1.9.3-p392 :002 > expr = ARGV.join(" ")
 => "4 3 + 2 *" 
1.9.3-p392 :003 > expr
 => "4 3 + 2 *"

Obtener un array con los terminales: El método split

>> x = "2 3 4 + *".split(/\s+/)
=> ["2", "3", "4", "+", "*"]      # un array con las diferentes partes de la cadena

Algoritmo de evaluación de una cadena en postfijo

Una vez que tenemos un array con los terminales de la cadena postfija el algoritmo es sencillo. Véase:
  1. Postfix Evaluation
  2. Una simulacion en YouTube

La idea es ir retirando terminales del array de terminales. Si es un operando se empuja en la pila. Si es un operador se retiran tantos operandos como aridad tiene el operador y se evalúa el operdor sobre los dos operandos:

[~/rubytesting/postfix]$ pry
[1] pry(main)> z = ['4', '3', '+', '2', '*']
=> ["4", "3", "+", "2", "*"]
[2] pry(main)> stack = []
=> []
[3] pry(main)> d = z.shift
=> "4"
[4] pry(main)> stack.push d
=> ["4"]
[5] pry(main)> z
=> ["3", "+", "2", "*"]
[6] pry(main)> stack
=> ["4"]
[7] pry(main)> d = z.shift
=> "3"
[8] pry(main)> stack.push d
=> ["4", "3"]
[9] pry(main)> d = z.shift
=> "+"
[10] pry(main)> op2 = stack.pop
=> "3"
[11] pry(main)> op1 = stack.pop
=> "4"
[12] pry(main)> stack.push eval "#{op1} #{d} #{op2}"
=> [7]
[13] pry(main)>

El método eval

En la sesión irb que sigue puede ver como se usa el método eval:

>> a = 2
=> 2
>> eval "4+#{a}"
=> 6

La Sentencia case

Puede usar una sentencia case para analizar cada uno de los terminales. La sintáxis es:

~/rubytesting$ cat -n case.rb 
     1  #!/usr/bin/env ruby
     2  def check(z)
     3    case z
     4      when 0 .. 2
     5        "baby"
     6      when *[3,4,5,6]  # La estrella '*' expande el array a una lista de argumentos 3,4,5,6
     7        "little child"
     8      when 7,8,9,10,11,12
     9        "child"
    10      when /^1\d$/ 
    11        "youth"
    12      else
    13        "adult"
    14    end
    15  end
    16  
    17  age = 1
    18  puts check(age)
    19  
    20  age = 5
    21  puts check(age)
    22  
    23  age = 12
    24  puts check(age)
    25  
    26  age = "14" # para que la regexp de la línea 10 funcione, deberá ser una cadena
    27  puts check(age)
Este programa produce como salida:
~/rubytesting$ ruby case.rb 
baby
little child
child
youth

Las comparaciones en un case

Dentro de un case las comparaciones se hacen con el operador ===. Así pues:

  case expr0
  when expr1, expr2
    stmt1
  when expr3, expr4
    stmt2
  else
    stmt3
  end
es equivalente a:

  _tmp = expr0
  if expr1 === _tmp || expr2 === _tmp
    stmt1
  elsif expr3 === _tmp || expr4 === _tmp
    stmt2
  else
    stmt3
  end

[13] pry(main)> (1..10) === 5
=> true
[14] pry(main)> (1..10) === 12
=> false
[15] pry(main)> /^ab*$/ === 'abb'
=> true
[16] pry(main)> /^ab*$/ === 'acb'
=> false
[19] pry(main)> String === "hello"
=> true
[20] pry(main)> String === 4
=> false
[22] pry(main)> 1 === 1
=> true
[23] pry(main)> Fixnum === Fixnum
=> false
[28] pry(main)> 4.is_a? Numeric
=> true
[30] pry(main)> 4.is_a? Fixnum
=> true

El operador prefijo *

El operador prefijo * sobre un array expande el array en una lista de argumentos:

irb(main)> a = "hello"
=> "hello"
irb(main)> a[1,3] # emepezando en la posicion 1 tomar 3
=> "ell"
irb(main)> a[*[1,3]]
=> "ell"
irb(main)> def c(x,y) x+y end
=> nil
irb(main)> c(1,2)
=> 3
irb(main)> c(*[1,2])
=> 3

La clase Math

Si quiere extender la práctica para añadir el seno, el coseno, etc. Tendrás que hacer uso del módulo Math:

MacBookdeCasiano:programmingRuby casiano$ irb
>> Math::PI
=> 3.14159265358979
>> x = Math.sin(Math::PI/2)
=> 1.0
>> x = Math.cos(Math::PI)
=> -1.0
Para tener los nombres exportados en nuestro espacio de nombres usaremos include include:
>> PI
NameError: uninitialized constant PI
    from (irb):4
>> include Math
=> Object
>> PI
=> 3.14159265358979
>> x = sin(PI/2)
=> 1.0

Casiano Rodriguez León 2015-06-18