欧美free性护士vide0shd,老熟女,一区二区三区,久久久久夜夜夜精品国产,久久久久久综合网天天,欧美成人护士h版

目錄

柚子快報(bào)邀請(qǐng)碼778899分享:大數(shù)據(jù)之Scala簡(jiǎn)介

柚子快報(bào)邀請(qǐng)碼778899分享:大數(shù)據(jù)之Scala簡(jiǎn)介

http://yzkb.51969.com/

大數(shù)據(jù)之Scala簡(jiǎn)介

一、Scala介紹1、Scala語(yǔ)言特點(diǎn)2、Scala和Java的關(guān)系3、Scala的環(huán)境搭建4、簡(jiǎn)單的Scala程序介紹5、Scala的編譯命令

二、變量和數(shù)據(jù)類型1、注釋2、變量和常用(重點(diǎn))3、標(biāo)識(shí)符命名4、數(shù)據(jù)類型(重點(diǎn))Java數(shù)據(jù)類型Scala數(shù)據(jù)類型

5、類型轉(zhuǎn)換自動(dòng)類型轉(zhuǎn)換強(qiáng)制類型轉(zhuǎn)換數(shù)值類型和String類型轉(zhuǎn)換

6、輸出和輸入

三、運(yùn)算符四、流程控制1、if-else,使用跟Java基本一致2、for循環(huán)(重點(diǎn))3、while和do.while循環(huán)控制4、循環(huán)中斷

五、函數(shù)式編程1、面向?qū)ο蠛兔嫦蚝瘮?shù)編程2、函數(shù)基本語(yǔ)法函數(shù)定義函數(shù)參數(shù)函數(shù)至簡(jiǎn)原則函數(shù)高階用法匿名函數(shù)函數(shù)柯里化和閉包遞歸控制抽象惰性加載

一、Scala介紹

1、Scala語(yǔ)言特點(diǎn)

學(xué)習(xí)Scala是因?yàn)镾park框架就是有Scala編寫的,想學(xué)習(xí)Spark,首先需要對(duì)Scala有一定的了解。 Scala的語(yǔ)言特點(diǎn): Scala是一門以Java虛擬機(jī)(JVM)為運(yùn)行環(huán)境并將面向?qū)ο蠛秃瘮?shù)式編程的最佳特性結(jié)合在一起的靜態(tài)類型編程語(yǔ)言(靜態(tài)語(yǔ)言需要提前編譯的如:Java、c、c++等,動(dòng)態(tài)語(yǔ)言如:js)。 (1)Scala是一門多范式的編程語(yǔ)言,Scala支持面向?qū)ο蠛秃瘮?shù)式編程。(多范式,就是多種編程方法的意思。有面向過(guò)程、面向?qū)ο?、泛型、函?shù)式四種程序設(shè)計(jì)方法。) (2)Scala源代碼(.scala)會(huì)被編譯成Java字節(jié)碼(.class),然后運(yùn)行于JVM之上,并可以調(diào)用現(xiàn)有的Java類庫(kù),實(shí)現(xiàn)兩種語(yǔ)言的無(wú)縫對(duì)接。 (3)Scala單作為一門語(yǔ)言來(lái)看,非常的簡(jiǎn)潔高效。

2、Scala和Java的關(guān)系

可以說(shuō)Scala就是在Java的基礎(chǔ)上,加上了自己函數(shù)式編程思想的語(yǔ)言。Scala的SDK中有Java的類庫(kù),有Scala自己特有的類庫(kù),也對(duì)Java的部分類庫(kù)做了特定的包裝。Scala同樣的經(jīng)過(guò)SDK編譯后,會(huì)生成.class文件,然后可以在各種環(huán)境(windows,linux,unix)的JVM上面運(yùn)行。也能說(shuō)Scala就是兼容Java的,所以Scala同時(shí)需要Java的JDK和自己的SDK的支持。

3、Scala的環(huán)境搭建

(1)首先需要按照對(duì)應(yīng)版本的Java的JDK,并配置好環(huán)境變量 (2)下載對(duì)應(yīng)的Scala安裝文件,并解壓到不帶中文的目錄下 (3)對(duì)應(yīng)的配置Scala的環(huán)境變量,SCALA_HOME,開啟cmd,輸入scala測(cè)試即可 (4)IDEA默認(rèn)不支持Scala的開發(fā),需要安裝插件;下載插件(scala-intellij-bin-2017.2.6.zip)并按照File->Setting->Plugins->Install plugin from disk的步驟找到并安裝插件 (5)Maven默認(rèn)也不支持Scala的開發(fā),需要引入Scala框架,在項(xiàng)目上右擊,按照Add Framework Support->選擇 Scala->點(diǎn)擊 OK步驟引入Scala框架

4、簡(jiǎn)單的Scala程序介紹

object Hello {

def main(args: Array[String]): Unit = {

println("HelloScala")

System.out.println("hello scala from java")

}

}

object:關(guān)鍵字,聲明一個(gè)單例對(duì)象(或者伴生對(duì)象)跟同名的類形成伴生關(guān)系; Scala中無(wú)Static關(guān)鍵字,由Object實(shí)現(xiàn)類似靜態(tài)方法的功能。 class:關(guān)鍵字,聲音一個(gè)伴生類,伴生類和伴生對(duì)象的私有屬性是可以共有的。 def:關(guān)鍵字,聲明方法,形式如: def 方法名(參數(shù)名:參數(shù)類型):返回值類型 = { 方法體 } Unit:空返回值類型,Java中空返回類型用Void,Scala中空返回有Unit。 [String] 表示泛型,字符串類型,Scala用中括號(hào)表示泛型,Java中用尖括號(hào)<>表示泛型。 Scala 中不用添加分號(hào)作為每行的結(jié)束,當(dāng)然添加了也是不會(huì)報(bào)錯(cuò),建議按照規(guī)范不必要添加。 scala 中可以直接調(diào)用Java中的類庫(kù),當(dāng)然使用類庫(kù)需要先導(dǎo)包,system.out.println() 這種常用的系統(tǒng)默認(rèn)預(yù)先導(dǎo)入包了。

5、Scala的編譯命令

Java的編譯命令:

javac HelloScala.java

Java的運(yùn)行命令:

java HelloScala

Scala的編譯命令:

scalac HelloScala.scala

Scala的運(yùn)行命令:

scala HelloScala

二、變量和數(shù)據(jù)類型

1、注釋

Scala 注釋使用和 Java 完全一樣。 (1)單行注釋:// (2)多行注釋:/* */ (3)文檔注釋:

/***

*

*/

2、變量和常用(重點(diǎn))

scala中有變量和常量之分,變量的值可以被修改,常量的值不能被修改;變量使用var 定義,常量使用val 定義。 java中變量和常量的定義如下,java中使用final關(guān)鍵字,來(lái)定義常量:

int a = 10

final int b = 20

scala中變量和常量的定義格式如下: var 變量名[:變量類型] = 初始值 val 常量名[:常量類型] = 初始值

var i:Int = 10

val j:Int = 20

關(guān)于var 和val 定義的一些規(guī)則如下: (1)聲明變量時(shí),類型可以省略,編譯器會(huì)自動(dòng)推導(dǎo),即類型推導(dǎo)

var a = 10 //系統(tǒng)自動(dòng)推導(dǎo)為Int類型

var name = "alice" //系統(tǒng)自動(dòng)推導(dǎo)為String類型

(2)類型確定后,就不能修改,說(shuō)明 Scala 是強(qiáng)數(shù)據(jù)類型語(yǔ)言

var a:Int = 123 //Int類型不能修改為String類型,或者String的值不能賦給Int類型的變量

a = "123" // error

(3)變量聲明時(shí),必須要有初始值

var a1: Int // error 必須要有初始值,這兩種方式都是錯(cuò)誤的

var a2 // error

(4)在聲明/定義一個(gè)變量時(shí),可以使用 var 或者 val 來(lái)修飾,var 修飾的變量可變,val 修飾的變量不可改。

var a: Int = 15

val b: Int = 25

a = 18

b = 20 // error,val 修飾的值不能改變

(5)var 修飾對(duì)象時(shí),對(duì)象和屬性均可變;val 修飾對(duì)象時(shí),對(duì)象不可變,對(duì)象里面的屬性可變

// var 修飾的常規(guī)變量可以修改,修飾的引用變量也是可以修改的,如下:

var alice = new Student(name="alice",age=20)

alice = new Student(name="Alice",age=18)

alice = NULL

// val 修飾的引用變量不能修改,但是類里面的屬性可以修改,同時(shí)必須在創(chuàng)建類時(shí)在屬性上面有定義,如:

Student(name:String,var age:Int)

val bob = new Student(name="Bob",age=13)

// bob = new Student(name="bob",age=20) // error,對(duì)象不能重新賦值,不能修改

bob.age = 14 // 類里面的屬性可以修改,下面打印將會(huì)打印出年齡為14

bob.printInfo()

3、標(biāo)識(shí)符命名

Scala的命名跟java的命名規(guī)則一樣,以字母或者下劃線開頭,后接字母、數(shù)字、下劃線。有以下兩個(gè)特例: (1)以操作符開頭,且只包含操作符(+ - * / # !)等為合法命名,如下命名為合法:

var -+/#:String = "hello"

(2)用反引號(hào) 包括的任意字符串,即使是 Scala 關(guān)鍵字(39 個(gè))也屬合法

var `for` = 123

var `def` = "123"

4、數(shù)據(jù)類型(重點(diǎn))

Java數(shù)據(jù)類型

Java基本類型: char、byte、short、int、long、float、double、boolean Java引用類型:(對(duì)象類型) 由于Java有基本類型,而且基本類型不是真正意義的對(duì)象,即使后面產(chǎn)生了基本類型的包裝類,但是仍然存在基本數(shù)據(jù)類型,所以Java語(yǔ)言并不是真正意思的面向?qū)ο蟆?Java基本類型的包裝類: Character、Byte、Short、Integer、Long、Float、Double、Boolean 注意:Java中基本類型和引用類型沒(méi)有共同的祖先類。但是scala是有的。

Scala數(shù)據(jù)類型

1、Scala數(shù)據(jù)類型 Scala的所有類型的祖先類是Any,下面分2大類,分別是AnyVal類(數(shù)值類型)和AnyRef類(引用類型),并且不管是數(shù)值類型還是引用類型都是對(duì)象。

AnyVal類(數(shù)值類型):

Unit: 空值類,相當(dāng)于Java當(dāng)中的void,常用于返回值為空的情況StringOps: 是對(duì)Java當(dāng)中的String的增強(qiáng)類型Char: 字符類型Boolean: 布爾類型Double -> Float -> Long -> Int -> Short -> Byte: 數(shù)值類型,精度逐漸遞減 AnyRef類(引用類型)

Scala collectionsOther Scala classesall java classesNull: 只有一個(gè)對(duì)象就是null,它是所有引用類型的子類

Nothing: 是所有數(shù)據(jù)類型的子類,主要用在一個(gè)函數(shù)沒(méi)有明確返回值時(shí)使用,因?yàn)檫@樣我們可以把拋出的返回值,返回給任何的變量或者函數(shù)。

需要注意的是: (1)Scala中所有類型,包括數(shù)據(jù)都是對(duì)象,且都是Any的子類 (2)Scala數(shù)據(jù)類型仍然遵守自動(dòng)轉(zhuǎn)換,低精度的值類型向高精度值類型轉(zhuǎn)換時(shí)會(huì)自動(dòng)轉(zhuǎn)換(隱式轉(zhuǎn)換) (3)Null 是所有引用類型的子類 (4)Nothing 是所有數(shù)據(jù)類型的子類

2、整數(shù)類型 整數(shù)類型(Byte[1]、Short[2]、Int[4]、Long[8]),Scala默認(rèn)的整數(shù)類型為Int型,聲明其他類型需要加上類型定義,且長(zhǎng)整型需要在后面加L或者l;

val a3 = 10 // int型

val a4:Long = 32333333333L // 長(zhǎng)整型定義

val a5:Byte = (10+20) // byte型

3、浮點(diǎn)類型 浮點(diǎn)類型(Float,Double),scala中默認(rèn)是Double類型,聲明Float類型,需要在后面添加f或F。

val a6 = 3.6 //Double型

val a7:Float = 3.8f //Float型

4、字符類型 用單引號(hào)‘’扣起來(lái)的表示字符。 5、布爾類型 (1)布爾類型也叫Boolean 類型,Booolean 類型數(shù)據(jù)只允許取值 true 和 false (2)boolean 類型占 1 個(gè)字節(jié) 6、Unit類型,Null類型和Nothing類型 Unit:表示無(wú)值,和Java語(yǔ)言中 void 等同。用作不返回任何結(jié)果的方法的結(jié)果的返回值類型。Unit 只有一個(gè)實(shí)例值,寫成(),可以查看里面的toString()方法。 Null:null , Null 類型只有一個(gè)實(shí)例值 null,null可以賦值給任意的引用類型AnyRef,不能賦值給數(shù)值類型AnyAvl Nothing:Nothing 類型在 Scala 的類層級(jí)最低端;它是任何其他類型(AnyRef、AnyAvl)的子類型。當(dāng)一個(gè)函數(shù),我們確定沒(méi)有正常的返回值時(shí)(也就是發(fā)生異常情況時(shí)),可以用 Nothing 來(lái)指定返回類型,這樣有一個(gè)好處,就是我們可以把返回的值(異常)賦給其它所有的函數(shù)或者變量(兼容性)

5、類型轉(zhuǎn)換

自動(dòng)類型轉(zhuǎn)換

當(dāng) Scala 程序在進(jìn)行賦值或者運(yùn)算時(shí),精度小的類型自動(dòng)轉(zhuǎn)換為精度大的數(shù)值類型,這個(gè)就是自動(dòng)類型轉(zhuǎn)換(隱式轉(zhuǎn)換)。按照順序可以有以下順序: Byte => Short => Int => Long => Float => Double 一般自動(dòng)轉(zhuǎn)換的規(guī)則如下: (1)自動(dòng)提升原則:有多種類型的數(shù)據(jù)混合運(yùn)算時(shí),系統(tǒng)首先自動(dòng)將所有數(shù)據(jù)轉(zhuǎn)換成精度大的那種數(shù)據(jù)類型,然后再進(jìn)行計(jì)算。 (2)把精度大的數(shù)值類型賦值給精度小的數(shù)值類型時(shí),就會(huì)報(bào)錯(cuò),反之就會(huì)進(jìn)行自動(dòng)類型轉(zhuǎn)換。 (3)byte,short和 char 之間不會(huì)相互自動(dòng)轉(zhuǎn)換,char在自動(dòng)轉(zhuǎn)換時(shí),會(huì)自動(dòng)轉(zhuǎn)換成int類型 (4)byte,short,char 他們?nèi)呖梢杂?jì)算,在計(jì)算時(shí)首先會(huì)轉(zhuǎn)換為 int 類型,然后再計(jì)算

強(qiáng)制類型轉(zhuǎn)換

精度高的想轉(zhuǎn)成精度低的就需要用到強(qiáng)制轉(zhuǎn)換,但要注意會(huì)造成精度降低或者溢出問(wèn)題,且強(qiáng)轉(zhuǎn)只針對(duì)于最近的操作數(shù)有效,常常會(huì)使用括號(hào)提升優(yōu)先級(jí)。

val a1:Int = 10*3.5.toInt // 結(jié)果為30,先將3.5轉(zhuǎn)為Int型的3

val a2:Int = (10*3.5).toInt // 結(jié)果為35,將整體結(jié)果35.0轉(zhuǎn)為35

var b1:Byte = 1

// 此處編譯報(bào)錯(cuò),因?yàn)閎1+1時(shí),系統(tǒng)默認(rèn)會(huì)將b1轉(zhuǎn)化成int類型,再相加;

// 但加完需要再賦給b1的時(shí)候,變成了int類型賦給Byte類型,因?yàn)槭蔷却蟮念愋唾x給精度小的類型,所以報(bào)錯(cuò)

// 此處需要加括號(hào)強(qiáng)轉(zhuǎn) b1 = (b1+1).toByte

b1 = b1+1

// 此處不會(huì)報(bào)錯(cuò),因?yàn)橹苯訉1轉(zhuǎn)成了Int類型

b1 += 1

數(shù)值類型和String類型轉(zhuǎn)換

(1)基本類型轉(zhuǎn) String 類型(語(yǔ)法:將基本類型的值+“” 即可)

var num: Int = 123

var strNum1 = num.toString // 或者

var strNum2 = num+""

(2)String 類型轉(zhuǎn)基本數(shù)值類型(語(yǔ)法:s1.toInt、s1.toFloat、s1.toDouble、s1.toByte、s1.toLong、s1.toShort)但要注意非數(shù)值的字符串轉(zhuǎn)成數(shù)值會(huì)報(bào)錯(cuò)。

var str1 = "123"

val str2 = "a123"

val num1 = str1.toInt

val num2 = str2.toInt //報(bào)錯(cuò)

6、輸出和輸入

輸出: (1)字符串連接,通過(guò)+號(hào)連接,* 重復(fù)

val name: String = alice

val age: Int = 20

println(age + "年齡" + name + "姓名")

println(name * 3) // 多次打印字符串

(2)printf 用法:字符串,通過(guò)%占位符傳值,并跟上對(duì)應(yīng)的類型

println("%d年齡 %s姓名",age,name)

(3)雙引號(hào)前面加上s""為字符串模板(插值字符串),通過(guò)

獲取變量值,后面的

{}獲取變量值,后面的

獲取變量值,后面的{}即可對(duì)應(yīng)相應(yīng)的值

println(s"${age}年齡 ${name}姓名")

(4)雙引號(hào)前面加上f"“為格式化輸出,%2.2f中%號(hào)為占位,小數(shù)點(diǎn)前面的2為前面保留2位,小數(shù)點(diǎn)后面為后面保留2位;雙引號(hào)前面加上raw”"為原格式輸出,不管后面帶的是什么

val num:Double = 2.5365

println(f"the number is ${num}%2.2f") //格式化模板字符串

# the number is 2.54

println(raw"the number is ${num}%2.2f") //raw 原樣輸出

# the number is 2.5365%2.2f

(5)多行字符串,在Scala中可以使用三個(gè)雙引號(hào)包圍多行字符串實(shí)現(xiàn),應(yīng)用 scala 的 stripMargin 方法,并使用“|”作為連接符,可以保留多行字符串的格式,如需加上引用,可以在加上s即可。

val sql = """

|select

| name,

| age

|from user

|where name="zhangsan" """.stripMargin

println(sql)

// 加上引用時(shí)

val sql1 = s"""

|select

| name,

| age

|from user

|where name="$name" and age=${age+2} """.stripMargin

println(sql1)

標(biāo)準(zhǔn)輸入: 基本的輸入有StdIn.readLine()、StdIn.readShort()、StdIn.readDouble() 。

// 1 輸入姓名

println("input name:")

var name = StdIn.readLine()

// 2 輸入年齡

println("input age:")

var age = StdIn.readShort()

// 3 輸入薪水

println("input sal:")

var sal = StdIn.readDouble()

文件讀取,并在控制臺(tái)輸出:

Source.fromFile("D:/Tootls/abc.txt").foreach(print)

也可以直接調(diào)用Java的接口實(shí)現(xiàn)文件讀取的功能:

val writer = new PrintWriter(new File(pathname="D:/Tootls/abc.txt"))

weiter.write("hello scala from java writer")

三、運(yùn)算符

1、基本語(yǔ)法 (1)對(duì)于除號(hào)“/”,它的整數(shù)除和小數(shù)除是有區(qū)別的:整數(shù)之間做除法時(shí),只保留整數(shù)部分而舍棄小數(shù)部分。需要保留小數(shù)可以在前面將數(shù)值轉(zhuǎn)換成Double類型,或者數(shù)字后面加小數(shù)點(diǎn)。 (2)對(duì)一個(gè)數(shù)取模 a%b,和 Java 的取模規(guī)則一樣。

運(yùn)算符運(yùn)算范例結(jié)果+正號(hào)+33-負(fù)號(hào)b=4; -b-4+加5+510-減6-42*乘3*412/除5/51%取模(取余)7%52+字符串相加“He”+”llo”“Hello”

2、關(guān)系運(yùn)算法

運(yùn)算符運(yùn)算范例結(jié)果==相等于4==3false!=不等于4!=3true<小于4<3false>大于4>3true<=小于等于4<=3false>=大于等于4>=3true

Java 和Scala 中關(guān)于“==”的區(qū)別:

Java: == 比較兩個(gè)變量本身的值,即兩個(gè)對(duì)象在內(nèi)存中的首地址; equals 比較字符串中所包含的內(nèi)容是否相同。 Scala:

== 更加類似于 Java 中的 equals,參照 jd 工具 eq 而比較兩個(gè)對(duì)象之間在內(nèi)存的地址使用的是eq函數(shù) str1.eq(str2)

3、邏輯運(yùn)算符 其中&&和||是二元運(yùn)算符,也就是需要前面后面兩個(gè)參數(shù),!為單元運(yùn)算符,只需要后面接一個(gè)參數(shù) (A && B)和(A || B) 也是惰性匹配,當(dāng)A為false時(shí),&&不會(huì)再判斷后面條件,因?yàn)橐粋€(gè)為false結(jié)果只能為false,所以后面B不會(huì)執(zhí)行。 當(dāng)A為true時(shí),|| 同樣不會(huì)再判斷后面的B條件,因?yàn)橹灰幸粋€(gè)為true結(jié)果只能為true,這種不會(huì)再執(zhí)行后面條件成為惰性匹配。

-- 判斷字符串是否為空

isNotEmpty(String s){

//如果按位與,s 為空,會(huì)發(fā)生空指針

return s!=null && !"".equals(s.trim());

}

4、scala運(yùn)算符的本質(zhì) 在 Scala 中其實(shí)是沒(méi)有運(yùn)算符的,所有運(yùn)算符都是方法。前面講過(guò)可以使用運(yùn)算法作為方法的命名,常用的運(yùn)算符已經(jīng)給scala命名為方法了。

方法有以下的省略規(guī)則: (1)當(dāng)調(diào)用對(duì)象的方法時(shí),點(diǎn).可以省略 (2)如果函數(shù)參數(shù)只有一個(gè),或者沒(méi)有參數(shù),()可以省略

// 標(biāo)準(zhǔn)的加法運(yùn)算

val i:Int = 1.+(1)

// 當(dāng)調(diào)用對(duì)象的方法時(shí),.可以省略

val j:Int = 1 + (1)

// 如果函數(shù)參數(shù)只有一個(gè),或者沒(méi)有參數(shù),()可以省略,這樣就跟平時(shí)的運(yùn)算一樣的,也可以跟java或其他語(yǔ)言兼容

val k:Int = 1 + 1

5、賦值運(yùn)算符和位運(yùn)算符跟Java基本一致。

四、流程控制

1、if-else,使用跟Java基本一致

(1)單分支

if (表達(dá)式) {

執(zhí)行代碼塊

}

(2)雙分支

if (表達(dá)式) {

執(zhí)行代碼塊1

} else {

執(zhí)行代碼塊2

}

(3)多分支

if (表達(dá)式) {

執(zhí)行代碼塊1

} else if (表達(dá)式2){

執(zhí)行代碼塊2

} else {

執(zhí)行代碼塊3

}

(4)跟Java或其他語(yǔ)言不同的是,scala的if-else是有返回值的,具體返回值取決于滿足條件的代碼體的最后一行內(nèi)容。 (4.1)在if-else的多分支里面,每個(gè)分支的返回值類型一樣的話,可以寫成下面形式:

// 返回值參數(shù)為res,類型為String

val res: String = if (age < 18) {

return "18"

} else {

return "19"

}

(4.2)Scala 中返回值類型不一致時(shí),取它們共同的祖先類型,這里返回兩個(gè)類型Int和String,但整體的返回為Unit,即為空()

val res: Unit = if (age < 18) {

return 18

} else {

return "19"

}

(5)Java中的三元運(yùn)算符可以使用if-else來(lái)實(shí)現(xiàn),scala中如果大括號(hào){}內(nèi)的邏輯代碼只有一行,大括號(hào)可以省略。如果省略大括號(hào),if 只對(duì)最近的一行邏輯代碼起作用。

//java中:

int result = flag ? 1 : 0

scala中省略大括號(hào)后,用if-else實(shí)現(xiàn):

println("input age:")

var age = StdIn.readInt()

val res: String = if (age > 18) "成年" else "童年"

println(res)

2、for循環(huán)(重點(diǎn))

基本語(yǔ)法如下:

for(i <- 1 to 3) // 將1到3的值依次賦給i

(1)i 表示循環(huán)的變量,<- to 都是規(guī)定的格式 (2)i 將會(huì)從 1-3 循環(huán),前后閉合 (3)當(dāng)想后面不閉合時(shí),使用range()或者使用until

(1) 范圍遍歷

for ( i <- 1 to 10) {} // 1-10 包含10

for ( i <- range(1,10) {} // 1-10 不包含10

for ( i <- 1 until 10) {} // 1-10 不包含10

(2) 集合遍歷,可以直接使用集合,數(shù)組,列表等

for ( i <- Array(2, 5, 8)){}

for ( i <- List(2, 5, 8)){}

for ( i <- Set(2, 5, 8)){}

(3) 循環(huán)守衛(wèi),當(dāng)不需要循環(huán)里面的某個(gè)值時(shí),使用循環(huán)守衛(wèi),相當(dāng)于Java中的continue

for ( i <- 1 to 10 if i != 5){} // 1-10 包含10,不要5

(4) 循環(huán)步長(zhǎng),默認(rèn)的步長(zhǎng)都是1,當(dāng)需要其他步長(zhǎng)時(shí),在后面的by關(guān)鍵字后面添加

for ( i <- 1 to 10 by 2){} // 取 1,3,5,7,9,步長(zhǎng)間隔為2

(5) 反轉(zhuǎn),當(dāng)需要取值從大到小時(shí),需將大的數(shù)放在前面,并且步長(zhǎng)設(shè)置為-1,或者直接在后面添加反轉(zhuǎn)的關(guān)鍵字,reverse

for ( i <- 10 to 1 by -1){} // 從10到1

for ( i <- 1 to 10 reverse){} // 從10到1

(6) 小數(shù),當(dāng)取的步長(zhǎng)不為整數(shù),為小數(shù)時(shí),因?yàn)楹竺娴牟介L(zhǎng)是跟前面數(shù)值的類型相關(guān)的,所以需將前面的類型轉(zhuǎn)成Double類型,才能設(shè)置小數(shù)步長(zhǎng)

for ( i <- 1.0 to 10.0 by 0.5){}

(7) 循環(huán)嵌套,類似于java當(dāng)中的嵌套

for ( i <- 1 to 3){

for (j <- 1 to 3){}

}

// scala當(dāng)中的循環(huán)嵌套也可以寫在同一行,用分號(hào)隔開,兩個(gè)處在相同的地位

for (i <- 1 to 4; j <- 1 to 5) {} //輸出4*5的矩陣

(8) 引入變量,引入另一個(gè)變量j,j跟i是有相關(guān)的關(guān)系的

for ( i <- 1 to 10; j = i * 2){}

(9) 循環(huán)返回值 默認(rèn)的返回值都是unit,當(dāng)需要返回值的時(shí)候,需要加關(guān)鍵字 yield

// 返回的是一個(gè)Vector向量,可以直接對(duì)集合或者列表里面的值做操作。

val b: immutable.IndexedSeq[Int] = for ( i <- 1 to 10) yield i * 2

3、while和do.while循環(huán)控制

while基本語(yǔ)法

循環(huán)變量初始化

while (循環(huán)條件) {

循環(huán)體(語(yǔ)句)

循環(huán)變量迭代

}

while說(shuō)明: (1)循環(huán)條件是返回一個(gè)布爾值的表達(dá)式 (2)while 循環(huán)是先判斷再執(zhí)行語(yǔ)句 (3)與 for 語(yǔ)句不同,while 語(yǔ)句沒(méi)有返回值,即整個(gè) while 語(yǔ)句的結(jié)果是Unit 類型() (4)因?yàn)?while 中沒(méi)有返回值,所以當(dāng)要用該語(yǔ)句來(lái)計(jì)算并返回結(jié)果時(shí),就不可避免的使用變量,而變量需要聲明在 while 循環(huán)的外部,那么就等同于循環(huán)的內(nèi)部對(duì)外部的變量造成了影響,所以不推薦使用,而是推薦使用 for 循環(huán)。

do.while基本語(yǔ)法:

循環(huán)變量初始化;

do{

循環(huán)體(語(yǔ)句)

循環(huán)變量迭代

} while(循環(huán)條件)

do.while說(shuō)明: (1)循環(huán)條件是返回一個(gè)布爾值的表達(dá)式 (2)do…while 循環(huán)是先執(zhí)行,再判斷

4、循環(huán)中斷

scala內(nèi)置結(jié)構(gòu)去掉了 break 和 continue 關(guān)鍵字,是為了更好的適應(yīng)函數(shù)式編程,推薦使用函數(shù)式的風(fēng)格解決 break 和 continue 的功能,而不是一個(gè)關(guān)鍵字。Scala 中使用 breakable 控制結(jié)構(gòu)來(lái)實(shí)現(xiàn) break 和 continue 功能。break功能也就是相當(dāng)于程序異常退出,卻沒(méi)有任何的異常信息,這里可以使用拋出異常,但不捕獲異常的方式實(shí)現(xiàn)退出功能。

def main(args: Array[String]): Unit = {

try {

for (elem <- 1 to 10) {

println(elem)

if (elem == 5) throw new RuntimeException // 隨便捕獲異常

}

}catch {

case e =>// 捕獲異常后,不做任何處理,可以實(shí)現(xiàn)break的功能

}

println("正常結(jié)束循環(huán)")

}

使用scala自帶的函數(shù),來(lái)實(shí)現(xiàn)退出功能,將需要break的代碼塊包裹在Breaks.breakable()里面,需要時(shí),調(diào)用Breaks.break()方法。

import scala.util.control.Breaks

def main(args: Array[String]): Unit = {

Breaks.breakable {

for (elem <- 1 to 10) {

println(elem)

if (elem == 5) Breaks.break()

}

}

println("正常結(jié)束循環(huán)")

}

導(dǎo)入包后,其實(shí)包名可以省略,當(dāng)方法的參數(shù)為空時(shí),括號(hào)也是可以省略,從而簡(jiǎn)化成下面的格式:

import scala.util.control.Breaks._

object TestBreak {

def main(args: Array[String]): Unit = {

breakable {

for (elem <- 1 to 10) {

println(elem)

if (elem == 5) break

}

}

println("正常結(jié)束循環(huán)")

}

}

五、函數(shù)式編程

1、面向?qū)ο蠛兔嫦蚝瘮?shù)編程

(1)面向?qū)ο缶幊?解決問(wèn)題,分解對(duì)象,行為,屬性,然后通過(guò)對(duì)象的關(guān)系以及行為的調(diào)用來(lái)解決問(wèn)題。對(duì)象的本質(zhì):就是對(duì)數(shù)據(jù)和行為的一個(gè)封裝。 Scala 語(yǔ)言是一個(gè)完全面向?qū)ο缶幊陶Z(yǔ)言,萬(wàn)物皆對(duì)象。 (2)函數(shù)式編程 解決問(wèn)題時(shí),將問(wèn)題分解成一個(gè)一個(gè)的步驟,將每個(gè)步驟進(jìn)行封裝(函數(shù)),通過(guò)調(diào)用這些封裝好的步驟,解決問(wèn)題。函數(shù)的本質(zhì):函數(shù)可以當(dāng)做一個(gè)值進(jìn)行傳遞。 Scala 語(yǔ)言是一個(gè)完全函數(shù)式編程語(yǔ)言,萬(wàn)物皆函數(shù)。 Scala就是將面向?qū)ο蠛秃瘮?shù)編程完美融合在一起了。

2、函數(shù)基本語(yǔ)法

基本語(yǔ)法如下所示:

def sum( x: Int ,y: Int ): Int = {

x + y

}

其中def是定義函數(shù)的關(guān)鍵字,sum 是函數(shù)名,x,y 是函數(shù)的參數(shù)名,Int 是參數(shù)的類型,第三個(gè)Int是函數(shù)的返回值類型,x+y是函數(shù)體的具體實(shí)現(xiàn)。

函數(shù)和方法的區(qū)別: (1)為完成某一功能的程序語(yǔ)句的集合,稱之為函數(shù),在類中的函數(shù)稱之方法。 (2)Scala 語(yǔ)言可以在任何的語(yǔ)法結(jié)構(gòu)中聲明任何的語(yǔ)法,也就是與位置沒(méi)有關(guān)系,但要注意語(yǔ)法的生命周期 (3)函數(shù)沒(méi)有重載和重寫的概念;方法可以進(jìn)行重載和重寫,Object(類)下面的方法可以重載,def 里面的函數(shù)不能重載,也就是不能重名 (4)Scala 中函數(shù)可以嵌套定義

函數(shù)定義

(1)函數(shù) 1:無(wú)參,無(wú)返回值 (2)函數(shù) 2:無(wú)參,有返回值 (3)函數(shù) 3:有參,無(wú)返回值 (4)函數(shù) 4:有參,有返回值 (5)函數(shù) 5:多參,無(wú)返回值 (6)函數(shù) 6:多參,有返回值

//(1)函數(shù) 1:無(wú)參,無(wú)返回值

def f1():Unit = {}

//(2)函數(shù) 2:無(wú)參,有返回值

def f2(): String = { return "name" }

//(3)函數(shù) 3:有參,無(wú)返回值

def f3(age: Int): Unit = {}

//(4)函數(shù) 4:有參,有返回值

def f4(age: Int): Int = { return age }

//(5)函數(shù) 5:多參,無(wú)返回值

def f5(name:String, age:Int): Unit = {}

//(6)函數(shù) 6:多參,有返回值

def f6(name:String, age:Int): Int = { return age}

函數(shù)參數(shù)

(1)可變參數(shù)(可以在使用前不確定參數(shù)的個(gè)數(shù),定義可變參數(shù),可變參數(shù)將以數(shù)組的形式傳入) (2)如果參數(shù)列表中存在多個(gè)參數(shù),那么可變參數(shù)一般放置在最后 (3)參數(shù)默認(rèn)值,一般將有默認(rèn)值的參數(shù)放置在參數(shù)列表的后面 (4)帶名參數(shù)

//(1)可變參數(shù),可變參數(shù)在類型后面添加*號(hào)

def f1(s: Strring*): Unit = {}

//(2)如果參數(shù)列表中存在多個(gè)參數(shù),那么可變參數(shù)一般放置在最后,不放置在最后無(wú)法給后面的參數(shù)賦值

def f2(name:String, s:String*): Unit = {}

//(3)參數(shù)默認(rèn)值,一般將有默認(rèn)值的參數(shù)放置在參數(shù)列表的后面,如果調(diào)用的時(shí)候傳遞了age的值,就會(huì)覆蓋默認(rèn)值

def f3(name:String, age:Int = 18): Unit = {}

//(4)帶名參數(shù)

def f4(name:String, age:Int = 18): Unit = {}

// 帶名參數(shù)調(diào)用是可以不按照順序調(diào)用,一般調(diào)用:

f4("bob",20)

// 帶名參數(shù)調(diào)用:

f4(age = 20,name = "bob")

// 或者

f4(name = "bob")

函數(shù)至簡(jiǎn)原則

函數(shù)至簡(jiǎn)原則,就是四個(gè)字,能省則省。 (1)return 可以省略,Scala 會(huì)使用函數(shù)體的最后一行代碼作為返回值,當(dāng)不是最后一行的值需要返回時(shí),return不能省 (2)如果函數(shù)體只有一行代碼,可以省略花括號(hào) (3)返回值類型如果能夠推斷出來(lái),那么可以省略(:冒號(hào)和返回值類型一起省略) (4)如果有 return,則不能省略返回值類型,必須指定 (5)如果函數(shù)明確聲明unit,那么即使函數(shù)體中使用 return 關(guān)鍵字也不起作用 (6)Scala 如果期望是無(wú)返回值類型,可以省略等號(hào) (7)如果函數(shù)無(wú)參,但是聲明了參數(shù)列表,那么調(diào)用時(shí),小括號(hào),可加可不加 (8)如果函數(shù)沒(méi)有參數(shù)列表,那么小括號(hào)可以省略,調(diào)用時(shí)小括號(hào)必須省略 (9)如果不關(guān)心名稱,只關(guān)心邏輯處理,那么函數(shù)名(def)可以省略

//函數(shù)標(biāo)準(zhǔn)寫法

def f( s : String ) : String = {

return s + " hello"

}

//(1)return可以省略,最后一行作為返回值

def f( s : String ) : String = {

s + " hello"

}

// (2)如果函數(shù)體只有一行代碼,可以省略花括號(hào)

def f( s : String ) : String = s + " hello"

// (3)返回值類型如果能夠推斷出來(lái),那么可以省略(:冒號(hào)和返回值類型一起省略)

def f( s : String ) = s + " hello"

//(4)如果有 return,則不能省略返回值類型,必須指定

def f( s : String ) : String = {

return s

}

//(5)如果函數(shù)明確聲明unit,那么即使函數(shù)體中使用 return 關(guān)鍵字也不起作用

def f( s : String ) : Unit = {

return s //此處的return毫無(wú)意義,編譯器也會(huì)提醒,因?yàn)椴粫?huì)返回

}

//(6)Scala 如果期望是無(wú)返回值類型,可以省略等號(hào),將無(wú)返回值的函數(shù)稱之為過(guò)程,可以跟其他語(yǔ)言做兼容

def f() {

println("hello") //無(wú)參數(shù),無(wú)返回值類型

}

//(7)如果函數(shù)無(wú)參,但是聲明了參數(shù)列表(就是函數(shù)名后面加了小括號(hào)()),那么調(diào)用時(shí),小括號(hào)可加可不加

def f() = "hello" // 函數(shù)只有一行,省略花括號(hào)

println(f()) //兩種都可以調(diào)用該函數(shù),一個(gè)帶()

println(f) //兩種都可以調(diào)用該函數(shù),一個(gè)不帶()

//(8)如果函數(shù)沒(méi)有參數(shù)列表(就是函數(shù)名后面不加小括號(hào)()),那么小括號(hào)可以省略,定義時(shí)沒(méi)有小括號(hào),則調(diào)用時(shí)也不能帶小括號(hào)

def f8 = "hello"

//println(f8()) // 該調(diào)用會(huì)報(bào)錯(cuò)

println(f8) // 正確的調(diào)用方式

//(9)如果不關(guān)心名稱,只關(guān)心邏輯處理,那么函數(shù)名def 和函數(shù)名稱都可以省略

def f9(name:String): Unit = {

println(name)

}

// 可以寫成下面這樣:也就是匿名函數(shù),相當(dāng)于lambda表達(dá)式

// 省略了函數(shù)定義,函數(shù)名,返回值類型

(name:String) => { println(name) }

函數(shù)高階用法

Scala的函數(shù)相對(duì)其他語(yǔ)言,有如下幾個(gè)高階用法,這也是Scala語(yǔ)言靈活的重要部分。 (1)函數(shù)可以作為值進(jìn)行傳遞 (2)函數(shù)可以作為參數(shù)進(jìn)行傳遞 (3)函數(shù)可以作為返回值進(jìn)行傳遞 當(dāng)函數(shù)作為值進(jìn)行傳遞的時(shí)候,或者作為返回值返回的時(shí)候,其實(shí)傳遞的就是該函數(shù)的引用地址,其實(shí)就是將函數(shù)的引用地址進(jìn)行賦值,從而可以將函數(shù)進(jìn)行傳遞。另外函數(shù)式編程有個(gè)特點(diǎn)就是函數(shù)中有自變量和因變量,簡(jiǎn)單理解就是x到y(tǒng)的過(guò)程,這個(gè)過(guò)程用符號(hào)來(lái)表示可以表示為 f(x) = y,而Scala的匿名函數(shù)或者說(shuō)Scala的函數(shù)類型定義就是跟這個(gè)類似,只是將符號(hào)換成了 => 。例如上面的匿名函數(shù)(name:String) => { println(name) },符號(hào)左邊是輸入,右邊就是輸出,跟函數(shù)的定義很類似,而Scala簡(jiǎn)化的原則就是將編程盡可能的寫成函數(shù)的形式。

//(1)函數(shù)可以作為值進(jìn)行傳遞

def f1(n:Int):Int = { // 有參函數(shù)

println("f1被調(diào)用")

n + 1

}

def f2():Int = { // 無(wú)參函數(shù)

println("f2被調(diào)用")

1

}

// 正常的調(diào)用

println(f1(5))

println(f2())

// 函數(shù)作為值進(jìn)行傳遞

val f3 = f1 _

val f4:Int=>Int = f1

val f5 = f2 _

val f6:()=>Int = f2

println(f3) // 直接打印函數(shù)的引用地址

println(f3(6)) // 傳參,調(diào)用函數(shù)f3,也就是調(diào)用f1

println(f6)

println(f6())

函數(shù)作為值進(jìn)行傳遞,函數(shù)名接上小括號(hào)就是函數(shù)的調(diào)用,而函數(shù)接上**空格下劃線 _**代表的就是函數(shù)本身,也就是函數(shù)的引用地址。當(dāng)我們想直接使用函數(shù)的名稱表示函數(shù)本身時(shí),需要在定義函數(shù)時(shí),加上函數(shù)的類型,也就是Int=>Int,表示參數(shù)為Int類型,返回值也為Int類型的參數(shù)。并且當(dāng)參數(shù)為空時(shí),小括號(hào)不能省略,也就是()=>Int。

//(2)函數(shù)可以作為參數(shù)進(jìn)行傳遞

// 函數(shù)f里面的參數(shù)為:fun: (Int,Int) => Int

// 其中fun是參數(shù)名,表示參數(shù)是函數(shù)類型的意思

// (Int,Int) => Int 為參數(shù)類型,表示輸入?yún)?shù)為兩個(gè)Int型,返回值為Int型的函數(shù)作為參數(shù)

def f1(fun: (Int,Int) => Int):Int = {

fun(1, 2)

}

// 定義一個(gè)函數(shù),參數(shù)和返回值類型和fun要求的類型一致,兩個(gè)參數(shù)為Int型,返回值也為Int型

def add(a: Int, b: Int): Int = a + b

// 將 add 函數(shù)作為參數(shù)傳遞給 f1 函數(shù),因?yàn)閭鬟f的是f1函數(shù)本身,所以需要加空格下劃線 _

// 但如果能夠推斷出來(lái)不是函數(shù)調(diào)用,而是函數(shù)傳遞,空格下劃線可以省略

// 此處可以自動(dòng)推斷,因?yàn)閒1中已經(jīng)定義了函數(shù)參數(shù)的類型

println(f1(add _))

println(f1(add))

函數(shù)作為參數(shù)簡(jiǎn)單理解就是之前的函數(shù),函數(shù)是固定的,也就是處理的流程固定,但數(shù)據(jù)是可變的。比如計(jì)算加法,可以傳遞1,2或者2,3實(shí)現(xiàn)加法效果,加法這個(gè)邏輯本身不變。函數(shù)作為參數(shù)進(jìn)行傳遞時(shí),可以看做數(shù)據(jù)不變,操作是可變的。比如也是計(jì)算1和2的加法,數(shù)據(jù)看成1和2不變,操作可以傳遞加法,也能傳遞減法。實(shí)際應(yīng)用中函數(shù)和數(shù)據(jù)都會(huì)是可變的狀態(tài),這樣就更加靈活了。

//(3)函數(shù)可以作為函數(shù)返回值返回

def f1():Int=>Unit = {

def f2(a: Int): Unit = {

println("f2調(diào)用" + a)

}

f2 // 將函數(shù)直接返回

}

println(f1()) // 打印的是f2的引用地址,因?yàn)檎{(diào)用了f1,里面返回的函數(shù)f2

printfln(f1()()) // f1() = f2,所以f1()() = f2(),這才是調(diào)用了f2,且f2的返回值為空

其中 Int=>Unit 是返回值的類型,因?yàn)榉祷氐氖呛瘮?shù)f2,所以Int=>Unit就是f2的函數(shù)類型,也就是f2的參數(shù)為Int型,返回值為空。

匿名函數(shù)

由上面的省略原則,我們可以得到匿名函數(shù)的基本定義和語(yǔ)法: 沒(méi)有名字的函數(shù)就是匿名函數(shù),其語(yǔ)法定義為:

(x:Int)=>{ 函數(shù)體 }

x:表示輸入?yún)?shù)名稱;Int:表示輸入?yún)?shù)類型;函數(shù)體:表示具體代碼邏輯。并且匿名函數(shù)還能再簡(jiǎn)化,也就是匿名函數(shù)的簡(jiǎn)化原則。

匿名函數(shù)簡(jiǎn)化規(guī)則: (1)參數(shù)的類型可以省略,會(huì)根據(jù)形參進(jìn)行自動(dòng)的推導(dǎo) (2)類型省略之后,發(fā)現(xiàn)只有一個(gè)參數(shù)時(shí),則圓括號(hào)可以省略;其他情況:沒(méi)有參數(shù)和參數(shù)超過(guò) 1 的則不能省略圓括號(hào) (3)匿名函數(shù)如果只有一行,則大括號(hào)也可以省略 (4)如果參數(shù)在調(diào)用時(shí)只出現(xiàn)一次,則參數(shù)省略且后面參數(shù)可以用_代替

// 定義函數(shù)f1,里面的參數(shù)為op操作函數(shù)

def f1(op:Int => Int) = {

op(1)

}

// 定義op操作函數(shù)

def op(n:Int):Int = {

n + 1

}

// 直接傳入op調(diào)用f1函數(shù)

val result = f1(op)

// 采用不定義op操作函數(shù),直接匿名函數(shù)的方式

val result1 = f1((n:Int) => { n+1 })

//(1)參數(shù)的類型可以省略,會(huì)根據(jù)形參進(jìn)行自動(dòng)的推導(dǎo),因?yàn)閒1中已經(jīng)規(guī)定了op的參數(shù)為Int型

val result2 = f1((n) => { n+1 })

//(2)只有一個(gè)參數(shù)時(shí),則參數(shù)圓括號(hào)可以省略;其他情況:沒(méi)有參數(shù)和參數(shù)超過(guò) 1 的則不能省略圓括號(hào)。

val result3 = f1(n => { n+1 })

//(3)匿名函數(shù)如果只有一行,則大括號(hào)也可以省略,就是相當(dāng)于傳入?yún)?shù)的操作,中間用 => 連接

val result4 = f1(n => n+1)

//(4)如果參數(shù)在調(diào)用時(shí)只出現(xiàn)一次,則前面參數(shù)省略且后面參數(shù)可以用_代替,參數(shù)n在調(diào)用時(shí)只用一次

val result5 = f1(_+1)

//(5)如果可以推斷出,當(dāng)前傳入的是一個(gè)函數(shù)體,而不是調(diào)用語(yǔ)句,可以直接省略下劃線

示例代碼,簡(jiǎn)單實(shí)現(xiàn)1和2的相加和相減:

// 定義a和b相加和相減的匿名函數(shù),并將其返回值賦給addFun和defFun

var addFun = (a: Int, b: Int) => { a + b }

var defFun = (a: Int, b: Int) => { a - b }

// 定義函數(shù)f,里面的參數(shù)是函數(shù),返回值是Int類型

// 參數(shù)的函數(shù)類型為:兩個(gè)Int參數(shù),返回值為Int類型

def f(fun: (Int,Int) => Int):Int = {

fun(1, 2)

}

// 使用匿名函數(shù)addFun和defFun作為參數(shù),傳遞給f,并將f的返回值打印出來(lái)

println(f(addFun))

println(f(defFun))

// 其實(shí)匿名函數(shù)addFun和defFun等價(jià)于里面的實(shí)現(xiàn)語(yǔ)句,并省略了參數(shù)類型和花括號(hào)

// 因?yàn)閒函數(shù)里面已經(jīng)規(guī)定了a,b只能會(huì)Int類型,所以可以省略參數(shù)類型,函數(shù)體在一行所以可以省略花括號(hào)

println(f((a, b) => a + b ))

println(f((a, b) => a - b ))

// 參數(shù)a和參數(shù)b只在后面調(diào)用的時(shí)候調(diào)用1次,所以可以用下劃線_來(lái)代替

println(f(_ + _))

println(f(_ - _))

函數(shù)柯里化和閉包

閉包: 如果一個(gè)函數(shù),訪問(wèn)到了它的外部變量或者外部參數(shù),那么這個(gè)函數(shù)和它所處的環(huán)境(也就是外部變量的值)一起,稱之為閉包。 有如下函數(shù):

def func(i:Int):String=>(Char=>Boolean) = {

def f1(s:String):Char=>Boolean = {

def f2(c:Char):Boolean = {

if (i == 0 && s == "" and c == '0') false else true

}

f2

}

f1

}

// 正常調(diào)用如下:

func(0)("")('0')

// 如果將函數(shù)作為值傳遞,則有如下:

f3 = func(0)

println(f3("")('0')

在將 func(0) 的值賦值給 f3 之后,在調(diào)用 f3 的時(shí)候,這時(shí)候的 func 中的參數(shù) i 在內(nèi)存中還存在嗎? 閉包就是為了解決這個(gè)問(wèn)題,因?yàn)閒unc已經(jīng)運(yùn)行完了,在棧內(nèi)存中,i 的值是不存在的,但是系統(tǒng)將func中的參數(shù) i 跟函數(shù) f1 一起打包了,并將其存放在堆內(nèi)存中,這樣才能確保運(yùn)行f1,f2時(shí)能正常用到外部的變量而不會(huì)導(dǎo)致出錯(cuò)。這個(gè)就叫做閉包。

// 正常多個(gè)參數(shù)的寫法

def add(a:Int,b:Int):Int = {

a + b

}

// addByB 使用局部變量(外部變量)的閉包寫法

def addByFour():Int=>Int = {

val a = 4

def addByB(b:Int) => Int = {

a + b

}

addByB

}

// addByB 使用外部參數(shù)的閉包寫法

def addByA(a:Int):Int=>Int = {

def addByB(b:Int) => Int = {

a + b

}

addByB

}

// 調(diào)用

println(addByFour()(5))

println(addByA(4)(5))

柯里化: 把一個(gè)參數(shù)列表的多個(gè)參數(shù),變成多個(gè)參數(shù)列表的過(guò)程,稱之為函數(shù)柯里化。上面介紹了閉包就是為了引入柯里化,因?yàn)榭吕锘牡讓泳褪怯瞄]包來(lái)實(shí)現(xiàn)的。 柯里化其實(shí)就是為了更符合函數(shù)式編程的思想,因?yàn)樵诤瘮?shù)式編程里面,是沒(méi)有多個(gè)參數(shù)對(duì)應(yīng)的,例如f(x1,x2) => y ??粗械氖菃蝹€(gè)值的對(duì)應(yīng)關(guān)系,例如 f(x1)(x2) => y 的表達(dá)式,這里f(x1)返回的另一個(gè)函數(shù)然后參數(shù)是x2,就形成了一對(duì)一的嵌套關(guān)系。

// 上面使用外部參數(shù)的柯里化寫法

def addCurrying addByAB(a:Int)(b:Int):Int = {

a + b

}

// 調(diào)用

println(addCurrying(4)(5))

柯里化的寫法,我們發(fā)現(xiàn)簡(jiǎn)介很多,并且跟調(diào)用的寫法如出一轍,而柯里化的內(nèi)部寫法其實(shí)就是用了閉包的策略,但我們更推薦這種寫法,因?yàn)檫@種寫法更符合函數(shù)式編程的思想,也更容易理解。

遞歸

遞歸的含義其實(shí)很簡(jiǎn)單:一個(gè)函數(shù)/方法在函數(shù)/方法體內(nèi)又調(diào)用了本身,我們稱之為遞歸調(diào)用。 遞歸的一些注意事項(xiàng): (1) 方法調(diào)用自身 (2) 方法必須要有跳出的邏輯 (3) 方法調(diào)用自身時(shí),傳遞的參數(shù)應(yīng)該有規(guī)律 (4) scala 中的遞歸必須聲明函數(shù)返回值類型

// 用遞歸實(shí)現(xiàn)階乘

def test(i : Int) : Int = {

if (i == 1) {

1

} else {

i * test(i - 1)

}

}

println(test(5))

在 test 函數(shù)的內(nèi)部,又調(diào)用了 test 本身,這樣的實(shí)現(xiàn)方式就叫做遞歸。但遞歸本身有一個(gè)缺點(diǎn),就是每次調(diào)用內(nèi)部 test 的時(shí)候,需要先將 i 的值保存下來(lái),這樣每遞歸一次,就要保存外部的變量一次,當(dāng)遞歸次數(shù)很多時(shí),就會(huì)耗費(fèi)很大的資源進(jìn)行保存。

尾遞歸: 正常的遞歸,內(nèi)層需要用到外層的參數(shù),導(dǎo)致在執(zhí)行內(nèi)層的時(shí)候,需要先將外層參數(shù)的值保留下來(lái)。 尾遞歸,就是將外層的參數(shù)作為內(nèi)層的參數(shù),傳遞進(jìn)去給內(nèi)層的參數(shù),進(jìn)而內(nèi)層函數(shù)在調(diào)用時(shí),就可以保留函數(shù)和參數(shù),從而不需要外層函數(shù)的任何信息,從而不管遞歸多少次,都達(dá)到只保留一個(gè)棧信息的效果。

@tailrec

def tailFact(n:Int):Int = {

def loop(n:Int, curRes:Int):Int = {

if (n == 0) return curRes

loop(n - 1,curRes * n)

}

loop(n, curRes = 1)

}

在 loop 內(nèi)部又調(diào)用了 loop,遞歸調(diào)用,但是每次調(diào)用 loop 時(shí),會(huì)將上一次的結(jié)果 curRes * n 作為參數(shù)在傳遞給 loop,就能實(shí)現(xiàn)每次只保存一次參數(shù)的效果,更推薦使用。 上面的 @tailrec 是一個(gè)尾遞歸的注解,也是一個(gè)尾遞歸的判斷,當(dāng)下面寫的函數(shù)不是尾遞歸的時(shí)候,編譯器就會(huì)報(bào)錯(cuò),是個(gè)很好的檢查工具。

控制抽象

(1)傳值調(diào)用:把值傳遞過(guò)去

// 傳值參數(shù)

def f0(a:Int):Unit = {

println("a = " + a)

println("a = " + a)

}

f0(23)

def f1():Int = {

println("f1被調(diào)用")

12

}

// f1 調(diào)用1次之后,將f1函數(shù)的值傳遞給f0,f0輸出兩次12

f0(f1())

(2)傳名調(diào)用:把代碼塊傳遞過(guò)去(不同于上面的函數(shù)作為參數(shù)傳遞,這里是代碼塊,不是函數(shù))

// 傳名參數(shù)

def f1():Int = {

println("f1被調(diào)用")

12

}

def f2(a: =>Int):Unit = {

println("a = " + a)

println("a = " + a)

}

// 打印2次 a = 23

f2(23)

// 打印2次f1 被調(diào)用,并打印2次a = 12

f2(f1())

這里相當(dāng)于將整個(gè)f1的代碼塊,當(dāng)成參數(shù)傳入f2中,所以a在f2中出現(xiàn)多少次,也就運(yùn)行了f1的代碼塊多少次。=>Int 整個(gè)類型表示代碼塊,不像函數(shù),函數(shù)要求無(wú)參數(shù)的話,小括號(hào)不能省略()=>Int,這里只要求返回值為Int型即可,不要求有參數(shù)或者其他。

惰性加載

當(dāng)函數(shù)返回值被聲明為 lazy 時(shí),函數(shù)的執(zhí)行將被推遲,直到我們首次對(duì)此取值,該函數(shù)才會(huì)執(zhí)行。這種函數(shù)我們稱之為惰性函數(shù)。

def main(args: Array[String]) = {

lazy val res1 = sum1(10, 30)

val res2 = sum2(15, 20)

println("------------------------------")

println("res1 =" + res1)

println("------------------------------")

println("res2 =" + res2)

}

def sum1(a:Int,b:Int):Int = {

println("sum1 被執(zhí)行。。。")

a + b

}

def sum2(a:Int,b:Int):Int = {

println("sum2 被執(zhí)行。。。")

a + b

}

如上,res2在賦值的時(shí)候,sum2就已經(jīng)運(yùn)行了,而res1需要在res1要使用時(shí),在println(“res1 =” + res1)時(shí),才會(huì)去執(zhí)行sum1的語(yǔ)句。 注意:lazy 不能修飾 var 類型的變量

柚子快報(bào)邀請(qǐng)碼778899分享:大數(shù)據(jù)之Scala簡(jiǎn)介

http://yzkb.51969.com/

精彩內(nèi)容

評(píng)論可見(jiàn),查看隱藏內(nèi)容

本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。

轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。

本文鏈接:http://gantiao.com.cn/post/17816898.html

發(fā)布評(píng)論

您暫未設(shè)置收款碼

請(qǐng)?jiān)谥黝}配置——文章設(shè)置里上傳

掃描二維碼手機(jī)訪問(wèn)

文章目錄