12

私は最近、ハスケルを学びました。できるだけ、純粋な関数型を他のコードに渡そうとしています。これの重要な側面は、すべての変数を不変の、すなわち定数として扱うことである。これを行うために、命令型のループを使用して実装される多くの計算は、再帰を使用して実行する必要があります。再帰は通常、関数呼び出しごとに新しいスタックフレームが割り当てられるためメモリペナルティが発生します。しかし、テールコール(コールされた関数の戻り値がすぐに呼び出し先の呼び出し元に返される)の特殊なケースでは、このペナルティは、テールコール最適化と呼ばれるプロセスによってバイパスされます(1つの方法では、スタックを適切に設定した後では、基本的にコールをjmpに置き換えます)。 MATLABはデフォルトでTCOを実行しますか、それともそれを伝える方法はありますか?MATLABはテールコールの最適化を実行しますか?

+0

は良くありませんアイディア。与えられた問題に適切なものを使用してください(もちろん実行可能です - 反復は純粋に関数型言語では実用的ではありません)。 – delnan

+0

適切なテールコール最適化を使用すると、「反復の回避」は、ソリューションのパフォーマンスではなく、問題の考え方になります。 MATLABがTCOを提供しない場合は、必要に応じて繰り返しを使用しますが、そうであれば、すべてのコードで一貫したパラダイムを使用できるようになります。 –

+1

私はparticukarでMATLABを知らない、私は一般的に話している。あなたがPythonをコーディングしていて、PythonにTCOがあったとしても、それは慣用ではないので、繰り返しループを使ってはいけません。言語とstdlibはイテレータなどを最大限に活用することに集中しています。パラダイムには弱点があるので、特定の問題を最もよく解決するものを使用してください(「ベスト」の定義はもちろん、残りの部分と一緒にうまくいく方法も含みます)。 – delnan

答えて

10

私は、単純な末尾再帰関数で定義する場合:

function tailtest(n) 
    if n==0; feature memstats; return; end 
    tailtest(n-1); 
end 

、それはかなり深く再帰するようにそれを呼び出す:

set(0,'RecursionLimit',10000); 
tailtest(1000); 

をスタックフレームがあるかのように、それは見ていませんたくさんの記憶を食べる。私はそれがはるかに深い再帰にする場合は、:

set(0,'RecursionLimit',10000); 
tailtest(5000); 

その後、(私のマシンで、今日)MATLABは、単にクラッシュ:プロセスがあっさり死にます。

これは、TCOを実行しているMATLABとは一致しません。関数が1つの引数だけではなくローカル変数を持たずに1つの場所でしか関数を呼び出せない場合は、誰もが望むほど簡単です。

So:いいえ、少なくともデフォルトでは、MATLABはTCOをまったく実行していないようです。私は(これまで)それを可能にするオプションを探していませんでした。もしあれば、私は驚くだろう。

スタックを吹き飛ばさない場合、再帰コストはいくらですか? Bill Cheathamの答えに私のコメントを見てください:それは時間のオーバーヘッドが些細ではないが、狂気ではないように見えます。

...私がそのコメントを残した後、Bill Cheathamが彼の答えを削除したことを除いて。 OK。だから、私はフィボナッチ関数と単純なテール再帰関数の単純な反復実装を行い、両方で本質的に同じ計算を行い、両方ともfib(60)で時間を計った。再帰的な実装は、反復的な実装よりも実行に約2.5倍の時間がかかりました。もちろん、相対的なオーバーヘッドは、1回の加算と1回の減算に比べて多くの作業を行う関数では小さくなります。

(私もdelnanの感情に同意:Haskellで自然に感じる一種の高度に再帰コードをMATLABでunidiomaticことが一般的にそうである)ちょうどそれを一体のための反復を回避

+3

スタックフレームのワークスペースからローカル変数をクリーンアップするとonCleanupで副作用が発生する可能性があるため、TCOはMatlabでは難しい場合があります()機能です。 MatlabはGCed Javaスタイルではありません。参照カウントなどを使用して決定論的です。 RAIIをサポートします。スタックフレームのエリートが安全かどうかを判断するには、テールコールで渡されなかったすべてのローカル変数を深く検索して、onCleanupを保持しているかどうかを確認する必要があります。高価なテスト。また、assignin(...、 'caller')またはevalin(...、 'caller')が呼び出される場合、少なくとも1つの親スタックフレームが保持される必要があります。合意した。ユニダイマティック。 –

関連する問題