Rubyでハッシュのキーをシンボルに統一する

ハッシュのキーは全てシンボルにしたい派.しかし,YAML を使っていると「key: value」を load したときに {"key"=>"value"} になってしまう.だからといって「:key: value」と書くのも面倒.

ということで Hash のキーをシンボルに統一するメソッド symbolize_keys を Hash クラスに追加した.

class Hash
  def symbolize_keys
    inject({}) do |options, (key, value)|
      value = value.symbolize_keys if defined?(value.symbolize_keys)
      options[(key.to_sym rescue key) || key] = value
      options
    end
  end
end

hash = {"a" => "aaa", "b" => {c: "ccc", "d"=>"ddd"}, "e" => "eee"}
# => {"a"=>"aaa", "b"=>{:c=>"ccc", "d"=>"ddd"}, "e"=>"eee"}
hash.symbolize_keys
# => {:a=>"aaa", :b=>{:c=>"ccc", :d=>"ddd"}, :e=>"eee"}

Rails を再帰的に利用できるように1行追加しただけ.再帰が嫌ならブロック内の1行目(value = ...)を削除.例のようにキーにシンボルと文字列が混ざっていても問題なく動作する.

Rubyで豊富なイテレータ(ブロック付きメソッド)を使用する理由

ary.each do で全てのケースをカバーできるのに、特別な構文をいくつも用意してタイプ量をわずかに減らすことに何の意味があるのでしょうか?

というコメントをいただいたので,自分なりの考えを述べたいと思う.

様々なイテレータがある理由,これは一言で言えば,「Ruby はコードの美しさに重きを置く言語だから」だ.Ruby のコードは最も読みやすく,最も短く,そして最も美しく書くことが求められる.Ruby のコードはエレガントでなければならないのだ.

これを踏まえた上で,Rubyの豊富なイテレータについて,私の考えるキーワードは3つ.

  1. 生産性
  2. 可読性
  3. 速度

まずは生産性.タイプ量が減るというよりも,やりたいことを最適なイテレータで書ける,やりたいことを直感的に書けるということの方が大きいと思う.

例えば map メソッド.これは配列全ての要素に同じ処理を行って新しい配列を作るというものだ.

文字列の配列を,その長さの配列に変換したいとき,map メソッドを用いて

ary = ["a", "abc", "abcdefg"]
ary.map {|e| e.length}          # => [1, 3, 7]

で書けることを,each を使ってどのように実現するだろうか.map を使えば簡単にできるのに,あえて each を使うのは無駄だし,each でこれ以上わかりやすく美しく書くことは不可能だろう.

これは,可読性にも直結しており,エレガントな Ruby プログラムは(Rubyistにとって)非常に読みやすい.短いコードに何をしたいのかがはっきり表現されている.これは良いソースコードとして大切な条件だ.

また,各イテレータに対して最適化が行えるため,単に each で書くよりも実行速度が早い(はず.間違ってたら申し訳ない).

ちょっとわかりにくいけど非常に便利なinjectメソッド - 勉強日記 でも述べたが,C言語には while 文と for 文が存在し,互いに変換可能である.しかし,while で書けることを for で書くことに意味がないと感じるだろうか.定数回のループであれば,while で書けたとしても私なら for を使う.

Ruby の each メソッドとその他のイテレータの関係は,この while と for に似たものだと私は考えている.どちらでも書けるが,適した場合には each 以外を使った方が嬉しいのだ.

Ruby には非常に豊富なイテレータが存在する.そのそれぞれのイテレータの使用方法などをググって,エレガントな使用例を探してみてほしい.また,それを each で書き直してみてほしい.きっと Rubyイテレータが豊富な理由がさがありがたく感じるはずだ.

each よりも別のイテレータを使う方が読みやすく書きやすい.これが私の考える Ruby の豊富なイテレータを使用する理由である.

Emacsで書いたorg文書とToodledoを同期するorg-toodledo

今までTodo管理にToodledoを使っていたが,これをEmacsで管理できないだろうかと思った.でも,Emacsが使えない環境でもTodoの確認をしたり,Toodledoと同期するiPhoneアプリも使い続けたいので,org文書に書いたTodoリストとToodledoのTodoリストを同期できる方法を探してみた.

同じことを考える人は多いようで,複数の実装が見つかった.しかも全部「org-toodledo.el」という名前.その中で,今もメンテナンスされているっぽいchristopherjwhite氏の org-toodledo.el を試してみたので導入方法を紹介する.使用する場合は自己責任で.

まず,org-toodledo をダウンロードしてくる.gitが使えるなら下のコマンドを叩く.

$ git clone git://github.com/hayamiz/twittering-mode.git

展開したディレクトリをロードパスに加えて,.emacsに以下を記述.

(require 'org-toodledo)
(setq org-toodledo-userid "<toodledo-userid>") ;; emailじゃなく,ToodledoのSettingsから見られる「Unique ID」を記述
(setq org-toodledo-password "<toodled-password>")

;; Useful key bindings for org-mode
(add-hook 'org-mode-hook
          (lambda ()
            (local-unset-key "\C-o")
            (local-set-key "\C-od" 'org-toodledo-mark-task-deleted)
            (local-set-key "\C-os" 'org-toodledo-sync)))
(add-hook 'org-agenda-mode-hook
          (lambda ()
            (local-unset-key "\C-o")
            (local-set-key "\C-od" 'org-toodledo-agenda-mark-task-deleted)))

これで同期の準備は完了.

もしもの時のためにToodledoをエクスポートしたら,新しいorg文書を作成し,M-x org-toodledo-initialize を実行.すると「TASKS」という項目が新規作成され,そこにToodledoで既に登録されているTodoがずらずら並ぶはずだ.

そのorgファイルに「** 新しいタスク」を追加し,C-c C-t を押して TODO マークをつけたら,ファイルをセーブする.すると,同期するか聞かれるので y を押すと,内容が Toodledo に反映され,同時に org 文書には Toodledo での詳細設定を記述する :PROPERTIES: も追加される.この :PROPERTIES: の書き方は逆に Toodledo に Todo を追加した後 org 文書で C-o s で同期したらある程度わかるだろう.

org-modeでのTodoリストの使い方は,

Emacs org-modeを使ってみる: (5) TODOリスト - 屯遁のパズルとプログラミングの日記

でも参考にするといい.Todo リスト以外の org-mode の使い方についてもよくまとまっている.とりあえず私はこれで快適な Todo 管理ができそうだ.

Ubuntu11.10でサスペンドやハイバネートに失敗する問題の解決方法

Ubuntu11.10でサスペンドやハイバネートをすると,画面は真っ暗になるものの電源が落ちずにフリーズ状態になる問題に悩まされた.その応急処置として以下の方法で解決した.

  1. dconf-editor をインストール (sudo apt-get install dconf-editor)
  2. dconf-editor の org > gnome > desktop > lockdown を選択
  3. disable-lock-screen にチェックを入れる

これでフリーズはしなくなった.ただし,サスペンド復帰時にパスワードは要求されない.完全な解決方法ではないが,サスペンドでフリーズするよりはマシだと思うので,同じ症状で悩まされている人は試してみてほしい.

EmacsのC-hをbackspaceとして使用する

Emacs の C-h は初期状態では help となっているが,これを backspace として使用したいと考える人は多いと思う.この方法には,以下の2通りある.

  1. global-set-key で C-h に delete-backward-char を割り当てる
  2. keyboard-translate を使用して C-h に backspace と同じ処理を割り当てる

前者は,ミニバッファで使えなかったり,他の elisp で delete-backward-char を乗っ取ったときに BS キーとの整合が取れなくなる場合があるので,できれば後者のほうがいい.

後者なら以下を .emacs に書けばよい.

(keyboard-translate ?\C-h ?\C-?)

ちなみに前者は以下を書く.

(global-set-key "\C-h" 'delete-backward-char)

アクティブウィンドウをもう1つのディスプレイに移動するRubyプログラム

デュアルディスプレイ環境だと,もう一方のディスプレイにアクティブウィンドウを移動したいことがある.しかし,そのようなショートカットはUbuntu11.10に存在しないため,自分でRubyスクリプトを書いた.

必要なものはxprop,xwininfo,wmctrl,それとRuby(1.9推奨だが1.8でも多分動く).Ruby環境があるUbuntuであれば,以下のコマンドで動かせるようになると思う.

$ sudo apt-get install wmctrl

Rubyスクリプトは以下.

switchdisplay.rb

これを適当なところに置いて,キーボードショートカットとして設定すれば,そのキーでアクティブなウィンドウがもう一方のウィンドウに移動する.

Javaで配列を標準出力する

Java を書いてるときに String 型の配列を標準出力したいなと思ってググったら、こんなページを上の方に見つけた。

String[] 型の配列を簡単に出力するには - Yahoo!知恵袋

一番簡単な方法は、

for (String str : strAry) {
    System.out.println(str);
}

と書いているが、おそらくこっちの方が楽。

System.out.println(Arrays.asList(strAry));

配列をリストに変換して出力している。これだと一行で書けるし、{"hoge", "huga", "piyo"} のようにわかりやすく出力してくれる。

注意しなければいけないのは、int型などのプリミティブ型配列だとこの方法は使えないということだ。この方法を使うならIntegerクラスなどラッパクラスの配列である必要がある。

追記

Twitterで言及をいただいた。「Arrays.toString()」で配列をわかりやすい文字列に変換できるらしい。

ということで、一番簡単な方法は

System.out.println(Arrays.toString(array));

となる。これなら array がプリミティブ型の配列でも出力できる。無知で申し訳ない。