2016-04-02 21 views
1

パラメータ:再帰関数は、私は、単純な再帰関数を作って、それが動作するように期待(しかし、それはしません)

open System 
open System.Threading 

let f = 
    let r = Random() 
    let rec d = 
     printfn "%d" (r.Next()) 
     Thread.Sleep(1000) 
     d 
    d 
f 

私は、次の作業機能(になってしまったが、理解なしにインテリセンスの助けを借りてなぜ)機能が動作しませんでした前回:

open System 
open System.Threading 

let f : unit = 
    let r = Random() 
    let rec d() = 
     printfn "%d" (r.Next()) 
     Thread.Sleep(1000) 
     d() 
    d() 
f 

は、なぜ私が明示的にunit()を述べる必要がありますか?

答えて

3

最初のバージョンでは、再帰オブジェクト(let rec d)または値が宣言されています。 dオブジェクトは再帰的ですが、オブジェクトはどのように再帰的になる可能性がありますか?それはどのようにそれを呼びますか?もちろん、これは意味をなさない。

F#で再帰オブジェクトを使用することはできません。これが、最初のバージョンが機能しない理由です。

2番目のバージョンでは、再帰関数(let rec d())を宣言しました。 ()を追加すると、dが関数であることが明示的に示されます。 unitで明示的に、関数f(ただ1回だけ)は何も返しません。少なくとも、fは特定の型ではない値を返します。 F#では、最も単純な関数であっても常に値を返す必要があります。

あなたの場合、F#はfが返すタイプを推測しようとします。特定の型の注釈がなく、fが特定の型を使用して特定の値を返す(計算のような)ものでない場合、F#コンパイラは汎用戻り型をfに割り当てますが、コードはあいまいであり、より具体的にはunit型(F#関数が返すことができる最も単純な型)を指定します。

実際、値制限エラーはF#の強力な型推論に関係しています。このエラーについてはthis interesting articleをご覧ください。

2

最初の試みでは、関数ではなく、を定義します。値dはそれ自体で定義されています。つまり、dが何であるかを知るためには、まずdが何であるかを知る必要があります。それがうまくいかないことは間違いありません!

これはもう少し明確にするために、私はあなたの定義は、このように同じ種類のものであることを指摘します:

let x = x 

あなたは、これが動作することを期待しますか?

dのパラメータをとしました。それは関数であり、値ではないパラメータです。比較:

let rec x() = x() 

実行時にスタックオーバーフローが発生しますが、少なくともコンパイルされます。これは、無条件に自分自身を呼び出す関数です。

具体的にはunitパラメータを指定する必要はありませんでした。あなたはそれを数字、文字列、またはジェネリック型にすることもできました。あなたが気にしないときは、unitが最も簡単なオプションです。

実際にはタイプにfの注釈を付ける必要はありませんでした。それは無関係なステップでした。

結論として、私は、第2のコードブロックであっても、fはまだの値であることを指摘したいと思います。機能ではなく、です。実際には、fのコードは、fが定義されているときに一度だけ実行され、他の式の一部としてfと言われるたびに実行されるわけではありません。

+0

'実際にタイプにfを注釈する必要はありませんでした。これは無関係なステップでした」しかし、この無関係なステップ(他のすべてが正しい場合)がなければ、それはまだエラー:値の制限をスローします。値 'f'は総称型val fを持つと推測されています: '_a>' f 'を単純なデータ項として定義するか、明示的な引数を持つ関数にするか、または一般的なものではない場合は、型注釈 – Bad

+0

あなたはそうです、私は価値の制限を忘れました。しかし 'f'を関数にすると、その問題はとにかく消えてしまいます。 –

+0

'f()'を呼び出してそれを呼び出すと(それ以外はすべて正しい)、この 'Value restriction'例外がスローされます。したがって、 'unit'を明示的に記述することが唯一の方法です。しかし、まだ答えに感謝、彼らはすべて本当に有用です。 – Bad

関連する問題