scala函数、运行机制(代码+注释)、柯里化的四种写法

/* 阅读建议 —— 读本文之前,

* 假设你使用过java,假设你使用过python

* 假设你了解不同的编程语言有不同的特性和使用场景

*/

首先,不要被SCALA的奇葩语法吓到了!scala也是一种函数式编程,它把java语言脚本化了,给人的感觉就是“所见即所得”,这一特性类似于Linux下的bash脚本,还有python语言也是脚本语言。

scala结合了java和python的优点,能静能动,很灵活。scala的语法很简洁,功能也很强大,但是代码很精简。实现同样的功能,代码量比java语言要少很多。scala代码的可读性也不错,虽然scala语法给人的感觉诡异、奇葩,但是熟悉之后就不奇葩了呀,就像你刚开始学C语言或者Java的时候也觉得语法诡异,但是熟悉之后就不觉得诡异了呀,对不对?

个人来说,我更愿意使用scala来写spark的应用。


————————————————————

【定义函数,创建函数】

(1)定义函数时带小括号,但是小括号里无参,那么调用此函数时带不带括号都OK, 

scala解释器示例:

Welcome to Scala 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_111).

Type in expressions for evaluation. Or try :help.


// 函数名带小括号, 无参。冒号: 声明了返回值类型为字符串

scala> def func1(): String = {

     |  var s = "hello scala~"

     |  s

     | }

func1: ()String        // 等号=意味着函数有返回值,若无等号则返回空(没有返回值)


scala> func1

res11: String = hello scala~


scala> print(func1)

hello scala~

scala> print(func1())

hello scala~


(2)定义函数时不带小括号,那么调用此函数时就不可以带小括号!

在scala解释器的示例如下:

scala> def func2: String = {

     |  var s2 = "hello again"

     |  s2

     | }

func2: String


scala> print(func2)

hello again

scala> print(func2())

<console>:13: error: not enough arguments for method apply: (index: Int)Char in class StringOps.

Unspecified value parameter index.

       print(func2())     //定义函数时无(),调用时用()则报错!

                  ^


(3)给函数传参

(3.1)按照定义函数时的参数顺序逐个传入。  IntelliJ IDEA的完整示例如下

object TestClosPack {

   
def main(args: Array[String]){
       
val str = test_args(2,1)      //调用函数,按定义时的参数顺序传参
        for(s <- str){print(s)}
    }


   
def test_args(a:Int, b:Int)={"|scala|"* (a+b)}
}


输出结果:

|scala||scala||scala|


(3.2)按照参数名称,但不按照参数顺序传入. IntelliJ IDEA的示例如下

object TestClosPack {

   
def main(args: Array[String]){
       
val str = test_args(b=1,a=2)    //调用函数,指定参数名称, 但不按照定义时的顺序传参
        for(s <- str){print(s)}
    }

   
def test_args(a:Int, b:Int)={"|scala|"* (a+b)}
}


输出结果:

|scala||scala||scala|


(4)匿名函数

语法:使用=>符号。箭头的左边是参数列表,右边是函数体,参数的类型可省略,Scala的类型推测系统会推测出参数的类型。使用匿名函数能让我们的代码变得简洁。

示例一:指定参数类型

// (a: Int, b:Int)  => a * b返回一个匿名函数,把此函数赋给变量multiply

scala> var multiply =  (a: Int, b:Int)  => a * b

multiply: (Int, Int) => Int = <function2>


scala> multiply(2,4)//调用匿名函数时,要加小括号,在小括号里面传参

res16: Int = 8


示例二:省略参数

scala> var currentUser = () => System.getProperty("user.name")    //获取此系统的当前用户

currentUser: () => String = <function0>


scala> currentUser()

res21: String = tonykidkid


scala> var currentMillsTime = () => System.currentTimeMillis    //获取当前系统(毫秒)时间戳

currentMillsTime: () => Long = <function0>


scala> currentMillsTime()//调用匿名函数时,要加小括号

res22: Long = 1492871662593



————————————————————

【函数的返回值问题】

1,声明函数的返回值类型,则在函数返回时一定要符合返回类型

2,可以不声明函数返回值类型,scala解释器会自动推断返回的数据类型

3,递归函数必须显式声明返回的类型

4,定义函数时没有等号= 则没有返回值。这类函数相当于一个执行过程

1,示例:

scala> def func3(a:Int) :Int = {

     |  a+10             // 两个整数相加,结果还是整数

     | }

func3: (a: Int)Int    // 意思是:形式参数的数据类型是Int,返回类型是Int


scala> func3(5)

res16: Int = 15


scala> print(func3(5))

15

scala> def func4(a:Int) :Int = {        //“: Int”方式声明了返回值类型为整数

     |  a + "10"

     | }

<console>:12: error: type mismatch;

 found   : String

 required: Int

        a + "10"    //错误:整数和字符串相加得到字符串,但是函数要求返回值类型是整数

          ^


2,定义函数时不声明返回类型 示例:

scala> def func5(a:Int) = {//未声明返回的类型,则自动推断,若无法推断则会报错

     |  a + "10"

     | }

func5: (a: Int)String    // 意思是:形式参数的数据类型是Int,返回类型是String


scala> func5 (3)

res18: String = 310


scala> :type func5(3)    //查看数据类型使用:type命令

String


3,递归函数必须声明返回值类型,以斐波那契数列为例:

/**斐波那契数列 ——第一个数是0,第二个数是1,
*
从第三个数开始,每个数都是它前两个的和: 0,1,1,2,3,5,8,13,21...

*/

scala> def fiboNums(num: Int): Int = {    //递归函数必须声明返回值类型

     |             if (num == 1) {return 0}

     |             if (num == 2) { 1}

     |             else {

     |                 var res = fiboNums(num - 2) + fiboNums(num - 1)

     |                 res

     |             }

     |         }

fiboNums: (num: Int)Int


scala> fiboNums(5)

res0: Int = 3


//非递归函数可以不声明返回类型

scala> def sum_fibo_nums(num: Int) = {

     |             import scala.collection.mutable.ArrayBuffer

     |             var list_fibo = ArrayBuffer[Int]()

     |             list_fibo.clear()

     |             for (n <- 1 to num) {

     |                 var res = fiboNums(n)

     |                 printf("%d个斐波那契数: %d\n", n, res)

     |                 list_fibo += res        //把每个数都追加到数组中

     |             }

     |             printf("%d个斐波那契数的和为:%d",num,list_fibo.sum)

     |         }

sum_fibo_nums: (num: Int)Unit


scala> fiboNums(5)

res0: Int = 3


scala> sum_fibo_nums(5)

1个斐波那契数: 0

2个斐波那契数: 1

3个斐波那契数: 1

4个斐波那契数: 2

5个斐波那契数: 3

5个斐波那契数的和为:7


4,示例

scala> def no_equals_symbol(a:Int, b:Int) {

     |  var c = a + b

     |  c

     | }

no_equals_symbol: (a: Int, b: Int)Unit      //定义函数时无等号=,则无返回值


scala> print(no_equals_symbol(1,2))

()



————————————————————

【scala如何解析函数的参数?两种方式】

1,传值调用(call-by-value):进入函数内部前,就已将参数表达式的值计算完毕

传值调用最好理解,就是在定义函数时声明参数及其数据类型,这意味着调用此函数时必须传一个确定类型的值作为参数

语法:使用 : 符号来设置传值调用,请看示例:

scala> def passvalue(arg:Long) = {    //传给函数参数是指定类型的值

     |   println("In function 'passvalue'")

     |   println("Value of parameter 'arg' is: " + arg)

     | }

passvalue: (arg: Long)Unit


scala> def get_milis_time() = {

     |   println("get milisecond in function 'get_milis_time()'")

     |   System.currentTimeMillis

     | }

get_milis_time: ()Long


//从输出内容的顺序可知,在进入passvalue函数内部之前,此函数的参数就已经有值了

scala> passvalue( get_milis_time() )

get milisecond in function 'get_milis_time()'

In function 'passvalue'

Value of parameter 'arg' is: 1492838244412


2,传名调用(call-by-name):只有在函数内部才计算参数表达式的值

语法:在变量名和变量的数据类型之间使用=>符号来设置传名调用。在scala解释器示例如下

scala> def passname( para : => Long) = {    //传给函数的参数是一个函数

     |   println("In function 'passname'")

     |   println("the value of parameter 'para' is: " + para)

     | }

passname: (para: => Long)Unit


scala> def get_milis_time() = {

     |   println("get milisecond in function 'get_milis_time()'")

     |   System.currentTimeMillis

     | }

get_milis_time: ()Long


//表达式的值只有在passname函数内部才计算

scala> passname( get_milis_time() )

In function 'passname'

get milisecond in function 'get_milis_time()'

the value of parameter 'para' is: 1492835629778


3,传值调用和传名调用混合使用

scala> def duplicate(a:String,b:Int, func1:(String,Int)=>String, func2:(String,Int)=>String)= { 

     |  var s = ""

     |  if (b>0) {

     |   s = func1(a,b)

     |  }

     |  if (b<=0) {

     |   s = func2(a,b)

     |  }

     |  s

     | }

duplicate: (a: String, b: Int, func1: (String, Int) => String, func2: (String, Int) => String)String


scala> var f1 = (s:String, i:Int) => s * i

f1: (String, Int) => String = <function2>


scala> var f2 = (s:String, i:Int) => "i<0 Cannot duplicate"

f2: (String, Int) => String = <function2>


scala> duplicate("hi~", 3, f1, f2)

res9: String = hi~hi~hi~


scala> duplicate("hi~", -3, f1, f2)

res10: String = i<0 Cannot duplicate


这中混合使用,其实是一个高阶函数的用法,高阶函数特点是,传给函数的参数类型是函数



————————————————————

【闭包】

闭包是个函数,其返回值由定义在函数外部的一个或多个变量决定

scala> val factor = 3

factor: Int = 3


// 匿名函数里面有个形参I,还有个定义在函数外部的变量factor

scala> val multiply = (I:Int) => I * factor

multiply: Int => Int = <function1>


scala> multiply(2)

res0: Int = 6


在intelliJ IDEA中的完整示例:

object TestClosPack {    // scala的这种object用法相当于java中的单实例模式
    def main(args: Array[String]){
       
var factor = 3.14
       
val multi = (i: Int) => i * factor

        println("multi(3) value is:" + multi(3))
        println("multi(10) value is: " + multi(10))
    }
}

输出:

multi(3) value is:9.42

multi(10) value is: 31.400000000000002


————————————————————

【函数的变长参数】

变长的参数意味着传入函数的参数列表,其长度可变,即可以有1个或多个参数

语法:参数类型后面加一个符号*

示例一:

scala> def get_sum(a:Int*) = {

     |  var s = 0

     |  for (x <- a) s += x;

     |  s

     | }

get_sum: (a: Int*)Int


scala> get_sum(1,3,5,7)    //可以往里传多个值

res17: Int = 16


示例二:

scala> def get_string(str:String*) = {

     |  var i = 0

     |  for (s <- str) {

     |   println("string value " + i + ": "+ s )

     |   i += 1

     |  }

     | }

get_string: (str: String*)Unit


scala> get_string("My","name", "is", "Tonykidkid")

string value 0: My

string value 1: name

string value 2: is

string value 3: Tonykidkid



————————————————————

【柯里化的四种写法】

科里化就是把普通的接收2个参数的函数,转化成接收1个参数的函数。调用科里化函数的时候,需要分两步:先传一个参数进去,再紧接着传第二个参数;就是分两次传参每次传1个参数。请看以下代码示例的注释


scala> def replicate(x:Int, y:String) = {  //尚未科里化,这是普通的函数写法

     |  y * x

     | }

replicate: (x: Int, y: String)String


scala> replicate(3, "scalA")    // 调用普通函数,一次性接收2个参数

res28: String = scalAscalAscalA


//科里化函数的第一种写法

scala> def currying_1(x:Int) = {   //不显式声明返回类型,scala会自动推断出返回类型

     |    y:String => y * x

     | }

currying_1: (x: Int)String => String      // =>符号意味着返回的是一个函数


// 调用时传入第一个参数3,返回一个新的函数;继续向新函数传入第二个参数”scalA”,返回最终结果

scala> currying_1(3)(“scalA")

res29: String = scalAscalAscalA


// 第二种写法:显式声明返回类型——函数

scala> def curry_2(x:Int) : String => String = {   // explicitly declare returned type

     |   y => y *x

     | }

curry_2: (x: Int)String => String       // =>符号左边的String对应的是y,=>符号右边的String就对应着y*x


scala> :type curry_2(3)//验证返回类型,确实是个函数

String => String


// 调用curry_2(3)会返回一个新的函数, 该函数以String为参数类型,再调用此函数时要传参

scala> curry_2(3)("scalA ")

res30: String = "scalA scalA scalA "


// 第三种写法

scala> def curry_3rd(x:Int)(y:String) = {

     |  y * x

     | }

curry_3rd: (x: Int)(y: String)String


scala> curry_3rd(3)("good!")    //效果等同于上面两种写法

res31: String = good!good!good!


// 第四种写法

scala> def curry_4th(x:Int)(y:String) : String = {

     |  y * x

     | }

curry_4th: (x: Int)(y: String)String


scala> curry_4th(3)("good!")

res35: String = good!good!good!



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值