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. 企业级应用TOMCAT

    第1章 Tomcat 1.1 Tomcat简介 Tomcat是一个免开放源代码的Web应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不多的场合下被普遍使用,是开发调试JSP程序的首选,另 ...

  2. 如何访问 Service?- 每天5分钟玩转 Docker 容器技术(99)

    前面我们已经学习了如何部署 service,也验证了 swarm 的 failover 特性.不过截止到现在,有一个重要问题还没有涉及:如何访问 service?这就是本节要讨论的问题. 为了便于分析 ...

  3. 查看.ssh文件在哪

    输入命令 ll -d ~/.ssh 后你就都明白了.

  4. [Asp.Net Core] 1. IIS中的 Asp.Net Core 和 dotnet watch

    在基于传统的.NET Framework的Asp.Net Mvc的时候,本地开发环境中可以在IIS中建立一个站点,可以直接把站点的目录指向asp.net mvc的项目的根目录.然后build一下就可以 ...

  5. 在C#中输出变量的地址

    int a = 3; GCHandle handle = GCHandle.Alloc(a); var pin = GCHandle.ToIntPtr(handle); Console.WriteLi ...

  6. 01.redis初识

    Redis学习: redis是什么? Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库.缓存和消息中间件. 不过Redis在生产环境中使用最多的功能是缓存系统.至于其 ...

  7. Centos下抓包

    刚才遇到一个问题,微信配置时token总是失败. 于是抓一下服务器的包.看看是否是数据传输出了问题. 先安装工具 [Shell] 纯文本查看 复制代码 ? 1 yum install -y wires ...

  8. Pyhton爬虫实战 - 抓取BOSS直聘职位描述 和 数据清洗

    Pyhton爬虫实战 - 抓取BOSS直聘职位描述 和 数据清洗 零.致谢 感谢BOSS直聘相对权威的招聘信息,使本人有了这次比较有意思的研究之旅. 由于爬虫持续爬取 www.zhipin.com 网 ...

  9. COM组件转换为.NET元数据2

    上一篇通过命令的方式实现COM组件与.NET元素的转换.这次直接在VS中转换. 以下为步骤:

  10. System.ServiceModel.CommunicationException: 已超过传入消息(65536)的最大消息大小配额。若要增加配额,请使用相应绑定元素上的 MaxReceivedMessageSize 属性。