関数
8.5 本章のまとめ
「脊髄反射的に」ポイントフリースタイル書き換えが身につくように頑張ります。
8.6 練習問題
いつものように実験しながら自力で解いていきます。
lstrip関数について
dropWhile関数はこんな感じの動き。
Prelude> dropWhile (== 1) [1, 1, 2, 1, 3] [2,1,3] Prelude> dropWhile (== '$') "$$$$###$$$" "###$$$" Prelude> dropWhile (== ' ') " 12345 67890" "12345 67890"
まずはベタで(ポイントフリースタイルを使わずに)解いてみる。
いつぞや話があったイディオム(unlines → 行ごとの処理 → lines)を使って実装してみよう。
こんな感じ。
- lstrip.hs
main = do cs <- getContents putStr $ lstrip cs lstrip :: String -> String lstrip cs = unlines $ map myDropWhile $ lines cs myDropWhile :: String -> String myDropWhile cs = dropWhile (== ' ') cs
そして動作させてみる。
% ghc -W -o lstrip lstrip.hs % cat test.txt abc abc abc abc bcd bcd bcd bcd abc abc cdecde % ./lstrip < test.txt abc abc abc abc bcd bcd bcd bcd abc abc cdecde
でも設問はポイントフリースタイルを使いなさいとのこと。
上のコードをポイントフリースタイルで書き直してみる。こんな感じ。
- lstrip2.hs
main = do cs <- getContents putStr $ lstrip cs {-- lstrip 各行の先頭にあるスペースを取り除く。 --} lstrip :: String -> String lstrip = unlines . map (dropWhile (== ' ')) . lines
うん、いい感じに縮まりましたね。
いきなりこのスタイルを捻り出すのは難しいですけどね。
rstrip関数について
さて次の設問へ進みます。lstripの逆(末尾のスペースを取り除く)ですね。
う〜ん、何といっても文字列を逆順に並べ替えたいですよね。
ということで実験してみます。
Prelude> reverse "abcdefg" "gfedcba" Prelude> reverse $ reverse "abcdefg" "abcdefg"
よしよし、reverse関数いけそうだ。lstripを使ってもよいのでこんな感じ。
- rstrip.hs
main = do cs <- getContents putStr $ reverse $ rstrip cs {-- rstrip 各行の末尾にあるスペースを取り除く。 --} rstrip :: String -> String rstrip = reverse . lstrip . reverse {-- lstrip 各行の先頭にあるスペースを取り除く。 --} lstrip :: String -> String lstrip = unlines . map (dropWhile (== ' ')) . lines
そのまま出力しても分かりにくいので、末尾のスペースをとった後、
逆順にして出力しています。結果はこんな感じ。
% ghc -W -o rstrip rstrip.hs % cat test2.txt abc abc abc abc bcd bcd bcd bcd abc abc cdecde % ./rstrip < test2.txt edcedc cba cba dcb dcb dcb dcb cba cba cba cba
あれれ、先頭に改行が出力されてる。
unlinesしたとき、各行に改行コードが付加されるけど、必要のない改行コードも付加されちゃってる模様。
入力を一気にreverseするから問題なわけです。
一行ずつreverseするように変えてみます。なお、やっぱりスペースだと分かりにくいので
@を消すように変えてます。
main = do cs <- getContents putStr $ unlines $ map (rstrip '@') $ lines cs {-- rstrip 末尾にある指定文字を取り除く。 --} rstrip :: Char -> String -> String rstrip c = reverse . (lstrip c) . reverse {-- lstrip 先頭にある指定文字を取り除く。 --} lstrip :: Char -> String -> String lstrip c = dropWhile (== c)
結果はこんな感じ。
% ghc -W -o rstrip rstrip.hs % cat test3.txt @@@@abc abc@@@@@@ @@@@@@@@@@@@abc@@ @ abc@@@ @bcd @ @ @ @ bcd @ @ @ @ @ @@@@bcd bcd @@ @@ @ @ @ @@@ abc abc @@@@@@@@ @ @ cdecde @@@@@@@@@@@@@@@@@@@@@@@ % ./rstrip < test3.txt @@@@abc abc @@@@@@@@@@@@abc@@ @ abc @bcd @ @ @ @ bcd @ @ @ @ @@@@bcd bcd @@ @@ @ @ @@@ abc abc @ @ cdecde
いい感じです♪
strip関数について
次は文字列の先頭と末尾のスペースを全て取り除くstrip関数。
単純に考えると、lstripを呼んでからrstripを呼べばよさげですね。こんな感じ。
- strip.hs
main = do cs <- getContents putStr $ unlines $ map (strip '@') $ lines cs {-- strip 各行の先頭・末尾にある指定された文字を取り除く。 --} strip :: Char -> String -> String strip c = (rstrip c) . (lstrip c) {-- rstrip 各行の末尾にある指定文字を取り除く。 --} rstrip :: Char -> String -> String rstrip c = reverse . (lstrip c) . reverse {-- lstrip 各行の先頭にある指定文字を取り除く。 --} lstrip :: Char -> String -> String lstrip c = dropWhile (== c)
結果はこんな感じ。
% ghc -W -o strip strip.hs % cat test3.txt @@@@abc abc@@@@@@ @@@@@@@@@@@@abc@@ @ abc@@@ @bcd @ @ @ @ bcd @ @ @ @ @ @@@@bcd bcd @@ @@ @ @ @ @@@ abc abc @@@@@@@@ @ @ cdecde @@@@@@@@@@@@@@@@@@@@@@@ [normal_haskell/]% ./strip < test3.txt abc abc abc@@ @ abc bcd @ @ @ @ bcd @ @ @ @ bcd bcd @@ @@ @ @@@ abc abc @ cdecde
うん、いい感じですね。
tail.hsについて
さて次は以前作成したtail.hsをポイントフリースタイルに書き換える問題。
こんな感じ。
main = do cs <- getContents putStr $ lastNLines 10 cs -- lastNLines n cs = unlines $ takeLast n $ lines cs lastNLines n = unlines . takeLast n . lines -- takeLast n ss = reverse $ take n $ reverse ss takeLast n = reverse . take n . reverse
コメントが修正前のコードです。どちらも引数が一つ減ってます。
fgrep.hsについて
そして最後の問題。以前作成したfgrep.hsをポイントフリースタイルに書き換える。
って、これ8.4の中でやってましたよね?
とりあえず修正後のコードはこんな感じです。
import System import List main = do args <- getArgs cs <- getContents putStr $ fgrep (head args) cs fgrep :: String -> String -> String fgrep pattern = unlines . filter (match pattern) . lines match :: String -> String -> Bool match pattern = any (pattern `isPrefixOf`) . tails