2

私は現在、Martin Odersky の Coursera クラス、Functional Programming Principles in Scala のビデオ講義に取り組んでいます。講義 2.1 では、基底関数 を使用した高階関数の構成を示していますsum()。彼は末尾再帰なしで階乗を実装しましたが、私は末尾再帰を使ってみました。これはまだ 1 行のコードだからです。その結果、Odersky ではなかったと思われる型の不一致が発生しました。の定義sum()では、パラメータfは を受け取り、Intを返しますInt。関数*を調整することでこれを回避できることはわかっていますが、これにより、一般的に高階関数を設計する方法について疑問が生じます。 柔軟な数の引数を取る関数を許可するために、この型定義を調整または回避する方法はありますか? 誰かが Haskell を少し見せてくれたことがあるのですが、Scala の関数パラメーターを同様に緩い方法で型付けできるかどうか疑問に思っています... あるいは、Scala によりネイティブな別の解決策があるかもしれません。私は昨日 Scala を使い始めたばかりで、コンピューター サイエンスの知識が限られていると仮定してください。まさにその通りです。

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


//Factorial with tail recursion.  The one in the lesson DOES NOT use tail recursion.
//prev is an accumulator.
def fact(a: Int, prev:Int = 1): Int =
        if (a == 0) prev else fact(a-1, a * prev)


def sumFactorials(a: Int, b: Int): Int = sum(fact, a, b)

fact()*次のように、現在の関数を別の関数内にネストすることで、これを修正できることを知っています。

def factorial(a: Int): Int ={
    def fact(n: Int, prev:Int = 1): Int =
            if (n == 0) prev else fact(n-1, n * prev)
    fact(a)
}

前述の Haskell での経験からの私の印象は、関数型プログラミングは、カリー化を可能にするために引数を 1 つだけ取る関数を促進するというものでした。とにかく、それは実際の質問とは少し関係がありますが、私の質問の精神で FP を恐ろしく解体しているだけなら、コメントで気軽にこれに対処してください。

4

1 に答える 1

2

ここでいくつかのことが起こっていると思います。

まず、ファクト関数を使用して何をしようとしているのか (その型が実際には 1 つ少ないパラメーターを持っているかのようにデフォルト値を持つ関数を使用する) は不可能だと思います。(Int,Int) => Int は、2 番目の Int のデフォルトが 1 であっても (Int,Int) => Int のままです。Int => Int として渡すことはできません。でも、あなたができると思うところはわかります。

第二に、事実関数と階乗関数は同等ではありません。factorial 関数は、再帰を何回行っても、'a' の元の値を何度も使用します。ファクト関数は、各呼び出しで減少する 'a' を使用します。

「固定」階乗関数に基づいて、関数は効果的に 3 つのパラメーターを取ります。元の a、減少する n、乗算する prev です。以下のコードは同等です。

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

def fact(a: Int, n: Int, prev:Int): Int = 
    if (n == 0) prev else fact(a, n-1, a * prev)

def sumFactorials(a: Int, b: Int): Int = sum(fact, a, b)

sum を次のように書くこともできません。

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

渡された関数 f にはデフォルトのパラメーターがある場合とない場合があるため、コンパイラーが f(a) 呼び出しを許可することは安全ではありません。

デフォルトのパラメーターは、関数のカリー化のようなものではなく、実際の関数に関連付けられていると彼らが言うように、「シンタックス シュガー」のように実装されていると思います。カリー化でこれを行うことができますが、カリー化された関数を明示的に宣言する必要があります。(階乗ではなく) 事実からのロジックを使用すると仮定すると、次のように実行できます。

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

def fact(prev:Int = 1)(a: Int): Int =
        if (a == 0) prev else fact(a * prev)(a-1)

def sumFactorials(a: Int, b: Int): Int = sum(fact(1), a, b)

ファクトのパラメータの位置を切り替える必要があることに注意してください。

于 2013-11-04T17:25:17.177 に答える