基本上我有一个类似
的评估方法['+','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解释器如何处理这个?
答案 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