史上最大のコーディングスキル判定(Rubyで書いた)

あげとこ。Ruby楽しいなぁ。楽しいけど今のところ便利なCとしてしか使えないのでこんなもんです。

でもこんなコードでも書いてみるとどの辺をもっと学べばよいかがわかってくる。

# -*- coding: Windows-31J -*-

#残りの手牌の配列restから、配列meldを取り去る
#取り去った残りの配列を返す。分けられなければnilを返す。
def slice_a_meld(rest, meld)
  result = rest[0..-1]
  meld.each do |n|
    if result.index(n) then
      result.slice!(result.index(n))
    else
      return nil
    end
  end
  return result
end

#残り手牌から3枚ずつメンツを取って、全部取れればそのメンツの組み合わせを出力
def slice_melds(rest, form)
  if rest.size == 0 then
    puts form
  else
    #残り手牌先頭がコーツの一部と仮定して探索
    new_meld = Array.new(3) { rest[0] }
    new_rest = slice_a_meld(rest, new_meld)
    if new_rest then
      slice_melds(new_rest, "(#{new_meld[0]}#{new_meld[1]}#{new_meld[2]})" + form)
    end
    
    #残り手牌先頭がシュンツの一部と仮定して探索
    new_meld = Array.new(3) { |i| (rest[0].to_i + i).to_s }
    new_rest = slice_a_meld(rest, new_meld)
    if new_rest then
      slice_melds(new_rest, "(#{new_meld[0]}#{new_meld[1]}#{new_meld[2]})" + form)
    end
  end
end


#プログラムここから
tehai = ARGV[0].split('').sort

#タンキ+メンツ4個パターン
tehai.uniq.each do |tanki|
  current_form = "[#{tanki}]"
  current_rest = slice_a_meld(tehai, [tanki])
  slice_melds(current_rest, current_form)
end

#雀頭+ターツ/トイツ+メンツ3個パターン
tehai.uniq.each do |head|
  #トイツをスライスしてみる
  current_form = "(#{head}#{head})"
  current_rest = slice_a_meld(tehai, [head, head])
  if current_rest then
    current_rest.uniq.each do |seed|
      #シャボテン
      semi_meld = [ seed, seed ]
      new_form = current_form + "[#{semi_meld[0]}#{semi_meld[1]}]"
      new_rest = slice_a_meld(current_rest, semi_meld)
      if new_rest then
        slice_melds(new_rest, new_form)
      end
      
      #ペンチャン/リャンメン
      if seed.to_i <= 8 then
        semi_meld = [ seed, (seed.to_i + 1).to_s ]
        new_form = current_form + "[#{semi_meld[0]}#{semi_meld[1]}]"
        new_rest = slice_a_meld(current_rest, semi_meld)
        if new_rest then
          slice_melds(new_rest, new_form)
        end
      end
      
      #カンチャン
      if seed.to_i <= 7 then
        semi_meld = [ seed, (seed.to_i + 2).to_s ]
        new_form = current_form + "[#{semi_meld[0]}#{semi_meld[1]}]"
        new_rest = slice_a_meld(current_rest, semi_meld)
        if new_rest then
          slice_melds(new_rest, new_form)
        end
      end
    end
  end
end

Ruby Twitter Gemを使った、プロキシ経由でのTwitter API呼び出しが失敗する件

Ruby 1.9.1(ActiveScriptRuby)+Ruby Twitter Gem 0.8.4をちょっと触る機会がありました。あれこれやって「これは面白い。愉快愉快」といじっていたところ、ダイレクトメッセージの削除がうまくできないという問題に遭遇しました。Ruby Twitter Gemから呼び出しているcrack 0.1.7のconvert_json_to_yamlが「これJSONちゃう」という例外を投げてきてました。ソースを見るとちゃんとJSONを返すURIを呼んでる。なのにJSONYAMLのメソッドがエラーを起こすということはそもそもTwitter側からちゃんとした返事が返ってきてないということと推察されます。

プロキシサーバー経由だったので問題の切り分けのため直結の回線で試したところ、それはちゃんと通ります。ということでプロキシが原因らしい。

で、さらにGoogle先生に聞いてみたところ、「POSTメソッド、かつ、リクエストボディが空」でのHTTPアクセスをなんちゃらしてしまうプロキシサーバーがあるらしい、という情報が見つかりました(「ボディが空ならGETでいいじゃん」みたいな動作かもしれません。わかりませんが)。

私の環境のプロキシがそれに該当するかどうか不明でしたが、Twitter API WikiRuby Twitter Gemのリファレンスやソースを見比べてほかのAPIを試してみると、なるほどボディが空でメソッドがPOSTなAPIに限ってうまく通らないようでした。プロダクト品質を求められているわけでもなくこれはRuby Twitter Gemが悪いわけじゃなさそうなのでまあ諦めようかと思ったんですが、せっかくなのでダミーのリクエストボディを入れてどうなるか試してみることにしました。

具体的には、ダイレクトメッセージの削除を行うTwitter APIをラップしているBase::direct_message_destroyメソッド(base.rb内)の定義にあるperform_post呼び出しの第2引数として

:body => { :dummy = "dummy" }

を追加しただけです(オリジナルの状態では引数は“/direct_messages/destroy/#{id}.json”の1個だけ。第2引数はperform_postメソッド定義で決められているデフォルト値{}になります)。結果、すんなりと呼び出し成功。やっぱりプロキシがなんか巨大なお世話をやってたみたい。

RESTのなんたるかみたいな話はよくわからないんですが、現象面で言えばURIをちゃんとIDとして使うとともにHTTPのメソッドの使い分けももっとちゃんとやろうよという話だと理解しています。「GETでいいじゃん」的な話はありえないわけで(実際にプロキシがそんなことやっていたかは不明ですが)。RESTがもっともっと広まればこのような動作をするプロキシというのはなくなっていくんでしょうか。

ちなみに同様の(=現状で一部のプロキシがうまく通してくれない)Twitter APIはほかにもあります(Ruby Twitter Gemでは、perform_postを(URI形式の)引数1個で呼び出しているメソッドでラップしているものに相当)。個々のAPIをラップするメソッドにいちいちダミーのボディを持たせるのではなくて、POSTを実行するperform_postそのものの定義の第2引数を

options={}

から

options={ :body => { :dummy = "dummy" }}

に書き換えれば、同種のAPI呼び出しの問題が一挙に解決するのではないかという気がしますが、よその個所にどう影響するかわからないので試していません。

経過があいまいなうえ、ワークアラウンドもいいところですが覚え書きとして。

xyzzyをWindows 7 x64にインストールするの巻・その3

タイトルにはWindows 7 x64と銘打ってますが、これは単に私がWindows 7 Professionalの64ビット版に入れて使ってるというだけで64ビット版固有の問題がなにかあったとかそれを解決したとかではありません。32ビット版やWindows Vista(特にセッション関係の設定とか)でxyzzyを使うときも同様に設定できるはずです。

カスタマイズ3:xyzzyセッション・ファイル(.ssn)をダブルクリックでセッションとして開く

  • セッションもまた、ないと使う気がしない私的超重要機能。xyzzyセッションは(xyzzyの)ウィンドウのタイリングと個々のウィンドウのバッファとのバインド状況などを保存したデータ。統合開発環境の「プロジェクト」みたいに複数のドキュメントを一括して読み込める。「ファイル」→「セッションの保存」でその時点の状況を記録した拡張子.ssnのセッション・ファイルを作成できる。
  • xyzzyが作成する.ssnファイルは単なるテキスト・ファイルなんだけどxyzzy.exeに"-s"オプションを付けて開くとセッション・ファイルと解釈してくれる(ファイルをfind-fileではなくload-sessionで開く=保存時に開いていたファイルをすべて読み込み、ウィンドウのタイル配置やバッファとのバインドも復元してくれる)。
  • それでかれこれ1年半くらい使ってきたけれど、Windows 7への移行に当たって考え直してみたらレジストリいじるほうが作業自体簡単で、下にも書いた履歴の問題がないメリットもあり、レジストリ変更で対処することにしました。以下は悪い子向け手順なので、よい子は下の「カスタマイズ3の2」まで読み飛ばしてください。
  • 悪い子向け手順1:.ssnを開くアプリケーションとしてxyzzyを指定(xyzzy.exeでもxyzzycli.exeでもお好みで)。.ssnファイルをダブルクリックしてダイアログで関連付けてもいいし、コントロールパネルの「プログラム」→「既定のプログラム」→「あるファイルの種類を特定のプログラムでオープン」を使ってもいい(この状態ではまだ.ssnはただのテキストとしてfind-fileで開く)。
  • 悪い子向け手順2:手順1の後、レジストリ・エディタでHKEY_CURRENT_USER\Software\Classesを開き、.ssn関係を見て、値が「〜xyzzy.exe" "%1"」(あるいはxyzzycli.exeかも)となっているエントリを「〜xyzzy.exe" "-s" "%1"」(同)に変更。以上でセッション・ファイルがセッションとして解釈されるようになる。超簡単。

カスタマイズ3の2:xyzzyセッション・ファイル(.ssn)をダブルクリックでセッションとして開く(よい子向け)

  • よい子はもちろん上述のXyzzy Wikiにあるスクリプトを追加すればOK。このスクリプトを使う方法には、C-x C-fで開いても*.ssnなら自動的にload-sessionしてくれるメリットもある(デフォルトではload-sessionはC-x 6 fの3ストローク必要)。
  • とはいえ、この方法には「ファイル」→「最近使ったファイル」の方にセッションの履歴が入り、「最近使ったセッション」には入らないという作用が伴う。それがいやなよい子向けに、レジストリをいじらないで済む手順も検討してみました。追記:以下の方法よりコメントにある.iniファイル使う方法のほうがよいかもです。
  • そこで2番目に簡単そうなWindows Script Hostで書いたバージョンが以下。これをxyzzy-s.vbsのようなファイル名で保存し、.ssnを開くアプリケーションとして関連付ければOK。コマンドプロンプトは開きません(cscript.exeがホストするので当たり前ですが)。これを私的なよい子向け手順の決定版とします。
Set objWshShell = WScript.CreateObject("WScript.Shell")
objWshShell.Run "C:\xyzzy\xyzzy.exe -s " & WScript.Arguments(0)
Set objWshShell = Nothing

WSH以外にも外部プロセスを起動できるプログラム処理系なら何でもできると思いますが、いずれにしてもエクスプローラ“ー”とxyzzy.exeの間に何らかのホストが一個はいるので釈然としない気持ちは残りますね。

カスタマイズ4(参考):S-SPC対策

  • 以前使用していたWindows Vista環境(英語版Windows Vista UltimateにMUIで日本語設定したもの+ATOK2008)で(環境のせいかどうかは不明だが)S-SPCが半角スペース入力にならない不具合が出てた。それに付け焼き刃的に対処。lispよく知らないので変なコードかもしれないが動いたのでよしとした。siteinit.lに記述。
;ATOK2008とVistaの組み合わせでS-SPCが半角スペース入力にならないのに付け焼き刃的対処
(defun bind-F20-to-SPC () (interactive) (insert " "))
(global-set-key #\F20 'bind-F20-to-SPC)
  • 購入したWindows 7は日本語版で、ATOKもまだ入れていないので現環境で必要になるかどうかは不明。必要性が生じたときに備えてここに記す。

積み残し作業

  • テキスト・キーワードの色づけ設定

xyzzyをWindows 7 x64にインストールするの巻・その2

手順や個々の設定内容はオーム社の書籍「入門xyzzy」(ISBN:9784274066009)を参考にしています。

カスタマイズ手順の概要

  • カスタマイズ設定を記述できるのは主に2カ所。(1)%XYZZYHOME%\.xyzzyファイルか、(2)xyzzy.exeと同じディレクトリにあるsite-list\siteinit.lファイル。どちらもインストール直後は存在しないので自分で作る。
  • .xyzzyへの記述はxyzzy.exeを起動するごとに毎回解釈実行される。siteinit.lへの記述はダンプファイル構築時に解釈実行される。よって.xyzzyの変更は次回起動時には自動的に反映される。siteinit.lに変更を加えたときにはCtrlとShiftを押しながらxyzzy.exeを起動することでダンプが再構築される(xyzzy.wxpとかのダンプファイルを手で消して再起動してもよい)。
  • そのような動作の性質から、ちょっとした変数設定のようなものは.xyzzyへ記述し、アドオンの組み込み的なカスタマイズはsiteinit.lへ記述するというのが大まかな役割分担(加えてsiteinit.lは全ユーザー共通になりますね。一人しか使わないから関係ないけど)のはずだけどいまどきのPCならどうでもいいような気がする。

カスタマイズ1:インクリメンタル・サーチの設定

  • 1行追加するだけ。これがないと使う気がしないほどの私的重要機能。
;インクリメンタル・サーチ
(require "isearch")

カスタマイズ2:バックアップ設定

  • あまり役立った経験はないのだけど保険的にやっておく。マイドキュメントにxyzzy.bakというディレクトリを掘って、そこにディレクトリ階層を保ったままでバックアップを作っていくように設定。
;ディレクトリ階層を保ったバックアップをマイドキュメント\xyzzy.bakに作成
(require "backup")
(setq *backup-directory* "C:/Users/xxxxxx/Documents/xyzzy.bak") ;バックアップ先
(setq *hierarchic-backup-directory* t) ;階層を保つ設定

ほんとは環境変数を参照して%XYZZYHOME%/xyzzy.bak的な環境非依存な記述ができるんだろうけど調べるのがめんどくさくてやってないです。どっちにしろxyzzy.bakは手掘りだし。

積み残し作業

  • セッション関係
  • テキスト・キーワードの色づけ表示設定

xyzzyをWindows 7 x64にインストールするの巻・その1

パソコンをセットアップしなおすたびに「どうやるんだっけ」と迷うことを3回くらい繰り返して不吉なにおいがしたので覚書

xyzzyは主に日本語のテキスト書きに使っています。たいしたカスタマイズはしていないです。

とりあえずxyzzyを使えるようにする

  • アーカイブxyzzy-0.2.2.235.lzh)をダウンロード、展開
  • 展開してできたフォルダの中のxyzzyフォルダをフォルダごとC:\に移動
  • ユーザー環境変数XYZZYHOMEを%USERPROFILE%\Documentsに設定(これで.xyzzyを読み取るディレクトリが決まる)
  • C:\xyzzy\xyzzy.exeを右クリック→送る→デスクトップ(ショートカットを作成)
  • デスクトップにできたショートカットのプロパティで「作業フォルダ」をXYZZYHOMEと同じフォルダに(これでxyzzyを起動直後にC-x C-fとかしたときのデフォルトのフォルダが決まる)。なお、マイドキュメントはコマンドプロンプトからだと"%USERPROFILE%My Documents"とかでもたどれたりするけどWindows VistaWindows 7ファイルシステム的には%USERPROFILE%\Documentsがネイティブなパス名みたい。こっちを指定しないとxyzzyはたどれないらしい。

ここまでで普通にxyzzyを起動して使う準備はできている(はず)。以下はカスタマイズ・ネタ。

積み残し作業

  • インクリメンタル・サーチ
  • バックアップ
  • セッション関係
  • テキスト・キーワードの色づけ設定