シカクいアタマでハスケルする
中学受験予備校の日能研が昔から、「シカクいアタマをマルくする」という広告を張っている。電車内のドア横・窓上ポスターによく掲載されているため、ご存じの方も多いだろう。
2015年2月掲載分(とウェブサイトに記載されているが、1月時点で既に掲載されている。雑誌の発売月みたいなものだろうか)の問題がこちら。*1
2015年2月掲載 鴎友学園女子中学校【算数】 | シカクいアタマをマルくする。 | 中学受験-小学生のための中学受験塾。日能研
さすがに良問だ。 (2/29を含めて)366日分を全て書き出すと時間が掛かり過ぎるため、「理詰めで短時間で解ける方法があるはずだ」「どうやったら候補を絞り込めるだろうか」と考えさせる。 また、問1と問2でほどよい難易度の上昇がある。 (大人にとっても、電車内で暗算しようとするとかなり歯ごたえがあってよい)
中学受験生にとって良問であると同時に、なにやらプログラミングで解くにも適した問題に見える。
- 日付ライブラリの基礎的な使用(366日の列挙)
- 数字のゼロ埋め(
1/1
は11
ではなく、0101
として扱う必要がある) - リストの最大、最小、フィルタなどの各種操作
が適度な難易度(中級者以上には、かなり物足りないと言われるかもだけど)で散りばめられている。
import Data.Time (Day, fromGregorian, toGregorian) import Data.List (minimumBy, maximumBy) import Data.Ord (comparing) char2segnum :: Char -> Int char2segnum '0' = 6 char2segnum '1' = 2 char2segnum '2' = 5 char2segnum '3' = 5 char2segnum '4' = 4 char2segnum '5' = 5 char2segnum '6' = 6 char2segnum '7' = 4 char2segnum '8' = 7 char2segnum '9' = 6 countSegnum :: String -> Int countSegnum = sum . map char2segnum zeroPadding :: Int -> Int -> String zeroPadding width = reverse . take width . (++ repeat '0') . reverse . show allDaysInLeap :: [Day] allDaysInLeap = [fromGregorian 2000 1 1..fromGregorian 2000 12 31] gregorian2string :: Day -> String gregorian2string date = let (_, m, d) = toGregorian date in zeroPadding 2 m ++ zeroPadding 2 d day2segnum :: Day -> Int day2segnum = countSegnum . gregorian2string dayHasMinSegnum :: (Day, Int) dayHasMinSegnum = minimumBy (comparing snd) $ zip allDaysInLeap (map day2segnum allDaysInLeap) dayHasMaxSegnum :: (Day, Int) dayHasMaxSegnum = maximumBy (comparing snd) $ zip allDaysInLeap (map day2segnum allDaysInLeap) daysHasSegnum :: Int -> Int daysHasSegnum n = length $ filter ((==n) . snd) $ zip allDaysInLeap (map day2segnum allDaysInLeap) main :: IO () main = do putStrLn $ "1.1: " ++ show dayHasMinSegnum putStrLn $ "1.2: " ++ show dayHasMaxSegnum putStrLn $ "2: " ++ show (daysHasSegnum 24)
出力はこのとおり
1.1: (2000-11-11,8) 1.2: (2000-08-08,26) 2: 16
日付を扱うのがちょっと大変かと思ったが、実際に試してみると非常に楽で助かった。Day型がEnumのインスタンスなので、1/1から12/31の列挙が[fromGregorian 2000 1 1..fromGregorian 2000 12 31]
で済むのが素晴らしい。
Rubyの(Date.new(2000,1,1)..Date.new(2000,12,31))
と全然変わらない。
*1:そもそもウェブサイトに掲載されていることを初めて知った。これからは「シカクいアタマをマルくするが貼ってあるけど人が前に立ってて問題が読めない..死のうかな」といったときもHPを見れば大丈夫だ。