nicovideo Thumbinfo popup の配付サイトを移動 & @grant 対応

超久々 Greasemonkey 更新情報です。

nicovideo Thumbinfo popup の配付サイトとして利用していた userscripts.org が死亡したらしいので、 新しい配付サイトとして Greasy Fork へと移動しました。

スクリプトの自動更新など行っている方は、上記サイトから再度スクリプトをダウンロードすると良いと思います。

ちなみに Greasemonkey 2.0 (と、Scriptish の最近のバージョン) で必要になったっぽい、 @grant 指定についても対応しています。 実は 5/2 にこっそりと更新していたのですが、このブログ上では告知していなかったので。。。

ついでに、 GitHubリポジトリを作成しました。

今後の方向性ですが、このスクリプトFirefox の独自構文を多様した作りになっているので、 ES6 や 7 の標準に従った作りにしていきたいな、と考えています。 Google Chrome 等との互換性をとるのも大変な作りになっていますし、Firefox 独自機能はいつ削除されるか分からないので。。。 あとは、もう少し全体的に見通しの良い作りにしたいですね。 めっきり静的型付け信者になってしまったので、 TypeScript への移行も選択肢の内かも。

swift-lang 処理系のインストールからFizzBuzzプログラムの実行まで

今話題の Swift 言語を触ってみました。Linuxで。

処理系のインストール

バイナリからのインストールは、Quickstart guideというテキストに非常に簡潔にまとまっています。

$ wget  http://swift-lang.org/packages/swift-0.94.1.tar.gz
$ tar xvf swift-0.94.1.tar.gz
$ cd swift-0.94.1
$ export PATH=$PATH:$PWD/bin

バイナリからのインストール手順は、たったこれだけ。簡単!!! ソースコードからのビルドは、噂によると少し大変なようです。

Swift プログラムの実行には、Java の処理系が必要なようです。 手元の環境では元々インストールされていたため何もしてませんが、 必要に応じて適当に無料ダウンロードしてきたりしてください。

Hello, world!

まずはお約束、hello world の実行。 Quickstart guide に手順が書いてありますが、実行ファイルが置いてある場所が違うためそのままだとエラーになるので注意。

$ cd swift-0.94.1/examples/swift/tutorial
$ swift hello.swift
Swift 0.94.1 swift-r7114 cog-r3803

RunID: 20140610-2237-vkvmx0j2
Progress:  time: 火, 10 6 2014 22:37:45 +0900
Final status: 火, 10 6 2014 22:37:45 +0900  Finished successfully:1

実行すると、 hello とも world とも言ってくれず、よく分からない出力が出てきますが、ご安心を。 "Hello, world!" という文字列は、hello.txt にちゃんと出力されています。

$ cat hello.txt
Hello, world!

hello world を標準出力ではなくファイルにはき出す言語、TeXくらいだと思っていました。。。

FIzzBuzz を書いてみる

swift の tutorial を見ると、処理系ダウンロードの後にいきなりシミュレーションとか並列実行的なことをしていて、いささかハードルが高いように感じます。 swift 初の自作プログラムの題材としては FizzBuzz を書いてみようと思います。 作り方としては、hello world をベースに、user guide を読んで必要そうな構文要素をピックアップします。 大体以下のような要素が必要になると思います。

  • プログラムの全体構成
  • 標準出力へ文字を出す
  • ループ構文
  • 条件分岐

プログラムの全体構成

さて、まずは hello world のソースをじっと見つめます。

type messagefile;

app (messagefile t) greeting() {
    echo "Hello, world!" stdout=@filename(t);
}

messagefile outfile <"hello.txt">;

outfile = greeting();

swift はスクリプト言語ということで、先頭から順番に実行されていくのでしょう。 最後の方の outfile = greeting(); などを見ると、トップレベルに文が書けるようです。 上から順番に読んでいくと、いきなり型宣言をしているようなのが気になります。 type とはなんぞや。user guide を読んでみます。

In Swift, structures are defined using the type keyword (there is no struct keyword).

type キーワードで構造体が定義されるそうです。では、先頭の messagefile というのは構造体名なのでしょうか。 たしかに、app 関数(?) では messagefile t というような宣言をしているように見えます。これは messagefile 型の t という変数があるということなのでしょうか。

なんだか、hello world をこれ以上読んでも理解が深まりそうにない気がしてきたので、とりあえずプログラムを書いてみましょう。

app () fizz() {
  echo "asdf";
}
fizz();

さて、なんとなくこれで端末に asdf と出力されてくれそうな気がします。 実行してみましょう。

$ swift fizzbuzz.swift
Could not compile SwiftScript source: line 1:6: expecting an identifier, found ')'

コンパイルできないと叱られてしまいました。identifierが続いて欲しかったのに、')' が来てしまったと。 空の括弧を app の後に書くのは恐らくだめなのでしょうね。

括弧を消してみました。これならどうでしょう。

$ cat fizzbuzz.swift
app fizz() {
  echo "asdf";
}
fizz();
$ swift fizzbuzz.swift
Swift 0.94.1 swift-r7114 cog-r3803

RunID: 20140610-2254-0j82vqr6
Progress:  time: 火, 10 6 2014 22:54:59 +0900
Final status: 火, 10 6 2014 22:54:59 +0900  Finished successfully:1

実行できました!!! しかし、出力文字列は、、、どこ? hello.swift では echo の後に stdout=@filename(t) なんて指定しているので、この指定を省略すれば標準出力にはき出してくれるんじゃねーの?と思ったんですが、見込み違いだったようです。

標準出力へ文字を出す

毎回ファイルにはき出された文字を見るのはめんどくさいので、標準出力に文字を出したいのですが、なんとかならないものか。users guide を見てみましょう。

。。。簡単に調べてみましたが、見つかりませんでした。。。仕方ないので、強引に解決します。

type messagefile;

app (messagefile t) fizz() {
  echo "asdf" stdout=@filename(t);
}

messagefile outfile <"/proc/self/fd/0">;

outfile = fizz();

実行してみます。

$ swift fizzbuzz.swift
Swift 0.94.1 swift-r7114 cog-r3803

RunID: 20140610-2300-5trv3m7d
Progress:  time: 火, 10 6 2014 23:00:41 +0900
asdf
Final status: 火, 10 6 2014 23:00:41 +0900  Finished successfully:1

なんかもういきなり hack な感じがしてとてもアレですが、標準出力に何か出すという目的は達成できたので良いとします。

ループ構文

さて、FizzBuzz を作る上で、必要なのがループ構文。 100回 echo を書いても良いのですが、さすがにそれは無駄だしめんどくさいです。 手続き型言語ならループ構文の一つや二つぐらいあるだろうし、気軽にできるでしょう。多分。 users guide を見てみましょう。Control Constructs なんて節がありますね。まさにコレだ。

ループには、 foreachiterate というものがあるようです。 foreach は配列 (または、コレクション全般?) の列挙に使うものでしょうし、コード例もそれっぽいです。 ここでは iterate を使うのが適切でしょう。

iterate のコード例を引用します。

iterate i {
    trace(i); // will print 0, 1, and 2
} until (i == 3);

do-while みたいですね。上記は、 iuntil の条件を満たすまでブロック内を実行するというコードです。 最後のi == 0 だと無限ループするのか、それとも一度もループが回らないのか、とても気になりますね。やってみましょう。

iterate i {
  trace(i);
} until (i == 3);

そもそも、コードを app のブロック (関数?) で囲む必要あったんだっけ?と疑問になったので、トップレベルでいきなりループを回すようにしてみました。 実行結果。

$ swift iterate.swift
Swift 0.94.1 swift-r7114 cog-r3803

RunID: 20140610-2320-u9xc7f29
Progress:  time: 火, 10 6 2014 23:20:21 +0900
SwiftScript trace: 0
SwiftScript trace: 1
SwiftScript trace: 2
Final status: 火, 10 6 2014 23:20:21 +0900

うまく動いているようです。 trace という関数名を見たときに、これは端末に出力するときに変な工夫しなくても簡単に出す方法あったかも、、、と思ったのですが、表示は上記のように完全にデバッグ向けっぽいので、余計なものなしに出すにはやっぱりさっきの hack が必要だったみたいですね。 さて、肝心の、until の中身を i == 0 にした場合についてやってみましょう。

iterate i {
  trace(i);
} until (i == 0);
$ swift iterate.swift
Swift 0.94.1 swift-r7114 cog-r3803

RunID: 20140610-2323-ag8rbbj0
Progress:  time: 火, 10 6 2014 23:23:51 +0900
SwiftScript trace: 0
SwiftScript trace: 1
SwiftScript trace: 2
SwiftScript trace: 3
SwiftScript trace: 4
...

無限ループです!!!これはますます do-while 感が高まりますね。 foreach は分かるのですが、もう一つのループ構文として while でも for でもなく do-until だけ採用しているというのもなかなか変わっている気がします。 users guide に書いていないだけかも知れませんが。。。

さて、これで FizzBuzz に必要な1から100まで数をループするというのはできそうですね。書いてみましょう。

type messagefile;

app (messagefile t) fizz() {
  iterate i {
    echo i stdout=@filename(t);
  } until (i >= 99);
}

messagefile outfile <"/proc/self/fd/0">;

outfile = fizz();
$ swift fizzbuzz.swift
Could not compile SwiftScript source: line 4:3: expecting an identifier, found 'iterate'

まさかのコンパイルエラー!!!!これは困る。users guide をざっと見た感じ、appはなんか特殊なプロシージャのようです。 ループとか書けないのかも。ちょっとよく分からないので、完全な理解は後で users guide を読んだ後にするとして、ここではなんか回避策を考えます。 これでどうでしょうか。

$ cat fizzbuzz.swift
type messagefile;

app (messagefile t) fizz() {
  echo "aaaa" stdout=@filename(t);
}

messagefile outfile <"/proc/self/fd/0">;

iterate i {
  outfile = fizz();
} until (i >= 99);
$ swift fizzbuzz.swift
Could not start execution
        Compile error in procedure invocation at line 10
        variable outfile is not writeable in this scope

コンパイルエラーは出なくなりましたが、実行時エラーが出ました。 このスコープでは outfile 変数は writeable ではない、と。 Rust の lifetime とか &mut の aliasing に関するエラーメッセージみたいですね。。。

スコープが駄目と言われたので、こうしてみました。

type messagefile;

app (messagefile t) fizz() {
  echo "aaaa" stdout=@filename(t);
}

iterate i {
  messagefile outfile <"/proc/self/fd/0">;
  outfile = fizz();
} until (i >= 99);

同じスコープで宣言した変数なら問題あるまい。

$ swift fizzbuzz.swift
Swift 0.94.1 swift-r7114 cog-r3803

RunID: 20140610-2335-r16kd5ig
Progress:  time: 火, 10 6 2014 23:35:57 +0900
aaaa
Duplicate mapping found:
        outfile (line 8) and outfile (line 8) are both used to write to file://localhost//proc/self/fd/0
aaaa
Execution failed:
        Exception in echo:
    Arguments: [aaaa]
    Host: localhost
    Directory: fizzbuzz-20140610-2335-r16kd5ig/jobs/m/echo-mtf8gvrl
Caused by:
        The cache already contains localhost:fizzbuzz-20140610-2335-r16kd5ig/shared/proc/self/fd/0.
        fizz, fizzbuzz.swift, line 9

うひー、なんかだめみたいです。同じ output を複数回使う事はできないのでしょうか。うーん。。。よくわからん。。。

ちょっとここであまり悩むのもあれなので、標準出力について妥協します!!!!

iterate i {
  trace(i);
} until (i >= 99);

ソースがかなり短くなりました。

$ swift fizzbuzz.swift
Swift 0.94.1 swift-r7114 cog-r3803

RunID: 20140610-2338-zck3zvnd
Progress:  time: 火, 10 6 2014 23:38:03 +0900
SwiftScript trace: 0
SwiftScript trace: 1
SwiftScript trace: 2
SwiftScript trace: 3
SwiftScript trace: 4
SwiftScript trace: 5
SwiftScript trace: 6
...
SwiftScript trace: 96
SwiftScript trace: 97
SwiftScript trace: 98
Final status: 火, 10 6 2014 23:38:03 +0900

できた!!!けど、数字が98までしか出ていません。

iterate i {
    trace(i);
    int j = i; // will print 0, 1, 2, and 3
} until (j == 3);

上記のサンプルプログラムで 0, 1, 2, 3 が出るなら、99まではいける、と思ったのですが、だめでした。 よくよく見ると、 int j = i; がとても重要なようです。users guide より引用します。

Variables declared inside the body of iterate can be used in the termination expression. However, their values will reflect the values calculated as part of the last invocation of the body, and may not reflect the incremented value of the iteration variable:

until 実行時点で i にはインクリメントされた値が入っているけど、j は body を抜ける時の i の値が入っている、ということみたいです。 つまり実行順序が、

  1. ji を代入
  2. i をインクリメント
  3. until の条件を判定

となるようです。うーん。な、なるほど。。。

というわけでプログラムを直してみました。 よくよく考えると FizzBuzz は 1 から 100 までの数字を出さなければいけないので、それも直しました。

iterate i {
  trace(i + 1);
} until (i == 100);

これで、1から100までが出力されました。

条件分岐

さて、ここまで来たらあと一息ですね! if の構文は C と同じようです。また、整数の剰余の演算子%% のようです。 ここまで分かれば、書けますね!!!

iterate i {
  int n = i + 1;

  if (n %% 3 == 0 && n %% 5 == 0) {
    trace("FizzBuzz");
  } else if (n %% 3 == 0) {
    trace("Fizz");
  } else if (n %% 5 == 0) {
    trace("Buzz");
  } else {
    trace(n);
  }

} until (i == 100);
$ swift fizzbuzz.swift
Could not compile SwiftScript source: line 6:10: expecting '{', found 'if'

前言撤回。C の if とは違い else{} は省略できないようです。

iterate i {
  int n = i + 1;

  if (n %% 3 == 0 && n %% 5 == 0) {
    trace("FizzBuzz");
  } else {
    if (n %% 3 == 0) {
      trace("Fizz");
    } else {
      if (n %% 5 == 0) {
        trace("Buzz");
      } else {
        trace(n);
      }
    }
  }

} until (i == 100);

入れ子が深いですが、できました。実行します。

$ swift fizzbuzz.swift
Swift 0.94.1 swift-r7114 cog-r3803

RunID: 20140610-2349-qkryujr6
Progress:  time: 火, 10 6 2014 23:49:45 +0900
SwiftScript trace: 1
SwiftScript trace: 2
SwiftScript trace: Fizz
SwiftScript trace: 4
SwiftScript trace: Buzz
SwiftScript trace: Fizz
SwiftScript trace: 7
SwiftScript trace: 8
SwiftScript trace: Fizz
SwiftScript trace: Buzz
SwiftScript trace: 11
SwiftScript trace: Fizz
SwiftScript trace: 13
SwiftScript trace: 14
SwiftScript trace: FizzBuzz
SwiftScript trace: 16
SwiftScript trace: 17
SwiftScript trace: Fizz
SwiftScript trace: 19
SwiftScript trace: Buzz
SwiftScript trace: Fizz
SwiftScript trace: 22
SwiftScript trace: 23
SwiftScript trace: Fizz
SwiftScript trace: Buzz
SwiftScript trace: 26
SwiftScript trace: Fizz
SwiftScript trace: 28
SwiftScript trace: 29
SwiftScript trace: FizzBuzz
SwiftScript trace: 31
SwiftScript trace: 32
SwiftScript trace: Fizz
SwiftScript trace: 34
SwiftScript trace: Buzz
SwiftScript trace: Fizz
SwiftScript trace: 37
SwiftScript trace: 38
SwiftScript trace: Fizz
SwiftScript trace: Buzz
SwiftScript trace: 41
SwiftScript trace: Fizz
SwiftScript trace: 43
SwiftScript trace: 44
SwiftScript trace: FizzBuzz
SwiftScript trace: 46
SwiftScript trace: 47
SwiftScript trace: Fizz
SwiftScript trace: 49
SwiftScript trace: Buzz
SwiftScript trace: Fizz
SwiftScript trace: 52
SwiftScript trace: 53
SwiftScript trace: Fizz
SwiftScript trace: Buzz
SwiftScript trace: 56
SwiftScript trace: Fizz
SwiftScript trace: 58
SwiftScript trace: 59
SwiftScript trace: FizzBuzz
SwiftScript trace: 61
SwiftScript trace: 62
SwiftScript trace: Fizz
SwiftScript trace: 64
SwiftScript trace: Buzz
SwiftScript trace: Fizz
SwiftScript trace: 67
SwiftScript trace: 68
SwiftScript trace: Fizz
SwiftScript trace: Buzz
SwiftScript trace: 71
SwiftScript trace: Fizz
SwiftScript trace: 73
SwiftScript trace: 74
SwiftScript trace: FizzBuzz
SwiftScript trace: 76
SwiftScript trace: 77
SwiftScript trace: Fizz
SwiftScript trace: 79
SwiftScript trace: Buzz
SwiftScript trace: Fizz
SwiftScript trace: 82
SwiftScript trace: 83
SwiftScript trace: Fizz
SwiftScript trace: Buzz
SwiftScript trace: 86
SwiftScript trace: Fizz
SwiftScript trace: 88
SwiftScript trace: 89
SwiftScript trace: FizzBuzz
SwiftScript trace: 91
SwiftScript trace: 92
SwiftScript trace: Fizz
SwiftScript trace: 94
SwiftScript trace: Buzz
SwiftScript trace: Fizz
SwiftScript trace: 97
SwiftScript trace: 98
SwiftScript trace: Fizz
SwiftScript trace: Buzz
Final status: 火, 10 6 2014 23:49:45 +0900

結果だけ見ると、普通の FizzBuzz プログラムですが、妙に遠回りしてしまった気がします。。。

まとめ

Swift の面白そうなところにはたどり着けませんでしたが、エラーメッセージ等から何か裏に潜んでいそうな気配を感じ、なかなか楽しめそうです。 ひとまず今回は体当たりしてみましたが、次はもう少し体系的に学んでからちゃんとしたプログラムを書きたいと思います。 あと、面白そうな言語を紹介してくれた Apple に感謝です。

無料ダウンロードできる言語処理系まとめ

言語は適当にチョイス。順番も適当。文言はパッと見で目立つものをピックアップ。 言語処理系にはランタイムとかコンパイルも含むゆるふわな分類です。

言語 ベンダ・処理系名 文言
Cなど GNU, GCC GCC, the GNU Compiler Collection
Rust Mozilla, rustc Rust is a systems programming language that runs blazingly fast, prevents almost all crashes*, and eliminates data races.
Java Oracle JRE JAVA + YOU, DOWNLOAD TODAY!
Free Java Download
あなたとJAVA, 今すぐダウンロード
無料Javaのダウンロード

飽きた。

特定の major-mode "以外" の場合に hook を設定にする emacs lisp

やり始めたら 30 分くらいハマってたのでメモ。

以下コードは、 rust-mode "以外" の場合に after-save-hookexecutable-make-buffer-file-executable-if-script-p を設定するものです。

(defun after-change-major-mode-hook-fn ()
  (unless (eq major-mode 'rust-mode)
    (add-hook 'after-save-hook 'executable-make-buffer-file-executable-if-script-p nil t)))
(add-hook 'after-change-major-mode-hook 'after-change-major-mode-hook-fn)

解説

  • add-hook は optional な第4引数に nil 以外を設定すると buffer-local な hook を設定可能
  • after-change-major-mode-hook は、バッファのメジャーモードが設定された後に呼び出される hook

上記の合わせ技で、特定メジャーモード以外の場合に hook を設定することを実現しています。

できなかったこと

上記にたどり着くまでに以下を試してみましたが、うまくいきませんでした。原因は分かっていません。

  • after-save-hook に global (非 buffer-local) な hook を追加する。rust-mode-hookremove-hook を buffer-local オプション付きで呼び出し、追加した hook を削除する。
  • change-major-mode-hook で buffer-local な hook を追加する。 rust-mode-hook の場合のみ remove-hook を呼び出し、追加した hook を削除する。

hook の動作原理を知らないと、このあたりはハマりどころとなって試行錯誤するハメになりそう。。。勉強せねば。。。

動機

Rust の attribute syntax が変更されたため、executable-make-buffer-file-executable-if-script-p が、以下のような 「crate_id が設定されているファイル」を 「shebang 付きの実行可能ファイル」と認識してしまうようになってしまいました。

#![crate_id = "foo"]
#![crate_type = "bin"]

fn main() {}

executable-make-buffer-file-executable-if-script-p は、スクリプトファイルを作成する際の chmod してパーミッション設定する手間を省いてくれる非常に便利な関数なのですが、rust のソースコードを保存する度に execute のパーミッションを設定されるのは非常に鬱陶しいかったため、rust-mode でだけ hook を削除したかった、というのが動機です。

最後に

Rust 0.10 が先日リリースされたので、みんなで使いましょう!!!

今回のリリースから、Mac/Linux 向けのバイナリや、公式の nightly-build が提供されるようになりました。もうLLVMコンパイルを長時間待つ必要はありません!!! あなたと rustlang, 今すぐダウンロード。

rustpkg test でプロダクトコード中に書かれたユニットテストを実行する

以下のようなパッケージ foo が存在したとします。

// src/foo/lib.rs
#[crate_id = "foo"];

pub fn super_complex_function() -> uint { 42 }

#[cfg(test)]
mod test {
  #[test]
  fn test_super_complex_function() {
    assert_eq!(42, super::super_complex_function());
  }
}

上記に対し、適当な src/foo/test.rs を作成して rustpkg test foo を実行しても、 lib.rs 中のユニットテストは実行されません。

// src/foo/test.rs
extern mod foo;
$ rustpkg test foo
WARNING: The Rust package manager is experimental and may be unstable

running 0 test

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured

以下のように test.rs のサブモジュールとして lib を読み込むと、ユニットテストが走行するようになります。

// src/foo/test.rs
mod lib;

#[test]
fn test() {}
$ rustpkg test foo
WARNING: The Rust package manager is experimental and may be unstable
/home/nksm/tmp/rustpkg/src/foo/lib.rs:1:1: 1:21 warning: crate-level attribute should be in the root module, #[warn(attribute_usage)] on by default
/home/nksm/tmp/rustpkg/src/foo/lib.rs:1 #[crate_id = "foo"];
                                        ^~~~~~~~~~~~~~~~~~~~

running 1 test
test lib::test::test_super_complex_function ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured

警告が出るのが嫌な場合は、 lib.rs はサブモジュールを読み込むだけの内容に留めておき、test.rs からもサブモジュールを参照するだけにすれば良いと思います。

rustpkg は、パッケージングのためのツールで、開発時にモジュール内部のユニットテストを実行させることは、本来の使い方からは外れているのだと思われます (筆者の勝手な想像ですが)。 ただ、開発時用にわざわざ別のビルド方法を準備するのもなんだか嫌な感じだったので、上記のように回避するのもありかなーと思いました。