Created
December 7, 2012 12:13
-
-
Save yakamoto69/4232911 to your computer and use it in GitHub Desktop.
A Pager, Handles Sequential Data as a Link of Pages.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class Pager[A](seq: Sequential[A], chunkSize: Int) { | |
| self => | |
| import Pager._ | |
| private val ls: GrowingList[Chunk[A]] = mkGrowingList(seq, chunkSize) | |
| var pageNo: Int = _ | |
| def hasNext: Boolean = { | |
| ls.get(pageNo + 1).isDefined | |
| } | |
| def next() { | |
| pageNo += 1 | |
| } | |
| def prev() { | |
| pageNo -= 1 | |
| } | |
| def get: Option[Chunk[A]] = { | |
| ls.get(pageNo) | |
| } | |
| def isValid: Boolean = ls.get(pageNo).isDefined | |
| // Iterator.continually or Iterator.iterate maybe make it more readable. | |
| def iterator: Iterator[Chunk[A]] = { | |
| new Iterator[Chunk[A]] { | |
| def hasNext = self.isValid | |
| def next() = { | |
| get match { | |
| case Some(chunk) => { | |
| self.next() | |
| chunk | |
| } | |
| case None => throw new IllegalStateException() | |
| } | |
| } | |
| } | |
| } | |
| } | |
| class GrowingList[N <: Node[N]](root: N, nextF: N => Option[N]) { | |
| /* TODO | |
| * At this moment, if a node is made once, it will never be changed. | |
| * I want to implement some validation feature, in which if a node is judged as invalid, the node will be recreated on demand. | |
| */ | |
| def get(ix: Int): Option[N] = { | |
| val nodes = Iterator.iterate(nextOf(root)) { prev => | |
| prev flatMap nextOf | |
| } | |
| // If None continues, though this is a bit inefficient, never mind it! | |
| nodes.drop(ix).next() | |
| } | |
| private def nextOf(n: N): Option[N] = { | |
| n.next(nextF(n)) | |
| } | |
| } | |
| trait Node[N <: Node[N]] { | |
| def next(init: => Option[N]): Option[N] | |
| } | |
| trait Sequential[A] { | |
| /** | |
| * If `from` is None, returns first elements. | |
| */ | |
| def getGt(from: Option[A], max: Int): Seq[A] | |
| } | |
| /** | |
| * If a chunk is the root, `lst` must be None. | |
| * Otherwise, `fst` and `lst` never be None. | |
| * It's ugly... What wrong happened? | |
| */ | |
| class Chunk[A](val fst: Option[A], | |
| val lst: Option[A]) extends Node[Chunk[A]] { | |
| // TODO This is a cache, use any caching library if exists. | |
| var n: Option[Option[Chunk[A]]] = None | |
| def next(init: => Option[Chunk[A]]): Option[Chunk[A]] = { | |
| // I don't like this. This looks too imperative. | |
| n getOrElse { | |
| val a = init | |
| n = Some(a) | |
| a | |
| } | |
| } | |
| override def toString = "fst:"+fst+",lst:"+lst | |
| } | |
| object Pager { | |
| def mkGrowingList[A](seq: Sequential[A], chunkSize: Int): GrowingList[Chunk[A]] = { | |
| val root = new Chunk[A](None, None) | |
| def nextF(prev: Chunk[A]): Option[Chunk[A]] = { | |
| // Whether can I convert List -> Option on any natural way? | |
| val data = seq.getGt(prev.lst, chunkSize) | |
| if (data.isEmpty) | |
| None | |
| else { | |
| val fst = Some(data.head) | |
| val lst = data.tail.lastOption orElse fst // fst and lst should have a value | |
| val c = new Chunk[A](fst, lst) | |
| Some(c) | |
| } | |
| } | |
| new GrowingList(root, nextF) | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment