wvogel日記

自分用の技術備忘録が多めです.

train number.....(0)

明日から一週間家を離れるのでブログの更新が滞ってしまう。
なので、その前になにか書こうかと思ったけど、ちょっと面倒な問題にぶつかってしまった.....


タイトルのとおり、
電車乗るときに買う切符に印字されている4桁の数字、
あれから、10の数字を作れるかどうかという判定をするプログラムを書こうかと思って。
とりあえず括弧と割り算は置いておいて、

  1. ,-,*の3つでやる

方針としては、
数字と演算子両方について、並び順の全パターンを列挙し、[String]にしてやってから、
各式についてParsecで値を計算してやれば良い。

数字の並び、及び演算子の並びには

import Data.List

permutations :: a -> [a]

を利用すれば簡単に列挙できる

だが、演算子

+,+,+  -,-,*

のような並びも許されるということを忘れていた.....

とりあえず、重複なしの場合の式の列の生成

module Train where
import Data.List

opes = permutations ['+','-','*']

train :: String -> [String]
train = concat.map (mysort opes).permutations

mysort :: [String] -> String -> [String]
mysort [] _ = []
mysort (o:os) vs =mysort' (zip (o++"_") vs):mysort os vs
  where
    mysort' :: [(Char,Char)] -> String
    mysort' [(_,v)] = [v]
    mysort' ((o,v):ovs) = v:o:mysort' ovs

関数名は適当です。
mysortも、どっちかというとcombinationsの方が正しい。
これで、とりあえず、暫定的ではあるが、式は生成できるので
演算子の重複は、opes = の箇所をいじっていくことにする)
Parsecで計算部を作りましょう。
今回は()を含んでいないので、以前Parsec使わずに作った簡易電卓でも代用出来ますが、()への拡張を考え、Parsecを使う

書いてみた。

run :: Show a => Parser a ->String -> IO()
run p input = case (parse p "" input) of
                Left _ -> putStr"Parse error at"
                Right  x -> print x

num:: Parser Int
num = read<$>many1 digit

express' :: Parser Int
express' = do foldr ($)<$>factor'<*> many (add<|>sub)
 where
  add = (+) <$>(char '+' >> factor')
  sub =flip (-) <$>(char '-' >> factor')

factor' :: Parser Int
factor' = do foldr ($)<$> num
               <*> (many $ (*) <$>(char '*'>> factor'))

一度do記法で書いたものを、Applicativeで書き直したものです。
まだまだ、いきなりApplicativeスタイルでは書き進められない。