b2ox 先生曰く,これが Haskell らしい解法らしいです w
module Main where import Text.ParserCombinators.Parsec import System.IO.Unsafe ( unsafePerformIO ) import Data.Char ( isNumber ) data AST = INT Integer | REAL Double | SYMBOL String | LIST [AST] deriving (Eq, Show) symbol :: Parser String symbol = many1 (alphaNum <|> (oneOf ".%_!$#|+-*/:<=>?@^&~")) intOrRealOrSymbol :: String -> IO AST intOrRealOrSymbol s = catch (readIO s >>= return . INT) (\ _ -> catch (readIO s >>= return . REAL) (\ _ -> return (symbolOrReal s))) where symbolOrReal s = let (n, m) = span isNumber s in if length n > 0 && m == "." then REAL (read n) else SYMBOL s atom :: Parser AST atom = symbol >>= return . unsafePerformIO . intOrRealOrSymbol list :: Parser AST list = between (char '(') (char ')') (sepBy (atom <|> list) spaces) >>= return . LIST test = parseTest list "(abc 1 12. 1.2.3 (1.2 1) 1.2)"
between とか使って,多少小手先の改良を加えたつもり.
___ ___ _ / _ \ /\ /\/ __(_) / /_\// /_/ / / | | GHC Interactive, version 6.6, for Haskell 98. / /_\\/ __ / /___| | http://www.haskell.org/ghc/ \____/\/ /_/\____/|_| Type :? for help.
Loading package base ... linking ... done. [1 of 1] Compiling Main ( C:/Documents and Settings/aloha/?????????? ??/haskell/intOrFloatOrSymbol.hs, interpreted ) Ok, modules loaded: Main. *Main> test Loading package parsec-2.0 ... linking ... done. LIST [SYMBOL "abc",INT 1,REAL 12.0,SYMBOL "1.2.3",LIST [REAL 1.2,INT 1],REAL 1.2]
僕は Haskell のレイアウト規則とかあんまりわかってないので,微妙にインデントに苦労した… セミコロンとプレース万歳 !!
# というわけで,C 脳 S 式脳の僕はたぶん Python や Ruby や JS は一生使わないと思います.インデントで意味が変わるような言語は,自動生成の時 pretty print が面倒だし (ぉ
readIO で Integer 型に強制しつつ無理やり読み込んで,駄目だったら例外キャッチしつつ Double 型で無理やり読み込んで,それでも駄目だったらシンボルとして読み込んだ後,unsafePerformIO でモナドを剥ぎ取るという荒技 www
readIO って便利だな〜,例外を吐くし実行も止まらない〜と.
いやぁ,Haskell は奥が深い.副作用の織り成す美の,なんと神秘的なことよ (感嘆)
結論 : 本物のプログラマは unsafePerformIO を恐れずに使う.みんな大人なんだから.
|