假设我们在Java中有以下结构:
class List {
// we're immutable
final List next;
final int value;
public List(int value, List next) {
this.next = next;
this.value = value;
}
}
Scala内置了对不可变单链表的支持。它将是:
val l = List(1, 2, 3) // immutable
那么,是否有可能在这种列表中创建一个循环(不可变的单链表)。按周期,我的意思是:
答案 0 :(得分:11)
您应该使用lazy
集合来创建无限集合。您可以使用Stream
:
def loopStream: Stream[Int] = {
lazy val loop: Stream[Int] = 3 #:: 4 #:: 5 #:: 6 #:: 7 #:: 8 #:: loop
1 #:: 2 #:: loop
}
loopStream.take(22).toList
// List(1, 2, 3, 4, 5, 6, 7, 8, 3, 4, 5, 6, 7, 8, 3, 4, 5, 6, 7, 8, 3, 4)
答案 1 :(得分:2)
简单地通过设计是不可能的。 Scala的列表是不可变的,你可以对现有列表做的就是删除它的头(它产生尾部,换句话说是一个本身不可变的列表)或者预先添加一个元素,从而产生一个新的不可变列表。 你可以获取列表的元素并重新添加它,但这只会创建一个更长的列表,其中两个元素是同一个实例,这不会创建一个循环。
您还可以有两个共享部分(或全部)尾部的列表,但您仍然无法创建循环。 这只是因为要创建一个更长的列表,您所能做的只是预先存在的列表,这意味着在列表头节点是(按设计)尾部节点<的旧实例/ em>的。情况总是如此。由此可见,循环将是一个矛盾。
所以简短的回答是否定的,你不能用scala(不可变)列表创建循环。
除此之外,Stream
s(如senia的答案所示)而不是List
s(尽管两者都是不可变集合)的原因是{{1}添加一个关键成分:懒惰。流节点是懒惰地构造的(节点基本上将 thumb 存储到实际节点的内容中),这允许稍后的节点引用较早(并且尚未构造的)节点,从而允许循环。
答案 2 :(得分:0)
另一种方法是在构造函数执行完毕之前使用'this'指针已存在的事实,并且作为构造函数的一部分,您可以调用作为参数传入的函数(而不是传递)直接引用'next'节点)将'this'指针传递给该函数。该函数负责根据传入的节点值生成对下一个节点的引用。一个粗略的例子如下:
class ListNode(val value: Int, genNext: ListNode => ListNode) {
val next = genNext(this)
}
def gen3(prev: ListNode): ListNode = new ListNode(3, any => prev)
def gen2(prev: ListNode): ListNode = new ListNode(2, gen3 _)
val loopingList = new ListNode(1, gen2 _) // will have 1 -> 2 -> 3 -> (back to) 2
当然,如果你没有对这个构造函数被调用的时间进行足够的控制,那么可以将所有种类的垃圾作为genNext函数传入...
答案 3 :(得分:0)
如前所述,如果没有某种懒惰,就无法创建一个不可变的循环结构。但是,您可以利用scalaz的Name。它有几个实现,其中Need
提供了延迟评估的值。这允许我们定义next
可以延期的链表:
import scalaz._
import scalaz.Scalaz._
final case class LList[T](value: T, next: Name[LList[T]])
extends Traversable[T]
{
@annotation.tailrec
override def foreach[U](f: T => U) {
f(value);
next.value.foreach(f);
}
}
这允许我们定义像cycle
这样的函数,它使用Need
推迟对最后一个循环引用的评估。所有其他引用都不需要是惰性的,所以我们只需将它们包装在Value
中(不会推迟任何内容)。
object LList {
def cycle[T](xs: T*): LList[T] = {
lazy val r: LList[T] =
xs.foldRight[Name[LList[T]]](Need(r))(
(x, r) => Value(LList(x,r))
).value;
r;
}
}