rand(Range) - 没有将Range隐式转换为Integer

时间:2018-01-17 03:07:55

标签: ruby

跟进问题How to create a random time between a range

Kernel#rand适用于Time范围:

require 'time'
rand(Time.parse('9 am')..Time.parse('11:30 am'))

但是当我尝试使用自定义类时,我最终得到了错误:

  

`rand':没有将Range隐式转换为Integer(TypeError)

class Int
  include Comparable

  attr_reader :num

  def initialize(num)
    @num = num
  end

  def succ
    Int.new(num + 1)
  end

  def <=>(other)
    num <=> other.num
  end

  def to_s
    "Int(#{num})"
  end

  def to_int
    @num
  end

  alias_method :inspect, :to_s
end

puts rand(Int.new(1)..Int.new(3))

为什么呢?我在自定义课程中缺少什么?我们可以在rand(Range)中使用这样的自定义类吗?

3 个答案:

答案 0 :(得分:4)

我不知道Kernel#randRange论点所期望的任何文档,但我们可以通过覆盖{{1}来了解正在发生的事情。在你的班级,然后看着事情崩溃:

respond_to?

这样做告诉我们def respond_to?(m) puts "They want us to support #{m}" super end 想要调用rand个实例上的#-#+方法。鉴于Int设计用于处理整数,这确实有意义。

因此,我们引入了加法和减法的快速实现:

rand(a..b)

我们开始通过调用def -(other) self.class.new(to_int - other.to_int) end def +(other) self.class.new(to_int + other.to_int) end 来获取rand Int

我不确定在哪里(或者如果)记录下来,所以你不得不原谅一点挥手。我通常会花一些时间来处理Ruby源代码来回答这类问题但我现在没时间。

答案 1 :(得分:3)

为了给@ mu-too-short-short的答案添加更多内容,我检查了Random#rand的来源,以下是rand(Range)的当前实现逻辑:

  1. vmax对象(call range_values)获取开始,结束和Range,其中vmax计算为(call id_minus) :

    vmax = end - begin
    

    vmax将用作稍后生成随机数的上限。

    这需要自定义类定义-方法。

  2. 根据vmax

    的类型生成随机数
    • 如果它不是Float且可以强制转换为Integerrb_check_to_int),则生成小于Integer的随机vmax。< / p>

      在这种情况下,-方法应返回Integer或响应to_int方法的对象。

    • 如果是Numeric且可以使用Float转换为to_f,(rb_check_to_float),则生成小于Float的随机数vmax -

      在这种情况下,Numeric方法应该返回Float个号码,可以使用方法to_f转换为begin

  3. 将随机数添加到+以产生结果(call id_add)。

    这要求自定义类定义Integer方法,该方法接受步骤2中生成的随机数的结果(Floatrand)并返回owl:sameAs的最终结果。

答案 2 :(得分:0)

我相信这个错误是因为您尝试在自定义类的对象上使用rand()。

`rand': no implicit conversion of Range into Integer (TypeError)

此错误消息明确提到ruby无法将您的范围转换为整数。根据您的代码段,以下工作,可能是您正在寻找的。

puts rand(Int.new(1).to_int..Int.new(3).to_int)