我想在ruby 1.9.3 ver中编写一个程序。它收集唯一值范围,然后计算这些范围内的数字量。
例如,让我们使用3个范围(1..3),(6..8)和(2..4)。它将返回具有两个范围(1..4),(6..8)和数量 - 7的数组。
我写了以下代码:
z= []
def value_ranges(start, finish, z)
range = (start..finish)
arr = z
point = nil
if arr.empty?
point = nil
else
arr.each { |uniq|
if overlap?(uniq,range) == true
point = arr.index(uniq)
break
else
point = nil
end
}
end
if point != nil
if arr[point].first >= start && arr[point].end <= finish
range = (start..finish)
elsif arr[point].first >= start
range = (start..arr[point].end)
elsif arr[point].end <= finish
range = (arr[point].first..finish)
else
range = (arr[point].first..arr[point].end)
end
arr[point] = range
else
arr << range
end
print arr
end
def overlap?(x,y)
(x.first - y.end) * (y.first - x.end) >= 0
end
当程序满足与已收集的两个范围重叠的范围时出现问题。
例如(1..5)(7..9)(11..19),下一个给定范围是(8..11)。
它应链接两个范围并返回以下结果 - (1..5),(7..19)。
我不知道如何在不创建无限循环的情况下重新检查整个数组。另外,计算范围内数量的最佳方法是什么?
答案 0 :(得分:1)
以下是两种类似Ruby的方法。
arr = [1..3, 6..8, 2..4]
#1高效方法
首先计算合并范围:
a = arr[1..-1].sort_by(&:first).each_with_object([arr.first]) do |r,ar|
if r.first <= ar.last.last
ar[-1] = ar.last.first..[ar.last.last,r.last].max
else
ar << r
end
end
#=> [1..4, 6..8]
然后计算这些范围内的元素总数:
a.reduce(0) { |tot,r| tot + r.size }
#=> [1..4, 6..8].reduce(0) { |tot,r| tot + r.size }
#=> 7
说明的
b = arr[1..-1]
#=> [6..8, 2..4]
c = b.sort_by(&:first)
#=> [2..4, 6..8]
enum = c.each_with_object([1..3])
#=> #<Enumerator: [2..4, 6..8]:each_with_object([1..3])>
枚举器enum
的内容将传递到块中,并由Enumerator#each分配给块变量,后者将调用Array#each。我们可以通过将枚举器转换为数组来查看枚举器的内容:
enum.to_a
#=> [[2..4, [1..3]], [6..8, [1..3]]]
我们可以使用Enumerator#next逐步完成枚举器。 each
传递给块的枚举器的第一个元素是[2..4, [1..3]]
。这将分配给块变量,如下所示:
r, ar = enum.next
#=> [2..4, [1..3]]
r #=> 2..4
ar #=> [1..3]
我们现在执行块计算
if r.first <= ar.last.last
#=> 2 <= (1..3).last
#=> 2 <= 3
#=> true
ar[-1] = ar.last.first..[ar.last.last,r.last].max
#=> ar[-1] = 1..[3,4].max
#=> ar[-1] = 1..4
#=> 1..4
else # not executed this time
ar << r
end
这不是那么神秘。所以我不必一直说“ar
的最后一个范围”,让我来定义:
ar_last = ar.last
#=> 1..3
首先,因为我们开始按每个范围的开头对范围进行排序,我们知道当enum
的每个元素都传递到块中时:
ar_last.first <= r.first
对于传递到块中的enum
的每个元素:
r.first <= ar_last.last
我们将r.last
与ar_last.last
进行比较。有两种可能性需要考虑:
r.last <= ar_last.last
,在这种情况下,两个范围重叠,因此ar_last
不会改变;和r.last > ar_last.last
,在这种情况下,ar_last
的上端必须增加到r.last
。下面,
2 = r.first <= ar_last.last = 3
4 = r.last > ar_last.last = 3
因此ar_last
已从1..3
更改为1..4
。
each
现在将enum
的最后一个元素传递到块中:
r, ar = enum.next
#=> [6..8, [1..4]]
r #=> 6..8
ar #=> [1..4]
if r.first <= ar.last.last
#=> (6 <= 4) => false this time
...
else # executed this time
ar << r
#=> ar << (6..8)
#=> [1..4, 6..8]
end
和
a = ar #=> [1..4, 6..8]
这次r.first > ar_last.last
,意味着范围r
不重叠ar_last
,因此我们将r
追加到ar
和ar_last
现在等于r
。
最后:
a.reduce(0) { |tot,r| tot + r.size }
#=> [1..4, 6..8].reduce(0) { |tot,r| tot + r.size }
#=> 7
我们也可以写:
a.map(&:size).reduce(:+)
#2简单但效率低下
这是一个简单但不是特别有效的方法,它使用Enumerable#slice_when,在v2.2中新建。
arr = [(1..3), (6..8), (2..4)]
计算满意的范围:
a = arr.flat_map(&:to_a)
.uniq
.sort
.slice_when { |i,j| i+1 != j }
.map { |ar| (ar.first..ar.last) }
#=> [1..4, 6..8]
这些范围内的元素总数按#1
计算说明的
以下是步骤:
b = arr.flat_map(&:to_a)
#=> [1, 2, 3, 6, 7, 8, 2, 3, 4]
c = b.uniq
#=> [1, 2, 3, 6, 7, 8, 4]
d = c.sort
#=> [1, 2, 3, 4, 6, 7, 8]
e = d.slice_when { |i,j| i+1 != j }
#=> #<Enumerator: #<Enumerator::Generator:0x007f81629584f0>:each>
a = e.map { |ar| (ar.first..ar.last) }
#=> [1..4, 6..8]
我们可以通过将枚举器转换为数组来查看枚举器e
的内容:
e.to_a
#=> [[1, 2, 3, 4], [6, 7, 8]]