user855
user855

Reputation: 19918

How to write this piece of code in Clojure

If I have something like this:

int foo() {
    if(somecondition) {
       // some code
       if(cond2) return -1;
       // some code again
    }
    if(cond3){
       // something again
    }
    return bar();
 }

How do I express it in Clojure?

it should not be written as if_elseif because both the somecondition and cond3 may be true and both may be execute.

From the suggestions below, I am suggesting one more solution. Please let me know if that is right or not:

(defn foo []
    (if (somecondition)
        ( (some-code) (if (cond2) -1 (some-code-again)) )
        1 ; empty statement
    )
    (if (cond3) (something-again) 1)
    (bar))

Upvotes: 2

Views: 448

Answers (6)

M Smith
M Smith

Reputation: 2028

First I defined some helper functions to verify when the functions are being called.

(defn some-code [] (println "some code"))
(defn some-code-again [] (println "some code again"))
(defn something-again [] (println "something again"))
(defn bar [] "bar")

Then I defined foo like this accepting the three conditionals so I could test it:

(defn foo [somecondition cond2 cond3]
  (if (and  somecondition cond2)
    (do (when somecondition (some-code)) -1)
    (do
      (when somecondition (some-code))
      (when cond3 (something-again))
      (bar))))

I took couple of liberties. First, the C code might allow cond2 to be set in some code which could be handled as well with a let statement. Also, I wouldn't normally have the do clauses but since I have no idea what the code really does, it is not possible to make meaningful function names.

Another style note: In C it is usually bad style to have multiple return statements in a function. Another bonus of converting to a functional style forced the paths through the code to become clearer.

EDIT: I verified this code with

(for [somecondition [true false] cond2 [true false] cond3 [true false] ]  
    (do 
        (println "========") 
        (println "round" somecondition cond2 cond3) 
        (println "result:" (foo somecondition cond2 cond3))))

Ignore the nils that showup, they are because println returns nil

Upvotes: 0

Carl Smotricz
Carl Smotricz

Reputation: 67750

You can undo the knots in the flow of control by refactoring some of the code out into a separate function; this will work in C and in Clojure too.

Here's my stab at it. Hand translated and not tested, so it could be buggy.

(defn foo []
  (let [foo2 (fn [] 
    (if cond3 "something again")
    (bar))]
    (if somecondition
      (do
        "some code"
        (if cond2
          -1
          (do
            "some code again"
            (foo2))))
      (foo2))))

UPDATE with some explanation.

Because ajay asked, I'd like to expose a little of the thinking that led me to the above solution.

; shortcuts

c1 = somecondition
c2 = cond2
c3 = cond3

; truth table (this helped me a bit with my thinking but didn't go directly into the design)

c1 c2 c3 results
----------------
T  T  .  "some code" -1
T  F  T  "some code" "some code again" "something again" bar()
T  F  F  "some code" "some code again" bar()
F  .  T  "something again" bar()
F  .  F  bar()

The way Clojure works, there's no "return". A Clojure function always returns the last expression to be evaluated. If we want to produce the same side effects (from "some code", "some code again" and "something again") and result as the C/Java code, we need to make the code run in such a way that the result is really the last thing executed.

The only way to be able to return -1 is to rework the code so there is a branch from the start that ends - really ends - at "-1". That means that the following code must be called from within one of the other IF branches. In fact, it appears in two branches. So as not to have to repeat the code, I pulled it into a function of its own.

Here's the translation of my code to pseudocode:

function foo2:

if c3 then
   "something again"
endif
bar()

function foo:

if c1 then
   "some code"
   if c2 then
     -1            <-- end 1
   else
     "some code again"
     foo2          <-- end 2
   endif
else
   foo2            <-- end 3
endif

end 1 is your tricky "return -1". end 2 includes "some code again", end 3 doesn't. Both end 2 and end 3 test c3, maybe do "something again" and then return bar().

Upvotes: 3

Jeremy Wall
Jeremy Wall

Reputation: 25237

That code example is ready made for when

(defn foo []
  (when somecondition
    ; code goes here
    (do-somecondition-code)
    (when cond2
       ; more code goes here
       (do-cond2-code)))
  (when cond3
      ; even more code here
      (do-cond3-code))
  (bar))

The code is clear readable and concise and does exactly what you specified.

Upvotes: 0

Arthur Ulfeldt
Arthur Ulfeldt

Reputation: 91534

Lets start off with some literal translations to establish a common ground:

int foo() {
(defn foo []

    if(somecondition) {
    (if somecondition 

       // some code
       (some-code 1 2 3)

       if(cond2) return -1;
       (if cond2 
          -1
          // some code again
          (some-code 1 2 3)

        }
              if(cond3){
              // something again
              }
              (if cond3
                  (something :again)

                  return bar();
                  (bar)))))
 }

We have to adjust this to make it what could be described at "all one big long return statement" or as people that really dig functional programming call it "a function".

(defn helper-function []
   (if cond3
      (something again))
   bar))

(defn foo []
    (if somecondition 
      (some-code 1 2 3)
      (if cond2 
          -1
          (helper-function)
     (helper-function)

The function has two places where the return point is determined. in clojure this is just the ends of the function. A clojure function returns the result of the last expression evaluated before the function is over otherwise we would be writing return everywhere. (helper-function) had to be called twice because there are two code paths that use it.

Upvotes: 1

Timothy Pratley
Timothy Pratley

Reputation: 10662

I think it would look something like this:

(defn foo
  []
  (if somecondition
    (do
      ; some code
      (if cond2
        -1
        (do
          ; somecode again
          (if cond3
            (do
              ; something again))
          (bar))))
    (do
      (if cond3
        (do
          ; something again))
      (bar))))

How ugly - don't do that :)

As far as I know the lack of control jumps is by design. This function is entirely side-effect driven which is a red flag that maybe the underlying problem may be better represented in another way, but it is hard to give any real advice as the example is entirely abstract. CL has a return-from which is rarely used "because all Lisp expressions, including control constructs such as loops and conditionals, evaluate to a value". There is no return-from in Clojure.

Upvotes: 1

dnolen
dnolen

Reputation: 18556

You could argue that this is bad style since it's hard to understand the flow of control unless of course this pattern is being used to return error conditions (which is common in C). Clojure supports exceptions. If you really want to send control elsewhere use them.

Upvotes: 3

Related Questions