Reputation: 151
As I've come to know it, the form of an if is (if [condition] [true] [false])
. Similarly, cond is (cond [condition] [true] ... [condition] [true] [false])
. Each true and false segment seems to only accept one action. If I want to represent the following logic:
if (i > 0)
{
a += 5;
b += 10;
}
I think I have to do:
(if (> i 0) (def a (+ a 5)))
(if (> i 0) (def b (+ b 10)))
Just so the second action isn't confused as a false result. Is this how it needs to be, or is there a way to create a larger body for an if?
p.s. I also suspect redefining a and b each time isn't the best way to increment, but also haven't seen a different way of doing that. I've had to also redefine lists when using conj.
Upvotes: 4
Views: 2047
Reputation: 13483
You are looking through the wrong end of the telescope. Bear in mind that
There is no Clojure equivalent of the statement a += 5;
.
However, expressions can have side effects: print
and the like accomplish nothing else. The do
form allows you to accomplish a series of side effects before returning a final value. For example,
(do (print a) (print b) (+ a b))
a
,b
,That's why, as you write of the if
form ...
Each true and false segment seems to only accept one action.
What Clojure does have is
let
form andlet
called loop
that implements a primitive
form of recursion that can replace simple uses of loops in
languages like C or Java.Between them, let
and its offspring loop
allow you to write most simple control structures. to determine if this applies to your program fragment ...
if (i > 0)
{
a += 5;
b += 10;
}
... we'd have to see the context.
However, here's a greatest common divisor function in C (untested)
long gcd (long i, long j)
{
long m = i, n = j;
while (n != 0)
{
long t = n;
n = m % n;
m = t;
}
}
and in Clojure
(defn gcd [i j]
(loop [m i, n j]
(if (zero? n)
m
(recur n (mod m n)))))
Both of these can be abbreviated.
Upvotes: 5
Reputation: 13185
The other answer covered the explicit question about having more than one expression in the if
branch (using do
or by using when
if there is no else branch as when
wraps its nested expressions implicit do
).
However, there is another issue in the question with using state which is usually local to the function. I don't think an atom
stored in a global var is the best way to handle that, and as Clojure programs tend to minimise global state it's usually better to keep the state local.
We use let
to define the local state and narrow its scope (NB it also supports destructuring):
(let [i 0
a 5
b 10]
(println i)
(println a)
(println b))
let
assigns a value to a local variable and it cannot be redefined. If we need to update local state we can use recursion by calling recur
directly on the function or by using loop
and recur
.
For example:
(defn plus [a b]
(if (> b 0)
(do
(println "Recurring...")
(recur (inc a) (dec b)))
(do
(println "Returning result")
a)))
Or:
(defn plus [a b]
(loop [a a
b b]
(if (> b 0)
(do
(println "Recurring...")
(recur (inc a) (dec b)))
(do
(println "Returning result")
a))))
Upvotes: 1
Reputation: 1332
The most direct transaction, using atoms instead of vars (def
), would be
;; assuming something like (def a (atom 0)) (def b (atom 0))
(if (> i 0)
(do
(swap! a + 5)
(swap! b + 10)))
or
(when (> i 0)
(swap! a + 5)
(swap! b + 10))
Upvotes: 7