Một sai lầm lịch sử và cách giải quyết
TL;DR: Hầu hết các ngôn ngữ không tìm được hành vi chính xác để tính năm nhuận.
Tuyên bố từ chối trách nhiệm: Mặc dù tôi đã cố gắng hết sức để cung cấp những hiểu biết chính xác về nhiều ngôn ngữ lập trình khác nhau nhưng tôi thừa nhận rằng tôi có thể không phải là chuyên gia về tất cả mọi ngôn ngữ. Nếu bạn phát hiện ra sai sót hoặc không đồng ý với bất kỳ điểm nào, vui lòng để lại nhận xét tôn trọng và tôi sẽ nhanh chóng giải quyết.
Việc xác định một năm có phải là năm nhuận hay không là một bài toán đơn giản.
Mọi học sinh đều có thể giải nó như bài tập lập trình đầu tiên của mình.
Để đơn giản hóa vấn đề, giả sử một Năm là năm nhuận khi nó chia hết cho 4, ngoại trừ nếu nó cũng chia hết cho 100, nhưng đó là năm nhuận nếu nó chia hết cho 400.
Thế giới thực và cơ học vũ trụ phức tạp hơn một chút, nhưng điều đó nằm ngoài phạm vi của bài viết này.
Hãy cùng khám phá cách một số ngôn ngữ lập trình giải quyết vấn đề này:
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;
Các ngôn ngữ này cố gắng tạo ra một ngày nhuận hợp lệ (hoặc không hợp lệ) và khai thác các giá trị trung thực .
Vụ hack này vi phạm nguyên tắc không nhanh và lạm dụng sai lầm tỷ đô .
Việc cố gắng tạo ngày không hợp lệ sẽ tạo ra ngoại lệ trong các ngôn ngữ nghiêm trọng vì điều này xảy ra trong miền thế giới thực .
Thực hiện các hành động khác, chẳng hạn như che giấu sai sót bên dưới bề mặt, vi phạm nguyên tắc ít gây ngạc nhiên nhất.
Ada:
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); }
Đi:
package main import ( "fmt" "time" ) func isLeapYear(year int) bool { return year%4 == 0 && (year%100 != 0 || year%400 == 0) }
Haskell:
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); }
Julia:
using Dates year = 2024 isleap(year)
Lua:
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);
Mục tiêu-C:
int yearNumber = 2024; BOOL isLeap = (yearNumber % 4 == 0 && yearNumber % 100 != 0) || (yearNumber % 400 == 0);
PowerShell:
$yearNumber = 2024 $isLeap = ($yearNumber % 4 -eq 0 -and $yearNumber % 100 -ne 0) -or ($yearNumber % 400 -eq 0)
Rỉ sét:
fn is_leap_year(year: i32) -> bool { (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0) }
Chuyện nhỏ:
| yearNumber | yearNumber := 2024. (yearNumber \\ 4 = 0) and: [(yearNumber \\ 100 ~= 0) or: [ yearNumber \\ 400 = 0 ]]
Các ngôn ngữ trên không cung cấp hỗ trợ gốc.
Bạn cần xác định các hàm toàn cục hoặc sử dụng hàm trợ giúp .
PHP (Một lần nữa):
<?php $yearNumber = 2024; $isLeap = checkdate(2, 29, $yearNumber);
R:
leap_year(2024)
Ruby:
year = 2024 is_leap = Date.leap?(year)
Nhanh:
let yearNumber = 2024 let isLeap = Calendar.current.isDateInLeapYear( Date(timeIntervalSince1970: TimeInterval(yearNumber)))
Những ngôn ngữ này sử dụng các hàm toàn cục để kiểm tra xem một năm có phải là bước nhảy vọt hay không.
Các phương thức toàn cầu của tiện ích này đặt nhầm trách nhiệm vào sai vị trí (điểm truy cập toàn cầu).
C#:
int yearNumber = 2024; bool isLeap = System.DateTime.IsLeapYear(yearNumber);
Phi tiêu:
import 'package:intl/intl.dart'; var year = 2024; var isLeap = DateTime(year).isLeapYear;
Perl:
use Time::Piece; my $yearNumber = 2024; my $isLeap = Time::Piece ->strptime("$yearNumber-01-01", "%Y-%m-%d")->leapyear;
Trăn:
import calendar leap = calendar.isleap(2024)
Visual Basic .NET:
Dim year As Integer = 2024 Dim isLeap As Boolean = DateTime.IsLeapYear(year)
Những ngôn ngữ này sử dụng trình trợ giúp làm thư viện để kiểm tra xem một năm có phải là bước nhảy vọt hay không.
Việc đặt sai vị trí có trách nhiệm không hiện diện trong một đối tượng thực mà trong một túi các hàm liên quan đến DateTime .
Java:
int yearNumber = 2024; boolean isLeap = java.time.Year.of(yearNumber).isLeap();
Kotlin:
val yearNumber = 2024 val isLeap = java.time.Year.of(yearNumber).isLeap
Scala:
val year = 2024 val isLeap = java.time.Year.of(year).isLeap
Những ngôn ngữ này dựa vào Năm để kiểm tra xem đó có phải là bước nhảy vọt hay không.
Giao thức gần với thế giới thực hơn trong song ngữ
Lưu ý rằng họ tạo các đối tượng Year chứ không phải các đối tượng Integer vì điều này cũng sẽ phá vỡ song ánh .
Năm có giao thức khác với số nguyên và việc lập mô hình Năm dưới dạng số nguyên cũng sẽ là một mùi tối ưu hóa sớm và là triệu chứng của việc trộn lẫn cái gì và cái gì .
Năm có thể cho biết đó có phải là bước nhảy vọt hay không (số nguyên không nên làm điều đó) và có thể cho bạn biết về các tháng của nó (là Tháng , không phải số nguyên dựa trên 0 , số nguyên dựa trên 1 hoặc chuỗi).
Ngược lại, khả năng của Integer mở rộng sang các phép tính số học như phép nhân và lũy thừa.
Việc biểu thị một thời điểm dưới dạng float , số nguyên hoặc bất kỳ loại dữ liệu nào khác sẽ dẫn đến hậu quả.
Bạn có thể chia một thời điểm trong thế giới thực thành những phân số rất nhỏ (nhưng không quá nhỏ )
Sử dụng phao không phải là một lựa chọn hợp lệ.
0,01 + 0,02 không phải là 0,03 và điều này gây ra hậu quả khủng khiếp khi xử lý các dấu phẩy động theo thời gian.
Chúng ta đang nói về những năm nhuận.
Những điều cần biết nếu một năm là một bước nhảy vọt?
Cơ chế ngày và giờ mà bạn lập mô hình cần biết ngày kế nhiệm ngày 28 tháng 2 năm 2024.
Nhưng đây KHÔNG phải là vấn đề của bạn.
Tuân theo nguyên tắc ẩn thông tin, bạn nên để lại trách nhiệm như một giao thức riêng tư.
Không có viên đạn bạc .
Sử dụng ngôn ngữ của bạn một cách khôn ngoan.
Hôm nay là ngày 29 tháng 2, một ngày nhuận để tạm dừng và suy ngẫm về những công cụ bạn sử dụng hàng ngày.
Hẹn gặp lại sau 4 năm nữa.