実世界のHaskell、印刷物の第4章、98ページ は、フォールドを使用してwords
を実装できるかどうかを尋ねます。これも私の質問です。
それは可能ですか?そうでない場合、なぜですか?もしそうなら、どうですか?
私は次のことを思いつきました。これは、各非スペースを出力リストの最後のWordの前に付加する必要があるという考え(これはotherwise
ガードで発生します)、およびスペースが空のWordがない場合は、出力リストに空のWordを追加します(これはif
-then
-else
で処理されます)。
myWords :: String -> [String]
myWords = foldr step [[]]
where
step x yss@(y:ys)
| x == ' ' = if y == "" then yss else "":yss
| otherwise = (x:y):ys
入力文字列の先頭のスペースは、文字列の出力リストで1つの先行する空の文字列になるため、このソリューションは明らかに間違っています。
上記のリンクで、他の読者のために提案されたソリューションのいくつかを調べましたが、それらの多くは私のソリューションと同様に機能しますが、それらは通常、たとえばtail
ing空の先行文字列がある場合。
他のアプローチはタプル(実際にはペアのみ)を使用するため、フォールドはペアを処理し、先頭/末尾のスペースを適切に処理できます。
これらすべてのアプローチにおいて、foldr
(または別のフォールド、fwiw)は、箱から出して最終的な出力を提供する関数ではありません。どういうわけか出力を何らかの方法で調整しなければならない何かが常にあります。
したがって、最初の質問に戻って、フォールドを使用してwords
を(末尾/先頭/繰り返しのスペースを正しく処理する方法で)実装することが実際に可能かどうかを尋ねます。 foldsを使用することで、折りたたみ関数が最も外側の関数でなければならないことを意味します:
myWords :: String -> [String]
myWords input = foldr step seed input
はい。少しトリッキーですが、CPSに慣れれば、単一のfoldr
だけを使用してこのジョブを適切に実行できます( 継続パススタイル )。以前、特別な種類の chunksOf
関数を示しました。
この種類のフォールドではアキュムレータ、つまりフォールドの結果は関数であり、最終的な結果が得られるように、それを恒等類の入力に適用する必要があります。したがって、ここでは単一の折り畳みを使用しており、そのタイプには関数が含まれているため、これは最終処理段階としてカウントされる場合とそうでない場合があります。議論するために開いてください:)
ws :: String -> [String]
ws str = foldr go sf str $ ""
where
sf :: String -> [String]
sf s = if s == " " then [""] else [s]
go :: Char -> (String -> [String]) -> (String -> [String])
go c f = \pc -> let (s:ss) = f [c]
in case pc of
"" -> dropWhile (== "") (s:ss)
otherwise -> case (pc == " ", s == "") of
(True, False) -> "":s:ss
(True, True) -> s:ss
otherwise -> (pc++s):ss
λ> ws " a b c "
["a","b","c"]
sf
:開始する関数の初期値。
go
:イテレーター関数
前の文字pc
と正しい文字c
の両方が手元にあるため、実際にはここではCPSの能力を十分に活用していません。上記のchunksOf
関数では、要素の昇順シーケンスが壊れるたびに[Int]
を[[Int]]
にチャンク化するときに非常に役立ちました。