Scala:递归地修改元素/列表的列表

时间:2011-06-18 09:34:17

标签: python scala recursion

我希望有人可以在Scala中为我提供一些基本的代码帮助。我用Python编写了一些演示代码。

考虑元素列表,其中元素可以包含整数或其他元素的列表。我想递归检查这个结构并修改它,同时保持整体结构。

为了在python中表示这一点,我将每个'element'设为一个带有一个键的字典('i'表示项目)。与该键对应的值是int或类似dicts的列表。因此,

lst = [{'i': 1}, {'i': 2}, {'i': [{'i': 5}, {'i': 6}]}, {'i': 3}]

def recurse(x):
  if isinstance(x, list):
    return [recurse(a) for a in x]
  else:
    if isinstance(x['i'], list):
      return dict(i=[recurse(a) for a in x['i']])
    else:
      return dict(i=(x['i'] + 1))

print "Input:"
for i in lst:
    print i
print "\nResult:\n%s" % recurse(lst)

>>>
Input:
{'i': 1}
{'i': 2}
{'i': [{'i': 5}, {'i': 6}]}
{'i': 3}

Result:    
[{'i': 2}, {'i': 3}, {'i': [{'i': 6}, {'i': 7}]}, {'i': 4}]

我理解这是一种奇怪的做法,但我提供的数据就是这样的结构。我认为我的问题是python允许你从同一个函数返回不同的类型,而我不相信Scala。

同样对于记录,Scala元素表示为Elem(4)或Elem(List(Elem(3)...,所以我假设模式匹配可以稍微进入。

3 个答案:

答案 0 :(得分:11)

我宁愿不称之为列表列表,因为这并不能说明这些列表包含的内容。结构是一棵树,更确切地说是一棵绿叶树,其中只有叶子中有数据。那将是:

sealed trait Tree[+A]
case class Node[+A](children: Tree[A]*) extends Tree[A]
case class Leaf[+A](value: A) extends Tree[A]

然后添加方法映射以对树中的每个值应用函数

sealed trait Tree[+A] {
  def map[B](f: A => B): Tree[B]
}
case class Node[+A](children: Tree[A]*) extends Tree[A] {
  def map[B](f : A => B) = Node(children.map(_.map(f)): _*)
}
case class Leaf[+A](value: A) extends Tree[A] {
  def map[B](f: A => B) = Leaf(f(value))
}

然后您的输入是:

val input = Node(Leaf(1), Leaf(2), Node(Leaf(5), Leaf(6)), Leaf(3))

如果你致电input.map(_ + 1),你会得到你的输出

由于varargs Tree [A] *,结果显示有点难看。您可以通过添加节点override def toString = "Node(" + children.mkString(", ") + ")"

来改进

您可能只想在一个地方使用该方法,无论是在课外,还是直接在树中。这里作为Tree中的方法

def map[B](f: A => B): Tree[B] = this match {
  case Node(children @ _*) => Node(children.map(_.map(f)): _*)
  case Leaf(v) => Leaf(f(v))
}

在Python中使用无类型的方式并不是很像scala,但可能已经完成了。

def recurse(x: Any) : Any = x match {
  case list : List[_] => list.map(recurse(_))
  case value : Int => value + 1
}

(将值直接放在列表中。带有键“i”的地图(字典)使其复杂化并强制接受编译器警告,因为我们必须强制执行无法检查的强制转换,即映射接受字符串as keys:case map:Map [String,_])


使用case Elem(content: Any)声音对我来说,与直接在List中放置值相比没有额外的安全性,同时更加冗长,并且没有将其称为树和区分节点和叶子的安全性和清晰度显而易见的。

答案 1 :(得分:3)

嗯,这里有些东西可行,但有点难看:

def make(o: Any) = Map('i' -> o) // m :: Any => Map[Char,Any]
val lst = List(make(1),make(2),make(List(make(5),make(6))),make(3)) // List[Any]

def recurce(x: Any): Any = x match {
  case l: List[_] => l map recurce
  case m: Map[Char,_] => val a = m('i')
    a match {
      case n: Int => make(n + 1)
      case l: List[_] => make(l map recurce)
    }
}

示例:

scala> recurce(lst)
res9: Any = List(Map((i,2)), Map((i,3)), Map((i,List(Map(i -> 6), Map(i -> 7)))), Map((i,4)))

答案 2 :(得分:3)

这个解决方案更安全,但不需要采用树的方式(这不是一个糟糕的解决方案,但有人已经成功了:)。它将是int或int列表的列表。因此,它只能有两个级别 - 如果你想要更多,可以做一棵树。

val lst = List(Left(1), Left(2), Right(List(5, 6)), Left(3))

def recurse(lst: List[Either[Int, List[Int]]]): List[Either[Int, List[Int]]] = lst match {
    case Nil => Nil
    case Left(n) :: tail => Left(n + 1) :: recurse(tail)
    case Right(l) :: tail => Right(l map (_ + 1)) :: recurse(tail)
}