2010-09-01 15 views
6

Estoy buscando una manera más idiomática, si es posible, para escribir el siguiente código clojure:forma idiomática para escribir la función de interoperabilidad .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 
) 

Este es el ClojureCLR y obras. (El hecho de que se trata de la variante CLR no importa mucho)

me gustaría deshacerse de las defs (sustituir por le deja? Pueden referirse unos a otros?)

¿Qué tal una manera mejor para llegar a la transmisión, teniendo en cuenta que ... el encadenamiento no funcionará porque necesito cerrar las transmisiones más adelante.

EDITAR: Después de la respuesta, encontré una manera mucho más fácil en .NET para descargar una página web usando la clase WebClient. Todavía utilizado muchos de los enfoques recomendados de Michal - sólo quería grabar lo que ahora creo que es la mejor respuesta:

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

Respuesta

6

El código de la pregunta puede ser reescrita bastante idiomáticamente como tal (módulo ninguna errata - aquí está la esperanza no hay ninguno):

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

Suponiendo que la versión .NET de with-open .Close llamadas a los objetos enlazados (como espero que podría, pero no será capaz de comprobar - sin REPL .NET a mano) y que .readToEnd consume ansiosamente toda la secuencia, esto podría simplificarse aún más a

Actualización: acaba de comprobar que las llamadas de with-open ClojureCLR .Dispose en los objetos enlazados. Si eso está bien en lugar de .Close, genial; Si se requiere .Close, usted puede escribir su propia versión de with-open.Close utilizar en su lugar (posiblemente copiar la mayor parte de 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)))) 

Algunos comentarios:

  1. No utilice def, defn etc. . en cualquier lugar, excepto en el nivel superior, a menos que realmente sepa que necesita hacer eso. (En realidad usarlas de inmediato dentro de un nivel superior let es ocasionalmente útil si necesita el objeto que se creó para cerrar en los locales let -bound ... Cualquier cosa más cobarde de lo que debe recibir muy cuidadoso escrutinio!)

    def & Co. crean Vars de nivel superior o restablecen sus enlaces de raíz; hacerlo en el curso de una operación regular de un programa es completamente contrario al espíritu funcional de Clojure. Tal vez lo más importante es que desde un punto de vista práctico, cualquier función que dependa de "poseer" un grupo de Vars solo se puede ejecutar por un hilo a la vez; no hay ninguna razón por la que download-web-page deba ser así limitado.

  2. let -los enlaces introducidos pueden no ser mutuamente recursivos; enlaces posteriores pueden referirse a enlaces anteriores, pero no al revés. Se pueden introducir funciones locales mutuamente recursivas con letfn; otros tipos de objetos mutuamente recursivos pueden ser algo menos convenientes de crear fuera del nivel superior (aunque de ninguna manera imposible). El código de la pregunta no se basa en valores mutuamente recursivos, por lo que let funciona bien.

Cuestiones relacionadas