Scala - reduce/foldLeft

4

我有一个嵌套的Mapm,如下所示:

m = Map("email" -> "a@b.com", "background" -> Map("language" -> "english"))

我有一个数组arr = Array("background","language")

如何使用foldLeft/reduce方法,在数组中查找字符串"english"。我尝试了以下方法:

arr.foldLeft(m) { (acc,x) => acc.get(x) }

但是我得到了以下错误信息:

<console>:10: error: type mismatch;
 found   : Option[java.lang.Object]
 required: scala.collection.immutable.Map[java.lang.String,java.lang.Object]
       arr.foldLeft(m) { (acc,x) => acc.get(x) }

没有可用的代码,但是这里有一个提示:m("background").asInstanceOf[Map[String,String]]("language")m("background") 是一个 java.lang.Object,你需要在某个地方进行额外的转换/匹配。然而,这种数据结构似乎有点奇怪,你应该考虑一些更面向对象的东西。 - Tomasz Nurkiewicz
2个回答

12
你应该注意类型。在这里,你从 m : Map[String, Any] 开始作为你的累加器。你与一个字符串x结合,并调用get,它返回一个Option[Object]。为了继续,你必须检查是否有值,检查这个值是否是一个Map,进行强制转换(由于类型擦除而不受检查,因此危险)。

我认为问题出在你的结构类型上,Map[String,Any] 不太准确地表示了你所拥有的。

假设你改用如下方式:

sealed trait Tree
case class Node(items: Map[String, Tree]) extends Tree
case class Leaf(s: String) extends Tree

您可以添加一些助手函数来使声明树更加容易。

object Tree {
  implicit def fromString(s: String) = Leaf(s)
  implicit def fromNamedString(nameAndValue: (String, String)) 
    = (nameAndValue._1, Leaf(nameAndValue._2))
}
object Node {
  def apply(items: (String, Tree)*) : Node = Node(Map(items: _*))
}

那么声明树就像你第一个版本一样容易,但类型更加精确。

m = Node("email" -> "a@b.com", "background" -> Node("language" -> "english"))

你可以在 trait Tree 中添加方法。

  def get(path: String*) : Option[Tree] = {
    if (path.isEmpty) Some(this)
    else this match {
      case Leaf(_) => None
      case Node(map) => map.get(path.head).flatMap(_.get(path.tail: _*))
    }
  }
  def getLeaf(path: String*): Option[String] 
    = get(path: _*).collect{case Leaf(s) =>s}

或者如果您更愿意使用折叠函数

  def get(path: String*) = path.foldLeft[Option[Tree]](Some(this)) {
    case (Some(Node(map)), p) => map.get(p)
    case _ => None
  }

4

将折叠作为嵌套地图的抽象并不被真正支持。此外,您的方法会使类型系统无法提供太多帮助。但是,如果您坚持,那么您需要一个递归函数:

def lookup(m: Map[String,Object], a: Array[String]): Option[String] = {
  if (a.length == 0) None
  else m.get(a(0)).flatMap(_ match {
    case mm: Map[_,_] => lookup(mm.asInstanceOf[Map[String,Object]],a.tail)
    case s: String if (a.length==1) => Some(s)
    case _ => None
  })
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接