Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby 如何将重叠结构与其中的范围合并_Ruby - Fatal编程技术网

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