4

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.

2
  • 1
    Note that you likely shouldn't be trying to mutate multiple values like that. Trying to force imperative style in Clojure is going to lead to needlessly long, ugly code. Use a reduction or loop to update a variable while iterating a list. It's hard to make a suggestion without more context of what you're trying to do, but the use of atom that the answer is suggesting likely isn't necessary, or even preferable. Commented Sep 21, 2018 at 16:29
  • Don't def inside your functions. Use let instead. Commented Sep 23, 2018 at 8:37

3 Answers 3

7

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))
Sign up to request clarification or add additional context in comments.

Comments

5

You are looking through the wrong end of the telescope. Bear in mind that

  • Clojure has no local variables.
  • Clojure has no actions (usually called statements), only expressions returning a value.

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))
  • prints a,
  • prints b,
  • returns their sum.

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

  • local name bindings, with the let form and
  • a derived version of let 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.

Comments

1

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

3 Comments

I see your point but I'm having difficulty making the connection from this to my ask. My motivation is rolling stats for an RPG character. I want to receive 7 values, drop the lowest one, and return a sorted list of the remaining 6. I currently have a loop that keeps track of the smallest number. If the current value is larger, add it to the list. Otherwise, swap and add the previous lowest to the list. I'm still trying to figure out how to sort the atom list, but as far as logic, what I'm trying to do can be seen here: repl.it/repls/FractalPartialAccess any suggestions to implement?
@buddingprogrammer I'm having a hard time understanding what the intent of that code is. Going on the description you just gave though, here's a way that's much simpler than what you're trying: gist.github.com/carcigenicate/231923dcdb413ebc6c533bb1ca44621a
@buddingprogrammer: (defn roll-stat [] (drop 1 (sort (repeatedly 7 #(inc (rand-int 6))))))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.