为什么我必须在此代码中使用(flush)?

时间:2012-03-18 04:33:27

标签: clojure

这是我与Clojure的第一次接触,因此我尝试编写简单的脚本,提供基于维基百科的翻译(欢迎任何评论/评论)

问题是:当我从翻译中删除(刷新)时,脚本输出nil而不是翻译的单词。为什么会这样?我显然遗漏了什么,但是什么? (println translations)得到的结果与flush相同(开头我尝试使用doseq / doall,但没有结果)

(使用Clojure 1.2并在逆时针方向上使用eclipse 3.7.2进行测试)

代码:

(ns wiki-translate
    (:require [clojure.contrib.http.agent :as h])
)

(defn get-url
    ([lg term] (str "http://" lg ".wikipedia.org/wiki/" term))
)

(defn fetch-url
    ([url] (h/string (h/http-agent url)))
)

(defn get-translations
    ([cnt]  (apply sorted-map (flatten (for  [s (re-seq #"(?i)interwiki-([^\"]+).*wiki\/([^\"]+)\".*/a>" cnt)] [(s 1) (s 2)])))))

(defn translate
    [term src-lg tgt-lg] (
        (def translations (get-translations (fetch-url (get-url src-lg term)))  )
        (flush)
        (if (contains?  translations tgt-lg) (get translations tgt-lg) "<NOT FOUND>")               
    )           
)

(println (translate "Shark" "en" "fr"))

2 个答案:

答案 0 :(得分:4)

translate函数有一个额外的括号级别,(flush)使其无意中起作用。没有(flush),代码为

((def translations (get-translations (fetch-url (get-url src-lg term))))
 (if (contains?  translations tgt-lg) (get translations tgt-lg) "<NOT FOUND>"))

Clojure根据its evaluation rules通过评估两个子表单并将第一个作为函数调用来评估此表单。在评估子表单后,表单变为

(#'translations
 "Requin")

因为第一个表单返回定义的Var,并及时定义它以使第二个表单在查找中成功。当您将Var作为函数调用时,the call is delegated to the value of the Var(即地图)和map implements function call as look-up之后,效果是在地图中查找“Requin”。地图没有该键的元素,因此值为nil。

在两者之间添加(flush)时,会发生相同的过程:

((def translations (get-translations (fetch-url (get-url src-lg term))))
 (flush)
 (if (contains?  translations tgt-lg) (get translations tgt-lg) "<NOT FOUND>"))
首先将

评估为

(#'translations
 nil
 "Requin")

再次调用#'translations值的地图。这次效果是查找nil,“Requin”作为在地图中找不到nil的情况下返回的默认值。

答案 1 :(得分:3)

您需要在翻译功能中使用let而不是def

(defn translate
    [term src-lg tgt-lg] 
    (let [translations (get-translations (fetch-url (get-url src-lg term)))]
      (if (contains?  translations tgt-lg) (get translations tgt-lg) "<NOT FOUND>")))   

let用于为let块中的表单创建本地绑定。使用def创建全局绑定(在当前命名空间中)。因此,基本上在执行退出代码之后,您可以在函数外部使用var transalations,因为它是使用全局范围的def创建的。

我不确定flush与def有什么关系才能使它工作。可能对def工作有深入了解的人可以对此有所了解,这肯定是一件有趣的事情。

<强>更新

有趣的是,将函数体包装在do中使其与def一起使用而没有刷新,但这不是你应该做的事情。使用let是首选方式。 do用于执行一系列具有副作用的表达式,因此似乎def是一种副作用表达式并使用doflush使其“实际上” “执行副作用。