2010年3月2日火曜日

Haskell で文字列の先頭と末尾から空白を取り除く

Ruby の strip メソッド

Ruby で文字列の先頭と末尾の空白を取るメソッドは strip 。ファイルの各行に対して処理したいなら、

while line=gets
  puts line.strip
end

Haskell で全く同じ関数あったかな?

 

Haskell で strip 関数の定義

自分で書くなら、まずは先頭の空白を削除する関数 lstrip を定義。次に、文字列を反転させて lstrip を適用すれば、末尾の空白を削除する rstrip 関数ができる。二つを組み合わせてできあがり。

lstrip 関数の実装。先頭要素が空白なら値を返さず、そうでなければそのまま返せばいい。

lstrip [] = []
lstrip xs'@(x:xs) | x == ' '  = lstrip xs
                  | otherwise = xs'

rstrip 関数はリストを逆転させてから lstrip 関数を適用する。

rstrip = reverse . lstrip . reverse

二つを組み合わせる。

strip = lstrip . rstrip

 

日本語のファイルに対して

日本語の書かれたファイルから入力するためには、UTF8モジュールを読み込んでおく。

import qualified System.IO.UTF8 as U

標準入力にファイルを指定して、各行に stirp 関数を適用。

main = U.getContents >>= U.putStr. trim
trim = unlines . map strip . lines

 

ここまでのコード
import qualified System.IO.UTF8 as U

lstrip [] = []
lstrip xs'@(x:xs) | x == ' '  = lstrip xs
                  | otherwise = xs'
                                  
rstrip = reverse . lstrip . reverse

strip = lstrip . rstrip

main = U.getContents >>= U.putStr. trim
trim = unlines . map strip . lines

 

より汎用的な関数へ

次に、半角の空白に加え、全角の空白も削除したいとする。

上記の lstrip 関数のガードに、以下のように条件を加えてもいいけれど、

x == ' ' || x == ' '

これだと条件が増えるごとに lstrip 関数を変更しなければならない。条件を関数に渡せるようにした方が後々使い回しができる。 関数が引数として渡せる言語では Strategy パターンのオンパレード。その中の言うなれば述語パターンみたいなもの。(cf. Haskell の Prelude 散策 (2) - 述語)

lstrip _ [] = []
lstrip p xs'@(x:xs) | p x = lstrip p xs
                    | otherwise = xs'

後は、この変更に合わせて、

import qualified System.IO.UTF8 as U

lstrip _ [] = []
lstrip p xs'@(x:xs) | p x = lstrip p xs
                    | otherwise = xs'

rstrip p = reverse . lstrip p . reverse

strip p = lstrip p . rstrip p

main = U.getContents >>= U.putStr. trim
trim = unlines . map (strip isSpace) . lines
isSpace x = x `elem` [' ', ' '] 

 

takeWhile, dropWhile

Trim (programming) - Wikipedia, the free encyclopedia には、

import Data.Char (isSpace)
trim      :: String -> String
trim      = f . f
   where f = reverse . dropWhile isSpace
あ~、dropWhile を使えばよかったのか。(@_@; Prelude の関数ですらスッと連想できないなぁ。 (+_+)