Ruby Lisp Interpreter转换算术符号?

时间:2017-01-18 03:55:12

标签: ruby lisp

基本上我有一个类似

的评估方法
['+','2','3']

将评估为5。

它目前设置如此,

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

这很好用,但如果没有巨人的话,必须有更好的方法来做到这一点。有没有办法让我转换符号并使用它?典型的lisp解释器如何处理这个?

7 个答案:

答案 0 :(得分:4)

Ruby是关于动态编程的。事实上,这会让你的代码变得非常容易:

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

如果这些令牌在途中被转换,那就更容易了:

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

然后你明白了:

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

现在这假设你只提供有效的操作,你没有做任何荒谬的事情,但对于微不足道的情况,它运作得很好。

如果您已经转换了所有内容,并且您希望使其更具可扩展性:

def evaluate(exp)
  op, *args = exp

  args.reduce(&op.to_sym)
end

然后你可以在任意列表上执行此操作:

evaluate([ '+', 2, 1, 3, 4 ])

答案 1 :(得分:3)

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

如果您希望使用两个以上的数字:

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

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

如果需要确保始终从方法中返回Numeric,则可以添加初始值(如果操作是除法或乘法,请确保选择任何非零数字):

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

答案 2 :(得分:1)

因为你问这通常是怎么做的。

您可以从操作到其实现创建查找表。然后使用第一个元素查找实现并将剩余的元素传递给该函数。

基本上,实现lisp所需的只是

  • evaluate
  • apply
  • 查找表

该内核甚至可能适合30-50行。我不认为您希望我们为您提供完整的实施。这将带来编写自己的口齿不清的所有乐趣。

因此,我将概述基本结构......

$table = {
  '+' => lambda { |a, b| a + b },
  '-' => lambda { |a, b| a - b },
  '*' => lambda { |a, b| a * b },
  '/' => lambda { |a, b| a.fdiv(b) },
}

def evaluate(args, context = $table)
  # A complete implementation of evaluate would make use of apply ...
  head, *tail = args
  context[head][*tail] 
end

def apply
  # ...
end

puts evaluate(['/', 4, 3])
# => 1.3333333333333333

恰好,您的lisp函数名称和相应的Ruby函数名称是相同的。但是并不总是给出,因此使用查找表是更典型的解决方案。我将lisp /映射到Ruby的fdiv以证明这一点。

有趣的是,这些第一个元素被称为符号,实际上Ruby也使用了非常相似的机制,因此也有符号。在内部,Ruby还使用嵌套查找表来组织类和常量,方法和局部变量,等等。

答案 3 :(得分:1)

  

典型的lisp解释器如何处理这个?

Lisp将符号用于全局函数名称。 符号是具有一些关联信息的命名事物。其中一个可以是功能

CL-USER 42 > (symbol-function '+)
#<Function + 4100044D34>

CL-USER 43 > (funcall (symbol-function '+) 1 2 3 4)
10

所以对于简单的表达式评估:

CL-USER 50 > (defun eval-expression (expression)
               (destructuring-bind (function &rest arguments)
                   expression
                 (apply function arguments)))
EVAL-EXPRESSION

CL-USER 51 > (eval-expression '(+ 1 2 3 4))
10

符号本身是在中组织的,您可以将其想象为某种专用表。将地图名称映射到符号:

CL-USER 52 > (find-symbol "+")
+
:INHERITED

如果您想要评估字符串,首先必须读取字符串以将字符串转换为Lisp数据。 阅读器将从当前包中查找符号+

CL-USER 53 > (eval-expression (read-from-string "(+ 1 2 3 4)"))
10

答案 4 :(得分:0)

从lisp角度来看(确切地说是常见的lisp),您可以将evaluate定义为:

(defun evaluate (func &rest args)
  (apply func args))

在第一行中,您定义evaluate函数以获取名为func的第一个参数,并将其余参数(如果有)放入名为args的列表中。

然后将args中存储的参数列表应用于存储在func中的函数。

用法示例:

(evaluate #'print "some text, printed by function evaluated by my own code!")
=> "some text, printed by function evaluated by my own code!"

(evaluate #'+ 2 3)
=> 5

这个看起来很奇怪的#'引用了一个函数名,否则就是#34;语言&#34;会尝试在传递给函数之前对所有参数进行评估。

我也强烈建议&#34;结构和解释计算机程序&#34;,至少视频!在中间有一个关于评估的伟大讲座,基本上是(几乎)5个白板上的任何计算机语言的核心! (嗯......绿色;)

答案 5 :(得分:0)

稍微清洁一点的版本:

def evaluate(operation, *numbers)
  numbers.map(&:to_i).reduce(operation)
end

evaluate('+', '2', '3')      # => 5
evaluate('/', '6', '2', '3') # => 1

答案 6 :(得分:0)

有许多不同的解决方案,你可以选择一个更漂亮的解决方案,有一些来自我:

exp = ['+','2','3']

def evaluate(exp)
  exp.map(&:to_i).inject(exp.shift)
end 

# > evaluate(exp)
# => 5

def evaluate(operation, *nums)
  nums.map(&:to_i).inject(operation)
end

# > evaluate(*exp)
# => 5