如果可能,我正在寻找一种更惯用的方法来编写以下clojure代码:
(import '(System.Net HttpWebRequest NetworkCredential)
'(System.IO StreamReader))
(defn downloadWebPage
"Downloads the webpage at the given url and returns its contents."
[^String url ^String user ^String password]
(def req (HttpWebRequest/Create url))
(.set_Credentials req (NetworkCredential. user password ""))
(.set_UserAgent req ".NET")
(def res (.GetResponse req))
(def responsestr (.GetResponseStream res))
(def rdr (StreamReader. responsestr))
(def content (.ReadToEnd rdr))
(.Close rdr)
(.Close responsestr)
(.Close res)
content
)
这是在ClojureCLR上工作的。 (事实上它是CLR变体并不重要)
我想摆脱defs(替换为let?他们可以互相引用吗?)
如何更好地获取流 - 请记住..链接不起作用,因为我需要稍后关闭流。
编辑:在回答之后,我发现在.NET中使用WebClient类下载网页更容易。我仍然使用了许多Michal推荐的方法 - 只想记录我现在认为最好的答案:(defn download-web-page
"Downloads the webpage at the given url and returns its contents."
[^String url ^String user ^String password]
(with-open [client (doto (WebClient.)
(.set_Credentials (NetworkCredential. user password "")))]
(.DownloadString client url)))
答案 0 :(得分:6)
来自问题的代码可以像这样公平地重写(模数任何拼写错误 - 这里希望没有):
(defn download-web-page
"Downloads the webpage at the given url and returns its contents."
[^String url ^String user ^String password]
(let [req (doto (HttpWebRequest/Create url)
(.set_Credentials (NetworkCredential. user password ""))
(.set_UserAgent ".NET"))
response (.GetResponse req)
response-stream (.GetResponseStream res)
rdr (StreamReader. response-stream)
content (.ReadToEnd rdr)]
(.Close rdr)
(.Close response-stream)
(.Close response)
content))
假设 with-open
的.NET版本在绑定对象上调用.Close
(正如我预期的那样,但无法检查 - 手头没有.NET REPL并且.readToEnd
急切地消耗整个流,这可以进一步简化为
更新:刚刚检查了ClojureCLR的with-open
在绑定对象上调用.Dispose
。如果可以代替.Close
,那就太好了;如果需要.Close
,您可以编写自己的with-open
版本来使用.Close
代替(可能会复制大部分the original):
(defn download-web-page
"Downloads the webpage at the given url and returns its contents."
[^String url ^String user ^String password]
(let [req (doto (HttpWebRequest/Create url)
(.set_Credentials (NetworkCredential. user password ""))
(.set_UserAgent ".NET"))]
(with-open [response (.GetResponse req)
response-stream (.GetResponseStream res)
rdr (StreamReader. response-stream)]
(.ReadToEnd rdr))))
一些意见:
除非您确实知道自己需要这样做,否则请勿在顶层使用def
,defn
等。 (实际上,如果您需要创建的对象在let
绑定的本地人之间关闭,那么在顶级let
内立即使用它们偶尔会很有用......任何比这更加时髦的东西都应该收到非常仔细审查!)
def
&公司创建顶级Vars或重置他们的根绑定;在程序的常规操作过程中这样做完全违背了Clojure的功能精神。也许更重要的是,从实际的POV来看,任何依赖于“拥有”一堆Vars的函数一次只能由一个线程执行;因此download-web-page
没有理由限制。
let
- 引入的绑定可能不是相互递归的;以后的绑定可能会引用早期的绑定,但不是相反。 letfn
可以引入相互递归的局部函数;其他类型的相互递归对象在顶层之外创建可能不太方便(尽管绝不是不可能的)。问题中的代码不依赖于相互递归的值,因此let
可以正常工作。