翔泳社「サンプルコード365+1」というカレンダーに自分のコードが掲載されることになりました。
2月のDevelopers Summitでの販売開始を予定しているようなので、見かけたらぜひご購入いただけると嬉しいです。(AmazonなどECサイトでの販売もあるようです)
※筆者は翔泳社とは一切無関係の一般エンジニアです
経緯
- ゲーム開発者向けイベント CEDEC の物販ブースにて、翔泳社さんの本を購入しました。
- その際に、翔泳社40周年の記念企画として「サンプルコード365+1」という企画について教えてもらいました。
- 曰く、翔泳社の中の人だけでなく一般のエンジニアからもコードを応募して、1冊の日めくりカレンダーに1日1コードを掲載するというものでした
- 折角なのでこの企画に応募したところ、みごと採用に至ったという経緯です。
- 自分の書いたコードが書籍(?)に載るのは初めてかもしれません。嬉しい
コードについて
コンセプト
- カレンダーに掲載されるコードということで、処理内容は暦に関するものにしようと思いました。
- Claudeくんにアイデアを求めたところ、「ツェラーの公式」というものを教えてもらいました。
- 詳細は後述しますが、この公式は年月日を与えるとその日が何曜日だったかを知ることができるというものです。
- そこまで複雑な公式ではないため、「30行程度」という応募条件を満たせると思いツェラーの公式を実装することに決めました。
ツェラーの公式の詳細
- 基本的には Wikipedia を見るのが早いです。(丸投げ)(Qiitaで数式を記述する方法を調べるのが面倒だった)
- Wikipediaでは第二項の26と10が何故か約分されていませんが、以下のコードでは約分して13と5として実装しています。
- 当然ですがグレゴリオ暦ですので、Γは
C/4 - 2Cで実装しています
実際のコード
自分が普段よく使っているC++と、個人的に興味を持っているRustの2つで実装しました。
- C++
#include <iostream>
#include <string>
int main() {
int year, month, day;
std::cout << "Input Year: ";
std::cin >> year;
std::cout << "Input Month: ";
std::cin >> month;
std::cout << "Input Day: ";
std::cin >> day;
if (month < 3) {
month += 12;
year--;
}
int dayOfMonth = day;
int yearLast2Digits = year % 100;
int yearFirst2Digits = year / 100;
int date = dayOfMonth + (13 * (month + 1)) / 5 + yearLast2Digits + (yearLast2Digits / 4)
+ (yearFirst2Digits / 4) - 2 * yearFirst2Digits;
date = (date % 7 + 7) % 7;
std::string daysOfWeek[] = {"Saturday", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday"};
std::cout << "The day of the week is: " << daysOfWeek[date] << std::endl;
return 0;
}
- Rust
use std::io;
fn main() {
let mut input_year = String::new();
println!("Input Year:");
io::stdin().read_line(&mut input_year);
let year: i32 = input_year.trim().parse().unwrap_or(0);
let mut input_month = String::new();
println!("Input Month:");
io::stdin().read_line(&mut input_month);
let month: i32 = input_month.trim().parse().unwrap_or(0);
let mut input_day = String::new();
println!("Input Day:");
io::stdin().read_line(&mut input_day);
let day: i32 = input_day.trim().parse().unwrap_or(0);
// Zeller’s congruence
let mut month_modified :i32 = month;
let mut year_modified: i32 = year;
if month < 3 {
month_modified = month + 12;
year_modified = year - 1;
}
let yearLast2Digits = year_modified % 100;
let yearFirst2Digits = year_modified / 100;
let f = day + (13 * (month_modified + 1)) / 5 + yearLast2Digits + yearLast2Digits / 4 + yearFirst2Digits / 4 - 2 * yearFirst2Digits;
let day_of_week = f % 7;
let day_names = ["Saturday", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday"];
println!("The day of the week is: {}", day_names[day_of_week as usize]);
}
解説
と言っても、ほぼツェラーの公式をそのまま実装しただけなのであまり解説することはありません。
C++版, Rust版ともにCLIから年月日情報の入力を受け取り、曜日情報を出力するという作りになっています。
-
yearLast2DigitsとyearFirst2Digitsの計算方法がそれぞれ% 100と/ 100なのが綺麗で気に入っています (偶然だとは思いますが) -
day_names配列に曜日の文字列を格納しておき、day_of_weekの計算値でそのままインデックスを指定すれば曜日を出力できるという作りは我ながら上手くできたんじゃないかと思っています (変数名はRust版で解説しています)- 少し前の自分なら switch で分岐とかやってそうな気がします。その場合に比べ圧倒的にコードが短く見やすくなっていると考えています
各種リンクなど
- カレンダー販売ページ : カレンダーの概要と、各種ECサイトへのリンク
- 企画ページ : このページからコードを投稿できました。(現在は募集は終わっています)
- 翔泳社40周年記念サイト : 本カレンダー以外にも、様々な記念本が発売されるようです