Scala學習筆記02_函數入門
函數入門
函數的定義與調用,在Scala中定義函數時,需要定義函數的函數名、參數、函數體。
scala> :paste// Entering paste mode (ctrl-D to finish)def sayHello(name:String, age:Int) = { if(age >= 18) { printf("Hi, %s, you are a big boy!
", name) age } else { printf("Hi, %s, you are a children!
", name) age }}// Exiting paste mode, now interpreting.sayHello: (name: String, age: Int)Intscala> sayHello("padluo", 30)Hi, padluo, you are a big boy!res1: Int = 30
Scala要求必須給出所有參數的類型,但是不一定給出函數返回值的類型,只要右側的函數體不包含遞歸的語句,Scala就可以自己根據右側的表達式推斷出返回類型。
在代碼塊中定義包含多行語句的函數體,
單行的函數,
scala> def sayHello(name:String) = printf("Hello, " + name)sayHello: (name: String)Unitscala> sayHello("padluo")Hello, padluo
如果函數體中有多行代碼,則可以用代碼塊的方式包裹多行代碼,代碼塊中最後一行的返回值就是整個函數的返回值,與Java中不同,不是使用return返回值的。
# 函數定義沒有寫=scala> :paste// Entering paste mode (ctrl-D to finish)def sum(n:Int) { var result = 0 for(i <- 1 to n) { result += i }}// Exiting paste mode, now interpreting.sum: (n: Int)Unit# 函數定義寫了=,但是最後一行是賦值語句scala> :paste// Entering paste mode (ctrl-D to finish)def sum(n:Int) = { var result = 0 for(i <- 1 to n) { result += i }}// Exiting paste mode, now interpreting.sum: (n: Int)Unit# 正確定義scala> :paste// Entering paste mode (ctrl-D to finish)def sum(n:Int) = { var result = 0 for(i <- 1 to n) { result += i } result}// Exiting paste mode, now interpreting.sum: (n: Int)Int
遞歸函數與返回類型,如果在函數體內遞歸調用函數自身,則必須手動給出函數的返回類型。
scala> :paste// Entering paste mode (ctrl-D to finish)def fab(n:Int):Int = { if(n<=0) 1 else fab(n-1) + fab(n-2)}// Exiting paste mode, now interpreting.fab: (n: Int)Intscala> fab(10)res5: Int = 144
默認參數和帶名參數
默認參數,在Scala中,有時我們調用某些函數時,不希望給出具體的參數值,而希望使用參數自身默認的值,此時就在定義函數時使用默認參數。
scala> :paste// Entering paste mode (ctrl-D to finish)def sayHello(name:String, age:Int=20) { print("Hello, " + name + ", your age is " + age)}// Exiting paste mode, now interpreting.sayHello: (name: String, age: Int)Unitscala> sayHello("leo")Hello, leo, your age is 20scala> sayHello("leo", 30)Hello, leo, your age is 30
如果給出的參數不夠,則會從左到右依次應用參數。
帶名參數,在調用函數時,也可以不按照函數定義的參數順序來傳遞參數,而是使用帶名參數的方式來傳遞。
scala> sayHello(age=30, name="leo")Hello, leo, your age is 30
還可以混合使用未命名參數和帶名參數,但是未命名參數必須排在帶名參數前面。
scala> :paste// Entering paste mode (ctrl-D to finish)def sayHello(firstName:String, middleName:String="Willian",lastName:String="Croft") = print(firstName + " " + middleName + " " + lastName)// Exiting paste mode, now interpreting.sayHello: (firstName: String, middleName: String, lastName: String)Unitscala> sayHello("leo")leo Willian Croftscala> sayHello("leo", lastName="Jack", middleName="Tom")leo Tom Jack
變長參數
如果函數體包含在花括弧中,但沒有前面的=號,那麼返回類型就是Unit,這樣的函數被稱為過程,過程不返回值,我們調用它僅僅是為了它的副作用,有人不喜歡這種簡明寫法定義過程,並建議大家總是顯式聲明Unit返回類型。
scala> :paste// Entering paste mode (ctrl-D to finish)def sum(nums:Int*) { var result = 0 for(num <- nums) { result += num } result}// Exiting paste mode, now interpreting.sum: (nums: Int*)Unitscala> sum(1,2,3,4,5)scala>
在Scala中,有時我們需要將函數定義為參數個數可變的形式,則此時可以使用變長參數定義函數。
scala> :paste// Entering paste mode (ctrl-D to finish)def sum(nums:Int*) = { var result = 0 for(num <- nums) { result += num } result}// Exiting paste mode, now interpreting.sum: (nums: Int*)Intscala> sum(1,2,3,4,5)res0: Int = 15
使用序列調用變長參數,如果想要將一個已有的序列直接調用變長參數函數,是不對的,如val s = sum(1 to 5)
。此時需要使用Scala特殊的語法將參數定義為序列sum(1 to 5: _*)
,讓Scala解釋器能夠識別。
scala> 1 to 5res6: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5)scala> sum(1 to 5)<console>:13: error: type mismatch; found : scala.collection.immutable.Range.Inclusive required: Int sum(1 to 5)scala> sum(1 to 5:_*)res3: Int = 15scala> sum(1 to 5: _*)res4: Int = 15scala> sum(1 to 5 : _*)res5: Int = 15
使用遞歸函數實現累加,
scala> :paste// Entering paste mode (ctrl-D to finish)def sum2(nums:Int*): Int = { if (nums.length == 0) 0 else nums.head + sum2(nums.tail: _*)}// Exiting paste mode, now interpreting.sum2: (nums: Int*)Intscala> sum(1,2,3,4,5)res8: Int = 15
過程、lazy值和異常
過程,在Scala中,定義函數時,如果函數體直接包裹在了花括弧裡面,而沒有使用=連接,則函數的返回值類型就是Unit。這樣的函數稱為過程。過程通常用於不需要返回值的函數。
過程還有一種寫法,就是將函數的返回值類型定義為Unit。
# 函數,沒有顯式聲明返回值類型,自動推斷返回值類型scala> def sayHello(name:String) = "Hello, " + namesayHello: (name: String)Stringscala> sayHello("padluo")res1: String = Hello, padluo# 過程,沒有使用=連接,調用它僅僅是為了它的副作用(print ...),即使塊最後的表達式有值,整個函數最終是沒有值返回的scala> def sayHello(name:String) {print("Hello, " + name); "Hello, " + name}sayHello: (name: String)Unitscala> sayHello("padluo")Hello, padluo# 顯式聲明返回值類型為Unit,即使塊最後的表達式有值,函數最終也是沒有值返回scala> def sayHello(name:String): Unit = "Hello," + namesayHello: (name: String)Unitscala> sayHello("padluo")scala>
lazy值,如果將一個變數聲明為lazy,則只有在第一次使用該變數時,變數對應的表達式才會發生計算,這種特性對於特別耗時的操作特別有用,比如打開文件進行IO,進行網路IO等。
scala> import scala.io.Source._import scala.io.Source._scala> lazy val lines = fromFile("/home/hadoop/test.txt").mkStringlines: String = <lazy>scala> print(lines)Hello Worldscala> lazy val lines = fromFile("/home/hadoop/test1.txt").mkStringlines: String = <lazy>scala> print(lines)java.io.FileNotFoundException: /home/hadoop/test1.txt (No such file or directory) at java.io.FileInputStream.open(Native Method) at java.io.FileInputStream.<init>(FileInputStream.java:146) at scala.io.Source$.fromFile(Source.scala:91) at scala.io.Source$.fromFile(Source.scala:76) at scala.io.Source$.fromFile(Source.scala:54) at .lines$lzycompute(<console>:14) at .lines(<console>:14) ... 32 elidedscala> val lines = fromFile("/home/hadoop/test1.txt").mkStringjava.io.FileNotFoundException: /home/hadoop/test1.txt (No such file or directory) at java.io.FileInputStream.open(Native Method) at java.io.FileInputStream.<init>(FileInputStream.java:146) at scala.io.Source$.fromFile(Source.scala:91) at scala.io.Source$.fromFile(Source.scala:76) at scala.io.Source$.fromFile(Source.scala:54) ... 32 elidedscala> def getLines = fromFile("/home/hadoop/test1.txt").mkStringgetLines: Stringscala> getLinesjava.io.FileNotFoundException: /home/hadoop/test1.txt (No such file or directory) at java.io.FileInputStream.open(Native Method) at java.io.FileInputStream.<init>(FileInputStream.java:146) at scala.io.Source$.fromFile(Source.scala:91) at scala.io.Source$.fromFile(Source.scala:76) at scala.io.Source$.fromFile(Source.scala:54) at .getLines(<console>:14) ... 32 elided
val lines = fromFile("/home/hadoop/test1.txt").mkString
,即使文件不存在也不會報錯,只有第一個使用變數時會報錯,證明了表達式計算的lazy特性。
異常,
scala> :paste// Entering paste mode (ctrl-D to finish)try { throw new IllegalArgumentException("illegal argument!")} catch { case _: IllegalArgumentException => print("sorry, error!")} finally { print("
release io resources!")}// Exiting paste mode, now interpreting.sorry, error!release io resources!scala> import java.io._import java.io._scala> :paste// Entering paste mode (ctrl-D to finish)try { throw new IOException("user defined exception")} catch { case e1: IllegalArgumentException => println("illegal argument") case e2: IOException => print("io exception")}// Exiting paste mode, now interpreting.io exception
本文首發於steem,感謝閱讀,轉載請註明。
https://steemit.com/@padluo
微信公眾號「padluo」,分享數據科學家的自我修養,既然遇見,不如一起成長。
http://weixin.qq.com/r/P3WGnj3E82CMrXn99yAt (二維碼自動識別)
讀者交流電報群
https://t.me/sspadluo
知識星球交流群
推薦閱讀:
※你是蘋果用戶,活該多交錢!老用戶與狗不得優惠?
※一文讀懂物聯網、雲計算與大數據的關係
※今日數據行業日報(2017.01.04)
※iCloud雲上貴州,2018數博會連接2億多用戶
※雨沐田:說說政府開放大數據