くーも
Rubyで cuda
を利用して GPGPU 使いたいの
で cumo
を導入してみた。
install
はじめに cuda
をインストールしておきます。
gem
に登録されているので、雑に gem install
実施してみます。
export CUDA_PATH="/opt/cuda"
export CPATH="$CUDA_PATH/include:$CPATH"
export LD_LIBRARY_PATH="$CUDA_PATH/lib64:$CUDA_PATH/lib:$LD_LIBRARY_PATH"
export PATH="$CUDA_PATH/bin:$PATH"
export LIBRARY_PATH="$CUDA_PATH/lib64:$CUDA_PATH/lib:$LIBRARY_PATH"
gem install cumo
とすると以下のようなエラーが出てだめです。
gcc -I. -Iinclude -Inarray -Icuda -I/home/katsyoshi/.rbenv/versions/2.5.1/include/ruby-2.5.0/x86_64-linux -I/home/katsyoshi/.rbenv/versions/2.5.1/include/ruby-2.5.0/ruby/backward -I/home/katsyoshi/.rbenv/versions/2.5.1/include/ruby-2.5.0 -I. -I/home/katsyoshi/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/numo-narray-0.9.1.3/ext/numo/narray -DHAVE_NUMO_NARRAY_H -DHAVE_DLFCN_H -DHAVE_DLOPEN -DHAVE_STDBOOL_H -DHAVE_STDINT_H -DHAVE_TYPE_BOOL -DHAVE_TYPE_U_INT8_T -DHAVE_TYPE_U_INT16_T -DHAVE_TYPE_INT32_T -DHAVE_TYPE_U_INT32_T -DHAVE_TYPE_INT64_T -DHAVE_TYPE_U_INT64_T -DHAVE_EXP10 -DHAVE_RB_CCOMPLEX -DHAVE_RB_THREAD_CALL_WITHOUT_GVL -I/opt/cuda/include -I/home/katsyoshi/.rbenv/versions/2.5.1/include -fPIC -O3 -ggdb3 -Wall -Wextra -Wno-unused-parameter -Wno-parentheses -Wno-long-long -Wno-missing-field-initializers -Wno-tautological-compare -Wno-parentheses-equality -Wno-constant-logical-operand -Wno-self-assign -Wunused-variable -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wdeclaration-after-statement -Wimplicit-function-declaration -Wdeprecated-declarations -Wmisleading-indentation -Wno-packed-bitfield-compat -Wsuggest-attribute=noreturn -Wsuggest-attribute=format -Wduplicated-cond -Wno-maybe-uninitialized -o narray/types/bit.o -c narray/types/bit.c
narray/gen/tmpl/lib.c:22:7: エラー: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘/’ token
#define m_map(x) m_num_to_data(rb_yield(m_data_to_num(x)))
^
narray/gen/tmpl/lib.c:23:29: エラー: 終端する > 文字がありません
^
narray/gen/tmpl/lib.c:23:29: 致命的エラー: static ID id_cast;: そのようなファイルやディレクトリはありません
コンパイルを停止しました。
make: *** [Makefile:453: narray/types/bit.o] エラー 1
公式リポジトリ覗いてもとくに問題なさそうなので gem
からのインストールは一旦おいてソースからインストールします。
build
公式リポジトリからダウンロードしてコンパイルをはじめます。
git clone https://github.com/sonotz/cumo.git
cd cumo
export CUDA_PATH="/usr/local/cuda"
export CPATH="$CUDA_PATH/include:$CPATH"
export LD_LIBRARY_PATH="$CUDA_PATH/lib64:$CUDA_PATH/lib:$LD_LIBRARY_PATH"
export PATH="$CUDA_PATH/bin:$PATH"
export LIBRARY_PATH="$CUDA_PATH/lib64:$CUDA_PATH/lib:$LIBRARY_PATH"
bundle install
rake compile
rake install
ってインストールできますが、その前に僕の環境が gcc-8.2.0
だったりして、これを gcc-6.4.0
に戻したりしてインストールしました。
run
これでインストール終わりましたので、 irb
で確認してみようと思います。
irb(main):001:0> require 'cumo/narray'
=> true
irb(main):002:0> Cumo::Int32.new(3, 3).fill(0)
=> Cumo::Int32#shape=[3,3]
[[0, 0, 0],
[0, 0, 0],
[0, 0, 0]]
とこれで実行されたことを確認できます。
と簡単に書きましたが、インストール後にGPUへのアクセス権が無いユーザーで実行したためエラーが出ます(エビデンス取得忘れ)。 手元のPCではGPUにアクセス権がvideo groupにもあるので利用ユーザーをvideo groupへ追加して再起動することでようやく動作確認ができます。
おわり
簡単にGPGPUを利用したプログラムが書けるようになりましたのでおわりです。 技術書典5はこれを使ったはなしを出展しますのできてみてくださいな。
え37でまってます
SATySFi使って本書いてみてる
技術書典5出しますので、それを書くために OCaml で作成された SATySFi を使いはじめました。
インストール
今回は OPAM を利用してインストールをしています。まあ単純にパッケージが今普段使いしてるOSにないだけなんですけどね。 インストール手順は 公式のREADME.md を参考にしています。
OPAM
OCaml Package Manager の略らしいです。
こいつは OCaml
のインストールもしてくれるのでこれを利用します。
OPAM
のインストールは簡単で以下のスクリプトを実行するだけです。
wget https://raw.github.com/ocaml/opam/master/shell/opam_installer.sh -O - | sh -s /usr/local/bin
OCaml
OPAM
のインストールが終ったら、 OCaml
をインストールします。
opam switch 4.06.0
eval `opam config env`
SATySFi
OPAM
の準備が整ったら SATySFi
の準備、インストールをします。
opam repository satysfi-external https://github.com/gfngfn/satysfi-external-repo.git
opam update
git clone https://github.com/gfngfn/SATySFi.git
cd SATiSFi
opam pin add satysfi .
opam install satysfi
compile!!!!
これでインストールされたのでとりあえずリポジトリにある demo/demo.saty
をコンパイルしてみましょう。
cd demo
make
なにも準備していないといきなりエラーがでますのでそれを公式を参考にして解消します。
解消できたら demo.pdf
ができますのでこれでつかえるようになります。
おわり
ちょっと簡単でしたが、 satysfi
の導入日記でした。
そんなことより技術書典5に出します。ネタは酒に関するネタでやっていく予定です。
仕上がっていれば え37
に居るとおもわれます。
wireguardをはじめました
title 通り、 wiregaurd
で家と さくらのVPS にあるサーバーを繋いでみました
install
導入は簡単で gentoo
は公式にあるので
sudo emerge wireguard
だけで、 ubuntu
の場合もインストールガイドがあるため簡単にインストールできます
sudo apt install software-properties-common
sudo add-apt-repository ppa:wireguard/wireguard
sudo apt-get update
sudo apt-get install wireguard
引込
あとは非常にかんたんでここを適宜読み替えることで 接続できます。
おわり
あとは、再起動時に自動で接続するように変更する必要がありそうですがつながったし、おそいのでこれでおわり
Hello, Itamae from docker!!
Itamae の plugin 書いててそろそろ test 欲しいなあとおもって2年くらいたちましたが、 ようやく Docker 使って test 書きました。
方向性
ここでテストを実施するとして何に対しておこなうのかというのを考えましたが、E2Eのテストだけで良いだろうという方向でテストを書くようにしています。
VMを立てて実行することにはチョット大袈裟だろうということでもっと簡単に、楽にということで docker
を選択しています。
itamae で E2E のテストなのでここでは serverspec を利用してテストを行います。
E2E のテスト準備として docker
内で最初に目的のレシピを実施し、そのレシピが正しく動作しているかをテストしています。
準備とテスト実施
準備として以下の gem
を追加しますが、 docker-api.gem の方はなくてもとくに問題ないです。
serverspec
の対象バックエンドで docker
が指定できるようになりますが、 docker
内 (docker run -t hoge bundle exec serverspec
など) で serverspec
を実施すれば実行できますので、好みで追加しましょう。
spec.add_development_dependency "docker-api"
spec.add_development_dependency "serverspec"
次に Dockerfile
を作成します。ここでは単純に必要な準備を実施、テストしたいディレクトリの追加、 bundler
を利用して gem
のインストールを実施してから、対象のディレクトリで itamae
の local
を指定して対象の docker
にレシピ適用します。そのあと docker run
や serverspec
を実施することでテストをすることが可能となります。
FROM ruby
RUN echo "gem: --no-rdoc --no-ri" >> /.gemrc
RUN gem install bundler
ADD . /app
WORKDIR /app
RUN bundle install
RUN bundle exec itamae local samples/recipe.rb
CMD bundle exec rake
itamae-plugin-resource-pip
itamae-plugin-resource-pip でできるようになってます。 とくに docker.io とかで公開していませんので、以下に例を
cd /path/to/itamae-plugin-resource-pip
docker build -t katsyoshi/itamae-plugin-resource-pip .
docker run -t katsyoshi/itamae-plugin-resource-pip bundle exec rake
おわり
これやろうと思って調べてたらみつからずに Dockerfile
書き終えて、travis を追加しようとしたときに、@sue445 さんがやってたのをみつけてしまった。。。
RubyKaigi2018
RubyKaigi2018 に行ってきた
1日目
1日目は、 ずっと会場Aにいました。
- Matz: Keynote
- Aaron Patterson: Analyzing and Reducing Ruby Memory Usage
- Kenta Murata, and Yusaku Hatanaka: Deep Learning Programming on Ruby
- Bozhidar Batsov: All About RuboCop
- Naotoshi Seo: Fast Numerical Computing and Deep Learning in Ruby with Cumo
- Emma Haruka Iwao: Exploring Internal Ruby Through C Extensions
- Lightning Talks
この日、今年の RubyKaigi 2018 で最も楽しみにしてたのが rubocop
の作者のトークで、彼のプロダクト(rubocop
, flycheck-ruby
)を利用してる身としては非常に楽しみでした。
DLと数値計算のやつは進捗が聞けてよかったっていうのとDeep Learningに関していうと難しいのかなあという感想が…
Haruka さんのやつは、やっぱり今の実装十分に速いってのがわかって、これ以上速くするには非常に大変だなってのがよかったですね。
LTはどれもよくて rib
も面白いし、 csvの高速化の話が特に好きでした。
2日目
2日目もほとんどA会場できいていたのと、GitHubの電源スペースにいました。
- Kouhei Sutou: My way with Ruby
- Kouichi Sasada: Guild Prototype
- Yuichiro Kaneko: RNode with code positions
- Yusuke Endo: Type Profiler: An analysis to guess type signatures
- Ruby Commiters vs. the World
この日のトークは特に楽しみにしていたのはなかったのですが、ここ最近毎年聞いていた @v0dro の発表を聞いていなかった。 金子さんの発表がわりとたのしく聞けて、「あーやっぱり型のはなしは興味ないな」って感じで遠藤さんの発表聞いてました。 この日の懇親会で飲みすぎた。
3日目
3日目はパフォーマンス改善を中心として聞いてました。
- Benoit Daloze: Parallel and Thread-Safe Ruby at High-Speed with TruffleRuby
- Takashi Kokubun: The Method JIT Compiler for Ruby 2.6
- Takeshi Watanabe: LuaJIT as a Ruby backend.
- Prasun Anand: High Performance GPU computing with Ruby
- Vladimir Makarov: Three Ruby performance projects
- TRICK 2018 (FINAL)
TuffleRubyとはやくてとてもよさそう。 国分さん、Vladimirの話はいつもどおりで安心した。
TRICK、どうしてこんなプログラムが思いつくのかさっぱりわからんがとにかく凄い以上の感想がうかんでこない。
おわり
RubyKaigi 2018に行ってきてたいへんたのしいイベントでした。
(型のはなしはやはり興味がないというかまだまだ理解が浅いのでTaPL読むべきだな)
API キーが凍結されてら
mikutter の Twitter API キーが凍結された ようです。
回避方法
とりあず、twitter の 開発者ページ から API キーを生成してください。 あとは以下の様に編集してください
diff --git a/Gemfile b/Gemfile
index 0a88a191..cb48cd4f 100644
--- a/Gemfile
+++ b/Gemfile
@@ -21,6 +21,7 @@ group :default do
gem 'pluggaloid', '>= 1.1.1', '< 2.0'
gem 'delayer-deferred', '>= 2.0', '< 3.0'
gem 'twitter-text', '>= 2.1.0'
+ gem 'dotenv'
end
group :test do
diff --git a/core/config.rb b/core/config.rb
index 6e3d95ba..455e3083 100644
--- a/core/config.rb
+++ b/core/config.rb
@@ -14,8 +14,8 @@ module CHIConfig
ACRO = "mikutter"
# 下の2行は馬鹿にしか見えない
- TWITTER_CONSUMER_KEY = "AmDS1hCCXWstbss5624kVw"
- TWITTER_CONSUMER_SECRET = "KOPOooopg9Scu7gJUBHBWjwkXz9xgPJxnhnhO55VQ"
+ TWITTER_CONSUMER_KEY = ENV["TWITTER_CONSUMER_KEY"]
+ TWITTER_CONSUMER_SECRET = ENV["TWITTER_SECRET_KEY"]
TWITTER_AUTHENTICATE_REVISION = 1
# pidファイル
diff --git a/mikutter.rb b/mikutter.rb
index da302ca1..94025817 100755
--- a/mikutter.rb
+++ b/mikutter.rb
@@ -32,6 +32,8 @@ require 'benchmark'
require 'webrick'
require 'thread'
require 'fileutils'
+require 'dotenv'
+Dotenv.load(".env")
require_relative 'core/utils'
あとは環境変数TWITTER_CONSUMER_KEY
と TWITTER_SECRET_KEY
を設定してあげると起動できるようになります
dotenv
環境変数を指定して毎回起動したくない、とか .bashrc
に書きたくない、とかある場合は
dotenv.gem
を使うことで良いかんじに読みとってくれるようになります。
つかいかたは Dotenv.load(file_name)
で file_name
にあるファイルのなかに環境変数を書けばよいです。
おわり
おわり。
別に dotenv.gem
いらんなこれ
1日はやくはじめた
2018/4/27 に休み入れて一日はやくGWはじめています。
毎月恒例
毎月月末金曜に知り合いの @n_kane と築地で朝御飯を食べにいってて 今月はGW直前だし朝からやってる 根津 鷹匠 で朝からそばもいこうとなっていってきた。 そういうわけで休みを入れていた。
国税局
前日あたりに instagram で ginzabrewery を眺めててどこだろと ggrks してみたら ここ の便利ページが出てきた。このページはだいたいブリュワリーとパブが一体化した 店が掲載されているよう。でよく行くCampion Ale や最近教えてもらった 十条すいけんブルワリー、 などが掲載されている。
ガハハハ
で午後は @n_kane も予定があったらしく、暇だったので上記国税局の便利ページを参考に ガハハビール に行ってみた。
流石に平日の午後3時だったので店員さん以外は誰もおらず、一人だけでした。 とりあえず Danchi IPA とレバーを頼んでみた。
-> <-
Danchi IPA は苦味がすくなく美味しいビールでした。
つぎに Marcy IPA とライスなしカツカレーを注文。 こちらは苦味が強くおいしいビールでした。
-> <-
そんなことより、謎のメニューが幾つかあり、店員さんに聞いてみたら、「だってお酒のむ人お米とか麺とかあんまりすきじゃないですよね」っていっておりました。 あと、軟骨バッファローチキンは頼んでないけどとてもよさそうだったので今度いく時に注文しよう。
おわり
国税局を調べたおかげで良いビール屋を見付けることができてよかったよかった。
NLP100 Knock Section IV
NLP100本ノック第4節おわりましたのでまとめます
第4章 形態素解析
形態素解析やからむずいやろとおもってた
準備
この章では、形態素解析済ファイルを作成する必要がありますが、毎回対象ファイルをダウンロード、解析して解いています。
ここではいつもどおり HashMap
を利用するため ANALYZED_MECAB_KEYS
を作成してこれをキーにします。
またよく利用する品詞を enum
で定義しておき、変換関数 inspect
を作成します。
const ANALYZED_MECAB_KEYS: [&str; 9] = ["pos", "pos1", "pos2", "pos3", "a", "b", "base", "read", "speech"];
enum PartOfSpeech {
VERB,
NOUN,
PARTICLE,
}
use PartOfSpeech::*;
fn inspect(val: PartOfSpeech) -> String {
match val {
VERB => "動詞",
NOUN => "名詞",
PARTICLE => "助詞",
}.to_string()
}
30. 形態素解析結果の読み込み
Map
を使えと指定があるので素直に利用します
fn feature(node: &Node) -> HashMap<String, String> {
let mut h: HashMap<String, String> = HashMap::new();
let surface: String = (&(node.surface)[..node.length as usize]).to_string();
h.insert("surface".to_string(), surface);
let values: Vec<String> = node.feature.split(",").map(|m| m.to_string()).collect();
for (a, b) in ANALYZED_MECAB_KEYS.iter().zip(values.iter()) {
h.insert(a.to_string(), b.to_string());
}
h
}
fn main() {
let url = "http://www.cl.ecei.tohoku.ac.jp/nlp100/data/neko.txt".to_string();
let neco: Vec<String> = NLP100::get(url).split("\n").filter(|f| f.ne(&"")).map(|m| m.to_string()).collect();
let mut morph = Vec::new();
for line in neco {
let mut tagger: Tagger = mecab::Tagger::new("");
let nodes: Node = tagger.parse_to_node(line);
let mut mecabu: Vec<HashMap<String, String>> = Vec::new();
for node in nodes.iter_next() {
match node.stat as i32 {
mecab::MECAB_BOS_NODE => (),
mecab::MECAB_EOS_NODE => (),
_ => {
mecabu.push(feature(&node));
}
}
}
morph.push(mecabu);
}
for morph in morphs {
for mecab in morph {
println!("{}", format!("surface: {}, base: {}, pos: {}, pos1: {}", mecab["surface"], mecab["base"], mecab["pos"], mecab["pos1"]));
}
println!("");
}
}
31. 動詞
動詞だけ抽出するので以下のコードで抽出し、表層形("surface"
)を取得する
fn verb(nodes: Vec<HashMap<String, String>>) -> Vec<HashMap<String, String>> {
nodes.iter().filter(|m| m["pos"] == inspect(VERB)).map(|hm| hm.clone()).collect()
}
32. 動詞の原形
動詞だけ抽出するので上記のコードで抽出し、原形("base"
)を取得する
33. サ変名詞
サ変接続を抽出
fn sa_noun(nodes: Vec<HashMap<String, String>>) -> Vec<HashMap<String, String>>{
noun(nodes).iter().filter(|node| node["pos1"] == "サ変接続").map(|hm| hm.clone()).collect()
}
34. 「AのB」
「の」を挾んでいる名詞を抽出
fn between_noun(node: &Node) -> Option<String> {
let mecab = feature(node);
if mecab["surface"] == "の" && mecab["pos"] == inspect(PARTICLE) && mecab["pos1"] == "連体化" {
let prev = feature(&node.prev().unwrap());
let next = feature(&node.next().unwrap());
Some(format!("{}{}{}", &prev["surface"], &mecab["surface"], &next["surface"]))
} else {
None
}
}
35. 名詞の連接
連続した名詞を抽出するが、mecab
でうまく関数化できなかったので割愛(あとでうかんだら追記)します。
36. 単語の出現頻度
単純に頻度をまとめ、 sort
するとよい
fn word_histgram(nodes: Vec<HashMap<String, String>>) -> HashMap<String, u64> {
let mut results: HashMap<String, u64> = HashMap::new();
for node in nodes {
let base = &node["base"];
*results.entry(base.to_string()).or_insert(0) += 1;
}
results
}
37. 頻度上位10語
上記の結果より .take(10)
するだけです。
38. ヒストグラム
37 と違いがわからずおわり。
39. Zipfの法則
単純に両対数グラフ化でおわり。
おわり
おわり
NLP 100 Knock section 3
正規表現苦手なようでだいぶ時間がかかりました。
第3章 正規表現
正規表現ときいて楽勝やろと思ってた時期もあったんですが・・・
準備
この章では、gzファイルに入ったJSONをパースする必要があるのでさきにgzファイルから読み込むようにします。
fn read_gzip(path: String) -> Vec<String> {
let mut file = File::open(path).unwrap();
let mut string = String::new();
Decoder::new(&mut file).unwrap().read_to_string(&mut string).unwrap();
string.split("\n").filter(|m| m.ne(&"")).map(|m| m.to_string()).collect::<Vec<String>>()
}
20. JSONデータの読み込み
こいつは、serde
の serde_json
を利用して from_str
で読み込むだけです。
let v: Value = match serde_json::from_str(&l.as_str()) {
Ok(v) => v,
Err(_) => { continue; },
};
21. カテゴリ名を含む行を抽出 \&\& 22. カテゴリ名の抽出
早見表 を参考に Regex::captures で抽出することで解決。
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;
extern crate regex;
extern crate serde_json;
use serde_json::Value;
use regex::Regex;
fn main() {
let args: Vec<String> = env::args().skip(1).collect();
let path = &args[0].to_string();
let keywords = &args[1..];
let file = File::open(path).unwrap();
let lines = BufReader::new(file).lines();
let re = Regex::new(r"\[\[Category:.+\]\]").unwrap();
let cap = Regex::new(r"\[\[Category:(?P<name>.+?)(?:\|.*)*\]\]").unwrap();
for l in lines {
let v: Value = serde_json::from_str(&l.unwrap()).unwrap();
let title = &v["title"].as_str().unwrap().to_string();
if keywords.contains(title) {
for content in v["text"].as_str().unwrap().to_string().split("\n").filter(|m| re.is_match(m)) {
for c in cap.captures_iter(content) {
println!("{}", &c["name"]);
}
}
}
}
}
23. セクション構造
こちらは前節の問題と同様に解けばよいですが、=の数 - 1
という罠があります。
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;
extern crate regex;
extern crate serde_json;
use serde_json::Value;
use regex::Regex;
fn main() {
let args: Vec<String> = env::args().skip(1).collect();
let path = &args[0].to_string();
let keywords = &args[1..];
let file = File::open(path).unwrap();
let lines = BufReader::new(file).lines();
let re = Regex::new(r"(?P<level>==+)(?P<headline>.+?)(?:=+)").unwrap();
for l in lines {
let v: Value = serde_json::from_str(&l.unwrap()).unwrap();
let title = &v["title"].as_str().unwrap().to_string();
if keywords.contains(title) {
for content in v["text"].as_str().unwrap().to_string().split("\n").filter(|m| re.is_match(m)) {
for caps in re.captures_iter(content) {
println!("level: {}, headline: {}", &caps["level"].len() - 1, &caps["headline"]);
}
}
}
}
}
24. ファイル参照の抽出
このあたりは問題ないとおもいます。
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;
extern crate regex;
extern crate serde_json;
use serde_json::Value;
use regex::Regex;
fn main() {
let args: Vec<String> = env::args().skip(1).collect();
let path = &args[0].to_string();
let keywords = &args[1..];
let file = File::open(path).unwrap();
let lines = BufReader::new(file).lines();
let re = Regex::new(r"\[\[(File|ファイル):(?P<filename>.+?)(?:\|.*)*(?:\|.*)*\]\]").unwrap();
for l in lines {
let v: Value = serde_json::from_str(&l.unwrap()).unwrap();
let title = &v["title"].as_str().unwrap().to_string();
if keywords.contains(title) {
for content in v["text"].as_str().unwrap().to_string().split("\n").filter(|m| re.is_match(m)) {
for caps in re.captures_iter(content) {
println!("filename: {}", &caps["filename"]);
}
}
}
}
}
25. テンプレートの抽出
こいつは大変でしたので参考サイト1 を参考に以下の正規表現でとりあえず抽出しています。
let re = Regex::new(r"(?ms)(?:^\{\{基礎情報.*?$)(?P<dict>.+?)(?:^\}\}$)").unwrap();
26. 強調マークアップの除去
こちらも同様に参考サイト2を参考に以下の正規表現で
let strong = Regex::new(r"'{2,5}").unwrap();
27. 内部リンクの除去
こちらも同様に参考サイト3を参考に以下の正規表現で
let link = Regex::new(r"(?:\[{1,2})(?P<link>.+?)(?:\|.+)?(?:\]{1,2})").unwrap();
28. MediaWikiマークアップの除去
どうよう4 (ry
let link = Regex::new(r"(?:\[{1,2})(?P<link>.+?)(?:\|.+)?(?:\]{1,2})").unwrap();
let lang = Regex::new(r"(?:\{\{lang\|.+?\|)(?P<lang>.+?)(?:\}\})").unwrap();
let markup = Regex::new(r"</*.+?>").unwrap();
29. 国旗画像のURLを取得する
こちらもさんこおおう5にしてますが、主にURL部分だけです。 こちらは Hyper を利用することで解決しています。
extern crate futures;
extern crate hyper;
extern crate hyper_tls;
extern crate libflate;
extern crate regex;
extern crate serde_json;
extern crate tokio_core;
use futures::{Future, Stream};
use hyper::Client;
use hyper_tls::HttpsConnector;
use libflate::gzip::Decoder;
use regex::Regex;
use serde_json::Value;
use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io::Read;
use tokio_core::reactor::Core;
fn get_image(query: String) -> String {
let re = Regex::new(r" ").unwrap();
let uri = format!("https://en.wikipedia.org/w/api.php?action=query&titles=File:{}&format=json&prop=imageinfo&iiprop=url", re.replace_all(&query, "%20"));
let uri = uri.parse().unwrap();
let mut core = Core::new().unwrap();
let handle = core.handle();
let client = Client::configure()
.connector(HttpsConnector::new(4, &handle).unwrap())
.build(&handle);
let work = client.get(uri).and_then(|res| {
res.body().concat2().and_then(move |body| {
let v: Value = serde_json::from_slice(&body).unwrap();
let page_ids = v["query"]["pages"].as_object().unwrap().keys().map(|m| m.to_string()).collect::<Vec<String>>();
let imageinfo = &v["query"]["pages"][&page_ids[0]]["imageinfo"];
let r = format!("{}", imageinfo[0]["url"].as_str().unwrap());
Ok(r)
})
});
core.run(work).unwrap()
}
fn read_gzip(path: String) -> Vec<String> {
let mut file = File::open(path).unwrap();
let mut string = String::new();
Decoder::new(&mut file).unwrap().read_to_string(&mut string).unwrap();
string.split("\n").filter(|m| m.ne(&"")).map(|m| m.to_string()).collect::<Vec<String>>()
}
fn main() {
let args: Vec<String> = env::args().skip(1).collect();
let path = &args[0].to_string();
let keywords = &args[1..];
let lines = read_gzip(path.to_string());
let re = Regex::new(r"(?ms)(?:^\{\{基礎情報.*?$)(?P<dict>.+?)(?:^\}\}$)").unwrap();
let bar_hat = Regex::new(r"(?ms)(?:^\|)").unwrap();
let dict = Regex::new(r"(?P<key>.+?)\s*=\s*(?P<val>.+)").unwrap();
let media = Regex::new(r"\[\[(File|ファイル):(?P<filename>.+?)(?:\|.*)*(?:\|.*)*\]\]").unwrap();
let strong = Regex::new(r"'{2,5}").unwrap();
let link = Regex::new(r"(?:\[{1,2})(?P<link>.+?)(?:\|.+)?(?:\]{1,2})").unwrap();
let lang = Regex::new(r"(?:\{\{lang\|.+?\|)(?P<lang>.+?)(?:\}\})").unwrap();
let markup = Regex::new(r"</*.+?>").unwrap();
for l in lines {
let v: Value = match serde_json::from_str(&l.as_str()) {
Ok(v) => v,
Err(_) => { continue; },
};
let text: String = match v["text"].as_str() {
Some(x) => x.to_string(),
None => { continue; },
};
let title: String = match v["title"].as_str() {
Some(x) => x.to_string(),
None => { continue; },
};
if keywords.contains(&title) {
let mut results: HashMap<String, String> = HashMap::new();
let re: String = match re.captures(&text) {
Some(caps) => caps["dict"].to_string(),
None => {
println!("cannot capture dict!!");
continue;
},
};
for line in bar_hat.split(&re).filter(|f| f.ne(&"")).map(|m| m.to_string()) {
let line = line.replace("\n", "");
let dict = match dict.captures(&line) {
Some(x) => x,
None => {continue;},
};
let val = dict["val"].to_string();
let key = dict["key"].to_string();
let file: String = media.replace_all(&val, "$filename").trim().to_string();
let strong: String = strong.replace_all(&file, "").trim().to_string();
let link: String = link.replace_all(&strong, "$link").trim().to_string();
let markup: String = markup.replace_all(&link, "").trim().to_string();
let val: String = match key.as_ref() {
"国旗画像" => get_image(markup.to_string()),
_ => lang.replace_all(&markup, "$lang").trim().to_string(),
};
results.insert(key, val);
}
for (k, v) in results {
println!("{}: {}", k, v);
}
}
}
}
おわり
とりあえず3章まで終わらせましたので形態素解析へと進みたいとおもいます(思っているだけ)
参考資料
-
素人の言語処理100本ノック:25, segavvy, https://qiita.com/segavvy/items/e402ad0a5b0f52453d7f ↩
-
素人の言語処理100本ノック:26, segavvy, https://qiita.com/segavvy/items/f6d0f3d6eee5acc33c58 ↩
-
素人の言語処理100本ノック:27, segavvy, https://qiita.com/segavvy/items/9a8137f045852bc299d6 ↩
-
素人の言語処理100本ノック:28, segavvy, https://qiita.com/segavvy/items/8c4567ec1124320d3354 ↩
-
素人の言語処理100本ノック:29, segavvy, https://qiita.com/segavvy/items/fc7257012d8a590185e5 ↩
NLP100ノック第2章
第2章を一通り終えたので書きます。
10. 行数のカウント
タイトルのままです。 Rust
では std::str::Lines
で count()
がありますので利用しておわりです。
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;
fn main() {
let mut args = env::args();
args.next();
for path in args {
let f = File::open(path).unwrap();
let br = BufReader::new(f);
println!("{}", br.lines().count());
}
}
11. タブをスペースに置換
コチラも、std::string::String.replace()
利用するだけです。
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;
fn main() {
let mut args = env::args();
args.next();
for path in args {
let file = File::open(path).unwrap();
let reader = BufReader::new(file);
for line in reader.lines() {
println!("{}", line.unwrap().replace("\t", " "));
}
}
}
12. 1列目をcol1.txtに,2列目をcol2.txtに保存
こちらは単純にファイルに書き込むのと、分割ができれば問題ないです。
use std::env;
use std::fs::File;
use std::io::{BufReader, BufWriter, Write};
use std::io::prelude::*;
fn main() {
let mut args = env::args();
args.next();
for path in args {
let f = path.clone().replace(".txt", "");
let s = path.clone().replace(".txt", "");
let file = File::open(path).unwrap();
let br = BufReader::new(file);
let mut first_column = BufWriter::new(File::create(format!("{}_col1.txt", f)).unwrap());
let mut second_column = BufWriter::new(File::create(format!("{}_col2.txt", s)).unwrap());
for line in br.lines() {
let words = line.unwrap().split("\t").map(|m| m.to_string()).collect::<Vec<String>>();
first_column.write(format!("{}\n", words[0]).as_bytes()).unwrap();
second_column.write(format!("{}\n", words[1]).as_bytes()).unwrap();
}
}
}
13. col1.txtとcol2.txtをマージ
こちらは以前利用した、zip
があれば問題ないです。
use std::env;
use std::fs::File;
use std::io::{BufReader, BufWriter, Write};
use std::io::prelude::*;
fn main() {
let mut args = env::args();
if args.len() < 2 { panic!("col1.txt col2.txt"); }
args.next();
let first = args.next().unwrap();
let second = args.next().unwrap();
let fr = BufReader::new(File::open(first).unwrap()).lines().map(|m| m.unwrap().to_string()).collect::<Vec<String>>();
let sr = BufReader::new(File::open(second).unwrap()).lines().map(|m| m.unwrap().to_string()).collect::<Vec<String>>();
let mut merge_file =
BufWriter::new(File::create("merge.txt".to_string()).unwrap());
for (x, y) in fr.iter().zip(&sr) {
merge_file.write(format!("{}\t{}\n", x, y).as_bytes()).unwrap();
}
}
14. 先頭からN行を出力
head
コマンドですので std::iter::Iterator.take()
を利用するだけです。
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;
fn main() {
let args = env::args().skip(1).collect::<Vec<String>>();
let file = File::open(&args[0]).unwrap();
let br = BufReader::new(file).lines().take((&args[1]).to_string().parse::<usize>().unwrap());
for line in br.map(|m| m.unwrap().to_string()).collect::<Vec<String>>() {
println!("{}", line);
}
}
15. 末尾のN行を出力
tail
コマンドです。こちらは std::iter::Iterator.skip()
を利用してやるだけです。
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;
fn main() {
let args = env::args().skip(1).collect::<Vec<String>>();
let file = File::open(&args[0]).unwrap();
let takes = (&args[1]).to_string().parse::<usize>().unwrap();
let br = BufReader::new(&file).lines();
let skips = br.count() - takes;
let file = File::open(&args[0]).unwrap();
let lines = BufReader::new(&file).lines().skip(skips);
for line in lines {
println!("{}", line.unwrap());
}
}
16. ファイルをN分割する
こちらの実装は素朴な実装とし、行数で分割しております。
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::BufWriter;
use std::io::prelude::*;
fn main() {
let args: Vec<String> = env::args().skip(1).collect();
let path = &args[0].to_string();
let file = File::open(path).unwrap();
let count: usize = BufReader::new(file).lines().count();
let div: usize = (&args[1]).to_string().parse().unwrap();
let file = File::open(path).unwrap();
let mut br = BufReader::new(file).lines();
let t = (count as f64/ div as f64).ceil() as usize;
for x in 1 .. div + 1 {
let l = br.by_ref();
let file = File::create(format!("{}.txt",x)).unwrap();
let mut bw = BufWriter::new(file);
for y in l.take(t).map(|m| m.unwrap().to_string()).collect::<Vec<String>>() {
let a = format!("{}\n", y);
bw.write(a.as_bytes()).unwrap();
println!("{}: {}", x, y);
}
}
}
17. 1列目の文字列の異なり
ファイル読込 + HashSet
で実装
use std::collections::HashSet;
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;
extern crate regex;
use regex::Regex;
fn main() {
let args: Vec<String> = env::args().skip(1).collect();
let path = &args[0].to_string();
let file = File::open(path).unwrap();
let re = Regex::new(r"\W+").unwrap();
let hs = BufReader::new(file).lines().map(|m|{
let l = m.unwrap().clone();
re.split(&l).next().unwrap().to_string()
}).collect::<HashSet<_>>();
for s in hs {
println!("{}", s);
}
}
18. 各行を3コラム目の数値の降順にソート
こちら、実数の比較を行う必要があり、すこし めんどう でした。
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;
fn main() {
let args: Vec<String> = env::args().skip(1).collect();
let path = &args[0].to_string();
let file = File::open(path).unwrap();
let mut val = BufReader::new(file).lines().map(|m| m.unwrap().split("\t").skip(2).next().unwrap().parse::<f64>().unwrap()).collect::<Vec<f64>>();
val.sort_by(|a, b| a.partial_cmp(b).unwrap());
for v in val { println!("{}", v); }
}
19. 各行の1コラム目の文字列の出現頻度を求め,出現頻度の高い順に並べる
18の問題を更にカウントできるように変更した。
use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;
enum Value {
USIZE(usize),
NONE(()),
}
fn main() {
let args = env::args().skip(1).collect::<Vec<String>>();
let file = File::open(&args[0]).unwrap();
let mut words: HashMap<String, usize> = HashMap::new();
for m in BufReader::new(&file).lines() {
let w = m.unwrap().to_string().split("\t").next().unwrap().to_string();
let v = match words.get(&w) {
None => 1,
Some(n) => n + 1,
};
words.insert(w, v);
}
let mut vars: Vec<(&String, &usize)> = words.iter().collect();
vars.sort_by(|a, b| b.1.cmp(a.1));
for (w, v) in vars {println!("{}: {}", w, v);}
}
おわり
この章は慣れてきたのか比較的楽に解けています。