Haskellぞのずげを通しお翻蚳。 2/2



TRANSLATIONの2番目の郚分は、Haskellの短くお難しい玹介です。 最初のものはここから入手できたす。

オリゞナルはこちら



くそハヌド郚分



おめでずうございたす あなたは本圓に遠くに行きたした。
そしお今、ゎミ、廃棄物、゜ドミヌが始たりたす:)。

あなたず私が䌌おいるなら、あなたはすでに機胜的なスタむルに少し動いおいたす。 あなたは怠lazがデフォルトで䞎える利点を理解しおいたす。 しかし、あなたは本圓にあなたが本圓に有甚なプログラムを曞くこずができる方法をただよく理解しおいたせん。 特に



準備をしおください、答えは最も単玔ではないかもしれたせん。
しかし、その利点は吊定できたせん。



03_地獄/ 01_IO / 01_progressive_io_example.lhs


IOを扱う



tl; dr 

IOで動䜜する兞型的な関数は、呜什型プログラムのように芋えたす。

 f :: IO a f = do x <- action1 action2 x y <- action3 action4 xy 

  • オブゞェクトに倀を割り圓おるには、 <-を䜿甚したす。
  • この䟋では、各行の匏のタむプはIO *です。
    • action1 :: IO b
    • action2 x :: IO ()
    • action3 :: IO c
    • action4 xy :: IO a
    • x :: b 、 y :: c

  • いく぀かのオブゞェクトはIO a型です。
    このようなオブゞェクトでは、玔粋な関数を䜿甚できたせん。
    玔粋な関数を䜿甚するには、 action2 (purefunction x)を実行する必芁がありたす。



このセクションでは、IOの䜿甚方法を瀺したすが、動䜜方法は瀺したせん。
Haskellがプログラムのクリヌンな郚分ず副䜜甚のある郚分をどのように分離するかがわかりたす。

構文の詳现が少し理解できない堎合でも、停止しないでください。
次のセクションでそれらに戻りたす。

䜕を取埗したいですか

ナヌザヌから番号のリストを取埗したす。 金額を印刷する


 toList :: String -> [Integer] toList input = read ("[" ++ input ++ "]") main = do putStrLn "   ( ):" input <- getLine print $ sum (toList input) 


このプログラムの動䜜は明らかです。
しかし、型を詳しく芋おみたしょう。
 putStrLn :: String -> IO () getLine :: IO String print :: Show a => a -> IO () 


doブロック内のすべおの匏がIO a型であるこずに気づきたしIO aか

 main = do putStrLn " ... " :: IO () getLine :: IO String print Something :: IO () 


<-文字の動䜜にも泚意しおください。

 do x <- something 


something :: IO a堎合、 x :: a 。

IO䜿甚に関する重芁な泚意。 doブロック内のすべおの行は、次の2぀の方法のいずれかで芋る必芁がありたす。

 action1 :: IO a --      a = () 

たたは
 value <- action2 --  -- bar zt :: IO b -- value :: b 


これらの2぀の゚ントリは、アクションを蚘述するさたざたな方法に察応しおいたす。 次のセクションの終わりたでに、この提案に完党に気付くでしょう。

03_地獄/ 01_IO / 01_progressive_io_example.lhs



03_地獄/ 01_IO / 02_progressive_io_example.lhs

このプログラムの動䜜を芋おみたしょう、たずえば、ナヌザヌが䜕か奇劙なものを入力した堎合はどうなりたすか
私達は詊みたす

  % runghc 02_progressive_io_example.lhs Enter a list of numbers (separated by comma): foo Prelude.read: no parse 


ああ 悪魔のような゚ラヌメッセヌゞずプログラムのクラッシュ
次に、゚ラヌメッセヌゞが読みやすいように、最初のステップを実行する必芁がありたす。

これを行うには、䜕かがうたくいかなかったこずを理解する必芁がありたす。
1぀の方法は、 Maybeタむプを䜿甚するこずです。
このタむプはHaskellプログラムで非垞によく䜿甚されたす。

 import Data.Maybe 

これは䜕ですか Maybeこれは1぀のパラメヌタを取るタむプです。 圌の定矩は次のずおりです。
 data Maybe a = Nothing | Just a 


これは、倀を䜜成たたは蚈算しようずしお゚ラヌが発生したかどうかを理解するための良い方法です。
maybeRead関数は、このアプロヌチの優れた䟋です。
この関数は、 read関数JSON文字列を凊理するjavascript関数evalに非垞に䌌おいたすに䌌おいたす。
しかし、䜕かがうたくいかない堎合、結果はNothingたす。
結果が正しい堎合、 Just <>を返しJust <> 。
この機胜を深く掘り䞋げないでください。
その䞭のコヌドはreadよりも䜎いです。 。

 maybeRead :: Read a => String -> Maybe a maybeRead s = case reads s of [(x,"")] -> Just x _ -> Nothing 


コヌドが読みやすくなったので、次の機胜を䜜成したす。
文字列の圢匏が間違っおいた堎合、 Nothingを返したす。
他の堎合、たずえば「1,2,3」の堎合、 Just [1,2,3]を返したす。

 getListFromString :: String -> Maybe [Integer] getListFromString str = maybeRead $ "[" ++ str ++ "]" 


次に、main関数を䜿甚しおコヌドをテストしたす。

 main :: IO () main = do putStrLn "  ,  :" input <- getLine let maybeList = getListFromString input in case maybeList of Just l -> print (sum l) Nothing -> error "  . ." 


゚ラヌが発生した堎合、玠晎らしい゚ラヌメッセヌゞが衚瀺されたす。

この堎合、メむン関数のdoブロック内の各匏のタむプはIO aたたです。
唯䞀の奇劙なこずはerrorです。
error msg関数は、あらゆるタむプの入力この堎合はIO ()を単玔に受け入れたす。

非垞に重芁なこずに泚意しおください-すべおの関数のタむプは事前定矩されおいたす。
タむプIOずこれを持぀関数は、 mainです。
これは、mainが玔粋な関数ではないこずを意味したす。
ただし、玔粋なgetListFromString関数を䜿甚したす。
宣蚀された関数型を芋るだけで、玔粋な関数ず副䜜甚のある関数を区別できたす。


なぜ玔粋な機胜が重芁なのですか
いく぀かのこずを忘れるこずができたすが、䞻な理由は3぀ありたす。



これらの理由により、クリヌンな関数ではできるだけ倚くのコヌドを保持する必芁がありたす。

03_地獄/ 01_IO / 02_progressive_io_example.lhs



03_地獄/ 01_IO / 03_progressive_io_example.lhs

次のステップは、ナヌザヌが正しい答えを入力するたで垞にナヌザヌをポヌリングするこずです。

最初の郚分は倉曎されたせん。
 import Data.Maybe maybeRead :: Read a => String -> Maybe a maybeRead s = case reads s of [(x,"")] -> Just x _ -> Nothing getListFromString :: String -> Maybe [Integer] getListFromString str = maybeRead $ "[" ++ str ++ "]" 


そしお、数字のリストを芁求する関数を䜜成し、入力された有効なリストを受け取るたで終了したせん。

 askUser :: IO [Integer] askUser = do putStrLn "  ,  :" input <- getLine let maybeList = getListFromString input in case maybeList of Just l -> return l Nothing -> askUser 


この関数のタむプはIO [Integer]です。
これは、IOアクションを䜿甚しお[Integer]ような結果を埗たこずを意味したす。
䞀郚の人々はそれをこのように説明したす

「これはIO内の[Integer] 」


I / Oメカニズムの内郚構造を理解したい堎合は、次のセクションをお読みください。
しかし、実際には、単にIOを䜿甚する堎合は、単玔なプログラムをいく぀か䜜成し、型に぀いお考えるこずを忘れないでください。

その結果、メむン関数ははるかにシンプルになりたした。

 main :: IO () main = do list <- askUser print $ sum list 


これでIO玹介は終わりです。 すべおが非垞に速く進みたした。 芚えおおいおほしいこずは次のずおりです。



少し緎習すれば、 IOを䜿甚しおも問題はありたせん。

挔習 

  • すべおの匕数を芁玄するプログラムを䜜成したす。 ヒント getArgs関数を䜿甚したす。



03_地獄/ 01_IO / 03_progressive_io_example.lhs


IOトリックの説明



tl; dr 

玔粋な機胜を区別するために、
main 、䞖界の状態を倉える関数ずしお定矩されたす
 main :: World -> World 

このタむプの関数には、副䜜甚があるこずが保蚌されおいたす。 兞型的なメむン関数を芋おください

 main w0 = let (v1,w1) = action1 w0 in let (v2,w2) = action2 v1 w1 in let (v3,w3) = action3 v2 w2 in action4 v3 w3 

この䟋には倚くの時間倀がありたす w1 、 w2およびw3 
次のステップにデヌタを転送するために䜿甚されたす。

bindたたは(>>=)関数を䜜成しおいたす。 bindおかげでbind名前付きの䞀時的な倀は必芁なくなりたした。

  main = action1 >>= action2 >>= action3 >>= action4 


おたけHaskellには構文シュガヌがありたす
  main = do v1 <- action1 v2 <- action2 v1 v3 <- action3 v2 action4 v3 



なぜこのような奇劙な構文が必芁なのですか IOのタむプは䜕ですか これはすべお䜕らかの魔法のように芋えたすが。

しばらくの間、玔粋な関数に぀いおは忘れたしょう。 副䜜甚に焊点を圓おる

 askUser :: IO [Integer] askUser = do putStrLn "  ,  :" input <- getLine let maybeList = getListFromString input in case maybeList of Just l -> return l Nothing -> askUser main :: IO () main = do list <- askUser print $ sum list 


たず、このコヌドはたるで通垞の呜什型蚀語で曞かれおいるかのように芋えたす。
Haskellは、サむドアクションコヌドが䞍可欠に芋えるほどクヌルです。
必芁に応じお、Haskellでwhileアナログを䜜成できたす。
実際には、 IOで䜜業する堎合、呜什型のスタむルがより適しおいたす。

しかし、あなたはおそらく録音がやや珍しいこずに気づいたでしょう。 それで、理由の詳现な説明に行きたした。

ほずんどの蚀語では、䞖界の状態は倧きな暗黙のグロヌバル倉数ず考えるこずができたす。 この暗黙的な倉数は、プログラムのどこからでもアクセスできたす。 たずえば、任意の関数でファむルから\を読み取るこずができたす。 䞀方、ファむルの有無は、䞖界のさたざたな状態ず芋なすこずができたす。

Haskellでは、䞖界の状態は明瀺的に蚭定されたす。 main機胜が䞖界の状態を倉える可胜性があるこずを明確に述べおいたす。 そしお、そのタむプは次のようになりたす。

 main :: World -> World 


ただし、この倉数はすべおの機胜で䜿甚できるわけではありたせん。
この倉数を䜿甚する関数はクリヌンではありたせん。
それを䜿甚しない関数は玔粋ですこの芏則には危険な䟋倖がありたす。しかし、実際のプログラムでは、それは必芁ありたせん。詳现なデバッグの堎合を陀きたす。

Haskellは、䞖界はmain関数ぞの入力パラメヌタヌであるず考えおいたす。
しかし、この関数の実際の型は次のようなものです

 main :: World -> ((),World) 


興味のある方は、匏のタむプはdata IO a = IO {unIO :: State# RealWorld -> (# State# RealWorld, a #)}です。すべおの#は最適化に関連しおおり、この䟋ではいく぀かのフィヌルドを入れ替えたした。しかし、党䜓的には、考え方は倉わっおいたせん。


タむプ()は空のタむプです。
空虚。

次のこずを忘れずに、関数を曞き盎したしょう。
 main w0 = let (list,w1) = askUser w0 in let (x,w2) = print (sum list,w1) in x 


たず、副䜜甚のあるすべおの関数には特定の型が必芁です。

 World -> (a,World) 


aは結果のタむプです。
たずえば、 getChar関数のタむプはWorld -> (Char,World)なければなりたせん。

別の非自明なこずは、関数が蚈算される順序です。
Haskellでは、 fabを蚈算しようずするず、いく぀かのオプションがありたす



蚀語は機胜的に玔粋であるため、このようなトリックは珟実的です。

メむン関数をよく芋るず、最初の行は2番目の行のパラメヌタヌを蚈算する必芁があるため、最初の行を2番目の行の前に蚈算する必芁があるこずは明らかです。

このトリックはうたく機胜したす。
蚈算の各ステップで、コンパむラは新しい倉曎された䞖界ぞのポむンタを枡したす。
内郚では、 printは次のように機胜したす。



今メむンはひどく芋えたす。 askUser関数でも同じこずをしたしょう

 askUser :: World -> ([Integer],World) 

に
 askUser :: IO [Integer] askUser = do putStrLn "  :" input <- getLine let maybeList = getListFromString input in case maybeList of Just l -> return l Nothing -> askUser 

埌

 askUser w0 = let (_,w1) = putStrLn "Enter a list of numbers:" in let (input,w2) = getLine w1 in let (l,w3) = case getListFromString input of Just l -> (l,w2) Nothing -> askUser w2 in (l,w3) 

䞀芋、しかしbutい。 これらの䞍噚甚なw*倉数を芋おください。

私たちが孊んだ教蚓は、機胜的に玔粋な蚀語でのI / Oの玠朎な実装はひどいように芋えるこずです。

幞いなこずに、この問題を解決するためのより盎接的なアプロヌチがありたす。 パタヌンが芋えたす。 各行は次のように衚されたす。
 let (y,w') = action xw in 


最初のパラメヌタヌx䞍芁な堎合でも、結果はカップル(answer, newWorldValue)たす。 各関数fは、次のような型が必芁です。
 f :: World -> (a,World) 

さらに、これらの関数の䜿甚方法が非垞に䌌おいるこずにも気付きたした。
 let (y,w1) = action1 w0 in let (z,w2) = action2 w1 in let (t,w3) = action3 w2 in ... 


各アクションは、0〜n個のパラメヌタヌを䜿甚できたす。 たた、特に、各アクションは入力ずしお前の行の結果を取るこずができたす。

たずえば、次のように曞くこずができたす。
 let (_,w1) = action1 x w0 in let (z,w2) = action2 w1 in let (_,w3) = action3 xz w2 in ... 


そしお、もちろん、 actionN w :: (World) -> (a,World) 。

重芁 泚意が必芁なパタヌンは2぀だけです。
 let (x,w1) = action1 w0 in let (y,w2) = action2 x w1 in 

そしお
 let (_,w1) = action1 w0 in let (y,w2) = action2 w1 in 






そしお今、ちょっずしたトリックがありたす。
䞖界の状態を保持する倉数を「消滅」させたす。 2぀の文字列をbindたす。 これを行うために、 bind関数を䜜成したす。
圌女のタむプは最初は奇劙に芋えたす

 bind :: (World -> (a,World)) -> (a -> (World -> (b,World))) -> (World -> (b,World)) 


(World -> (a,World))はIOアクションのタむプであるこずを忘れないでください。
簡単にするために名前を倉曎したしょう

 type IO a = World -> (a, World) 


いく぀かの䟋

 getLine :: IO String print :: Show a => a -> IO () 


getLineは、パラメヌタずしおワヌルドを取り、ペア(String,World)を返すIOアクションです。 getLine型はIO Stringなるず蚀えたす。
たた、「IOで囲たれた」文字列を返すIOアクションずしお認識するこずもできたす。

print機胜も非垞に興味深いです。 入力パラメヌタヌを受け取り、衚瀺したす。 しかし、実際には2぀のパラメヌタヌが必芁です。 最初のパラメヌタヌは衚瀺される倀であり、2番目は䞖界の状態です。 結果ずしお、ペア((),World)返したす。 ぀たり、䞖界の状態を倉曎したすが、デヌタを返したせん。

この型は、 bind関数の型定矩を簡玠化したす。

 bind :: IO a -> (a -> IO b) -> IO b 


bindは匕数ずしお2぀のIOアクションを取り、別のIOアクションを返したす。

次に、いく぀かの重芁なパタヌンを曎新したす。 最初はこのようなものでした
 let (x,w1) = action1 w0 in let (y,w2) = action2 x w1 in (y,w2) 


タむプに泚意しおください。
 action1 :: IO a action2 :: a -> IO b (y,w2) :: IO b 

おなじみですね。
 (bind action1 action2) w0 = let (x, w1) = action1 w0 (y, w2) = action2 x w1 in (y, w2) 

基本的な考え方は、この関数を䜿甚しおWorldパラメヌタヌを非衚瀺にするこずです。 行こう 取埗したいものの䞀郚を次に瀺したす。
 let (line1,w1) = getLine w0 in let ((),w2) = print line1 in ((),w2) 


そしお今、バむンド関数を䜿甚したす
 (res,w2) = (bind getLine (\l -> print l)) w0 


printは(World -> ((),World))であるため、 res = () null型であるこずがわかりたす。
ここに特別なストリヌトマゞックが衚瀺されない堎合は、3行のプログラムを䜜成しおみおください。
 let (line1,w1) = getLine w0 in let (line2,w2) = getLine w1 in let ((),w3) = print (line1 ++ line2) in ((),w3) 

次のようなものです
 (res,w3) = bind getLine (\line1 -> bind getLine (\line2 -> print (line1 ++ line2))) 


気づいた
はい、これ以䞊World䞀時倉数はありたせん
これはMAです。 GI 私は

しかし、別の構文を䜿甚できたす。
bindを(>>=)眮き換えたしょう。
(>>=)これは䞭眮関数であり、
(+) ; リコヌル3 + 4 ⇔ (+) 3 4

 (res,w3) = getLine >>= \line1 -> getLine >>= \line2 -> print (line1 ++ line2) 

ほほほ 明けたしおおめでずうございたす Haskellには構文シュガヌがありたす

 do x <- action1 y <- action2 z <- action3 ... 


次のものに眮き換えるこずができたす。

 action1 >>= \x -> action2 >>= \y -> action3 >>= \z -> ... 


action2でxを䜿甚し、 action2でxをyずずもに䜿甚できたす。

しかし、 <-䜿甚しない文字列はどうでしょうか
簡単 blindBind関数がありたす
 blindBind :: IO a -> IO b -> IO b blindBind action1 action2 w0 = bind action (\_ -> action2) w0 


私はこの衚珟を意図的に単玔化したせんでした。
もちろん、コヌドをよりシンプルにしたい堎合は、挔算子(>>)䜿甚できたす。

そしおそう

 do action1 action2 action3 


に倉わる
 action1 >> action2 >> action3 

ちなみに、もう1぀の非垞に䟿利な機胜を次に瀺したす。
 putInIO :: a -> IO a putInIO x = IO (\w -> (x,w)) 


これは、玔粋な倀を「IOコンテキスト」にプッシュする暙準的な方法です。
通垞、 putInIOはputInIOず呌ばれたす。
Haskellを孊習するずき、この関数名は非垞にわかりにくいものです。 return他の蚀語のピアreturn倧きく異なりたす。



03_地獄/ 01_IO / 21_Detailled_IO.lhs

最埌に、䟋を曞き換えおみたしょう。
 askUser :: IO [Integer] askUser = do putStrLn "  ,  :" input <- getLine let maybeList = getListFromString input in case maybeList of Just l -> return l Nothing -> askUser main :: IO () main = do list <- askUser print $ sum list 


次のように曞き換えるこずができたす。

 import Data.Maybe maybeRead :: Read a => String -> Maybe a maybeRead s = case reads s of [(x,"")] -> Just x _ -> Nothing getListFromString :: String -> Maybe [Integer] getListFromString str = maybeRead $ "[" ++ str ++ "]" askUser :: IO [Integer] askUser = putStrLn "  ,  :" >> getLine >>= \input -> let maybeList = getListFromString input in case maybeList of Just l -> return l Nothing -> askUser main :: IO () main = askUser >>= \list -> print $ sum list 


これで、このコヌドをコンパむルしお、機胜するこずを確認できたす。

そしお、 (>>)ず(>>=)なしでこれがどのように芋えるか想像しおみたしょう。

03_地獄/ 01_IO / 21_Detailled_IO.lhs



03_地獄/ 02_Monads / 10_Monads.lhs

モナド




次に、秘密を明らかにしたしょうIOはモナドです。
モナドを䜿甚するず、構文糖衣蚘法を䜿甚できるこずを意味したす。
しかし、最も重芁なのはデザむンパタヌンです。これにより、よりクリヌンで理解しやすいコヌドを蚘述できたす。

重芁な泚意 

  • モナドは必ずしも副䜜甚に関連付けられおいるわけではありたせん
    倚くの玔粋なモナドがありたす。
  • モナドの本質は蚈算の構成にありたす



Haskell蚀語に関しおは、 Monadは型クラスです。
このクラスのむンスタンスになるには、関数(>>=)およびreturnを定矩する必芁がありたす。
関数(>>)は(>>=)基づいお自動的に䜜成されたす。
Monadクラスのほが完党な定矩を次に瀺したす。

 class Monad m where (>>=) :: ma -> (a -> mb) -> mb return :: a -> ma (>>) :: ma -> mb -> mb f >> g = f >>= \_ -> g --         --         fail :: String -> ma fail = error 

備考

  • classキヌワヌドはあなたが思うものではありたせん。
    Haskell のクラスは、OOPのクラスではありたせん 。
    Haskellのクラスは、JavaたたはCのむンタヌフェヌスに䌌おいたす。
    それをtypeclassず呌ぶ方が良いでしょう。
    これは倚くのクラスを意味したす。
    型がこのクラスに属するためには、その型はクラスのすべおの機胜を実装する必芁がありたす。
  • この特定の堎合、タむプmは匕数を取るこずができるタむプmなければなりたせん。
    たずえば、 IO a 、およびMaybe a [a]など。
  • 䟿利なモナドであるためには、関数が特定の法埋に準拠しおいる必芁がありたす。
    あなたの機胜がこれらの法埋に違反する堎合、奇劙なこずが起こりたす
     return a >>= k == ka m >>= return == m m >>= (\x -> kx >>= h) == (m >>= k) >>= h 





たぶんモナド



Monadむンスタンスには倚くのタむプがありたす。
最も簡単な䟋はMaybeです。
Maybe倀のセットがある堎合、モナドを䜿甚しおそれらを操䜜できたす。 特に、ネストされたif..then..else..を取り陀くず䟿利ですif..then..else..

耇雑な銀行業務を想像しおください。マむナスに入らずに取匕の履歎がある堎合にのみ、700ナヌロのボヌナスを請求できたす。

 deposit value account = account + value withdraw value account = account - value eligible :: (Num a,Ord a) => a -> Bool eligible account = let account1 = deposit 100 account in if (account1 < 0) then False else let account2 = withdraw 200 account1 in if (account2 < 0) then False else let account3 = deposit 100 account2 in if (account3 < 0) then False else let account4 = withdraw 300 account3 in if (account4 < 0) then False else let account5 = deposit 1000 account4 in if (account5 < 0) then False else True main = do print $ eligible 300 -- True print $ eligible 299 -- False 

03_地獄/ 02_Monads / 10_Monads.lhs



03_地獄/ 02_モナド/ 11_Monads.lhs

そしお、MaybeずそのMonad゚ンティティを䜿甚しお、このコヌドで物事を敎理したしょう。
 deposit :: (Num a) => a -> a -> Maybe a deposit value account = Just (account + value) withdraw :: (Num a,Ord a) => a -> a -> Maybe a withdraw value account = if (account < value) then Nothing else Just (account - value) eligible :: (Num a, Ord a) => a -> Maybe Bool eligible account = do account1 <- deposit 100 account account2 <- withdraw 200 account1 account3 <- deposit 100 account2 account4 <- withdraw 300 account3 account5 <- deposit 1000 account4 Just True main = do print $ eligible 300 -- Just True print $ eligible 299 -- Nothing 


03_地獄/ 02_モナド/ 11_Monads.lhs



03_地獄/ 02_モナド/ 12_Monads.lhs

すでに悪くはありたせんが、さらに改善するこずができたす

 deposit :: (Num a) => a -> a -> Maybe a deposit value account = Just (account + value) withdraw :: (Num a,Ord a) => a -> a -> Maybe a withdraw value account = if (account < value) then Nothing else Just (account - value) eligible :: (Num a, Ord a) => a -> Maybe Bool eligible account = deposit 100 account >>= withdraw 200 >>= deposit 100 >>= withdraw 300 >>= deposit 1000 >> return True main = do print $ eligible 300 -- Just True print $ eligible 299 -- Nothing 


それで、モナドがコヌドをより゚レガントにするこずができるこずを蚌明したした。
䞀般的に、 Maybe同様の䜿甚は、ほずんどの呜什型蚀語で機胜したす。
これはかなり自然なデザむンです。

重芁な泚意

結果がNothingである最初の蚈算は、以降の蚈算をすべお停止したす。
぀たり、すべおのコヌドが実行されるわけではありたせん。
たた、この最適化は、蚀語の遅延のおかげで、完党に無料で利甚できたす。


Maybe定矩(>>=)を䜿甚しお、このコヌドを曞き換えるこずができたすMaybe

 instance Monad Maybe where (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b Nothing >>= _ = Nothing (Just x) >>= f = fx return x = Just x 


Maybe 、このような小さな䟋でもモナドはその䟡倀を蚌明したMaybe 。 IOモナドの䜿甚も確認したした。 しかし、もっず興味深い䟋がありたす-リスト。

03_地獄/ 02_モナド/ 12_Monads.lhs



03_Hell / 02_Monads / 13_Monads.lhs

リストモナド



リストモナドを䜿甚するず、非決定的な蚈算をモデル化できたす。
䟋
 import Control.Monad (guard) allCases = [1..10] resolve :: [(Int,Int,Int)] resolve = do x <- allCases y <- allCases z <- allCases guard $ 4*x + 2*y < z return (x,y,z) main = do print resolve 

MA GI。I .:

 [(1,1,7),(1,1,8),(1,1,9),(1,1,10),(1,2,9),(1,2,10)] 


リストモナド甚の構文糖衣もありたす

  print $ [ (x,y,z) | x <- allCases, y <- allCases, z <- allCases, 4*x + 2*y < z ] 


モナドの完党なリストは提䟛したせんが、それらはたくさんありたす。
モナドを䜿甚するず、倚くのタスクを簡玠化できたす。
特に、モナドは次の堎合に非垞に䟿利です。



この堎所に着いたら、おめでずうございたす
これでカンフヌモナドがわかりたしたもちろん、それらに慣れるには緎習が必芁です。自分で曞いおください。しかし、あなたはすでにこの方向に倧きな䞀歩を螏み出しおいたす

03_Hell / 02_Monads / 13_Monads.lhs

アプリ



このセクションはHaskellの研究には盎接適甚されたせん。䞀郚の詳现にのみ泚意を払っおいたす。



04_付録/ 01_More_on_infinite_trees / 10_Infinite_Trees.lhs

無限の朚に぀いおもう䞀぀



Infinite Structuresセクションでは、いく぀かの単玔な無限構造を芋おきたした。残念ながら、ツリヌには2぀のプロパティがありたせん。

  1. ツリヌノヌドに重耇はありたせん
  2. 敎然ずした朚


このセクションでは、最初のプロパティを扱いたす。
2番目に぀いおは、少し匱めたすが、できるだけ理想に近づけるようにしたす。

たず、䞀連の疑䌌乱数からリストを䜜成したしょう。

 shuffle = map (\x -> (x*3123) `mod` 4331) [1..] 


メモリを曎新するには、実装を耇補したす treeFromList
 treeFromList :: (Ord a) => [a] -> BinTree a treeFromList [] = Empty treeFromList (x:xs) = Node x (treeFromList (filter (<x) xs)) (treeFromList (filter (>x) xs)) 

およびtreeTakeDepth

 treeTakeDepth _ Empty = Empty treeTakeDepth 0 _ = Empty treeTakeDepth n (Node x left right) = let nl = treeTakeDepth (n-1) left nr = treeTakeDepth (n-1) right in Node x nl nr 


結果を芋おみたしょう
 main = do putStrLn "take 10 shuffle" print $ take 10 shuffle putStrLn "\ntreeTakeDepth 4 (treeFromList shuffle)" print $ treeTakeDepth 4 (treeFromList shuffle) 


 % runghc 02_Hard_Part/41_Infinites_Structures.lhs take 10 shuffle [3123,1915,707,3830,2622,1414,206,3329,2121,913] treeTakeDepth 4 (treeFromList shuffle) < 3123 : |--1915 : | |--707 : | | |--206 : | | `--1414 : | `--2622 : | |--2121 : | `--2828 : `--3830 : |--3329 : | |--3240 : | `--3535 : `--4036 : |--3947 : `--4242 

やった皌いだただし、ブランチに远加するものがある堎合にのみ機胜したす。

䟋
 treeTakeDepth 4 (treeFromList [1..]) 


無限に実行されたす。
これは、コヌドが匏の先頭を取埗しようずしおいるためfilter (<1) [2..]です。
しかしfilter、匏の結果が空のリストであるこずを理解するほど賢くはありたせん。

ただし、これは非厳栌なプログラムができるこずの良い䟋です。

読者のための緎習



04_付録/ 01_More_on_infinite_trees / 10_Infinite_Trees.lhs



04_付録/ 01_More_on_infinite_trees / 11_Infinite_Trees.lhs

Davateはわずかに倉曎されお
treeFromListおりshuffle、この問題を取り陀くために䜜成されおいたす。

最初の問題は私たちの実装における乱数の䞍足ですshuffle。異なる数倀
のみを生成したした4331。
機胜を改善したすshuffle。

 shuffle = map rand [1..] where rand x = ((px) `mod` (x+c)) - ((x+c) `div` 2) px = m*x^2 + n*x + o -- some polynome m = 3123 n = 31 o = 7641 c = 1237 


このバヌゞョンの関数は、倀に䞊限ず䞋限がないので私は心から願っおいたす優れおいたす。しかし、シャッフルの改善は、無限ルヌプを回避するのに十分ではありたせん。

厳密に蚀えば、リストが空かどうかはわかりたせんfilter (<x) xs。
この問題を解決するために、バむナリツリヌの実装を少し壊したす。新しい実装では、䞀郚のノヌドで次の条件が満たされたせん。

巊の芁玠たたは右は​​、ツリヌのルヌトの倀よりも厳密に小さいたたは倧きい必芁がありたす。


この堎合、ツリヌは実質的に順序付けられたたたになりたす。さらに、ツリヌを䜜成する段階で、ノヌドの䞀意性を確保したす。

新しいバヌゞョンでtreeFromListは、単にに眮き換えたしfilterたsafefilter。

 treeFromList :: (Ord a, Show a) => [a] -> BinTree a treeFromList [] = Empty treeFromList (x:xs) = Node x left right where left = treeFromList $ safefilter (<x) xs right = treeFromList $ safefilter (>x) xs 


この関数はsafefilterほが同じですfilterが、無限ツリヌを凊理するずきに無限ルヌプに陥るこずはありたせん。10,000ステップで適切なアむテムが芋぀からない堎合、怜玢を䞭止したす。

 safefilter :: (a -> Bool) -> [a] -> [a] safefilter fl = safefilter' fl nbTry where nbTry = 10000 safefilter' _ _ 0 = [] safefilter' _ [] _ = [] safefilter' f (x:xs) n = if fx then x : safefilter' f xs nbTry else safefilter' f xs (n-1) 


プログラムを実行しお喜ぶ

 main = do putStrLn "take 10 shuffle" print $ take 10 shuffle putStrLn "\ntreeTakeDepth 8 (treeFromList shuffle)" print $ treeTakeDepth 8 (treeFromList $ shuffle) 


異なる倀の衚瀺間の遅延が同じではないこずに気づいたかもしれたせん。
これは、Haskellが必芁に応じお各倀を蚈算するためです。
この堎合、番号が画面に衚瀺されるずきに必芁になりたす。

怜玢の深さをから8に増やしおも100、この
関数は機胜したすが、同時にすべおのRAMを消費するこずはありたせん


読者のための緎習



 treeFromList' [] n = Empty treeFromList' (x:xs) n = Node x left right where left = treeFromList' (safefilter' (<x) xs (fn) right = treeFromList' (safefilter' (>x) xs (fn) f = ??? 


04_Appendice/01_More_on_infinite_trees/ 11_Infinite_Trees.lhs


( , )



ありがずう/r/haskellず
/r/programming。
あなたのコメントは非垞に貎重です。

特に、英語のテキストの曎新に費やした時間に぀いお、Emmに 1000回感謝したす。ありがずうございたす

Source: https://habr.com/ru/post/J153383/


All Articles