scape
scape

Reputation: 707

How could I write a function call once with an nested let in an if-let?

I have these functions:

(def i (atom {})) ;incremented/calculated file stats

(defn updatei [n fic fos]
  (swap! i conj {(keyword n)[fic fos]}))

(defn calcfo [fo fi fis]
  (if-let [n (get @i fo)] ;find a previous record?
    (let [fic (inc (first n)), fos (+ fis (second n))] ;increment the stats
      (updatei fo fic fos))
    (let [fic 1, fos fis] ;if not then: first calc recorded
      (updatei fo fic fos))))

How could I write (updatei fo fic fos) once, instead of having it listed twice in the function? Is there a secret or-let I am unaware of?

-Hypothetical code-

(defn calcfo [fo fi fis]
  (if-let [n (get @i fo)] ;find a previous record?
    (let [fic (inc (first n)), fos (+ fis (second n))] ;increment the stats
    (or-let [fic 1, fos fis] ;if not then: first calc recorded
      (updatei fo fic fos)))))

Or am I thinking of this too imperatively versus functionally?

EDIT:

I decided this made the most sense to me:

(defn calcfo [fo fis]
  (apply updatei fo
  (if-let [[rfc rfos] (get @i fo)] ;find a previous record?
       [(inc rfc) (+ rfos fis)] ;increment the stats
       [1 fis]) ;if not then: first calc recorded
      ))

Thanks for the great answers!

Upvotes: 2

Views: 110

Answers (3)

A. Webb
A. Webb

Reputation: 26446

A rearrangement might help

(defn calcfo [fo fi fis]
  (apply updatei fo
    (if-let [n (get @i fo)]
      [(inc (first n)), (+ fis (second n))]
      [1, fis] )))

Upvotes: 3

juan.facorro
juan.facorro

Reputation: 9920

What about using an if and then destructuring? Here's an approach:

(defn calcfo [fo fi fis]
  (let [n         (get @i fo)                                 ;find a previous record?
        [fic fos] (if n 
                    [(-> n first inc) (-> n second (+ fis))]  ;increment the stats
                    [1 fis])]                                 ;if not then: first calc recorded
      (updatei fo fic fos)))

The argument fi doesn't seem to be being used so maybe you could remove it from the argument list.

(defn calcfo [fo fis] ,,,)

The usage of first and second could also be avoided with the use of destructuring when binding n in the let form:

(defn calcfo [fo fis]
  (let [[x y & _]  (get @i fo)
        [fic fos]  (if x [(inc x) (+ fis y)] [1 fis])]
    (updatei fo fic fos)))

Upvotes: 3

Alex D
Alex D

Reputation: 30445

I think you would sidestep the whole problem and make your code better if you rewrote updatei, something like:

 (defn- safe+ [a b]
   (if a (if b (+ a b) a) b))
 (defn updatei [n fic fos]
   (swap! i update-in [(keyword n)] #(vector (safe+ fic (first %)) (safe+ fos (second %)))))

There may be a better way to write that code, but the basic idea is to use update-in to either store the new values (if nothing was stored for that key before), or combine them with what is already there.

Upvotes: 2

Related Questions