ubnt-intrepid's blog

書いてあることがブログの内容です

Rust のコマンドラインオプション解析色々

本エントリは Rust その2 Advent Calendar 2016 - Qiita の 10 日目の記事です。

本記事では、Rust を使って CLI アプリケーションを作成してきた過程で得られた知見のひとつとして、コマンドラインオプション解析に用いる crate を簡単にまとめたいと思います。

基本事項

コマンドライン引数は std::env モジュールの args() (または args_os() )を用いて取得します。 最初の要素には通常実行ファイルのパスが格納され、引数自体は2番目以降に格納されます。

use std::env;

fn main() {
  let program: String = env::args().next().unwrap();
  let args: Vec<String> = env::args().skip(1).collect();
}

getopts

getopts は公式の提供しているコマンドライン解析用の crate です。 使い勝手としては Python の argparse が近いと思います。

github.com

getopts の特長として、必要最小限の機能しか提供していないためシンプルであり、導入における敷居が低いことが挙げられます。 そのため、単純なオプションを解析を行う場合にはこれで十分事足りると思います。

使い方は以下のようになります(公式ドキュメントの例を一部修正)。

extern crate getopts;
use std::{env, process};
use getopts::Options;

#[derive(Debug)]
struct Args {
  input: Vec<String>,
  output: Option<String>,
  // ...
}

fn print_usage(program: &str, opts: &Options) {
  let brief = format!("Usage: {} FILE [options]", program);
  print!("{}", opts.usage(&brief));
  process::exit(0);
}

fn parse_args() -> Args {
  let args: Vec<String> = env::args().collect();
  let program = args[0].clone();

  let mut opts = Options::new();
  opts.optopt("o", "", "set output file name", "NAME");
  opts.optflag("h", "help", "print this help menu");
  // ...

  let matches = opts.parse(&args[1..])
    .unwrap_or_else(|f| panic!(f.to_string()));

  if matches.opt_present("h") {
    print_usage(&program, &opts);
  }

  if matches.free.is_empty() {
    print_usage(&program, &opts);
  }

  Args {
    input: matches.free.clone(),
    output: matches.opt_str("o"),
    // ...
  }
}

fn main() {
  let args = parse_args();
  println!("{:?}", args);
}

docopt.rs

docopt.rs はドキュメント文字列を元にオプション解析器を生成する crate です。 多言語にも同名のパッケージがあると思いますが、それらと同じ感覚で使用することが出来ます。

github.com

docopt.rs 特有の機能として、RustcDecodable を実装した構造体を用いてオプションの解析結果を 受け取ることができます。 また、nightly 限定ですがドキュメントを解析して解析結果を受け取る構造体を自動生成する docopt! という マクロを使用することでオプション解析を簡略化することが出来ます(僕は stable 派なので使ったことないですが…)。

使用例は以下の通りです。

extern crate docopt;
extern crate rustc_serialize;

use docopt::Docopt;

const USAGE: &'static str = r"
JSON version of xargs
Usage:
  jsonargs [--parallel] <name> [<args>...]
  jsonargs (-h | --help)
Options:
  -h --help     Show this message.
  --parallel    Run each command parallel
";

#[derive(Debug, RustcDecodable)]
struct Args {
  flag_parallel: bool,
  arg_name: String,
  arg_args: Vec<String>,
}

fn main() {
  let args = Docopt::new(USAGE)
    .and_then(|opt| opt.decode())
    .unwrap_or_else(|e| e.exit());
  let Args { arg_name: name, arg_args: args, flag_parallel: parallel } = args;

  // ...
}

docopt.rs の詳細な使用方法ははすでに良質な解説記事がありますのでそちらもご参照ください。

qiita.com

clap.rs

getoptsdocopt.rs はどちらかと言うと小規模なアプリケーション向けのパーサであり、 複数のサブコマンドを用いるなど複雑なオプションが必要なアプリケーション向きでは無いです。

複雑なコマンドラインオプションの解析には clap.rs を用いることが出来ます。

github.com

claprustup でも用いられているコマンドラインパーサであり、 公式リポジトリで紹介されている機能一覧を見てもわかるように とにかく高機能です(語彙力)。

とりあえず気になるものだけ列挙してみると、次のような機能があります。

  • 色付きのヘルプメッセージの出力に対応
  • YAML によるオプション定義への対応
  • 各種シェル用の補完スクリプトの自動生成 (Bash, fish shell, Zsh)

使用例は以下の通りです。

extern crate clap;
use clap::{App, AppSettings, Arg};

fn build_app() -> clap::App<'static, 'static> {
  let program = std::env::args()
    .nth(0)
    .and_then(|s| {
      std::path::PathBuf::from(s)
        .file_stem()
        .map(|s| s.to_string_lossy().into_owned())
    })
    .unwrap();

  App::new(program)
    .about("find files")
    .version("0.0.1")
    .author("Author name <author@example.com>")
    .setting(AppSettings::VersionlessSubcommands)
    .arg(Arg::from_usage("-i --ignore=[IGNORE]   'Ignored pattern'"))
    .arg(Arg::from_usage("-m --matches=[MATCHES] 'Pattern to match'"))
    .arg(Arg::from_usage("-a --absolute          'Show absolute path'"))
    .arg(Arg::from_usage("-d --directory         'Show only directories'"))
    .arg(Arg::from_usage("-A --async             'Search asynchronously'"))
    .arg(Arg::from_usage("-M --max-items=[N]     'Limit of displayed items'"))
    // ...
}

fn main() {
  let matches = build_app().get_matches();
  let matchre : Option<&str> = matches.value_of("matches");
  let ignore: Option<&str> = matches.value_of("ignore");
  // ...、
}

個人的に便利だと思ったのが、各種シェル用の補完関数を自動生成する機能です。 例えば Zsh 用の補完スクリプトを生成する場合は次のようにすることで可能です。

use clap::Shell;

let mut file = std::fs::OpenOptions::new()
  .write(true)
  .create(true)
  .open(concat!("_", env!("CARGO_PKG_NAME")))
  .unwrap();

build_app().gen_completions_to(env!("CARGO_PKG_NAME"), Shell::Zsh, &mut file);

おわりに

以上、Rust でコマンドラインオプションの解析に使える crate の紹介でした。

近況

衝動的に Rust で色々作ったので、宣伝がてら忘れないようにまとめておく。 正直 Rust 関係ない気もするが気にしない。 あと全体的にドキュメントが貧弱なので他の人に使ってもらうためにはもう少し頑張らないといけない気がする。

ghqrs

github.com

Git のローカルリポジトリの管理ツール。 名前からお察しの通り ghq の Rust クローンである。 といっても ghq との互換性はそこまで意識しておらず、自分が使いやすければ良いかなぁというモチベーションでコマンドラインオプションを設定した。 現状は対応している VCS が Git だけだったりと色々物足りないのでそのうち本腰入れて開発を進めたいと考えている。

dot.rs

github.com

dotfiles の管理ツール。 もともと dotfiles の管理には ssh0 氏の作った dot というツールを使っていたのだけど、どうせなら bash/zsh に依存しないものが欲しくなり作ってみた。

github.com

Windows で使うことを意識したため、シンボリックリンクの作成時に自動的に管理者へと昇格するなどの機能をつけた。

なお、実装時には下のプロジェクトを微妙に参考にした(マッピング機能など)。というより、このリポジトリを見つけてしまったのが開発の動機だったりする。

github.com

vcs_info.rs

github.com

こちらはバージョン管理システムの情報をプロンプトに表示するためのツール。 Zshvcs_info が遅かったのでかっとなって作ってみた。 現状は Git, Mercurial, Subversion のみに対応している。 表示形式の変更などはまだ対応していないが、そのうちするかも。

これを作る過程で Go 言語の勉強がてらも作ってみたので一応紹介しておく。 github.com

regrun.rs

github.com

レジストリに登録された Path の値を検索してプログラムを実行する CLI ツール。 これはまだ始めたばかりのプロジェクトで、今後の展開次第では direnv 的なものに移行する可能性がある。

おわりに

最近精神が荒んできている気がするので安寧がほしい

深層カルマンフィルタ (5)

前回は状態空間モデルにおける変分下限を導出した。 この関数をパラメータ  \phi について微分可能にするために必要な認識モデル  q への制約を考えることが今回の目標である。

いよいよ深層学習っぽい話になると期待している。

続きを読む

深層カルマンフィルタ (4)

…さて、ここからが本題。 同じ著者による新しい論文がアップロードされていたので、こちらも適宜参照しながら読み進めていく。

[1609.09869] Structured Inference Networks for Nonlinear State Space Models

続きを読む

深層カルマンフィルタ (3)

今回は変分自己符号化器 (VAE) における確率勾配変分ベイズ (SGVB) の説明。 なお、SGVB 自体の説明は本家の論文、および以下のものを参考にしたのでそちらも参照されたい。 正直、下の資料さえあれば今回の内容は飛ばしても良い気もするが、一応まとめておく。

続きを読む

深層カルマンフィルタ (2)

昨日の続き。

と言いつつ、昨日の内容についてはおろかその他もろもろの理解が不十分だったことに気づいたため、そのあたりを復習しつつ読み進めてる。 そのため、本題に入る前に大幅に脱線するので注意されたい(自然消滅しないよう頑張ります)。

続きを読む

深層カルマンフィルタ (1)

文献: [1511.05121] Deep Kalman Filters の感想 + 学習メモ。 流し読みしてみた感じそこまで難しいことはしてなさそうだったので、RNN の勉強がてら読んでみてる。

参考文献

深層カルマンフィルタ関連

その他参考にしたもの

関連ありそうで面白そうなもの(適当に選出)

概要

時系列データのモデリング機械学習(およびシステム同定など多くの分野)において重要な課題であり、多くのモデルが提案されている。それ自体膨大でありここですべてを網羅するのは困難であるため、ここでは文献中で取り上げられているものについて軽くまとめてみた。

  • 隠れマルコフモデル (hidden Markov model; HMM)
    観測系列の背後に離散的な値を取る潜在変数を仮定したモデル。 潜在変数の系列はマルコフ連鎖から生成される。

  • カルマンフィルター (Kalman filter; KF)
    線形システムにおいて、ノイズを伴う雑音から状態を推定するための無限インパルス応答フィルターのひとつ。 正確には、 KF という用語が指しているのはモデルではない(KF で用いられる対象の動的システムがモデル)。 古くから用いられてきたモデルであるが、線形性および雑音の正規性など前提条件が多いことが問題になる。 非線形システムへの適用を考慮し幾つかのバリエーションが存在する (EKF, UKF)。

  • 動的ベイジアンネットワーク (dymamic bayesian network; DBN) (訳はこれでいいのか不明)
    HMM, KF などを一般化したグラフィカルモデル。HMM や KF は DBN の特殊な場合として導出することが可能。 一般に複雑な状態遷移確率を設定した場合の学習則における各種値を解析的に求めるのは難しく、 モンテカルロ法や変分近似などを用いた近似計算が併用される。パーティクルフィルタやその他諸々を参照されたい。

  • 再帰ニューラルネットワーク (recurrent neural network; RNN)
    再帰的な構造を持つニューラルネットワーク(適当)。 NN の出力を確率とみなせば確率分布の近似にも用いることができる([1411.7610] Learning Stochastic Recurrent Networks など) 本文献では学習時において(変分)事後分布  q_{\phi} を近似するのに用いている

本論文は、上のモデルのうち非線形システムの同定を、カルマンフィルタで用いられている正規性の仮定などをうまく使って頑張る話。 観測と入力との間に複雑な非線形を持つような時系列データに対する生成モデルの学習における(深層学習を用いた)統一的な枠組みを与えている感じ。 結果が非線形システムとして与えられる点も制御・強化学習との組み合わせがきれいに出来そうで好印象。

...疲れたのでまずはここまで


(追記: 2016-10-15 03:04) 内容の重複するしている箇所を削除