如何将范围分成N部分

Posted

技术标签:

【中文标题】如何将范围分成N部分【英文标题】:How to split a range into N parts 【发布时间】:2016-09-27 15:13:50 【问题描述】:

我想知道如何在 ruby​​ 中将一个范围拆分为 N 个部分,同时将它们添加到一个散列中,并为每个生成的范围生成一个从零开始的值。

例如:

range = 1..60
p split(range, 4)
#=> 1..15 => 0, 16..30 => 1, 31..45 => 2, 46..60 => 3

我已经阅读了How to return a part of an array in Ruby? 以了解如何将范围切片为数组,以及其他一些有关如何将切片转换回范围的内容,但我似乎无法将所有部分拼凑在一起以创建我想要的方法。

感谢您的帮助

【问题讨论】:

有人刚刚对我的回答投了赞成票,所以我过来看看问题是什么。我看到您从未选择过答案。如果您发现任何答案有帮助,请考虑选择您喜欢的答案。哎呀,我看到只有一个。 【参考方案1】:
range = 1..60

range.each_slice(range.last/4).with_index.with_object()  |(a,i),h|
  h[a.first..a.last]=i 
  #=> 1..15=>0, 16..30=>1, 31..45=>2, 46..60=>3

步骤如下:

enum0 = range.each_slice(range.last/4)
  #=> range.each_slice(60/4)
  #   #<Enumerator: 1..60:each_slice(15)> 

您可以将此枚举器转换为数组以查看它将生成并传递给each_with_index 的 (4) 个元素:

enum0.to_a
  #=> [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
  #    [16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30],
  #    [31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45],
  #    [46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60]] 

enum1 = enum0.with_index
  #=> #<Enumerator: #<Enumerator: 1..60:each_slice(15)>:with_index>
enum1.to_a
  #=> [[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], 0],
  #    [[16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30], 1],
  #    [[31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45], 2],
  #    [[46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60], 3]] 

enum2 = enum1.with_object()
  #=> #<Enumerator: #<Enumerator: #<Enumerator: 1..60:each_slice(15)>
  #     :with_index>:with_object()>
enum2.to_a
  #=> [[[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], 0], ],
  #    [[[16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30], 1], ],
  #    [[[31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45], 2], ],
  #    [[[46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60], 3], ]]

仔细检查enum1enum2 的计算返回值。您可能想将它们视为“复合”枚举器。 enum2 的四个数组的第二个和最后一个元素是由块变量h 表示的空哈希。该哈希将在后续计算中构建。

enum2.each  |(a,i),h| h[a.first..a.last]=i 
  #=> 1..15=>0, 16..30=>1, 31..45=>2, 46..60=>3 

each 传递给块的enum2 的第一个元素(在执行enum.each... 之前)是

arr = enum2.next
  #=>[[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], 0], ]

使用并行赋值(有时称为多重赋值)将块变量赋值给arr的元素

(a,i),h = arr
  #=> [[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], 0], ] 
a #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] 
i #=> 0 
h #=> 

因此块计算是

h[a.first..a.last]=i 
  #=> h[1..15] = 0

现在

h #=> 1..15=>0 

enum2 生成的其他 3 个元素中的每一个的计算都类似。

表达式

enum2.each  |(a,i),h| h[(a.first..a.last)]=i 

也可以写成

enum2.each  |((f,*_,l),i),h| h[(f..l)]=i 

【讨论】:

.each_slice(15).map.with_index |d, i| (d.first..d.last) =&gt; i 可能更具可读性 @llya,需要一个散列,而不是散列数组,每个散列都有一个键值对。但是,您通过范围的建议是一个很好的建议。我会将其纳入我的答案中。 感谢您的回答!不幸的是,在我的机器ruby 2.2.3p173 (2015-08-18 revision 51636) [x64-mingw32] 上,您的代码返回[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]=&gt;0, [16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]=&gt;1, [31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45]=&gt;2, [46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60]=&gt;3 而不是1..15=&gt;0, 16..30=&gt;1, 31..45=&gt;2, 46..60=&gt;3 ,在某处有轻微的疏忽吗?如果不是,我如何检测单个整数是否落在键范围之间并返回其值。再次感谢。 继续:在我使用 hash.detect |k,v| k === 4.last 返回 4 所在范围的值之前。这将不再起作用,因为哈希的类型是array=&gt;integer 而不是range=&gt;integer。有没有其他方法可以解决这部分问题?编辑:hash.detect |k,v| k.include?(4).last 现在可以使用。再次感谢。 兰迪,很抱歉给您带来不便。我回滚到我原来的答案,我相信这是正确的。我做了一个我认为会简化的更改,但没有彻底测试它以发现它是错误的。我将添加解释。

以上是关于如何将范围分成N部分的主要内容,如果未能解决你的问题,请参考以下文章

将图像对象分成 N 个相等像素的部分(方法)

将元素平分成差值最小的两个集合(DP)

jQuery使用第n个子元素将列表项分成两部分

如何将 XML 文档分成三份(或者,甚至更好,分成 n 份)?

如何将@RenderBody() 分成两部分

如何将列表分成两部分?