Что самое полезное, что вы сделали в менее чем 50 строках Clojure? - PullRequest
25 голосов
/ 11 августа 2009

Кажется, что Clojure может быть хорошим шансом стать популярным Лиспом. Мне было интересно, сколько людей фактически приняли его, чтобы решить некоторые из небольших, но реальных проблем, с которыми они столкнулись. Поскольку у Clojure нет записи в Pleac , я подумал, что было бы здорово, если бы люди опубликовали свои небольшие решения проблем, которые они решили в Clojure.

Ответы [ 9 ]

15 голосов
/ 13 августа 2009

Печатает прогноз погоды через Yahoo! Погода .

(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
9 голосов
/ 15 августа 2009

Не очень полезно само по себе, но идея похожа на JSON в Javascript - вы можете перемещать структуры данных Clojure в файловую систему и из нее. Принято из Практической базы данных Common Lisp пример:

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

6 голосов
/ 25 октября 2009

Самая полезная вещь, которую я написал для себя в 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)))

Я использую это все время в своей работе. Очень полезно для гистограмм.

Брайан Карпер любезно предложил следующую улучшенную форму функции.

(defn tally-map [coll]
  (reduce (fn [h n]
            (assoc h n (inc (or (h n) 0))))
          {} coll))
6 голосов
/ 14 августа 2009

Это создает миниатюру из изображения. Изображение может быть локальным файлом, удаленным 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)))))

Создание эскиза JPG из локального PNG:

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

Создание эскиза GIF из удаленного файла JPG:

(make-thumbnail (java.net.URL. "http://blog.stackoverflow.com/wp-content/uploads/justice-league-small.jpg") "small.gif" 250)
6 голосов
/ 13 августа 2009

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 / языка Clojure-1996.html

4 голосов
/ 10 сентября 2009

Clojure, вероятно, имеет функцию питания, но я был очень взволнован, когда понял это:

(defn pow [base exp] (reduce * (replicate exp base)))
2 голосов
/ 26 сентября 2009

При написании приложений на 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.

1 голос
/ 27 марта 2014

Более серьезный ответ на этот раз:

Меня часто разочаровывает функция трассировки стека REPL, отображающая только восемь строк. Так что теперь это в файле разработки для всех моих проектов:

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

Я хочу argmin и argmax все время .

(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)))
1 голос
/ 27 марта 2014

Ну, этот код действительно предназначен для меня и больше ни для кого. Я бросил это вместе через двадцать минут на днях для курса по марковским процессам. Я конечно нашел это полезным. Я использовал это, чтобы убедить профессора в том, что мой теоретический анализ проблемы был правильным. Честно говоря, классная штука (я думаю!) На самом деле - только первая функция, 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

Эти два числа отвечают, через чрезмерное моделирование, двум различным интерпретациям вопроса здесь .

Я должен признаться, что немного почистил свой код для всех вас. В тот день, когда я написал это, я обнаружил, что Clojure разрешил Unicode в именах символов. Возможно, я изменил язык из-за немного . ;-) Итак ... эти первые три функции на самом деле выглядят так в моем файле!

(Λ σ [p] (let [r (rand)] (|| (take-while #(< % r) (∮ + p)))))
(Λ Δ [Ξ ξ] (⊞ ξ (σ (§ Ξ (last ξ)))))
(Λ Π [Ξ i ω] (λ [] (⟳ [ξ [(σ i)]] (⇒ (ω ξ) ξ (⟲ (Δ Ξ ξ))))))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...