23
23. セクション構造
記事中に含まれるセクション名とそのレベル(例えば"== セクション名 =="なら1)を表示せよ
23.hs
{-# LANGUAGE OverloadedStrings, DeriveGeneric #-} import Data.Aeson as A import Control.Applicative import System.IO.UTF8 as I8 import System.Environment import Data.Maybe import Data.ByteString.Lazy.UTF8 as B import GHC.Generics import Text.Regex.Posix import Prelude as P data Article = Article { text :: String, title :: String } deriving (Eq, Show, Generic) instance FromJSON Article main = do filename <- head <$> getArgs body <- fromString <$> I8.readFile filename I8.writeFile "23.txt" $ unlines $ map f $ filter isHeadline $ concatMap P.lines $ map (text . fromJust . A.decode) $ B.lines $ body where f x = headline x ++ "," ++ depth x isHeadline = ((=~ ("^={2,6}.*={2,6}" :: String)) :: String -> Bool) headline = filter (\x -> (x /= ' ') && (x /= '=')) depth = show . P.length . ((=~ ("=+" :: String)) :: String -> String)
22
22. カテゴリ名の抽出
記事のカテゴリ名を(行単位ではなく名前で)抽出せよ.
22.hs
{-# LANGUAGE OverloadedStrings, DeriveGeneric #-} import Data.Aeson as A import Control.Applicative import System.IO.UTF8 as I8 import System.Environment import Data.Maybe import Data.ByteString.Lazy.UTF8 as B import GHC.Generics import Text.Regex.Posix import Prelude as P data Article = Article { text :: String, title :: String } deriving (Eq, Show, Generic) instance FromJSON Article main = do filename <- head <$> getArgs body <- fromString <$> I8.readFile filename I8.writeFile "22.txt" $ unlines $ map f2 $ filter f $ concatMap P.lines $ map (text . fromJust . A.decode) $ B.lines $ body where f = ((=~ ("^[[][[]:?(カテゴリ|Category):" :: String)) :: String -> Bool) f2 = tail . ((=~ (":[^]|]*" :: String)) :: String -> String)
21
21. カテゴリ名を含む行を抽出
記事中でカテゴリ名を宣言している行を抽出せよ.
21.hs
{-# LANGUAGE OverloadedStrings, DeriveGeneric #-} import Data.Aeson as A import Control.Applicative import System.IO.UTF8 as I8 import System.Environment import Data.Maybe import Data.ByteString.Lazy.UTF8 as B import GHC.Generics import Text.Regex.Posix import Prelude as P data Article = Article { text :: String, title :: String } deriving (Eq, Show, Generic) instance FromJSON Article main = do filename <- head <$> getArgs body <- fromString <$> I8.readFile filename I8.writeFile "21.txt" $ unlines $ filter (\x -> x =~ ("^[[][[]:?(カテゴリ|Category)" :: String) :: Bool) $ concatMap P.lines $ map (text . fromJust . A.decode) $ B.lines $ body
ヘルプ:Wiki マークアップ早見表 - WordPress Codex 日本語版
wikipediaの記法では、日本語の「カテゴリ」を許可しているのかわからなかったが、いくつかあるみたいだったので、抽出した。
20
Wikipedia記事のJSONファイルを読み込み,「イギリス」に関する記事本文を表示せよ.問題21-29では,ここで抽出した記事本文に対して実行せよ.
20.hs
{-# LANGUAGE OverloadedStrings, DeriveGeneric #-} import Data.Aeson as A import Control.Applicative import System.IO.UTF8 as I8 import System.Environment import Data.Maybe import Data.ByteString.Lazy.UTF8 import GHC.Generics data Article = Article { text :: String, title :: String } deriving (Eq, Show, Generic) instance FromJSON Article main = do filename <- head <$> getArgs body <- fromString <$> I8.readFile filename I8.writeFile "out2.txt" $ text $ fromJust $ A.decode body
JSONの読み込み方法
言語処理100本ノックの第3章に入るにあたり、JSON形式の読み取り方法について調べた。
標準のライブラリにはないため、別途パッケージが必要で、Googleで検索した限りでは、下記の2つの名前が上がった。
aeson: Fast JSON parsing and encoding | Hackage
json: Support for serialising Haskell to and from JSON | Hackage
この他にもHackageのJSONカテゴリにはいくつもあるが、とりあえず読み込めればよいため、この2つを試した。
要求としては、日本語(UTF8)が処理できて、宣言したdataに取り込めることの2点があるため、
下記の構造を持つJSONデータを読み込み、titleを出力するサンプルを作成することにした。
{ "title" : "...", "text" : "..." }
Aeson
まずは、Aesonでのサンプル。Aesonのバージョンは0.9.0.1。
下記のプログを参考に作成した。
Parsing JSON with Aeson - School of Haskell | FP Complete
{-# LANGUAGE OverloadedStrings, DeriveGeneric #-} import Data.Aeson as A import Control.Applicative import System.IO.UTF8 as I8 import System.Environment import Data.Maybe import Data.ByteString.Lazy.UTF8 import GHC.Generics data Article = Article { text :: String, title :: String } deriving (Eq, Show, Generic) instance FromJSON Article main = do filename <- head <$> getArgs body <- fromString <$> I8.readFile filename I8.writeFile "out2.txt" $ title $ fromJust $ A.decode body
json
次に、jsonのサンプルプログラム。バージョンは0.9.1
下記のブロクを参考に作成
Haskell で XML と JSON をパースする方法 - satosystemsの日記
{-# LANGUAGE DeriveDataTypeable #-} import Text.JSON import Text.JSON.Generic import Control.Applicative import System.IO.UTF8 as I8 import System.Environment data Article = Article { text :: String, title :: String } deriving (Eq, Show, Data, Typeable) main = do filename <- head <$> getArgs body <- I8.readFile filename I8.writeFile "out.txt" $ text $ decodeJSON body
まとめ
若干心配していた日本語の扱いもどちらも問題なく、この程度の利用では、利便性はあまり変わらなかった。
ひとまず、Aesonを使用していこうと思う。
調べた限りでは、Aesonのほうが検索のヒットが多く、
また、jsonよりもAesonを薦めている記事があったので。
19
19. 各行の1コラム目の文字列の出現頻度を求め,出現頻度の高い順に並べる
各行の1列目の文字列の出現頻度を求め,その高い順に並べて表示せよ.確認にはcut, uniq, sortコマンドを用いよ.
19.hs
import System.Environment import System.IO.UTF8 as I8 import qualified Data.Map as M import Data.List import Data.Maybe main = do filename <- getArgs >>= return . head body <- I8.readFile filename let cfs = M.fromList $ map (\x -> (head x, length x) ) $ group $ sort $ map (head . words) $ lines body I8.writeFile "19.out.txt" $ unlines $ sortBy (\x y -> compareByCf cfs (head $ words x) (head $ words y)) $ lines body compareByCf cfs x y = fromMaybe EQ $ do xt <- M.lookup x cfs yt <- M.lookup y cfs return $ compare xt yt
実行結果が出現頻度順に並んでないと勘違いして、だいぶ悩んでしまった。
出現頻度順に並んでも、同じ単語が続けて現れるわけではないですね...。