递归和 Kernel#rand (Ruby) 的奇怪行为
Strange behavior with recursion and Kernel#rand (Ruby)
我在Ruby中有这样的方法:
def guess
random_guess = rand(4)
if random_guess == 0
guess
end
puts random_guess
end
guess
如你所见,它的目的是生成一个介于0和4之间的随机数,然后如果该数字为0则重复。这个想法是为了防止该方法打印出0。但是,它似乎并没有出于某种奇怪的原因做出这种行为。例如,这里是 IRB 中的一个例子,当 random_guess
得到一个值 0:
2.2.0 :001 > def guess
2.2.0 :002?> random_guess = rand(4)
2.2.0 :003?> if random_guess == 0
2.2.0 :004?> guess
2.2.0 :005?> end
2.2.0 :006?>
2.2.0 :007 > puts random_guess
2.2.0 :008?> end
=> :guess
2.2.0 :009 > guess
2
0
0
0
0
=> nil
2.2.0 :010 >
当它没有得到值 0 时,程序正常运行并简单地打印出该范围内的任何其他随机数:
2.2.0 :010 > guess
1
=> nil
2.2.0 :011 >
我想知道的是为什么程序在 random_guess
为 0 时发生的递归行为如此奇怪。看起来它是重复出现的,但它似乎没有在正确的条件下停止,即当 random_guess
不为 0 时。为什么会这样?
当 random_guess
为 0
时,guess
递归调用自身,这是正常工作的。您看到的输出是因为 puts random_guess
在 之后 执行了递归。因此,当您看到输出为,例如:
3
0
guess
是这样调用的:random_guess
得到 0
,所以调用 guess
.
- 里面内
guess
,random_guess
得到3
,把
当前 random_guess
,即 3
。退出 inner guess
- 将
random_guess
的值放入outterguess
,即
0
,然后退出。
它完全按照您的指示进行操作。如果你得到一个非零,你不输入 if
-子句,所以你立即打印非零并完成。
但是,如果您生成的第一个值为零,则您进入递归并重复该过程,直到生成一个非零值。只有这样你才能绕过 if
来遇到打印语句和递归中的 return 。由于仅当您有零时才调用递归,因此您现在打印该零和 return.
最终结果,输出将是一个非零值,然后是出现的所有零,以相反的顺序弹出堆栈。
因为它recursive
。在维护每个剩余指令的堆栈中。
假设如果出现 0,它会在堆栈中添加 puts random_guess
行。在完成递归 guess
调用后,它会弹出剩余的指令。
所以它打印 0 直到 guess
的许多递归调用。
# closer to your original design
# fyi, returns values 1 - 3
def guess
random_guess = 0
while random_guess == 0
random_guess = get_random_guess(4)
end
random_guess
end
def get_random_guess(max_guess)
rand(4)
end
# test guess 100 times
(1..100).each { puts "guess=#{guess}" }
# faster, note the use of rand without a max parameter.
# in this case, rand returns a float between 0 and 1
# this return values between min_guess and max_guess
def faster_guess(min_guess, max_guess)
(rand() * (max_guess + 1 - min_guess)).to_i + min_guess
end
# test faster_guess 100 times
# will return values between 1 - 4
(1..100).each { puts "faster_guess=#{faster_guess(1,4)}" }
因为您将 puts
语句放在您的方法中,它会在每次调用时打印出随机猜测的值,无论它是否是 0
。递归调用自身的方法只会暂时中断该点的控制流,直到它移回堆栈。
您想要做的是使用方法 return 第一个不是 0
的值。示例:
def guess
random_guess = rand(4)
(random_guess > 0) ? random_guess : guess
end
puts guess
# 1 (never 0)
在此修改后的方法中,如果条件大于 0
,则条件将 return random_guess
。如果不满足,就会递归调用自己,直到满足条件。
请注意,递归的这种用法理论上 创造了无限递归的可能性,尽管这在实践中永远不会发生,但该方法的执行时间将是随机的。
我在Ruby中有这样的方法:
def guess
random_guess = rand(4)
if random_guess == 0
guess
end
puts random_guess
end
guess
如你所见,它的目的是生成一个介于0和4之间的随机数,然后如果该数字为0则重复。这个想法是为了防止该方法打印出0。但是,它似乎并没有出于某种奇怪的原因做出这种行为。例如,这里是 IRB 中的一个例子,当 random_guess
得到一个值 0:
2.2.0 :001 > def guess
2.2.0 :002?> random_guess = rand(4)
2.2.0 :003?> if random_guess == 0
2.2.0 :004?> guess
2.2.0 :005?> end
2.2.0 :006?>
2.2.0 :007 > puts random_guess
2.2.0 :008?> end
=> :guess
2.2.0 :009 > guess
2
0
0
0
0
=> nil
2.2.0 :010 >
当它没有得到值 0 时,程序正常运行并简单地打印出该范围内的任何其他随机数:
2.2.0 :010 > guess
1
=> nil
2.2.0 :011 >
我想知道的是为什么程序在 random_guess
为 0 时发生的递归行为如此奇怪。看起来它是重复出现的,但它似乎没有在正确的条件下停止,即当 random_guess
不为 0 时。为什么会这样?
当 random_guess
为 0
时,guess
递归调用自身,这是正常工作的。您看到的输出是因为 puts random_guess
在 之后 执行了递归。因此,当您看到输出为,例如:
3
0
guess
是这样调用的:random_guess
得到0
,所以调用guess
.- 里面内
guess
,random_guess
得到3
,把 当前random_guess
,即3
。退出 innerguess
- 将
random_guess
的值放入outterguess
,即0
,然后退出。
它完全按照您的指示进行操作。如果你得到一个非零,你不输入 if
-子句,所以你立即打印非零并完成。
但是,如果您生成的第一个值为零,则您进入递归并重复该过程,直到生成一个非零值。只有这样你才能绕过 if
来遇到打印语句和递归中的 return 。由于仅当您有零时才调用递归,因此您现在打印该零和 return.
最终结果,输出将是一个非零值,然后是出现的所有零,以相反的顺序弹出堆栈。
因为它recursive
。在维护每个剩余指令的堆栈中。
假设如果出现 0,它会在堆栈中添加 puts random_guess
行。在完成递归 guess
调用后,它会弹出剩余的指令。
所以它打印 0 直到 guess
的许多递归调用。
# closer to your original design
# fyi, returns values 1 - 3
def guess
random_guess = 0
while random_guess == 0
random_guess = get_random_guess(4)
end
random_guess
end
def get_random_guess(max_guess)
rand(4)
end
# test guess 100 times
(1..100).each { puts "guess=#{guess}" }
# faster, note the use of rand without a max parameter.
# in this case, rand returns a float between 0 and 1
# this return values between min_guess and max_guess
def faster_guess(min_guess, max_guess)
(rand() * (max_guess + 1 - min_guess)).to_i + min_guess
end
# test faster_guess 100 times
# will return values between 1 - 4
(1..100).each { puts "faster_guess=#{faster_guess(1,4)}" }
因为您将 puts
语句放在您的方法中,它会在每次调用时打印出随机猜测的值,无论它是否是 0
。递归调用自身的方法只会暂时中断该点的控制流,直到它移回堆栈。
您想要做的是使用方法 return 第一个不是 0
的值。示例:
def guess
random_guess = rand(4)
(random_guess > 0) ? random_guess : guess
end
puts guess
# 1 (never 0)
在此修改后的方法中,如果条件大于 0
,则条件将 return random_guess
。如果不满足,就会递归调用自己,直到满足条件。
请注意,递归的这种用法理论上 创造了无限递归的可能性,尽管这在实践中永远不会发生,但该方法的执行时间将是随机的。