你在不到50行Clojure中做过的最有用的事情是什么?

时间:2009-08-11 16:42:04

标签: clojure

Clojure似乎喜欢它可能会成为一个受欢迎的Lisp。我想知道有多少人真正采用它来解决他们遇到的一些小而实际的问题。由于Clojure在Pleac中没有条目,我认为如果人们将他们的小解决方案发布到他们在Clojure中解决的问题会很棒。

9 个答案:

答案 0 :(得分:15)

这会通过Yahoo! Weather打印天气预报。

(ns weather
  (:use (clojure [xml :only [parse]] [zip :only [xml-zip]])
        (clojure.contrib duck-streams str-utils pprint)
        (clojure.contrib.zip-filter xml)))

(defn fetch-xml [uri]
  (xml-zip
   (parse
    (org.xml.sax.InputSource.
     (java.io.StringReader.
      (slurp* (java.net.URI. (re-gsub #"\s+" "+" (str uri)))))))))

(defn yahoo-weather
  ([loc-code] (yahoo-weather loc-code "c"))
  ([loc-code unit]
     (let [rss (fetch-xml (str "http://weather.yahooapis.com/forecastrss?p=" loc-code "&u=" unit))]
       (if (= (text (xml1-> rss :channel :item :title)) "City not found")
         "City not found.  Go to http://weather.yahoo.com/, search for your city, and look in the URL for the location code."
         (let [[units loc wind atm ast] (map #(xml1-> rss :channel (keyword (str "yweather:" %)))
                                             ["units" "location" "wind" "atmosphere" "astronomy"])
               conditions (xml1-> rss :channel :item :yweather:condition)
               date (re-find #"\d+:\d+.*" (xml1-> rss :channel :item :pubDate text))
               fors (xml-> rss :channel :item :yweather:forecast)]
           (cl-format true
"Weather for ~a, ~a (~a)
    Temperature: ~a\u00B0 ~a
     Wind Chill: ~a\u00B0 ~a, ~a ~a
     Conditions: ~a
       Humidity: ~a%
      Barometer: ~a ~a
 Sunrise/Sunset: ~a / ~a

Forecast:
~{  ~{~a: ~a. Hi ~2d, Lo ~2d.~}~^~%~}
"
                      (attr loc :city) (attr loc :region) date
                      (attr conditions :temp) (attr units :temperature)
                      (attr wind :chill) (attr units :temperature) (attr wind :speed) (attr units :speed)
                      (attr conditions :text)
                      (attr atm :humidity)
                      (attr atm :pressure) (attr units :pressure)
                      (attr ast :sunrise) (attr ast :sunset)
                      (map #(list (attr % :day)
                                  (attr % :text)
                                  (attr % :high)
                                  (attr % :low))
                           fors)))))))

例如:

user> (weather/yahoo-weather "CAXX0328")
Weather for North Vancouver,  (10:00 am PDT)
    Temperature: 14° C
     Wind Chill: 14° C, 8.05 kph
     Conditions: Light Rain Shower
       Humidity: 88%
      Barometer: 1018 mb
 Sunrise/Sunset: 6:01 am / 8:32 pm

Forecast:
  Thu: Few Showers. Hi 18, Lo 12.
  Fri: AM Showers. Hi 19, Lo 12.
nil

答案 1 :(得分:9)

本身并不是特别有用,但这个想法类似于Javascript中的JSON - 您可以将Clojure数据结构移入和移出文件系统。通过Practical Common Lisp's Database示例:

(ns storage (:import (java.io File PushbackReader FileReader FileWriter)))

(defn load-data
  "Loads data from the given file."
  [filepath]
  (do
    ;; the let block creates the file if it doesn't exist
    ;; reader throws an exception if there's no parsable data struct
    (let [file (new File filepath)]
      (if (not (.exists file))
        (do
          (.createNewFile file)
          (doto (new FileWriter filepath) (.write "{}") .close))))
    (read (new PushbackReader (new FileReader filepath)))))

(defn dump-data
  "Exports data structure to a file."
  [filepath data]
  (doto (new FileWriter filepath) (.write (str data)) .close))

使用示例:

user=> (dump-data "test.dat" {:a [1 2 3] :b "hello" :c true})
#<FileWriter java.io.FileWriter@186df0f>

user=> (load-data "test.dat")
{:a [1 2 3], :b "hello", :c true}

当然要为你的程序编写自己的(复杂的)保存机制。我确信只需改变一些通过Java提供的读者就可以完全从字符串中读取。

答案 2 :(得分:6)

99瓶啤酒


(defn bottles [n & [capitalize]]
  (str (if (> n 0) n (if capitalize "No more" "no more"))
    " bottle" (if (= 1 n) "" "s") " of beer" ))

(defn bot-wall [n & cap] (str (bottles n cap) " on the wall"))

(defn sing
  ;  Default is 99 times.
  ([]  (sing 99))
  ([stock]
    (doseq [i (range stock -1 -1)]
      (printf "%s, %s.\n%s.\n\n"
        (bot-wall i true) (bottles i)
        (apply str
          (if (> i 0)
            ["Take one down and pass it around, " (bot-wall (dec i))]
            ["Go to the store and buy some more, " (bot-wall stock)]
          ))))))

(sing)

http://99-bottles-of-beer.net/language-clojure-1996.html

答案 3 :(得分:6)

这会从图像创建缩略图。图像可以是本地文件,远程URL或javax.imageio.ImageIO可以读取的任何其他内容(感谢Java!)。输出可以是javax.imageio.ImageIO可以写入的任何图像格式。

(use '(clojure.contrib java-utils))
(defn make-thumbnail
  "Given an input image (File, URL, InputStream, ImageInputStream),
   output a smaller, scaled copy of the image to the given filename.
   The output format is derived from the output filename if possible.
   Width should be given in pixels."
  ([image out-filename width]
     (if-let [format (re-find #"\.(\w+)$" out-filename)]
       (make-thumbnail image out-filename width (nth format 1))
       (throw (Exception. "Can't determine output file format based on filename."))))
  ([image out-filename width format]
     (let [img (javax.imageio.ImageIO/read image)
           imgtype (java.awt.image.BufferedImage/TYPE_INT_RGB)
           width (min (.getWidth img) width)
           height (* (/ width (.getWidth img)) (.getHeight img))
           simg (java.awt.image.BufferedImage. width height imgtype)
           g (.createGraphics simg)]
       (.drawImage g img 0 0 width height nil)
       (.dispose g)
       (javax.imageio.ImageIO/write simg format (as-file out-filename)))))

从本地PNG创建JPG缩略图:

(make-thumbnail (java.io.File. "some-image.png") "thumb.jpg" 150)

从远程JPG创建GIF缩略图:

(make-thumbnail (java.net.URL. "http://blog.stackoverflow.com/wp-content/uploads/justice-league-small.jpg") "small.gif" 250)

答案 4 :(得分:6)

我在Clojure中为自己写的最有用的东西是几乎无足轻重的功能:

(defn tally-map
 " Create a map where the keys are all of the unique elements in the input
   sequence and the values represent the number of times those elements
   occur. Note that the keys may not be formatted as conventional Clojure
   keys, i.e. a colon preceding a symbol."
  [aseq]
  (apply merge-with + (map (fn [x] {x 1}) aseq)))

我在工作中一直使用这个。对直方图非常有用。

Brian Carper非常友好地建议以下改进形式的功能。

(defn tally-map [coll]
  (reduce (fn [h n]
            (assoc h n (inc (or (h n) 0))))
          {} coll))

答案 5 :(得分:4)

Clojure可能有一个强大的功能,但当我想出这个时,我真的很兴奋:

(defn pow [base exp] (reduce * (replicate exp base)))

答案 6 :(得分:2)

编写Swing应用程序,JMenuBar的东西总是很烦人。感谢dorun / map,它更容易:

(let [menus
[
 {:name "File" :mnemonic \F
  :items
  [
   {:name "Open" :mnemonic \O :fn file-open}
   :separator 
   {:name "Exit" :mnemonic \x :fn file-exit}
   ]
  }

 {:name "Help" :mnemonic \H
  :items
  [
   :separator 
   {:name "About..." :mnemonic \A :fn help-about}
   ]
  }
 ]

menu-fns
(into
 {}
 (mapcat
  (fn [menu]
    (map
     (fn [item] [(:name item) (:fn item)])
     (:items menu)))
  menus))

ui-frame
(proxy [JFrame ActionListener] ["UI Frame"]
  (actionPerformed
   [event]
    (let [command (.getActionCommand event)
      menu-fn (get menu-fns command)]

      ;; Handle menu commands
      (if menu-fn
    (apply menu-fn [this]))
      ))
  )
]

(defn new-menu [listener]
  (let [menubar (JMenuBar.)]
(dorun
 (map
  (fn [x]
    (let [menu (JMenu. (:name x))]
      (.setMnemonic menu (int (:mnemonic x)))
      (.add menubar menu)
      (dorun
       (map
    (fn [item]
      (if (= :separator item)
        (.addSeparator menu)
        (let [menu-item
          (if (:mnemonic item)
            (JMenuItem. (:name item) (int (:mnemonic item)))
            (JMenuItem. (:name item)))]
          (.addActionListener menu-item listener)
          (.add menu menu-item))))
    (:items x)))))
  menus))

menubar))

现在我不需要子菜单,但是new-menu获取它们是一个微不足道的变化。此外,添加图标,活动/非活动状态等只是menus中的更多字段。

答案 7 :(得分:1)

嗯,这段代码真正适合我和其他人。前几天我把它扔到了二十分钟,一起学习马尔可夫过程。我当然发现它很有用。我用它来说服教授,我对问题的理论分析是正确的。老实说,酷位(我想!)实际上只是第一个函数sample。我在很多项目中都使用了这样的功能,不知何故,当我匆忙地将其搞砸时,我偶然发现了我最好的解决方案。

(defn sample
   "Samples once a discrete random distribution defined by
   a vector. E.g., (sample [0.25 0.2 0.1 0.45]) should output
   '0' 25% of the time, '1' 20% of the time, etc."
   [p]
   (let [r (rand)]
      (count (take-while #(< % r) (reductions + p)))))
(defn transition
   "Given a transition matrix and a history vector, returns
   the history with an additional time step added."
   [m h]
      (conj h (sample (nth m (last h)))))
(defn process-gen
   "Takes a transition probability matrix, initial state
   probabilities, and a function.
   The initial state probs should take the form [p .. q].
   The function should accept the full process history
   and return true if the process should stop.  
   Returns a function of no arguments that returns a full
   simulated history of the process."
   [m i f]
   (fn [] (loop [h [(sample i)]] (if (f h) h (recur (transition m h))))))
(defn transition2
   "Given a transition matrix and the current state, returns
    a sampled state for the next time step."
   [m s]
   (sample (nth m s)))
(defn lazy-process
   "Takes a transition probability matrix, initial state
   probabilities, and a function.
   The initial state probs should take the form [p .. q].
   Returns a function which, when run, produces an infinite
   lazy list sampling the process."
   [m i]
   (fn [] f
      ([] (f (sample initial)))
      ([s] (let [i (transition2 m s)]
            (cons i (lazy-seq (f i)))))))

删除评论:

(defn sample [p]
   (let [r (rand)]
      (count (take-while #(< % r) (reductions + p)))))
(defn transition [m h]
      (conj h (sample (nth m (last h)))))
(defn process-gen [m i f]
   (fn [] (loop [h [(sample i)]] (if (f h) h (recur (transition m h))))))
(defn transition2 [m s]
   (sample (nth m s)))
(defn lazy-process-gen [m i]
   (fn [] f
      ([] (f (sample initial)))
      ([s] (let [i (transition2 m s)]
            (cons i (lazy-seq (f i)))))))

使用示例:

user=>(def squirrel-matrix [[0.8797 0.0212 0.0981 0.0010]
[0.0382 0.8002 0.0273 0.1343]
[0.0527 0.0041 0.8802 0.0630]
[0.0008 0.0143 0.0527 0.9322]])
user=>(def my-process (process-gen squirrel-matrix [1 0 0 0] #(and (> (count %) 1) (= 0 (last %)))))
user=> (/ (reduce + (map (comp dec count) (repeatedly 1000000 my-process))) 1000000.)
5.820319
user=> (let [hs (reduce + (filter #(> % 1) (map (comp dec count) (repeatedly 1000000 my-process))))] (/ (reduce + hs)) (count hs)))
5002699/120880 ; ~41.386

这两个数字通过相当过度的模拟回答问题here的两种不同解释。

我必须承认我为所有人清理了一些代码。我写这篇文章的那天是我发现Clojure在符号名称中允许使用Unicode的那一天。在修改语言时,我可能已经过了 little 。 ;-)所以......前三个函数实际上在我的文件中看起来像这样!

(Λ σ [p] (let [r (rand)] (|| (take-while #(< % r) (∮ + p)))))
(Λ Δ [Ξ ξ] (⊞ ξ (σ (§ Ξ (last ξ)))))
(Λ Π [Ξ i ω] (λ [] (⟳ [ξ [(σ i)]] (⇒ (ω ξ) ξ (⟲ (Δ Ξ ξ))))))

答案 8 :(得分:1)

这次是一个更严肃的答案:

我经常对只显示八行的REPL堆栈跟踪功能感到沮丧。所以这现在在我所有项目的开发文件中:

(defn stack
   [n]
   (clojure.stacktrace/print-stack-trace (clojure.stacktrace/root-cause *e) n))

我一直希望argminargmax

(defn argmin
   ([f x] x)
   ([f x y] (if (< (f x) (f y)) x y))
   ([f x y & more] (reduce (partial argmin f) (argmin f x y) more)))
(defn argmax
   ([f x] x)
   ([f x y] (if (> (f x) (f y)) x y))
  ([f x y & more] (reduce (partial argmax f) (argmax f x y) more)))

最后,为了稳健地评估算法,我在数据集上使用了这个函数:

(defn kfolds
   "Given an integer k and a collection of data, this
   partitions the data into k non-overlapping collections,
   then returns a list of length k, where the ith item is
   itself a list of two items: 
      (1) the union of all but the ith partition
      (2) the ith partition.
   If (count data) is not divisible by k, a few points (< k)
   will be left out."
   [k data]
   (let [total (count data)
         fold-size (int (/ total k))
         folds-test (take k (partition fold-size fold-size [] data))
         folds-train (map #(apply concat %)
                        (map #(take (dec k)
                                 (drop %
                                       (cycle folds-test)))
                           (range 1 (inc k))))]
         (map list folds-train folds-test)))