1. 程式人生 > >Scala 程式設計—第五節:函式與閉包

Scala 程式設計—第五節:函式與閉包

1.函式定義

如下,定義一個函式,用來比較兩數大小得出最大值

def max(x: Int, y: Int): Int = {
  if (x> y) x
  else y
}

以上述定義的函式為例,看下scala函式的基本構成
這裡寫圖片描述

max 函式可以簡寫為如下

def max(x: Int, y: Int) = if (x > y) x else y

函式呼叫

println(max(3, 5)) // 執行結果:5

2.方法定義

Scala 有方法與函式,二者在語義上的區別很小,Scala 方法是類的一部分,而函式是一個物件可以賦值給一個變數,換言之就是在類中定義的函式即是方法。

如下,定義了兩個方法,用於讀取指定檔案並列印輸出所有長度超過設定值得行

package com.harvey

import scala.io.Source

object LongLines {

  def processFile(filename : String, width : Int): Unit = {
    val source = Source.fromFile(filename)
    for (line <- source.getLines) {
      processLine(filename, width, line)
    }
  }

  private def processLine(filename : String, width : Int, line : String): Unit = {
    if (line.length > width) {
      println(filename + ":" + line.trim)
    }
  }
}

main方法呼叫執行

package com.harvey

object FindLongLines {
  def main(args: Array[String]): Unit = {
    val width = 45;
    val filename = "D:\\code\\study\\scalademo\\src\\main\\java\\com\\harvey\\LongLines.scala"
    LongLines.processFile(filename, width)
  }
}

執行結果:

D:\code\study\scalademo\src\main\java\com\harvey\LongLines.scala:def processFile(filename : String, width : Int): Unit = {
D:\code\study\scalademo\src\main\java\com\harvey\LongLines.scala:private def processLine(filename : String, width : Int, line : String): Unit = {

3.本地函式

上述例子中,使用的private修飾了processLine方法,使之成為私有方法(類比java中的私有方法),只能在LongLines 類中使用,Scala中還提供了另一種方式,可以把函式定義在別的函式之內,即巢狀函式,如果本地變數一樣,修改程式碼如下

def processFile(filename: String, width: Int): Unit = {
  def processLine(filename: String, width: Int, line: String): Unit = {
    if(line.length > width) {
      println(filename + ": " + line)
    }
  }
  val source = Source.fromFile(filename)
  for (line <- source.getLines){
    processLine(filename, width, line)
  }
}

執行結果同上

4.頭等函式

Scala的函式是頭等函式,不僅可以定義和呼叫函式,還可以寫成匿名的字面量,並作為值傳遞。
對數執行遞增操作的函式字面量例子:

(x: Int) => x + 1

=>指明這個函式把左邊的東西(任意整數x)轉變成右邊的東西(x + 1),即把任意整數對映成x + 1,簡單理解就是=>左邊是表示輸入,右邊表示轉換操作

函式值就是物件也是函式,可以存入變數,也可以使用括號來呼叫

var incr = (x: Int) => x + 1
print(incr(10)) // 執行結果:11

如果想讓函式字面量包含多條語句,可以使用花括號包住函式體,例如

var incr = (x: Int) => {
  print("result = ")
  x + 1
}
print(incr(10)) // 執行結果:result = 11

5.函式字面量的短格式

Scala 提供了許多方法來取出冗餘資訊並把函式字面量寫的更簡短,例如

var someNumbers = List(-1, -3, 2, 4, 9, -9)
var result = someNumbers.filter((x: Int) => x > 0)
print(result) // 執行結果:List(2, 4, 9)

可以將filter中的函式進行簡寫,去掉型別

var result = someNumbers.filter(x => x > 0)

6.佔位符語法

如果想讓函式字面量更簡潔,可以把下劃線當做一個或更多引數的佔位符,只要每個引數在函式字面量中出現一次,比如,_>0 檢查值是否大於零

var result = someNumbers.filter(_> 0)

可以把_看作表示式需要被“填入”的“空白”。這個空白在每次函式呼叫的時候用函式的引數填入。例如
someNumbers初始化值為List(-1, -3, 2, 4, 9, -9),filter(_>0),裡面的_首先使用-1替換,即-1>0,然後再用-3替換,依次類推。

需要注意,有時把下劃線當作引數的佔位符,編譯器可能無法推斷缺失的引數型別,例如

val f = _ + _

會報如下錯誤

missing parameter type for expanded function ((x$1, x$2) => x$1.$plus(x$2))
    val f = _ + _

這種情況下,使用冒號指定型別即可

val f = (_: Int) + (_: Int)
print(f(2, 8)) // 執行結果:10

7.部分應用函式

下劃線不僅能替換單個引數,還可以替換整個引數列表,例如:寫成println()或println。如下

someNumbers.foreach(println(_))

Scala把這種簡短格式直接看作你輸入瞭如下程式碼:

someNumbers.foreach(x => println(x))

上面例子中的下劃線不是單個引數的佔位符,而是整個引數列表的佔位符,以這種方式使用下劃線時,其實就是一個部分應用函式。我們再看個例子,如下

def sum(a: Int, b: Int, c: Int) = a + b + c
print(sum(1, 2, 3)) // 執行結果:6

部分應用函式是一種表示式,不需要函式所需要的所有引數,比如:要建立呼叫sum的部分應用表示式,而不提供任何3個所需引數,只要在“sum”之後加上下劃線,然後可以把得到的函式存入變數

var a = sum _
print(a(1, 2, 3)) // 執行結果:6

實際發生的事情是這樣的:名為a的 變數只是一個函式值物件。這個函式值由scala編譯器依照部分應用函式表示式sum _,自動產生類的例項。編譯器產生的類有一個apply方法帶3個引數。之所以帶3個引數是因為sum _表示式缺少的引數數量為3,Scala編譯器把表示式a(1, 2, 3)翻譯成對函式值的apply方法的呼叫,傳入3個引數1、2、3。因此a(1, 2, 3)是下列程式碼的短格式

a.apply(1, 2, 3)

8.閉包

在理解閉包是什麼意思前,我們先來看個例子

val addMore = (x: Int) => x + more
print(addMore(10))

執行報錯:

Error:(37, 35) not found: value more
    val addMore = (x: Int) => x + more

x變數是一個繫結變數,它在函式的上下文有明確意義,被定義函式的唯一引數是Int,值函式呼叫的時候被賦值,稱這種變數為繫結變數(Bound Variable)。more是一個沒有給定含義的不定變數,這裡的more是一個自由變數(Free Variable)。所以在使用的時候,編譯器會報錯。改寫上述程式碼如下

var more = 1
val addMore = (x: Int) => x + more
print(addMore(10)) // 執行結果:11

像這種執行時確定more型別及值的函式稱為閉包,more是個自由變數,在執行時其值和型別得以確定
這是一個由開放(free)到封閉的過程,因此稱為閉包。

9.重複引數

Scala中,可以指明函式的最後一個引數是重複的,即可變引數,類似java中動態引數。標註一個重複引數,在引數型別後面加一個星號

scala> def echo(args: String*) = for(arg <- args) println(arg)
echo: (args: String*)Unit
scala> echo()
scala> echo("hello")
hello
scala> echo("hello", "scala")
hello
scala

可能你會想到,在傳入多個引數的時候,把引數封裝為一個Array,我們來看下,是否可以

val arr = Array("hello", "scala")
echo(arr)

執行報錯:

Error:(51, 10) type mismatch;
 found   : Array[String]
 required: String
    echo(arr)

函式內部,重複引數的型別是宣告引數型別的陣列,想要實現將陣列中的每個元素當做引數,而不是當做單一的引數傳給echo,在陣列引數後面新增一個冒號和_*符號,如下

scala> echo(arr: _*)
hello
scala

相關推薦

Scala 程式設計函式

1.函式定義 如下,定義一個函式,用來比較兩數大小得出最大值 def max(x: Int, y: Int): Int = { if (x> y) x else y } 以上述定義的函式為例,看下scala函式的基本構成 max 函式可以簡寫為

Scala 程式設計類和物件(二)

前言:         類和物件第二節,主要介紹:單例物件、伴生物件與伴生類、apply方法、抽象類 1.單例物件 Java語言中,如果想直接使用類名點的方式呼叫方法或屬性,直接用static修飾即可。但Scala語言不支援靜態成員,而提供了object物件,這

Akka併發程式設計——Actor模型(四)

本節主要內容: 1. 停止Actor 1. 停止Actor (1)通過ActorSystem.shutdown方法停止所有 Actor的執行 /* *停止Actor:ActorSystem.shutdown方法 */ object Example_1

Scala學習六彈函式匿名函式

一、函式是第一等公民 Scala中,函式上升和變數同等的位置,或者說函式也是一種變數。 Scala中的函式可以作為實參傳遞給另一個函式; 函式可以作為返回值; 函式可以賦值給變數(這個變數需符合函式的型別的變數); 函式可以儲存在資料結構之中。 函式如同普通變數一樣,也具有

學習筆記=>《你不知道的JavaScript(上卷)》作用域

什麼是詞法作用域?   在之前講過,我們平常寫程式碼的時候,建立一個變數和方法的時候在其書寫的位置(所在環境)會形   成一個作用域,即為詞法作用域,該作用域中的屬性和方法只能在當前環境內使用。   閉包   最簡單的一個閉包例項: function fun(){ va

Scala入門到精通—— 函式

本節主要內容 (一)函式字面量(值函式) (二)匿名函式 (三)函式的簡化 (四)函式引數 (四)閉包 函式字面量(值函式) 函式字面量(function literal),也稱值函式(function values),指的是函式可以賦值給變數

SignalR大雜燴(MVC融合、全局的幾個配置、跨域的應用、C/S程序充當Client和Server)

融合 變化 方法 signalr 以管理員身份運行 cal 大小 整合 ref 一. 說在前面的話   本節主要在前面章節的基礎上補充了幾個簡單的知識點,比如:第三方調用通過 GlobalHost.ConnectionManager.GetHubContext<

全國計算機等級考試二級教程--python語言程式設計(2018年版)函式和程式碼複用

宣告:本篇文章只是個人知識盲區、知識弱點、重點部分的歸納總結,望各位大佬不喜勿噴。梳理順序是按照書籍的實際順序梳理,轉載請註明出處。 作者:sumjess   一、函式的基本使用:       函式包括:函式的定義和函式的使用。 &nb

Akka併發程式設計——Actor模型(

本將主要內容: 1. !訊息傳送,Fire-and-Forget訊息模型 2. ?訊息傳送,Send-And-Receive-Future訊息模型 Akka提供了兩種訊息模型:fire-and-forget和Send-And-Receive-Future。

詳細講解Java中的介面繼承

前言大家好,我是 Vic,今天給大家帶來詳細講解Java中的介面與繼承的概述,希望你們喜歡什麼是介面(interface)介面中的方法都是抽象方法,public許可權,全是抽象函式,不能生成物件interface Student{ public void read(); pu

《C# 爬蟲 破境之道》第二境 爬蟲應用 — 小總結帶來的優化重構

在上一節中,我們完成了一個簡單的採集示例。本節呢,我們先來小結一下,這個示例可能存在的問題: 沒有做異常處理 沒有做反爬應對策略 沒有做重試機制 沒有做併發限制 …… 呃,看似平靜的表面下還是隱藏著不少殺機的…… 但本節不打算對付上述問題,而是先關注一個隱藏更深的問題,這個問題,可能會牽扯很多人(包括我☹

JQuery框架源碼簡析(1)

err cal content browser active lac rda setting right (轉自老惠的博客) JQuery是一個應用廣泛、非常優秀的JavaScript框架,其代碼簡潔而優雅,有很多值得我們學習的地方。這裏僅僅對其代碼結構做一個簡單的分析

手動安裝K8smaster節點kubectl命令部署

docker k8s kubernetes 容器 1、部署kubectl 命令行工具準備二進制包[root@k8smaster ~]# cd /usr/local/src/kubernetes/client/bin[root@k8smaster bin]# cp kubectl /opt/ku

38hashCode()toString()equals()函數的作用,內部類和匿名內部類

指向 它的 函數 ati 使用外部 開發 算法 博客 else hashCode()和toString() Hash算法是把任意長度的數據通過hash算法成為散列值 hashCode() public int hashCode(){ int result = 10;

十一、Go基礎程式設計遞迴函式函式型別、匿名函式

1. 遞迴函式 遞迴指函式可以直接或間接的呼叫自身。 遞迴函式通常有相同的結構:一個跳出條件和一個遞迴體。所謂跳出條件就是根據傳入的引數判斷是否需要停止遞迴,而遞迴體則是函式自身所做的一些處理。 //通過迴圈實現1+2+3……+100 func Test01() int { i

團隊部落格-測試釋出(科利爾拉弗隊)

測試: BUG:  (1)主頁帖子列表排序出錯,未按照帖子最新回覆和帖子釋出時間排序,已修復  (2)註冊時郵箱驗證和註冊成功跳轉不完善,未修復  (3)資料傳輸方式錯誤,已修復 場景測試:  預期使用者可以跟使用其他相似的社群網站一樣正常使用本網站,但是貓

Akka併發程式設計——Actor模型(六)

主要內容: 1. Typed Actor定義 2. Typed Actor建立 3. 訊息傳送 1. Typed Actor定義 Akka中的Typed Actor是Active Objects設計模式的實現,Active Objects模式將方法的執

Akka併發程式設計——Actor模型(三)

本將主要內容: 1. Actor引用、Actor路徑 1. Actor引用、Actor路徑 下圖是Akka官方文件中給出的一張圖 該圖清晰地說明了ActorPath,ActorRef,Actor及ActorSystem之間的關係,並說明了Actor整

python全棧開發中級班全程筆記(第二模組、三章)4 函式進階

python全棧開發筆記第二模組 第三章4節:函式進階 一、函式進階 1、函式進階--名稱空間   名稱空間,又稱名稱空間,或名字空間,就是存放變數名的地方比如 n = 1 , 1是存在記憶體的,但n 這個名字和繫結關係就儲存在名稱空間   *名稱空間和作用域是有直接關係的,   &nb

golang語言學函式

定義函式: func 函式名(引數1....引數n ) 返回值  { 函式體 } 需要注意的是,go語言的函式,可以有多個返回值,並且返回值可以取名字。不限於2個返回值,可以3個,4個或更多返回值。 另外,go語言支援可變引數列表,但是用的時候就當切片來用。 go語言的函式可以