无法在 ruby 中使用 eval 执行 Class(argument)

Not able to do Class(argument) with eval in ruby

我有这样的功能:

def check_if_correct_type(type, value)
    # nil.test!
    # eval(type.classify(value)) rescue return false
    # true
    case type
    when "integer"
        !!Integer(value) rescue return false
    when "float"
        !!Float(value) rescue return false
    else
        return true
    end
    true
end

样本是

 check_if_correct_type("integer", "a")

我试过像这样更改函数:

check_if_correct_type(type, value)
  !!(eval(type.classify(value))) rescue return false
  true
end

这是抛出错误。我该如何解决。我对元编程还很陌生,所以有点迷茫。


更新 1:

"adfadf".kind_of?(String) #=> true
123.kind_of?(String)      #=> false

# The "Fixnum" class is actually used for integers
"adfadf".kind_of?(Fixnum) #=> false
123123.kind_of?(Fixnum)   #=> true 

12.3.kind_of?(Float)      #=> true
"sadf".kind_of?(Float)    #=> false
12.kind_of?(Float)        #=> false

上面的方法对我不起作用 kind_of?函数将找到对象的类型,对我来说答案需要是这样的:

check_if_correct_type("integer", "1221") #=> true
check_if_correct_type("float", "1.24") #=> true
check_if_correct_type("string", "asds12") #=> true
check_if_correct_type("float", "asdasd1.24") #=> false

其中

"1.24".kind_of?(浮动)#=> false

这就是转换对我有用的原因。希望现在问题更清楚了。


更新 2:

如果我使用 public 发送,这就是我得到的结果。

!!public_send("integer".capitalize("1")) ArgumentError:参数数量错误(1 代表 0) 来自(撬):4:在capitalize' [5] pry(main)> !!public_send("integer".classify("1")) ArgumentError: wrong number of arguments (1 for 0) from /home/aravind/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/activesupport-4.2.0/lib/active_support/core_ext/string/inflections.rb:187:in分类'

注意:分类是 Rails 上 Ruby 的一部分,而不是 Ruby。

我看不出在此示例中使用元编程有什么意义。您应该避免在不需要它的地方使用它。一般来说,你的程序的逻辑应该是:

a) 检查输入值的类型。 b) 将类型与作为参数输入的类型进行比较。或者在代码中:

def check_if_correct_type(type, value)
  actual_type = value.class.name
  return actual_type.downcase == type.downcase
end

p check_if_correct_type('string', 'test') #=> true
p check_if_correct_type('integer', 'test2') #=> false

这可以在一行中做得更短,但分两行来更清楚地说明发生了什么。

如果你想检查一个对象的class,正确的方法是这样的:

"adfadf".kind_of?(String) #=> true
123.kind_of?(String)      #=> false

# The "Fixnum" class is actually used for integers
"adfadf".kind_of?(Fixnum) #=> false
123123.kind_of?(Fixnum)   #=> true 

12.3.kind_of?(Float)      #=> true
"sadf".kind_of?(Float)    #=> false
12.kind_of?(Float)        #=> false

没有理由使用 Integer()Float() 方法来检查类型。这些是 类型转换 方法,它们会将其他类型转换为 Float 或 Fixnum。如果您确实想尝试转换可转换为 Float 或 numeric 的类型,这是一种方法,但可能有更好的方法。

一般来说,您不应该计划将引发和拯救异常作为普通程序流程的一部分;一个原因是因为它非常慢。异常应该用于错误和 unusual/exceptional 条件,而不是经常引发异常的常规条件。

绝对不要开始将 eval 带入其中,天哪,你为什么要那样做?

我建议你把你的方法写成这样:

def correct_type?(type, str)
  case type.downcase
  when "integer"
    !!to_integer(str)
  when "float"
    !!to_float(str)
  else
    raise ArgumentError, "type must be 'integer' or 'float'"
  end
end

其中to_integer(value)(to_float(value))是一种方法,returnsvalue.to_i(value.to_f)如果value是字符串表示整数(浮点数),否则 returns nilto_integerto_float 方法很有用,因为它们会告诉你字符串是否可以转换为给定的数值类型,如果可以,则给出数值。

在考虑如何实施 to_integerto_float 之前,我想质疑 correct_type? 的必要性。而不是:

str = "33"
if correct_type?("integer", str)
  n = str.to_i
  puts n
else
  ...
end

这样写是不是更好:

if (n = to_integer("33"))
  puts n
else
  ...
end

方法to_integerto_float基本上有两种写法。第一个是你采取的方法:

def to_integer(str)
  raise ArgumentError unless str.is_a? String
  s = str.gsub(/\s/,'')
  Integer(s) rescue nil
end

def to_float(str)
  raise ArgumentError unless str.is_a? String
  s = str.gsub(/\s/,'')
  return nil if to_integer(s)
  Float(s) rescue nil
end

to_integer("3")     #=> 3 
to_integer("-3")    #=> -3 
to_integer("+  3")  #=> 3 
to_integer("cat")   #=> nil 
to_integer("3.14")  #=> nil 
to_integer(:cat)    #=> ArgumentError: ArgumentError

to_float("3.14")    #=> 3.14 
to_float("-3.14")   #=> -3.14 
to_float("+  3.14") #=> 3.14 
to_float("cat")     #=> nil 
to_float("3")       #=> nil 
to_float(:cat)      #=> ArgumentError: ArgumentError

第二种方法是使用正则表达式:

def to_integer(str)
  raise ArgumentError unless str.is_a? String
  s = str.gsub(/\s/,'')
  s[/^[+-]?\s*\d+$/] ? s.to_i : nil
end

def to_float(str)
  raise ArgumentError unless str.is_a? String
  s = str.gsub(/\s/,'')
  return nil if to_integer(s)
  s[/^[+-]?\s*\d+\.\d+$/] ? s.to_f : nil
end

to_integer("3")     #=> 3 
to_integer("-3")    #=> -3 
to_integer("+  3")  #=> 3 
to_integer("cat")   #=> nil 
to_integer("3.14")  #=> nil 
to_integer(:cat)    #=> ArgumentError: ArgumentError

to_float("3.14")    #=> 3.14 
to_float("-3.14")   #=> -3.14 
to_float("+  3.14") #=> 3.14 
to_float("cat")     #=> nil 
to_float("3")       #=> nil 
to_float(:cat)      #=> ArgumentError: ArgumentError

无需使用eval发送消息。您可以只使用 send 代替:

def check_if_correct_type(type, value)
  !!send(type.capitalize, value) rescue return false
  true
end

注意:在 Ruby 核心库或 Ruby 标准库中的任何地方都没有名为 classify 的方法。另请注意,盲目捕获所有异常是一个非常糟糕的主意。

这就是我最终解决问题的方式

def check_if_correct_type(type, value)
    !!eval("#{type.classify}(value)") rescue return false
    true
end

下面是此函数的示例输出,以防您想知道它是否是单词

[25] pry(main)> value = "1"
=> "1"
[26] pry(main)> !!eval("#{type.classify}(value)")
=> true
[27] pry(main)> value = "a"
=> "a"
[28] pry(main)> !!eval("#{type.classify}(value)")
ArgumentError: invalid value for Float(): "a"
from (pry):28:in `eval'
[29] pry(main)> value = "1.4"
=> "1.4"
[30] pry(main)> type = "integer"
=> "integer"
[31] pry(main)> !!eval("#{type.classify}(value)")
ArgumentError: invalid value for Integer(): "1.4"
from (pry):31:in `eval'