Ruby 如何将重叠结构与其中的范围合并
我有一个类Ruby 如何将重叠结构与其中的范围合并,ruby,Ruby,我有一个类Format,它是一个数据结构,包含有关文本格式的信息: Format = Struct.new(:from, :to, :attributes) (我不在乎它是Format.new(from,to,attrs)还是Format.new(range,attrs)) 在Format实例f1和f2中,我想定义一个操作merge\u formats(f1,f2),它合并重叠的格式,例如当语法高亮显示和选择应应用于相同的文本区域时。类似地,对于范围不相交、仅在一侧重叠等情况,可通过下图进行描
Format
,它是一个数据结构,包含有关文本格式的信息:
Format = Struct.new(:from, :to, :attributes)
(我不在乎它是Format.new(from,to,attrs)
还是Format.new(range,attrs)
)
在Format
实例f1
和f2
中,我想定义一个操作merge\u formats(f1,f2)
,它合并重叠的格式,例如当语法高亮显示和选择应应用于相同的文本区域时。类似地,对于范围不相交、仅在一侧重叠等情况,可通过下图进行描述:
INPUT:
+---+---+---+---+---+---+---+---+---+
format1 = | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | , attributes1
+---+---+---+---+---+---+---+---+---+
+---+---+---+---+
format2 = | 4 | 5 | 6 | 7 | , attributes2
+---+---+---+---+
RESULT:
+---+---+---+
part1 = | 1 | 2 | 3 |, attributes1
+---+---+---+
+---+---+---+---+
part2 = | 4 | 5 | 6 | 7 | , attributes1 + attributes2
+---+---+---+---+
+---+---+
part3 = | 8 | 9 | , attributes1
+---+---+
特定应用程序应如下所示:
merge_formats(Format.new(1, 9, attributes1), Format.new(4, 7, attributes2))
# => [Format.new(1, 3, attributes1), Format.new(4, 7, attributes1 + attributes2), Format.new(8, 9, attributes1)]
attribute1
和attribute2
是可以添加的任何内容,在本例中是标志(整数,因此是|
运算符)。如果它们是:attribute1
和:attribute2
,那么作为addition“操作将是数组。push
,结果可能是:
# => [Format.new(1, 3, [:attribute1]), Format.new(4, 7, [:attribute1, :attribute2]), Format.new(8, 9, [:attribute1])]
有没有一个好的Ruby方法来解决这个问题
我遇到了一个问题,其中一个答案巧妙地使用了
flat_map
来检测边缘,但这在我的情况下是不可用的,因为其他信息丢失了。我想到了这个,我不喜欢它:
Format = Struct.new(:from, :to, :attributes) do
def self.compare(f1, f2)
if f2.from < f1.from && f2.to > f1.to
:includes
elsif f2.from >= f1.from && f2.to <= f1.to
:inside
elsif f2.from < f1.from && (f1.from..f1.to).include?(f2.to)
:left
elsif (f1.from..f1.to).include?(f2.from) && f2.to > f1.to
:right
else
:outside
end
end
def self.merge(f1, f2)
case compare(f1, f2)
when :includes
[Format.new(f2.from, f1.from-1, f2.attributes),
Format.new(f2.from, f1.to, f1.attributes | f2.attributes),
Format.new(f1.to+1, f2.to, f2.attributes),
]
when :inside
if f2.from == f1.from && f2.to == f1.to
[Format.new(f2.from, f2.to, f1.attributes | f2.attributes)]
else
[Format.new(f1.from, f2.from-1, f1.attributes),
Format.new(f2.from, f2.to, f1.attributes | f2.attributes),
Format.new(f2.to+1, f1.to, f1.attributes),
]
end
when :left
r = [Format.new(f2.from, f1.from-1, f2.attributes),
Format.new(f1.from, f2.to, f1.attributes | f2.attributes)]
r << Format.new(f2.to+1, f1.to, f1.attributes) if f2.to != f1.to
r
when :right
r = []
r << Format.new(f1.from, f2.from-1, f1.attributes) if f2.from != f1.from
r << Format.new(f2.from, f1.to, f1.attributes | f2.attributes)
r << Format.new(f1.to+1, f2.to, f2.attributes)
r
else
if f2.from < f1.from
[f2, f1]
else
[f1, f2]
end
end
end
end
Format=Struct.new(:from,:to,:attributes)do
def自比较(f1、f2)
如果f2.fromf1.to
:包括
elsif f2.from>=f1.from&&f2.to f1.to
:对
其他的
:外面
结束
结束
def自合并(f1、f2)
案例比较(f1、f2)
时间:包括
[Format.new(f2.from,f1.from-1,f2.attributes),
Format.new(f2.from,f1.to,f1.attributes | f2.attributes),
Format.new(f1.to+1,f2.to,f2.attributes),
]
时间:内部
如果f2.from==f1.from&&f2.to==f1.to
[Format.new(f2.from,f2.to,f1.attributes | f2.attributes)]
其他的
[Format.new(f1.from,f2.from-1,f1.attributes),
Format.new(f2.from,f2.to,f1.attributes | f2.attributes),
Format.new(f2.to+1,f1.to,f1.attributes),
]
结束
时间:左
r=[Format.new(f2.from,f1.from-1,f2.attributes),
Format.new(f1.from,f2.to,f1.attributes | f2.attributes)]
r一个可能的解决方案(我不是红宝石专家):
还有一个测试合并:
# file .../split_range/spec/t_merge_spec.rb
require 't'
RSpec.describe Format do
describe '.merge' do
=begin
let(:rleft) { 1..5 }
let(:rcenter) { 10..20 }
let(:rright) { 24..26 }
let(:rovleft) { 8..12 }
let(:rinside) { 12..16 }
let(:rovright) { 18..24 }
=end
context 'moving r1 over r2' do
it 'non overlapping r1 on the left of r2' do
f1 = Format.new(1, 5, [:foo])
f2 = Format.new(10, 20, [:bar])
expect(Format.merge(f1, f2)).to eq([Format.new(1, 5, [:foo]), Format.new(10, 20, [:bar])])
end
it 'overlapping r1 on the left of r2' do
f1 = Format.new(8, 12, [:foo])
f2 = Format.new(10, 20, [:bar])
expect(Format.merge(f1, f2)).to \
eq([Format.new(8, 9, [:foo]), Format.new(10, 12, [:foo, :bar]), Format.new(13, 20, [:bar])])
end
it 'overlapping r1 inside r2' do
f1 = Format.new(12, 16, [:foo])
f2 = Format.new(10, 20, [:bar])
expect(Format.merge(f1, f2)).to \
eq([Format.new(10, 11, [:bar]), Format.new(12, 16, [:foo, :bar]), Format.new(17, 20, [:bar])])
end
it 'overlapping r1 on the right of r2' do
f1 = Format.new(18, 24, [:foo])
f2 = Format.new(10, 20, [:bar])
expect(Format.merge(f1, f2)).to \
eq([Format.new(10, 17, [:bar]), Format.new(18, 20, [:foo, :bar]), Format.new(21, 24, [:foo])])
end
it 'non overlapping r1 on the right of r2' do
f1 = Format.new(24, 26, [:foo])
f2 = Format.new(10, 20, [:bar])
expect(Format.merge(f1, f2)).to eq([Format.new(10, 20, [:bar]), Format.new(24, 26, [:foo])])
end
end # context 'moving r1 over r2'
context 'moving r2 under r1' do
it 'non overlapping r2 on the left of r1' do
f1 = Format.new(10, 20, [:foo])
f2 = Format.new(1, 5, [:bar])
expect(Format.merge(f1, f2)).to eq([Format.new(1, 5, [:bar]), Format.new(10, 20, [:foo])])
end
it 'overlapping r2 on the left of r1' do
f1 = Format.new(10, 20, [:foo])
f2 = Format.new(8, 12, [:bar])
expect(Format.merge(f1, f2)).to \
eq([Format.new(8, 9, [:bar]), Format.new(10, 12, [:foo, :bar]), Format.new(13, 20, [:foo])])
end
it 'overlapping r2 inside r1' do
f1 = Format.new(10, 20, [:foo])
f2 = Format.new(12, 16, [:bar])
expect(Format.merge(f1, f2)).to \
eq([Format.new(10, 11, [:foo]), Format.new(12, 16, [:foo, :bar]), Format.new(17, 20, [:foo])])
end
it 'overlapping r2 on the right of r1' do
f1 = Format.new(10, 20, [:foo])
f2 = Format.new(18, 24, [:bar])
expect(Format.merge(f1, f2)).to \
eq([Format.new(10, 17, [:foo]), Format.new(18, 20, [:foo, :bar]), Format.new(21, 24, [:bar])])
end
it 'non overlapping r2 on the right of r1' do
f1 = Format.new(10, 20, [:foo])
f2 = Format.new(24, 26, [:bar])
expect(Format.merge(f1, f2)).to eq([Format.new(10, 20, [:foo]), Format.new(24, 26, [:bar])])
end
end # context 'moving r1 over r2'
end # describe '.merge'
end # describe Format
执行:
$ ruby -w lib/t.rb
#<struct Format from=1, to=3, attributes=[:foo]>
#<struct Format from=4, to=7, attributes=[:foo, :bar]>
#<struct Format from=8, to=9, attributes=[:foo]>
$ rspec -fd
Format
.merge
moving r1 over r2
non overlapping r1 on the left of r2
overlapping r1 on the left of r2
overlapping r1 inside r2
overlapping r1 on the right of r2
non overlapping r1 on the right of r2
moving r2 under r1
non overlapping r2 on the left of r1
overlapping r2 on the left of r1
overlapping r2 inside r1
overlapping r2 on the right of r1
non overlapping r2 on the right of r1
SplitRange
#partition
non overlapping
values of r1 smaller than r2 go to :ar1left
values of r2 smaller than r1 go to :br2left
values of r1 greater than r2 go to :dr1right
values of r2 greater than r1 go to :er2right
overlapping r2 from left to right
values of r2 smaller than r1 go to :br2left
values of r2 (left) common to r1 go to :ccommon
values of r2 (inside) common to r1 go to :ccommon
values of r2 (right) common to r1 go to :ccommon
values of r2 greater than r1 go to :er2right
overlapping r1 from left to right
values of r1 smaller than r2 go to :ar1left
values of r1 (left) common to r2 go to :ccommon
values of r1 (inside) common to r2 go to :ccommon
values of r1 (right) common to r2 go to :ccommon
values of r1 greater than r2 go to :dr1right
Finished in 0.0093 seconds (files took 0.11066 seconds to load)
24 examples, 0 failures
所有测试都是绿色的(Ruby 2.4,RSpec 3.6)。一个可能的解决方案(我不是Ruby专家):
还有一个测试合并:
# file .../split_range/spec/t_merge_spec.rb
require 't'
RSpec.describe Format do
describe '.merge' do
=begin
let(:rleft) { 1..5 }
let(:rcenter) { 10..20 }
let(:rright) { 24..26 }
let(:rovleft) { 8..12 }
let(:rinside) { 12..16 }
let(:rovright) { 18..24 }
=end
context 'moving r1 over r2' do
it 'non overlapping r1 on the left of r2' do
f1 = Format.new(1, 5, [:foo])
f2 = Format.new(10, 20, [:bar])
expect(Format.merge(f1, f2)).to eq([Format.new(1, 5, [:foo]), Format.new(10, 20, [:bar])])
end
it 'overlapping r1 on the left of r2' do
f1 = Format.new(8, 12, [:foo])
f2 = Format.new(10, 20, [:bar])
expect(Format.merge(f1, f2)).to \
eq([Format.new(8, 9, [:foo]), Format.new(10, 12, [:foo, :bar]), Format.new(13, 20, [:bar])])
end
it 'overlapping r1 inside r2' do
f1 = Format.new(12, 16, [:foo])
f2 = Format.new(10, 20, [:bar])
expect(Format.merge(f1, f2)).to \
eq([Format.new(10, 11, [:bar]), Format.new(12, 16, [:foo, :bar]), Format.new(17, 20, [:bar])])
end
it 'overlapping r1 on the right of r2' do
f1 = Format.new(18, 24, [:foo])
f2 = Format.new(10, 20, [:bar])
expect(Format.merge(f1, f2)).to \
eq([Format.new(10, 17, [:bar]), Format.new(18, 20, [:foo, :bar]), Format.new(21, 24, [:foo])])
end
it 'non overlapping r1 on the right of r2' do
f1 = Format.new(24, 26, [:foo])
f2 = Format.new(10, 20, [:bar])
expect(Format.merge(f1, f2)).to eq([Format.new(10, 20, [:bar]), Format.new(24, 26, [:foo])])
end
end # context 'moving r1 over r2'
context 'moving r2 under r1' do
it 'non overlapping r2 on the left of r1' do
f1 = Format.new(10, 20, [:foo])
f2 = Format.new(1, 5, [:bar])
expect(Format.merge(f1, f2)).to eq([Format.new(1, 5, [:bar]), Format.new(10, 20, [:foo])])
end
it 'overlapping r2 on the left of r1' do
f1 = Format.new(10, 20, [:foo])
f2 = Format.new(8, 12, [:bar])
expect(Format.merge(f1, f2)).to \
eq([Format.new(8, 9, [:bar]), Format.new(10, 12, [:foo, :bar]), Format.new(13, 20, [:foo])])
end
it 'overlapping r2 inside r1' do
f1 = Format.new(10, 20, [:foo])
f2 = Format.new(12, 16, [:bar])
expect(Format.merge(f1, f2)).to \
eq([Format.new(10, 11, [:foo]), Format.new(12, 16, [:foo, :bar]), Format.new(17, 20, [:foo])])
end
it 'overlapping r2 on the right of r1' do
f1 = Format.new(10, 20, [:foo])
f2 = Format.new(18, 24, [:bar])
expect(Format.merge(f1, f2)).to \
eq([Format.new(10, 17, [:foo]), Format.new(18, 20, [:foo, :bar]), Format.new(21, 24, [:bar])])
end
it 'non overlapping r2 on the right of r1' do
f1 = Format.new(10, 20, [:foo])
f2 = Format.new(24, 26, [:bar])
expect(Format.merge(f1, f2)).to eq([Format.new(10, 20, [:foo]), Format.new(24, 26, [:bar])])
end
end # context 'moving r1 over r2'
end # describe '.merge'
end # describe Format
执行:
$ ruby -w lib/t.rb
#<struct Format from=1, to=3, attributes=[:foo]>
#<struct Format from=4, to=7, attributes=[:foo, :bar]>
#<struct Format from=8, to=9, attributes=[:foo]>
$ rspec -fd
Format
.merge
moving r1 over r2
non overlapping r1 on the left of r2
overlapping r1 on the left of r2
overlapping r1 inside r2
overlapping r1 on the right of r2
non overlapping r1 on the right of r2
moving r2 under r1
non overlapping r2 on the left of r1
overlapping r2 on the left of r1
overlapping r2 inside r1
overlapping r2 on the right of r1
non overlapping r2 on the right of r1
SplitRange
#partition
non overlapping
values of r1 smaller than r2 go to :ar1left
values of r2 smaller than r1 go to :br2left
values of r1 greater than r2 go to :dr1right
values of r2 greater than r1 go to :er2right
overlapping r2 from left to right
values of r2 smaller than r1 go to :br2left
values of r2 (left) common to r1 go to :ccommon
values of r2 (inside) common to r1 go to :ccommon
values of r2 (right) common to r1 go to :ccommon
values of r2 greater than r1 go to :er2right
overlapping r1 from left to right
values of r1 smaller than r2 go to :ar1left
values of r1 (left) common to r2 go to :ccommon
values of r1 (inside) common to r2 go to :ccommon
values of r1 (right) common to r2 go to :ccommon
values of r1 greater than r2 go to :dr1right
Finished in 0.0093 seconds (files took 0.11066 seconds to load)
24 examples, 0 failures
所有测试都是绿色的(Ruby 2.4,RSpec 3.6)。解决方案的结构
我已经定义了
Format = Struct.new(:range, :attributes)
类格式的实例,例如
Format.new(10..20, :BAR)
这里的@attribute
等于一个符号,但它可以是任何Ruby对象
然后,我将构造并返回格式
的实例数组,例如
Format.new(12..15, [:FOO, :BAR])
这意味着格式
的原始实例的@属性
等于:FOO
和:BAR
的值为@范围
,覆盖区间12..15
。此外,格式
的这些实例(其中@attributes
的值是一个数组)具有问题要求的非重叠范围。数组中元素的顺序(@attributes
的值)未指定
然后可以根据需要操纵@属性的值(数组)。例如,[3,5]
可能会转换为3 | 5
代码
def merge_formats(*formats)
fmod = formats.map { |e| Format.new(e.range, [e.attributes]) }.
sort_by { |e| e.range.begin }
a = []
while fmod.any?
b = []
while fmod.any? && (b.empty? || (fmod.first.range.begin == b.first.range.begin))
b << fmod.shift
end
next_end = b.min_by { |f| f.range.end }.range.end
next_end = [next_end, fmod.first.range.begin-1].min if fmod.any?
a << Format.new(b.first.range.begin..next_end, b.map { |f| f.attributes.first })
while b.any?
f = b.shift
fmod.unshift(Format.new(next_end+1..f.range.end, f.attributes)) if
f.range.end > next_end
end
end
a
end
def merge_格式(*格式)
fmod=formats.map{| e | Format.new(e.range,[e.attributes])。
按{| e | e.range.begin}对u进行排序
a=[]
而fmod有吗?
b=[]
而fmod.any?&(b.empty?| |(fmod.first.range.begin==b.first.range.begin))
b[#,,
# #,
# #]
f1=新格式(12..16,:条)
f2=新格式(10..11,:FOO)
合并_格式(f1、f2)
#=> [#,
# #]
f1=新格式(12..16,:条)
f2=新格式(10..20,:FOO)
f3=新格式(14..24,:BAZ)
f4=新格式(15..18,:QUX)
合并格式(f1、f2、f3、f4)
#=> [#,
# #,
# #,
# #,
# #,
# #,
# #]
解决方案的结构
我已经定义了
Format = Struct.new(:range, :attributes)
类格式的实例,例如
Format.new(10..20, :BAR)
这里的@attribute
等于一个符号,但它可以是任何Ruby对象
然后,我将构造并返回格式
的实例数组,例如
Format.new(12..15, [:FOO, :BAR])
这意味着格式
的原始实例的@属性
等于:FOO
和:BAR
的值为@范围
,覆盖区间12..15
。此外,格式
的这些实例(其中@attributes
的值是一个数组)具有问题要求的非重叠范围。数组中元素的顺序(@attributes
的值)未指定
然后可以根据需要操纵@属性的值(数组)。例如,[3,5]
可能会转换为3 | 5
代码
def merge_formats(*formats)
fmod = formats.map { |e| Format.new(e.range, [e.attributes]) }.
sort_by { |e| e.range.begin }
a = []
while fmod.any?
b = []
while fmod.any? && (b.empty? || (fmod.first.range.begin == b.first.range.begin))
b << fmod.shift
end
next_end = b.min_by { |f| f.range.end }.range.end
next_end = [next_end, fmod.first.range.begin-1].min if fmod.any?
a << Format.new(b.first.range.begin..next_end, b.map { |f| f.attributes.first })
while b.any?
f = b.shift
fmod.unshift(Format.new(next_end+1..f.range.end, f.attributes)) if
f.range.end > next_end
end
end
a
end
def merge_格式(*格式)
fmod=formats.map{| e | Format.new(e.range,[e.attributes])。
按{| e | e.range.begin}对u进行排序
a=[]
而fmod有吗?
b=[]
而fmod.any?&(b.empty?| |(fmod.first.range.begin==b.first.range.begin))
b[#,,
# #,
# #]
f1=新格式(12..16,:条)
f2=新格式(10..11,:FOO)
合并_格式(f1、f2)
#=> [#,
# #]
f1=新格式(12..16,:条)
f2=新格式(10..20,:FOO)
f3=新格式(14..24,:BAZ)
f4=新格式(15..18,:QUX)
合并格式(f1、f2、f3、f4)
#=> [#,
# #,
# #,
# #,
# #,
# #,
# #]
属性是常量(例如,FOO