このディレクトリの索引
http://hibari.2ch.net/test/read.cgi/tech/1296387672/371
#  [1] 授業単元:万年カレンダーの作成  
#  [2] 問題文(含コード&リンク):  
#  ユーザーが指定した年・月のカレンダーを表示するアプリケーションを作る。  
#  ユーザーに年と月の入力させ、指定された月から4ヶ月分のカレンダーを表示させる。  
#  詳しい説明・仕様 http://ime.nu/www1.axfc.net/uploader/File/so/58289  
#  
#  1.ユーザーが指定可能な日付の範囲
#  西暦1年の1月から西暦9999年の9月まで ※表示範囲は9999年12月まで
#  
#  2.暦の条件
#  2.1.曜日
#   西暦1年1月1日を月曜日とする。
#  
#  2.2.うるう年
#   ・4で割り切れる年はうるう年
#   ・ただし100で割り切れる年はうるう年ではない
#   ・ただし400で割り切れる年はうるう年
#  
#  2.3.祝日・国民の休日
#   ・祝日が日曜の場合は翌日以降の平日を振替休日にする
#   ・国民の休日とは、国民の祝日にはさまれた平日を指す 例)2009/09/22
#  
#  元旦     1月1日
#  成人の日   1月第2月曜日
#  建国記念日  2月11日
#  春分の日※
#  昭和の日   4月29日
#  憲法記念日  5月3日
#  みどりの日  5月4日
#  こどもの日  5月5日
#  海の日    7月の第3月曜日
#  敬老の日   9月の第3月曜日
#  秋分の日※
#  体育の日   10月の第2月曜日
#  文化の日   11月3日
#  勤労感謝の日 11月23日
#  天皇誕生日  12月23日
#  
#  春分の日・秋分の日は次の計算式によって求める。
#  a)西暦1980年〜2099年
#   春分の日(3月X日)
#    X=INT(20.8431+0.242194*(Year-1980)-INT((Year-1980)/4))
#    秋分の日(9月X日)
#    X=INT(23.2488+0.242194*(Year-1980)-INT((Year-1980)/4))
#  (Yearは西暦の年、INT()は、括弧内の小数部切り捨てを意味する)
#  
#  b)a以外の年
#   春分の日:3月23日,秋分の日:9月23日
#  
#  3.動作の流れ
#  1)プログラムを実行
#  2)年入力をうながすメッセージの表示 ex.西暦何年のカレンダーを表示しますか?
#  3)ユーザーが年情報の入力する
#  4)(?)入力された年情報が1〜9999だった場合は、月情報の入力をうながすメッセージが表示される。 ex.何月から表示しますか?
#   (?)未入力や、無効な数値・文字が入力された場合はエラーメッセージが表示され、再入力をうながして2)にもどる ex.年は1〜9999の範囲で入力してください
#  5)ユーザーが月情報を入力する
#  6)(?)入力された月情報が1〜12(9999年の場合は1〜9)だった場合は、指定された年月から4ヶ月分のカレンダーが表示される
#   (?)未入力や、無効な数値・文字が入力された場合はエラーメッセージが表示され、再入力をうながして4)(?)にもどる ex.月は1〜12の範囲で入力してください
#  7)続いてカレンダーを表示するかをユーザーに問う 
#  ex.続けて表示しますか? 1:次の4ヶ月を表示/2:年と月を指定する/9:終了する ユーザーの入力値により処理続行または終了する
#  
#  4.出力イメージ
#  ・カレンダーは指定年月から4ヶ月分を一度に表示する
#  ・各月を横に2ヶ月ずつ、2段に表示する
#  ・土日祝日及び振替休日は、日付前にマークをつけ、平日と区別する
#  ・各月ごとのレイアウトが自由 
#  
#  
#  ↓コマンドプロンプトでの出力イメージ
#  
#  
#  2009 9月                        2009 10月
#    日  月  火  水  木  金  土  9月と同じ要領で 
#           1   2   3   4 * 5
#   @ 6   7   8   9  10  11 *12
#   @13  14  15  16  17  18 *19
#   @20 #21 $22 #23  24  25 *26
#   @27  28  29  30
#  
#  2009 11月                  2009 12月
#  9月と同じ要領で              9月と同じ要領で
#  
#  
#  
#  
#  
#  
#  
#  @:日曜日 *:土曜日 #:祝日 $:休日 &:振替休日
#  
#  1:次の4ヶ月を表示 2:年と月を指定する 9:終了する 

カレンダー表示(_年,_月) :-
        四ヶ月分のカレンダー((_年,_月,_カレンダー,_年_1,_月_1,_カレンダー_1,_年_2,_月_2,_カレンダー_2,_年_3,_月_3,_カレンダー_3),
        カレンダー見出し(_年,_月,_年_1,_月_1),
        二ヶ月ならびカレンダー表示(_カレンダー,_カレンダー1),
        write('\n\n'),
        カレンダー見出し(_年_2,_月_2,_年_3,_月_3),
        二ヶ月ならびカレンダー表示(_カレンダー_2,_カレンダー3),!.

カレンダー見出し(_年_1,_月_1,_年_2,_月_2) :-
        write_formatted('%4d年%2d月                      %4d年%2d月\n',[_年_1,_月_1,_年_2,_月_2]),!.

二ヶ月ならびカレンダー表示([],[]) :- !.
二ヶ月ならびカレンダー表示([L1|R1],[L2|R2]) :-
        flat(L1,S1),
        flat(L2,S2),
        write_formatted('%t    %t\n',[S1,S2]),
        二ヶ月ならびカレンダー表示(R1,R2).

四ヶ月分のカレンダー(_年,_月,_カレンダー,_年_1,_月_1,_カレンダー_1,_年_2,_月_2,_カレンダー_2,_年_3,_月_3,_カレンダー_3) :-
        カレンダー(_年,_月,_カレンダー),
        翌月(_年,_月,_年_1,_月_1),
        カレンダー(_年_1,_月_1,_カレンダー_1),
        翌月(_年_1,_月_1,_年_2,_月_2),
        カレンダー(_年_2,_月_2,_カレンダー_2),
        翌月(_年_2,_月_2,_年_3,_月_3),
        カレンダー(_年_3,_月_3,_カレンダー_3),!.

カレンダー(_年,_月,_カレンダー) :-
        length(Cal,35),
        'Zellerの公式を用いて曜日を得る'(_振替休日の年,_振替休日の月,_日,_曜日を表す値,日曜),        
        length(L0,_曜日を表す値),
        append(L0,L1,Cal),
        この月の最終日は(_最終日),
        日付を振る(1,_最終日,L1),
        付加する符号を振る(1,_最終日,_年,_月,L1),
        すべての変数に空白を埋める(Cal,_カレンダー),!.

日付を振る(N,_最終日,_) :- N > _最終日,!.
日付を振る(N,_最終日,[[_,_,_,N]|R]) :-
        N < 10,
        N2 is N + 1, 
        日付を振る(N2,_最終日,R),!.
日付を振る(N,_最終日,[[_,_,N10,N0]|R]) :-
        N >= 10,
        N10 is N // 10,
        N0 is N mod 10,
        N2 is N + 1, 
        日付を振る(N2,_最終日,R),!.

付加する符号を振る(N,_最終日,_年,_月,[[_,'#',D1,D2]|R]) :-
        国民の祝日(_,_年,_月,N),
        N2 is N + 1,
        付加する符号を振る(N2,_最終日,_年,_月,R),!.
付加する符号を振る(N,_最終日,_年,_月,[[_,'$',D1,D2]|R]) :-
        国民の休日(_,_年,_月,N),
        N2 is N + 1,
        付加する符号を振る(N2,_最終日,_年,_月,R),!.
付加する符号を振る(N,_最終日,_年,_月,[[_,'&',D1,D2]|R]) :-
        振替休日(_,_年,_月,N),
        N2 is N + 1,
        付加する符号を振る(N2,_最終日,_年,_月,R),!.
付加する符号を振る(N,_最終日,_年,_月,[[_,'@',D1,D2]|R]) :-
        'Zellerの公式を用いて曜日を得る'(_振替休日の年,_振替休日の月,_日,_曜日を表す値,日曜),
        N2 is N + 1,
        付加する符号を振る(N2,_最終日,_年,_月,R),!.
付加する符号を振る(N,_最終日,_年,_月,[[_,'*',D1,D2]|R]) :-
        'Zellerの公式を用いて曜日を得る'(_振替休日の年,_振替休日の月,_日,_曜日を表す値,土曜),
        N2 is N + 1,
        付加する符号を振る(N2,_最終日,_年,_月,R),!.
付加する符号を振る(N,_最終日,_年,_月,[_|R]) :-
        N2 is N + 1,
        付加する符号を振る(N2,_最終日,_年,_月,R),!.
        
すべての変数に空白を埋める([],[]) :- !.
すべての変数に空白を埋める([V|R1],[[' ',' ',' ',' ']|R2]) :-
        var(V),
        すべての変数に空白を埋める(R1,R2),!.
すべての変数に空白を埋める([L|R1],[L1|R2]) :-
        変数を空白に変換(L,L1),
        すべての変数に空白を埋める(R1,R2),!.

変数を空白に変換([],[]) :- !.
変数を空白に変換([V|R1],[' '|R1]) :-
        var(V),
        変数を空白に変換(R1,R2),!.
変数を空白に変換([V|R1],[V|R1]) :-
        \+(var(V)),
        変数を空白に変換(R1,R2),!.

この月の最終日は(_年,2,29) :- うるう年(_年),!.
この月の最終日は(_年,2,28) :- \+(うるう年(_年)),!.
この月の最終日は(_年,_月,31) :-
        append(_,[_月|_],[1,3,5,7,8,10,12]),!.
この月の最終日は(_年,_月,30) :-
        append(_,[_月|_],[4,6,9,11]),!.

祝日が日曜の場合は翌日以降の平日を振替休日にする(_振替休日の年,_振替休日の月,_振替休日の日) :-
        国民の祝日(_,_振替休日の年,_振替休日の月,_日),
        'Zellerの公式を用いて曜日を得る'(_振替休日の年,_振替休日の月,_日,_,日曜),
        振替休日を探す(_振替休日の年,_振替休日の月,_日,_振替休日の日).

国民の休日とは、国民の祝日にはさまれた平日を指す(_国民の休日の年,_国民の休日の月,_国民の休日の日) :-
        前日(_国民の休日の年,_国民の休日の月,_国民の休日の日,_前日の年,_前日の月,_前日の日),
        翌日(_国民の休日の年,_国民の休日の月,_国民の休日の日,_翌日の年,_翌日の月,_翌日の日),
        国民の祝日(_,_前日の年,_前日の月,_前日の日),
        国民の祝日(_,_翌日の年,_翌日の月,_翌日の日),!.

振替休日を探す(_振替休日の年,_振替休日の月,_日,_振替休日の日) :-
        between(_日,31,_振替休日の日),
        \+(国民の祝日(_,_振替休日の年,_振替休日の月,_振替休日の日)),
        'Zellerの公式を用いて曜日を得る'(_振替休日の年,_振替休日の月,_振替休日の日,_,_曜日),
        \+(_曜日=日曜),!.

翌月(_年,12,_翌年,1) :-
        _翌年 is _年 + 1,!.
翌月(_年,_月,_年,_翌月) :-
        _翌月 is _月 + 1.

前日(_年,_月,_日,_前日の年,_前日の月,_前日の日) :-
        前日のの月がわり調整(_年,_月,_日,_前日の年,_前日の月,_前日の日).

翌日(_年,_月,_日,_翌日の年,_翌日の月,_翌日の日) :-
        翌日の月がわり調整(_年,_月,_日,_翌日の年,_翌日の月,_翌日の日).

前日の月がわり調整(_整数年,1,1,_変更された整数年,12,31) :-
        _変更された整数年 は _整数年 - 1,!.
前日の月がわり調整(_整数年,_整数月,1,_整数年,_変更された整数月,31) :-
        member(_整数月,[2,4,6,8,9,11]),
        _変更された整数月 は _整数月 - 1,!.
前日の月がわり調整(_整数年,_整数月,1,_整数年,_変更された整数月,30) :-
        member(_整数月,[5,7,10,12]),
        _変更された整数月 は _整数月 - 1,!.
前日の月がわり調整(_整数年,3,1,_整数年,2,29) :-
        0 is _整数年 mod 4,!.
前日の月がわり調整(_整数年,3,1,_整数年,2,28) :-
        \+(0 is _整数年 mod 4),!.
前日の月がわり調整(_整数年,_整数月,_整数日,_整数年,_整数月,_変更された整数日) :-
        _変更された整数日 は _整数日 - 1,!.

翌日の月がわり調整(_整数年,12,31,_変更された整数年,1,1) :-
        _変更された整数年 は _整数年 + 1,!.
翌日の月がわり調整(_整数年,_整数月,30,_整数年,_変更された整数月,1) :-
        member(_整数月,[4,6,8,9,11]),
        _変更された整数月 は _整数月 + 1,!.
翌日の月がわり調整(_整数年,_整数月,31,_整数年,_変更された整数月,1) :-
        member(_整数月,[1,3,5,7,10]),
        _変更された整数月 は _整数月 + 1,!.
翌日の月がわり調整(_整数年,2,29,_整数年,3,1) :-
        0 is _整数年 mod 4,!.
翌日の月がわり調整(_整数年,2,28,_整数年,3,1) :-
        \+(うるう年(_整数年)),!.
翌日の月がわり調整(_整数年,_整数月,_整数日,_整数年,_整数月,_変更された整数日) :-
        _変更された整数日 は _整数日 + 1,!.

祝休日の記号(日曜日,'@').
祝休日の記号(土曜日,'*').
祝休日の記号(国民の祝日,'#').
祝休日の記号(国民の休日,'$').
祝休日の記号(振替休日,'&').

国民の祝日(元旦,_年,1,1).
国民の祝日(成人の日,1,_日) :-
        between(8,14,_日),
        'Zellerの公式を用いて曜日を得る'(_年,1,_日,_,月曜).
国民の祝日(建国記念日,_年,2,11).
国民の祝日(春分の日,_年,3,_日) :-
        春分の日(_年,3,_日).
国民の祝日(昭和の日,_年,4,29).
国民の祝日(憲法記念日,_年,5,3).
国民の祝日(みどりの日,_年,5,4).
国民の祝日(こどもの日,_年,5,5).
国民の祝日(海の日,_年,7,_日) :-
        between(15,21,_日),
        'Zellerの公式を用いて曜日を得る'(_年,7,_日,_,月曜).
国民の祝日(敬老の日,_年,9,_日) :-
        between(15,21,_日),
        'Zellerの公式を用いて曜日を得る'(_年,9,_日,_,月曜).
国民の祝日(秋分の日,_年,9,_日) :-
        秋分の日(_年,9,_日).
国民の祝日(体育の日,_年,10,_日) :-
        between(8,14,_日),
        'Zellerの公式を用いて曜日を得る'(_年,10,_日,_,月曜).
国民の祝日(文化の日,_年,11,3).
国民の祝日(勤労感謝の日,_年,11,23).
国民の祝日(天皇誕生日,_年,12,23).

春分の日(_年,3,_日) :-
        _年 >= 1980,
        _年 =< 2099,
        _日 is truncate(20.8431+0.242194*(_年-1980)-truncate((_年-1980)/4)),!.
春分の日(_年,3,23).

秋分の日(_年,9,_日) :-
        _年 >= 1980,
        _年 =< 2099,
        _日 is truncate(23.2488+0.242194*(_年-1980)-truncate((_年-1980)/4)),!.
秋分の日(_年,9,23).

うるう年(_年) :-
        0 is _年 mod 400,!.
うるう年(_年) :-
        0 is _年 mod 100,
        !,
        fail.
うるう年(_年) :-
        0 is _年 mod 4,!.
うるう年(_年) :-
        \+(0 is _年 mod 4),
        fail.

'Zellerの公式を用いて曜日を得る'(_年,1,_日,_曜日を表す値,_曜日) :-
    _年_1 is _年 - 1,
    'Zellerの公式を用いて曜日を得る'(_年_1,13,_日,_曜日を表す値,_曜日),!.
'Zellerの公式を用いて曜日を得る'(_年,2,_日,_曜日を表す値,_曜日) :-
    _年_1 is _年 - 1,
    'Zellerの公式を用いて曜日を得る'(_年_1,14,_日,_曜日を表す値,_曜日),!.
'Zellerの公式を用いて曜日を得る'(_年,_月,_日,_曜日を表す値,_曜日) :-
    _曜日を表す値 is (_年 + truncate(_年 / 4) - truncate(_年 / 100) + truncate(_年 / 400) + truncate((13 * _月 + 8) / 5) + _日) mod 7,
    曜日を表す値(_曜日を表す値,_曜日),!.

曜日を表す値(0,日曜).
曜日を表す値(1,月曜).
曜日を表す値(2,火曜).
曜日を表す値(3,水曜).
曜日を表す値(4,木曜).
曜日を表す値(5,金曜).
曜日を表す値(6,土曜).