clojure.core / map不起作用

时间:2014-02-05 09:49:10

标签: clojure

我想弄清楚为什么我的一个地图调用无效。我正在建立一个爬行器,目的是学习Clojure。

(use '[clojure.java.io])

(defn md5
  "Generate a md5 checksum for the given string"
  [token]
  (let [hash-bytes
         (doto (java.security.MessageDigest/getInstance "MD5")
               (.reset)
               (.update (.getBytes token)))]
       (.toString
         (new java.math.BigInteger 1 (.digest hash-bytes)) ; Positive and the size of the number
         16)))

(defn full-url [url base]
  (if (re-find #"^http[s]{0,1}://" url)
    url
    (apply str "http://" base (if (= \/ (first url))
                                url
                                (apply str "/" url)))))
(defn get-domain-from-url [url]
  (let [matcher (re-matcher #"http[s]{0,1}://([^/]*)/{0,1}" url)
        domain-match (re-find matcher)]
    (nth domain-match 1)))
(defn crawl [url]
  (do
    (println "-----------------------------------\n")
    (if (.exists (clojure.java.io/as-file (apply str "theinternet/page" (md5 url))))
      (println (apply str url " already crawled ... skiping \n"))
      (let [domain (get-domain-from-url url)
            text (slurp url)
            matcher (re-matcher #"<a[^>]*href\s*=\s*[\"\']([^\"\']*)[\"\'][^>]*>(.*)</a\s*>" text)]
        (do
          (spit (apply str "theinternet/page" (md5 url)) text)
          (loop [urls []
               a-tag (re-find matcher)]
            (if a-tag
              (let [u (nth a-tag 1)]
                (recur (conj urls (full-url u domain)) (re-find matcher)))
              (do
                (println (apply str "parsed: " url))
                (println (apply str (map (fn [u]
                                             (apply str "-----> " u "\n")) urls)))
                (map crawl urls)))))))))
(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (crawl "http://www.example.com/"))

首次调用地图有效:

 

(println (apply str (map (fn [u]
    (apply str "-----> " u "\n")) urls)))

(println (apply str (map (fn [u] (apply str "-----> " u "\n")) urls)))

但第二个电话似乎被忽略了。

(map crawl urls)函数正在按预期工作,正在刷新网址,使用正则表达式解析crawl标记以获取href并且a中的累积按预期工作,但是我使用loopmap调用了crawl,并且忽略了对urls的调用。

此外,如果我尝试拨打map,则会再次忽略此呼叫。

我几周前开始了我的Clojure冒险,所以任何建议/批评都是最受欢迎的。

谢谢

1 个答案:

答案 0 :(得分:2)

在Clojure中,map很懒惰。从文档中,映射:

  

返回一个惰性序列,该序列由将f应用于的结果组成   每个coll的第一项的集合,然后将f应用于集合   每个coll中的第二个项目,直到任何一个colls   累。

您的抓取功能是一种带副作用的功能 - 您spit - 将某些结果发送到文件,然后println报告进度。但是,因为map返回一个懒惰的序列,所以这些事情都不会发生 - 结果序列永远不会被明确地实现,所以它可以保持懒惰。

有许多方法可以实现延迟序列(例如使用map创建),但在这种情况下,您希望使用具有副作用的函数迭代序列,最好使用doseq

  

反复执行身体(可能是副作用)     绑定和过滤由“for”提供。不保留     序列的头部。返回nil。

如果您将(map crawl urls)的来电替换为(doseq [u urls] (crawl u)),则应获得所需的结果。

注意:您对地图的第一次调用按预期工作,因为您使用(apply str)实现了结果。如果不评估序列,就无法(apply str)