开发者

let inside cond

I'm working with clojure and while I've dabbled with lisps before, I'm having trouble finding a clean way to nest let statements in cond statements. For example, consider the following function:

(defn operate-on-list [xs]
  (let [[unpack vector] (first xs)]
    (cond
      [(empty? xs)开发者_如何学C 'empty
       unpack vector
       :else (operate-on-list (rest xs))])))

It's a pretty standard recursive operation on a list, but it needs to do some work on the first element in the list before it works with the contents. The issue, of course, is that the list may be empty.

In this example, it wouldn't be hard to change unpack to ((first xs) 0) and vector to ((first xs) 1), but this quickly gets ugly if more work needs to be done on (first xs).

Is there any way to effectively use a let statement part-way through a cond?

Thanks.

-Nate


In cases like these, you're best off using if-let:

(defn operate-on-list [xs]
  (if-let [[unpack v] (first xs)]
    (cond
      unpack v
      :else  (operate-on-list (rest xs)))))

This code walks the given list seq-able (list, vector, array...) of vectors and returns the second element of the first vector whose first element is true (meaning not false or nil). nil is returned if no such vector is found.

Note that vector is a built-in function, so I've chosen v as the variable name, just in case the need to use the function in the body arises in the future. More importantly, you're using too many brackets in your cond syntax; fixed in this version.

UPDATE: Two additional things worth noting about if-let:

  1. The way if-let works, if (first xs) happens to be nil (false would be the same), the destructuring binding never takes place, so Clojure won't complain about not being able to bind nil to [unpack v].

  2. Also, if-let accepts an else clause (in which you can't refer to the variables bound in if-let bindings vector -- though if you're in the else clause, you know they where false or nil anyway).



    ;use conditional let: http://richhickey.github.com/clojure-contrib/cond-api.html

    (use 'clojure.contrib.cond)

    (cond-let [b]  
      nil  b
      12  (prn (+ b 1))
      :else 17 )

    ;==> 13
    

Another good example can be found here http://www.mail-archive.com/clojure@googlegroups.com/msg03684.html


Sort of like this, with a let inside the scope of the cond?

(defn operate-on-list [list]
  (let [ el_first (first list) ]
    (cond
      (nil? el_first) (println "Finished")
      :else (do 
       (let [ list_rest (rest list) ]
                (println el_first)  
                (operate-on-list list_rest))))))

(operate-on-list '(1 2 3))

The output is:

1
2
3
Finished
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜