ITコンサルの日常

ITコンサル会社に勤務する普通のITエンジニアの日常です。

「Ansi Common Lisp」7章練習問題 - Haskell版

1. ファイル名を引数として、ファイルの各行を構成するストリングをリストとして返す関数を定義せよ。

うーん、相変わらずモナド分からん。。
近いものはできた。

import IO

fileToStrLine :: String -> IO ()
fileToStrLine fileName = do handle <- openFile fileName ReadMode
                            line <- hGetContents handle
                            print . lines $ line
                            hClose handle

main = fileToStrLine "ex7-1.hs"

結果はこう。

["import IO","","fileToStrLine :: String -> IO ()","fileToStrLine fileName = do handle <- openFile \"ex7-1.hs\" ReadMode","                            line <- hGetContents handle","                            print . lines $ line","                            hClose handle","","main = fileToStrLine \"ex7-1.hs\""]

3. コメントを%で示すようなテキストファイルのフォーマットがあるとする。この文字が出現した場合、その行のそれ以降の部分は全て無視される。2つのファイル名を引数とし、第2のファイルに第1のファイルのコメントを除外したコピーを作成する関数を定義せよ。

import IO
import List
import Maybe

removePercentComment :: String -> String -> IO ()
removePercentComment inFile outFile = do
      inHandle <- openFile inFile ReadMode
      outHandle <- openFile outFile WriteMode
      cs <- hGetContents inHandle
      hPutStr outHandle $ unlines $ map removeAfterPercent $ lines cs
      hClose inHandle
      hClose outHandle

removeAfterPercent :: String -> String
removeAfterPercent str = if index == -1 then str else take index str
  where index = charIndex '%' str

charIndex :: Char -> String -> Int
charIndex c str = if index == Nothing then -1 else fromJust index
  where index = elemIndex c str


main = removePercentComment "infile" "outfile"

IOモナドが分かってきたような、分かってないような。

(2008.08.28 追記)
コメント欄でtakeWhileを使ってはどうかとご指摘いただきましたので、
書き直してみました。

import IO
import List
import Maybe

removePercentComment :: String -> String -> IO ()
removePercentComment inFile outFile = do
      inHandle <- openFile inFile ReadMode
      outHandle <- openFile outFile WriteMode
      cs <- hGetContents inHandle
      hPutStr outHandle $ unlines $ map (\str -> takeWhile (/='%') str) $ lines
cs
      hClose inHandle
      hClose outHandle

main = removePercentComment "infile" "outfile"

すっきり!
結果はこう。

taka@taka-desktop:~/lisp$ more infile
abc % def
%ghi
%%
jklmn
opqrs%
taka@taka-desktop:~/lisp$
taka@taka-desktop:~/lisp$ more outfile
abc


jklmn
opqrs
taka@taka-desktop:~/lisp$

どうでもいいが、Unixでファイルを見るときに使うコマンドで、
vi(view)派とmore(less)派がいるとかって話があったなあ。
僕は断然more派なんですが、まあきっとemacs派とかいろいろあるんでしょうね。

4. 浮動小数点数の2次元配列を引数として、桁をそろえて表示する関数を定義せよ。各要素は、小数点以下2桁を表示し、フィールドの幅は10字分とする。(全てこれで収まるものと仮定せよ。) 関数array-dimensions(配列を引数として、各次元の長さ(整数)をリストにして返す)が必要となるだろう。

import Text.Printf

main = printFloatArray2d ([[1.234, 2.34], [3.4567, 4]] :: [[Float]])

printFloatArray2d :: [[Float]] -> IO a
printFloatArray2d (x:[]) = printFloatArray x
printFloatArray2d (x:xs) = do printFloatArray x
                              printFloatArray2d xs

printFloatArray :: [Float] -> IO a
printFloatArray (x:[]) = printf "%10.2f\n" x
printFloatArray (x:xs) = do printf "%10.2f " x
                            printFloatArray xs

結果はこう。

      1.23       2.34
      3.46       4.00
*** Exception: Prelude.undefined

一応出来てるけど、なんかエラーも出てる。
http://itpro.nikkeibp.co.jp/article/COLUMN/20080630/309793/?ST=develop&P=2
に色々書いてあるけど、STモナドとか出てきてお腹いっぱい。。
ま、追々かな。