Clojureで「言語処理100本ノック 2015」を解いてみようの続き。
目次
第2章: UNIXコマンドの基礎
hightemp.txtは,日本の最高気温の記録を「都道府県」「地点」「℃」「日」のタブ区切り形式で格納したファイルである.以下の処理を行うプログラムを作成し,hightemp.txtを入力ファイルとして実行せよ.さらに,同様の処理をUNIXコマンドでも実行し,プログラムの実行結果を確認せよ.
10. 行数のカウント
行数をカウントせよ.確認にはwcコマンドを用いよ.
(require '[clojure.string :as str]) (count (str/split (slurp "hightemp.txt") #"\n")) ; => 24
$ wc -l hightemp.txt 24 hightemp.txt
slurp
便利すぎ。(slurp "http://clojuredocs.org/")
のようにHTTPもHTTPSも対応しているのね。
11. タブをスペースに置換
タブ1文字につきスペース1文字に置換せよ.確認にはsedコマンド,trコマンド,もしくはexpandコマンドを用いよ.
(require '[clojure.string :as str]) (map #(println %) (map #(str/replace % #"\t" " ")(str/split (slurp "http://www.cl.ecei.tohoku.ac.jp/nlp100/data/hightemp.txt") #"\n"))) ; 高知県 江川崎 41 2013-08-12 ; 埼玉県 熊谷 40.9 2007-08-16 ; 岐阜県 多治見 40.9 2007-08-16
12. 1列目をcol1.txtに,2列目をcol2.txtに保存
各行の1列目だけを抜き出したものをcol1.txtに,2列目だけを抜き出したものをcol2.txtとしてファイルに保存せよ.確認にはcutコマンドを用いよ.
(require '[clojure.string :as str]) (def hightemp (map #(str/split % #"\t") (str/split (slurp "http://www.cl.ecei.tohoku.ac.jp/nlp100/data/hightemp.txt") #"\n"))) (defn save-file [file-name file-body] (with-open [f-out (clojure.java.io/writer file-name :append true)] (.write f-out file-body))) (def col1-body (str/join "\n" (map #(first %) hightemp))) (def col2-body (str/join "\n" (map #(second %) hightemp))) (save-file "col1.txt" col1-body) (save-file "col2.txt" col2-body)
13. col1.txtとcol2.txtをマージ
12で作ったcol1.txtとcol2.txtを結合し,元のファイルの1列目と2列目をタブ区切りで並べたテキストファイルを作成せよ.確認にはpasteコマンドを用いよ.
(require '[clojure.string :as str]) (def col1 (str/split (slurp "col1.txt") #"\n")) (def col2 (str/split (slurp "col2.txt") #"\n")) (println (str/join "\n" (map #(str/join "\t" %) (partition 2 (interleave col1 col2))))) ;; => 高知県 江川崎 ;; 埼玉県 熊谷 ;; 岐阜県 多治見 ;; 山形県 山形 ;; (以下略)
14. 先頭からN行を出力
自然数Nをコマンドライン引数などの手段で受け取り,入力のうち先頭のN行だけを表示せよ.確認にはheadコマンドを用いよ.
(require '[clojure.string :as str]) (def hightemp-txt (str/split (slurp "http://www.cl.ecei.tohoku.ac.jp/nlp100/data/hightemp.txt") #"\n"))) (defn head [no input] (map #(println (str %)) (take no input))) (head 5 hightemp-txt) ;; => 高知県 江川崎 41 2013-08-12 ;; 埼玉県 熊谷 40.9 2007-08-16 ;; 岐阜県 多治見 40.9 2007-08-16 ;; 山形県 山形 40.8 1933-07-25 ;; 山梨県 甲府 40.7 2013-08-10
15. 末尾のN行を出力
自然数Nをコマンドライン引数などの手段で受け取り,入力のうち末尾のN行だけを表示せよ.確認にはtailコマンドを用いよ.
(defn tail [no input] (map #(println (str %)) (take-last no input))) (tail 3 hightemp-txt) ;; => 山梨県 大月 39.9 1990-07-19 ;; 山形県 鶴岡 39.9 1978-08-03 ;; 愛知県 名古屋 39.9 1942-08-02
16. ファイルをN分割する
自然数Nをコマンドライン引数などの手段で受け取り,入力のファイルを行単位でN分割せよ.同様の処理をsplitコマンドで実現せよ.
(require '[clojure.string :as str]) (def hightemp-seq (str/split (slurp "http://www.cl.ecei.tohoku.ac.jp/nlp100/data/hightemp.txt") #"\n")) (defn split-no [no] (/ (count hightemp-seq) no)) (defn division [no] (map #(println % "\n") (map #(str/join "\n" %) (partition (split-no no) hightemp-seq))))
17. 1列目の文字列の異なり
1列目の文字列の種類(異なる文字列の集合)を求めよ.確認にはsort, uniqコマンドを用いよ.
(require '[clojure.string :as str]) (def hightemp (map #(str/split % #"\t") (str/split (slurp "http://www.cl.ecei.tohoku.ac.jp/nlp100/data/hightemp.txt") #"\n"))) (distinct (map #(first %) hightemp))
18. 各行を3コラム目の数値の降順にソート
各行を3コラム目の数値の逆順で整列せよ(注意: 各行の内容は変更せずに並び替えよ).確認にはsortコマンドを用いよ(この問題はコマンドで実行した時の結果と合わなくてもよい).
(require '[clojure.string :as str]) (def hightemp (map #(str/split % #"\t") (str/split (slurp "http://www.cl.ecei.tohoku.ac.jp/nlp100/data/hightemp.txt") #"\n"))) (map println (map #(str/join "\t" %) (reverse (sort-by #(nth % 2) hightemp)))) ;; user => 高知県 江川崎 41 2013-08-12 ;; 岐阜県 多治見 40.9 2007-08-16 ;; 埼玉県 熊谷 40.9 2007-08-16 ;; 山形県 山形 40.8 1933-07-25 ;; 山梨県 甲府 40.7 2013-08-10
sort-by
の使い方にハマった。(sort-by keyfn coll)
なので、3コラム目を取得するような関数を渡せば良い。
19. 各行の1コラム目の文字列の出現頻度を求め,出現頻度の高い順に並べる
各行の1列目の文字列の出現頻度を求め,その高い順に並べて表示せよ.確認にはcut, uniq, sortコマンドを用いよ.
(def hightemp (map #(str/split % #"\t") (str/split (slurp "http://www.cl.ecei.tohoku.ac.jp/nlp100/data/hightemp.txt") #"\n"))) (reverse (sort-by second (frequencies (map #(first %) hightemp)))) ;; user => (["山梨県" 3] ["埼玉県" 3] ["群馬県" 3] ["山形県" 3] ["愛知県" 2] ["岐阜県" 2] ["静岡県" 2] ["千葉県" 2] ["和歌山県" 1] ["高知県" 1] ["大阪府" 1] ["愛媛県" 1])
frequencies
関数はこの問題のためにあるような関数ですね。
第2章を終えて
簡単なアルゴリズムならCojureで書けるようになった。 100本ノックは、一旦終わりにする。
今後は、並列処理を中心に細かいトピックを拾っていきたいのと、どこかで使っていきたい。