2010-09-01 14 views
6

Tôi đang tìm kiếm một cách thành ngữ hơn, nếu có thể, để viết mã clojure sau:cách Idiomatic để viết chức năng NET interop

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

Đây là trên ClojureCLR và các công trình. (Thực tế là nó là biến thể CLR không quan trọng nhiều)

Tôi muốn thoát khỏi defs (thay thế bởi phép? Họ có thể tham khảo với nhau?)

Làm thế nào về một cách tốt hơn để truy cập luồng - lưu ý rằng .. chuỗi sẽ không hoạt động vì tôi cần Đóng các luồng sau này.

EDIT: Sau khi trả lời, tôi tìm thấy một cách dễ dàng hơn trong .NET để tải xuống một trang web bằng cách sử dụng lớp WebClient. Tôi vẫn còn sử dụng nhiều phương pháp tiếp cận đề nghị Michal của - chỉ muốn ghi lại những gì bây giờ tôi tin là câu trả lời tốt nhất:

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

Trả lời

6

Mã từ các câu hỏi có thể được viết lại khá idiomatically như vậy (modulo bất kỳ lỗi chính tả - đây là hy vọng có không có):

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

Giả sử các phiên bản .NET của with-open cuộc gọi .Close tại đối tượng ràng buộc (như tôi mong đợi nó có thể, nhưng sẽ không thể để kiểm tra - không .NET REPL trong tầm tay) và rằng .readToEnd háo hức tiêu thụ toàn bộ luồng, điều này có thể được đơn giản hóa hơn nữa thành

Cập nhật: Chỉ cần kiểm tra xem số with-open của ClojureCLR là .Dispose trên đối tượng bị ràng buộc. Nếu đó là ok thay vì .Close, tuyệt vời; nếu .Close là cần thiết, bạn có thể viết phiên bản của riêng bạn with-open sử dụng .Close thay vì (có thể sao chép hầu hết 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)))) 

Một số nhận xét:

  1. Không sử dụng def, defn vv bất cứ nơi nào ngoại trừ ở cấp cao nhất trừ khi bạn thực sự biết bạn cần phải làm điều đó. (Trên thực tế sử dụng chúng ngay lập tức bên trong một top-level let là thỉnh thoảng hữu ích nếu bạn cần các đối tượng được tạo ra để đóng trên let người dân địa phương -bound ... Bất cứ điều gì nhiều hơn sôi nổi hơn nên nhận rất giám sát cẩn thận!)

    def & Công ty tạo Vars cấp cao nhất hoặc đặt lại các ràng buộc gốc của chúng; làm như vậy trong quá trình hoạt động thường xuyên của chương trình hoàn toàn trái ngược với tinh thần chức năng của Clojure. Có lẽ quan trọng hơn từ một POV thực tế, bất kỳ chức năng nào dựa vào "sở hữu" một loạt các Vars chỉ có thể được thực thi bởi một luồng tại một thời điểm; không có lý do tại sao download-web-page nên do đó bị hạn chế.

  2. let các ràng buộc được giới thiệu có thể không đệ quy hai lần; các ràng buộc sau này có thể đề cập đến các ràng buộc trước đó, nhưng không phải là cách khác. Các hàm cục bộ đệ quy có thể được giới thiệu với letfn; các loại đối tượng đệ quy khác có thể hơi kém thuận tiện để tạo ra bên ngoài cấp cao nhất (mặc dù không có nghĩa là không thể). Mã từ câu hỏi không dựa vào các giá trị đệ quy lẫn nhau, vì vậy let hoạt động tốt.