一、Scala 入门

本贴最后更新于 2084 天前,其中的信息可能已经物是人非

一、Scala 入门


val msg = "hello world !"

Scala 对于这样的定义能够根据类型推导判断出 msg 的真正类型,但是在定义函数的时候,如下:

def hof(f: Int => Int, str: String): String = {
    f(str.toInt).toString
  }

参数列表中的参数必须要指定参数类型,因为这里无法进行类型推导。但是函数的结果类型是可以被推导出来的。

在 Java 中,从方法里返回的值的类型被称为返回类型。在 Scala 中,同样的概念被叫做结果类型!
Java 中的自增符号(如:i++、++i) 在 Scala 中是不起作用的。
Scala 中所有的操作符都是方法调用,例如:1+2 = (1).+(2) 、0 to 2 = (0).to(2),注意:这种语法只有在明确方法调用的接受者才有效。例如不能写成 println 10,但是可以写成 Console println 10 。


Array & List

Scala 数组是可变的同类对象序列,例如,Array[String] 的所有对象都是 String,而且尽管数组在实例化之后长度固定(长度不可变),但是它的元素值却是可变的。所以说数组是可变的。
Scala 中列表类(List)才是不可变的同类对象序列,Scala 中的 List 和 Java 中的不一样,Scala 中的 List 一旦创建了就不可变。实际上,Scala 的列表是为了实现函数式风格的编程而设计的。
集合或数组中的 apply 方法其实就是一个拥有可变参数的工厂方法,定义在相应的伴生对象中。

val array = Array.apply(1, 2, 3);
array(0) = 1

val list = List.apply(1, 2, 3)
list(0) = 1 // 直接报错,因为List一旦创建,其中的值就不可改变。

列表的合并
使用 ::::: 都可以将两个 List 合并到一起。其实这两个东西的本质就是两个方法,不过这两个方法的方法名都是以 : 结尾,当方法名以冒号结尾时,方法是被右操作数调用

val list1 = List.apply(1, 2, 3)
val list2 = List.apply(4, 5, 6)
println(0 :: list1) // List(0, 1, 2, 3)
println(list1 ::: list2) // List(1, 2, 3, 4, 5, 6)
println(list1.::(0)) // List(0, 1, 2, 3)
println(list1 :: 0) // 报错,因为该方法的以冒号结尾,调用者是右操作数,但是Int没有这个方法
println(0 ::: list1) // 报错,因为 ::: 只能作用与两个List之间
val list3 = 1 :: 2 :: 3 :: 4 :: Nil
println(list3) // result:List(1, 2, 3, 4)

Nil 是空列表的简写,所以可以使用 consa(::)操作符把所有元素都串起来,因为::的调用者是右操作数。
为什么列表不支持添加(append)操作?
List 类没有提供 append 操作,因为随着列表增长,append 的耗时将呈线性增长,而是用 :: 做前缀则仅耗用固定的时间。如果想通过添加元素来构造列表,有两个选择:

1.把想添加的元素通过前缀进去,然后再调用 reverse。
2.使用 ListBuffer,一种弄提供 append 操作的可变列表,完成后再调用 toList。

QQ 截图 20171026105905.png-84.2kB

QQ 截图 20171026110017.png-78.2kB


Tuple

元组(Tuple) 与列表一样,都是不可变的,但是 与列表的区别是 元组可以包含不同类型的元素。在 Java 中可以用 JavaBean 来封装参数,在 Scala 中可以使用 Tuple 达到相同的效果,而且更简单。例如:

val pair = (123, "字符串")
println(pair._1)
println(pair._2)

元组的实际类型取决于它含有的元素数量和这些元素的类型。理论上可以创建任意长度的元组,但是当前 Scala 库仅支持到 Tuple22。
为什么不能用访问列表的方法来访问元组?
如,为什么不能用 pair(0)。原因是列表中 apply 方法始终返回同样的类型,但是元组中的数据类型不尽相同,而且元组的索引是从 1 开始的。


Set & Map

Scala 中的 Set 和 Map 也有可变和不可变两种类型,分别在不同包下进行实现,其中 immutable 包下是不可变的。
QQ 图片 20171026113747.png-27kB
QQ 截图 20171026113839.png-23.4kB

Set:
Scala 中 Set 默认使用的是不可变的,但是不论是否可变,两者都可以调用 += 这个方法,其中 不可变的调用这个方法会返回一个新的 Set,所以在要用 var 来声明。Map 同理。

var set1 = HashSet.apply(1, 2, 3)
set1 += 4
import collection.mutable.HashSet
val set2 = HashSet.apply(1, 2, 4)
set2 += 3
println(set1) // Set(1, 2, 3, 4)
println(set2) // Set(1, 2, 3, 4)
var map1 = HashMap.apply(1 -> "1", 2 -> "2")
map1 += 3 -> "3"
import collection.mutable.HashMap
val map2 = HashMap.apply(1 -> "1", 2 -> "2")
map2 += 3 -> "3"

函数式风格

没有副作用的方法既是函数式风格。要想保证函数无副作用这项特性,只能依靠编程人员的习惯,即

1,函数入口使用参数运算,而不修改它
2,函数内不修改函数外的变量,如全局变量
3,运算结果通过函数返回给外部(出口)

我理解的副作用是,返回值为空、在函数内部修改参数对象状态或外部状态。这里要仔细想想,多琢磨一下。

    def widthOfLength(s: String) = s.length.toString.length

    val lines = Source.fromFile("D:\\nio-data.txt").getLines().toList
    val maxWidth = lines.reduceLeft(
      (a, b) => if (a.length > b.length) a else b
    ).length.toString.length

    for (line <- lines) {
      val numSpaces = maxWidth - widthOfLength(line)
      val padding = " " * numSpaces
      println(padding + line.length + " | " + line)
    }

输出:

10 | test123456
 3 | 123
 4 | 1231
 2 | 23
 2 | 23
 1 | 1

从文件读取内容,并将每行的长度和内容按照对齐的方式打印出来。
第三行中 getLines()之后必须要 toList,否则程序不会报错,但是会没有任何内容打印。因为 getLines()返回的是枚举器,一旦完成遍历,枚举器就失效了;而通过 toList()把它转换成 List,就可以把文件所有行存到内存中,可以随时使用。
第四行 lines.reduceLeft 这个方法比较新颖。具体自行百度。

  • Simon
    20 引用 • 10 回帖
  • Scala

    Scala 是一门多范式的编程语言,集成面向对象编程和函数式编程的各种特性。

    13 引用 • 11 回帖 • 107 关注
  • 函数式
    9 引用 • 12 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • UniVista

    感觉和 python 差不多啊!

    1 回复
  • 其他回帖
  • shiweichn
    作者

    从语法层面来看很多语言都有相似之处。我认为 Scala 可以比 Python 写出更地道的 函数式代码。😄