2014-04-22 23 views
1

我想包裝功能內的去例程(如下面的方法1)。方法2工作得很好,但方法1沒有。唯一的區別是在方法1中,我將一個通道作爲參數傳遞給一個函數,並且該函數位於函數內部。關於例程和函數的確切規則是什麼?圍繞功能調用去例行程序

(defn doit [ch i] 
    (print "g\n") 
    (async/>! ch i) 
    (print "f\n")) 

;方法1

(let [c1 (async/chan)] 
    (async/go (while true 
     (let [[v ch] (async/alts! [c1])] 
     (println "Read" v "from" ch)))) 
    (dotimes [i 10] 
    (async/go (doit c1 i)))) 

;方法2

(let [ch (async/chan)] 
    (async/go (while true  
     (let [[v ch] (async/alts! [ch])] 
      (println "Read" v "from" ch)))) 
    (dotimes [i 10] 
    (async/go 
     (print "g\n") 
     (async/>! ch i) 
     (print "f\n")))) 

我也注意到,如果我刪除了去的方法1,並將其移動到這樣做的功能,如下圖所示,該功能打印「g」但不是「f」,但其他方式正常工作。爲什麼?

(defn doit [ch i] 
    (async/go 
    (print "g\n") 
    (async/>! ch i) 
    (print "f\n"))) 

回答

1

>!<!是不是真的是被調用函數(見herehere)。宏go宏識別這兩個符號,並根據這兩個運算符的語義自動奇蹟地生成代碼。因此,這個宏無法知道函數在內部使用>!<!運算符,因爲它所獲得的只是調用該函數的表單。

方法1在每次調用實際上是拋出一個異常doit因爲實際的代碼>!<!只是一個說法,總是失敗。在lein repl開始的REPLy會話中評估此方法的代碼顯示了一堆例外(準確地說是10)的異常Exception in thread "async-dispatch-46" java.lang.AssertionError: Assert failed: >! used not in (go ...) block。如果您通過nREPL客戶端使用REPL,您可能不會看到這一點,因爲異常在服務器中異步拋出,客戶端沒有考慮到這一點。

此外,而不是使用(print "something\n")你可以只使用(println "something"),沒有真正與你的問題有關,但我想我提到了這一點。

+0

好的,謝謝。這仍然不能解釋爲什麼當我將宏移動到doit函數時,不會打印「f」字符串。 – Tim

+0

@Tim當用'go'塊評估'doit'函數時,在同一個REPL會話中(用'lein repl'開始),我看到'f's打印的交錯字符串'Read v from# ManyToManyChannel ...>'。您沒有看到它打印的原因可能與您沒有看到該異常的原因相同(即輸出異步發生,您的nREPL客戶端不能很好地處理)。 –