長話短說,我正在觀看Simon Peyton-Jones 的演講,當時21:41他參考了一段話:
我正在處理一個錯誤,很沮喪,然后在 ghci 中輸入了“修復錯誤”……
我試過了。
結果:
λ> import Data.Function -- here is fix
λ> fix error
"*** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: *** Exception: and goes on like this towards infinity
一開始我只是想這到底是fix做什么的?
所以我看了一些型別
λ> :t error
error :: [Char] -> a
λ> :t fix
fix :: (a -> a) -> a
因此
λ> :t fix error
fix error :: [Char]
但顯然這仍然沒有告訴我太多關于結果的資訊。
然而,更奇怪的是, even take 10 $ fix errororlength $ take 10 $ fix error是一個像上面那樣永無止境的輸出(除了后者的輸出,length …,缺少初始的")。
我在看什么?
需要明確的是,目前我對 hackage 的檔案還不太了解。并不是說我花了超過 3 分鐘的時間,但我仍然迷失在它的第一行。
uj5u.com熱心網友回復:
fix計算函式的不動點;定點是您可以提供給函式的值,它將產生與結果完全相同的值。
例如,如果你有函式f _ = "hello"(或const "hello"),那么這個函式的一個不動點就是字串"hello"。確實fix f是"hello"。
許多函式有多個固定點,因此檔案fix需要指定它回傳哪一個。例如:
g :: Integer -> Integer
g x
| even x = x
| otherwise = x 1
每個偶數都是 的一個不動點g,但fix g承諾(按其型別)是一個特定的Integer。哪一個?
檔案說fix產生最小不動點,并進一步闡明這意味著最小定義的值是輸入函式的不動點。請注意,“最少定義”并不是指定義的最小值,而是指具有最小“定義”的值。這是一個技術領域,我并不像我想成為的那樣,在非正式意義上:像1 :: Integer, True,()等這樣的值Just 'a'是完全定義的,因為你可以完全評估它們而不會出錯。底部值(undefined、let x = x in x等)完全未定義。中間是類似的值1 : 2 : undefined,您可以在其中查看某些結構而不會遇到錯誤,但內部某處有一個底部。
底部也是如此fix g(當我嘗試時,GHC 檢測到無限回圈并中止它),因為這g undefined是一個錯誤(并且所有底部都是“相同的值”為此目的)。
事實證明,對于您在使用fix. 對于任何嚴格的函式(以任何方式檢查其引數的函式),底部將是一個固定點,這就是fix要計算的點。那么我們為什么要關心它呢?
fix具有很大的理論意義,因為您可以使用它在缺乏直接支持的語言中實作遞回。在這樣的遞回定義中:
sum :: [Integer] -> Integer
sum [] = 0
sum (x : xs) = x sum xs
實際上發生了一些令人印象深刻的事情。您正在定義sum,但sum在其自己的定義范圍內。實作該功能比編譯僅使用預先存在的定義的定義要困難一些。假設我們做不到,但我們仍然想寫sum. 你可以這樣做:
sum' :: ([Integer] -> Integer) -> [Integer] -> Integer
sum' _ [] = 0
sum' rec (x : xs) = x rec xs
sum = fix sum'
現在每個定義都只使用以前定義過的東西;沒有自我參考。而不是直接呼叫自身sum'接收一個額外的引數,這是它應該在串列尾部呼叫的函式。該函式引數必須具有我們最初想要給出的相同型別sum,這使得我們可以呼叫它sum'的實體的型別。事實證明, 的最小不動點是等價于原來的函式!a -> afixsum'sum
它這樣做的方式是通過生成表單的“無限嵌套”運算式sum' (sum' (sum' (sum' ...)))(這基本上是它找到任何函式的不動點的方式;答案已經很長了,所以我不會詳細說明為什么它確實有效這里)。每個都sum'收到一個引數,說明如何處理串列的尾部;該引數本身是另一個呼叫sum',需要一個引數說明如何處理原始串列尾部的尾部,并且該引數是另一個呼叫sum',依此類推。最終(如果串列是有限的)我們達到了空串列的基本情況并且不需要下一級嵌套sum'呼叫,因此嵌套運算式沒有盡頭并不重要!(這顯然不適用于熱切評估的語言)
事實證明,這是一種通用模式,您可以應用將直接遞回轉換為fix.
綜上所述,希望你能明白為什么fix error會這樣。Willem Van Onsem 的回答在這里很好,所以我不會重復。但基本上fix error必須想出s一個error s相當于s. 這對于任何非底部字串當然是不可能的,因為error總是產生底部(這就是它的全部意義),所以fix error必須是某種形式的底部。在搜索固定點時,它會生成一個無限嵌套的運算式error (error (error ...)),并且當 GHC 列印一條錯誤訊息時,該訊息本身會生成一個錯誤,其訊息是另一個錯誤等等,您所看到的是產生的輸出。
uj5u.com熱心網友回復:
它產生error (error (error (error …))). 既然error有型別[Char] -> a,我們就知道這里a ~ [Char],所以error (error (error (error …)))就會有型別[Char]。一旦我們對此進行評估,它就會引發錯誤,因此它會列印*** Exception: 然后旨在列印訊息,但這也是一個,error所以它會列印另一個*** Exception: 等等。
為什么也
take 10 $ fix error一樣呢?
因為當它打算獲取串列的前 10 個元素時也會出錯,因為評估串列會引發錯誤,并且在列印例外時,將開始相同的行為。
因此它永遠不會生成Chars 串列:它引發錯誤并開始列印錯誤訊息,而不是回傳它。
uj5u.com熱心網友回復:
一種簡單的思考方式是,您可以使用它來將嵌套運算式本身作為引數來使其遞回。例如,在這里我使用它來制作一個允許數字括號嵌套的決議器:
ghci> :m Data.Function Text.Parsec
ghci> parseTest (fix $ \ nestedDigit -> digit <|> char '(' *> nestedDigit <* char ')') "((3))"
'3'
您可以將其視為常規遞回系結的替代方案:
ghci> let
ghci| nestedDigit :: Parsec String () Char
ghci| nestedDigit = digit <|> char '(' *> nestedDigit <* char ')'
ghci|
ghci> parseTest nestedDigit "((3))"
'3'
如果您想將運算式保留在它使用的地方,因為它變得遞回,它有助于避免撰寫 pattern (let foo = ... foo ... in foo)。
正如 Ben 的回答所顯示的那樣,它還有更多用途,但在實踐中,除此之外,我還沒有找到另一個好的用途。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/469978.html
下一篇:Java中的陣列搜索例外
