176 lines
14 KiB
Markdown
176 lines
14 KiB
Markdown
---
|
||
title: カレンダー関係の手続きを作ってみました
|
||
author: kazu634
|
||
date: 2009-03-15
|
||
wordtwit_post_info:
|
||
- 'O:8:"stdClass":13:{s:6:"manual";b:0;s:11:"tweet_times";i:1;s:5:"delay";i:0;s:7:"enabled";i:1;s:10:"separation";s:2:"60";s:7:"version";s:3:"3.7";s:14:"tweet_template";b:0;s:6:"status";i:2;s:6:"result";a:0:{}s:13:"tweet_counter";i:2;s:13:"tweet_log_ids";a:1:{i:0;i:4527;}s:9:"hash_tags";a:0:{}s:8:"accounts";a:1:{i:0;s:7:"kazu634";}}'
|
||
categories:
|
||
- gauche
|
||
- Lisp
|
||
|
||
---
|
||
<div class="section">
|
||
<p>
|
||
車輪の再発名であることはわかっていますが、
|
||
</p>
|
||
|
||
<ul>
|
||
<li>
|
||
うるう年の判定
|
||
</li>
|
||
<li>
|
||
曜日の判定
|
||
</li>
|
||
</ul>
|
||
|
||
<p>
|
||
をgaucheで作成しました。背景としては、Perlで作ったスクリプトをgaucheに移植しよう考えています。PerlだとDate::Simpleというモジュールがあって、それがすべてバックグラウンドで処理してくれていたのですが、今回は一から作ってみることにしました。
|
||
</p>
|
||
|
||
<h4>
|
||
うるう年の判定
|
||
</h4>
|
||
|
||
<p>
|
||
Wikipediaにはこんな風に書かれています:
|
||
</p>
|
||
|
||
<blockquote title="閏年 - Wikipedia" cite="http://ja.wikipedia.org/wiki/%E9%96%8F%E5%B9%B4">
|
||
<p>
|
||
次の規則に従って400年に97回の閏年が設けられる。1暦年は平均365.2425日(365日と5時間49分12秒)で、約3320年に1日の割合で暦と季節がずれる。
|
||
</p>
|
||
|
||
<ol>
|
||
<li>
|
||
西暦年が4で割り切れる年は閏年
|
||
</li>
|
||
<li>
|
||
ただし、西暦年が100で割り切れる年は平年
|
||
</li>
|
||
<li>
|
||
ただし、西暦年が400で割り切れる年は閏年
|
||
</li>
|
||
</ol>
|
||
|
||
<p>
|
||
<cite><a href="http://ja.wikipedia.org/wiki/%E9%96%8F%E5%B9%B4" onclick="__gaTracker('send', 'event', 'outbound-article', 'http://ja.wikipedia.org/wiki/%E9%96%8F%E5%B9%B4', '閏年 – Wikipedia');" target="_blank">閏年 – Wikipedia</a></cite>
|
||
</p>
|
||
</blockquote>
|
||
|
||
<p>
|
||
というわけで、これを単純に置き換えてみました:
|
||
</p>
|
||
|
||
<pre class="syntax-highlight">
|
||
<span class="synSpecial">(</span>define <span class="synSpecial">(</span>isleap? year<span class="synSpecial">)</span>
|
||
<span class="synSpecial">(</span><span class="synStatement">cond</span>
|
||
[<span class="synSpecial">(</span><span class="synStatement">=</span> <span class="synSpecial">(</span>remainder year <span class="synConstant">400</span><span class="synSpecial">)</span> <span class="synConstant"></span><span class="synSpecial">)</span> <span class="synConstant">"Leap"</span>]
|
||
[<span class="synSpecial">(</span><span class="synStatement">=</span> <span class="synSpecial">(</span>remainder year <span class="synConstant">100</span><span class="synSpecial">)</span> <span class="synConstant"></span><span class="synSpecial">)</span> <span class="synConstant">"Not Leap"</span>]
|
||
[<span class="synSpecial">(</span><span class="synStatement">=</span> <span class="synSpecial">(</span>remainder year <span class="synConstant">4</span><span class="synSpecial">)</span> <span class="synConstant"></span> <span class="synSpecial">)</span> <span class="synConstant">"Leap"</span>]
|
||
[else <span class="synConstant">"Not Leap"</span>]<span class="synSpecial">))</span>
|
||
</pre>
|
||
|
||
<p>
|
||
これは簡単でした。
|
||
</p>
|
||
|
||
<h4>
|
||
曜日の判定
|
||
</h4>
|
||
|
||
<p>
|
||
曜日の判定は「ツェラーの公式」というのがあるそうです:
|
||
</p>
|
||
|
||
<blockquote title="ツェラーの公式 - Wikipedia" cite="http://ja.wikipedia.org/wiki/%E3%83%84%E3%82%A7%E3%83%A9%E3%83%BC%E3%81%AE%E5%85%AC%E5%BC%8F">
|
||
<p>
|
||
ツェラーの公式(Zeller’s congruence)は、西暦の年、月、日からその日が何曜日であるかを算出する公式である。
|
||
</p>
|
||
|
||
<p>
|
||
まず、求めたい日の年の下2桁を削ったもの(年/100の小数点以下切り捨て)をJ、年の下2桁(年 mod 100)をK、月をm、日をq、曜日をhとする。ただし求めたい日の月が1月、2月の場合はそれぞれ前年の13月、14月とする(例えば、2007年1月1日なら2006年13月1日と考える)。
|
||
</p>
|
||
|
||
<p>
|
||
<a href="http://upload.wikimedia.org/math/2/7/8/278e8aed83117a55800e1ed99c6dbe0a.png" onclick="__gaTracker('send', 'event', 'outbound-article', 'http://upload.wikimedia.org/math/2/7/8/278e8aed83117a55800e1ed99c6dbe0a.png', '');" class="http-image" target="_blank"><img src="http://upload.wikimedia.org/math/2/7/8/278e8aed83117a55800e1ed99c6dbe0a.png" class="http-image" alt="http://upload.wikimedia.org/math/2/7/8/278e8aed83117a55800e1ed99c6dbe0a.png" /></a>
|
||
</p>
|
||
|
||
<p>
|
||
<cite><a href="http://ja.wikipedia.org/wiki/%E3%83%84%E3%82%A7%E3%83%A9%E3%83%BC%E3%81%AE%E5%85%AC%E5%BC%8F" onclick="__gaTracker('send', 'event', 'outbound-article', 'http://ja.wikipedia.org/wiki/%E3%83%84%E3%82%A7%E3%83%A9%E3%83%BC%E3%81%AE%E5%85%AC%E5%BC%8F', 'ツェラーの公式 – Wikipedia');" target="_blank">ツェラーの公式 – Wikipedia</a></cite>
|
||
</p>
|
||
</blockquote>
|
||
|
||
<p>
|
||
これをgaucheで置き換えてみました:
|
||
</p>
|
||
|
||
<pre class="syntax-highlight">
|
||
<span class="synSpecial">(</span>define <span class="synSpecial">(</span>day_of_week? year month day<span class="synSpecial">)</span>
|
||
<span class="synComment">;; 求めたい日の月が1月、2月の場合はそれぞれ前年の13月、14月とする</span>
|
||
<span class="synSpecial">(</span><span class="synStatement">when</span> <span class="synSpecial">(</span><span class="synStatement">=</span> month <span class="synConstant">1</span><span class="synSpecial">)</span>
|
||
<span class="synSpecial">(</span><span class="synStatement">set</span>! month <span class="synSpecial">(</span><span class="synStatement">+</span> month <span class="synConstant">12</span><span class="synSpecial">))</span>
|
||
<span class="synSpecial">(</span><span class="synStatement">set</span>! year <span class="synSpecial">(</span><span class="synStatement">-</span> year <span class="synConstant">1</span><span class="synSpecial">)))</span>
|
||
<span class="synSpecial">(</span><span class="synStatement">when</span> <span class="synSpecial">(</span><span class="synStatement">=</span> month <span class="synConstant">2</span><span class="synSpecial">)</span>
|
||
<span class="synSpecial">(</span><span class="synStatement">set</span>! month <span class="synSpecial">(</span><span class="synStatement">+</span> month <span class="synConstant">12</span><span class="synSpecial">))</span>
|
||
<span class="synSpecial">(</span><span class="synStatement">set</span>! year <span class="synSpecial">(</span><span class="synStatement">-</span> year <span class="synConstant">1</span><span class="synSpecial">)))</span>
|
||
<span class="synComment">;; ツェラーの公式</span>
|
||
<span class="synSpecial">(</span><span class="synStatement">let*</span>
|
||
<span class="synSpecial">((</span>j <span class="synSpecial">(</span>quotient year <span class="synConstant">100</span><span class="synSpecial">))</span> <span class="synComment">;作業用の変数</span>
|
||
<span class="synSpecial">(</span>k <span class="synSpecial">(</span>modulo year <span class="synConstant">100</span><span class="synSpecial">))</span> <span class="synComment">;作業用の変数</span>
|
||
<span class="synSpecial">(</span>result <span class="synComment">;ツェラーの公式で計算している部分</span>
|
||
<span class="synSpecial">(</span>modulo <span class="synSpecial">(</span><span class="synStatement">-</span> <span class="synSpecial">(</span><span class="synStatement">+</span> day <span class="synSpecial">(</span>quotient <span class="synSpecial">(</span><span class="synStatement">*</span> <span class="synSpecial">(</span><span class="synStatement">+</span> month <span class="synConstant">1</span><span class="synSpecial">)</span> <span class="synConstant">26</span><span class="synSpecial">)</span> <span class="synConstant">10</span><span class="synSpecial">)</span> k <span class="synSpecial">(</span>quotient k <span class="synConstant">4</span><span class="synSpecial">)</span> <span class="synSpecial">(</span>quotient j <span class="synConstant">4</span><span class="synSpecial">))</span> <span class="synSpecial">(</span><span class="synStatement">*</span> j <span class="synConstant">2</span><span class="synSpecial">))</span> <span class="synConstant">7</span><span class="synSpecial">)))</span>
|
||
<span class="synComment">;; PerlのDate::Simpleのdays_of_weekメソッドと同じ返り値に変更</span>
|
||
<span class="synComment">;; ツェラーの公式は「0なら土曜日、1なら日曜日、2なら月曜日、……、6なら金曜日」</span>
|
||
<span class="synComment">;; これを「0なら日曜日、1なら月曜日、2なら火曜日、……、6なら土曜日」に変更</span>
|
||
<span class="synSpecial">(</span><span class="synStatement">if</span> <span class="synSpecial">(</span><span class="synStatement">=</span> result <span class="synConstant"></span><span class="synSpecial">)</span> <span class="synConstant">6</span>
|
||
<span class="synSpecial">(</span><span class="synStatement">-</span> result <span class="synConstant">1</span><span class="synSpecial">))</span>
|
||
<span class="synSpecial">))</span>
|
||
</pre>
|
||
|
||
<p>
|
||
詰まった点は2点ありました。一つ目はgauche(lisp)では変数は非破壊的に扱われるいうことです。具体的に言うと、
|
||
</p>
|
||
|
||
<pre class="syntax-highlight">
|
||
<span class="synSpecial">(</span><span class="synStatement">when</span> <span class="synSpecial">(</span><span class="synStatement">=</span> month <span class="synConstant">1</span><span class="synSpecial">)</span>
|
||
<span class="synSpecial">(</span><span class="synStatement">set</span>! month <span class="synSpecial">(</span><span class="synStatement">+</span> month <span class="synConstant">12</span><span class="synSpecial">))</span>
|
||
<span class="synSpecial">(</span><span class="synStatement">set</span>! year <span class="synSpecial">(</span><span class="synStatement">-</span> year <span class="synConstant">1</span><span class="synSpecial">)))</span>
|
||
</pre>
|
||
|
||
<p>
|
||
この部分をこんな風に書いていました:
|
||
</p>
|
||
|
||
<pre class="syntax-highlight">
|
||
<span class="synSpecial">(</span><span class="synStatement">when</span> <span class="synSpecial">(</span><span class="synStatement">=</span> month <span class="synConstant">1</span><span class="synSpecial">)</span>
|
||
<span class="synSpecial">(</span><span class="synStatement">+</span> month <span class="synConstant">12</span><span class="synSpecial">)</span>
|
||
<span class="synSpecial">(</span><span class="synStatement">-</span> year <span class="synConstant">1</span><span class="synSpecial">))</span>
|
||
</pre>
|
||
|
||
<p>
|
||
これだとmonth, yearの値が変わらない。。。破壊的な変数の操作を行うset!を使うべきところでした。
|
||
</p>
|
||
|
||
<p>
|
||
二つ目はletで先に宣言した変数を次の変数で使うためにはlet*を使わなければいけないということです。具体的には、
|
||
</p>
|
||
|
||
<pre class="syntax-highlight">
|
||
<span class="synSpecial">(</span><span class="synStatement">let*</span>
|
||
<span class="synSpecial">((</span>j <span class="synSpecial">(</span>quotient year <span class="synConstant">100</span><span class="synSpecial">))</span> <span class="synComment">;作業用の変数</span>
|
||
<span class="synSpecial">(</span>k <span class="synSpecial">(</span>modulo year <span class="synConstant">100</span><span class="synSpecial">))</span> <span class="synComment">;作業用の変数</span>
|
||
<span class="synSpecial">(</span>result <span class="synComment">;ツェラーの公式で計算している部分</span>
|
||
<span class="synSpecial">(</span>modulo <span class="synSpecial">(</span><span class="synStatement">-</span> <span class="synSpecial">(</span><span class="synStatement">+</span> day <span class="synSpecial">(</span>quotient <span class="synSpecial">(</span><span class="synStatement">*</span> <span class="synSpecial">(</span><span class="synStatement">+</span> month <span class="synConstant">1</span><span class="synSpecial">)</span> <span class="synConstant">26</span><span class="synSpecial">)</span> <span class="synConstant">10</span><span class="synSpecial">)</span> k <span class="synSpecial">(</span>quotient k <span class="synConstant">4</span><span class="synSpecial">)</span> <span class="synSpecial">(</span>quotient j <span class="synConstant">4</span><span class="synSpecial">))</span> <span class="synSpecial">(</span><span class="synStatement">*</span> j <span class="synConstant">2</span><span class="synSpecial">))</span> <span class="synConstant">7</span><span class="synSpecial">)))</span>
|
||
<span class="synComment">;; PerlのDate::Simpleのdays_of_weekメソッドと同じ返り値に変更</span>
|
||
<span class="synComment">;; ツェラーの公式は「0なら土曜日、1なら日曜日、2なら月曜日、……、6なら金曜日」</span>
|
||
<span class="synComment">;; これを「0なら日曜日、1なら月曜日、2なら火曜日、……、6なら土曜日」に変更</span>
|
||
<span class="synSpecial">(</span><span class="synStatement">if</span> <span class="synSpecial">(</span><span class="synStatement">=</span> result <span class="synConstant"></span><span class="synSpecial">)</span> <span class="synConstant">6</span>
|
||
<span class="synSpecial">(</span><span class="synStatement">-</span> result <span class="synConstant">1</span><span class="synSpecial">))</span>
|
||
<span class="synSpecial">)</span>
|
||
</pre>
|
||
|
||
<p>
|
||
この部分をletにしてしまうと、resultでj,kが使えないです(正確に言うと、letの中で評価される順番は不定だから、resultが一番最初に評価されてj,kがundefになる可能性がある…ということらしい)。letの中で上から順番に評価したい場合は、let*を使えば良いとのこと。
|
||
</p>
|
||
</div>
|