2

I'm stuck using the and operator, how can you test for multiple conditions. I am very close but am stuck to solving this with clojure.

(defn leap [year] (cond (and (zero? (rem year 4)) (zero? (rem year 100))) true :else false))

Thank you for your help.

Andrew Barber
  • 39,603
  • 20
  • 94
  • 123
reZach
  • 8,945
  • 12
  • 51
  • 97
  • 3
    If you’ve solved your problem, you should either post your answer below, which you can accept after 48 hours, or, if you think your question/answer are of no relevance to future visitors, you can delete your question entirely. You should *not* simply edit your question with the solution. – Andrew Marshall Apr 09 '14 at 01:28

4 Answers4

5

you are using and properly, but your logic is wrong and should not be using and

(defn leap
  [year]
  (cond (zero? (mod year 400)) true
        (zero? (mod year 100)) false
        (zero? (mod year 4)) true
        :default false))

(this is according to the rules for leap years in the Gregorian Calendar as listed on the wikipedia page for Leap Year).

I have avoided any usage of nested logic operators because the purpose of cond is to simplify what would otherwise be a complex nested conditional into a linear sequence of choices where the first appropriate choice is selected.

Ideally one should be using a library like clj-time for any time / date logic, because these things are always much harder than anticipated to do properly and generally.

Additionally, one could use condp, though in this case I think it obfuscates more than it clarifies:

(condp #(zero? (mod %2 %))
    year
  400 true
  100 false
  4 true
  false)
noisesmith
  • 20,076
  • 2
  • 41
  • 49
4

Don’t reinvent the wheel, use clj-time’s number-of-days-in-the-month:

(require 'clj-time.core)

(defn leap-year? [year]
  (= 29 (clj-time.core/number-of-days-in-the-month year 2)))

or alternatively:

(defn leap-year? [year]
  (= 366 (clj-time.core/in-days
           (clj-time.core/interval (clj-time.core/date-time year 1 1)
                                   (clj-time.core/date-time (+ 1 year) 1 1)))))
Andrew Marshall
  • 95,083
  • 20
  • 220
  • 214
  • 1
    Or `(import java.util.Calendar)` and `(defn leap-year? [year] (-> (doto (Calendar/getInstance) (.set Calendar/YEAR year)) (.getActualMaximum Calendar/DAY_OF_YEAR) (> 365)))` – A. Webb Apr 10 '14 at 15:59
  • @A.Webb Which, for reference, is the same algorithm from [Java Code for calculating Leap Year](https://stackoverflow.com/a/1021373/211563). – Andrew Marshall Apr 11 '14 at 01:30
3

You forgot a case (what happens if the year is divisible by 400?), and the first condition isn't right. Here's another approach, as suggested in this answer:

(defn leap [year]
  (cond (not (zero? (mod year 4)))   false
        (not (zero? (mod year 100))) true
        (zero? (mod year 400))       true
        :else false))

Also from the same link, here's another potentially faster solution, albeit much harder to understand:

(defn leap [year]
  (and (zero? (bit-and year 3))
       (or (not (zero? (mod year 25)))
           (zero? (bit-and year 15)))))
Community
  • 1
  • 1
Óscar López
  • 232,561
  • 37
  • 312
  • 386
  • I like the source, and for those who are interested: http://stackoverflow.com/questions/3220163/how-to-find-leap-year-programatically-in-c/11595914#11595914 – reZach Apr 09 '14 at 01:42
0

Better, I think, without explicit true and false:

(defn leap? [year]
  (let [div? #(->> % (mod year) zero?)]
    (and (div? 4) (or (not (div? 100)) (div? 400)))))

We can maybe make it clearer by defining

(defmacro implies [p q] (or (not p) q))

... and expressing the body of the function thus:

(and (div? 4) (implies (div? 100) (div? 400)))

Note: leap? is a predicate, so conventionally ends with ?.

Thumbnail
  • 13,293
  • 2
  • 29
  • 37