scala函數(shù)概念解析

“執(zhí)大象,天下往旬昭。
往而不害篙螟,安平太。
樂(lè)與餌问拘,過(guò)客止遍略。
道之出口,淡乎其無(wú)味骤坐,視之不足見(jiàn)墅冷,聽(tīng)之不足聞,用之不足既或油∧蓿”[1]

scala作為支持函數(shù)式編程的語(yǔ)言,scala函數(shù)式編程是scala的重中之重顶岸,spark當(dāng)中的計(jì)算都是用scala函數(shù)式編程來(lái)做腔彰,高級(jí)函數(shù)也是其獨(dú)特的一個(gè)特性,并且spark基于集合辖佣,這樣可以使scala發(fā)揮其對(duì)于集合計(jì)算的強(qiáng)大功能霹抛。首先,函數(shù)/變量同是一等公民卷谈,函數(shù)與變量同等地位杯拐,函數(shù)的定義可以單獨(dú)定義,可以不依賴于類、接口或者object端逼,而且獨(dú)立存在朗兵,獨(dú)立使用,并且可以賦值給變量顶滩。

函數(shù)傳名調(diào)用(Call-by-Name)余掖、傳值調(diào)用(Cal-by-Value)

Scala的解釋器在解析函數(shù)參數(shù)(function arguments)時(shí)有兩種方式:

  • 傳值調(diào)用(call-by-value)
    先計(jì)算參數(shù)表達(dá)式的值(reduce the arguments),再應(yīng)用到函數(shù)內(nèi)部礁鲁;
  • 傳名調(diào)用(call-by-name)
    將未計(jì)算的參數(shù)表達(dá)式直接應(yīng)用到函數(shù)內(nèi)部盐欺。
package com.cgoshine.sh.demo  
  
object Add {  
  def addByName(a: Int, b: => Int) = a + b   
  def addByValue(a: Int, b: Int) = a + b   
} 

addByName是傳名調(diào)用,addByValue是傳值調(diào)用仅醇。語(yǔ)法上可以看出冗美,使用傳名調(diào)用時(shí),在參數(shù)名稱和參數(shù)類型中間有一個(gè)=>符號(hào)析二。

覺(jué)得傳名參數(shù)會(huì)很少用到粉洼,跟java的傳參習(xí)慣看起就可以了。

指定函數(shù)參數(shù)名

一般情況下函數(shù)調(diào)用參數(shù)甲抖,就按照函數(shù)定義時(shí)的參數(shù)順序一個(gè)個(gè)傳遞。但是我們也可以通過(guò)指定函數(shù)參數(shù)名心铃,并且不需要按照順序向函數(shù)傳遞參數(shù)准谚。

object Test {
   def main(args: Array[String]) {
        printInt(b=5, a=7);
   }
   def printInt( a:Int, b:Int ) = {
      println("Value of a : " + a );
      println("Value of b : " + b );
   }
}

可變參數(shù)

Scala 通過(guò)在參數(shù)的類型之后放一個(gè)*星號(hào)來(lái)設(shè)置可變參數(shù)(可重復(fù)的參數(shù))。

object Test {
   def main(args: Array[String]) {
        printStrings("Java", "Scala", "Python");
   }
   def printStrings( args:String* ) = {
      var i : Int = 0;
      for( arg <- args ){
         println("Arg value[" + i + "] = " + arg );
         i = i + 1;
      }
   }
}

遞歸函數(shù)

object Test {
   def main(args: Array[String]) {
      for (i <- 1 to 10)
         println(i + " 的階乘為: = " + factorial(i) )
   }
   
   def factorial(n: BigInt): BigInt = {  
      if (n <= 1)
         1  
      else    
         n * factorial(n - 1)
   }
}

默認(rèn)參數(shù)值

Scala 可以為函數(shù)參數(shù)指定默認(rèn)參數(shù)值去扣,使用了默認(rèn)參數(shù)柱衔,你在調(diào)用函數(shù)的過(guò)程中可以不需要傳遞參數(shù),這時(shí)函數(shù)就會(huì)調(diào)用它的默認(rèn)參數(shù)值愉棱,如果傳遞了參數(shù)唆铐,則傳遞值會(huì)取代默認(rèn)值。

object Test {
   def main(args: Array[String]) {
        println( "返回值 : " + addInt() );
   }
   def addInt( a:Int=5, b:Int=7 ) : Int = {
      var sum:Int = 0
      sum = a + b
      return sum
   }
}

高階函數(shù)

高階函數(shù)(Higher-Order Function)就是操作其他函數(shù)的函數(shù)奔滑。
Scala 中允許使用高階函數(shù), 高階函數(shù)可以使用其他函數(shù)作為參數(shù)艾岂,或者使用函數(shù)作為輸出結(jié)果。

  • 將定義的函數(shù)賦給某變量
def func1(s:String):Unit = {
    println(s)
  }

val func2 = func1 _

val變量名 = 函數(shù)名+空格+_

這里函數(shù)名后面必須要有空格朋其,表明是函數(shù)的原型王浴。
高階函數(shù)是函數(shù)的參數(shù)也是函數(shù)。(因?yàn)楹瘮?shù)的參數(shù)可以是變量梅猿,而函數(shù)又可以賦值給變量氓辣,即函數(shù)和變量地位一樣,所以函數(shù)參數(shù)也可以是函數(shù))袱蚓。

scala> val iGreeting = (content:String) => println(content)
iGreeting: String => Unit = $$Lambda$1052/5181771@257f30f7
scala> def sendGreeting(func:(String) => Unit,content:String){func(content)}
sendGreeting: (func: String => Unit, content: String)Unit
scala> sendGreeting(iGreeting,"scala")
scala

首先我們定義了一個(gè)函數(shù)sendGreeting,這個(gè)函數(shù)有兩個(gè)參數(shù)钞啸,第一個(gè)參數(shù)是一個(gè)函數(shù),函數(shù)名是func,他有一個(gè)String類型的參數(shù)并且返回值是unit空的体斩;第二個(gè)參數(shù)是String類型的變量名為content的變量梭稚,函數(shù)體是將第二個(gè)參數(shù)作為第一個(gè)參數(shù)也就是函數(shù)func的參數(shù),來(lái)調(diào)用第一個(gè)函數(shù)硕勿,整個(gè)函數(shù)返回值為unit空哨毁。這里只要傳入的函數(shù)的格式與定義的一致就行。

scala> val array = Array(1,2,3,4,5,6)
array: Array[Int] = Array(1, 2, 3, 4, 5, 6)

scala> array.map(item => item*2)
res2: Array[Int] = Array(2, 4, 6, 8, 10, 12)

Array.map()作用源武,他會(huì)遍歷array中每一個(gè)元素扼褪,并將每個(gè)元素作為具體的值傳給map中的作為參數(shù)的函數(shù)。

高階函數(shù)有個(gè)非常有用的特性是類型推斷粱栖。其可以自動(dòng)推斷出參數(shù)的類型话浇,而且對(duì)于只有一個(gè)的參數(shù)的函數(shù),可以省略掉小括號(hào)闹究,并且在函數(shù)的參數(shù)作用的函數(shù)體內(nèi)只是用一次函數(shù)的輸入?yún)?shù)的值話幔崖,就可省略掉函數(shù)名,用下劃線(_)代替渣淤。

scala> val array = Array(1,2,3,4,5,6)
array: Array[Int] = Array(1, 2, 3, 4, 5, 6)

scala> array.map(_ * 2)
res3: Array[Int] = Array(2, 4, 6, 8, 10, 12)

scala> array.map(_ * 2).foreach(println(_))
2
4
6
8
10
12

scala> array.map(_ * 2).foreach(println _)
2
4
6
8
10
12

scala> array.map(_ * 2).foreach(println)
2
4
6
8
10
12

scala> array.map(_ * 2).filter(_ > 6).foreach(println)
8
10
12

內(nèi)嵌函數(shù)

我們可以在 Scala 函數(shù)內(nèi)定義函數(shù)赏寇,定義在函數(shù)內(nèi)的函數(shù)稱之為局部函數(shù)。

object Test {
   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)
   }
}

匿名函數(shù)

spark中大都用的是匿名函數(shù)(不為函數(shù)命名)价认,然后將其復(fù)制個(gè)一個(gè)變量嗅定。
匿名函數(shù)格式:

Val 變量名 = (參數(shù):類型) => 函數(shù)體

val func1 = (s:String) => println(s)
func1("scala")

偏應(yīng)用函數(shù)

Scala 偏應(yīng)用函數(shù)是一種表達(dá)式,你不需要提供函數(shù)需要的所有參數(shù)用踩,只需要提供部分渠退,或不提供所需參數(shù)。

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)
   }
}

輸出結(jié)果:

$ scalac Test.scala
$ scala Test
Tue May 22 15:53:39 CST 2018----message1
Tue May 22 15:53:39 CST 2018----message2
Tue May 22 15:53:39 CST 2018----message3

log() 方法接收兩個(gè)參數(shù):date 和 message脐彩。我們?cè)诔绦驁?zhí)行時(shí)調(diào)用了三次碎乃,參數(shù) date 值都相同,message 不同惠奸。
我們可以使用偏應(yīng)用函數(shù)優(yōu)化以上方法梅誓,綁定第一個(gè) date 參數(shù),第二個(gè)參數(shù)使用下劃線(_)替換缺失的參數(shù)列表佛南,并把這個(gè)新的函數(shù)值的索引的賦給變量证九。

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)
   }
}

輸出結(jié)果:

$ scalac Test.scala
$ scala Test
Tue May 22 15:53:39 CST 2018----message1
Tue May 22 15:53:39 CST 2018----message2
Tue May 22 15:53:39 CST 2018----message3

函數(shù)柯里化(Function Currying)

柯里化(Currying)指的是將原來(lái)接受兩個(gè)參數(shù)的函數(shù)變成新的接受一個(gè)參數(shù)的函數(shù)的過(guò)程。新的函數(shù)返回一個(gè)以原有第二個(gè)參數(shù)為參數(shù)的函數(shù)共虑。其也利用了閉包的特性愧怜。
定義一個(gè)函數(shù):

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

把這個(gè)函數(shù)變一下形:

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

這種方式(過(guò)程)就叫柯里化。


  1. 老子《道德經(jīng)》第三十五章妈拌,老子故里拥坛,中國(guó)鹿邑蓬蝶。 ?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市猜惋,隨后出現(xiàn)的幾起案子丸氛,更是在濱河造成了極大的恐慌,老刑警劉巖著摔,帶你破解...
    沈念sama閱讀 221,820評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缓窜,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡谍咆,警方通過(guò)查閱死者的電腦和手機(jī)禾锤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)摹察,“玉大人恩掷,你說(shuō)我怎么就攤上這事」┖浚” “怎么了黄娘?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,324評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)克滴。 經(jīng)常有香客問(wèn)我逼争,道長(zhǎng),這世上最難降的妖魔是什么劝赔? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,714評(píng)論 1 297
  • 正文 為了忘掉前任誓焦,我火速辦了婚禮,結(jié)果婚禮上望忆,老公的妹妹穿的比我還像新娘罩阵。我一直安慰自己竿秆,他們只是感情好启摄,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著幽钢,像睡著了一般歉备。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上匪燕,一...
    開(kāi)封第一講書(shū)人閱讀 52,328評(píng)論 1 310
  • 那天蕾羊,我揣著相機(jī)與錄音,去河邊找鬼帽驯。 笑死龟再,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的尼变。 我是一名探鬼主播利凑,決...
    沈念sama閱讀 40,897評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼浆劲,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了哀澈?” 一聲冷哼從身側(cè)響起牌借,我...
    開(kāi)封第一講書(shū)人閱讀 39,804評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎割按,沒(méi)想到半個(gè)月后膨报,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,345評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡适荣,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評(píng)論 3 340
  • 正文 我和宋清朗相戀三年现柠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片束凑。...
    茶點(diǎn)故事閱讀 40,561評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡晒旅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出汪诉,到底是詐尸還是另有隱情废恋,我是刑警寧澤,帶...
    沈念sama閱讀 36,238評(píng)論 5 350
  • 正文 年R本政府宣布扒寄,位于F島的核電站鱼鼓,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏该编。R本人自食惡果不足惜迄本,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望课竣。 院中可真熱鬧嘉赎,春花似錦、人聲如沸于樟。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,417評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)迂曲。三九已至靶橱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間路捧,已是汗流浹背关霸。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,528評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留杰扫,地道東北人队寇。 一個(gè)月前我還...
    沈念sama閱讀 48,983評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像章姓,于是被迫代替她去往敵國(guó)和親佳遣。 傳聞我的和親對(duì)象是個(gè)殘疾皇子炭序,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • Scala與Java的關(guān)系 Scala與Java的關(guān)系是非常緊密的!苍日! 因?yàn)镾cala是基于Java虛擬機(jī)惭聂,也就是...
    燈火gg閱讀 3,452評(píng)論 1 24
  • 函數(shù)和對(duì)象 1、函數(shù) 1.1 函數(shù)概述 函數(shù)對(duì)于任何一門(mén)語(yǔ)言來(lái)說(shuō)都是核心的概念相恃。通過(guò)函數(shù)可以封裝任意多條語(yǔ)句辜纲,而且...
    道無(wú)虛閱讀 4,581評(píng)論 0 5
  • 原文鏈接:https://github.com/EasyKotlin 值就是函數(shù),函數(shù)就是值拦耐。所有函數(shù)都消費(fèi)函數(shù)耕腾,...
    JackChen1024閱讀 5,988評(píng)論 1 17
  • 桃花落,閑池閣杀糯,山盟雖在扫俺,錦書(shū)難托,莫固翰,莫狼纬,莫! 「函數(shù)(Function)」是函數(shù)式編程的基本單元骂际。本文將重點(diǎn)討...
    劉光聰閱讀 4,031評(píng)論 6 13
  • 她的笑疗琉,就像冬日的暖陽(yáng)。
    看哪里呢_0b9d閱讀 215評(píng)論 0 1