Scala 学习 -- 列表

Scala 学习 -- 列表

一、列表与数组的区别

1.列表是不可变的, 列表的元素不能通过赋值改变。

2.列表是递归的(linked list), 数组是平的

二、 List类型

1.同构: 同一个列表所有元素类型相同

2.协变:对每一组类型S和T, 如果S是T的子类型,则List[S]是List[T]的子类型

注:由于列表是协变的,对于任何类型T,List[Nothing]都是List[T]的子类型

三、构建列表

所有列表的构建源自两个基础构建单元: Nil 和 ::。其中Nil表示空列表,中缀操作符::表示在列表前追加元素。

注: ::(cons) 右结合

val nums = List[Int](1,2,3) 	
等价于
val nums = 1 :: 2 :: 3 :: Nil

四、列表基本操作

列表的基本操作包括三种:head、 tail、 isEmpty

//head	返回列表的第一个元素
scala> nums.head
res0: Int = 1

//tail 返回列表中除第一个元素之外的所有元素
//tail方法只对非空列表有定义
scala> nums.tail
res1: List[Int] = List(2, 3)

//isEmpty返回列表是否为空列表
scala> nums.isEmpty
res2: Boolean = false

基于列表基本操作的插入排序

def isort(xs:List[Int]):List[Int] = {
 if (xs.isEmpty) Nil
 else insert(xs.head, isort(xs.tail))
}

def insert(x:Int, xs:List[Int]):List[Int] = {
 if(xs.isEmpty || x < xs.head) x::xs
 else xs.head :: (insert(x, xs.tail))
}

五、列表模式

List(...)是一个由类库定义的提取器模式的实例。

:: =》 存在类scala.::,用于构造非空列表

scala> val fruit = "apples" :: ("oranges" :: ("pears" :: Nil))
fruit: List[String] = List(apples, oranges, pears)
scala> val List(a, b, c) = fruit
a: String = apples
b: String = oranges
c: String = pears

六、List类的初阶方法

初阶方法 -- 一个方法不接受任何函数作为入参

//  :::  ======>  拼接两个列表,列表拼接操作为右结合
scala> List(1, 2):::List(3, 4, 5)
res3: List[Int] = List(1, 2, 3, 4, 5)
//可能实现
def append[T](xs:List[T], ys:List[T]):List[T] = {
xs match {
  case List() => ys
  case x::xs1 => x :: append(xs1, ys)
}
}


// length ======> 获取列表的长度
// 注: isEmpty 速度优于 length == 0
scala> List(1,2,3).length
res4: Int = 3




// init ======> 返回最后一个元素外的剩余部分
scala> List(1,2,3).init
res5: List[Int] = List(1, 2)


// last =====> 返回非空列表的最后一个元素
scala> List(1,2,3).last
res6: Int = 3


// reverse =====> 列表反转
scala> List(1,2,3).reverse
res0: List[Int] = List(3, 2, 1)


// take =====> 返回列表xs的前n个元素
//若n大于xs.length,返回整个xs列表
scala> List(1,2,3).take(2)
res1: List[Int] = List(1, 2)

scala> List(1,2,3).take(4)
res2: List[Int] = List(1, 2, 3)


//drop =====> 返回除前n个元素之外的所有元素
//若n大于等于xs.length,则返回空列表
scala> List(1,2,3).drop(2)
res3: List[Int] = List(3)

scala> List(1,2,3).drop(4)
res4: List[Int] = List()


//splitAt =====> 将列表从指定下标位置切开,返回这两个列表组成的对偶
//xs splitAt n  == (xs take n, xs drop n) 
scala> List(1,2,3).splitAt(1)
res5: (List[Int], List[Int]) = (List(1),List(2, 3))


//apply =====> 从任意位置选取元素
//对列表而言,并不常用, 耗时与下标长度成正比
//xs apply n = (xs drop n).head
scala> List(1,2,3).apply(1)
res7: Int = 2


//indices =====> 返回包含了指定列表所有有效下标的列表
scala> val ans = List('a','b','c','d','e')
ans: List[Char] = List(a, b, c, d, e)

scala> ans.indices
res12: scala.collection.immutable.Range = Range 0 until 5

scala> ans.indices.toList
res13: List[Int] = List(0, 1, 2, 3, 4)


//flatten =====> 接受一个列表的列表并将它扁平化,返回单个列表(仅应用于元素都是列表的列表)
scala> List(List(1), List(2,3), List(4,5)).flatten
res15: List[Int] = List(1, 2, 3, 4, 5)


//zip =====> 接受两个列表,返回一个由对偶组成的列表
scala> val ans = List('a','b','c','d','e')
ans: List[Char] = List(a, b, c, d, e)

scala> ans.indices zip ans
res16: IndexedSeq[(Int, Char)] = Vector((0,a), (1,b), (2,c), (3,d), (4,e))


//zipWithIndex
scala> ans.zipWithIndex
res1: List[(Char, Int)] = List((a,0), (b,1), (c,2), (d,3), (e,4))

//unzip =====> 任何元组列表可通过unzip转换同由列表组成的元组
scala> val zipped = ans.zipWithIndex
zipped: List[(Char, Int)] = List((a,0), (b,1), (c,2), (d,3), (e,4))

scala> zipped.unzip
res2: (List[Char], List[Int]) = (List(a, b, c, d, e),List(0, 1, 2, 3, 4))


//toString =====> 返回列表的标准字符串表现形式
scala> val ans = List('a','b','c','d','e')
ans: List[Char] = List(a, b, c, d, e)

scala> ans.toString
res3: String = List(a, b, c, d, e)


//mkString =====> 表现形式
//xs mkstring (pre, sep , post)
// xs -- 要显示的列表
// pre -- 出现在最前面的前缀字符串
// sep -- 在元素间显示的分隔字符串
// post -- 出现在最后面的后缀字符串
scala> ans mkString ("[", "$", "}")
res6: String = [a$b$c$d$e}

                
//addString =====> mkString变种,将构建出来的字符串
//追加到一个StringBuilder对象
//注:mkString、 addString继承自List的超特质Travelsable
scala> val buf = new StringBuilder
buf: StringBuilder =

scala> ans addString (buf, "{", ", ", "}")
res7: StringBuilder = {a, b, c, d, e}

                
//toArray、 toList =====> 数组与列表间转换
scala> val ans = List('a','b','c','d','e')
ans: List[Char] = List(a, b, c, d, e)

scala> val arr = ans.toArray
arr: Array[Char] = Array(a, b, c, d, e)

scala> arr.toList
res9: List[Char] = List(a, b, c, d, e)

                
//copyToArray =====> 将列表中的元素依次复制到目标数组的指定位置
scala> val arr = new Array[Int](10)
arr: Array[Int] = Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)

scala> List(1,2,3).copyToArray(arr, 3)
res10: Int = 3

scala> arr
res11: Array[Int] = Array(0, 0, 0, 1, 2, 3, 0, 0, 0, 0)

                
//iterator -----> 迭代器
scala> val it = List("a", "b", "c").iterator
it: Iterator[String] = <iterator>

scala> it.next
res12: String = a

scala> it.next
res13: String = b

scala> it.next
res14: String = c

scala> it.next
java.util.NoSuchElementException: head of empty list
  at scala.collection.immutable.Nil$.head(List.scala:599)
  at scala.collection.immutable.Nil$.head(List.scala:598)
  at scala.collection.StrictOptimizedLinearSeqOps$$anon$1.next(LinearSeq.scala:252)
  ... 28 elided

七、归并排序

归并排序机制:

首先,如果列表有零个或一个元素,那么它已然是排好序的,因此可以直接返回。

更长一些的列表将会被切分成俩个子列表,每个子列表各含约一半原列表的元素。每个子列表被递归地调用同一个函数来排序,然后两个排好序地子列表会通过一次归并操作合在一起。

归并排序的复杂度为nlog(n),其中n为i输入列表的长度

//msort 是柯里化将函数定制为一种特定比较函数的特例
def msort[T](less: (T,T) => Boolean)
(xs:List[T]):List[T] = {
    def merge(xs:List[T], ys:List[T]):List[T] = {
        (xs, ys) match {
            case (Nil, _) => ys
            case (_, Nil) => xs
            case(x::xs1, y::ys1) => {
                if(less(x, y)) x::merge(xs1, ys)
                else y::merge(xs,ys1)
            }
            val n = xs.length/2
            if (n == 0) xs 
            else {
                val (ys,zs) = xs.splitAt(n)
                merge(msort(less)(ys), msort(less)(zs))
            }
        }
    }
}

八、List类的高阶方法

//xs map f 其中类型为List[T]的列表xs, 类型为T=>U的函数f为操作元
//它将返回一个通过应用f到xs的每个元素后得到的列表
scala> List(1,2,3).map(_ + 1)
res16: List[Int] = List(2, 3, 4)

//flatMap 与 map类似,右侧操作元为一个返回元素列表的函数
scala> val words = List("the", "quick", "brown", "fox")
words: List[String] = List(the, quick, brown, fox)

scala> words.map(_.toList)
res17: List[List[Char]] = List(List(t, h, e), List(q, u, i, c, k), List(b, r, o, w, n), List(f, o, x))

scala> words.flatMap(_.toList)
res18: List[Char] = List(t, h, e, q, u, i, c, k, b, r, o, w, n, f, o, x)

//xs foreach p 其中右操作元p是一个过程(结果类型为Unit的函数)
//简单地将过程应用到列表中地每个元素
scala> var sum = 0
sum: Int = 0

scala> List(1, 2, 3).foreach(sum += _)

scala> sum
res20: Int = 6

//xs filter p, 其中两个操作元分别是类型为List[T]的xs 和
//类型为T => Boolean的前提条件函数p
//交出xs中所有p(x)为true的元素x
scala> List(1,2,3,4).filter(_ % 2 == 0)
res21: List[Int] = List(2, 4)

//partition 与 filter类似,但返回的是一对列表,其一为包含所有前提条件为true的元素, 其二为包含所有前提条件为false的元素
//xs partition p == (xs.filter p, xs filter(!p(_)))
scala> List(1,2,3,4).partition(_ % 2 == 0)
res22: (List[Int], List[Int]) = (List(2, 4),List(1, 3))

//xs find p 接收列表xs和前提条件函数p两个操作元,返回一个可选值
//若xs中存在一个元素x满足P(x)为true,那么返回Some(x)
//若对所有元素p均为false,则返回None
scala> List(1,2,3,4).find(_ % 2 == 0)
res23: Option[Int] = Some(2)

scala> List(1,2,3,4).find(_ < 0)
res24: Option[Int] = None

//xs takeWhile p 取表头连续符合p条件的最长前缀
scala> List(2,4,6,5,8).takeWhile(_ % 2 == 0)
res26: List[Int] = List(2, 4, 6)

//xs dropWhile p 去除表头连续符合p条件的最长前缀
scala> List(2,4,6,5,8).dropWhile(_ % 2 == 0)
res27: List[Int] = List(5, 8)

//xs span p == (xs takeWhile p, xs dropWhile p)
scala> List(2,4,6,5,8).span(_ % 2 == 0)
res28: (List[Int], List[Int]) = (List(2, 4, 6),List(5, 8))

//xs forall p 其中xs为列表,p为前提条件
//如果列表中所有元素都满足p则返回true,否则false
scala> List(2,4,6,5,8).forall(_ % 2 == 0)
res29: Boolean = false

//xs exists p 其中xs为列表,p为前提条件
//如果列表中存在一个元素满足前提条件p则返回true,否则返回false
scala> List(2,4,6,5,8).exists(_ % 2 == 1)
res30: Boolean = true

// (z /: xs)(op) =====> 左折叠涉及三个对象:起始值z,列表xs和二元操作op, 折叠结果以z为前缀 
// (z /: List(a, b, c))(op) == op(op(op(z,a), b), c)
scala> val words = List("the", "quick", "brown", "fox")
words: List[String] = List(the, quick, brown, fox)

scala> ("%%" /: words)(_ + " " + _)
            ^
      warning: method /: in trait IterableOnceOps is deprecated (since 2.13.0): Use foldLeft instead of /:
res31: String = %% the quick brown fox

//(List(a, b, c) : z)(op) == op(a, op(b, op(c,z)))
//右折叠与左折叠区别,第一个操作元为要折叠列表,第二个为起始值
scala> (words : "%%")(_ + " " + _)
             ^
      warning: method : in trait IterableOnceOps is deprecated (since 2.13.0): Use foldRight instead of :
res32: String = the quick brown fox %%

//xs sortWith before 其中xs为列表,before为一个 用来比较两个元素的函数
scala> List(1,3,2,-1,0).sortWith(_ < _)
res33: List[Int] = List(-1, 0, 1, 2, 3)

九、List对象方法

//从元素创建列表 List.apply
scala> List.apply(1, 2, 3)
res34: List[Int] = List(1, 2, 3)

//创建数值区间 List.range
//List.range(from, until) -- 创建一个包含从from开始递增搭配until-1的数的列表
scala> List.range(1,5)
res35: List[Int] = List(1, 2, 3, 4)
//List.range(from, until, step) -- step为间隔值
scala> List.range(5, 1, -2)
res36: List[Int] = List(5, 3)

//创建相同元素的列表 List.fill(length)(z)
//length 为大于等于0的复制长度, z为需要重复的元素
scala> List.fill(5)("hello")
res37: List[String] = List(hello, hello, hello, hello, hello)
//参数多于1个时,会创建多维列表
scala> List.fill(5,5)("hello")
res39: List[List[String]] = List(List(hello, hello, hello, hello, hello), List(hello, hello, hello, hello, hello), List(hello, hello, hello, hello, hello),
List(hello, hello, hello, hello, hello), List(hello, hello, hello, hello, hello))

//表格化一个函数 List.tabulate
//tabulate 构建一个根据给定的函数计算的元素的列表,入参与fill一致
scala> List.tabulate(5)(n => n*n)
res40: List[Int] = List(0, 1, 4, 9, 16)

scala> List.tabulate(5,5)(_ * _)
res41: List[List[Int]] = List(List(0, 0, 0, 0, 0), List(0, 1, 2, 3, 4), List(0, 2, 4, 6, 8), List(0, 3, 6, 9, 12), List(0, 4, 8, 12, 16))

//拼接多个列表 List.concat
scala> List.concat(List(1,2),List('a', 'b'))
res43: List[AnyVal] = List(1, 2, a, b)

//同时处理多个列表
scala> (List(10,20), List(1,5)).zipped.map(_ + _)
      ^
      warning: method tuple2ToZippedOps in object Predef is deprecated (since 2.13.0): Use xs.lazyZip(ys).
res45: List[Int] = List(11, 25)


十、Scala类型推断算法

Scala的类型推断基于程序流。对于方法调用m(args),类型推断算法首先检查m的类型是否已知。如果m的类型已知,那么这个类型信息就被用于推断入参的预期类型。若m的类型未知,类型推断算法将优先检查方法入参来决定方法的正确类型。

一般来说,当类型推断算法需要推断一个多态方法的类型参数时,它会考虑第一个参数列表里的所有入参的类型。当我们设计一个接收某些非函数的入参和一个函数入参时,一般将函数入参单独放在最后一个参数列表中。在柯里化的方法调用中只考虑第一个参数列表来判定方法类型的规则是-核心问题。