我对Ruby和编程很新,我目前正在尝试使用Chris Pine的书。在第10章中,有一项任务要求您返回一个洗牌版本的数组。
我为shuffle方法本身尝试了以下代码,它起作用了:
def shuffle array
recursive_shuffle array, []
end
def recursive_shuffle unshuffled, shuffled
if unshuffled.length == 1
shuffled.push unshuffled[0]
shuffled.pop
return shuffled
end
rand_index = rand(unshuffled.length - 1)
shuffled.push unshuffled[rand_index]
unshuffled.delete_at(rand_index)
recursive_shuffle(unshuffled, shuffled)
end
puts(shuffle(array))
我想知道两件事。
首先,我尝试不使用shuffled.pop
和rand_index = rand(unshuffled.length)
代替length - 1
。
然而,在答案中,最后一个元素之前的元素始终为空白,否则它被洗牌并且没有元素丢失。然后我认为问题应该在rand
中,并添加了- 1
。
此时,数组再次正确地进行了混洗,但这次洗牌数组总是有一个空白的最后一个元素。这就是我添加shuffled.pop
。
它可以正常工作,但我不明白为什么它会在前两个实例中添加一个空白元素。
另外,如果我有一个包含2个元素的数组,则其长度应为2,而rand_index
应为rand(1)
,但不是rand(1)
应该始终为0?尽管如此,前面提到的代码仍然适当地改组了具有2个元素的数组。
我查看了有关Ruby,shuffle和rand的各种其他主题,但无法找到类似的问题。
答案 0 :(得分:2)
随机播放方法中的第一个块:
if unshuffled.length == 1
shuffled.push unshuffled[0]
shuffled.pop
return shuffled
end
没有做你想做的事。
如果unshuffled.length
是1
,那么数组中只有一个元素,所以你只需将它添加到shuffled数组中,然后返回一个混洗数组:
if unshuffled.length == 1
return shuffled.push(unshuffled.pop)
end
或坚持原来的方法:
if unshuffled.length == 1
shuffled.push unshuffled[0]
return shuffled
end
此外,调用push
会将新元素推送到您正在调用push
的数组末尾。例如。如果a = [1, 2]
,则a.push(3)
会产生[1, 2, 3]
。
在数组上调用pop
,"弹出"数组的最后一个元素。例如。如果a = [1, 2, 3]
并且您致电a.pop
,则a == [1, 2]
。
将这两个概念放在一起,您可以看到,如果您将某些东西推到一个阵列上,然后将其向右弹回,那么您将无法完成任何工作。例如。如果您拨打a = [1, 2]
然后a.push(3)
,则a
会显示为[1, 2, 3]
。致电a.pop
会让您回到[1, 2]
。
另一个问题是对rand
的调用(如评论中提到的@pjs)。
rand
将返回一个小于您提供的(非零)整数的随机整数,因此无需递减传递给它的length
。{0}}。 rand
行可以更改为:rand_index = rand(unshuffled.length - 1)
整合此更改应该会给您留下类似的内容:
def recursive_shuffle unshuffled, shuffled
if unshuffled.length == 1
return shuffled.push(unshuffled.pop)
end
rand_index = rand(unshuffled.length)
shuffled.push unshuffled[rand_index]
unshuffled.delete_at(rand_index)
recursive_shuffle(unshuffled, shuffled)
end
哪个应该可以正常工作。
这可以进一步缩短为:
def recursive_shuffle(unshuffled, shuffled = [])
return shuffled.push(unshuffled.pop) if unshuffled.length == 1
shuffled.push(unshuffled.delete_at(rand(unshuffled.length)))
recursive_shuffle(unshuffled, shuffled)
end
甚至:
def recursive_shuffle(arr)
return arr if arr.length == 1
shuffle = [arr.sample]
shuffle + recursive_shuffle(arr - shuffle)
end
答案 1 :(得分:2)
这里有一种类似Ruby的方式,您可以实现自己的shuffle
版本。步骤如下:
my_shuffle
上创建实例方法Array
,以模仿实例方法Array#shuffle的功能(但不需要实现替代方式shuffle
可以已使用:shuffle(random: rng) → new_ary
- 请参阅文档)。self
(要洗牌的数组)的副本进行操作,以便self
不会发生变异" (改变的)。enum = arr.size.times
,生成数字序列0,1,...,arr.size-1
。块中没有使用序列的值;重要的是该块执行arr.size
次。 a
的元素并将其推送到arr
来构建由块变量a
表示的数组。<强>代码强>
class Array
def my_shuffle
arr = self.dup
arr.size.times.with_object([]) { |_,a| a << arr.delete_at(rand(arr.size)) }
end
end
示例强>
[1,3,2,7,4,1].my_shuffle
#=> [2, 3, 4, 1, 1, 7]
<强>解释强>
首先,我使用Enumerator#each_object仅仅保存了两个步骤(下面是a = []
和a
)。它产生与以下相同的结果:
def my_shuffle
arr = self.dup
a = []
arr.size.times { a << arr.delete_at(rand(arr.size)) }
a
end
要洗牌的数组是:
ar = [1,3,2,7,4,1]
然后
arr = ar.dup
#=> [1, 3, 2, 7, 4, 1]
n = arr.size
#=> 6
enum0 = n.times
#=> #<Enumerator: 6:times>
enum1 = enum0.with_object([])
#=> #<Enumerator: #<Enumerator: 6:times>:with_object([])>
仔细检查enum1
的返回值。它可能被认为是一个复合调查员&#34;。我们可以通过将enum1
转换为数组来看到enum1.to_a
#=> [[0, []], [1, []], [2, []], [3, []], [4, []], [5, []]]
生成的元素:
a
每个数组的第二个元素对应于块变量[]
。最初它是一个空数组,因为Enumerable#each_with_object的参数是enum1
。
我们可以将Enumerator#next发送到b,a = enum1.next
#=> [0, []]
以生成枚举器的第一个元素,并将块变量设置为等于该值:
b
a
和b #=> 0
a #=> []
的值是通过使用并行分配(又名多次分配)获得的:
c = arr.delete_at(rand(ar.size))
#=> arr.delete_at(rand(6))
#=> arr.delete_at(4)
#=> 4
a << c
#=> [4]
然后我们可以执行块计算:
a
[4]
现在为arr
,[1, 3, 2, 7, 1]
等于ar
且b
未更改。
按照惯例,我已使用下划线替换了块变量b,a = enum1.next
#=> [1, [4]]
c = arr.delete_at(rand(ar.size))
#=> arr.delete_at(rand(5))
#=> arr.delete_at(0)
#=> 1
a << c
#=> [4, 1]
arr
#=> [3, 2, 7, 1]
,因为它未在块计算中使用。
接下来我们有
protected void Application_Start()
{
var jsonMediaTypeFormatter = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
jsonMediaTypeFormatter.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.All;
jsonMediaTypeFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
}
其余的计算方法类似。
答案 2 :(得分:2)
如果你要让数组洗牌的目标应该使用内置的shuffle
方法,那么它的正确性和速度都比你要编写的任何东西都快。
如果您的目标是实现自己的版本,那么这不适合递归,您应该实现Fisher-Yates shuffle以避免bias:
def fy_shuffle(arr)
a_copy = arr.dup # don't mutate original array!
a_copy.each_index do |i|
j = i + rand(a_copy.length - i)
a_copy[i], a_copy[j] = a_copy[j], a_copy[i] if i != j
end
end
如果您的目标是了解递归,那么dinjas的答案可以修复原始实现中的问题,并且只要数组不够大就无法完成堆栈就可以正常工作。