在Scala中平铺任意嵌套的List

时间:2013-08-26 22:31:00

标签: scala

  def flatten(l: List[_]): List[_] = {
    def iflatten(l: List[_], ret: List[_]): List[_] = l match {
      case Nil => ret
      case h :: Nil =>
        if( h.isInstanceOf[List[_]]) { iflatten(h, ret) }
        else {
          l.head :: ret
          iflatten(l.tail, ret)
        }
    }
  }

我知道有多种方法可以做到这一点,而且我不能100%确定我的方式是正确的。我想测试一下,但我遇到的一个问题是在我调用的第二个case语句中:

... { iflatten(h, ret) }

我收到编译错误:

error: type mismatch;
found   : Unit
required: List[?]

我正在尝试解决这些类型问题,以了解有关类型系统的更多信息,因为它与我过去使用的不同。关于编译器为什么抱怨的任何建议都将不胜感激。

5 个答案:

答案 0 :(得分:9)

我没有得到与iflatten(h,ret)相关的错误。 我得到了found : Unit; required : List[?]错误,但是它指的是你没有调用iflatten自己扁平化:在定义之后,你需要在flatten定义的末尾调用函数。

def flatten(l: List[_]): List[_] = {
  def iflatten(l: List[_], ret: List[_]): List[_] = l match {
    case Nil => ret
    case (h:List[_]) :: tail => iflatten(tail,iflatten(h,ret))
    case h :: tail => iflatten(tail,h::ret)
  }
  iflatten(l,List()).reverse
}

至于代码本身,您可以(并且应该)在匹配时验证类型。 另请注意,case h :: Nil仅匹配1长度列表。

对于算法,您需要在其自身内部调用iflatten(这是任意嵌套发生的地方)。

答案 1 :(得分:3)

我认为你只是错过了演员。

if( h.isInstanceOf[List[_]]) { iflatten(h.asInstanceOf[List[_]], ret) }

或者:模式匹配会更漂亮。

h match {
  case hList: List[_] => 
    iflatten(hList, ret)
  case _ => 
    l.head :: ret
    iflatten(l.tail, ret)
}

(警告:这只是我的头脑,我没有通过编译器放任何东西)

编辑 - Marth将其合并到之前的模式匹配中的解决方案看起来比我的好。

答案 2 :(得分:3)

实际上,我怀疑问题在于你定义内部方法iflatten,但从不调用它,因此外部方法flatten不会返回任何内容(即默认为返回类型的Unit,与列出[_]的返回类型。

尝试添加以下内容作为外部展平方法的最后一行:

iflatten(l, Nil)

除此之外,您的代码还有其他各种问题,例如不处理所有匹配情况:您处理包含一个元素的列表 - h :: Nil - 但不是几个元素。您可能需要h :: theRest之类的内容,然后在某处使用theRest - 可能作为递归调用的ret参数。

你也使用支票h.isInstanceOf[List[_]](通常使用isInstanceOf是scala中的代码异味)但是然后尝试递归地将h传递给iflatten而不将其转换为List[_](对于例如,在@ ChristopherMartin的回答中,虽然使用asInstanceOf是一个更大的代码味道)。 @Marth的回答给出了一个如何避免这些显式类型检查的好例子。

答案 3 :(得分:2)

答案 4 :(得分:1)

抱歉,这段代码非常复杂。我试图简化它并得到这个解决方案:

    scala> :paste
    // Entering paste mode (ctrl-D to finish)

        def flatten(l : List[_]) : List[_] = l flatMap {
          case l1 : List[_] => flatten(l1)
          case otherwise => List(otherwise)
        }


    // Exiting paste mode, now interpreting.

    flatten: (l: List[_])List[_]

    scala> flatten(List(1,2,3))
    res3: List[Any] = List(1, 2, 3)

    scala> flatten(List(1,2,List(3,4)))
    res4: List[Any] = List(1, 2, 3, 4)

    scala> flatten(List(List(1,List(2),3),4,List(4,5)))
    res5: List[Any] = List(1, 2, 3, 4, 4, 5)

修复代码(添加对iflat的调用)后,我做了以下重构:

  1. 删除了内部方法
  2. 使用内置的flatMap进行迭代(因此可以消除或简化某些case表达式)
  3. 用类型警卫替换instanceOf
  4. 我认为更简单的解决方案是使用shapeless library(提示:查找“样板”部分)。