Use Rules

Leap
Leap in Crystal
module Year
  RULES = [
    p_divisible_by?(4),
    p_or(
      p_negate(p_divisible_by?(100)),
      p_divisible_by?(400)),
  ]

  def self.leap?(year) : Bool
    RULES.map(&.call(year)).all?
  end

  def self.p_divisible_by?(num : Int32) : Proc(Int32, Bool)
    ->(year : Int32) { year.divisible_by?(num) }
  end

  def self.p_negate(test : Proc(A, Bool)) : Proc(A, Bool) forall A
    ->(input : A) { !test.call(input) }
  end

  def self.p_or(a : Proc(A, Bool), b : Proc(A, Bool)) : Proc(A, Bool) forall A
    ->(input : A) { a.call(input) || b.call(input) }
  end
end

This solution relies heavily on the usage of Procs. Proc is a datatype that represents a function pointer, it is recommended that you read the procs-blocks concept page before continuing.

The RULES constant is an array of function pointers (Proc) that represent the rules that must be satisfied for a year to be a leap year. The first rule is that the year must be divisible by 4, the second rule is that the year must be divisible by 400 or not divisible by 100. The 2nd rule includes a 3 level deep nested Proc that is created using the p_or and p_negate methods. The leap? method takes a year and checks if all the rules are satisfied. It does that by mapping all the function pointer in the RULES array and since all the elements are Proc(Int32, Bool), then we can use the call method to call the function pointer with the year as an argument. The result of the map method is an array of Bools, since all the Procs return a Bool. Then we use the all? method to check if all the elements in the array are true.

The p_divisible_by? method takes a number and returns a Proc that takes a year and returns a Bool indicating if the year is divisible by the number. The forall annotation is used to indicate that the type A is generic and can be any type. The p_negate method takes a Proc and returns a Proc that negates the result of the given Proc. The p_or method takes two Procs and returns a Proc that returns true if either of the given Procs return true.

11th Dec 2024 · Found it useful?