Scala编程基础12:Scala函数3

1.Scala高阶函数

高阶函数(Higher-Order Function)就是操作其他函数的函数。Scala允许使用高阶函数,高阶函数可以使用其他函数作为参数,或者使用函数作为输出结果。以下实例中,apply()函数使用了另一个函数f和值v作为参数,而函数f又调用了参数v:

object Test1 {
    def main(args:Array[String]){
        println(apply(layout, 10));
    }
    def apply(f: Int => String, v: Int) = f(v);
    def layout[A](x: A) = "[" + x.toString() + "]";
}

编译并执行以上代码,输出结果如下:

E:\Scala>scalac Test1.scala
E:\Scala>scala Test1
[10]

2.内嵌函数

Scala支持内嵌函数:即在一个函数内部定义另一个函数,定义在函数内部的函数称为局部函数。以下实例使用内嵌函数来实现阶乘运算:

object Test2 {
    def main(args:Array[String]){
        println(factorial(0));
        println(factorial(1));
        println(factorial(2));
        println(factorial(3));
    }
    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);
    }
}

编译并执行以上代码,输出结果如下:

E:\Scala>scalac Test2.scala
E:\Scala>scala Test2
1
1
2
6

3.Scala匿名函数

Scala中定义匿名函数的语法很简单,使用一个箭头”=>”,箭头左边是参数列表,箭头右边是函数体。使用匿名函数可以简化代码。下面的表达式定义了一个匿名函数,接受一个Int类型的输入参数x,函数体的功能是将x+1后返回,将该匿名函数赋值给一个变量inc:

var inc = (x:Int)=>x+1

上述定义的匿名函数,等价于下面的函数形式:

def add2 = new Function1[Int,Int]{
def apply(x:Int):Int = x+1;
}

以上实例的inc现在可以作为一个函数,使用方法如下:

var res = inc(99)

同样匿名函数可以有多个输入参数:

var mul = (x:Int,y:Int)=>x*y

现在mul可以作为一个函数,使用方式如下:

println(mul(3,4))

当然也可以没有输入参数:

var userDir = ()=>{System.getProperty("user.dir");}

现在userDir可以作为一个函数使用,使用方式如下:

println(userDir())

注意:前面介绍的Scala普通函数参数列表的特性同样适用于Scala匿名函数。

举个例子:

object Test3 {
    def main(args:Array[String]){
        println("multiplier(1) value = "+multiplier(1));
        println("multiplier(2) value = "+multiplier(2));
    }
    var factor = 3;
    var multiplier = (i:Int)=>i*factor;
}

编译并执行以上代码,输出结果如下:

E:\Scala>scalac Test3.scala
E:\Scala>scala Test3
multiplier(1) value = 3
multiplier(2) value = 6

4.Scala偏应用函数

Scala偏应用函数是一种表达式,不需要提供函数需要的所有的参数,只需要提供部分,或者不提供所需参数。如下实例,打印日志信息:

import java.util.Date;
object Test4 {
    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);
    }
}

编译并执行以上代码,输出结果如下:

E:\Scala>scalac Test4.scala
E:\Scala>scala Test4
Tue May 15 21:40:16 CST 2018----message1
Tue May 15 21:40:16 CST 2018----message2
Tue May 15 21:40:16 CST 2018----message3

以上实例模拟了一个记录日志的程序,在实例中,log()方法接收两个参数:date和message。在程序执行过程中,调用了三次,参数date的值都相同,message值不同。我们可以使用偏应用函数简化以上方法,绑定第一个参数date,第二个参数使用下划线(_)替换缺失的参数列表,并把这个新的函数值的索引赋值给一个变量,修改后实例如下:

import java.util.Date;
object Test5 {
    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);
    }
}

编译并执行以上代码,输出结果如下:

E:\Scala>scalac Test5.scala
E:\Scala>scala Test5

Tue May 15 21:49:38 CST 2018----message1
Tue May 15 21:49:38 CST 2018----message2
Tue May 15 21:49:38 CST 2018----message3

以上实例相当于将原来两个参数的函数重新定义为一个参数的函数,简化了程序实现。

5.Scala函数柯里化(Currying)

柯里化(Currying)指的是将原来接受两个参数的函数变成新的接受一个参数的函数的过程。新的函数返回一个以原有第二个参数为参数的函数。比较抽象,我们看一个实例来更好的理解这个过程:

正常情况下,定义一个函数的方式如下:

def add(x:Int,y:Int)=x+y

调用这个函数的方式如下:

add(1,2)

现在我们将这个函数变形一下:

def add(x:Int)(y:Int)=x+y

此时应该这样调用该函数:

add(1)(2)

等价于前一种调用方式,结果都是3.

像这样,将一个多维函数变形为一个一维函数链的过程,就叫做Scala的函数柯里化过程。

实现过程

add(1)(2)实际上是依次调用了两个普通函数(非柯里化函数),第一个调用使用一个参数x,返回一个函数类型的值,第二次使用参数y调用这个返回的函数,得到最终的计算结果。

实质上最先演变成这样一个方法:

def add(x:Int)=(y:Int)=>x+y

这个函数接收一个x为参数,返回一个匿名函数,该匿名函数又接收一个y参数,函数体为x+y。现在对这个方法进行调用:

val result = add(1)

这里的result是一个匿名函数:

(y:Int)=>1+y

所以为了得到最终的结果,继续调用result函数:

val sum = result(2)

最终打印出来的结果就是3.

下面是一个完整的实例:

object Test6 {
    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;
    }
}

编译并执行以上代码,输出结果如下:

E:\Scala>scalac Test6.scala
E:\Scala>scala Test6
str1 + str2 = Hello, Scala !

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容