プログラマとプロマネのあいだ

プログラマもやるし、プロマネもやるし、たまに似非アーキとか営業っぽいこともやるITエンジニアがスキルアップの話を中心に日常を綴るブログです。

0〜9の数字を使って、掛け算して、Go! リストモナド編

前回までのプログラムはこちら

とりあえず、

モナドでつなげてみることに。
つなげられるのがモナドのいいところ(なのか?)

import List
import Monad

main = putStr $ unlines $ map format $ perm [0,1,2,3,5,6,7,8,9] >>= filter1 >>=
filter2

perm :: [Int] -> [[Int]]
perm [] = [[]]
perm xs = concat [map (x:) $ perm (delete x xs) | x <- xs]

test :: [Int] -> Bool
test [a,b,c,d,e,f,g,h,i] = if (400 + a*10 + b) * (c*10 + d) == (e*10000 + f*1000
 + g*100 + h*10 + i) then True else False

format :: [Int] -> String
format [a,b,c,d,e,f,g,h,i] = listToString [4,a,b] ++ " x " ++ listToString[c,d]
++ " = " ++ listToString [e,f,g,h,i]

listToString :: (Show a) => [a] -> String
listToString l = concatMap show l

-- 数式を満たせるかどうか
filter1 :: [Int] -> [[Int]]
filter1 xs = if test xs == True then [xs] else mzero

-- 計算結果の先頭がゼロ以外
filter2 :: [Int] -> [[Int]]
filter2 xs = if (head $ snd $ splitAt 4 xs) == 0 then mzero else [xs]

結果はこう。

402 x 39 = 15678
495 x 36 = 17820

なんだか若干重かったですが、一応できました。
計算のところは、

perm [0,1,2,3,5,6,7,8,9] >>= filter1 >>= filter2

ですっきり!?


filter1、filter2関数で、条件に当てはまらないときmzeroってのを返却するようにしてますが、これを空リストの空リストにすると、空リストが大量に結果に含まれてしまい、大変なことになります。
concatMapするときに、リストをconcatMapするのと、リストのリストをconcatMapするのとの違いだと思うんですが、まあよく分かりません(汗


ちなみに、昨日やった文字列のリストを扱ったサンプルについて、空リストをmzeroに置き換えてみると、同じように動きます。

import Monad

main = print $ ["aaa", "bbb", "ccc", "bbc"] >>= startsWithB >>= endsWithC

startsWithB :: String -> [String]
startsWithB xs = if (head xs) == 'b' then [xs] else mzero

endsWithC :: String -> [String]
endsWithC xs = if (head $ reverse xs) == 'c' then [xs] else mzero

結果はこう。

["bbc"]

いい感じです。