Kur*_*out 6 clojure clojureclr
如果可能的话,我正在寻找一种更惯用的方法来编写以下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
)
Run Code Online (Sandbox Code Playgroud)
这是在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)))
Run Code Online (Sandbox Code Playgroud)
来自问题的代码可以像这样公平地重写(模数任何拼写错误 - 这里希望没有):
(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))
Run Code Online (Sandbox Code Playgroud)
假设绑定对象的.NET版本with-open调用.Close(正如我预期的那样,但无法检查 - 手头没有.NET REPL)并且.readToEnd急切地消耗整个流,这可以进一步简化为
更新:刚刚检查了ClojureCLR 对绑定对象的with-open调用.Dispose.如果可以代替.Close,那很好; 如果.Close需要,您可以编写自己的版本with-open来.Close代替使用(可能复制大部分原文):
(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))))
Run Code Online (Sandbox Code Playgroud)
一些评论:
不要使用def,defn等等,除了在最顶层,除非你真的知道你需要做的任何地方.(实际上,let如果你需要创建的对象来关闭let当地人,那么立即在顶层使用它们偶尔会很有用......任何比这更加时髦的东西应该得到非常仔细的审查!)
def&Co.创建顶级Vars或重置其根绑定; 在程序的常规操作过程中这样做完全违背了Clojure的功能精神.也许更重要的是,从实际的POV来看,任何依赖于"拥有"一堆Vars的函数一次只能由一个线程执行; 没有理由download-web-page应该这样限制.
let - 引入的绑定可能不是相互递归的; 以后的绑定可能会引用早期的绑定,但不是相反.可以引入相互递归的本地函数letfn; 其他类型的相互递归对象在顶层之外创建可能不太方便(尽管绝不是不可能的).问题中的代码不依赖于相互递归的值,因此let工作正常.