如何创建各种“嵌套分割”

时间:2010-07-12 00:46:01

标签: ruby string

这看起来应该相当简单,但出于某种原因,我想不出正确的方法:

我的字符串h看起来像one(two(three four) five six) seven

我想将其拆分为一个哈希数组,以便输出类似于

{'one' => 
       {'two' => 
              {'three' => nil, 'four' => nil},
        'five'=>nil, 'six'=>nil
       }, 'seven'=>nil}

我们可以假设括号数量相等。

有没有简单的方法可以做到这一点?在一种鼓励使用外观的语言中,这将是相对简单的;我不认为我已经使用Ruby足够长的时间来感受Ruby做这种问题的方式了。

谢谢!

3 个答案:

答案 0 :(得分:1)

没有任何背景,很难给你任何可能在更一般情况下工作的东西。

此代码适用于您的特定示例,只使用正则表达式和eval,但我不想在实践中使用这样的代码。

对于更复杂的字符串解析,您可以使用http://treetop.rubyforge.org/或类似方法进行调查。但是,你正在进入写自己语言的领域。

h = "one(two(three four) five six) seven"

s = h.tr "()", "{}"
s = "{#{s}}"
s = s.gsub /(\w+)/, '"\1" =>'
s = s.gsub /\>\s\"+/, '> nil, "'
s = s.gsub /\>\}+/, '> nil },'
s = s[0..-2]

puts h
r = eval(s)
puts r.inspect
puts r.class.name

是否有一些具体的例子,你试图得到答案?

另外,我可以补充说,如果你能够提供更自然地映射到Ruby的字符串,你可以让你的生活更轻松。显然这取决于你是否掌握了来源。

答案 1 :(得分:1)

这是一个递归解决方案:

def f(str)
  parts = ['']
  nesting_level = 0
  str.split('').each do |c|
    if c != ' ' or nesting_level > 0
      parts.last << c
    end
    if [' ', ')'].include?(c) and nesting_level == 0
      parts << ''
    end
    case c
    when '('
      nesting_level += 1
    when ')'
      nesting_level -= 1
    end
  end
  hash = {}
  parts.each do |seg|
    unless seg.include?('(')
      hash[seg] = nil
    else
      key = seg[/^[^\(\) ]+/]
      value = seg[(key.length + 1)..(seg.length - 2)].to_s
      hash[key] = f value
    end
  end
  hash
end

f 'one(two(three four) five six) seven' #=> {"one"=>{"two"=>{"three"=>nil, "four"=>nil}, "five"=>nil, "six"=>nil}, "seven"=>nil}

答案 2 :(得分:0)

使用嵌套的正则表达式组。不像解析器/扫描器那样高效,因为这将在递归调用期间重新扫描子组。

def hash_from_group(str)
    ret = {}
    str.scan(/
        (?<key_name>\w+)
        (?<paren_subgroup>
            \(
                (?:
                    [^()]
                    |
                    \g<paren_subgroup>
                )*  # * or + here, depending on whether empty parens are allowed, e.g. foo(bar())
            \)
        )? # paren_subgroup optional
    /x) do
        md = $~
        key,value = md[:key_name], md[:paren_subgroup]
        ret[key] = value ? hash_from_group(value) : nil
    end
    ret
end


p hash_from_group('one(two(three four) five six) seven') # => {"one"=>{"two"=>{"three"=>nil, "four"=>nil}, "five"=>nil, "six"=>nil}, "seven"=>nil}