forで2重ループ

ネストさせて重ねるものだと思ってたけど違ったのでメモ。 この辺はちょっとわかりづらいな・・・・。

user> (for [i (range 0 10)
            n (range 0 10)]
        println (str "i=" i "n=" n)))
i=0n=0
i=0n=1
i=0n=2
i=0n=3
i=0n=4
・
・
・
i=9n=6
i=9n=7
i=9n=8
i=9n=9
(nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil)

Webページの内容(ソース)を取得する

ただ単純にWebページのソースを文字列として取ってくるだけなら

(slurp "<URL>")

と、これだけでOK

User-Agent等を指定したい場合なんかはこんな感じでやるらしい。

(slurp (-> (java.net.URL. url)
           .openConnection
           (doto (.setRequestProperty "User-Agent" "<User-Agent文字列>"))
           .getContent))

Clojureでデータを指定のキーごとにまとめる

user> (def data 
        [{:type "key1" :value "key1-value"} 
         {:type "key1" :value "key1-value2"} 
         {:type "key2" :value "key2-value"} 
         {:type "key3" :value "key3-value"}])

こんなデータを、:typeの値ごとにまとめたい(SQLでいうところのGROUP BYみたいなことをしたい) という場面があったので調べてみたら、そのものズバリgroup-byという関数があったのでメモしておく

group-by - clojure.core | ClojureDocs - Community-Powered Clojure Documentation and Examples

user> (group-by :type data)
{"key1" [{:type "key1", :value "key1-value"} {:type "key1", :value "key1-value2"}], "key2" [{:type "key2", :value "key2-value"}], "key3" [{:type "key3", :value "key3-value"}]}

第一引数に分類するキーを指定し、第二引数にデータ自体を指定すると 内容を一つづつ見ていき、第一引数で指定したキーの値ごとにベクターを作るという動きになっている模様。

これだけでも十分使えるんだけど、どうせならキーの値の部分をキーワードにしたかったので更に調べたら 第位置引数には関数も指定できて、その関数を通した結果が分類のキーになるというのを知ったので こんな感じにしてみたらうまいこと動いた。

user> (group-by (fn [x] (keyword (:type x))) data)
{:key1 [{:type "key1", :value "key1-value"} {:type "key1", :value "key1-value2"}], :key2 [{:type "key2", :value "key2-value"}], :key3 [{:type "key3", :value "key3-value"}]}

更に後から同僚の人にAnonymouse Function Literalなんてのを教えてもらって更に短くできることを知った。 http://clojure.org/reference/reader#_dispatch

user> (group-by #(keyword (:type %)) data)
{:key1 [{:type "key1", :value "key1-value"} {:type "key1", :value "key1-value2"}], :key2 [{:type "key2", :value "key2-value"}], :key3 [{:type "key3", :value "key3-value"}]}

REPL上でのnamespaceのリロード

project.cljのdependenciesに

[org.clojure/tools.namespace "0.2.11"]

を追加して

REPL上で

user> (require '[clojure.tools.namespace.repl :refer [refresh]])
user> (refresh)

とやればできる。

ちなみに、refreshした時に出るエラーで EOF while reading, starting at line xxと出たら大体カッコの対応が取れてない。

2016-06-17追記

CIDERで M-x cider-refresh もしくは C-c C-x でいけるということを知った

Emacs/CIDER で 5 倍快適 REPL リロードライフ - tnoda-clojure

ホントに5倍早くなった。スゴイ。

cider-mode で M-.(cider-find-var) が効かなかった

理由はなんてことなくて単純にns読んでませんでしたってだけでした。 なんてことないけどホントよく忘れる。。。

user> (in-ns 'sample01.core)
#namespace[sample01.core]

M-x cider-jack-in してから毎回コレうつの面倒だし、 REPL起動した時のデフォルトのnamespace決められないかちょっと調べてみよう。

herokuのClearDBの接続情報用の環境変数からclojure.jdbc用の接続情報を作る

ClearDBの接続情報は環境変数のCLEARDB_DATABASE_URLに入っているので 正規表現で各項目を抽出しmapを作る。

(def database-infos
  (re-find #"([a-z]*)://([0-9a-zA-Z]*):([0-9a-zA-Z]*)@(.*)/([0-9a-zA-z_]*).*" (env :cleardb-database-url)))

(def mysql-db {:subprotcol (nth database-infos 1)
               :subname (clojure.string/join ["//" (nth database-infos 4) ":3306"])
               :user (nth database-infos 2)
               :password (nth database-infos 3)})

うーん、いまひとつな感がある。 もっといいやり方あると思うんだけどなぁ。

2016-04-18 追記

↑みたいにパースしなくても文字列を渡せばOKですよと
id:ayato0211 あやぴーさんが教えてくれました。

(def mysql-db (env :cleardb-database-url))

これでスッキリ。

あやぴーさんありがとうございますm(_ _)m

clojure.java.jdbc - JDBC-based SQL Interface 0.6.1 API documentation

もうちょっとドキュメントちゃんと読まな・・・。