LoginSignup
18
18

More than 5 years have passed since last update.

カレンダー整形問題をHaskellで、それも日本語で読めるように書いてみた

Last updated at Posted at 2013-11-06

カレンダー整形問題Haskellの回答に、日本語による解説をコメントとしてではなく動作する関数として加えてみた。個人的に気に入っているのは「を」関数。
なおgistに投稿済み。

module Main where

import Data.List.Split
import Data.Time.Calendar
import Data.Time.Calendar.WeekDate
import Data.Time.Clock
import Data.Time.Format
import Data.Time.LocalTime
import Data.Text (pack, unpack, center)
import System.Locale

主処理 = main
main = 現在日付を取得 >>= 指定日付を表示用に加工 >>= プリントアウト

現在日付を取得 = getZonedTime
プリントアウト = putStrLn

指定日付を表示用に加工 :: ZonedTime -> IO String
指定日付を表示用に加工 任意の日付 = do
  日付               <- 任意の日付 `` 扱いやすい型に変換                           -- ZonedTime -> Day
  日付リスト         <- 日付 `` 週毎の日付に分割して先頭週の開始曜日までは空文字を詰めたリストに変換 -- Day -> [[String]]
  日付行リスト       <- 日付リスト `` 行毎の文字列に連結したリストに変換           -- [[String]] -> [String]
  ヘッダ             <- 日付 `` カレンダのヘッダに変換                             -- [String] -> [String]
  ヘッダ付き行リスト <-  ヘッダ `` (日付行リスト `と連結`)                         -- [String] -> [String] -> [String]
  ヘッダ付き行リスト `` 改行を挟んで連結して返す                                   -- [String] -> String

 :: a -> (a -> b) -> IO b
 a f = return $ f a

と連結 xs ys = ys ++ xs

改行を挟んで連結して返す = unlines

行毎の文字列に連結したリストに変換 = toLinesFromTokens
toLinesFromTokens :: [[String]] -> [String]
toLinesFromTokens = map (\ss -> drop 1 $ concat $ map padTok ss)
         -- 上でdrop 1しているのは
         -- 3桁左寄せしてから連結しているため、
         -- 各行の先頭に空白文字が1つ入るのが気に入らないため
  where
    padTok s = case length s of
                 0 -> "   "
                 1 -> "  " ++ s
                 2 -> " "  ++ s
                 _ -> error "unexpected token -> " ++ s

weekdayCount = 7
lineCharCountMax = weekdayCount * 3 - 1

カレンダのヘッダに変換 = toHeader
toHeader :: Day -> [String]
toHeader day = let s   = formatTime defaultTimeLocale "%B %Y" day
                   fst = unpack $ center lineCharCountMax ' ' $ pack s in
               [fst, "Su Mo Tu We Th Fr Sa"]

週毎の日付に分割して先頭週の開始曜日までは空文字を詰めたリストに変換 = toLeftPaddedMonthDayList
toLeftPaddedMonthDayList :: Day -> [[String]]
toLeftPaddedMonthDayList day = chunksOf 7 core
  where
    core = let fstDate = toMonthFirstDate day in
           (toLeftPaddableListFromMonthFirstDate fstDate) ++ (map show $ toMonthDateList day)

扱いやすい型に変換 = toDayFromZonedTime
toDayFromZonedTime :: ZonedTime -> Day
toDayFromZonedTime = utctDay . zonedTimeToUTC

指定日付が所属する月の全ての日付のリスト = toMonthDateList
toMonthDateList :: Day -> [Int]
toMonthDateList day = let (y,m,_) = toGregorian day
                          c = gregorianMonthLength y m in
                      [1..c]

指定日付が所属する月の先頭の週の、開始曜日までを埋める空文字のリスト = toLeftPaddableListFromMonthFirstDate
toLeftPaddableListFromMonthFirstDate :: Day -> [String]
toLeftPaddableListFromMonthFirstDate day = take (toWeekdayIndex day) $ repeat ""

指定日付が所属する月の先頭日付 = toMonthFirstDate
toMonthFirstDate :: Day -> Day
toMonthFirstDate day = let (y,m,_) = toGregorian day in
                       fromGregorian y m 1

日曜日を0としたときの曜日のインデックス = toWeekdayIndex
toWeekdayIndex :: Day -> Int
toWeekdayIndex day = case toWeekDate day of
                      (_,_,7) -> 0
                      (_,_,d) -> d
18
18
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
18