web-dev-qa-db-ja.com

どのように修正を使用し、どのように機能しますか?

私はfixのドキュメントに少し混乱していました(ただし、今何をすべきかは理解していると思います)ので、ソースコードを調べました。それは私をより混乱させました:

fix :: (a -> a) -> a
fix f = let x = f x in x

これはどの程度正確に固定小数点を返しますか?

コマンドラインで試してみることにしました。

Prelude Data.Function> fix id
...

そして、そこにぶら下がっています。公平を期すために、これは少し遅い私の古いMacbookにあります。ただし、この関数をtooにすることはできません。idに渡されるものは同じものを返すためです(CPU時間を消費しないことは言うまでもありません)。何が悪いのですか?

83
Jason Baker

あなたは何も悪いことをしていません。 _fix id_は無限ループです。

fixが関数の最小固定小数点を返すと言うとき、それは ドメイン理論 の意味での意味です。したがって、fix (\x -> 2*x-1)notが_1_を返すことになります。これは、_1_はその関数の固定小数点ですが、 least1つはドメインの順序で。

ドメインの順序を1段落または2段落で説明することはできないので、上記のドメイン理論のリンクを参照します。これは優れたチュートリアルであり、読みやすく、非常にわかりやすくなっています。私はそれを強くお勧めします。

10,000フィートからのビューの場合、fixrecursionの概念をエンコードする高次関数です。式がある場合:

_let x = 1:x in x
_

結果は無限リスト_[1,1..]_になりますが、fixを使用して同じことを言うことができます。

_fix (\x -> 1:x)
_

(または単にfix (1:))は、_(1:)_関数の固定小数点を見つけてくれます。IOW値xこのような_x = 1:x_...のように上記で定義。定義からわかるように、fixはこのアイデアにすぎません。再帰は関数にカプセル化されます。

これは、再帰の本当に一般的な概念でもあります-この方法で任意の再帰関数を記述できます 多相再帰を使用する関数を含む 。たとえば、典型的なフィボナッチ関数は次のとおりです。

_fib n = if n < 2 then n else fib (n-1) + fib (n-2)
_

このようにfixを使用して書くことができます:

_fib = fix (\f -> \n -> if n < 2 then n else f (n-1) + f (n-2))
_

演習:fixの定義を展開して、fibのこれら2つの定義が同等であることを示します。

しかし、完全な理解のために、ドメイン理論について読んでください。それは本当にクールなものです。

84
luqui

私はこれを完全に理解しているとは主張していませんが、これが誰かを助ける場合は...

fixの定義を検討してください。 _fix f = let x = f x in x_。気が遠くなる部分は、xが_f x_として定義されていることです。しかし、少し考えてみてください。

_x = f x
_

X = f xなので、その右側のxの値を代入できますよね?だから….

_x = f . f $ x -- or x = f (f x)
x = f . f . f $ x -- or x = f (f (f x))
x = f . f . f . f . f . f . f . f . f . f . f $ x -- etc.
_

したがって、トリックは、終了するためにfが何らかの構造を生成する必要があるため、後のfが実際に完全な状態を気にせずに、その構造にパターンマッチングして再帰を終了できるようにする必要があります。パラメータの「値」(?)

もちろん、luquiが示すように、無限リストを作成するようなことをしたくない場合を除きます。

TomMDの階乗の説明は良いです。 Fixの型シグネチャは_(a -> a) -> a_です。 _(\recurse d -> if d > 0 then d * (recurse (d-1)) else 1)_の型シグネチャは_(b -> b) -> b -> b_、つまり_(b -> b) -> (b -> b)_です。つまり、a = (b -> b)と言えます。このようにして、fixは_a -> a_、または実際には_(b -> b) -> (b -> b)_という関数を受け取り、a型の結果、つまり_b -> b_を返します。つまり、別の機能です!

ちょっと待って、関数ではなく固定小数点を返すことになっていると思いました。まあ、そうですね(関数はデータなので)。あなたが階乗を見つけるための決定的な機能を私たちに与えたと想像することができます。再帰の方法がわからない関数を与えたため(そのパラメーターの1つが再帰に使用される関数です)、fixに再帰の方法を教えました。

後のfがパターンマッチングして終了できるように、fは何らかの構造を生成する必要があると私が言ったことを覚えていますか?まあそれは正確ではないでしょうね。 TomMDは、xを展開して関数を適用し、基本ケースに移行する方法を示しました。彼の機能では、彼はif/thenを使用し、それが終了の原因です。置換を繰り返した後、inの定義全体のfix部分は、最終的にxに関して定義されなくなり、それが計算可能で完全な状態になります。

23
Dan Burton

フィックスポイントを終了する方法が必要です。あなたの例を拡大すると、それが終了しないのは明らかです:

fix id
--> let x = id x in x
--> id x
--> id (id x)
--> id (id (id x))
--> ...

これは私が修正を使用する実際の例です(私は修正を頻繁に使用せず、おそらく疲れていた/これを書いたときに読み取り可能なコードについて心配していなかったことに注意してください):

(fix (\f h -> if (pred h) then f (mutate h) else h)) q

WTF、あなたは言う!ええ、はい、しかし、ここにはいくつかの本当に役立つ点があります。まず最初に、最初のfix引数は通常、「再帰」ケースである関数である必要があり、2番目の引数は処理対象のデータです。名前付き関数と同じコードを次に示します。

getQ h
      | pred h = getQ (mutate h)
      | otherwise = h

あなたがまだ混乱しているなら、おそらく階乗はもっと簡単な例でしょう:

fix (\recurse d -> if d > 0 then d * (recurse (d-1)) else 1) 5 -->* 120

評価に注意してください:

fix (\recurse d -> if d > 0 then d * (recurse (d-1)) else 1) 3 -->
let x = (\recurse d -> if d > 0 then d * (recurse (d-1)) else 1) x in x 3 -->
let x = ... in (\recurse d -> if d > 0 then d * (recurse (d-1)) else 1) x 3 -->
let x = ... in (\d -> if d > 0 then d * (x (d-1)) else 1) 3

ああ、見ただけですか?そのxは、thenブランチ内の関数になりました。

let x = ... in if 3 > 0 then 3 * (x (3 - 1)) else 1) -->
let x = ... in 3 * x 2 -->
let x = ... in 3 * (\recurse d -> if d > 0 then d * (recurse (d-1)) else 1) x 2 -->

上記では、x = f xを覚えておく必要があるため、x 2だけではなく、末尾に2の2つの引数があります。

let x = ... in 3 * (\d -> if d > 0 then d * (x (d-1)) else 1) 2 -->

そして、ここでやめます!

16
10
VlatkoB

私がそれを理解する方法は、それはあなたがそれを与える同じものを出力するように、それは関数の値を見つけます。問題は、常に未定義(または無限ループ、haskellでは、未定義ループと無限ループは同じ)または最も未定義のものが何でも選択されることです。たとえば、idでは、

λ <*Main Data.Function>: id undefined
*** Exception: Prelude.undefined

ご覧のとおり、undefinedは固定小数点であるため、fixがそれを選択します。代わりに(\ x-> 1:x)を実行した場合。

λ <*Main Data.Function>: undefined
*** Exception: Prelude.undefined
λ <*Main Data.Function>: (\x->1:x) undefined
[1*** Exception: Prelude.undefined

したがって、fixは未定義を選択できません。無限ループにもう少し接続するため。

λ <*Main Data.Function>: let y=y in y
^CInterrupted.
λ <*Main Data.Function>: (\x->1:x) (let y=y in y)
[1^CInterrupted.

繰り返しになりますが、若干の違いがあります。では、固定点は何ですか? repeat 1

λ <*Main Data.Function>: repeat 1
[1,1,1,1,1,1, and so on
λ <*Main Data.Function>: (\x->1:x) $ repeat 1
[1,1,1,1,1,1, and so on

同じです!これが唯一の固定小数点であるため、fixはそれで解決する必要があります。申し訳ありませんfix、無限ループがないか、未定義です。

2
PyRulez