scala方法与函数、闭包、字符串
一、scla方法与函数
Scala 有方法与函数,二者在语义上的区别很小。
- Scala 方法是类的一部分,而函数是一个对象,可以赋值给一个变量。换句话来说在类中定义的函数即是方法。
Scala 中的方法跟 Java 的类似,方法是组成类的一部分。
Scala 中的函数则是一个完整的对象,Scala 中的函数其实就是继承了 Trait 的类的对象。
Scala 中使用 val 语句可以定义函数,def 语句定义方法。
eg:
def m(x:Int) = x+3 val f = (x:Int) => x+3
注意:有些翻译上函数(function)与方法(method)是没有区别的。
1、方法声明
格式:
- def functionName ([参数列表]) : [return type]
不写等于号和方法主体,那么方法会被隐式声明为抽象(abstract),包含它的类型于是也是一个抽象类型。
2、方法定义
def 关键字
格式:
def functionName ([参数列表]) : [return type] = {
function body
return [expr]
}
说明:return type 可以是任意合法的 Scala 数据类型。参数列表中的参数可以使用逗号分隔。
如果方法没有返回值,可以返回为 Unit,这个类似于 Java 的 void
def addInt(a:Int,b:Int):Int = { var sum:Int = 0 sum = a+b return sum } def printMe():Unit = { println("Hello,Scala!") }
3、方法调用
Scala 提供了多种不同的方法调用方式:
以下是调用方法的标准格式:
functionName( 参数列表 )
如果方法使用了实例对象来调用,可以使用类似java的格式 (使用 . 号):
[instance.]functionName( 参数列表 )
eg:
object Test { def main(args: Array[String]){ println(addInt(5,7)) } def addInt(a:Int,b:Int):Int = { var sum:Int = 0 sum = a+b return sum } Scala 也是一种函数式语言,所以函数是 Scala 语言的核心。
1、Scala 函数传名调用(call-by-name)
Scala的解释器在解析函数参数(function arguments)时有两种方式:
- 传值调用(call-by-value):先计算参数表达式的值,再应用到函数内部;
- 传名调用(call-by-name):将未计算的参数表达式直接应用到函数内部
在进入函数内部前,传值调用方式就已经将参数表达式的值计算完毕,而传名调用是在函数内部进行参数表达式的值计算的。
这就造成了一种现象,每次使用传名调用时,解释器都会计算一次表达式的值。
eg:
object Test { def time() = { println("获取时间,单位为纳秒") System nanoTime() } def delayed(t: => Long) = { println("在delayed方法内") println("参数:" + t) t } def main(args: Array[String]){ delayed(time()) } }
运行结果:
在delayed方法内
获取时间,单位为纳秒
参数:19655563565059
获取时间,单位为纳秒
说明:delay 方法打印了一条信息表示进入了该方法,接着 delay 方法打印接收到的值,最后再返回 t。
2、Scala 指定函数参数名
一般情况下函数调用参数,就按照函数定义时的参数顺序一个个传递。但是也可以通过指定函数参数名,并且不需要按照顺序向函数传递参数
eg:
object Test { def printInt(a:Int,b:Int) = { println(a) println(b) } def main(args: Array[String]) { printInt(b=5,a=7) } }
3、Scala 函数 - 可变参数
Scala 允许指明函数的最后一个参数可以是重复的,即不需要指定函数参数的个数,可以向函数传入可变长度参数列表。
Scala 通过在参数的类型之后放一个星号来设置可变参数(可重复的参数)。
eg:
4、Scala 递归函数
递归函数在函数式编程的语言中起着重要的作用。
- Scala 同样支持递归函数。
- 递归函数意味着函数可以调用它本身。
eg:使用递归函数来计算阶乘:
object Test { def factorial(n:BigInt) : BigInt ={ if (n <= 1) 1 else n*factorial(n-1) } def main(args: Array[String]) { for (i <- 1 to 10) println(i + "的阶乘为:=" + factorial(i)) } }
运行结果:
5、Scala 函数 - 默认参数值
Scala 可以为函数参数指定默认参数值,使用了默认参数,在调用函数的过程中可以不需要传递参数,这时函数就会调用它的默认参数值,如果传递了参数,则传递值会取代默认值。
eg:
object Test { def main(args: Array[String]) { println( "返回值 : " + addInt() ); //12 } def addInt( a:Int=5, b:Int=7 ) : Int = { var sum:Int = 0 sum = a + b return sum } }
6、Scala 高阶函数
高阶函数(Higher-Order Function)就是操作其他函数的函数。
- Scala 中允许使用高阶函数, 高阶函数可以使用其他函数作为参数,或者使用函数作为输出结果。
以下实例中,apply() 函数使用了另外一个函数 f 和 值 v 作为参数,而函数 f 又调用了参数 v:
eg:
object Test { def main(args: Array[String]) { println(apply(layout,10)) //[10] } def apply(f:Int => String,v:Int) = f(v) def layout[A](x:A) = "[" + x.toString() + "]" }
7、Scala 函数嵌套
在 Scala 函数内定义函数,定义在函数内的函数称之为局部函数。
eg:实现阶乘运算,并使用内嵌函数:
object Test { def main(args: Array[String]) { println( factorial(0) ) //1 println( factorial(1) ) //1 println( factorial(2) ) //2 println( factorial(3) ) //6 } def factorial(i: Int): Int = { def fact(i: Int, accumulator: Int): Int = { if (i <= 1) accumulator else fact(i - 1, i * accumulator) } fact(i, 1) } }
8、Scala 匿名函数
Scala 中定义匿名函数的语法:箭头左边是参数列表,右边是函数体。
eg:接受一个Int类型输入参数的匿名函数:
var inc = (x:Int) => x+1
等价于
def add2 = new Function1[Int,Int]{ def apply(x:Int):Int = x+1; }
eg:var x = inc(7)-1
在匿名函数中定义多个参数:
var mul = (x: Int, y: Int) => x*y println(mul(3, 4))
也可以不给匿名函数设置参数,如下所示:
var userDir = () => { System.getProperty("user.dir") } println( userDir() )
eg:
object Test { def main(args: Array[String]) { println( "multiplier(1) value = " + multiplier(1) ) println( "multiplier(2) value = " + multiplier(2) ) } var factor = 3 val multiplier = (i:Int) => i * factor }
9、Scala 偏应用函数
Scala 偏应用函数是一种表达式,不需要提供函数需要的所有参数,只需要提供部分,或不提供所需参数。
eg:打印日志信息:
import java.util.Date object Test { def main(args: Array[String]) { val date = new Date log(date, "message1" ) Thread.sleep(1000) log(date, "message2" ) Thread.sleep(1000) log(date, "message3" ) } def log(date: Date, message: String) = { println(date + "----" + message) } }
说明:log() 方法接收两个参数:date 和 message。在程序执行时调用了三次,参数 date 值都相同,message 不同。
可以使用偏应用函数优化以上方法,绑定第一个 date 参数,第二个参数使用下划线(_)替换缺失的参数列表,并把这个新的函数赋给变量。以上实例修改如下:
import java.util.Date object Test { def main(args: Array[String]) { val date = new Date val logWithDateBound = log(date,_:String) logWithDateBound("message1") Thread.sleep(1000) logWithDateBound("message2") Thread.sleep(1000) logWithDateBound("message3") } def log(date: Date, message: String) = { println(date + "----" + message) } }
10、Scala 函数柯里化(Currying)
柯里化(Currying)指的是将原来接受两个参数的函数变成新的接受一个参数的函数的过程。新的函数返回一个以原有第二个参数为参数的函数。
eg:定义一个函数:
def add(x:Int,y:Int)=x+y
add(1,2)
def add(x:Int)(y:Int) = x + y
add(1)(2)
最后结果都一样是3,这种方式(过程)就叫柯里化。
-
实现过程
add(1)(2) 实际上是依次调用两个普通函数(非柯里化函数),第一次调用使用一个参数 x,返回一个函数类型的值,第二次使用参数y调用这个函数类型的值。
def add(x:Int)=(y:Int)=>x+y 接收一个x为参数,返回一个匿名函数,该匿名函数的定义是:接收一个Int型参数y,函数体为x+y。 val result = add(1) 返回一个result,那result的值应该是一个匿名函数:(y:Int)=>1+y 所以为了得到结果,我们继续调用result。 val sum = result(2) 最后打印出来的结果就是3。
eg:
object Test { def main(args: Array[String]) { val str1:String = "Hello, " val str2:String = "Scala!" println( "str1 + str2 = " + strcat(str1)(str2) ) } def strcat(s1: String)(s2: String) = { s1 + s2 } }
二、Scala 闭包
闭包(closure):计算机编程领域的专业名词,指可以包含自由(未绑定到特定对象)变量的代码块
闭包源于要执行的代码块和为自由变量提供绑定的计算环境(作用域)两者的结合。
闭包是一个函数,返回值依赖于声明在函数外部的一个或多个变量。
闭包通常来讲可以简单的认为是可以访问一个函数里面局部变量的另外一个函数。
val multiplier = (i:Int) => i * 10 val multiplier = (i:Int) => i * factor 在 multiplier 中有两个变量:i 和 factor。其中的一个 i 是函数的形式参数,在 multiplier 函数被调用时,i 被赋予一个新的值。然而,factor不是形式参数,而是自由变量,考虑下面代码: var factor = 3 val multiplier = (i:Int) => i * factor 这里引入一个自由变量 factor,这个变量定义在函数外面。 函数变量 multiplier 成为一个"闭包",因为它引用到函数外面定义的变量,定义这个函数的过程是将这个自由变量捕获而构成一个封闭的函数。
即闭包=代码+用到的非局部变量
在函数外面声明了一个全局变量factor,值函数中的factor捕获到这个全局变量,从而使i*factor变成一个正常的函数定义,使函数定义闭合,形成闭包。
eg:
object Test { def main(args: Array[String]) { println( "muliplier(1) value = " + multiplier(1) ) println( "muliplier(2) value = " + multiplier(2) ) } var factor = 3 val multiplier = (i:Int) => i * factor }
三、Scala 字符串
eg: object Test { object Test { //将字符串赋值给一个常量: val greeting: String = "Hello,World!" def main(args: Array[String]) { println( greeting ) } } } 以上实例定义了变量 greeting,为字符串常量,它的类型为 String (java.lang.String)。
- 在 Scala 中,字符串的类型实际上是 Java String,它本身没有 String 类。
- 在 Scala 中,String 是一个不可变的对象,所以该对象不可被修改。这就意味着你如果修改字符串就会产生一个新的字符串对象。
1、创建字符串
eg:
var greeting = "Hello World!";
或
var greeting:String = "Hello World!";
不一定为字符串指定 String 类型,因为 Scala 编译器会自动推断出字符串的类型为 String。
当然也可以直接显示的声明字符串为 String 类型
eg:
object Test {
val greeting: String = "Hello, World!"
def main(args: Array[String]) {
println( greeting )
}
}
String 对象是不可变的,如果需要创建一个可以修改的字符串,可以使用 String Builder 类
eg:
object Test { def main(args: Array[String]) { val buf = new StringBuilder; buf += 'a' buf ++= "bcdef" println( "buf is : " + buf.toString ) //buf is : abcdef } }
2、字符串长度
可以使用 length() 方法来获取字符串长度:
object Test { def main(args: Array[String]) { var palindrome = "www.runoob.com"; var len = palindrome.length(); println( "String Length is : " + len ); } }
3、字符串连接
String 类中使用 concat() 方法或加号(+)来连接两个字符串:
string1.concat(string2);
eg:
object Test { def main(args: Array[String]) { var str1 = "菜鸟教程官网:"; var str2 = "www.runoob.com"; var str3 = "菜鸟教程的 Slogan 为:"; var str4 = "学的不仅是技术,更是梦想!"; println( str1 + str2 ); println( str3.concat(str4) ); }
4、创建格式化字符串
String 类中可以使用 printf() 方法来格式化字符串并输出,String format() 方法可以返回 String 对象而不是 PrintStream 对象。
eg:
object Test { def main(args: Array[String]) { var floatVar = 12.456 var intVar = 2000 var stringVar = "菜鸟教程!" var fs = printf("浮点型变量为 " + "%f, 整型变量为 %d, 字符串为 " + " %s", floatVar, intVar, stringVar) println(fs) //浮点型变量为 12.456000, 整型变量为 2000, 字符串为 菜鸟教程!() } }
5、String 方法
下表列出了 java.lang.String 中常用的方法,可以在 Scala 中使用:
序号 | 方法及描述 |
---|---|
1 |
char charAt(int index) 返回指定位置的字符 |
2 |
int compareTo(Object o) 比较字符串与对象 |
3 |
int compareTo(String anotherString) 按字典顺序比较两个字符串 |
4 |
int compareToIgnoreCase(String str) 按字典顺序比较两个字符串,不考虑大小写 |
5 |
String concat(String str) 将指定字符串连接到此字符串的结尾 |
6 |
boolean contentEquals(StringBuffer sb) 将此字符串与指定的 StringBuffer 比较。 |
7 |
static String copyValueOf(char[] data) 返回指定数组中表示该字符序列的 String |
8 |
static String copyValueOf(char[] data, int offset, int count) 返回指定数组中表示该字符序列的 String |
9 |
boolean endsWith(String suffix) 测试此字符串是否以指定的后缀结束 |
10 |
boolean equals(Object anObject) 将此字符串与指定的对象比较 |
11 |
boolean equalsIgnoreCase(String anotherString) 将此 String 与另一个 String 比较,不考虑大小写 |
12 |
byte getBytes() 使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中 |
13 |
byte[] getBytes(String charsetName 使用指定的字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中 |
14 |
void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) 将字符从此字符串复制到目标字符数组 |
15 |
int hashCode() 返回此字符串的哈希码 |
16 |
int indexOf(int ch) 返回指定字符在此字符串中第一次出现处的索引 |
17 |
int indexOf(int ch, int fromIndex) 返回在此字符串中第一次出现指定字符处的索引,从指定的索引开始搜索 |
18 |
int indexOf(String str) 返回指定子字符串在此字符串中第一次出现处的索引 |
19 |
int indexOf(String str, int fromIndex) 返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始 |
20 |
String intern() 返回字符串对象的规范化表示形式 |
21 |
int lastIndexOf(int ch) 返回指定字符在此字符串中最后一次出现处的索引 |
22 |
int lastIndexOf(int ch, int fromIndex) 返回指定字符在此字符串中最后一次出现处的索引,从指定的索引处开始进行反向搜索 |
23 |
int lastIndexOf(String str) 返回指定子字符串在此字符串中最右边出现处的索引 |
24 |
int lastIndexOf(String str, int fromIndex) 返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索 |
25 |
int length() 返回此字符串的长度 |
26 |
boolean matches(String regex) 告知此字符串是否匹配给定的正则表达式 |
27 |
boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len) 测试两个字符串区域是否相等 |
28 |
boolean regionMatches(int toffset, String other, int ooffset, int len) 测试两个字符串区域是否相等 |
29 |
String replace(char oldChar, char newChar) 返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的 |
30 |
String replaceAll(String regex, String replacement 使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串 |
31 |
String replaceFirst(String regex, String replacement) 使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串 |
32 |
String[] split(String regex) 根据给定正则表达式的匹配拆分此字符串 |
33 |
String[] split(String regex, int limit) 根据匹配给定的正则表达式来拆分此字符串 |
34 |
boolean startsWith(String prefix) 测试此字符串是否以指定的前缀开始 |
35 |
boolean startsWith(String prefix, int toffset) 测试此字符串从指定索引开始的子字符串是否以指定前缀开始。 |
36 |
CharSequence subSequence(int beginIndex, int endIndex) 返回一个新的字符序列,它是此序列的一个子序列 |
37 |
String substring(int beginIndex) 返回一个新的字符串,它是此字符串的一个子字符串 |
38 |
String substring(int beginIndex, int endIndex) 返回一个新字符串,它是此字符串的一个子字符串 |
39 |
char[] toCharArray() 将此字符串转换为一个新的字符数组 |
40 |
String toLowerCase() 使用默认语言环境的规则将此 String 中的所有字符都转换为小写 |
41 |
String toLowerCase(Locale locale) 使用给定 Locale 的规则将此 String 中的所有字符都转换为小写 |
42 |
String toString() 返回此对象本身(它已经是一个字符串!) |
43 |
String toUpperCase() 使用默认语言环境的规则将此 String 中的所有字符都转换为大写 |
44 |
String toUpperCase(Locale locale) 使用给定 Locale 的规则将此 String 中的所有字符都转换为大写 |
45 |
String trim() 删除指定字符串的首尾空白符 |
46 |
static String valueOf(primitive data type x) 返回指定类型参数的字符串表示形式 |