[Ruby] Ruby の for と each の違いは変数のスコープ
ハマったのでメモ。 for と each の違いです。
t = [] for i in [1,2,3] # [1,2,3].each do |i| t << Thread.new do puts "start #{i}" sleep i p Thread.current puts "end #{i}" end end p t while t[0].alive? end t[1].kill t[2].kill p t
こいつに thread.rb とか名前つけて、実行
$ ruby thread.rb start 1 start 2 start 3 [#<Thread:0x100169be8 sleep>, #<Thread:0x100169ad0 sleep>, #<Thread:0x1001699b8 sleep>] #<Thread:0x100169be8 run> end 3 [#<Thread:0x100169be8 dead>, #<Thread:0x100169ad0 dead>, #<Thread:0x1001699b8 dead>]
アレ?ちゃんと t[0] が終わってるんだし、そこは end 1 って出てほしいんだけど。
そこで、 for のところを each に変えて実行。
$ ruby thread.rb start 1 start 2 start 3 [#<Thread:0x100169b20 sleep>, #<Thread:0x1001699b8 sleep>, #<Thread:0x100169850 sleep>] #<Thread:0x100169b20 run> end 1 [#<Thread:0x100169b20 dead>, #<Thread:0x1001699b8 dead>, #<Thread:0x100169850 dead>]
こんどはちゃんと end 1 が出る。
あれーと悩んでいたら、次のページを発見。
for =>そのfor文が定義されているスコープに対して、iを定義する
http://ipcom.withgood.net/wordpress/?p=484
each=>eachブロック内でのみ生存する新たなスコープを用意し、そこでiを定義する
つまり、この場合は i がグローバルになっちゃうので、 for のループごとに i の値が書き変わっちゃうのが原因でした。
3つめのスレッドを作った時点で i の値は 3 になり、1つめのスレッドが終了したときその i の値を参照するので 3 が出力されると。
Ruby では基本的に for ではなく each を使うのがよさそうですね。。
[Emacs] Emacs の popup.el を使って、 MacOSX の Dictionary.app から辞書をひく
Mac の Dictionary.app 便利ですね。
http://sakito.jp/mac/dictionary.html
なんかに使い方が書いてあって、 Emacs との連携も書いてあるのですがいかんせん新しいウィンドウを作ってしまうのがスマートでない。
popup.el なんていう素晴しいインターフェイスもあることだし、ここはひとつツールチップの中で表示させてみてはどうか。
と思って書いたのが以下です。
上記ページの dict.py が必要です。
;; dict.py is from http://sakito.jp/mac/dictionary.html (defun dictionary () "dictionary.app" (interactive) (let ((word (if (and transient-mark-mode mark-active) (buffer-substring-no-properties (region-beginning) (region-end)) (read-string "Dictionary: "))) (cur-buffer (current-buffer)) (tmpbuf " * dict-process *")) (set-buffer (get-buffer-create tmpbuf)) (erase-buffer) (insert word "\n") (let ((coding-system-for-read 'utf-8-mac) (coding-system-for-write 'utf-8-mac)) (call-process "~/scripts/dict.py" nil tmpbuf nil word) ;; specify full pass of dict.py (let ( (str (buffer-substring (point-min) (- (point-max) 2)))) (set-buffer cur-buffer) (popup-tip str :scroll-bar t)) ))) (global-set-key (kbd "C-M-d") 'dictionary)
だいたい思い通りに動いているんだけど、問題点が2つほど。
- ツールチップに入りきらない場合を考えて popup-tip のスクロールバーを表示させているのだけど、実際にスクロールする方法がわからない。
- dict.py から辞書をひくとき、日本語で辞書をひくと例文まで取得できるけど英語でひくと例文までは取得できない。
1つめはキーマップ定義したりしてなんとかなるんじゃないかなーとか思っているんだけど。
とりあえず応急処置としては、
(popup-tip str :scroll-bar t :height 30)
とかやれば広くなるので、表示量が増えます。デフォルトは 15 です。
2つめは Dictionary.app の API が悪くて、 dict.py 中の
DCSCopyTextDefinition(None, word, (0, len(word)))
という箇所。こいつの挙動がよくわからない。
他に API あるんじゃないのと思って調べてみたけどよくわからなかった。
なので、あくまでざっくりと調べる用で本当にきちんと調べたいときは Dictionary.app から調べましょうということで。
[Ruby] Ruby
訓練のためにと思って Ruby を使ってあるプログラムを書いています。
一応専門がプログラミング言語理論とか型理論とかその辺で、Javaに慣れた身としては戸惑うことがたくさんあります。
というか動的型付け言語全般の話かもしれません。
特に慣れないのが
- メソッドや変数に型注釈を付けない
という点。
型注釈、 Type Annotation っていうのは最大のドキュメンテーションです。
これがないコードがいかに読みにくいか。
自分の卒論は型推論の実装でしたが、そもそも型推論って理論的には面白いけど、(Type Annotation を付けないという点で)実用上そこまで意味があると思えないんですよね。
あとは、まあ Ruby ってどうなのと思うものもいくつかありまして、
- 後置 if 読みにくい
- unless の存在が許せない
- 抽象クラスがない
- メソッド内で @変数名 を使うとそれがインスタンス変数になる(クラス宣言時に最初にインスタンス変数を列挙できない)
- private :変数名 とか attr :メソッド名 とかキモイ
- あとやっぱり end キモイ
とか、そのへんですかね
まともに書き出してまだ1週間くらいで、まだまだ慣れない。。
[メモ] gitサーバの構築
あるプロジェクトで複数人でgitを使って開発しようということになったので、gitサーバを構築したメモ。環境はさくらVPS上の CentOS 5.4 です(゚∀゚)
1. gitインストール
まずgitをインストールします。。rootで作業。
remote# vi /etc/yum.repos.d/CentOS-Base.repo
して、以下を追加
[dag] name=Dag RPM Repository for Redhat EL5 baseurl=http://apt.sw.be/redhat/el$releasever/en/$basearch/dag gpgcheck=1 enabled=0 gpgkey=http://dag.wieers.com/packages/RPM-GPG-KEY.dag.txt
あとは
remote# yum --enablerepo=dag -y install git
でおk。ソースとってきて make && make install でも簡単らしいです(´∞`)
2. リポジトリ作成
/var/git 以下に置くことにしました。
remote# mkdir -p /var/git remote# cd /var/git remote# mkdir test.git remote# git --bare init --shared
で、空のリポジトリができました。
remote# groupadd dev remote# usermod -G dev user remote# chown root:dev -R .
として dev グループの人用に設定します。サーバ側の設定は以上
3. クライアントからリポジトリを登録
ローカルで適当なリポジトリを作ってサーバ側に登録します。
local$ mkdir test local$ cd test local$ git init
空のリポジトリができます。次にリモートリポジトリの情報を登録します。
local$ git remote add origin ssh://user@server/var/git/test.git
でおkです。あるいは、 ssh://user@server の部分は .ssh/config のホスト名を指定しても大丈夫っぽいです。
local$ git remote add origin myhost:/var/git/test
こんな感じ。
local$ echo hoge > test.txt local$ git add test.txt local$ git commit -m "First commit"
で、ローカルのリポジトリに test.txt をコミットしました。これをサーバに転送します。
local$ git push origin master
と打って origin に master を push します。
4. 確認
うまく行ってそうなら、豪快にディレクトリごと削除してリモートから clone してみます。
local$ cd ../ local$ rm -rf test local$ git clone ssh://user@server/var/git/test.git
これで元通り。
[メモ] ssh でリモートログイン
ssh でリモートログインするときはパスワードを手打ちしていたけど、リモートでちょっと大規模なファイルの編集をする必要が出てきて、 Emacs の tramp をきちんと使いたくなったのでパスワードをいちいち打つのが面倒になってきた。
そこで、公開鍵と秘密鍵を設定してパスワードなしでログインできるようにした。
以下設定の手順:
1. 秘密鍵の作成
ローカルから
local$ ssh-keygen
と入力。
Enter file in which to save the key (/Users/dai/.ssh/id_rsa):
はそのままで Enter、
Enter passphrase (empty for no passphrase): Enter same passphrase again:
2. リモート側に公開鍵を設定
scp でリモートに鍵を転送して、 authorized_keys を設定する。
local$ scp ~/.ssh/id_rsa.pub user@www.host.com: local$ ssh user@www.host.com remote$ mv id_rsa.pub .ssh/authorized_keys remote$ chmod 600 .ssh/authorized_keys
でOK. ただし、 authorized_keys がすでに存在する場合 (すでに他のマシンからの公開鍵を設定している場合) は
remote$ cat id_rsa.pub >> ~/.ssh/authorized_keys remote$ rm id_rsa.pub
と追記する。以上でとりあえず終了。
local$ ssh www.host.com
でパスワードなしでいけるようになる。あ、 Mac の場合はキーチェーンアクセスがどうとか出てきた。
ホストに別名を付けたい場合や、複数リモートの設定をする場合はさらに以下を。
3. 複数リモートの設定を書く
2. までの手順リモートマシンごとにやる。 id_rsa ファイルは上書きされないように適宜名前を変えておく。
ローカルの ~/.ssh/config を開いて、
Host host1 HostName www.host1.com Port 22 User hoge IdentityFile ~/.ssh/id_rsa_host1 Host host2 HostName www.host2.com Port 22 User fuga IdentityFile ~/.ssh/id_rsa_host2
と記入する。これで、
local$ ssh host1
とすれば hoge@host1 に、
local$ ssh host2
とすれば fuga@host2 に ssh できるようになる。
4. .zshrc にホスト名を記述
.zshrc に
zstyle ':completion:*' users-hosts host1 host2
などと記述しておけば
$ ssh <TAB>
で補完してくれる。 user@ を前に付けてもいける。