6

可能であれば、次の 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 クラスを使用して Web ページをダウンロードするはるかに簡単な方法を見つけました。私はまだ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)))
4

1 に答える 1

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

(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. などをトップ レベル以外で使用しないでdefください。defn(実際にトップレベル内でそれらをすぐに使用すると、作成中のオブジェクトが-bound ローカルletを閉じる必要がある場合に便利な場合があります...それよりもファンキーなものは、非常に慎重に精査する必要があります!)let

    def& Co. トップレベルの変数を作成するか、ルートバインディングをリセットします。プログラムの通常の操作の過程でこれを行うことは、Clojure の機能的な精神に完全に反します。おそらくより重要なことは、実用的な POV から、Var の束を「所有」することに依存する関数は、一度に 1 つのスレッドでしか実行できません。download-web-pageこのように制限する理由はありません。

  2. let-導入されたバインディングは相互に再帰的ではない場合があります。後のバインディングは以前のバインディングを参照する場合がありますが、その逆はできません。相互に再帰的なローカル関数はletfn;で導入できます。他のタイプの相互に再帰的なオブジェクトは、トップ レベルの外で作成するのがやや不便かもしれません (決して不可能ではありません)。質問のコードは、相互に再帰的な値に依存していないため、正常にlet動作します。

于 2010-09-01T23:00:32.017 に答える