入門ビッグデータ---Scala関数と閉パッケージ

6174 ワード

一、関数
1.1関数と方法
Scalaでは関数とメソッドの違いは非常に小さく,関数がオブジェクトのメンバーである場合,このような関数をメソッドと呼び,そうでない場合は正常な関数である.
//     
def multi1(x:Int) = {x * x}
//     
val multi2 = (x: Int) => {x * x}

println(multi1(3)) //   9
println(multi2(3)) //   9
defを使用して関数を定義することもできます.
def multi3 = (x: Int) => {x * x}
println(multi3(3))  //   9 
multi2multi3は本質的に区別されない.これは、関数が一等公民であるためである.val multi2 = (x: Int) => {x * x}この文はdefを用いて関数を予め定義し、その後変数multi2に値を付与することに相当する.
1.2関数タイプmulti2multi3は本質的に同じですが、関数としてはどのようなタイプですか?どちらのタイプも実際にはInt => Intであり、前のIntは入力パラメータタイプを表し、後のIntは戻り値タイプを表す.
scala> val multi2 = (x: Int) => {x * x}
multi2: Int => Int = $$Lambda$1092/594363215@1dd1a777

scala> def multi3 = (x: Int) => {x * x}
multi3: Int => Int

//        ,    :(    ,     ...)=>     
scala> val multi4 = (x: Int,name: String) => {name + x * x }
multi4: (Int, String) => String = $$Lambda$1093/1039732747@2eb4fe7

1.3一等公民&匿名関数
Scalaでは、関数は一等公民です.これは、関数を定義して呼び出すだけでなく、値として渡すこともできることを意味します.
import scala.math.ceil
object ScalaApp extends App {
  //     ceil       fun,      (_)     ceil         
  val fun = ceil _
  println(fun(2.3456))  //   3.0

}

Scalaでは、(x: Int) => 3 * xが匿名関数であるなど、各関数に名前を付ける必要はありません.
object ScalaApp extends App {
  // 1.    
  (x: Int) => 3 * x
  // 2.    
  val fun = (x: Int) => 3 * x
  // 3.        
  val array01 = Array(1, 2, 3).map((x: Int) => 3 * x)  
  // 4.           
  val array02 = Array(1, 2, 3).map(_ * 3)
  // 5.      
  val array03 = Array(1, 2, 3).map(fun)
  
}

1.4特殊な関数式
1.可変長パラメータリスト
Javaで可変長のパラメータを渡すにはString ...argsという形式を使う必要があります.Scalaでは等価にargs: String*と表現されています.
object ScalaApp extends App {
  def echo(args: String*): Unit = {
    for (arg 

2.名前付きパラメータを渡す
関数にパラメータを渡すときに、特定のパラメータ名を指定できます.
object ScalaApp extends App {
  
  def detail(name: String, age: Int): Unit = println(name + ":" + age)
  
  // 1.           
  detail("heibaiying", 12)
  // 2.              ,          
  detail(age = 12, name = "heibaiying")

}

3.デフォルトパラメータ
関数を定義するときに、パラメータのデフォルト値を指定できます.
object ScalaApp extends App {

  def detail(name: String, age: Int = 88): Unit = println(name + ":" + age)

  //        age  ,      
  detail("heibaiying")
  detail("heibaiying", 12)

}

二、閉包
2.1閉パッケージの定義
var more = 10
// addMore       :           more            
val addMore = (x: Int) => x + more

上記の関数addMoreには、2つの変数xとmoreがあります.
  • x:この関数のパラメータであるため、関数のコンテキストに明確な定義があるバインド変数です.
  • more:フリー変数(free variable)です.関数の数値面量は、moreに意味を与えていないからです.

  • 定義に従って:関数を作成するときに自由変数をキャプチャする必要がある場合、キャプチャされた変数への参照を含む関数を閉パッケージ関数と呼びます.
    2.2自由変数の変更
    ここで、閉パケットは、変数自体、すなわち変数自体への参照を取得します.これは、次のことを意味します.
  • 閉パケット外部による自由変数の修正は、閉パケット内部で可視である.
  • 閉パケット内部の自由変数の修正は、閉パケット外部でも見られる.
  • //    more   
    scala> var more = 10
    more: Int = 10
    
    // more          ,          
    scala> val addMore = (x: Int) => {x + more}
    addMore: Int => Int = $$Lambda$1076/1844473121@876c4f0
    
    scala> addMore(10)
    res7: Int = 20
    
    //        more     ,        more   
    scala> more=1000
    more: Int = 1000
    
    scala> addMore(10)
    res8: Int = 1010
    

    2.3自由変数マルチコピー
    フリー変数はプログラムの変更に伴って変更され、複数のコピーが生成される可能性がありますが、閉パッケージは作成時に有効な変数のコピーを指します.
    //       more   
    scala> var more = 10
    more: Int = 10
    
    //       
    scala> val addMore10 = (x: Int) => {x + more}
    addMore10: Int => Int = $$Lambda$1077/1144251618@1bdaa13c
    
    //       
    scala> addMore10(9)
    res9: Int = 19
    
    //      more   
    scala> var more = 100
    more: Int = 100
    
    //         
    scala> val addMore100 = (x: Int) => {x + more}
    addMore100: Int => Int = $$Lambda$1078/626955849@4d0be2ac
    
    //          more   
    scala> addMore100(9)
    res10: Int = 109
    
    //             more   
    scala> addMore10(9)
    res11: Int = 19
    
    //        more    100
    scala> more
    res12: Int = 100
    

    上記の例から、再宣言more後、グローバルなmoreの値は100であるが、閉パケット関数addMore10については、10のmoreを参照するか、仮想マシンによって実現され、仮想機会保証more変数は、再宣言後も元の取得された変数のコピーがスタック上で生存し続けることを保証する.
    三、高次関数
    3.1パラメータとして関数を使用する
    関数を定義するときに入力関数をパラメータとしてサポートします.このとき、新しく定義した関数は高次関数と呼ばれます.
    object ScalaApp extends App {
    
      // 1.    
      def square = (x: Int) => {
        x * x
      }
    
      // 2.      :           Int => Int    
      def multi(fun: Int => Int, x: Int) = {
        fun(x) * 100
      }
    
      // 3.      
      println(multi(square, 5)) //    2500
        
      // 4.      
      println(multi(_ * 100, 5)) //    50000
    
    }
    

    3.2関数コリー化
    上で定義した関数は1つのパラメータリストのみをサポートし、コリー化関数は複数のパラメータリストをサポートします.コリー化とは,2つのパラメータを受け入れた関数を1つのパラメータを受け入れる関数に変える過程を指す.新しい関数は、元の2番目のパラメータをパラメータとします.
    object ScalaApp extends App {
      //        
      def curriedSum(x: Int)(y: Int) = x + y
      println(curriedSum(2)(3)) //   5
    }
    

    ここでcurriedSumを呼び出すと、実際には従来の関数呼び出しを2回連続して行い、実際に実行されたコリー化プロセスは以下の通りです.
  • 第1回目の呼び出しはxというInt型パラメータを受信し、第2回目の呼び出しに使用する関数を返し、xが2であると仮定すると、関数2+yを返す.
  • 返される関数は、パラメータyを受信し、値2+3の値を計算して返します.

  • コリー化の中間戻り関数を得るのも簡単です.
    object ScalaApp extends App {
      //        
      def curriedSum(x: Int)(y: Int) = x + y
      println(curriedSum(2)(3)) //   5
    
      //        10         10 + y
      val plus: Int => Int = curriedSum(10)_
      println(plus(3)) //    13
    }
    

    コリー化は複数のパラメータリストをサポートし、複数のパラメータは左から右の順にコリー化操作を実行します.
    object ScalaApp extends App {
      //        
      def curriedSum(x: Int)(y: Int)(z: String) = x + y + z
      println(curriedSum(2)(3)("name")) //    5name
      
    }
    

    参考資料
  • Martin Odersky . Scalaプログラミング(第3版)[M].电子工业出版社2018-1-1
  • ケイ.S.ホストマン速学Scala(第2版)[M].电子工业出版社2017-7

  • シリーズゲート