【问题标题】:Need a guide to tweak this nested for-loop in functional way需要一个指南来以功能方式调整这个嵌套的 for 循环
【发布时间】:2015-01-12 16:16:03
【问题描述】:

我正在处理这个 scala 代码:

  case class M(a: String, b: Int)

  val mm1 = List(Map("a" -> "001", "b" -> 12), Map("a" -> "002", "b" -> 25), Map("a" -> "003", "b" -> 100))
  val mm2 = List(M("001", 12), M("004", 99), M("003", 100))

  def validate(mm1: List[Map[String, Any]], mm2: List[M]): Boolean = {

    for (m1 <- mm1) {
      var found = false
      val a = m1("a")

      // nested loop: iterate mm2
      breakable {
        for (m2 <- mm2) {
          if (a == m2.a) {
            assert(m1("b") == m2.b)
            found = true
            break  // if found just break
          }
        }
      }

      // All of elements in mm1 must exist in mm2.
      // We will know this if "found" variable is set to true.
      assert(found == true)
    }
    true
  }

基本上validate 方法只是确保 mm1 中的所有值都存在于 mm2 中(基于“文本”键/属性),反之则没有必要。然后还要确保 mm1 和 mm2 的每个元素的属性值相等。

我正在尝试将validate 方法转换为函数式编程风格。但是不知道如何处理mm1中所有的检查项都应该在mm2中。

这是我尝试理解的内容:

  def validate2(mm1: List[Map[String, Any]], mm2: List[M]): Boolean = {        
    for {
      m1 <- mm1
      a = m1("a")
      m2 <- mm2
    } {
      if (a == m2.a) {
        assert(m1("b") == m2.b)
      }
    }
    true
  }

不是很好,因为我不能将var found = false 放在 for 理解中。所以我无法检查 mm1 中的所有项目是否存在于 mm2 中。

任何人都可以提供帮助或有更好的想法(可能使用递归或根本不使用var found)?

【问题讨论】:

    标签: scala functional-programming


    【解决方案1】:

    这可以用forall(它接受一个谓词并在集合中的所有元素都满足它时返回true)和exists(它也接受一个谓词,但只需要一场比赛):

    def validate(mm1: List[Map[String, Any]], mm2: List[M]) = mm1.forall { m1 =>
      mm2.exists {
        case M(a, b) => a == m1("a") && b == m1("b")
      }
    }
    

    请注意,如果我看到 validate(...) 方法返回时,我并没有使用 assert 来失败并出现无效输入异常——这本质上是不起作用的,并且它破坏了方法类型签名的隐式契约。一个布尔值,如果参数有效,我将假设该值为真,否则为假。您可以使用一些异常和功能组合器的混合来实现与原始版本相同的行为,但我强烈建议不要这样做。

    我还建议避免在地图上进行可能失败的查找(例如,使用 getgetOrElse 并明确处理错误的可能性),而不是使用 Map[String, Any]

    【讨论】:

    • OP的代码是一样的。问题是关于以功能样式重写,而不是优化。
    • Op 的代码也总是返回 true ......你的不是 ;)
    • 那里“真”的原因是因为代码是要在 Specs2 中运行的。在 Specs2 中,您必须返回一个结果(例如,如果测试成功,则返回布尔值 true)。
    • @suud 然后也许使用must beTrue匹配器
    【解决方案2】:

    考虑这样的事情,也许:

    val ms = mm2.map(m => m.a -> m.b).toMap
    mm1.foreach { m => 
        assert(m.get(a).flatMap(ms.get).exists(_ == m("b")))
    }
    
    true
    

    【讨论】:

    • 抱歉,我不确定您的目标到底是什么,但我认为它不起作用,并且使用 "" 作为某种哨兵值而不是 明确表示你正在做的是一个坏主意。
    • 好点。删除了哨兵......为什么说它不起作用呢?我很确定确实如此。
    • 它总是返回 true,一方面。
    • 是的,除非断言失败。就像在 OP 的要求中一样……您的版本在这方面实际上是错误的;)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-10
    • 2022-01-10
    • 2019-01-08
    • 2016-05-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多