あなたの問題を解決するためのヒントをいくつかお伝えします。
まず、定義するすべての関数に型を与えることは非常に役立ちます。これにより、関数が実行していると思われることを実行していない場合、コンパイラはすぐに通知できます。また、プログラムのさまざまな部分を結合する必要がある場合にも役立ちます。
第 2 に、関数ごとに 1 つの問題だけを解決することは非常に良い考えです。あなたの intotwo 関数はこれによく従い、リストを非常にうまく分割する仕事をします。ただし、結合関数は多すぎるように見えるため、書き込みが難しくなります。その関数を 2 つの小さな関数に分割します。
最後に、 という非常に便利な特殊関数がありundefined
ます。これはあらゆる型にマッチし、関数を書くのに役立ちます。秘訣は、関数全体が未定義に等しい状態 (およびコンパイルは実行時にクラッシュする) から開始し、目的の動作を行い、未定義がなくなるまで関数を徐々に改善することです。
まず、intotwo 関数に型シグネチャを追加します。タイプは[a] -> ([a], [a])
です。
次に、空の行に到達するまで入力から行を読み取り、読み取った行のリストを返す関数を作成します。この関数の型は次のとおりです。
readLinesUntilEmpty :: IO [String]
readLinesUntilEmpty = undefined
これをほぼ行う実装は次のとおりです。
readLinesUntilEmpty = do
nextLine <- getLine
rest <- readLinesUntilEmpty
return (nextLine : rest)
ただし、これは読み取りを停止することはありません ( nextLine が空であるかどうかがチェックされないことに注意してください)。
次の関数は、これを行う方法を示しています (ただし、実装の一部は省略しています)。
readLinesUntilEmpty = do
nextLine <- getLine
case nextLine of
"" -> do
undefined -- TODO fix me
_ -> do
undefined -- TODO fix me
そこから残りを把握できるはずです。
次に、intotwo 関数は文字列の 2 つのリストを返しますが、それらを再び結合する必要があります。たとえば["this", "that"]
、 に変わる必要があります"this\nthat"
。そのような関数のタイプは次の[String] -> String
とおりです。
joinLines :: [String] -> String
joinLines = undefined -- TODO
これは inttotwo 関数よりも書きやすい関数なので、問題ないはずです。
最後に、これら 3 つの関数をリンクして結果を得ることができます。
combine :: IO (String, String)
combine = do
lines <- readLinesUntilEmpty
let (oddLines, evenLines) = intotwo lines
return $ (joinLines oddLines, joinLines evenLines)