2010-09-01 45 views
6

我正在寻找一种更地道的方式,如果可能的话,习惯的方法来写下面的Clojure代码:编写.NET互操作功能

(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的(通过更换让?他们可以称呼对方?)

如何更好的方法要注意流 - 请记住,链接不起作用,因为我需要稍后关闭流。

编辑:在答案后,我发现在.NET中使用WebClient类下载网页的简单方法。我还使用了许多的米哈尔商推荐的方法 - 只是想记录一下我现在认为是最好的答案:

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

回答

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电话 .Close在绑定对象的.NET版本(因为我想到它可能,但不会是能够检查 - 手头没有.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)))) 

一些评论:

  1. 不要使用defdefn等除了顶级的任何地方,除非你真的知道你需要这样做。 (实际使用的时候,立即顶级let里面,如果你需要创建的对象来收,较let结合的当地人......什么比这更时髦应接受非常仔细推敲是偶尔有用!)

    def & Co.创建顶级变量或重置它们的根绑定;在程序的正常运行过程中这样做完全违背了Clojure的功能精神。也许更重要的是从一个实际的POV中,依赖于“拥有”一堆Vars的任何函数一次只能由一个线程执行;没有理由为什么download-web-page应该因此受到限制。

  2. let-引入的绑定可能不是相互递归的;后来的绑定可能指的是更早的绑定,但不是其他方式。相互递归的本地函数可以引入letfn;其他类型的相互递归对象在顶层之外创建可能不太方便(尽管绝不可能)。该问题的代码不依赖于相互递归的值,所以let工作正常。