なぜこの最初のウ機能な取扱いの無限のリストは、この第二のスニペットに成功し、無数のリストがありますか?
-
21-08-2019 - |
質問
私ウ機能は、以下のようなものです。ものの失敗に対し無限リストの成功に対し無限のリストが表示されます。そこで広く用いられていた時間を捻それが無い.
両方のスニペットをされてしまうと、残念ながらの実施が検討されている言葉"機能プレリュード.両方の作戦の有限のリストが表示されます。
こちらのバージョンの取り扱いはいたしません。無限リスト:
myWords_FailsOnInfiniteList :: String -> [String]
myWords_FailsOnInfiniteList string = foldr step [] (dropWhile charIsSpace string)
where
step space ([]:xs) | charIsSpace space = []:xs
step space (x:xs) | charIsSpace space = []:x:xs
step space [] | charIsSpace space = []
step char (x:xs) = (char : x) : xs
step char [] = [[char]]
こちらのバージョンも取り扱いをしており無限のリスト:
myWords_anotherReader :: String -> [String]
myWords_anotherReader xs = foldr step [""] xs
where
step x result | not . charIsSpace $ x = [x:(head result)]++tail result
| otherwise = []:result
注意:"charIsSpace"は単に名称変更の文字に置き換わります。isSpace.
以下の通訳のセッション、この失敗に対して無限のリストが一つが成功する.
*Main> take 5 (myWords_FailsOnInfiniteList (cycle "why "))
*** Exception: stack overflow
*Main> take 5 (myWords_anotherReader (cycle "why "))
["why","why","why","why","why"]
編集:への対応は、以下だと思いを理解しています。ここで私の結論の修正コード:
結論:
- 最大の犯人は私の最初の2方程式に始まる"ステップスペース[]"および"ステップchar[]". マッチングのパラメータのステップ機能に対する""がん, ので、力の第2回argに対する評価が、問題点については、下記の通りであります).
- ポイントになと思っていたのですが(++)が評価し、その右引数以上連結実績、なんだか。なので、思いついたものはこの数値を変更することにより=(char:x):xs"を"=[char:x]++xs".そのた 誤った.
- ポイントになると思ったパターンマッチングのargに対しx:xs)の機能が失敗に対し無限のリストが表示されます。った ほとんど そしてこないのだとか!評価のargに対しx:xsしているパターンマッチ、 ま 一部再帰.で"右クランク"までのヒット、":"(通称:"連結").まっていないターネットに接続されており、機能しないの成功に対して無限の一覧です。しかし、 この特定の場合, もOKですが私の機能との出会いのスペース、は、"連結"が発生します。の評価によりマッチングの対(x:xs)停止権、無限再帰を回避するた.そこで、"x"が照合されますが、xsのままに、サンクなので問題はありません。(メガネーシュのために本当に助かってい把握します。
- 一般的に、できる 言 第二引数をとるべきとん 力の評価で.またマッチするx:xsきますので、そちxsだいたいどんな力を評価します。
なので、この修正コードです。また、普段はしないようにしてヘッド、テール、というだけでは一部機能がなかったことが必要で書く練習をするパターンマッチング等価です。
myWords :: String -> [String]
myWords string = foldr step [""] (dropWhile charIsSpace string)
where
step space acc | charIsSpace space = "":acc
step char (x:xs) = (char:x):xs
step _ [] = error "this should be impossible"
この正常に作品に対し無限のリストが表示されます。りがございますので予めご了承のヘッド、テールまたは(++)オペレーターです。
現在、重要な点に注意: 初めて書いたのを修正コードがありませんでしたが、3方程式チに対するステップ_[]".結果としての警告を---網羅的なパターン。明らかでは良い考えでそういう風にならないように警告が表示されます。
ものに思っています。記事で書いたように、上記る でOKパターンマッチのargに対し[].がいないように警告が表示されます。
しかし、時に"ステップ_[]"方程式、スーツケースが広げられました!な問題と無限リスト!.なぜですか?
が、3方程式のコードを修正しいとは!?
実際には、以下の折れたバージョン。でも同じコードを除き、その後にパターンのための空のリスト上のパターン:
myWords_brokenAgain :: String -> [String]
myWords_brokenAgain string = foldr step [""] (dropWhile charIsSpace string)
where
step _ [] = error "this should be impossible"
step space acc | charIsSpace space = "":acc
step char (x:xs) = (char:x):xs
ただいまーるスタックオーバーフローでは、この場合のステップと呼ばれるのは、通訳かどうかをチェックした場合の方程式の一番のである。つくば地区においては、必見た場合のargは[].いることを行い、評価しなければならな第ばいけない。
移動方程式を以下にその他の方程式による3方程式がないたので、最初または第二のパターン 常にマッチ.第3次方程式はありませるの網羅的なパターン警告が表示されます。
これは大きな学びます。皆さん、ありがとうござる。
解決
その他一切の出力を生成する前に、2番目の引数を評価し、まだfoldrは無限のリストに適用されたときに2番目の引数は、最終的にステップの別の呼び出しの結果に依存し、常にそのステップで問題を指摘していますます。
これは、このように書かれている必要はありませんが、それは特定のフォーマットを有するステップ初期引数に依存していると、それはヘッド/テールがうまくいかないことを確認するためには非常に難しいので、あなたの第二のバージョンは、一種の醜いです。 (私も、彼らがいないということは100%確実ではないよ!)
何をすべきことは再構築、それは、少なくともいくつかの状況で入力リストに依存せずに出力を生成しますので、最初のバージョンです。特に、我々は文字が空白でない場合、出力リスト内の少なくとも1つの要素が常に存在だということがわかります。その結果、最初の要素を製造した後まで、第2引数のパターンマッチングを遅らせます。あなたは、スペースの無限のリストを渡した場合、その場合は、無限再帰ことができる唯一の方法は、任意の出力を生成しに行かない、その場合には、あるため、文字が空白の場合は、まだリストに依存することになりますが、それは大丈夫ですループは(それが他に何ができますか?)の言葉のために正常な動作です。
他のヒント
手で式を拡張してみます:
take 5 (myWords_FailsOnInfiniteList (cycle "why "))
take 5 (foldr step [] (dropWhile charIsSpace (cycle "why ")))
take 5 (foldr step [] (dropWhile charIsSpace ("why " ++ cycle "why ")))
take 5 (foldr step [] ("why " ++ cycle "why "))
take 5 (step 'w' (foldr step [] ("hy " ++ cycle "why ")))
take 5 (step 'w' (step 'h' (foldr step [] ("y " ++ cycle "why "))))
次の展開は何ですか?あなたはstep
のためのパターンマッチさせるために、あなたはそれは空のリストだかいないかどうかを知る必要があることがわかります。それを見つけるためには、あなたは、少なくとも少しそれを評価する必要があります。しかし、第二項は、あなたがのためにパターンマッチングしている非常に機能によってfoldr
減少であることを起こります。言い換えれば、ステップ関数は、自分自身を呼び出すことなく、その引数で見ることができない、ので、あなたは無限再帰を持っています。
コントラストというあなたの第二の機能の拡張でます:
myWords_anotherReader (cycle "why ")
foldr step [""] (cycle "why ")
foldr step [""] ("why " ++ cycle "why ")
step 'w' (foldr step [""] ("hy " ++ cycle "why ")
let result = foldr step [""] ("hy " ++ cycle "why ") in
['w':(head result)] ++ tail result
let result = step 'h' (foldr step [""] ("y " ++ cycle "why ") in
['w':(head result)] ++ tail result
あなたはおそらく宇宙に到達するまで、この拡大が継続することを確認することができます。スペースに達すると、「頭の結果は、」値を取得します、とあなたは答えの最初の要素を生産しています。
私は、この第2の機能は、スペースを含まない、無限の文字列をオーバーフローすると思われます。あなたはなぜ見ることができますか?
番目のバージョンは、実際には、のまでresult
を評価しません。最初のバージョンは、その上にパターンマッチングにより、すぐにのresult
を評価します。
これらの無限リストとキーはあなたが出力は常に入力の「先に滞在する」ことができるように、リストの要素を要求し始める前に、の何かのを生産しなければならないということです。
(私はこの説明は非常に明確ではないように感じるが、それは私ができる最善のです。)
ライブラリ関数foldr
この実装(または類似の)を有している:
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f k (x:xs) = f x (foldr f k xs)
foldr _ k _ = k
myWords_FailsOnInfiniteList
の結果がで...ので、無限のリストに依存インナーfoldr
の結果に依存するstep
の結果に依存foldr
の結果に依存し、myWords_FailsOnInfiniteList
は無限の量を使用します。その最初の単語を生成する前に、空間と時間ます。
step
でmyWords_anotherReader
機能は、それが最初の単語の最初の文字を生成した後まで、内側foldr
の結果を必要としません。 Apocalispが言うように、それは次の単語を生成する前に最初の単語が生産されているように、尾のサンクが成長tail ([...] ++ tail ([...] ++ tail (...)))
を保持しているため残念ながら、それは、O(最初の単語の長さ)のスペースを使用します。
これとは対照的に、と比較
myWords :: String -> [String]
myWords = myWords' . dropWhile isSpace where
myWords' [] = []
myWords' string =
let (part1, part2) = break isSpace string
in part1 : myWords part2
のように定義することができるライブラリ関数を使用して
break :: (a -> Bool) -> [a] -> ([a], [a])
break p = span $ not . p
span :: (a -> Bool) -> [a] -> ([a], [a])
span p xs = (takeWhile p xs, dropWhile p xs)
takeWhile :: (a -> Bool) -> [a] -> [a]
takeWhile p (x:xs) | p x = x : takeWhile p xs
takeWhile _ _ = []
dropWhile :: (a -> Bool) -> [a] -> [a]
dropWhile p (x:xs) | p x = dropWhile p xs
dropWhile _ xs = xs
中間結果を生成することは、将来の演算によりアップ保持されることはありません、その結果の各要素は、消費のために利用可能になるようにのみO(1)の空間が必要であること。 お知らせ <時間>
補遺
だから、ここ改訂コードです。私は通常、私は同等のパターンマッチングを書く練習を必要とするので、それらが部分的に機能しているという理由だけで、頭と尾を避け、してみます。
myWords :: String -> [String] myWords string = foldr step [""] (dropWhile charIsSpace string) where step space acc | charIsSpace space = "":acc step char (x:xs) = (char:x):xs step _ [] = error "this should be impossible"
(脇:あなたはあなたのwords "" == []
気にしますが、ライブラリーからmyWords "" = [""]
、ないかもしれませんが、末尾のスペースと同様の問題を)
myWords_anotherReader
上で大いに改善されたルックス、そしてfoldr
ベースのソリューションのためのかなり良いです。
\n -> tail $ myWords $ replicate n 'a' ++ " b"
これは、O(n)の時間よりも良いを行うことは可能ではないですが、myWords_anotherReader
とmyWords
の両方が、ここでO(n)のスペースを取ります。これはfoldr
の使用与え避けられないかもしれません。
さらに悪いことに、
\n -> head $ head $ myWords $ replicate n 'a' ++ " b"
myWords_anotherReader
はO(1)であったが、myWords
パターンマッチングは、更に結果を必要とするため、新しい(x:xs)
は、O(N)である。
あなたがでこれを回避することができます。
myWords :: String -> [String]
myWords = foldr step [""] . dropWhile isSpace
where
step space acc | isSpace space = "":acc
step char ~(x:xs) = (char:x):xs
~
は「反駁できないパターン」を紹介しています。反駁できないパターンが失敗することはありませんし、すぐに評価を強制しないでください。