Scala関数式プログラミングプロセスを深く理解する

5789 ワード

Scala関数式プログラミングプロセスを深く理解する
私たちはすぐに変態の過程を始めます
キューブ和が必要なら、そうすることができます.

35 * 35 * 35 
68 * 68 * 68 

問題ありません.抽象的に関数を書きます.

def cube(n: Int) = n * n * n 
cube(35) 
cube(68)

手間が省けて、1から10の立方和を求めれば、OK、再帰を書きます

def cube(n: Int) = n * n * n 
 def sumCube(a: Int, b: Int): Int = 
   if (a > b) 0 else cube(a) + sumCube(a + 1, b) 
 
sumCube(1, 10)

少し変態して、立方和、平方和、階乗和、依然としてそれらの関数を書き出してそして順次計算して欠点がありません

def cube(n: Int) = n * n * n 
def id(n: Int) = n 
def square(n : Int) = n * n 
def fact(n: Int): Int = 
  if (n == 0) 1 else n * fact(n - 1) 
 
def sumCube(a: Int, b: Int): Int = 
  if (a > b) 0 else cube(a) + sumCube(a + 1, b) 
 
def sumSquare(a: Int, b: Int): Int = 
  if(a > b) 0 else square(a) + sumSquare(a + 1, b) 
  
def sumFact(a: Int, b: Int): Int = 
  if (a > b) 0 else fact(a) + sumFact(a + 1, b) 
 
def sumInt(a: Int, b: Int): Int = 
  if(a > b) 0 else id(a) + sumInt(a + 1, b)  
 
 sumCube(1, 10) 
 sumInt(1, 10) 
 sumSquare(1, 10) 
 sumFact(1, 10)

そして、同じ論理のif elseを書いたことに気づきました.おかしくないように見えますか.このような無脳な操作はもちろん避けなければなりません.
これらの関数名は異なるが処理論理が同じスラグを1つの関数にカプセル化し,この関数をパラメータとして高次関数に付与し,実行結果は伝達されたパラメータタイプにのみ関係し,すなわちcube,square,factを1つのfに汎化する.

def cube(n: Int) = n * n * n 
def id(n: Int) = n 
def square(n : Int) = n * n 
def fact(n: Int): Int = 
  if (n == 0) 1 else n * fact(n - 1) 
//    
def sum(f: Int=>Int, a:Int, b:Int): Int = 
  if(a>b) 0 else f(a)+sum(f, a+1, b)
//               
def sumCube(a: Int, b: Int): Int = sum(cube, a, b) 
def sumSquare(a: Int, b: Int): Int = sum(square, a, b) 
def sumFact(a: Int, b: Int): Int = sum(fact, a, b) 
def sumInt(a: Int, b: Int): Int = sum(id, a, b) 
 
 sumCube(1, 10) 
 sumInt(1, 10) 
 sumSquare(1, 10) 
 sumFact(1, 10)

しかし、このように書くと、前にcube、idの初期定義を定義し、後に定義を続けるという問題があります.実際には包装されています.いいえ、削除して、匿名関数の機能を使って呼び出しをさらに簡略化します.多くの場合,パラメータとして伝達される関数ではなく高次関数に関心を持つため,関数を単独で定義する必要はない.称賛に値するのはScalaの中で匿名の関数の文法を定義するのはとても簡単で、矢印の左側はパラメータのリストで、右側は関数体で、パラメータのタイプは省略することができて、Scalaのタイプはシステムを推測してパラメータのタイプを推測します.匿名関数を使用すると、コードがより簡潔になります.

//           
def fact(n: Int): Int = 
if (n == 0) 1 else n * fact(n - 1) 
 
def sum(f: Int => Int, a: Int, b: Int): Int = 
  if (a > b) 0 else f(a) + sum(f, a + 1, b) 
 
//               
def sumCube(a: Int, b: Int): Int = sum(x => x * x * x, a, b) 
def sumSquare(a: Int, b: Int): Int = sum(x => x * x, a, b) 
def sumFact(a: Int, b: Int): Int = sum(fact, a, b) 
def sumInt(a: Int, b: Int): Int = sum(x => x, a, b) 
 

sumCube(1, 10) 
sumInt(1, 10) 
sumSquare(1, 10) 
sumFact(1, 10)


ここまで書いて問題の解決の差は多くありませんが、よく考えてみると、関数式プログラミングの真の意味は、このような2つのパラメータが伝わるのではなく、別の出力に入力され、面倒に見えます.

def fact(n: Int): Int = 
if (n == 0) 1 else n * fact(n - 1) 
 
//     
def sum(f: Int => Int): (Int, Int) => Int = { 
  def sumF(a: Int, b: Int): Int = 
   if (a > b) 0 else f(a) + sumF(a + 1, b) 
 
  sumF 
} 
//               
def sumCube: Int = sum(x => x * x * x) 
def sumSquare: Int = sum(x => x * x) 
def sumFact: Int = sum(fact) 
def sumInt: Int = sum(x => x) 
 
//                ! 
sumCube(1, 10) 
sumInt(1, 10) 
sumSquare(1, 10) 
sumFact(1, 10)

実際にこの時sumの中に入ってきたのは匿名の関数で、g(f(x))の中のf(x)に似ていて、脳補演算ではなくそのf(x)を呼び出す必要があります.
もう一度脳の穴を開けてみましょう.sumが関数を返している以上、これらの関数を直接使用することができます.呼び出しコマンドを繰り返し書く必要はありません.sumCube(1,10)クラスの文は省くことができます.

def fact(n: Int): Int = 
  if (n == 0) 1 else n * fact(n - 1) 
 
//     
def sum(f: Int => Int): (Int, Int) => Int = { 
  def sumF(a: Int, b: Int): Int = 
   if (a > b) 0 else f(a) + sumF(a + 1, b) 
  sumF 
}

//          ! 
sum(x => x * x * x) (1, 10) //=> sumCube(1, 10) 
sum(x => x) (1, 10)      //=> sumInt(1, 10) 
sum(x => x * x) (1, 10)   //=> sumSquare(1, 10) 
sum(fact) (1, 10)       //=> sumFact(1, 10)


最後に、このコードをさらに最適化するために、高次関数の構文糖を使用することもできます.

//         sum   
 def sum(f: Int => Int): (Int, Int): Int = { 
 def sumF(a: Int, b: Int): Int = 
  if (a > b) 0 else f(a) + sumF(a + 1, b) 
 
 sumF 
} 
//         sum   
 def sum(f: Int => Int)(a: Int, b: Int): Int = 
 if (a > b) 0 else f(a) + sum(f)(a + 1, b)

私はかえって文法糖を使うほうが理解しやすく、私たちが学んだ数学言語に傾いていると思います.
読者は「元のsum関数をこのような形式に変換すると、メリットはどこにあるのか」と聞くかもしれません.答えは私たちがもっと多くの可能性を得たことです.例えば、和を求め始めたばかりの上下限はまだ確定していません.私たちはプログラムの中で関数をsumに伝えることができます.sum(fact)は完全に合法的な表現で、後続の上下限が確定したら、他の2つのパラメータを伝えることができます.sum関数については、a,bパラメータをさらに変換することで、sum関数は1つのパラメータしか受信できないたびに、別のパラメータを受信する関数を返し、呼び出した後、1つのパラメータだけを受信する関数を返す関数になります.これが伝説のコリー化で、どんなに完璧な形式だろう.現実の世界では、確かにこのような関数式プログラミング言語があります.それはHaskellです.Haskellでは、すべての関数はコリー化されています.つまり、すべての関数は1つのパラメータしか受信しません.

//       sum   
 def sum(f: Int => Int)(a: Int) (b: Int): Int = 
if (a > b) 0 else f(a) + sum(f)(a + 1)(b) 
 
//             ! 
 sum(x => x * x * x)(1)(10) //=> sumCube(1, 10) 
 sum(x => x)(1)(10)      //=> sumInt(1, 10)
 


もし疑問があれば伝言を残してあるいは当駅のコミュニティに行って討論を交流して、読書に感謝してみんなを助けることができることを望んで、みんなの当駅に対する支持に感謝します!