【Haskell】nlコマンドを作成した際の備忘録
今回はLinuxコマンドのnlをHaskellで作成した備忘録です。
よろしければお手元のLinux環境でnlコマンドを打った後に見ていただければと思います。
この記事の概要
私が書いたソースコードとAIに書いてもらったコードを比較して、今後に生かせそうな点をまとめたいと思います。
下記にソースコードへのリンクを添付しておきます。
入出力について
私はHaskellの入出力をあまり分かっておらず、以下のように書きました
main :: IO ()
main = do
putStrLn "ファイルを指定してください"
path <- getLine
withFile path ReadMode $ \handle -> do
contents <- hGetContents handle
let lineList = nlCommand $ lines contents
mapM_ TIO.putStrLn lineList
実際はnl {file path}と書くわけですから、コマンドラインから引数を受け取らなければなりません。それにプラスして今回はData.Textを使っているので、withFileではなくData.Text.IO.readFileを使って直にTextで読み込んだ方が良いです。
これらを修正すると
main :: IO ()
main = do
args <- getArgs
contents <- case args of
[path] -> TIO.readFile path
_ -> TIO.getContents
TIO.putStr $ nl contents
このようになり、かなり簡略化されたのがわかります、これからもData.Textにはお世話になるでしょうから、ここで覚えときたいですね。
無限リストとの合成について
私はzipを用いて無限リストとテキストファイルの各行を合成しようと思ったので、以下のようなコードを書きました。
addLineNum :: [T.Text] -> [(Int, T.Text)]
addLineNum = zip [1 ..] . filter (not . T.null)
これだとfilterが空白行を削除してしまうのでnlと挙動が違います、また返り値がタプルになってしまうので、出力時に不便です。そこでzipWithという関数が存在するらしく、それは一つの関数と二つのリストを受け取って、受け取った関数を二つのリストに適用する関数です。
これを扱うことで返り値がタプルになることなく、不便なく運用できるということです。
下記がAIが書いてくれたお手本です。
nl :: T.Text -> T.Text
nl = T.unlines . zipWith formatLine [1 ..] . T.lines
formatLine :: Int -> T.Text -> T.Text
formatLine n line
| T.null line = T.empty
| otherwise = T.justifyRight 6 ' ' (tshow n) <> "\t" <> line
- T.linesは
linesと一緒で\nで区切ってリストにしてくれます。 - T.justifyRightは数値分右にずらします。
私はタプルをTextにするのに苦労したのですが、zipWithを知っていればもっと簡単でしたね…
tshowについて
tshow :: Int -> T.Text
tshow = T.pack . show
AIによると特定の型をTextに変換する際によく使われるらしいです。
感想
初めてプログラミングやった時みたいでした、chromeと睨めっこして書いてました。
私はずっとAIがあったので、昔の開発を知らないのですが、睨めっこして書いていたら、職場の先輩が知らないライブラリ使って、簡潔に書いてきたみたいな、そんな感じがします。
今はライブラリはAIが使ったのを確認して、挙動を確認すればいいですから、開発前にリファレンスを漁る作業があったと考えるとぎょっとします、その時代のエンジニアにはなれそうにないです…
しばらくは「あー行き詰まった」って時に局所的にAIを使う方針で、Linux commandを書いていこうかなと思います。