Scala関数式プログラミングプロセスを深く理解する
Scala関数式プログラミングプロセスを深く理解する
私たちはすぐに変態の過程を始めます。
立方和が必要なら、こうしてもいいです。
これらの関数名を異なっていますが、処理ロジックの同じスラグを一つの関数にカプセル化します。そしてこの関数はパラメータとして高次関数に値を付けます。運転の結果は入ってきたパラメータタイプだけと関係があります。つまり、cube、square、fact、一般化は一つのfになります。
私達はもう一回脳の穴を開けます。sumが戻ってきたのは関数ですから、直接これらの関数を使ってもいいです。もう一回の呼び出し命令を書く必要がありません。sumCube(1、10)類の文はいらないです。
読者は、元のsum関数をこのような形に転化させると、メリットはどこにありますか?答えは私たちがより多くの可能性を得ました。例えば、最初の和を求める上下限はまだ確定していません。プログラムの中で一つの関数をsumに送ることができます。sum(fact)は完全に合法的な表現です。後の上下限が決まったら、他の二つのパラメータを伝えます。sum関数については、さらにa,bパラメータを再変換して、sum関数は、パラメータを一つしか受信できない関数になります。その後、もう一つのパラメータを受信する関数を返します。呼び出した後、もう一つのパラメータだけを受信する関数を返します。これは伝説のカリー化で、どんなに完璧な形ですか?実際の世界では、確かにこのような関数式プログラミング言語があります。それはHaskellです。Haskellでは、すべての関数はカリー化されています。つまり、すべての関数はパラメータだけを受信します。
私たちはすぐに変態の過程を始めます。
立方和が必要なら、こうしてもいいです。
35 * 35 * 35
68 * 68 * 68
問題ないです。抽象的に、関数を書きます。
def cube(n: Int) = n * n * n
cube(35)
cube(68)
手間が省けます。1から10までの立方和を求めるなら、再帰と書いてください。
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をいっぱい書いていますが、おかしくないですか?このような脳のない操作はもちろん避けなければなりません。これらの関数名を異なっていますが、処理ロジックの同じスラグを一つの関数にカプセル化します。そしてこの関数はパラメータとして高次関数に値を付けます。運転の結果は入ってきたパラメータタイプだけと関係があります。つまり、cube、square、fact、一般化は一つの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で匿名関数を定義するシンタックスは簡単であり、矢印の左側はパラメータリスト、右側は関数体、パラメータのタイプは省略可能であり、Scalのタイプ推定システムはパラメータのタイプを推測する。匿名関数を使うと、コードがより簡潔になります。
//
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)
ここに書いてある問題の解決はほぼ同じですが、私達はよく考えてみます。関数式プログラミングの真の意味は、一つはもう一つの出力に入力します。このような二つのパラメータが伝わってくるのではなく、面倒くさいように見えます。
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)は完全に合法的な表現です。後の上下限が決まったら、他の二つのパラメータを伝えます。sum関数については、さらにa,bパラメータを再変換して、sum関数は、パラメータを一つしか受信できない関数になります。その後、もう一つのパラメータを受信する関数を返します。呼び出した後、もう一つのパラメータだけを受信する関数を返します。これは伝説のカリー化で、どんなに完璧な形ですか?実際の世界では、確かにこのような関数式プログラミング言語があります。それはHaskellです。Haskellでは、すべての関数はカリー化されています。つまり、すべての関数はパラメータだけを受信します。
// 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)
疑問があれば、メッセージをお願いします。あるいは、当駅のコミュニティで交流して討論してください。ありがとうございます。