【问题标题】:How do I implement an iterator for an existing singly linked list?如何为现有的单链表实现迭代器?
【发布时间】:2017-12-05 09:55:38
【问题描述】:

我正在尝试使用依赖注入为此单链表实现可迭代。我期待 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)

【问题讨论】:

    标签: scala


    【解决方案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 中编写代码,您不妨花几分钟时间了解该语言的用途。你会越来越欣赏它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-09
      • 2018-09-10
      • 2016-09-27
      • 1970-01-01
      • 2021-05-22
      相关资源
      最近更新 更多