Old/openspace.timedia.co.jp/marthon/RRB-all
- 文字列を複製する
- 文字列を反復する
- 文字列の長さを得る
- 文字列を検索する
- 特定の文字・文字列の出現回数を調べる
- 文字列の出現頻度を調べる
- 文字列を比較する
- 文字列が空行か調べる
- 数値を表す文字列か調べる
- 文字列を数値に変換する
- 文字が大文字か小文字かを調べる
- 文字を大文字・小文字に変換する
- 文字列の一部を取り出す
- 最初にマッチした部分を取り出す
- マッチした部分すべてを取り出す
- 正規表現の「()」にマッチした部分を取り出す
- 正規表現にマッチした部分の前後の文字列を取り出す
- 特定の文字を含む部分の長さを調べる
- 文字列を段落に分ける
- 文字列を行に分ける
- 文字列を単語に分ける
- 正規表現で文字列を分割する
- 文字列を文字ごとに処理する
- 文字列を連結する
- 正規表現にマッチした部分を置換する
- 特定の文字(列)を置換する
- 特定の文字をエスケープする
- 文字を二重にしてエスケープする
- 不可視文字を表示する
- 特定のエスケープ方式にする
- 文字列の一部を取り除く
- 文字列の先頭・末尾から空白を取り除く
- タブと半角空白文字を変換する
- インデントを変更する
- 文字列の末尾に文字列を追加する
- 文字列をn文字ずつに分割する
- 文字列を最大n文字に切り詰める
- メッセージダイジェストを作成する
- 正規表現のメタ文字をエスケープする
- いろいろなテキストフォーマットを解析する
- 「,」で区切られたデータ(CSV)を処理する
- XML を解析する
- 値がいくつかあり、それを格納した配列を作る
- 多次元配列を作成する
- 配列やハッシュの全要素を順に処理する
- 配列の要素を変更する
- 配列の要素をシャッフルする
- 配列の要素数を調べる
- 配列の要素の出現回数を調べる
- 配列から複数の要素を一度に取得する
- 配列のインデックスと要素のペアを取得する
- 配列から重複する要素を取り除く
- 配列から条件を満たす要素を取得する
- 配列の要素を検索する
- 2つの配列に共通する要素を調べる
- 2 つの配列の差を取る
- 配列を連結する
- 配列の要素の順序を逆転する
- 一定範囲の整数を要素とする配列を作成する
- 配列を(「,」つきで)連結して文字列にする
- ハッシュに要素を追加する
- ハッシュ内にキーが存在するか調べる
- ハッシュの要素を削除する
- ハッシュの要素を挿入した順に取り出す
- 配列やハッシュをソートする
- ハッシュをマージする
- 巨大で疎な配列を使う
- スタックやキューを使う
- パス名がパターンにマッチするか調べる。
- 現在のディレクトリ名を取得する
- 現在のディレクトリを変更する
- 回数を指定して処理を繰り返す
- 最大値・最小値を求める
- 数値の総和を求める
- 絶対値を求める
- 割り算の余りを求める
- べき乗を求める
- 最大公約数と最小公倍数を求める
- 平方根を求める
- 三角関数を計算する
- 直交座標を局座標に変換する
- 対数計算を行う
- 浮動小数点を丸める
- 複素数を使う
- 有理数を使う
- コマンドライン引数を解析する
- TCP クライアントを作る
- TCP サーバを作る
文字列を複製する
dup s = s ++ s
pure functional だと実態が複製されたかどうかは関係ない。
文字列を反復する
Prelude> foldr1 (++) $ replicate 3 "abc"
"abcabcabc"
replicate に 0 をわたすとこける。
Prelude> concat $ replicate 3 "abc"
"abcabcabc"
でどうよ。
strReplicate :: Int -> String -> String
strReplicate n s = iterate (s++) "" !! n
なんてのはどうよ。
strReplicate :: Int -> String -> String
strReplicate n s = iterate (showString s) "" !! n
もあるよ。scanl でいいんじゃない? –t15u
Prelude> scanl (++) [] (repeat "abc") !! 3
"abcabcabc"
強引にunfoldrで書いてみた。–さかい
strReplicate :: Int -> String -> String
strReplicate _ [] = []
strReplicate n [email protected](x:xs) = unfoldr phi (n,s)
where phi (m,y:ys) = Just (y,(m,ys))
phi (0,[]) = Nothing
phi (m,[]) = Just (x,(m-1,xs))
unfoldr phi x = case phi x of
Nothing -> []
Just (a,y) -> a : unfoldr phi y
うっ。。。–nobsun
さかいさんのを参考に別版を書いてみました。
strReplicate :: Int -> String -> String
strReplicate n s = unfoldr f (n,s,s)
where f (_,_,[]) = Nothing
f (0,_,_) = Nothing
f (n,[x],s) = Just (x,(n-1,s,s))
f (n,x:xs,s) = Just (x,(n,xs,s))
文字列の長さを得る
Prelude> length "abc"
3
文字列を検索する
パターンが含まれるかどうか調べる
import Text.Regex
import Data.Maybe
isMatch pat str = isJust $ matchRegex (mkRegex pat) str
パターンが出現する最初のバイト位置を調べる
パターンが出現する最後のバイト位置を調べる
マッチの詳しい情報を得る
文字列中に複数マッチする
import Text.Regex
rxScan pat str
= case matchRegexAll (mkRegex pat) str of
Just (before, "", "", _) -> []
Just (before, "", after, _) -> rxScan pat (tail after)
Just (before, matched, after, _) -> matched : rxScan pat after
Nothing -> []
パターンを含む行のみを処理する
パターンを含む行に f を適用し、残りはそのまま。
grep :: String -> (String -> String) -> [String] -> [String]
grep pat f ss = [ case matchRegex (mkRegex pat) line of
Just _ -> f line
Nothing -> line
| line <- ss
]
grep pat f ss = [ (case matchRegex (mkRegex pat) line of {Just _ -> f; _-> id}) line | line <- ss ]
ううむ。
grep pat f = map (\ l -> case matchRegex (mkRegex pat) l of {Nothing->id;_->f} $ l)
特定の文字・文字列の出現回数を調べる
count x = length . filter (x==)
count xs [] = 0
count xs ys = (if isPrefixOf xs ys then (1+) else id) (count $ tail ys)
文字列の出現頻度を調べる
count xs [] = 0
count xs ys = (if isPrefixOf xs ys then (1+) else id) (count xs $ tail ys)
こういう入力例だと効率がすごく悪い。KMPでやれば少しはましか。
count "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaab" (take 9999 $ repeat 'a')
0
Excelのデータで特定の文字列の出現頻度を調べる。カット&ペーストでa.txtファイルを作る。
$KCODE='EUC'
f=open("a.txt")
txt=f.read
ar=%w(田中 和田)
ar.each {|n|
print n,"::"
puts txt.scan(n).length
}
f.close
出力結果
$ ruby scan.rb
田中::5
和田::2
文字列を比較する
Main> "foo" == "bar"
False
Main> "foo" == "foo"
True
Main> "foo" < "bar"
False
文字列が空行か調べる
本当の空行
main = getContents >>= print . filter (""==) . lines
空白のみの行も空行とみなす
main = getContents >>= print . filter (all isSpace) . lines
数値を表す文字列か調べる
isNumStr :: String -> Bool
isNumStr "" = False
isNumStr [email protected](x:xs)
| x `elem` "+-" = all isDigit xs
| otherwise = all isDigit xxs
8進とか?16進とかは?
isNumStr "" = False
isNumStr [email protected](x:xs)
| x `elem` "+-" = isNumStr' xs
| otherwise = isNumStr' xxs
where isNumStr' "0" = True
isNumStr' ('0':x:xs)
| toLower x == 'x' = all isHexDigit xs
| toLower x == 'o' = all isOctDigit xs
| toLower x == 'b' = all (flip elem "01") xs
| otherwise = all isOctDigit (x:xs)
isNumStr' xs = all isDigit xs
こんなもんでしょうか。 – y.hanatani
文字列を数値に変換する
read "123" :: Int
文字が大文字か小文字かを調べる
Prelude> :module +Char
Prelude Char> isLower 'a'
True
Prelude Char> isLower 'A'
False
Prelude Char> isUpper 'a'
False
Prelude Char> isUpper 'A'
True
文字を大文字・小文字に変換する
Prelude> :module +Char
Prelude Char> toUpper 'a'
'A'
Prelude Char> toLower 'A'
'a'
Prelude Char> map toUpper "aBcDeFg"
"ABCDEFG"
Prelude Char> map toLower "aBcDeFg"
"abcdefg"
文字列の一部を取り出す
Prelude> take 3 . drop 3 $ "abcdefghi"
"def"
最初にマッチした部分を取り出す
正規表現と同じことをリスト処理関数でやってみる。
> take 3 $ head $ filter (isPrefixOf "a") $ tails "xxxabcfffpat"
"abc"
head が失敗した時がちょっといや。
> (case filter (isPrefixOf "a") $ tails "xxxabcfffpat" of (x:_) -> Just x; _ -> Nothing) >>= Just . take 3
Just "abc"
無理矢理回避してみる。失敗した時は Nothing
マッチした部分すべてを取り出す
import Text.Regex
import Data.List
matchRegexList rx xs
= unfoldr f xs
where f x = matchRegexAll rx x >>= (\ (_, m, rest, _) -> Just (m, rest))
正規表現の「()」にマッチした部分を取り出す
import Text.Regex
import Data.List
matchRegexList' rx xs
= concat $ unfoldr f xs
where f x = matchRegexAll rx x >>= (\ (_, _, rest, m) -> Just (m, rest))
正規表現にマッチした部分の前後の文字列を取り出す
import Text.Regex
> matchRegexAll (mkRegex "ab*c") "xabbcz" >>= \ (b, _, a, _) -> Just (b, a)
Just ("x","z")
特定の文字を含む部分の長さを調べる
matchingLength xs ys = length [y | y <- ys, elem y xs]
これは
matchingLength xs = length . filter (flip elem xs)
と同じ。 xs も消せないの? – t15u
matchingLength :: Eq a => [a] -> [a] -> Int matchingLength = (length .) . filter . (flip elem)
この場合型宣言は必要。haskell-jp:467
文字列を段落に分ける
parags :: [String] -> [[String]]
parags [] = []
parags xs = p:parags xs'
where
(p, xs') = (applySnd (dropWhile (""==)) . break (""==)) xs
applySnd f (x,y) = (x,f y)
これだと、複数の空白だけでなりたつパラグラフできちゃうよ。 こんなのではどうでしょう。
paragraphs :: String -> [[String]]
paragraphs = parags . map trimSpace . lines
trimSpace :: String -> String
trimSpace = reverse . dropWhile isSpace . reverse . dropWhile isSpace
parags :: [String] -> [[String]]
parags [] = []
parags xs = p : parags (dropWhile ("" ==) ps)
where
(p,ps) = break ("" ==) xs
これじゃ、インデントが消えちゃうじゃん。ダメポ。
これで動くかな。=> 動くみたい。
parags = filter (not . null) . map (dropWhile (all isSpace)) . groupBy (\_ x -> not $ all isSpace x)
List> parags $ lines testdata
[[" A "," AA "],["B","BB"],["C"]]
テストデータ
testdata = " \n\n A \n AA \n \n\nB\nBB\n\n\n\n \n\nC\n\n"
文字列を行に分ける
Prelude> lines "foo\nbar\nbaz"
["foo","bar","baz"]
lines は Prelude で定義されている
lines :: String -> [String]
lines "" = []
lines s = let (l,s') = break ('\n'==) s
in l : case s' of [] -> []
(_:s'') -> lines s''
文字列を単語に分ける
Prelude> words "foo bar baz"
["foo","bar","baz"]
words は Prelude で定義されている
words :: String -> [String]
words s = case dropWhile isSpace s of
"" -> []
s' -> w : words s''
where (w,s'') = break isSpace s'
正規表現で文字列を分割する
import Text.Regex
import Data.List
splitWithRx rstr str = splitWithRx' str
where
rx = mkRegex rstr
splitWithRx' str = case matchRegexAll rx str of
Just (b, _, a, _) -> b : splitWithRx' a
Nothing -> [str]
splitWithRx'' rs str = unfoldr f str
where
rx = mkRegex rs
f s = matchRegexAll rx s >>= (\(b, _, a, _) -> Just (b, a))
空文字列にマッチする正規表現を与えるのは危険。
ふたつめの方法だと最後の要素が消えてしまう。最後にマッチするスプリッタ があればうまくいくんだけど。
文字列を文字ごとに処理する
Prelude> sequence_ [print x | x <- "abc"]
'a'
'b'
'c'
文字列を連結する
リストですから,リストの連結の演算子 ++ で連結できます.
Prelude> "hello " ++ "wor" ++ "ld"
"hello world"
正規表現にマッチした部分を置換する
import Text.Regex
sub re s str = case matchRegexAll re str of
Just (b, _, a, _) -> b ++ s ++ a
_ -> str
gsub re s str = case matchRegexAll re str of
Just (b, _, a, _) -> b ++ s ++ gsub re s a
_ -> str
特定の文字(列)を置換する
ruby 版とは挙動が違います。str の中から xs を見つけて ys に置換します。
tr str [] _ = str
tr str xs ys = tr' str xs []
where tr' str [] _ = ys ++ tr' str xs []
tr' (s:str) (z:zs) rs
| s == z = tr' str zs (s:rs)
| otherwise = (reverse rs) ++ s:tr' str xs []
tr' [] _ rs = reverse rs
> tr "ruby 1.8.1" "." "_"
"ruby 1_8_1"
> tr "ruby 1ten6ten8" "ten" "."
"ruby 1.6.8"
特定の文字をエスケープする
escape xs y = flip (>>=) f
where f x | x `elem` xs = y:x:[]
| otherwise = x:[]
unescape x (y:yy:ys)
| x == y = yy:unescape x ys
| True = y:unescape x (yy:ys)
unescape x ys = ys
> escape "!?_" '_' "Hi! How_are_you?"
"Hi_! How__are__you_?"
文字を二重にしてエスケープする
不可視文字を表示する
特定のエスケープ方式にする
文字列の一部を取り除く
正規表現にマッチした部分を取り除く
import Text.Regex
sub rx x = matchRegexAll (mkRegex rx) x
>>= (\(before, _, after, _) -> Just (before ++ after))
*Main> sub "a+" "aabbcaa"
Just "bbcaa"
*Main> sub "b+" "aabbcc"
Just "aacc"
*Main> sub "b*" "aabbcc"
Just "aabbcc"
*Main> sub "b+" "aacc"
Nothing
+は最長マッチ、*は最短マッチのようだ。 => これは間違い。 単に先頭でマッチしてしまうだけ。
特定の文字を取り除く
import List
delete' :: Eq a => a -> [a] -> [a]
delete' _ [] = []
delete' e (x:xs)
| e == x = delete' e xs
| True = x : delete' e xs
↑これはあほ
delete' e xs = filter (e/=) xs
List> delete' 'o' "gooooooooogle"
"ggle"
squeeze :: Eq a => a -> [a] -> [a]
squeeze _ [] = []
squeeze e [x] = [x]
squeeze e (x:[email protected](xx:_))
| e == x && e == xx = squeeze e xxs
| otherwise = x : squeeze e xxs
Prelude> squeeze 'g' "gggooogle"
"gooogle"
文字列先頭・末尾の文字を取り除く
List> init "bar"
"ba"
List> tail "bar"
"ar"
インデックスを指定して取り除く
applyFst f (x, y) = (f x, y)
main = print $ applyFst init $ splitAt 3 "abcde"
("ab","de")
文字列の先頭・末尾から空白を取り除く
ナイーブな方法
Char> (dropWhile isSpace . reverse . dropWhile isSpace . reverse) " foo bar "
"foo bar"
せっかくだから、関数にしよう。
trimSpace :: String -> String
trimSpace = dropWhile is Space . reverse . dropWhile isSpace . reverse
タブと半角空白文字を変換する
untabify :: Int -> String -> String
untabify tabstop str = concat (unfoldr phi (0,str))
where phi (n, '\t':s)
= Just (replicate (tabstop - (n `mod` tabstop)) ' ', (0,s))
phi (n, c:s) = Just ([c], (n+1,s))
phi (_, []) = Nothing
tabify :: Int -> String -> String
tabify tabstop str = f 0 str
where f n ('\t':str) = f (n+tabstop) str
f n (' ':str) = f (n+1) str
f n str = case n `divMod` tabstop of
(t,s) ->
replicate t '\t' ++ replicate s ' ' ++
case str of
[] -> []
x:xs -> x : f 0 xs
Ruby版も同じだと思うけど、逆変換になってないな。
レシピ1.7 も参照。
インデントを変更する
インデントする関数
-- 文字列の各行を半角空白文字n個分インデントする
indent0 :: Int -> String -> String
indent0 n = unlines . map (replicate n ' ' ++) . lines
-- タブ対応版
indent :: Maybe Int -> Int -> String -> String
indent (Just tabstop) n = unlines . map f . lines
where f line = tabify tabstop (replicate n ' ' ++ untabify tabstop line)
indent Nothing n = indent0 n
インデントを下げる関数
-- 文字列の各行を半角空白文字n個分アンインデントする
unindent0 n = unlines . map (f n) . lines
where f 0 s = s
f n (' ':s) = f (n-1) s
f n s = s
-- タブ対応版
unindent :: Maybe Int -> Int -> String -> String
unindent (Just tabstop) n = tabify tabstop . unindent0 n . untabify tabstop
unindent Nothing n = unindent0 n
文字列の末尾に文字列を追加する
> "abc" ++ "def"
"abcdef"
文字列をn文字ずつに分割する
import Data.List
divide n xs = case splitAt n xs of
(a, []) -> [a]
(a, b) -> a : foo n b
Main> foo 3 ['a'..'z']
["abc","def","ghi","jkl","mno","pqr","stu","vwx","yz"]
文字列を最大n文字に切り詰める
*Main> take 5 "abcdefgh"
"abcde"
*Main> take 5 "\x3042\x3044\x3046\x3048\x304a\x304b\x304d"
"\12354\12356\12358\12360\12362"
*Main> take 5 "abc"
"abc"
*Main> take 5 "ab\n\ncde"
"ab\n\nc"
*Main> take 5 ""
""
メッセージダイジェストを作成する
- http://web.comlab.ox.ac.uk/oucl/work/ian.lynagh/md5/
- http://web.comlab.ox.ac.uk/oucl/work/ian.lynagh/sha1/
正規表現のメタ文字をエスケープする
escape :: [Char] -> Char -> String -> String
escape meta esc str = concat [ if c == esc || c `elem` meta
then [esc, c]
else [c]
| c <- str
]
foldr版
escape :: [Char] -> Char -> String -> String
escape meta esc = foldr phi []
where phi c s = if c == esc || c `elem` meta
then esc:c:s
else c:s
いろいろなテキストフォーマットを解析する
URI
Network.URI の parseURI :: String -> Maybe URI でパースできる
Prelude> :module +Network.URI
Prelude Network.URI> parseURI "http://[email protected]:80/path/to/file?q=query#part"
Preludeng package network ... linking ... done.
Preludehttp://[email protected]:80/path/to/file?q=query#part
Prelude Network.URI> parseURI "http://[email protected]:80/path/to/file?q=query#part" >>= scheme
Prelude Network.URI> parseURI "http://[email protected]:80/path/to/file?q=query#part" >>= return . scheme
Prelude"http"
Prelude Network.URI> parseURI "http://[email protected]:80/path/to/file?q=query#part" >>= return . authority
Prelude"[email protected]:80"
Prelude Network.URI> parseURI "urn:isbn:4797324295"
Just urn:isbn:4797324295
メール
RD
RDoc
Rubyのソースコード
その他、プログラム言語のソースコード
「,」で区切られたデータ(CSV)を処理する
区切り記号での分割
divide x = unfoldr (\xs -> case break (x==) xs of
(_, []) -> Nothing
(p, q) -> Just (p, tail q)
)
実行例:
Main> divide ',' "ab,cd,ef,g,,hi,,,"
["ab","cd","ef","g","","hi","",""]
特に区切り記号のエスケープなどは考えていない。 あ、これじゃ最後の要素がやっぱり消えてしまう。 こういうとき、unfold ってどう使えばいいんだろ。 – Y. Hanatani 2004/08/02 12:41:04 JST
XML を解析する
HaXml を使ってみる。
あおきにっきの RDF から title 要素を抜き出す。
import Text.XML.HaXml
import Text.XML.HaXml.Pretty
import System.IO
import System.Environment
main = do ~[arg] <- getArgs
h <- openFile arg ReadMode
str <- hGetContents h
case xmlParse "err.txt" str of
(Document _ _ (Elem _ _ contents))
-> mapM_ print $ map getTitle contents
hClose h
getTitle = map content . (keep /> tag "title" /> txt)
適当に aoki.rdf というのを用意して、
$ runhugs rdf.hs aoki.rdf | nkf -e
[あおきにっき つっこみつき]
[2004-11-14のツッコミ[3] (青木)]
[るびま 3 号]
[2004-11-14のツッコミ[2] (たむら)]
[2004-11-14のツッコミ[1] (arton)]
[Linux本 (2)]
[Linuxプログラミング本]
[daily build]
[反省]
[ほしみ]
[2004-11-12のツッコミ[4] (青木)]
[2004-11-12のツッコミ[3] (yuco)]
[2004-11-12のツッコミ[2] (青木)]
[tDiary / コメント SPAM 避けチェックボックスパッチ]
[2004-11-12のツッコミ[1] (halchan)]
[2004-11-08のツッコミ[2] (青木)]
値がいくつかあり、それを格納した配列を作る
book :: Array Int String
book = array (0,2) (zip [0..] ["title", "author", "publisher"])
listArray を使うと zip がいらない。
book :: Array Int String
book = listArray (0,2) ["title" , "author" , "publisher"]
汎用オヤジ版 – t15u
mkArray :: Int -> [a] -> Array Int a
mkArray i0 ls = listArray (i0, i0+length ls-1) ls
多次元配列を作成する
array ((1, 1), (9, 9)) [((x, y), z) | x <- [1..9], y <- [1..9], let z = x == y]
リスト内包表記の中にletが書けるって初めて知ったよ。
リストのリストから二次元配列を作ろうと思ったら 添字の順序に注意が必要。 transpose を使うと縦横を反転できる。
xs = [[1,2,3]
,[4,5,6]
,[7,8,9]
]
array ((1, 1), (3, 3)) $ zip [(x, y) | y <- [1..3], x <- [1..3]] $ concat xs
配列やハッシュの全要素を順に処理する
リストに直してからあとはお好きなように。
Data.Array
elems :: Ix i => Array i e -> [e]
assocs :: Ix i => Array i e -> [(i, e)]
Data.HashTable
toList :: HashTable key val -> IO [(key, val)]
Data.FiniteMap
fmToList :: FiniteMap key elt -> [(key, elt)]
keysFM :: FiniteMap key elt -> [key]
eltsFM :: FiniteMap key elt -> [elt]
配列の要素を変更する
Array> listArray (1,26) ['a'..'z'] // [(4, '?'), (6, '#')]
array (1,26) [(1,'a'),(2,'b'),(3,'c'),(4,'?'),(5,'e'),(6,'#'),(7,'g'),(8,'h'),(9
,'i'),(10,'j'),(11,'k'),(12,'l'),(13,'m'),(14,'n'),(15,'o'),(16,'p'),(17,'q'),(1
8,'r'),(19,'s'),(20,'t'),(21,'u'),(22,'v'),(23,'w'),(24,'x'),(25,'y'),(26,'z')]
配列の要素をシャッフルする
リストのシャッフル
import Random
shuffle :: (RandomGen g) => g -> [a] -> ([a], g)
shuffle g l = f (g, l, length l)
where f (g,l,0) = ([],g)
f (g,l,n) =
case randomR (0,n-1) g of
(pos,g') ->
case pickup pos l of
(x,l') ->
let (dest,g'') = f (g',l',n-1)
in (x:dest, g'')
pickup :: Int -> [a] -> (a,[a])
pickup 0 (a:ax) = (a,ax)
pickup n (a:ax) = case pickup (n-1) ax of
(x,ax') -> (x,a:ax')
pickup _ _ = error "shouldn't happen"
import Array
import System.Random
shuffle :: [a] -> IO [a]
shuffle ls
= do { arr' <- mksff n (listArray (0,n) ls)
; return (elems arr')
}
where
n = length ls - 1
mksff i a = if i == 0
then return a
else do { j <- randomRIO (0,i-1)
; mksff (i-1) (a//[(j,a!i),(i,a!j)])
}
配列の要素数を調べる
bounds か length . indices
配列の要素の出現回数を調べる
import Data.FiniteMap
test = "haskeller"
count xs = foldl f emptyFM xs
where f fm x = addToFM fm x (1 + lookupWithDefaultFM fm 0 x)
main = print $ fmToList $ count test
[('a',1),('e',2),('h',1),('k',1),('l',2),('r',1),('s',1)]
配列ならば elems でリストに直してから適用すればいい。
配列から複数の要素を一度に取得する
-fglasgow-exts
indexes arr xs = array (1,length xs) [(n, arr ! x) | x <- xs | n <- [1..]]
配列のインデックスと要素のペアを取得する
assocs
配列から重複する要素を取り除く
リストから重複する要素を取り除く
uniq :: Eq a => [a] -> [a]
uniq [] = []
uniq (x : xs) = x : uniq [y | y <- xs, y /= x]
*Main> uniq "abccb"
"abc"
nub (名前の由来:謎) 使え
Prelude> :module List
Prelude List> nub "abccb"
"abc"
隣接している要素のみを比較
uniq :: Eq a => [a] -> [a]
uniq (x:xx:xs)
| x == xx = uniq (x:xs)
| otherwise = x:uniq (xx:xs)
uniq xs = xs
*Main> uniq "abccb"
"abcb"
配列から条件を満たす要素を取得する
filter
配列なら elems してから filter
配列の要素を検索する
www.sampou.org HowTo:List より
List> elem 'a' "baca"
True
List> elemIndex 'a' "baca"
Just 1
List> elemIndices 'a' "baca"
[1,3]
List> find even [1,2,3,4]
Just 2
List> findIndex even [1,1,2,3]
Just 3
List findIndices even [1,2,3,4]
[1,3]
2つの配列に共通する要素を調べる
List> union [1,2,3] [2,3,4]
[1,2,3,4]
List> intersect [1,2,3] [2,3,4]
[2,3]
2 つの配列の差を取る
[x | x <- "Hello, World", not $ elem x "abcde"]
"Hllo, Worl"
配列を連結する
一次元の Int な配列なら
append :: Array Int a -> Array Int a -> Array Int a
append a a' = array (x, y + (w - z) + 1)
(assocs a ++ [((i + y - z + 1), e) | (i, e) <- assocs a'])
where
(x, y) = bounds a
(z, w) = bounds a'
リストなら
(++)
配列の要素の順序を逆転する
一次元配列の場合
import Array
reverseArray a = ixmap (x, y) (\z -> y + x - z) a
where
(x, y) = bounds a
Main> reverseArray (array (2, 10) [(i, i) | i <- [2..10]])
array (2,10) [(2,10),(3,9),(4,8),(5,7),(6,6),(7,5),(8,4),(9,3),(10,2)]
リストの場合
Main> reverse [1..9]
[9,8,7,6,5,4,3,2,1]
一定範囲の整数を要素とする配列を作成する
import IX
> range (1,10)
[1,2,3,4,5,6,7,8,9,10]
配列を(「,」つきで)連結して文字列にする
List> intersperse ',' "baca"
"b,a,c,a"
List> foldr1 (++) $ intersperse ", " ["Haskell", "Scheme", "OCaml"]
"Haskell, Scheme, OCaml"
ハッシュに要素を追加する
Data.HashTable
insert hash key elt
Data.FiniteMap
addToFM fm key elt
ハッシュ内にキーが存在するか調べる
lookup :: HashTable key val -> key -> IO (Maybe val)
lookupFM :: Ord key => FiniteMap key elt -> key -> Maybe elt
ハッシュの要素を削除する
delete :: HashTable key val -> key -> IO ()
delFromFM :: Ord key => FiniteMap key elt -> key -> FiniteMap key elt
delListFromFM :: Ord key => FiniteMap key elt -> [key] -> FiniteMap key elt
ハッシュの要素を挿入した順に取り出す
リストを併用する
配列やハッシュをソートする
Data.FiniteMap の fmToList の出力結果は sorted.
ハッシュをマージする
plusFM :: Ord key => FiniteMap key elt -> FiniteMap key elt -> FiniteMap key elt
*Main Data.FiniteMap> fmToList $ plusFM (listToFM [(1,1)]) (listToFM [(1,2)])
[(1,2)]
第二引数の要素が優先されるようです。
巨大で疎な配列を使う
Data.FiniteMap
か
Data.HashTable
を使う
スタックやキューを使う
リスト2つを使った実装
nullQ :: Queue a
nullQ = Q [] []
addQ :: Queue a -> a -> Queue a
addQ (Q xs ys) y = Q xs (y:ys)
removeQ :: Queue a -> (a, Queue a)
removeQ (Q [] []) = error "Queue is null"
removeQ (Q (x:xs) ys) = (x, Q xs ys)
removeQ (Q [] ys) = (head r, Q (tail r) []) where r = reverse ys
リスト2つを使った実装 その2
-- invariant: forall (Q xs ys). null xs => null ys
data Queue a = Q ![a] ![a]
nullQ :: Queue a
nullQ = Q [] []
addQ :: Queue a -> a -> Queue a
addQ (Q [] ys) y = Q [y] ys
addQ (Q xs ys) y = Q xs (y:ys)
removeQ :: Queue a -> Maybe (a, Queue a)
removeQ (Q [] _) = Nothing
removeQ (Q (x:xs) ys) = Just (x, q xs ys)
where q [] ys = Q (reverse ys) []
q xs ys = Q xs ys
リスト1つと関数1つを使った実装
-- invariant: forall Q xs f. null xs => f==id
data Queue a = Q ![a] !([a] -> [a])
nullQ :: Queue a
nullQ = Q [] id
addQ :: Queue a -> a -> Queue a
addQ (Q [] f) x = Q [x] f
addQ (Q xs f) x = Q xs (f . (x:))
removeQ :: Queue a -> Maybe (a, Queue a)
removeQ (Q [] _) = Nothing
removeQ (Q (x:xs) f) = Just (x, q xs f)
where q [] f = Q (f []) id
q xs f = Q xs f
Data.Queue みたいなのはない DData.Queue
パス名がパターンにマッチするか調べる。
data Fmatch = C Char
| R [(Char, Char)]
| NR [(Char, Char)]
| OC
| AC deriving Show
fmatch :: [Fmatch] -> String -> Bool
fmatch (C x:xs) (y:ys) = x == y && fmatch xs ys
fmatch (R (x:xx):xs) (y:ys) = fst x <= y && y <= snd x && fmatch (R xx:xs) ys
fmatch (NR (x:xx):xs) (y:ys) = (y < fst x || snd x < y) && fmatch (NR xx:xs) ys
fmatch (OC:xs) (y:ys) = fmatch xs ys
fmatch (AC:xs) (y:ys) = fmatch (AC:xs) ys || fmatch xs (y:ys)
fmatch [] [] = True
fmatch _ _ = False
mkFmatch :: String -> [Fmatch]
mkFmatch ('?':xs) = OC:mkFmatch xs
mkFmatch ('*':xs) = AC:mkFmatch xs
mkFmatch ('[':'^':xs) = cons $ applyFst NR $ mkFmatch' xs
mkFmatch ('[':xs) = cons $ applyFst R $ mkFmatch' xs
mkFmatch (x:xs) = C x:mkFmatch xs
mkFmatch "" = []
mkFmatch' :: String -> ([(Char, Char)], [Fmatch])
mkFmatch' (x:'-':xx:xs) = applyFst ((x, xx):) $ mkFmatch' xs
mkFmatch' (']':xs) = ([], mkFmatch xs)
mkFmatch' (x:xs) = applyFst ((x, x):) $ mkFmatch' xs
cons (x, xs) = x:xs
applyFst f (x, y) = (f x, y)
現在のディレクトリ名を取得する
System.Directory の getCurrentDirectory :: IO FilePath を使う
Prelude> :module +System.Directory
Prelude System.Directory> getCurrentDirectory >>= putStrLn
/home/sakai/
現在のディレクトリを変更する
System.Directory の setCurrentDirectory :: FilePath -> IO () を使う。
Prelude> :module +System.Directory
Prelude System.Directory> setCurrentDirectory "/usr"
Prelude System.Directory> getCurrentDirectory >>= putStrLn
/usr
ビット演算を行える型のクラスは Data.Bits のBitsクラスです。
Prelude> :module +Data.Bits
Prelude Data.Bits> :info Bits
-- Bits is a class
class (Num a) => Bits a where {
(.&.) :: a -> a -> a;
(.|.) :: a -> a -> a;
xor :: a -> a -> a;
complement :: a -> a;
shift :: a -> Int -> a {- has default method -};
rotate :: a -> Int -> a {- has default method -};
bit :: Int -> a {- has default method -};
setBit :: a -> Int -> a {- has default method -};
clearBit :: a -> Int -> a {- has default method -};
complementBit :: a -> Int -> a {- has default method -};
testBit :: a -> Int -> Bool {- has default method -};
bitSize :: a -> Int;
isSigned :: a -> Bool;
shiftL :: a -> Int -> a {- has default method -};
shiftR :: a -> Int -> a {- has default method -};
rotateL :: a -> Int -> a {- has default method -};
rotateR :: a -> Int -> a {- has default method -};
}
例:
回数を指定して処理を繰り返す
times n f = foldl (.) id $ replicate n f
Prelude> (foldl (.) id $ replicate 3 (2*)) 1
8
Monad だと mapM_ (replicate n f) でいいのかな。
最大値・最小値を求める
Prelude> max 1 2
2
Prelude> min 1 2
1
Prelude> maximum [1..3]
3
Prelude> minimum [1..3]
1
数値の総和を求める
sum :: (Num a) => [a] -> a が便利
Prelude> sum [1,2,3,4,5]
15
絶対値を求める
abs :: (Num a) => a -> a を使います。
Prelude> abs 2
2
Prelude> abs 0
0
Prelude> abs (-2)
2
Prelude> abs 2.3
2.3
Prelude> abs (-2.3)
2.3
Prelude> :module +Complex
Prelude Complex> abs (3 :+ 4)
5.0 :+ 0.0
割り算の余りを求める
Interal クラスの rem, div, mod, quot, divMod, quotRem といったメソッドを使います。
Prelude> :info Integral
-- Integral is a class
class (Real a, Enum a) => Integral a where {
rem :: a -> a -> a {- has default method -};
div :: a -> a -> a {- has default method -};
mod :: a -> a -> a {- has default method -};
quot :: a -> a -> a {- has default method -};
divMod :: a -> a -> (a, a) {- has default method -};
quotRem :: a -> a -> (a, a);
toInteger :: a -> Integer;
}
Prelude> 5 `div` 2
2
Prelude> 5 `mod` 2
1
Prelude> (5 `div` 2) * 2 + (5 `mod` 2)
5
Prelude> 5 `divMod` 2
(2,1)
Prelude> 10 `divMod` (-3)
(-4,-2)
Prelude> 10 `quotRem` (-3)
(-3,1)
べき乗を求める
- (**) :: (Floating a) => a -> a -> a
- (^) :: (Num a, Integral b) => a -> b -> a
- (^^) :: (Fractional a, Integral b) => a -> b -> a
の三つの関数があるので、型によって使い分ける。
Prelude> 3 ^ 4
81
Prelude> 4 ^^ (-2)
6.25e-2
Prelude> 10 ^ 0
1
Prelude> 2 ** 3.5
11.313708498984761
Prelude> 2 ^ 128
340282366920938463463374607431768211456
複素数のべき乗も計算できる
Prelude> :module +Data.Complex
Prelude Data.Complex> (0 :+ 3) ** (4 :+ 1)
7.658580080790688 :+ 14.995755028498099
最大公約数と最小公倍数を求める
最大公約数
Prelude> 1105 `gcd` 3570
85
最小公倍数
Prelude> 123 `lcm` 33
1353
平方根を求める
Floating クラスの sqrt :: a -> a を使います。
*Main> sqrt 10
3.1622776601683795
*Main> sqrt 2.0
1.4142135623730951
*Main> :module +Data.Complex
*Main Data.Complex> 2 ** 0.5
1.4142135623730951
*Main Data.Complex> sqrt (-1 :: Complex Double)
-0.0 :+ 1.0
*Main Data.Complex> sqrt (-10 :: Complex Double)
-0.0 :+ 3.1622776601683795
三角関数を計算する
Floatingクラスのメソッドを使います。
Prelude> :info Floating
-- Floating is a class
class (Fractional a) => Floating a where {
pi :: a;
log :: a -> a;
sqrt :: a -> a {- has default method -};
exp :: a -> a;
logBase :: a -> a -> a {- has default method -};
(**) :: a -> a -> a {- has default method -};
cos :: a -> a;
tan :: a -> a {- has default method -};
sin :: a -> a;
acos :: a -> a;
atan :: a -> a;
asin :: a -> a;
cosh :: a -> a;
tanh :: a -> a {- has default method -};
sinh :: a -> a;
acosh :: a -> a;
atanh :: a -> a;
asinh :: a -> a;
}
deg2rad :: (Floating a) => a -> a
deg2rad deg = deg / (360 / (pi * 2))
*Main> sin (deg2rad 30)
0.49999999999999994
直交座標を局座標に変換する
Data.Complex の polor :: (RealFloat a) => Complex a -> (a, a) を使うと便利です。
*Main> :module +Data.Complex
*Main Data.Complex> polar (3 :+ 4)
(5.0,0.9272952180016122)
対数計算を行う
Floating のメソッドである以下のような関数を使います。
- log :: (Floating a) => a -> a
- sqrt :: (Floating a) => a -> a
- exp :: (Floating a) => a -> a
- logBase :: (Floating a) => a -> a -> a
*Main> logBase 10 1000
2.9999999999999996
*Main> log 10
2.302585092994046
*Main> exp (log 10)
10.000000000000002
*Main> logBase 2 8
3.0
*Main> logBase 2 10
3.3219280948873626
浮動小数点を丸める
RealFrac のround, truncate, floor, ceiling といったメソッドを使います。
*Main> :info RealFrac
-- RealFrac is a class
class (Real a, Fractional a) => RealFrac a where {
properFraction :: forall b. (Integral b) => a -> (b, a);
round :: forall b. (Integral b) => a -> b {- has default method -};
truncate ::
forall b. (Integral b) => a -> b
{- has default method -};
floor :: forall b. (Integral b) => a -> b {- has default method -};
ceiling ::
forall b. (Integral b) => a -> b
{- has default method -};
}
*Main> round 1001.2
1001
*Main> truncate 1001.2
1001
*Main> ceiling 1001.2
1002
*Main> floor 1001.2
1001
*Main> round (-1001.2)
-1001
*Main> truncate (-1001.2)
-1001
*Main> ceiling (-1001.2)
-1001
*Main> floor (-1001.2)
-1002
*Main> round 1001.5
1002
*Main> truncate 1001.5
1001
*Main> ceiling 1001.5
1002
*Main> floor 1001.5
1001
*Main> round (-1001.5)
-1002
*Main> truncate (-1001.5)
-1001
*Main> ceiling (-1001.5)
-1001
*Main> floor (-1001.5)
-1002
複素数を使う
Complex http://www.haskell.org/onlinereport/complex.html
中置データコンストラクタ :+ で,実部と虚部から作成.極座標表現からは mkPolar .逆に複素数から絶対値と偏角のタプルを得るには polar .
Prelude> :module Complex
Prelude Complex> 3 :+ 5
3.0 :+ 5.0
Prelude Complex> mkPolar 3 (pi / 4)
2.121320343559643 :+ 2.1213203435596424
Prelude Complex> polar $ mkPolar 3 (pi / 4)
(3.0,0.7853981633974482)
演算については,Complex は Eq, Read, Show, Num, Fractional, RealFloat の各クラスのインスタンスなので,ふつうの算術演算子が使える.
(※:虚部が負の場合,負号のすぐ外側の括弧は必須)
Prelude> :module Complex
Prelude Complex> (3 :+ 5) + (2 :+ (-2))
5.0 :+ 3.0
Prelude Complex> (3 :+ 5) - (2 :+ (-2))
1.0 :+ 7.0
Prelude Complex> (3 :+ 5) * (2 :+ (-2))
16.0 :+ 4.0
Prelude Complex> (3 :+ 5) / (2 :+ (-2))
(-0.5) :+ 2.0
射影関数は realPart と imagPart .
Prelude> :module Complex
Prelude Complex> realPart (3 :+ 5)
3.0
Prelude Complex> imagPart (3 :+ 5)
5.0
そのほか,共軛・cis (与えられた偏角で絶対値が 1 の複素数を返す:名前は θ に対して Cos θ + i Sin θ を返す,から,だろう(by nobsun)) ・極座標への射影.
Prelude> :module Complex
Prelude Complex> conjugate (16 :+ 4)
16.0 :+ (-4.0)
Prelude Complex> cis 10
(-0.8390715290764524) :+ (-0.5440211108893698)
Prelude Complex> magnitude (1 :+ 1)
1.4142135623730951
Prelude Complex> phase (1 :+ 1)
0.7853981633974483
有理数を使う
Ratio モジュールの一部
Ratio モジュール http://www.haskell.org/onlinereport/ratio.html
中置の抽象データコンストラクタ % で作れる.export されてないので,直接データコンストラクタを使うことはできない.
演算は,Eq, Ord, Num, Real, Fractional, RealFrac, Enum, Read, Show のインスタンスであるので,
コマンドライン引数を解析する
data Option = All
| Usr_
| Usr String
| Fmt String
| Help deriving Show
cmdOpt :: String -> [Option]
cmdOpt = cmdOpt' . words
where
cmdOpt' ("-a":xs) = All : cmdOpt' xs
cmdOpt' ("--all":xs) = All : cmdOpt' xs
cmdOpt' ("-u":x:xs)
| head x == '-' = Usr_ : cmdOpt' (x:xs)
| otherwise = Usr x : cmdOpt' xs
cmdOpt' ("--user":x:xs)
| head x == '-' = Usr_ : cmdOpt' (x:xs)
| otherwise = Usr x : cmdOpt' xs
cmdOpt' ("-f":x:xs) = Fmt x : cmdOpt' xs
cmdOpt' ("--format":x:xs) = Fmt x : cmdOpt' xs
cmdOpt' ("--help":_) = [Help]
同じことを二回書く(-aと–allなど)は嫌だなあ。
こういうときは System.Console.GetOpt を使う。
TCP クライアントを作る
import Network
import IO
crlf = "\r\n"
main = withSocketsDo sockMain
sockMain = do h <- connectTo "www.sampou.org" (PortNumber 80)
hPutStrLn h "GET / HTTP/1.1"
hPutStrLn h "HOST: www.sampou.org"
hPutStrLn h crlf
str <- hGetContents h
putStrLn str
TCP サーバを作る
送った文字列の長さを返してくれるサーバ
import Network
import IO
main = withSocketsDo sockMain
sockMain = do sock <- listenOn (PortNumber 7001)
loop sock
loop sock = do (h, _, _) <- accept sock
eof <- hIsEOF h
if eof then return ()
else do str <- hGetLine h
hPutStrLn h (show (length str))
hClose h
loop sock
横長
import Network
import IO
main = withSocketsDo sockMain
sockMain =listenOn (PortNumber 7001) >>= loop
loop sock = do (h, _, _) <- accept sock
eof <- hIsEOF h
if eof then return ()
else do hGetLine h >>= hPutStrLn h . show . length >> hClose h >> loop sock
明示的な再帰を消してみる。……と思ったけど、これじゃ振る舞いが変わっちゃう。
import Network
import IO
import Monad
main = withSocketsDo $ sequence_ (repeat f)
f = do sock <- listenOn (PortNumber 7001)
(h, _, _) <- accept sock
eof <- hIsEOF h
if eof
then return ()
else do str <- hGetLine h
hPutStrLn h (show (length str))
hClose h
最終更新 : 2004/11/20 18:17:33 JST