基本的に、機能的なスタイルでは、データのストリームを取得する IO 操作を、現在のキャラクターと現在の状態に基づいたステートフル トランジションの純粋な操作から分割する必要があります。
Tikhon の Haskell ソリューションは非常にクリーンですが、入力データに対して 3 つのパスを実行するため、結果が計算されるまで入力全体がメモリに格納されます。データを段階的に処理することができます。これは以下で Text パッケージを使用して行いますが、他の高度な Haskell ツールは使用しません (これにより、非 Haskeller による理解が犠牲になる可能性があります)。
まず、プリアンブルがあります。
{-# LANGUAGE BangPatterns #-}
import Data.Text.Lazy as T
import Data.Text.Lazy.IO as TIO
次に、プロセスの状態を保持するデータ構造を定義します (状態 IN/OUT と共に文字数、単語数、行数)。
data Counts = Cnt { nc, nl, nw :: !Int
, state :: State }
deriving (Eq, Ord, Show)
data State = IN | OUT
deriving (Eq, Ord, Show)
ここで、簡単に使用できるように「ゼロ」状態を定義します。通常、多くのヘルパー関数を作成するか、 lense のようなパッケージを使用して、Counts
構造内の各フィールドを簡単にインクリメントしますが、この答えは省略します:
zeros :: Counts
zeros = Cnt 0 0 0 OUT
それでは、一連の if/else ステートメントを純粋なステート マシンに変換します。
op :: Counts -> Char -> Counts
op c '\n' = c { nc = nc c + 1, nw = nw c + 1, nl = nl c + 1, state=OUT }
op c ch | ch == ' ' || ch == '\t' = c { nc = nc c + 1, state=OUT }
| state c == OUT = c { nc = nc c + 1, nw = nw c + 1, state = IN }
| otherwise = c { nc = nc c + 1 }
最後に、main
関数は入力ストリームを取得し、操作を文字に重ねます。
main = do
contents <- TIO.getContents
print $ T.foldl' op zeros contents
編集:あなたは構文を理解していないと述べました。ここで説明するさらに単純なバージョンを次に示します。
import Data.Text.Lazy as T
import Data.Text.Lazy.IO as TIO
op (nc, nw, nl, st) ch
| ch == '\n' = (nc + 1, nw + 1 , nl + 1, True)
| ch == ' ' || ch == '\t' = (nc + 1, nw , nl , True)
| st = (nc + 1, nw + 1 , nl , False)
| otherwise = (nc + 1, nw , nl , st)
main = do
contents <- TIO.getContents
print $ T.foldl' op (0,0,0,True) contents
ステートメントは、使用するおよび関数へのimport
アクセスを提供します。getContents
foldl'
この関数は、基本的に C の if/elseif/else シリーズに似た一連op
のガード (次のようなパーツ) を使用します。| ch = '\n'
タプル( ... , ... , ... , ... )
にはすべての状態が含まれています。Haskell 変数は不変であるため、前の変数の値に 1 を追加する (または追加しない) ことで、新しいタプルを作成します。