设为首页 加入收藏

TOP

Scalaz(23)- 泛函数据结构: Zipper-游标定位(四)
2017-10-10 12:13:24 】 浏览:9691
Tags:Scalaz 数据结构 Zipper- 游标 定位
; zv : Option[scalaz.Zipper[Int]] = Some(Zipper(<lefts>, 2, <rights>))
45 46 zv.get.show //> res8: scalaz.Cord = Zipper(Stream(), 2, Stream(10,12,1,5,4)) 47 zv.get.toList //> res9: List[Int] = List(2, 10, 12, 1, 5, 4)

我在上面的程序里在for{...}yield里面逐条添加指令从而示范游标当前焦点和集合元素跟随着的变化。这段程序可以说就是一段行令程序。
回到上面提到的效率和代码质量讨论。我们提过scalaz提供Zipper就是为了使集合操作编程更简明优雅,实际情况是怎样的呢?

举个例子:有一串数字,比如:List(1,4,7,9,5,6,10), 我想找出第一个高点元素,它的左边低,右边高,在我们的例子里是元素9。如果我们尝试用习惯的行令方式用索引去编写这个函数:

def peak(list: List[Int]): Option[Int] = { list.indices.find { index => val x = list(index) index > 0 && index < list.size - 1 && x > list(index - 1) && x > list(index + 1) }.map(list(_)) }

哇!这东西不但极其复杂难懂而且效率低下,重复用find索引导致速度降到O(n * n)。如果用Array会把效率提高到O(n),不过我们希望用immutable方式。那么用函数式编程方式呢?

def peak_fp(list: List[Int]): Option[Int] = list match { case x :: y :: z :: tl if y > x && y > z => Some(y) case x :: tl => peak(tl) case Nil => None } 

用模式匹配(pattern matching)和递归算法(recursion),这段程序好看多了,而且效率也可以提高到O(n)。

但我们再把情况搞得复杂一点:把高点值增高一点(+1)。还是用FP方式编写:

def raisePeak(list: List[Int]): Option[List[Int]] = { def rec(head: List[Int], tail: List[Int]): Option[List[Int]] = tail match { case x :: y :: z :: tl if y > x && y > z => Some((x :: head).reverse ::: ((y +1) :: z :: tl)) case x :: tl => rec(x :: head, tl) case Nil => None } rec(List.empty, list) }

代码又变得臃肿复杂起来。看来仅仅用FP编程方式还不足够,还需要用一些新的数据结构什么的来帮助。scalaz的Zipper可以在这个场景里派上用场了:

def raisePeak_z(list: List[Int]): Option[List[Int]] = { for { zipper <- list.toZipper peak <- zipper.positions.findNext( z => (z.previous, z.next) match { case (Some(p), Some(n)) => p.focus < z.focus && n.focus < z.focus case _ => false }) } yield (peak.focus.modify(_ + 1).toStream.toList) }

用Zipper来写程序表达清楚许多。这里用上了Zipper.positions:

/** * A zipper of all positions of the zipper, with focus on the current position. */ def positions: Zipper[Zipper[A]] = { val left = std.stream.unfold(this)(_.previous.map(x => (x, x))) val right = std.stream.unfold(this)(_.next.map(x => (x, x))) zipper(left, this, right) }

positions函数返回类型是Zipper[Zipper[A]]符合findNext使用。我们前面已经提到:使用Zipper的成本约为O(n)。

 

 

 

 

 

 

首页 上一页 1 2 3 4 下一页 尾页 4/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Scalaz(22)- 泛函编程思维: C.. 下一篇\x 开头编码的数据解码成中文

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目