Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby Lisp Interpreter convert arithmetic symbol?

Tags:

ruby

lisp

Essentially I have a evaluate method that takes something like

['+','2','3']

which would evaluate to 5.

It's currently set up like so,

def evaluate(exp)
    f = exp[0]
    if f == '+' then
        return exp[1].to_i + exp[2].to_i
    elsif f == '-' then
        return exp[1].to_i - exp[2].to_i
    elsif f == '*' then
        return exp[1].to_i * exp[2].to_i
    elsif f == '/' then
        return exp[1].to_i / exp[2].to_i
    end
end

This works fine, but there has to be a better way to do this without a giant if. Is there a way for me to convert the symbol and use it? How do typically lisp interpreters handle this?

like image 585
Fianchetto Avatar asked Feb 21 '26 23:02

Fianchetto


2 Answers

Ruby's all about dynamic programming. In fact this makes your code ridiculously easy:

def evaluate(exp)
  exp[1].to_i.send(exp[0], exp[2].to_i)
end

It'd be even easier if those tokens were converted on the way in:

exp.map! do |token|
  case (token)
  when /\A\-?\d+\z/
    token.to_i
  else
    token
  end
end

Then you get this:

def evaluate(exp)
  exp[1].send(exp[0], exp[2])
end

Now this presumes you're only supplying valid operations, that you're not doing anything absurd, but for trivial cases it works quite well.

If you've converted everything and you're looking to make this more extensible:

def evaluate(exp)
  op, *args = exp

  args.reduce(&op.to_sym)
end

Then you can do this on arbitrary lists:

evaluate([ '+', 2, 1, 3, 4 ])
like image 116
tadman Avatar answered Feb 24 '26 11:02

tadman


def evaluate(*args)
  operation, *numbers = args
  numbers.map!(&:to_i)
  numbers.first.public_send(operation, numbers.last)
end

If you expect more, than two numbers to be used:

def evaluate(*args)
  operation, *numbers = args
  numbers.map!(&:to_i).inject(&operation.to_sym)
end

evaluate('+','2','3', 4, 5, 6)
#=> 20

You can add an initial value if you need to make sure to always return a Numeric from the method (make sure to select any non-zero number, if operation is division or multiplication):

def evaluate(*args)
  operation, *numbers = args
  initial_value = %w(/ *).include?(operation) ? 1 : 0
  numbers.map!(&:to_i).inject(initial_value, &operation.to_sym) #<==== note 0
end

evaluate '+'
#=> 0
like image 39
Andrey Deineko Avatar answered Feb 24 '26 12:02

Andrey Deineko



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!