Synology社とQNAP社のファイルシステムを巡る戦い

QNAP社が、Btrfsファイルシステム(≒Synology社のNAS)のことをサイト上で批判しています。

www.synology.com

www.qnap.com

QNAP社のサイトを見ると、

QNAP NASがBtrfsファイルシステムを使用しないのはなぜですか?

(QNAPが採用している)ext4は、より高速で、安定しています

Btrfsは、低速です

のように、Synology社のNASと比較した際の優位性を述べています。日本の企業ではこのような相手を名指しで批判するマーケティングはお目にかかることが無いので、とても興味深いです。

私はファイルシステムの専門家ではないため、どちらのファイルシステムが優れている等を論ずることはできませんが、実際のところはどうなのでしょうか。とりあえず、Synology社のNASを買ってみたいと思います。

Clojureで「言語処理100本ノック 2015」(その3)

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本ノックは、一旦終わりにする。

今後は、並列処理を中心に細かいトピックを拾っていきたいのと、どこかで使っていきたい。

Clojureで「言語処理100本ノック 2015」(その2)

Clojureで「言語処理100本ノック 2015」を解いてみようの続き

目次

第1章: 準備運動

06. 集合

"paraparaparadise"と"paragraph"に含まれる文字bi-gramの集合を,それぞれ, XとYとして求め,XとYの和集合,積集合,差集合を求めよ.さらに,'se'というbi-gramがXおよびYに含まれるかどうかを調べよ.

(require '[clojure.string :as str])
(use 'clojure.set)

(defn n-gram [n target & cond]
  (if (= (first cond) "word")
      (map #(apply str %) (partition n 1 (str/split target #"\W+")))
      (map #(apply str %) (partition n 1 (seq target)))))

(def x (set (n-gram 2 "paraparaparadise")))
(def y (set (n-gram 2 "paragraph")))

x
; => #{"ad" "di" "is" "ap" "se" "ra" "ar" "pa"}
y
; => #{"ph" "ap" "ag" "gr" "ra" "ar" "pa"}

; 和集合
(union x y)
; => #{"ad" "di" "is" "ph" "ap" "se" "ag" "gr" "ra" "ar" "pa"}

; 差集合
(difference x y)
; => #{"ad" "di" "is" "se"}

; 積集合
(intersection x y)
; => #{"ap" "ra" "ar" "pa"}

; 'se'というbi-gramがXおよびYに含まれるかどうか
(contains? x "se")
; => true

(contains? y "se")
; => false

07. テンプレートによる文生成

引数x, y, zを受け取り「x時のyはz」という文字列を返す関数を実装せよ.さらに,x=12, y="気温", z=22.4として,実行結果を確認せよ.

(defn nlp07 
  [x y z]
  (println (str x "時の" y "は" z)))

(nlp07 12 "気温" 22.4)
; => 12時の気温は22.4

08. 暗号文

与えられた文字列の各文字を,以下の仕様で変換する関数cipherを実装せよ.

英小文字ならば(219 - 文字コード)の文字に置換 その他の文字はそのまま出力 この関数を用い,英語のメッセージを暗号化・復号化せよ.

(require '[clojure.string :as str])
(defn cipher
  [input]
  (apply str (map #(cipher-convert %) (str/split input #""))))

(defn cipher-convert
  [input]
  (if-let [c (re-find #"[a-z]" input)]
    (char (- 219 (first (.getBytes c))))
    input))

(cipher "Hello, world!")
; => "Hvool, dliow!"

(cipher (cipher "Hello, world!"))
; => "Hello, world!"

09. Typoglycemia

スペースで区切られた単語列に対して,各単語の先頭と末尾の文字は残し,それ以外の文字の順序をランダムに並び替えるプログラムを作成せよ.ただし,長さが4以下の単語は並び替えないこととする.適当な英語の文(例えば"I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind .")を与え,その実行結果を確認せよ.

Typoglycemiaとは、単語を構成する文字を並べ替えても、最初と最後の文字が合っていれば読めてしまう現象らしい。

(require '[clojure.string :as str])

; 先頭と最後を除く
(defn middle [input] (rest (butlast input)))

; 各単語の先頭と末尾の文字は残し,それ以外の文字の順序をランダムに並び替える
(defn typoglycemia-word [word]
  (str 
    (first word)
    (apply str (shuffle (middle word)))
    (last word)))

(defn typoglycemia [input]
  (if (>= (count input) 4) (typoglycemia-word input) input))

(str/join " " (map typoglycemia (str/split "I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind ." #" ")))
; => "I clno'dut bleeive taht I cloud atclluay urntasednd waht I was ridneag : the peheaonnml poewr of the huamn mnid ."

以上で1章は終わり。

Clojureの基本的な文法にも慣れ、これくらいの処理であれば簡単にかけるようになった。

Clojureで「言語処理100本ノック 2015」(その1)

最後まで達成できるか分かりませんが、言語処理100ノックをClojureで解いていきたいと思います。

目次

言語処理100本ノック 2015とは

www.cl.ecei.tohoku.ac.jp

言語処理100本ノックは,実践的な課題に取り組みながら,プログラミング,データ分析,研究のスキルを楽しく習得することを目指した問題集です

つまり、プログラミング言語の習得だけではなく、データ分析のスキルも得ることが出来るというわけです。

第1章: 準備運動

00. 文字列の逆順

文字列"stressed"の文字を逆に(末尾から先頭に向かって)並べた文字列を得よ.

(apply str (reverse "stressed"))

01. 「パタトクカシーー」

「パタトクカシーー」という文字列の1,3,5,7文字目を取り出して連結した文字列を得よ.

(apply str 
  (map-indexed 
    (fn [idx, item] 
      (when (even? idx) item)) 
    "パタトクカシーー"))

一度解いた後、もっと簡潔な書き方を思いついた。

(apply str (take-nth 2 "パタトクカシーー"))

02. 「パトカー」+「タクシー」=「パタトクカシーー」

「パトカー」+「タクシー」の文字を先頭から交互に連結して文字列「パタトクカシーー」を得よ.

(apply str (interleave (seq "パトカー") (seq "タクシー")))

interleave は、2つ以上のリストの項目を交互に返します。

03. 円周率

"Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."という文を単語に分解し,各単語の(アルファベットの)文字数を先頭から出現順に並べたリストを作成せよ.

(require '[clojure.string :as str])

(defn word-list [str]
  (remove (fn [x] (= x "")) 
    (str/split str #"\W")))

(map count (word-list "Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."))

結果は、 (3 1 4 1 5 9 2 6 5 3 5 8 9 7 9) のように円周率になります。

04. 元素記号

"Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."という文を単語に分解し,1, 5, 6, 7, 8, 9, 15, 16, 19番目の単語は先頭の1文字,それ以外の単語は先頭に2文字を取り出し,取り出した文字列から単語の位置(先頭から何番目の単語か)への連想配列(辞書型もしくはマップ型)を作成せよ.

(require '[clojure.string :as str])

(defn word-list [input]
  (remove #(= % "") 
    (str/split input #"\W")))


(defn word-slice [idx input]
  (if (some #(= (+ idx 1) %) '(1 5 6 7 8 9 15 16 19))
    (hash-map (str (first input)) idx)
    (hash-map (apply str (take 2 input)) idx)))


(reduce merge 
  (map #(word-slice (first %) (second %)) 
    (map-indexed vector
      (word-list "Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."))))
))

結果

{"Si" 13, "K" 18, "He" 1, "Ca" 19, "Al" 12, "S" 15, "H" 0, "Be" 3, "C" 5, "F" 8, "B" 4, "Cl" 16, "P" 14, "O" 7, "Na" 10, "N" 6, "Ar" 17, "Li" 2, "Mi" 11, "Ne" 9}

05. n-gram

与えられたシーケンス(文字列やリストなど)からn-gramを作る関数を作成せよ.この関数を用い,"I am an NLPer"という文から単語bi-gram,文字bi-gramを得よ.

(require '[clojure.string :as str])

(defn n-gram [n target & cond]
  (if (= (first cond) "word")
      (map #(apply str %) (partition n 1 (str/split target #"\W+")))
      (map #(apply str %) (partition n 1 (seq target)))))

(n-gram 3 "I am an NLPer")
; => ("I a" " am" "am " "m a" " an" "an " "n N" " NL" "NLP" "LPe" "Per")

(n-gram 2 "I am an NLPer")
; => ("I " " a" "am" "m " " a" "an" "n " " N" "NL" "LP" "Pe" "er")

(n-gram 2 "I am an NLPer" "word")
; => ("Iam" "aman" "anNLPer")

ifの後がダサい。

Clojureのインストール Using Brew

2011年にプログラミング言語の1つであるClojure入門しようとしたけど、時間だけが過ぎていた。こんどこそは入門しようと思い、Mac(high sirrera)にClojureをインストールした。

Javaのインストール

$ brew cask install java

Clojureのインストール

$ brew install clojure

Clojureの起動

$ clj
user=> (reverse "Hello World!")
(\! \d \l \r \o \W \space \o \l \l \e \H)

artisan.hatenablog.com

Day One ClassicでDropboxと同期できなくなった

最強の日記アプリと誉れ高いDay One。これまで日記のデータをDropboxに保存し、複数デバイスで同期して利用していたが、気づいたら同期出来なくなっていた。なんでろうと思ったら、サポートに記載あった。

Syncing with Dropbox in Day One Classic | Day One Help

  • 2017年9月28日に、DropboxがDay Oneとデータを同期できるAPIを廃止
  • Day One Classicは2016年3月でサポート終了したため、対応せず

とりあえず、暫定対策としてiCloudで同期して利用している。そろそろDay One 2へ移行する必要があるかもしれない。