paint-brush
重新思考闰年:为什么你最喜欢的编程语言的方法可能有缺陷经过@mcsee
1,059 讀數
1,059 讀數

重新思考闰年:为什么你最喜欢的编程语言的方法可能有缺陷

经过 Maximiliano Contieri5m2024/02/29
Read on Terminal Reader

太長; 讀書

大多数语言无法找到闰年计算的正确行为
featured image - 重新思考闰年:为什么你最喜欢的编程语言的方法可能有缺陷
Maximiliano Contieri HackerNoon profile picture
0-item

一个历史错误以及如何解决它


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);

Objective-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 ]]

上述语言不提供本机支持。

您需要定义全局函数或使用helpers

错误的全球方法

PHP(再次):

 <?php $yearNumber = 2024; $isLeap = checkdate(2, 29, $yearNumber);

回复:

 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;

Python:

 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

这些语言依靠年份来检查是否是跳跃。

该协议在双射方面更接近现实世界

请注意,它们创建Year对象而不是Integer对象,因为这也会破坏双射

Year与整数有不同的协议,将 Year 建模为整数也会是一种过早的优化味道,也是混合“what”和“how”的症状。

Year可以判断它是否是一个闰年(整数不应该这样做),并且可以告诉您它的月份(是Months ,而不是基于 0 的整数、基于 1 的整数或字符串)。

相反, Integer的功能扩展到算术运算,例如乘法和求幂。

时间不是玩笑

将时间点表示为floatinteger或任何其他数据类型都会产生后果。

您可以将现实世界中的时间点分解为微小的分数(但不能太小

使用浮点数不是一个有效的选项。

0.01 + 0.02不是 0.03,这对于处理浮点时间点会产生可怕的后果。

挑战

我们一直在谈论闰年。

需要什么才能知道一年是否为闰年?

您建模的日期和时间机制需要知道 2024 年 2 月 28 日的后继日期。

但这不是你的问题。

遵循信息隐藏原则,您应该将责任保留为私有协议。

结论

没有银弹

明智地使用你的语言。

今天是 2 月 29 日,这是一个停下来反思一下您每天使用的工具的闰日。

4年后见。