scala入门

4 篇文章 0 订阅
2 篇文章 0 订阅

scala入门

1、使用scala解释器

在命令提示符窗口输入scala,即可进入scala的命令行模式。输入:

1+2
// 输出3

2、定义变量

Scala的变量分为两种:val和var。

val跟Java的final变量类似,一旦初始化就不能被重新复制。但是变量指向的那个对象是有可能发生改变的。(又得看那个对象是否可变的)

而var则不同,类似Java的非final变量,在整个生命周期内var可以被重新赋值。

val msg = "hello, world"
var i = 0

上面两句时类型推断后的结果。

完整的结构:

val msg:String = "hello, world"
var i:Int = 0

4、定义函数

函数定义由def开始,然后是函数名和圆括号中以逗号隔开的参数列表。

每个参数的后面都必须加上以冒号(:)开始的类型标注,因为scala编译器并不会推断函数参数的类型。

在max的参数列表的右括号之后,": Int"定义的是max函数自己的结果类型(result type)。

函数体是等号和用花括号括起来的。

函数体之前的等号也有特别的含义,表示在函数式的世界观里,函数定义的是一个可以获取到结果值的表达式。

scala的if表达式可以返回一个结果,就像Java的三元表达式(ternary operator)。

def max(x: Int, y: Int): Int = {
    if(x > y) x else y
}

ps:如果函数是递归的(recursive),就必须显式 的给出函数的结果类型。

5、编写scala脚本

虽然scala被设计为帮助程序员构建大型的软件系统,但同时也适用于脚本编写。

echo 'println("Hello, world, from a script")' >> hello.scala

然后执行:

scala hello.scala

命令行参数可以通过名为args的scala数组获取。scala数组的第一个元素式args(0),而不是Java那样的写法args[0]。

5、用while做循环;用if做判断

将下面的代码录入echoargs.scala文件

var i = 0
while(i < args.length){
    if(i != 0)
    	print(" ")
    print(args(i))
    i += 1
}
println()

执行命令:

scala echoargs.scala Scala is even more fun
// 输出:Scala is even more fun

6、用foreach和for遍历

函数式编程语言的主要特征之一就是函数是一等的语法单元。

比如,打印每一个命令行参数的另一种方式:

args.foreach(arg => println(arg))
// 根据函数至简原则,也可以写成这样:
args.foreach(println)

ps:函数字面量的语法:用圆括号括起来的一组带名字的参数、一个右箭头和函数体。

对比指令式编程语言中的for循环,scala只提供for表达式。比如

for (arg <- args){
    println(arg)
}
// <-符号的左边式"arg",这是一个val变量的名字,而不是var
// 实际情况是:对于args数组中的每一个元素,一个新的名为arg的val会被创建出来,初始化成元素的值,这时for表达式的循环体才被执行。

7、用类型参数化数组

参数化的意思是在创建实例时对实例做“配置”,可以用一个值来参数化一个实例,做法是在构造方法的括号中传入对象参数。例如,实例化一个新的java.math.BigInteger,并用值"123456"对他进行参数化:

val big = new java.math.BigInteger("12345")

初始化一个数组

val arr = new Array[String](3+1)

scala通行规则:

如果一个方法只接收一个参数,在调用它的时候,可以不用英文据点或圆括号。

如(1).+(2)可以简写为 1 + 2。

ps:这种方式仅在显式的给出方法调用的目标对象时才有效。不能"println 10"。

scala从技术上讲并没有操作符重载(operator overloading),因为它实际上并没有传统意义上的操作。类似+、-、*、/这样的字符可以被用作方法名。

scala为什么用括号(而不是方括号)访问数组

数组不过是类的实例,这一点跟其它scala实例没有区别。

当你用一组圆括号将一个或多个值包起来,并将其应用(apply)到某个对象时,scala会将这段代码转换成对这个对象的名为apply的方法调用。

同理,当我们尝试对通过圆括号应用了一个或多个参数的变量进行赋值时,编译器会将代码转换成对update方法的调用,这个update方法接收两个参数:圆括号括起来的值,以及等号右边的对象。

ps:当然,这样的代码仅在对象的类型实际上定义了apply、update等方法时才能通过编译。并且scala在编译代码时,会尽可能使用Java数组、基本类型和原生的算数指令。

apply工厂方法

val names = Array("zero","one","two")

实际调用了名为apply的工厂方法,这个方法创建并返回了新的数组。该方法定义在Array的伴生对象中。

8、使用列表

函数式编程的重要理念之一就是方法不能有副作用。一个方法唯一要做的就是计算并返回一个值。

一个好处是方法不在互相纠缠在一起,因此变得更可靠、更易复用。

另一个好处(作为静态类型的编程语言)是类型检查器会检查方法的入参和出参,因此逻辑通常都是以类型错误的形式出现。

将这个函数式的哲学应用到对象的世界意味着让对象不可变。

列表

scala数组是一个拥有相同类型的对象的可变序列。对于需要拥有相同类型的对象的不可变序列的场景,可以使用scala的List类。

利用List类的apply工厂方法,获得一个List对象。

val ontTwoThree = List(1,2,3)

由于List是不可变的,它们的行为优点类似Java的字符串:当你调用列表的某个方法,而这个方法的名字看上去像是会改变列表时,它实际是创建并返回一个带有新值的新的列表。

比如,“::”方法,读作“cons”,他在一个已有列表的前面添加一个新的元素,并返回这个新的列表。

val oneTwo = List(1,2)
val zeroOneTwo = 0 :: oneTwo

如果方法名的最后一个字符是冒号,该方法的调用会发生在它的有操作元上。

表示空列表的快捷方式是Nil,初始化一个新列表的另一种方式是用::将元素串接起来,并将Nil作为最后一个元素。

9、使用元组

元组也是不可变的,可以容纳不同类型的元素。当你需要从方法返回多个对象时,元组非常有用。

在Java中遇到类似情况通常会创建一个类似JavaBean那样的类来承载多个返回值,而用scala可以简单的返回一个元组。

使用:要实例化一个新的元组,只需要将对象放在圆括号当中,用逗号隔开即可。实例化完成后,可以用英文句点、下画线和从1开始的序号来访问每一个元素。

val pair = (99,"Luftballons")
println(pair._1)
println(pair._2)

scala会自动推断出这个元组的类型时Tuple2[Int, String],并将之作为变量pair的类型。

"."跟用于访问字段调用方法时使用的方式相同。

本例中,访问的是一个名为_1的字段。

访问元组中的元素

apply方法永远只返回同一种类型,但元组里的元素可以是不同类型的。这些_N表示的字段名从1开始而不是从0开始,是由其它通用支持静态类型元组的语言设定的传统,比如Haskell和ML

ps:在一组大小相同位置连续的空间里面,每个元素都可以表示为编号*大小。比如

val arr = Array(1,2,3,4)
// 假设arr数组头节点的开始的内存编号为base+0*4,即元素1的位置
// 元素2的位置为base+1*4
// 元素3的位置为base+2*4
// 元素4的位置为base+3*4
// 基于这种快速索引的思想,大多数数组下标都是从0开始的。
// 元组下标从1开始,出了历史原因之外,也说明它的数据结构不是上面描述的那样。

最多定义22个元素的元组。

10、使用集和映射

由于scala兼有函数式和指令式的编程风格的优势,其集合类库特意对可变和不可变的集合进行了区分。比如数组永远是可变的,列表永远是不可变的。同时还提供了集(set)和映射(map)的可变和不可变的不同选择,但使用同样的简单名字。对于集和映射而言,scala通过不同的类继承关系来区分可变和不可变版本。

Set

通过调用Set伴生对象的名为apply的工厂方法创建集。实际调用了scala.collection.immutable.Set的伴生对象的apply方法,返回一个默认的、不可变的Set的对象。

var jetSet = Set("Boeing", "Airbus")
jetSet += "Lear"
// jetSet指向一个包含"Boeing"、"Aribus"、"Lear"的新集。
println(jetSet.contains("Cessna"))

如果想要一个可变集,需要做一次引入(import)。

import语句让你在代码中使用简单名字,比如Set,而不是更长的完整名。

比如需要一个不可变的HaseSet:

import scala.collection.immutable.HashSet
val hashSet = HashSet("tomatoes","chilies")
println(hashSet + "coriander")

ps:当集是不可变的,我们并不需要对hashSet重新赋值,所以它是val。与之对应的是,当集是可变

Map

import scala.collection.mutable
val treasureMap = mutable.Map[Int,String]()
// 工厂方法没有传参时,编译器无法推断出映射的类型。
treasureMap += (1 -> "Go to island.")
// scala允许任何对象调用->方法的机制,即隐式转换(impllict conversion)
treasureMap += (2 -> "Find big X on ground.")
treasureMap += (3 -> "Dig.")
println(treasureMap(2))
// apply根据key获取value值

不向工厂方法传入任何内容,创建一个空的映射。通过->和+=方法向映射添加键值对(key/value pair)。scala编译器会将二元的操作,如1 -> "Go to island."转成成标准的方法调用,集(1).->(“Go to island.”)。可以在scala的任何对象上调用这个->方法,它将返回包含键和值两个元素的元组。

ps:一般有是否可变选择的,默认都是不可变的。Array和List被单独拆开了。

11、识别函数式编程风格

val变量

一个显著标志时如果代码包含var变量的,它通常时指令式风格的;而如果代码完全没有var(也就死说代码只包含val),那么它可能时函数式的。

因此,一个向函数式风格转变的方向是尽可能不用var。

输出函数

带有副作用的标志性特征是结果类型为Unit。如果一个函数并不返回任何有意的值,也就是Unit这样结果类型所表达的意思,那么这个函数存在于世上的唯一意义就是产生某种副作用。

函数式编程的做法式顶一个将传入的args作为格式化(用于打印)的方法,但知识返回这个格式化的字符串。没有副作用的函数式代码

def formatArgs(args: Array[String]) = args.mkString("\n")

mkString方法可以被用于任何可被跌打访问的集合(包括数组、列表、集、映射),返回一个包含了对所有元素调用toString的结果的字符串,传入的字符串分割。

然后将它的结果传给println进行输出:

println(formatArgs(args))
// 返回值为Unit

每个有用的程序都会有某种形式的副作用。否则它对于外部世界就没有任何价值。倾向于无副作用的函数鼓励设计出带有副作用最小化的程序。这样做的好处之一式让你的程序更容易测试。比如

val res = formatArgs(Array("zero","one","two"))
assert(res == "zero\none\ntwo")

要测试formatArgs函数变得非常简单,只需要检查它的结果即可。

ps:scala程序员的平衡心态

倾向于使用val、不可变对象和没有副作用的方法,优先选择它们。不过当有特定的需要和理由时,也不要拒绝var、可变对象和带有副作用的方法。

12、从文件读取文本行

import scala.io.Source
// 引入scala.io包的名为Source的类。然后检查是不是命令行至少给出了一个参数。
if(args.length > 0){
    for(line <- Source.fromFile(agrs(0)).getLines())
    // 表达式Source.fromFile(args(0))尝试打开指定的文件并返回一个Source对象,在这个对象上,继续调用getLines方法。getLines方法返回一个Iterator[String],每次迭代都给出一行内容,去掉最后的换行符。
    	println(line.length + " " + line)
}
else
	Console.err.println("Please enter filename")
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值