【问题标题】:Break out of loop in scala while iterating on list迭代列表时在scala中跳出循环
【发布时间】:2015-07-02 10:28:21
【问题描述】:

我正在尝试解决一个问题。

问题: 给你一个由 4 种颜色的 N 个球组成的序列:红色、绿色、黄色和蓝色。当且仅当以下所有条件都为真时,该序列才充满颜色:

红球和绿球一样多。 黄球和蓝球一样多。 序列的每个前缀中红球和绿球的数量之差最多为 1。 序列的每个前缀中黄球和蓝球的数量之差最多为1。 你的任务是编写一个程序,对于给定的序列,如果它充满颜色则打印 True,否则打印 False。

我的解决方案:对于每个字符串,我生成所有可能的前缀和后缀来验证条件编号 3 和 4。但这需要更多时间。

我们可以遍历字符串并验证条件,而不是每次都生成前缀和验证条件。当条件不满足时,我想跳出循环。我无法以实用的方式获得它。有人可以帮助我如何实现它。

我的解决方案:

object Test {

    def main(args: Array[String]) {

      def isValidSequence(str: String) = {
        def isValidCondition(ch1:Char, ch2:Char, m:Map[Char, Int]):Boolean = m.getOrElse(ch1, 0) - m.getOrElse(ch2, 0) > 1
        def groupByChars(s:String) = s.groupBy(ch => ch).map(x => (x._1, x._2.length))
        def isValidPrefix(s:String):Boolean = (1 to s.length).exists(x => isValidCondition('R', 'G', groupByChars(s.take(x))))

        val x = groupByChars(str)
        lazy val cond1 = x.get('R') == x.get('G')
        lazy val cond2 = x.get('B') == x.get('Y')
        lazy val cond3 = isValidPrefix(str)
        lazy val cond4 = isValidPrefix(str.reverse)

        cond1 && cond2 && !cond3 && !cond4
      }
      def printBoolValue(b:Boolean) = if(b) println("True") else println("False")

      val in = io.Source.stdin.getLines()
      val inSize = in.take(1).next().toInt
      val strs = in.take(inSize)
      strs.map(isValidSequence(_)).foreach(printBoolValue)
    }
}

【问题讨论】:

  • 顺便说一句,您可能希望s.inits 在 isValidPrefix 中返回所有前缀。
  • 如果你使用.par,你会同时进行多次迭代,所以真的不清楚“跳出循环”是什么意思。
  • 为了让它更快,我使用了.par。不打算使用。将删除它
  • 您的代码还有其他问题。对于条件 4,您似乎正在检查后缀,而不是带有黄色/蓝色的前缀。对于 isValidCondition,您正在检查第一种颜色的 # 不比第二种颜色多 1,但条件是差异最多为一个(换句话说,检查需要是对称的)。重复的 groupByChars 有点矫枉过正,只需 s.count('R'==) 就可以了。以此类推。

标签: algorithm scala functional-programming


【解决方案1】:

作为另一个答案,这里有一个更直接的解决方案,它可以缩短差异检查。

val valid = List("RGYBRGYB")      
val invalid = List("RGYBR", "RGYBY", "RGYBY", "RGYYB")

def checkBalls(s:String) = {
def differences(s:String, a:Char, b:Char) = {
  def differenceHelp(s:String, a:Char, b:Char, current:Int):Boolean = {
      if (current < -1 || current > 1) false
      else if (s.length == 0) true
      else differenceHelp(s.tail, a, b,
           if (s.head == a) current + 1 else if (s.head == b) current - 1 else current)
    }

  differenceHelp(s, a, b, 0)
}

lazy val cond1 = s.count('R'==) == s.count('G'==)
lazy val cond2 = s.count('Y'==) == s.count('B'==)
lazy val cond3 = differences(s, 'R', 'G')
lazy val cond4 = differences(s, 'Y', 'B')
cond1 && cond2 && cond3 && cond4
} 
valid.forall(checkBalls(_))                       //> res0: Boolean = true
invalid.forall(!checkBalls(_))                    //> res1: Boolean = true

编辑:作为优化,我们可以将 cond1 作为 cond3 的一部分(并将 cond2 作为 cond4 的一部分)。当且仅当字符串末尾的计数为 0 时,每个都有相等的数量。我们可以检查差异并仅在这种情况下返回true。所以这给了

def checkBalls(s:String) = {
def differences(s:String, a:Char, b:Char) = {
  def differenceHelp(s:String, a:Char, b:Char, current:Int):Boolean = {
      if (current < -1 || current > 1) false
      else if (s.length == 0) (count == 0) // <- this line changed
      else differenceHelp(s.tail, a, b,
           if (s.head == a) current + 1 else if (s.head == b) current - 1 else current)
    }

  differenceHelp(s, a, b, 0)
}

lazy val cond3 = differences(s, 'R', 'G')
lazy val cond4 = differences(s, 'Y', 'B')
cond3 && cond4
} 

它像以前的版本一样通过了测试。通过一次调用differences 进行 R/G 和 Y/B 检查可以稍微加快速度,但这看起来有点过度专业化。

【讨论】:

    【解决方案2】:

    如果需要,这里有一个使用流的解决方案。

    代码:-

    object RGYB extends App {
    
    val validPattern = List(
            "RG","RYBG","RYGB","RBGY",
            "GR","GYBR","GYRB","GBRY",
            "YB","YRGB","YRBG","YGRB",
            "BY","BRGY","BRYG","BGYR"
            )
    
            val pattern ="RGRG"
            pattern.sliding(4).foreach { x1 =>
            val count = validPattern.filter { p1 => {
                x1.equalsIgnoreCase(p1)
            } 
            }.size
            if(count<1)
            {
                x1.sliding(2).foreach {
                    x2=>
                    val counter  = validPattern.filter { p2 => {
                        x2.equalsIgnoreCase(p2)
                    } 
                    }.size
                    if(counter<1)
                    {
                        println("false !! not valid due to "+x2);
                        System.exit(0)
                    }
                }
                println("false !! not valid due to "+x1);
                System.exit(0)
            }
    }
    
    println("True !!"+pattern+" Is a valid string pattern")
    }
    

    【讨论】:

      【解决方案3】:

      所以,诀窍是首先检查最长的前缀。如果失败,我们就完成了。否则,我们采用下一个最长的前缀并递归。如果我们得到空字符串,它会传递所有前缀,因此它是有效的。

      def isValidPrefix(s: String): Boolean = 
      if (s.length == 0)
        true
      else if (!isValidCondition('R', 'G', groupByChars(s)))
        false
      else isValidPrefix(s.init)
      

      【讨论】:

      • 虽然一种更快的方法是遍历字符串(仅一次),为每个红色计数 1,为每个绿色计数 -1,并确保总数的绝对值不超过 1 .
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-15
      • 2019-05-17
      • 2011-02-14
      • 1970-01-01
      相关资源
      最近更新 更多