歴史的な間違いとその解決方法
TL;DR: ほとんどの言語は、うるう年の計算の正しい動作を見つけることができません。
免責事項: 私はさまざまなプログラミング言語について正確な洞察を提供するために最善を尽くしてきましたが、すべての言語の専門家ではない可能性があることを認めます。間違いを見つけたり、同意できない点がある場合は、敬意を持ってコメントを残してください。すぐに対処します。
ある年がうるう年であるかどうかを判断するのは、単純な数学的問題です。
すべての生徒が最初のプログラミング課題として解くことができます。
問題を単純化するために、年が 4 で割り切れる場合は閏年とします。ただし、100 で割り切れる場合は除きますが、400 で割り切れる場合は閏年とします。
現実世界と宇宙の力学はもう少し複雑ですが、それはこの記事の範囲を超えています。
いくつかのプログラミング言語がこの問題をどのように解決するかを見てみましょう。
PHP:
<?php $yearNumber = 2024; $isLeap = date('L', mktime(0, 0, 0, 1, 1, $yearNumber));
SQL (PostgreSQL):
SELECT (EXTRACT(year FROM TIMESTAMP '2024-02-29') IS NOT NULL) AS is_leap_year;
これらの言語は、有効な (または無効な) うるう日を作成し、真実の値を利用しようとします。
このハッキングはフェイルファストの原則に違反し、数十億ドルの間違いを悪用します。
無効な日付を作成しようとすると、現実世界のドメインで発生するため、本格的な言語では例外がスローされるはずです。
間違いを水面下に隠すなど、他の行為を行うと、驚きを最小化する原則に違反します。
エイダ:
function Is_Leap_Year (Year : Integer) return Boolean is begin return (Year mod 4 = 0 and then Year mod 100 /= 0) or else (Year mod 400 = 0); end Is_Leap_Year;
C/C++:
bool isLeapYear(int year) { return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); }
行く:
package main import ( "fmt" "time" ) func isLeapYear(year int) bool { return year%4 == 0 && (year%100 != 0 || year%400 == 0) }
ハスケル:
import Data.Time.Calendar (isLeapYear) let year = 2024 let isLeap = isLeapYear year
JavaScript/TypeScript:
function isLeapYear(year) { return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0); }
ジュリア:
using Dates year = 2024 isleap(year)
ルア:
local year = 2024 local isLeap = (year % 4 == 0 and year % 100 ~= 0) or (year % 400 == 0)
MATLAB:
year = 2024; isLeap = mod(year, 4) == 0 && (mod(year, 100) ~= 0 || mod(year, 400) == 0);
目標-C:
int yearNumber = 2024; BOOL isLeap = (yearNumber % 4 == 0 && yearNumber % 100 != 0) || (yearNumber % 400 == 0);
パワーシェル:
$yearNumber = 2024 $isLeap = ($yearNumber % 4 -eq 0 -and $yearNumber % 100 -ne 0) -or ($yearNumber % 400 -eq 0)
さび:
fn is_leap_year(year: i32) -> bool { (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0) }
雑談:
| yearNumber | yearNumber := 2024. (yearNumber \\ 4 = 0) and: [(yearNumber \\ 100 ~= 0) or: [ yearNumber \\ 400 = 0 ]]
上記の言語はネイティブ サポートを提供しません。
グローバル関数を定義するか、ヘルパーを使用する必要があります。
PHP (もう一度):
<?php $yearNumber = 2024; $isLeap = checkdate(2, 29, $yearNumber);
R:
leap_year(2024)
ルビィ:
year = 2024 is_leap = Date.leap?(year)
迅速:
let yearNumber = 2024 let isLeap = Calendar.current.isDateInLeapYear( Date(timeIntervalSince1970: TimeInterval(yearNumber)))
これらの言語は、グローバル関数を使用して、年がうるうかどうかを確認します。
これらのユーティリティグローバル メソッドは、誤って責任を間違った場所 (グローバル アクセス ポイント) に置きます。
C#:
int yearNumber = 2024; bool isLeap = System.DateTime.IsLeapYear(yearNumber);
ダーツ:
import 'package:intl/intl.dart'; var year = 2024; var isLeap = DateTime(year).isLeapYear;
パール:
use Time::Piece; my $yearNumber = 2024; my $isLeap = Time::Piece ->strptime("$yearNumber-01-01", "%Y-%m-%d")->leapyear;
パイソン:
import calendar leap = calendar.isleap(2024)
Visual Basic .NET:
Dim year As Integer = 2024 Dim isLeap As Boolean = DateTime.IsLeapYear(year)
これらの言語は、年がうるうかどうかをチェックするためのライブラリとしてヘルパーを使用します。
間違って配置されたものは、実際のオブジェクトには存在せず、 DateTime 関連の関数の中に存在します。
ジャバ:
int yearNumber = 2024; boolean isLeap = java.time.Year.of(yearNumber).isLeap();
コトリン:
val yearNumber = 2024 val isLeap = java.time.Year.of(yearNumber).isLeap
スカラ:
val year = 2024 val isLeap = java.time.Year.of(year).isLeap
これらの言語は、年に基づいて閏かどうかを確認します。
全単射ではプロトコルがより現実世界に近くなります
これにより全単射も壊れてしまうため、 IntegerオブジェクトではなくYearオブジェクトが作成されることに注意してください。
Yearには整数とは異なるプロトコルがあり、 Year を整数としてモデル化することは、時期尚早な最適化の匂いであり、 what と howが混在している兆候となります。
Year は、それが閏であるかどうかを知ることができ (整数では閏を行うべきではありません)、その月についても知ることができます (これは、 0 から始まる整数、1 から始まる整数、または文字列ではなく、 Months です)。
逆に、 Integerの機能は乗算や累乗などの算術演算に拡張されます。
ある時点をfloat 、 integer 、またはその他のデータ型で表すと、結果が生じます。
現実世界では、ある時点をほんの少しずつ区切ることができます (ただし、 小さすぎることはありません)。
floatの使用は有効なオプションではありません。
0.01 + 0.02 は0.03 ではありません。これは、浮動小数点時点の処理に恐ろしい結果をもたらします。
うるう年の話をしてきました。
その年が閏年であるかどうかを知る必要があるのは何ですか?
モデル化する日付と時刻のメカニズムでは、2024 年 2 月 28 日の後継を知る必要があります。
しかし、これはあなたの問題ではありません。
情報隠蔽の原則に従い、プライベート プロトコルとして責任を負う必要があります。
特効薬はありません。
言語を賢く使用してください。
今日 2 月 29 日は、毎日使用しているツールについて立ち止まって振り返るための閏日です。
4年後にお会いしましょう。