【问题标题】:How to check validity of brackets in wrong order如何以错误的顺序检查括号的有效性
【发布时间】:2018-11-08 13:27:31
【问题描述】:

我必须编写一个接受字符串的方法,如果括号不为空,则返回true,括号和大括号正确关闭否则返回false。

这是我得到的:

  def empty_brackets?(string)
    %w[ () [] {} ].none?(&string.method(:include?))
  end

  def valid_string?(string)
    match_count = 0

    string.each_char do |c|
      match_count += 1 if [ '[', '{', '(' ].include?(c)
      match_count -= 1 if [ ']', '}', ')' ].include?(c)
    end
    match_count == 0
  end

我认为我的valid_string? 方法不是很可靠,但最重要的是它没有通过括号顺序错误的测试,例如)somebrackets(。您能告诉我如何解决它吗?

【问题讨论】:

    标签: ruby-on-rails ruby


    【解决方案1】:

    我的尝试,将所有左括号推入数组中,如果匹配则弹出右括号:

    BRACKET_PAIRS = { '[' => ']', '{' => '}', '(' => ')' }
    
    def valid?(string)
      string.each_char.with_object([]) do |char, bracket_stack|
        if BRACKET_PAIRS.keys.include? char
          bracket_stack << char
        elsif BRACKET_PAIRS.values.include? char
          if char != BRACKET_PAIRS[bracket_stack.last]
            return false
          else
            bracket_stack.pop
          end
        end
      end.empty?
    end
    

    【讨论】:

      【解决方案2】:

      这是一个非常有效的版本。它使用正则表达式,并将所有大括号 ({}[]()) 拉出到一个小数组中,然后继续摆弄你的对,直到没有任何东西。第二次它找到一个不匹配的对,它退出并返回 false。它也不会将字符串拆分为 char 数组,因为这将使用两倍的内存(一次用于整个字符串,一次用于拆分为单个字符的字符串)。

      BRACKET_PAIRS = { '{' => '}', '[' => ']', '(' => ')' }
      BRACKET_REGEX = /#{BRACKET_PAIRS.to_a.flatten.map { |v| Regexp.escape(v) }.join('|')}/
      
      def valid?(string)
        brackets = string.scan(BRACKET_REGEX)
        while brackets.size > 0
          first = brackets.shift
          last = brackets.pop
          return(false) unless BRACKET_PAIRS[first] == last
        end
      
        return(true)
      end
      

      如果根本没有大括号,此代码将返回 true。

      如果您不喜欢这样,请这样做:

      BRACKET_PAIRS = { '{' => '}', '[' => ']', '(' => ')' }
      BRACKET_REGEX = /#{BRACKET_PAIRS.to_a.flatten.map { |v| Regexp.escape(v) }.join('|')}/
      
      def valid?(string)
        brackets = string.scan(BRACKET_REGEX)
        return(false) unless brackets.size > 0 # this line is added
      
        while brackets.size > 0
          first = brackets.shift
          last = brackets.pop
          return(false) unless BRACKET_PAIRS[first] == last
        end
      
        return(true)
      end
      

      附带说明,您需要避免像在示例中那样在循环中创建数组:

      string.each_char do |c|
        match_count += 1 if [ '[', '{', '(' ].include?(c)
        match_count -= 1 if [ ']', '}', ')' ].include?(c)
      end
      

      此代码将在您的 string 变量中创建两个数组和每个字符 6 个字符串。这是非常低效的,您将使用比必要更多的 RAM 和 CPU。您至少希望将这两个数组放在循环之外,因为它们不会改变,理想情况下甚至可以将它们设为常量,这样您甚至不会在每次调用该方法时都将它们设为常量。当您的程序启动并永远使用它时,将它们设为一个。像这样的小事情实际上会产生很大的不同,尤其是在循环中使用时。

      【讨论】:

      • 关于不将字符串拆分为数组的好方法,我倾向于忘记使用#each_char 而不是#chars
      • @MarcinKołodziej 我实际上也没有在我的回答中使用each_char,尽管您完全正确,使用each_charchars.each 更有效,因为它不会产生新数组。我最近在哈希中也了解到了each_keyeach_value,这再次与keys.eachvalues.each 相比很好,因为您再次没有制作毫无意义的数组。您可以节省垃圾收集器的工作越多越好。特别是如果您的 Ruby 版本较旧。
      • 你没有,但我做到了,我回去更正了。无论如何,很高兴看到有人关心不在 Ruby 中实例化多个临时数据结构。
      • RuboCop 确实不错,但有时要小心,尤其是在性能方面,会想到这个特殊问题:github.com/rubocop-hq/rubocop/issues/3633
      • 是的,我们有时肯定会与“风格”警察战斗,尽管我没有意识到 .zero?== 0 慢。感谢您的信息!
      【解决方案3】:
      CLOSE_TO_OPEN = { ']'=>'[', '}'=>'{', ')'=>'(' }
      
      def valid?(str)
        str.each_char.with_object([]) do |c,stack|
          case c
          when '[', '{', '('
            stack << c
          when ']', '}', ')'
            return false unless stack.pop == CLOSE_TO_OPEN[c]
          end
        end.empty?
      end
      
      valid? "[a]{b[c{d}e]fg}" #=> true
      valid? "[a]{b[c{d]e}fg}" #=> false
      

      通过添加一些puts语句可以看到方法的行为。

      def valid?(str)
        str.each_char.with_object([]) do |c,stack|
          puts "c=#{c}, stack=#{stack}"
          case c
          when '[', '{', '('
            stack << c
            puts "  stack after 'stack << #{c}' = #{stack}"
          when ']', '}', ')'
            print "  stack.pop (#{stack.last||'nil'})==CLOSE_TO_OPEN[#{c}] " +
                  "(#{CLOSE_TO_OPEN[c]})=>"
            puts stack.last == CLOSE_TO_OPEN[c] ? "true, so continue" :
              "false, so return false"
            return false unless stack.pop == CLOSE_TO_OPEN[c]
          end
        end.tap { |stack| puts "At end, '#{stack}.empty?` (#{stack.empty?})" }.empty?
      end
      

      valid? "[a]{b[c{d}e]fg}"
      c=[, stack=[]
        stack after 'stack << [' = ["["]
      c=a, stack=["["]
      c=], stack=["["]
        stack.pop ([)==CLOSE_TO_OPEN[]] ([)=>true, so continue
      c={, stack=[]
        stack after 'stack << {' = ["{"]
      c=b, stack=["{"]
      c=[, stack=["{"]
        stack after 'stack << [' = ["{", "["]
      c=c, stack=["{", "["]
      c={, stack=["{", "["]
        stack after 'stack << {' = ["{", "[", "{"]
      c=d, stack=["{", "[", "{"]
      c=}, stack=["{", "[", "{"]
        stack.pop ({)==CLOSE_TO_OPEN[}] ({)=>true, so continue
      c=e, stack=["{", "["]
      c=], stack=["{", "["]
        stack.pop ([)==CLOSE_TO_OPEN[]] ([)=>true, so continue
      c=f, stack=["{"]
      c=g, stack=["{"]
      c=}, stack=["{"]
        stack.pop ({)==CLOSE_TO_OPEN[}] ({)=>true, so continue
      At end, '[].empty?` (true)
        #=> true
      

      valid? "[a]{b[c{d]e}fg}"
      c=[, stack=[]
        stack after 'stack << [' = ["["]
      c=a, stack=["["]
      c=], stack=["["]
        stack.pop ([)==CLOSE_TO_OPEN[]] ([)=>true, so continue
      c={, stack=[]
        stack after 'stack << {' = ["{"]
      c=b, stack=["{"]
      c=[, stack=["{"]
        stack after 'stack << [' = ["{", "["]
      c=c, stack=["{", "["]
      c={, stack=["{", "["]
        stack after 'stack << {' = ["{", "[", "{"]
      c=d, stack=["{", "[", "{"]
      c=], stack=["{", "[", "{"]
        stack.pop ({)==CLOSE_TO_OPEN[]] ([)=>false, so return false
        #=> false
      

      【讨论】:

        猜你喜欢
        • 2014-01-11
        • 1970-01-01
        • 2011-01-31
        • 2019-07-14
        • 1970-01-01
        • 2023-03-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多