2010-01-14 75 views
5

我正在和clojure一起工作,虽然我之前曾经和lisp一起讨论过,但我很难找到一种干净的方法在cond语句中嵌套let语句。例如,考虑下面的函数:let cond

(defn operate-on-list [xs] 
    (let [[unpack vector] (first xs)] 
    (cond 
     [(empty? xs) 'empty 
     unpack vector 
     :else (operate-on-list (rest xs))]))) 

这是名单上的一个非常标准的递归操作,但它需要做的列表中的第一个元素上的一些工作与它的内容的作品前。这个问题当然是列表可能是空的。

在这个例子中,它不会是很难改变unpack((first xs) 0)vector((first xs) 1),但如果更多的工作需要在(第一XS)做到了这一点很快变得难看。

有没有什么办法有效地使用let语句部分通过cond?

谢谢。

-Nate

+0

嗨发现 - 这是我不清楚你想在这里实现什么。您可能会发现这个有趣的http://www.assembla.com/spaces/clojure/tickets/200和http://groups.google.com/group/clojure/browse_thread/thread/c1097ce07506fc39请考虑说明该功能应该是什么做你的问题,以及一些示例输入和输出。该语法看起来不像一个有效的cond语句,因为cond的主体被包装在一个向量中。 – 2010-01-15 00:16:47

回答

11

在这样的情况下,你最好使用if-let

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

此代码走给出 列表 SEQ-能( list,vector,array ...)并返回第一个向量的第一个元素为true的第二个元素(意思是不是falsenil)。如果未找到此类矢量,则返回nil

请注意,vector是一个内置函数,所以我选择了v作为变量名,以防将来需要使用函数。更重要的是,你在cond语法中使用了太多的括号。在此版本中修复。

UPDATE:两个额外的事情值得一提的约if-let

  1. 的方式if-let作品,如果(first xs)恰好是nilfalse将是相同的),解构绑定从来没有发生,所以Clojure不会抱怨不能将nil绑定到[unpack v]

  2. 此外,if-let接受一个else子句(您可以在其中不是指if-let绑定向量绑定的变量 - 但如果你在else子句中的时候,你知道他们在哪里falsenil反正)。

+2

另请参阅'when-let',当你只有一个分支时更具惯用意义。 – kotarak 2010-01-15 09:05:40

+0

第二个注意事项:注意'if-let'在这里工作,因为列表应该包含向量。一般情况下'(when-let [x(first s)] ...)'* *不代替'(when-let [s(seq s)](let [f(first s)] ... ))'。 – kotarak 2010-01-15 09:09:15

2

有点像这样,用letcond的范围内?

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

输出是:

1 
2 
3 
Finished 
+4

你的'do'是多余的。 – 2010-01-15 19:46:46

+0

@Brian Carper:你是对的;你可以简单地省略do。对我而言,这是一个Elisp宿醉(特别是预后)。 – 2010-01-18 23:13:10