【问题标题】:(Scala) Keeping the structure of a list in the output(Scala)在输出中保留列表的结构
【发布时间】:2016-02-10 01:16:01
【问题描述】:

我需要一点帮助。

我一直在研究 Scala 的功能方面。几乎所有的工作都在列表中,在大多数情况下,我可以解决问题,但我遇到了一个小死胡同。在某些问题中,我无法保留列表的原始形式(结构)。在输出中,所有嵌套列表都被展平。连接 ::: 使列表变平(即原始列表的一个元素)并附加 :: 给我一个编译错误,因为它需要通用 T 类型(不是列表)。

一个非常简单的例子,我想删除列表中与输入匹配的第一个元素:

def removeFirst[T](obj: T, list: List[T]): List[T] = {
  if (list isEmpty) Nil
  else{
  val fin: List[T] = list.head match {
    case headAsList: List[T] => if (containsWithNestedLists(obj, headAsList))
      removeFirst(obj, headAsList) ::: list.tail
    else headAsList ::: removeFirst(obj, list.tail)
    case _ => if (list.head == obj) list.tail
    else if (list.tail == List()) List(list.head)
    else list.head :: removeFirst(obj, list.tail)
  }
  fin
  }
}

对于一级深度列表,它可以正常工作,但是 出来的输出 removeFirst(1,List(List(1,2,3),1,2,3,4,5,6))List(2, 3, 1, 2, 3, 4, 5, 6),理想情况下我想要 List(List(2,3),1, 2,3,4,5,6)).

或者更具体的输入removeFirst(1,List(List(2,3,List()),List(1,2,3),1,2,3,4,5,6,List(2,3,List()))) 应该有输出=List(List(2,3,List()),List(2,3),1,2,3,4,5,6,List(1,2,3,List()))

我还发现删除通用 T 并在其位置使用 Any 可以解决问题,但我也知道 Any 是一个很大的 no-no 和临时解决一个永久性问题,因为它在其他功能中没有帮助。

据我所知,我还没有在互联网上看到有用的解决方案,所以我不得不问。我错过了什么,需要调试还是有其他功能可以帮助我?我最接近答案的方法是以某种方式使用 append ::,但我可能错了。

【问题讨论】:

  • 您能否提供一些示例来说明您在函数中要查找的内容?示例:double: Int => Int, double(5) === 10
  • 对于这个特定的功能,我确实有一个可以按我的意图工作的版本 def removeFirst(obj: Any, list: List[Any]): List[Any] = { if (list isEmpty) Nil else { val fin: List[Any] = list.head match { case headAsList: List[Any] => if (containsWithNestedLists(obj, headAsList)) removeFirst(obj, headAsList) :: list.tail else headAsList :: removeFirst(obj , list.tail) case _ => if (list.head == obj) list.tail else list.head :: removeFirst(obj, list.tail) } fin } } 如果你测试一些例子,你可能会得到什么..
  • 对于 cmets 中的多行代码,我礼貌地建议使用 gist
  • ..我打算作为输出,比我能解释的要好得多。但我遇到的主要问题是保持结构完整(使用泛型,而不是Any)。该功能可以删除元素的最后一次出现、所有出现、第 N 次出现、删除第 N 级列表中的所有元素、将元素添加到第 N 级列表等。编辑:抱歉代码,我是新手 :)

标签: list scala functional-programming nested-lists


【解决方案1】:

您的示例看起来好像您想从列表中删除某个元素(如果存在)。 对于平面列表,您可以更简单地执行此操作:

def removeFirst[T](obj: T, list: List[T]) = list match {
  case `obj` :: rest => rest
  case _ => list
}

这将执行以下操作:

> removeFirst(1, List(1, List(1,2,3)))
res57: List[Any] = List(List(1, 2, 3))
> removeFirst(1, List(2, List(1,2,3)))
res58: List[Any] = List(2, List(1, 2, 3))
> removeFirst(List(2,3), List(List(2,3), List(1,2,3)))
res59: List[List[Int]] = List(List(1, 2, 3))

但是,您似乎想对任意嵌套的列表执行此操作。这不是直接可能的,因为 Scala 无法表达它的确切类型。您需要的类型类似于

type NestedList[T] = List[T union NestedList[T]]

Scala 没有联合类型,你不能以这种方式进行递归定义,所以你也不能这样做:

type NestedList[T] = List[Either[T, NestedList[T]]] 

但是,如果您使用类而不是类型,则可以这样做:

case class NestedList[T](value: List[Either[T, NestedList[T]]])

现在你可以这样写你的算法了:

def removeFirst[T](obj: T, list: NestedList[T]): NestedList[T] = {
  val rest = list.value match {
    case Left(`obj`) :: tail => tail
    case __ => list.value
  }
  NestedList(rest.map {
    case Right(r) => Right(removeFirst(obj, r))
    case Left(r) => Left(r)
  })
}

你可以这样做:

> removeFirst(1, NestedList(List(Left(1), Left(2),  Right(NestedList(List(Left(1),Left(3)))))))
res71: NestedList[Int] = NestedList(List(Left(2), Right(NestedList(List(Left(3))))))

构建和分解这些结构当然有点麻烦。所以也许使用密封的抽象类和两个案例类而不是使用 Either 来构建一个合适的树类会更好。

【讨论】:

  • 谢谢你的回答,基本上这不能以纯粹的功能方式完成(它需要类),但我不得不承认,对于非常容易完成的事情来说,这似乎太复杂和不优雅其他函数式语言。
  • 您指的是哪种功能语言?我选择不尝试使这个更清洁,因为我不想定义我自己的树类型,但我可以很容易地做到这一点。在任何其他不允许递归类型定义的函数式语言中,这不会有太大的不同。而且您也误会了:这是纯粹的功能代码。我在这里使用的类只是一个简单的数据类型。
  • 对不起,是的,我混淆了我的意思。我的意思是,它(可能)不能纯粹使用 Scala 提供的 List 数据类型来完成,并且需要更改类型,而不是函数。至于哪些函数式语言,我主要会说 Common Lisp(在其中这种对列表的操作是最简单的),但我也能够在 Prolog(不是真正的函数式编程语言)和 Haskell 中解决这类问题.
  • 嗯,lisp 不是静态类型的。你可以在 Scala 中使用 Any。在 Haskell 中,我希望它非常相似,你会为它定义一个递归数据类型,不是吗?如果你只通过嵌套来定义它 Either 你会在 Haskell 中得到几乎相同的东西。无论如何,我真的看不出这是一个常见的问题。如果是这样,就会有一个嵌套列表数据类型。它很容易构建。
  • 对所有帐户都正确。问题不在于构建新的数据类型,我只是认为可以使用 Scala 和泛型中的基本 List 数据类型来解决此类问题,但我想它必须与 Any 一起完成。感谢您的帮助。
猜你喜欢
  • 1970-01-01
  • 2020-08-22
  • 2012-03-08
  • 2019-11-26
  • 1970-01-01
  • 2021-03-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多