Stream(immutable)

Stream是惰性列表。实现细节涉及到lazy懒惰求值传名参数等等技术(具体细节详见维基百科-求值策略)。

StreamList是scala中严格求值非严格求值两个代表性不可变函数式数据结构。

考虑字符串拼接的表达式"foo"+"bar"的到"foobar",空串""就是这个操作的单位元(identity,数学中又称幺元),也就是说s+""或者""+s的值就是s。如果将三个字符串相加s+t+r,由于操作是可结合的(associative),所以说((s+t)+r)(s+(t+r))的结果一样。

在函数式编程里,把这类代数称为monoid。结合律(associativity)和同一律(identity)法则被称为monoid法则。

可折叠数据结构

比如ListStreamTreeVector等等这类都是可折叠数据结构。monoid与折叠操作有着紧密联系。

比如words=List("aa","bb","cc"),运用折叠方法如下:

words.foldRight("")((a,b)=>a+b) == ((""+"aa")+"bb")+"cc"
words.foldLeft("")((a,b)=>a+b) == "aa"+("bb"+("cc"+""))

首先实现Stream.fold方法,然后用fold去实现map filter flatMap等等高阶函数(大可不必仅使用fold去编写其它函数,就像尺规作图没有刻度一样。这样写仅仅为了好玩,没有银弹No Silver Bullet)。至于mapflatMap是什么?函子(Functor)是对map的泛化,Monad是对flatMap的泛化(相关概念参见Fp in Scala)。

fold源码如下(采用尾递归):

  def fold[B](z: =>B)(f:(A,B)=>B):B={
@tailrec
def loop(stream: Stream[A])(result: =>B)(f:(A,B)=>B):B=stream match {
case Empty =>result
case Cons(h,t) =>loop(t())(f(h(),result))(f)
}
loop(self)(z)(f)
}

Stream实现如下:

trait Stream[+A] {self=>
import Stream.{cons,empty}
def fold[B](z: =>B)(f:(A,B)=>B):B={
@tailrec
def loop(stream: Stream[A])(result: =>B)(f:(A,B)=>B):B=stream match {
case Empty =>result
case Cons(h,t) =>loop(t())(f(h(),result))(f)
}
loop(self)(z)(f)
}
def toList=fold(Nil:List[A])((a,b)=> ::(a,b)).invert
def exits(p:A=>Boolean)=fold(false)((a,b)=>p(a)||b)
def forall(p:A=>Boolean)=fold(true)((a,b)=>p(a)&&b)
def invert=fold(Empty:Stream[A])((a,b)=>cons(a,b))
def map[B](f:A=>B)=fold(Empty:Stream[B])((a,b)=> cons(f(a),b)).invert
def ++[B>:A](stream:Stream[B])=invert.fold(stream)((a,b)=>{cons(a,b)})
def flatMap[B>:A](f:A=>Stream[B])=fold(Empty:Stream[B])((a,b)=>{f(a)++b}).invert
def filter(p:A=>Boolean)=fold(Empty:Stream[A])((a,b)=>p(a) match {
case true =>cons(a,b)
case false =>b
}).invert
def take(n:Int)=fold((n,empty[A]))((a,b)=> b._1 match {
case r if r >0=>(r-1,cons(a,b._2))
case 0=>(0,b._2)
})._2.invert
def drop(n:Int)=fold((n,empty[A]))((a,b)=>b._1 match {
case r if r>0 =>(r-1,b._2)
case 0 => (0,cons(a,b._2))
})._2
}
case class Cons[+A](h:()=>A,t:()=>Stream[A]) extends Stream[A]
case object Empty extends Stream[Nothing]
object Stream{
def cons[A](hd: =>A,tl: =>Stream[A]) :Stream[A]={
lazy val head=hd
lazy val tail=tl
Cons(()=>head,()=>tail)
}
def empty[A]:Stream[A]=Empty
def apply[A](xs:A*):Stream[A]=
if(xs.isEmpty)
empty
else
cons(xs.head,apply(xs.tail:_*))
}

List(immutable)

scala中的List实现方式类似于c语言中的链表数据结构,但是不同于链表,List是不可变的。就像字符串操作a=b+c,字符串a加上字符串b的到新的字符串c,同理,不可变的List,al=bl++cl,listbl与listcl相连接的到新的listal,而blcl本身不变。相比抽象数据结构ADT,我更愿意把List称作代数数据结构。其不变性与函数式数据结构中的数据共享有关(通过复制与共享实现了不变性)。

至于exists forall takeWhile take filter map foldRight等函数均采用尾递归方式,无需考虑爆栈问题。与官方库基于CanBuildFrom这类的虚类型实现方式不同,本程序完全采用递归实现。

trait List[+A]{self=>
def tail=self match {
case Nil => Nil
case _::t=>t
}
def init:List[A]=self match {
case Nil=> Nil
case h::Nil => Nil
case h::t=> ::(h,t.init)
}
def print={
def loop(list: List[A]):String=list match {
case Nil=>"Nil"
case h::t=>s"$h :: ${loop(t)}"
}
loop(self)
}
override def toString= print
def setHead[B>:A](newHead:B)= ::(newHead,self.tail)
def drop(n:Int):List[A]=n match {
case 0=>self
case _ if n>0 => self match {
case _::t=> t.drop(n-1)
}
case _ if n<0 =>Nil
}
def dropWhile(p:A=>Boolean):List[A]=self match {
case Nil=>Nil
case h::t=>p(h) match {
case true => t.dropWhile(p)
case false=> self
}
}
def ++[B>:A](list: List[B]):List[B]=self match {
case Nil=>list
case h::t=> ::(h,t++list)
}
def foldRight[B](z:B)(f:(A,B)=>B)={
@tailrec
def loop(list: List[A])(z:B)(f:(A,B)=>B):B=list match {
case Nil => z
case h::t => loop(t)(f(h,z))(f)
}
loop(self)(z)(f)
}
def invert=foldRight(Nil:List[A])(::(_,_))
def length=foldRight(0)((_,b)=>b+1)
def flatMap[B>:A](f:B=>List[B]):List[B]=self match {
case Nil=> Nil
case h::t=> f(h)++t.flatMap(f)
}
def map[B](f:A=>B)={
@tailrec
def loop(list: List[A])(result:List[B])(f:A=>B):List[B]=list match {
case Nil=>result
case h::t => loop(t)(::(f(h),result))(f)
}
loop(self)(Nil:List[B])(f).invert
}
def filter(p:A=>Boolean):List[A]={
@tailrec
def loop(list: List[A])(result:List[A])(p:A=>Boolean):List[A]=list match {
case Nil => result
case h::t => p(h) match {
case true =>loop(t)(::(h,result))(p)
case false=>loop(t)(result)(p)
}
}
loop(self)(Nil)(p).invert
}
def take(n:Int):List[A]={
@tailrec
def loop(list: List[A])(result:List[A])(n:Int):List[A]=list match {
case Nil=> result
case h::t => n match {
case 0 =>result
case _ if n>0 =>loop(t)(::(h,result))(n-1)
}
}
loop(self)(Nil)(n).invert
}
def takeWhile(p:A=>Boolean):List[A]={
@tailrec
def loop(list: List[A])(result:List[A])(p:A=>Boolean):List[A]=list match {
case Nil=>result
case h::t=>p(h) match {
case true => loop(t)(::(h,result))(p)
case false => result
}
}
loop(self)(Nil)(p).invert
}
def forall(p:A=>Boolean):Boolean={
@tailrec
def loop(list: List[A])(result:Boolean)(p:A=>Boolean):Boolean=list match {
case Nil => result
case h::t => p(h) match {
case true => loop(t)(result)(p)
case false=>false
}
}
loop(self)(true)(p)
}
def exists(p:A=>Boolean):Boolean={
@tailrec
def loop(list: List[A])(result:Boolean)(p:A=>Boolean):Boolean=list match {
case Nil => result
case h::t => p(h) match {
case true=>true
case false=>loop(t)(result)(p)
}
}
loop(self)(false)(p)
}
}
case class ::[+A](h:A,t:List[A]) extends List[A]
case object Nil extends List[Nothing]
object List{
def empty[A]:List[A]=Nil
def apply[A](xs:A*):List[A]={
if(xs.isEmpty) empty[A]
else ::(xs.head,apply(xs.tail:_*))
}
}

Tree

trait Tree[+A] {
}
case object EmptyNode extends Tree[Nothing]{
override def toString: String = "Nil"
}
case class Node[+A](value:A,left:Tree[A],right:Tree[A]) extends Tree[A]
object Tree{
def node[A](value:A,left:Tree[A]=EmptyNode,right:Tree[A]=EmptyNode)=Node(value,left,right)
def empty[A]:Tree[A]=EmptyNode
}

剑指Offers上的有关题目(基于scala解法)

数组中只出现一次的数字

0 0 0 ^ 0 = 0
0 1 0 ^ 1 = 1
1 1 1 ^ 1 = 0

一个整型数组里除了一个数字外,其它数字均出现两次,如数组Array(2, 3, 6, 3, 2, 5, 5),返回结果应该为6,程序一句话就ok

scala写算法-List、Stream、以及剑指Offer里部分题目基于scala解法的更多相关文章

  1. 面试经典算法题集锦——《剑指 offer》小结

    从今年 3 月份开始准备找实习,到现在校招结束,申请的工作均为机器学习/数据挖掘算法相关职位,也拿到了几个 sp offer.经历这半年的洗礼,自己的综合能力和素质都得到了一个质的提升. 实话说对于未 ...

  2. 《剑指offer》全部题目-含Java实现

    1.二维数组中的查找 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. publi ...

  3. 剑指offer算法总结

    剑指offer算法学习总结 节选剑指offer比较经典和巧妙的一些题目,以便复习使用.一部分题目给出了完整代码,一部分题目比较简单直接给出思路.但是不保证我说的思路都是正确的,个人对算法也不是特别在行 ...

  4. JS数据结构与算法 - 剑指offer二叉树算法题汇总

    ❗❗ 必看经验 在博主刷题期间,基本上是碰到一道二叉树就不会碰到一道就不会,有时候一个下午都在搞一道题,看别人解题思路就算能看懂,自己写就呵呵了.一气之下不刷了,改而先去把二叉树的基础算法给搞搞懂,然 ...

  5. 《剑指offer》面试题11: 数值的整数次方

    面试题11: 数值的整数次方 剑指offer面试题11,题目如下 实现函数double power(double base,int exponent),求base的exponent次方, 不得使用库 ...

  6. 【剑指offer】旋转数组的最小值

    採用二分查找的策略,重点要考虑一些边界情况:旋转了0元素.即输入的是一个升序排列的数组.仅仅包括一个数字的数组.有非常多反复数字的数组等. AC代码: #include<stdio.h> ...

  7. LeetCode—剑指 Offer学习计划

    第 1 天 栈与队列(简单) 剑指 Offer 09. 用两个栈实现队列 class CQueue { public: CQueue() { } stack<int>s1,s2; void ...

  8. 【剑指offer】丑数

    把只包含因子2.3和5的数称作丑数(Ugly Number).例如6.8都是丑数,但14不是,因为它包含因子7. 习惯上我们把1当做是第一个丑数.求按从小到大的顺序的第N个丑数. leetcode上也 ...

  9. 剑指offer之字符串是否为数值

    1. 题目 这是<剑指offer>上的一道题,刚开始觉得这是一道挺简单的题目,后来发现自己太年轻了,考虑的因素太少了,思考了而是分钟还是无从下手,看了作者的思路深深被他折服了,题目如下: ...

随机推荐

  1. androidStudio 中 gradle 常用功能

    1. gradle 使用 svn 当前版本信息. def getSvnRevision() { new ByteArrayOutputStream().withStream { os -> de ...

  2. cardview和Palette,ActionBar颜色随图改变

    CardView是一个控件,Palette是取色工具(工具类),本文会对他们进行比较细致的介绍,相信机制的各位看完一定轻而易举地实现ActionBar随图改变的特效. 首先看一下效果图: Gradle ...

  3. Android Studio 升级到3.0 提示 java.lang.NoClassDefFoundError

    Android Studio 升级到3.0 提示 java.lang.NoClassDefFoundError 这个问题折腾了2个小时,最后解决了,Stack Overflow 上也有一次类似的问题, ...

  4. Unity20172.0 Android平台打包

    Android SDK及Jdk百度网盘下载链接:https://pan.baidu.com/s/1dFbEmdz 密码:pt7b Unity20172.0 Android平台打包 简介说明: 第一步: ...

  5. Mybatis(二)参数(Parameters)传递

    Mybatis参数(Parameters)传递  1..单个参数 可以接受基本类型,对象类型,集合类型的值.这种情况MyBatis可直接使用这个参数,不需要经过任何处理. <!-- 根据id查询 ...

  6. Servlet&JSP-HTTP报文头获取及应用

    完整代码请参考:https://github.com/devway9/java-exercise/tree/master/servlet-jsp 目录 1 HttpServletRequest获取报文 ...

  7. 工厂模式(Factory Method)

    1.工厂方法模式(Factory Method) 工厂方法模式分为三种: 1-1.普通工厂模式,就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建. 举例如下:(我们举一个发送邮件和短信的例子 ...

  8. 数据结构与算法--Boyer-Moore和Rabin-Karp子字符串查找

    数据结构与算法--Boyer-Moore和Rabin-Karp子字符串查找 Boyer-Moore字符串查找算法 注意,<算法4>上将这个版本的实现称为Broyer-Moore算法,我看了 ...

  9. Nginx负载均衡使用ip

    upstream test1{ server 192.168.1.213; server 192.168.1.37; } server { listen 80; # default backlog=2 ...

  10. Web 动画帧率(FPS)计算

    我们知道,动画其实是由一帧一帧的图像构成的.有 Web 动画那么就会存在该动画在播放运行时的帧率.而帧率在不同设备不同情况下又是不一样的. 有的时候,一些复杂或者重要动画,我们需要实时监控它们的帧率, ...