Subscribed unsubscribe Subscribe Subscribe

Observable と Undoable を使ってみる。

Scala Observable Collection

はっきりしたことは分からないけど、なんかバグっぽい動きをする。

import scala.collection.mutable._

object test extends Application {
    // XXX: なんとかならないのかこれ
    class ObservableArrayBuffer[T] extends ArrayBuffer[T]
          with ObservableBuffer[T, ObservableArrayBuffer[T]]
    type Act[T] = Message[(Location, T)] with Undoable
    type Sub[T] = Subscriber[Act[T], ObservableArrayBuffer[T]]

    val oab = new ObservableArrayBuffer[Int]
    val undoQueue = new ArrayBuffer[Act[Int]]

    oab.subscribe(new Sub[Int] {
        def notify(pub: ObservableArrayBuffer[Int], ev: Act[Int]): Unit = {
            println(ev)
            undoQueue += ev
        }
    })

    // 要素を追加したりする
    oab.insert(0, 0)
    oab.insertAll(0, List(1, 2))
    oab.insert(0, 3)
    oab -= 1
    println(oab)

    // キューの中身を一覧
    println("--")
    for (idx <- 0 until undoQueue.length reverse) 
        println(idx, undoQueue(idx))

    // undo を適用
    println("--")
    for (idx <- 0 until undoQueue.length reverse) {
        undoQueue(idx).undo
        println(oab)
    }
}

実行結果:

Include((Index(0),0))
Include((Index(0),1))
Include((Index(1),2))
Include((Index(0),3))
Remove((Index(1),1))
ArrayBuffer(3, 2, 0)
--
(4,Remove((Index(1),1)))
(3,Include((Index(0),3)))
(2,Include((Index(1),2)))
(1,Include((Index(0),1)))
(0,Include((Index(0),0)))
--
Include((Index(1),1))
ArrayBuffer(3, 1, 2, 0)
Remove((Index(1),1))
ArrayBuffer(3, 2, 0)
Remove((Index(2),0))
ArrayBuffer(3, 2)
java.lang.ExceptionInInitializerError
    ...

使い方が間違っていなければ、だけど、なんかどうも undo のときにインデックスが 1 つずれている気がする。

scala-2.7.1-final のソースコードに付属している scala/collection/mutable/ObservableBuffer.scala を見ると、insertAll の実装は次のようになっていた。

  abstract override def insertAll(n: Int, iter: Iterable[A]): Unit = {
    super.insertAll(n, iter)
    var i = n
    val it = iter.elements
    while (it.hasNext) {
      publish(new Include((Index(i), it.next)) with Undoable {
        def undo { remove(i) }
      })
      i = i + 1
    }
  }

この無名クラス (structural type) のメンバの undo メソッドで呼ばれている remove の引数はループの過程でどんどん変わっていくような気がするんだけど気のせいだろうか。

import scala.collection.mutable._

object test2 extends Application {
    var n = 0
    var m = new ListBuffer[{ def v: Unit }]
    0 until 2 foreach(i => {
        m += new { def v = println(n) }
        n = n + 1
    })
    m foreach(_.v)
}

これの結果は

2
2

となるし。