您可以使用continuations插件。插件翻譯後,它與Oleg的Cont
monad和shift
和reset
有相似之處。棘手的部分是弄清楚類型。因此,這裏是我的翻譯:
import util.continuations._
import collection.mutable.ListBuffer
sealed trait Zipper[A] { def zipUp: Seq[A] }
case class ZDone[A](val zipUp: Seq[A]) extends Zipper[A]
case class Z[A](current: A, forward: Option[A] => Zipper[A]) extends Zipper[A] {
def zipUp = forward(None).zipUp
}
object Zipper {
def apply[A](seq: Seq[A]): Zipper[A] = reset[Zipper[A], Zipper[A]] {
val coll = ListBuffer[A]()
val iter = seq.iterator
while (iter.hasNext) {
val a = iter.next()
coll += shift { (k: A=>Zipper[A]) =>
Z(a, (a1: Option[A]) => k(a1.getOrElse(a)))
}
}
ZDone(coll.toList)
}
}
的延續插件有支持while
循環,但不是map
或flatMap
,所以我已經使用while
和可變ListBuffer
捕捉到可能被更新元素的選擇。 make_zipper
函數被翻譯成夥伴Zipper.apply
- 一個典型的Scala位置來創建新的對象或集合。數據類型被翻譯成一個密封的特徵,並用兩個case類擴展它。我已經將zip_up函數作爲Zipper
的方法,並針對每個案例類使用不同的實現。也很典型。
這是在行動:
object ZipperTest extends App {
val sample = for (v <- 1 to 5) yield (v, (1 to v).reduceLeft(_ * _))
println(sample) // Vector((1,1), (2,2), (3,6), (4,24), (5,120))
def extract134[A](seq: Seq[A]) = {
val Z(a1, k1) = Zipper(seq)
val Z(a2, k2) = k1(None)
val Z(a3, k3) = k2(None)
val Z(a4, k4) = k3(None)
List(a1, a3, a4)
}
println(extract134(sample)) // List((1,1), (3,6), (4,24))
val updated34 = {
val Z(a1, k1) = Zipper(sample)
val Z(a2, k2) = k1(None)
val Z(a3, k3) = k2(None)
val Z(a4, k4) = k3(Some(42 -> 42))
val z = k4(Some(88 -> 22))
z.zipUp
}
println(updated34) // List((1,1), (2,2), (42,42), (88,22), (5,120))
}
我是怎麼計算的類型shift
,k
和reset
或如何翻譯T.mapM
?
我看着mapM
,我知道它會讓我得到一個Cont
,但我不確定Cont
裏面有什麼,因爲它取決於轉變。所以我從換班開始。忽略haskell return
以構建Cont
,班次返回Zipper
。我也猜想,我需要添加一個A
類型的元素到我的集合來構建。所以這個轉變將出現在預計類型爲A
的元素的「洞」中,因此k
將是A=>?
函數。我們假設這一點。在我不太確定的類型之後,我會打上問號。所以我開始:
shift { (k: A?=>?) =>
Z(a, ?)
}
接下來,我看(硬)在(k . maybe a id)
。函數maybe a id
將返回一個A
,這與k
作爲參數一致。這相當於a1.getOrElse(a)
。另外,因爲我需要填寫Z(a, ?)
,所以我需要弄清楚如何從選項中獲得一個功能到Zipper。最簡單的方法是假設k
返回Zipper
。另外,看看如何使用拉鍊或k1(Some(a))
,我知道我必須讓用戶有選擇地更換元素,這就是forward
函數所做的。它繼續與原始a
或更新a
。它開始有意義。所以現在我有:
shift { (k: A=>Zipper[A]) =>
Z(a, (a1: Option[A]) => k(a1.getOrElse(a)))
}
接下來,我看mapM
一次。我發現它是由return . ZDone
組成的。再次忽略return
(因爲它僅適用於Cont
monad),我看到ZDone
將採用最終的集合。所以這很完美,我只需要在其中放入coll
,當程序到達那裏時,它會有更新的元素。此外,reset
中的表達式類型現在與返回類型k
(Zipper[A]
)一致。
最後我填寫編譯器可以推斷的重置類型,但是當我猜對的時候,它給了我一種(錯誤的)自信心,我知道發生了什麼。
我的翻譯不像Haskell那樣一般或者很漂亮,因爲它不保留集合中的類型,並且使用突變,但希望它更易於理解。
編輯:下面是保留的類型和使用一個不變的列表,以便z.zipUp == z.zipUp
版本:
import util.continuations._
import collection.generic.CanBuild
import collection.SeqLike
sealed trait Zipper[A, Repr] { def zipUp: Repr }
case class ZDone[A, Repr](val zipUp: Repr) extends Zipper[A, Repr]
case class Z[A, Repr](current: A,
forward: Option[A] => Zipper[A, Repr]) extends Zipper[A, Repr] {
def zipUp = forward(None).zipUp
}
object Zipper {
def apply[A, Repr](seq: SeqLike[A, Repr])
(implicit cb: CanBuild[A, Repr]): Zipper[A, Repr] = {
type ZAR = Zipper[A, Repr]
def traverse[B](s: Seq[A])(f: A => [email protected][ZAR]): List[B]@cps[ZAR] =
if (s.isEmpty) List()
else f(s.head) :: traverse(s.tail)(f)
reset {
val list = traverse(seq.toSeq)(a => shift { (k: A=>ZAR) =>
Z(a, (a1: Option[A]) => k(a1.getOrElse(a)))
})
val builder = cb() ++= list
ZDone(builder.result): ZAR
}
}
}
順便說一句,這裏有上階延續單子其他資源:
感謝這非常詳細的答案! (順便說一句:如果你想編輯它,我認爲你的意思是「seq」而不是「extract」的第二行中的「sample」)。你是什麼意思,「它不保存集合中的類型」?我沒有看到代碼中出現「Any」。 – 2013-04-11 17:36:36
另外,你說延續不支持地圖,但這就是我所指的與Rompf幻燈片的鏈接(參見幻燈片48,瞭解支持延續的地圖的定義)。 我需要以純粹的功能性風格來做到這一點,所以我會從你寫的內容開始,嘗試修改它。對此有何建議? – 2013-04-11 17:55:59
我能夠弄清楚如何使用Rompf的掛起地圖來製作一個純粹的功能版本:http://pastie.org/7460281感謝您的幫助! – 2013-04-12 03:20:21