是否可以在不可变链表中创建一个循环?

时间:2013-08-14 10:02:48

标签: java algorithm scala linked-list

假设我们在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

那么,是否有可能在这种列表中创建一个循环(不可变的单链表)。按周期,我的意思是:

enter image description here

4 个答案:

答案 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;
  }
}