8

when I call the math() function, to "times", the REPL returns nil. when I use "add", it works fine... help!

(defn math [opr x y ]
    (if(= opr "times")
        (* x y)
    )
    (if(= opr "add")
        (+ x y)
    )
)
(math "times" 8 8)
jeshaitan
  • 301
  • 1
  • 4
  • 16
  • 1
    Remember, the last expression of a function is the expression that is returned. In your code, if `opr` does not equal `"add"`, the return value is always `nil`. You may want to consider using a case statement. –  Feb 10 '14 at 15:38
  • Minor comment: (if x y) is an expression in clojure and not a statement. I figured I'd mention it to avoid possible further confusion: http://stackoverflow.com/questions/19132/expression-versus-statement Cheers – ClojureMostly Feb 11 '14 at 12:41

4 Answers4

18

The problem is that your function is a sequence of two-clause if-forms.

  • Clojure performs the elements of the sequence in turn, returning the result of the last.
  • If the condition fails, a two-clause if-form returns nil.

The quickest repair is, as WeGi suggested, to nest the ifs:

(defn math [opr x y]
  (if (= opr "times")
    (* x y)
    (if (= opr "add")
      (+ x y))))

However, there are better ways:

(defn math [opr x y]
  (case opr
    "add" (+ x y)
    "times" (* x y)))

... and, leaving C / Java idioms behind, ...

(defn math [opr x y]
  ((case opr
     "add" +
     "times" *)
   x y))

... or ...

(defn math [opr x y]
  (({"add" +, "times" *} opr) x y))
Thumbnail
  • 13,293
  • 2
  • 29
  • 37
14

I like using a cond statement for multiple conditions.

;; Will return nil if the cond statement fails all the predicates
(defn math [opr x y ]
  (cond (= opr "times") (* x y)
        (= opr "add") (+ x y)))

WeGi is correct in that Clojure will always return the last statement in the function.

amalloy
  • 89,153
  • 8
  • 140
  • 205
onit
  • 6,306
  • 3
  • 24
  • 31
5

condp works when all your predicates have the same structure:

(defn my-calc
  [operator x y]
    (condp = operator
      "times" (* x y)
      "plus" (+ x y))) 

=> (var user/my-calc)
(my-calc "times" 2 3)
=> 6
(my-calc "plus" 2 3)
=> 5
ChrisDevo
  • 1,222
  • 2
  • 13
  • 17
2

In Clojure the last statement is the Return Statement. So Clojure Checks for "times", even if it is true it checks then for "add" and because your call is made with "times" the second if evaluates to nil. Which is returned.

You could use do or nest your if statements to solve your Problem.

WeGi
  • 1,908
  • 1
  • 21
  • 33
  • 1
    `do` alone won't solve the problem, as `do` still returns its tail value. Using `do` would require a side-effect, and be nasty. `cond` or `if` nesting are the only really correct answers here. – arrdem Feb 10 '14 at 22:44