package cn.edu.jxnu.scala.fb
import cn.edu.jxnu.scala.fb.datastructures.List.foldRight
import scala.annotation.tailrec
/**
* 第三章
*
* @author 梦境迷离
* @version 1.0, 2019-04-19
*/
object datastructures extends App {
//打印x
Console println List.x
//打印删除第一个元素
Console println List.tail(List(1, 2, 3, 4, 5, 6))
//修改头元素
Console println List.setHead(List(1, 2, 3, 4, 5, 6), 2)
//删除前2个元素
Console println List.drop(List(1, 2, 3, 4, 5, 6), 2)
//删除列表中大于1的元素(或满足前缀判断的)
Console println List.dropWhile[Int](List(1, 2, 3, 4, 5, 6), a => a > 5) //未知原因,>无法生效
Console println List.dropWhile[String](List("hello", "hello111", "hello444", "world"), a => a.startsWith("hello"))
//柯里化的,第一个参数传入时返回一个函数,并使用第二个参数作为本函数的入参
Console println List.dropWhile2(List("hello", "hello111", "hello444", "world2"))(a => a.startsWith("hello"))
//将a2复制到a1后面
Console println List.append(List(1, 4), List(2, 3))
//删除最后一个元素
Console println List.init(List(1, 2, 3, 4, 5, 6))
//对foldRight传入Nil和Cons
Console println foldRight(Cons(1, Cons(2, Cons(3, Nil))), Nil: List[Int])(Cons(_, _))
Console println Cons(1, foldRight(Cons(2, Cons(3, Nil)), Nil: List[Int])(Cons(_, _)))
Console println Cons(1, Cons(2, foldRight(Cons(3, Nil), Nil: List[Int])(Cons(_, _))))
//计算List的长度
Console println List.length(List(1, 2, 3, 4, 5, 6))
//左折叠实现
Console println List.sum3(List(1, 2, 3, 4, 5, 6))
Console println List.product3(List(1, 2, 3, 4, 5, 6))
Console println List.length3(List(1, 2, 3, 4, 5, 6))
//反转列表
Console println List.reverse(List(1, 2, 3, 4, 5, 6))
//使用右折叠实现append
Console println List.appendViaFoldRight(List(1, 4), List(2, 3))
//拼接列表
Console println List.concat(List(List(1, 2), List(3, 4), List(2, 3)))
//每个元素加1
Console println List.add1(List(1, 2))
//使用过滤,删除奇数
Console println List.filter(List(1, 2, 3, 4, 5, 6))(_ % 2 == 1)
//使用map(每个元素都变成List),与concat拼接
Console println List.flatMap(List(1, 2, 3, 4, 5, 6))(i => List(i, i))
//过滤
Console println List.filterByFlatMap(List(1, 2, 3, 4, 5, 6))(_ % 2 == 1)
//列表对应位相加
Console println List.addPairwise(List(1, 2, 3, 4, 5, 6), List(1, 2, 3, 4, 5, 6))
//判断子集合
Console println List.hasSubsequence(List(1, 2, 3, 4, 5, 6), List(1, 2))
//sealed表面本接口的实现类必须在当前文件中,且限定A是协变的,即List[A]是List[B]的子类,当且仅当A是B的子类
sealed trait List[+A]
//空的List是任何list的子类
case object Nil extends List[Nothing]
//非空的List必然是head+子集合
case class Cons[+A](head: A, tail: List[A]) extends List[A]
//列表操作
object List {
/**
* 书上原定义方法
*
* @param ints
* @return
*/
def sum(ints: List[Int]): Int = ints match {
case Nil => 0
case Cons(x, xs) => x + sum(xs)
}
/**
* 书上原定义方法
*
* @param ds
* @return
*/
def product(ds: List[Double]): Double = ds match {
case Nil => 1.0
case Cons(0.0, _) => 0.0
case Cons(x, xs) => x * product(xs)
}
/**
* 书上原定义方法
*
* @param as
* @tparam A
* @return
*/
def apply[A](as: A*): List[A] = {
if (as.isEmpty) Nil
else Cons(as.head, apply(as.tail: _*)) //注意这里是用: _*,表示将集合作为可变参数传递,而不是集合整体
}
/**
* 3.1:List 数据构造器的模式匹配
*
* 引申:注意是右结合,调用顺序是反的。目前使用的Scala2.12
* :: {{{1 :: List(2, 3) = List(2, 3).::(1) = List(1, 2, 3)}}}
*
* ::: {{{List(1, 2) ::: List(3, 4) = List(3, 4).:::(List(1, 2)) = List(1, 2, 3, 4)}}}
*/
val x = List(1, 2, 3, 4, 5) match {
case Cons(x, Cons(2, Cons(4, _))) => x
case Nil => 42
case Cons(x, Cons(y, Cons(3, Cons(4, _)))) => x + y
case Cons(h, t) => h + sum(t)
case _ => 101
}
/**
* 3.2:实现tail函数,删除一个List的第一个元素.时间开销是常量级,如果是Nil,在实现的时候有什么不同的选择?
*
* @param list
* @tparam A
* @return
*/
def tail[A](list: List[A]): List[A] = {
list match {
case Nil => sys.error("空列表的尾无法获取")
case Cons(_, t) => t
}
}
/**
* 3.3:使用与3.1相同思路实现用一个不同的值替代列表中第一个元素
*
* @param list
* @param a
* @tparam A
* @return
*/
def setHead[A](list: List[A], a: A): List[A] = {
list match {
case Nil => sys.error("空列表的尾无法操作")
case Cons(_, t) => Cons(a, t)
}
}
/**
* 3.4:把tail泛化为drop函数,用于从列表中删除前n个元素。时间开销与drop的元素个数成正比(不能复制列表)
*
* @param list
* @param n
* @tparam A
* @return
*/
def drop[A](list: List[A], n: Int): List[A] = {
//使用了临时变量,不好
// var ll = list
// for (i <- 0 until n) {
// ll = tail(ll)
// }
// ll
if (n <= 0) list
else list match {
case Nil => Nil
case Cons(_, t) => drop(t, n - 1) //转化为删除n-1个元素
}
}
/**
* 3.5:删除列表中前缀符合判断的元素
*
* @param list
* @param f
* @tparam A
* @return
*/
def dropWhile[A](list: List[A], f: A => Boolean): List[A] = {
list match {
case Cons(h, t) if f(h) => dropWhile(t, f) //转化为对每个头元素进行判断
case _ => list
}
}
/**
* 3.5:改进高阶函数的类型推导,调用时不需要再使用声明第二个参数的类型
*
* 因为参数组里的类型信息会从第一个参数传递到第二个参数,因为第一个参数类型是Int,第二个也为Int
*
* @param list
* @param f
* @tparam A
* @return
*/
def dropWhile2[A](list: List[A])(f: A => Boolean): List[A] = {
list match {
case Cons(h, t) if f(h) => dropWhile(t, f) //转化为对每个头元素进行判断
case _ => list
}
}
/**
* 书上原定义方法
*
* @param a1 被解开,并放在a2的前面
* @param a2
* @tparam A
* @return
*/
def append[A](a1: List[A], a2: List[A]): List[A] = {
a1 match {
case Nil => a2
case Cons(h, t) => Cons(h, append(t, a2))
}
}
/**
* 3.6:返回除最后一个元素之外的所有元素
*
* @param list
* @tparam A
* @return
*/
def init[A](list: List[A]): List[A] = {
list match {
case Nil => sys.error("空列表的尾无法操作")
case Cons(_, Nil) => Nil
case Cons(h, t) => Cons(h, init(t))
}
}
/**
* 书上原定义方法
*
* 右折叠简单运用
*
* @param as
* @param z 起始值
* @param f 独立出来,让类型系统推出f的输入类型
* @tparam A 元素类型
* @tparam B 起始值类型
* @return
*/
def foldRight[A, B](as: List[A], z: B)(f: (A, B) => B): B = {
as match {
case Nil => z
case Cons(x, xs) => f(x, foldRight(xs, z)(f))
}
}
def sum2(ns: List[Int]): Int = {
//foldRight函数不是面向特定的元素类型,泛化的类型不必一定与List中的元素类型相同
foldRight(ns, 0)((x, y) => x + y)
}
def product2(ns: List[Double]): Double = {
foldRight(ns, 1.0)(_ * _) //(_ * _)是(x,y) => x*y的简写
}
//3.7:问题有点难,第五章再来回顾
//3.8:对foldRight传入Nil和Cons
foldRight(Cons(1, Cons(2, Cons(3, Nil))), Nil: List[Int])(Cons(_, _))
Cons(1, foldRight(Cons(2, Cons(3, Nil)), Nil: List[Int])(Cons(_, _)))
Cons(1, Cons(2, foldRight(Cons(3, Nil), Nil: List[Int])(Cons(_, _))))
Cons(1, Cons(2, Cons(3, foldRight(Nil, Nil: List[Int])(Cons(_, _)))))
Cons(1, Cons(2, Cons(3, Nil)))
/**
* 3.9:使用foldRight计算LIst的长度
*
* @param as
* @tparam A
* @return
*/
def length[A](as: List[A]): Int = {
//每存在一个元素对acc进行加1,右折叠从1开始
foldRight(as, 1)((_, acc) => acc + 1)
}
/**
* 3.10:使用尾递归实现,防止List太大造成StackOverflow
*
* @param as
* @param z
* @param f
* @tparam A
* @tparam B
* @return
*/
def foldLeft[A, B](as: List[A], z: B)(f: (B, A) => B): B = {
as match {
case Nil => z
case Cons(h, t) => foldLeft(t, f(z, h))(f)
}
}
/**
* 3.11-1:使用foldLeft实现
*
* @param ns
* @return
*/
def sum3(ns: List[Int]): Int = {
foldLeft(ns, 0)(_ + _)
}
/**
* 3.11-2:使用foldLeft实现
*
* @param ns
* @return
*/
def product3(ns: List[Double]): Double = {
foldLeft(ns, 1.0)(_ * _)
}
/**
* 3.11-3:使用foldLeft实现
*
* @param as
* @tparam A
* @return
*/
def length3[A](as: List[A]): Int = {
//左折叠从0开始,第一个参数是B,第二个才是A,acc是长度计算,_是元素
foldLeft(as, 0)((acc, _) => acc + 1)
}
/**
* 3.12:反转列表。使用一个折叠实现
*
* @param list
* @tparam A
* @return
*/
def reverse[A](list: List[A]): List[A] = {
//默认传入空列表,elements参数是返回类型,h参数是A元素。相当于每次做链接的头插入操作
foldLeft(list, List[A]())((elements, h) => Cons(h, elements))
}
//3.13:(使用foldLeft实现foldRight,避免栈溢出)
def foldRightViaFoldLeft[A, B](l: List[A], z: B)(f: (A, B) => B): B = {
foldLeft(reverse(l), z)((b, a) => f(a, b))
}
def foldRightViaFoldLeft_1[A, B](l: List[A], z: B)(f: (A, B) => B): B = {
foldLeft(l, (b: B) => b)((g, a) => b => g(f(a, b)))(z)
}
//我们正在调用“foldright”,将“b”类型实例化为“b=>b”,然后
//使用'z'参数调用生成的函数。尝试用等号替换等号来扩展定义。
//使用一个简单的例子,比如“foldLeft(list(1,2,3),0)”注意,这些实现更重要的是理论上的兴趣——它们不安全,也不适用于大的列表
def foldLeftViaFoldRight[A, B](l: List[A], z: B)(f: (B, A) => B): B = {
foldRight(l, (b: B) => b)((a, g) => b => g(f(b, a)))(z)
}
/**
* 3.14:根据foldLeft或者foldRight实现append函数
*
* @param l
* @param r
* @tparam A
* @return
*/
def appendViaFoldRight[A](l: List[A], r: List[A]): List[A] = {
//列表,初始值r,拼接方法是构造函数
foldRight(l, r)(Cons(_, _))
}
/**
* 3.15:写一个函数将一组列表连接成一个单个列表。它的运行效率应该随着所有列表的总长度线性增长,试着用我们定义过的函数。
*
* @param l
* @tparam A
* @return
*/
def concat[A](l: List[List[A]]): List[A] = {
//列表集,初始值列表Nil,拼接函数append
foldRight(l, Nil: List[A])(append)
}
/**
* 3.16:对列表中每个元素进行加1操作
*
* @param list
* @return
*/
def add1(list: List[Int]): List[Int] = {
//列表、初始值Nil、操作函数(List的尾部一定是Nil,用右折叠,第一个元素是已知)
foldRight(list, Nil: List[Int])((h, t) => Cons(h + 1, t))
}
/**
* 3.17:将列表的每个元素改为String类型
*
* @param list
* @return
*/
def doubleToString(list: List[Double]): List[String] = {
foldRight(list, Nil: List[String])((h, t) => Cons(h.toString, t))
}
/**
* 3.18:对列表中的每个元素进行修改,并维持列表结构
*
* @param as
* @param f
* @tparam A
* @tparam B
* @return
*/
def map[A, B](as: List[A])(f: A => B): List[B] = {
// foldRightViaFoldLeft(as, Nil:List[B])((h,t) => Cons(f(h),t))
foldRight(as, Nil: List[B])((h, t) => Cons(f(h), t))
}
/**
* 3.19:从列表中删除不满足断言的元素,并用它删除一个List[Int]中所有奇数
*
* @param as
* @param f
* @tparam A
* @return
*/
def filter[A](as: List[A])(f: A => Boolean): List[A] = {
foldRight(as, Nil: List[A])((h, t) => if (f(h)) Cons(h, t) else t)
}
/**
* 3.20:与map相似,但是传入的f函数是返回列表,这个f返回的列表会被塞到flatMap最终返回的列表中
*
* @param as
* @param f
* @tparam A
* @tparam B
* @return
*/
def flatMap[A, B](as: List[A])(f: A => List[B]): List[B] = {
concat(map(as)(f))
}
/**
* 3.21:使用flatMap实现filter
*
* @param as
* @param f
* @tparam A
* @return
*/
def filterByFlatMap[A](as: List[A])(f: A => Boolean): List[A] = {
flatMap(as)(a => if (f(a)) List(a) else Nil)
}
/**
* 3.22: 接收两个列表,对相应元素相加构造出新的列表
*
* @param a
* @param b
* @return
*/
def addPairwise(a: List[Int], b: List[Int]): List[Int] = {
(a, b) match {
case (Nil, _) => Nil
case (_, Nil) => Nil
case (Cons(h1, t1), Cons(h2, t2)) => Cons(h1 + h2, addPairwise(t1, t2))
}
}
/**
* 3.23:与上面相同,进行泛化
*
* @param a
* @param f
* @tparam A
* @return
*/
def zipWith[A, B, C](a: List[A], b: List[B])(f: (A, B) => C): List[C] = (a, b) match {
case (Nil, _) => Nil
case (_, Nil) => Nil
case (Cons(h1, t1), Cons(h2, t2)) => Cons(f(h1, h2), zipWith(t1, t2)(f))
}
/**
* 3.24:检查一个List子序列是否包含另一个List
*
* @param sup
* @param sub
* @tparam A
* @return
*/
@tailrec
def hasSubsequence[A](sup: List[A], sub: List[A]): Boolean = sup match {
//sup是空集时,当且仅当sub是空集,集合相等
case Nil => sub == Nil
case _ if startsWith(sup, sub) => true
case Cons(h, t) => hasSubsequence(t, sub)
}
/**
* 判断sub的元素是否均存在于sup
*
* @param sup
* @param sub
* @tparam A
* @return
*/
@tailrec
def startsWith[A](sup: List[A], sub: List[A]): Boolean = {
(sup, sub) match {
//空集是任何集合的子集,此时sup还有元素
case (_, Nil) => true
//均是非空集
case (Cons(h, t), Cons(h2, t2)) if h == h2 => startsWith(t, t2)
case _ => false
}
}
}
/*=============未测试=========================*/
//定义树结构
sealed trait Tree[+A]
//叶子
case class Leaf[A](value: A) extends Tree[A]
//非叶子
case class Branch[A](left: Tree[A], right: Tree[A]) extends Tree[A]
//树(二叉树)
//与List类似,Tree也只有两种情况,叶子与非叶子(非空树)
object Tree {
/**
* 3.25:统计树的节点数
*
* @param t
* @tparam A
* @return
*/
def size[A](t: Tree[A]): Int = t match {
case Leaf(_) => 1
//左右子树个数+1(root)
case Branch(l, r) => 1 + size(l) + size(r)
}
/**
* 3.26:获取Tree中最大的元素值
*
* @param t
* @return
*/
def maximum(t: Tree[Int]): Int = t match {
case Leaf(l) => l
case Branch(l, r) => maximum(l) max maximum(r)
}
/**
* 3.27:计算根节点到叶子的最大深度
*
* @param t
* @tparam A
* @return
*/
def depth[A](t: Tree[A]): Int = t match {
//叶子深度为-
case Leaf(l) => 0
//左边最大深度+右边最大深度+1
case Branch(l, r) => 1 + (depth(l) max depth(r))
}
/**
* 3.28:类似List中map
*
* @param t
* @tparam A
* @tparam B
* @return
*/
def map[A, B](t: Tree[A])(f: A => B): Tree[B] = t match {
case Leaf(n) => Leaf(f(n))
case Branch(l, r) => Branch(map(l)(f), map(r)(f))
}
/**
* 3.29 泛化上面三个函数,进一步抽象三个函数
*
* 与List类似,使用折叠
*
* @param t 需要操作的二叉树
* @param f 入参是树的元素类型A,返回参数是具体操作函数g的参数类型B
* @param g 具体操作函数
* @tparam A
* @tparam B
* @return
*/
def fold[A, B](t: Tree[A])(f: A => B)(g: (B, B) => B): B = t match {
case Leaf(a) => f(a)
//对左子树应用操作函数且对右子树应用操作函数
case Branch(l, r) => g(fold(l)(f)(g), fold(r)(f)(g))
}
def sizeViaFold[A](t: Tree[A]): Int = {
//对左右子树进行size操作,从1开始
fold(t)(_ => 1)(1 + _ + _)
}
def maximumViaFold(t: Tree[Int]): Int = {
//对左右子树进行max操作,从任意元素开始
fold(t)(a => a)(_ max _)
}
def depthViaFold[A](t: Tree[A]): Int = {
//对左右子树进行depth操作,从0开始
fold(t)(_ => 0)((d1, d2) => 1 + (d1 max d2))
}
}
}