paint-brush
Repensar los años bisiestos: por qué el enfoque de su lenguaje de programación favorito puede ser defectuosopor@mcsee
1,059 lecturas
1,059 lecturas

Repensar los años bisiestos: por qué el enfoque de su lenguaje de programación favorito puede ser defectuoso

por Maximiliano Contieri5m2024/02/29
Read on Terminal Reader

Demasiado Largo; Para Leer

La mayoría de los idiomas no logran encontrar el comportamiento correcto para el cálculo del año bisiesto.
featured image - Repensar los años bisiestos: por qué el enfoque de su lenguaje de programación favorito puede ser defectuoso
Maximiliano Contieri HackerNoon profile picture
0-item

Un error histórico y cómo solucionarlo


TL;DR: La mayoría de los idiomas no logran encontrar el comportamiento correcto para el cálculo del año bisiesto.


Descargo de responsabilidad: si bien he hecho todo lo posible para brindar información precisa sobre varios lenguajes de programación, reconozco que es posible que no sea un experto en todos. Si detecta un error o no está de acuerdo con algún punto, deje un comentario respetuoso y lo solucionaré de inmediato.

El estado del arte

Determinar si un año es bisiesto (o no) es un simple problema matemático.

Cada estudiante puede resolverlo como su primera tarea de programación.

Para simplificar el problema, supongamos que un año es bisiesto cuando es divisible por 4, excepto si también es divisible por 100, pero es bisiesto si es divisible por 400.

El mundo real y la mecánica cósmica son un poco más complicados, pero eso está más allá del alcance de este artículo.

Exploremos cómo varios lenguajes de programación resuelven este problema:

Enfoque horrible

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;

Estos lenguajes intentan crear un día bisiesto válido (o no válido) y explotar valores veraces .

Este truco viola el principio de falla rápida y abusa del error de mil millones de dólares .

Intentar crear una fecha no válida debería generar una excepción en lenguajes serios, ya que esto sucede en el dominio del mundo real .

Realizar otras acciones, como ocultar errores bajo la superficie, viola el principio del menor asombro.

Comportamiento perdido

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

Ir:

 package main import ( "fmt" "time" ) func isLeapYear(year int) bool { return year%4 == 0 && (year%100 != 0 || year%400 == 0) }

Haskel:

 import Data.Time.Calendar (isLeapYear) let year = 2024 let isLeap = isLeapYear year

JavaScript/Mecanografiado:

 function isLeapYear(year) { return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0); }

julia:

 using Dates year = 2024 isleap(year)

Lúa:

 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 objetivo:

 int yearNumber = 2024; BOOL isLeap = (yearNumber % 4 == 0 && yearNumber % 100 != 0) || (yearNumber % 400 == 0);

Potencia Shell:

 $yearNumber = 2024 $isLeap = ($yearNumber % 4 -eq 0 -and $yearNumber % 100 -ne 0) -or ($yearNumber % 400 -eq 0)

Óxido:

 fn is_leap_year(year: i32) -> bool { (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0) }

Charla:

 | yearNumber | yearNumber := 2024. (yearNumber \\ 4 = 0) and: [(yearNumber \\ 100 ~= 0) or: [ yearNumber \\ 400 = 0 ]]

Los idiomas anteriores no brindan soporte nativo.

Necesita definir funciones globales o utilizar ayudas .

Enfoque global incorrecto

PHP (otra vez):

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

R:

 leap_year(2024)

Rubí:

 year = 2024 is_leap = Date.leap?(year)

Rápido:

 let yearNumber = 2024 let isLeap = Calendar.current.isDateInLeapYear( Date(timeIntervalSince1970: TimeInterval(yearNumber)))

Estos lenguajes utilizan funciones globales para comprobar si un año es bisiesto.

Estos métodos globales de servicios públicos colocan erróneamente la responsabilidad en la ubicación incorrecta (un punto de acceso global).

Ayudantes mal enfoque

C#:

 int yearNumber = 2024; bool isLeap = System.DateTime.IsLeapYear(yearNumber);

Dardo:

 import 'package:intl/intl.dart'; var year = 2024; var isLeap = DateTime(year).isLeapYear;

Perla:

 use Time::Piece; my $yearNumber = 2024; my $isLeap = Time::Piece ->strptime("$yearNumber-01-01", "%Y-%m-%d")->leapyear;

Pitón:

 import calendar leap = calendar.isleap(2024)

Visual Basic .NET:

 Dim year As Integer = 2024 Dim isLeap As Boolean = DateTime.IsLeapYear(year)

Estos lenguajes utilizan ayudas como bibliotecas para comprobar si un año es bisiesto.

La responsabilidad fuera de lugar no está presente en un objeto real sino en una bolsa de funciones relacionadas con DateTime .

El año que se acerca

Java:

 int yearNumber = 2024; boolean isLeap = java.time.Year.of(yearNumber).isLeap();

Kotlin:

 val yearNumber = 2024 val isLeap = java.time.Year.of(yearNumber).isLeap

Escala:

 val year = 2024 val isLeap = java.time.Year.of(year).isLeap

Estos idiomas se basan en el Año para comprobar si es un salto.

El protocolo se acerca más al mundo real en la biyección.

Observe que crean objetos Year y no objetos Integer ya que esto también rompería la biyección .

Un Año tiene un protocolo diferente al de un número entero, y modelar un Año como un número entero también sería un olor a optimización prematuro y un síntoma de mezclar el qué y el cómo .

Un Año puede saber si es un salto (un número entero no debería hacerlo) y puede informarle sobre sus meses (que son Months , no enteros basados en 0 , enteros basados en 1 o cadenas).

Por el contrario, las capacidades de un Integer se extienden a operaciones aritméticas como la multiplicación y la exponenciación.

El tiempo no es una broma.

Representar un punto en el tiempo como flotante , entero o cualquier otro tipo de datos tiene consecuencias.

Puedes dividir un punto en el tiempo en el mundo real en fracciones pequeñas (pero no demasiado pequeñas )

Usar flotadores no es una opción válida.

0,01 + 0,02 no es 0,03, y esto tiene consecuencias terribles al tratar con puntos de coma flotante en el tiempo.

El reto

Hemos estado hablando de años bisiestos.

¿Cuáles son las necesidades para saber si un año es bisiesto?

La mecánica de fecha y hora que modelas necesita saber el sucesor del 28 de febrero de 2024.

Pero este NO es tu problema.

Siguiendo el principio de ocultación de información, debes dejar la responsabilidad como un protocolo privado.

Conclusión

No hay bala de plata .

Utilice su idioma sabiamente.

Hoy es 29 de febrero, un día bisiesto para hacer una pausa y reflexionar sobre las herramientas que utilizas a diario.

Nos vemos en 4 años.