如何为现有的单链表实现迭代器?

时间:2017-07-01 16:56:52

标签: scala

我正在尝试使用依赖注入为这个单链表实现一个iterable。我期望hasNext返回true而next()返回第一个元素。但它出错了。我在这里做错了什么?

提前感谢您的帮助。

// Definition for singly-linked list from codefights:
class ListNode[T](x : T) {
  var value: T = x
  var next: Option[ListNode[T]] = None
}
//This class will provide iterator for the above list.    
implicit class ln(lns: Option[ListNode[Int]]) extends Iterable[Option[ListNode[Int]]] {

  var ptr = lns  //temporary variable to store the state of iterator

  override def iterator = new Iterator[Option[ListNode[Int]]] {
    //if the current node pointed to is None, there are no elements.
    override def hasNext = ptr match {case None=> false; case _ => true}

    //store the current ptr to temp variable and return it.
    override def next = { var tmp = ptr; ptr = ptr.get.next; tmp }
  }

  //defined +: to perform tests
  def +:(that: Option[ListNode[Int]]) = that match {
    case None => lns
    case _ => that.get.next = lns; that
  }


}

//create 3 nodes that will be linked in next step
var a = new ListNode(1)
var b = new ListNode(2)
var c = new ListNode(3)


//link the 3 nodes
val xx : Option[ListNode[Int]]= Some(c) +: Some(b) +: Some(a)

//I know this is not dependency injection. But I wanted to debug the issue.
val ax: ln = new ln(xx)
//ax.size

//checking the iterator manually
ax.iterator.hasNext
ax.iterator.next()

Output:

> defined class ListNode
> defined class ln
> a: ListNode[Int] = ListNode@79c41e43 b: ListNode[Int] =
> ListNode@5fbdb591 c: ListNode[Int] = ListNode@a8e723
> 
> xx: Option[ListNode[Int]] = Some(A$A131$A$A131$ListNode@a8e723)
> 
> ax: ln = A.A131.A.A131(Some(A$A131$A$A131$ListNode@a8e723),
> Some(A$A131$A$A131$ListNode@5fbdb591),
> Some(A$A131$A$A131$ListNode@79c41e43))
> 
> res0: Boolean = false java.util.NoSuchElementException: None.get  at
> scala.None$.get(palindrom.sc:345)     at
> scala.None$.get(palindrom.sc:343)     at
> #worksheet#.ln$$anon$1.next(palindrom.sc:23)  at 

#worksheet#.ln$$anon$1.next(palindrom.sc:20)    at #worksheet#.get$$instance$$res1(palindrom.sc:39)     at #worksheet#.#worksheet#(palindrom.sc:84)

更新: 我不允许修改类ListNode。我之前没有提到过。我的错。我刚刚在下面添加了toString用于清晰打印。

但我根据@Dima建议修改了其余的代码。我很惊讶!!因为,我可以像常规列表一样使用地图。非常感谢!!

class ListNode[T](x : T) {
  var value: T = x
  var next: Option[ListNode[T]] = None
  override def toString = x.toString
}



implicit class ln(val lns: ListNode[Int]) extends  Iterable[Int] {

  override def iterator = Iterator.iterate(Option(lns))(_.flatMap(_.next))
    .takeWhile(_.nonEmpty)
    .flatten
    .map(_.value)

  def +:(that: Int) : ListNode[Int] = {
    val newNode = new ListNode(that)
      newNode.next = Some(lns)
      newNode}


}


val xx : ListNode[Int]= 1 +: 2 +: new ListNode(3)

xx.foreach(println)

xx.map( _ +1)
  

输出

     

xx:ListNode [Int] = 1

     

res0:Iterable [Int] = List(2,3,4)

1 个答案:

答案 0 :(得分:1)

不要这样做。只是不要。对不起,我开始打字几次试图解释它有什么问题,但我不能......它所有错误。

回到绘图板。 首先,废弃可变状态。你不需要它。现在,只是假装scala中没有var

case class ListNode[T](value: T, next: Option[ListNode[T]] = None) {
   def +:(head: T) = ListNode(head, Some(this))
}

现在。看起来很奇怪,你的迭代器迭代Option[ListNode[T]]而不是T,或者至少是ListNode[T]。 将其包装到Option似乎完全没用。 这会迭代T,因为它似乎最明智,如果您想要ListNode[T],请删除最后一个.map。 如果您坚持使用Option[ListNode[T]],请同时删除.map.flatten

object ListNode {
   implicit class Iterable[T](val node: ListNode[T]) extends AnyVal {
      def iterator = Iterator
         .iterate(Option(node))(_.flatMap(_.next))
         .takeWhile(_.nonEmpty)
         .flatten
         .map(_.value)
   }
}

现在您可以执行以下操作:

 (1 +: 2 +: ListNode(3))
   .iterator
   .toList // returns List(1,2,3)

就是这样。如果您要在scala中编写代码,您可能需要花几分钟时间来学习如何使用该语言。你会逐渐欣赏它。