トップ 追記

Route 477

過去の日記


2014-09-21

[ruby][event] 近況報告@RubyHiroba2014 (パターンマッチ、Hash#map、Opal/Yeahの話)

本日行われたRubyHiroba 2014 (RubyKaigiの後夜祭的イベント) にて、「近況報告」というタイトルでRubyの話をしてきました。

以下はテキスト版です(内容は少し差異があります)。

近況について

いちおうBoomがメインの感心事のつもりなのですが、あまり進んでいません。
理由の一つはケータイを持って社寺仏閣を回るゲームにうつつを抜かしていたことですが、最近はプレイしてないのでもう大丈夫だと思います。

Rubyとパターンマッチについて

Boomの実装にはpattern-matchというgemを使っています。その名の通り、Rubyでパターンマッチをやるためのライブラリです。
Rubyにはパターンマッチがない、と多くの人が思っていると思いますが、gemを使うとそれなりにやりたいことができたりするんですよね。

Rubyでパターンマッチするためのgemはいくつか存在します。

  • pattern-match gem
    辻本さん(speaker)のやつ。

  • patm gem
    todeskingさんのやつ。pattern-match gemより機能が少ない代わりに速いらしい

  • egison gem
    エギさん(speaker)の作っているエギソンという言語のパターンマッチ機能をRubyに移植したもの。
    「..., x, x+2, ...」みたいなパターン(non-linearというらしい)が書けたりしてすごい。
    詳細はRubyKaigi1日目の録画を参照。

さてところで、パターンマッチって何なんでしょうか。HaskellやOCamlなどのいわゆる関数型言語によくある機能ですが、知らない人のために一言で言ってみると、

オブジェクトに対する正規表現

みたいなものかなと。

正規表現を使うと、文字列に対して

str = "add 1 2"
if str =~ /add (\d+) (\d+)/
  puts "Add #{$1} and #{$2}"
end

利点は、if式とかで頑張るよりもずっと簡潔に書けることで、欠点は、Rubyの文法とは別に正規表現の文法を覚えないといけないことです。

パターンマッチも同様で、例えば「こういうパターンの配列」みたいな条件を簡潔に書けるようになります。その代わりに追加でパターンの書き方を覚えないといけないのも同様です。

ary = [:add, 1, 2]
match(ary){
  with(_[:add, x, y]){
    puts "Add #{x} and #{y}"
  }
}

pattern-match gemでは配列だけでなく、ユーザ定義クラスについてもマッチさせることができます(Scala由来なのかな?)。

class Book  # has :title, :price
  def initialize(title, price)
    @title, @price = title, price
  end
  attr_reader :title, :price

  def self.deconstruct(val)
    accept_self_instance_only(val)
    return [val.title, val.price]
  end
end

book = Book.new("Programming Ruby", 3400)
match(book){
  with(Book.(/Ruby/, 0..5000)) {  # タイトルに「Ruby」が入っていて
    p book                        # 値段が5000円以下の場合にマッチ
  }
}

パターンマッチはRubyKaigi初日のRuby Commiters vs the Worldでも話が出たのですが、「本当に入れるならいろいろと詰めないといけないところがあるので、叩き台の提案をどうぞ」みたいな結論になっていましたね。

とりあえずgem i pattern-matchで簡単に入るので、ぜひ試してほしいと思います。

Enumerable#hash_by(仮)について

僕はRubyコミッタではないのですが、いくつかCRubyへの機能提案を通したことがあります。

  • __dir__ (2.0.0)
  • String#lines returns Array (2.0.0)
  • Enumerable#lazy (2.0.0)

で、あと一つ入れたいメソッドがあって、それは「Hashを返すHash#map」です。
RubyのHashって、mapするとArrayになっちゃうんですよね。

h = {a: 1, b: 2}
h.map{|k, v|
  [k.to_s, v]
}
#=> [["a", 1], ["b", 2]]
# Wanted {"a" => 1, "b" => 2}

こういうものが欲しいとき、従来のRubyではHash#[]とかを使って「キーと値の組の配列」をハッシュに変換する必要がありました。

h = {a: 1, b: 2}
Hash[h.map{|k, v|
  [k.to_s, v]
}]
#=> {"a" => 1, "b" => 2}

Ruby 2.1からはEnumerable#to_hというメソッドが入ったので、「キーと値の組の配列」をto_hすることでハッシュが作れて、だいぶ良くなりました。

h = {a: 1, b: 2}
h.map{|k, v|
  [k.to_s, v]
}.to_h
#=> {"a" => 1, "b" => 2}

が、これって「ハッシュから配列の配列を作って、それをハッシュにする」っていうコードなんですよね。やりたいのは「ハッシュを加工した別のハッシュを作る」ことなので、これでは意図が十分に表現されていない、と考えます。

議論

この問題に対しては、前に一度チケットを立てたことがあります。

このときはHash#applyかHash#hash_mapみたいな名前はどうか?と提案していますね。それに対してまつもとさんが、Hash#collectの挙動を変えたらいいじゃん、と言っています。どうでしょうか?

  • Array#mapとArray#collectはalias
  • Hash#mapとHash#collectもalias(今のところ)
  • しかしそういえばHash#reject, Hash#selectはハッシュを返す (特にHash#selectは1.9からそうなった)
  • そういわれるとHash#collectがハッシュを返すのは綺麗な気がする
  • 互換性の問題がなければ:-P

という感じで、そのあともいろいろな名前が出るのですがacceptには至らないのでした。

それで、最近また提案したのがこのチケットです。
to_hはいまブロックを取らないのですが、これに直接ブロックを渡せるようにするのはどう?という案です。

User = Struct.new(:id, :name)
users = [User.new(1, "Alice"), User.new(2, "Bob")]

users.hash_by{|u| [u.id, u.name]}
# {1 => "Alice", 2 => "Bob"}
h = {a: 1, b: 2}
h.to_h{|k, v|
  [k.to_s, v]
}
#=> {"a" => 1, "b" => 2}

この案は残念ながら「to_hという名前でこの挙動は変じゃない?」という話で終わりそうです。
が、「こういうメソッド自体は欲しい」という意見もあったので、良い名前があれば採用されるかもしれません。いまのところ自分の中で一番マシなのはEnumerable#hash_byですが、そこまで良い名前でもないような気もして…。

Opal(Ruby to Javascriptコンパイラ)について

最後はOpalの話です。

去年のRubyHirobaでもちょっと紹介しましたけど、RubyからJavaScriptへのコンパイラですね。厳密には、文字列がimmutableだったりとか100%のRubyではないんですけど、普通にRubyを書く感じで使える程度にはなっています。

Why Opal Matters #1

Opalになんで注目してるかというと、近年ではJavaScriptを使っていろいろなことが出来るようになっています。

JavaScript x HTML5
JavaScript x WebSocket
JavaScript x WebRTC
JavaScript x Mobile
 ...

Opalがあれば、このへんがぜんぶRubyを使って遊べるわけです。

Ruby(Opal) x HTML5
Ruby(Opal) x WebSocket
Ruby(Opal) x WebRTC
Ruby(Opal) x Mobile
 ...

例として、Yeahというすごい名前のゲームフレームワークがあります。gem install yeah で入ります。
会場ではこれのライブコーディングをしました。以下は別に作った(というか移植した)ゲームです。こっちはyeahのgit masterを使っています(bundle exec yeah serveしてください)。

Why Opal Matters #2

もう一つ、Opalの応用として面白いものが出ています。
VoltというWebアプリケーションフレームワークで、「サーバ側もクライアント側もRubyで書く」のですが、なんかクライアントでデータを変更するとシームレスにサーバ側に通知されるとか書いてあってヤバそうな感じです。

Webアプリのサーバ側・クライアント側を同じ言語で書く、というアイデアは昔からあるものですが(Linksとか)、Node.jsの登場で、いろいろ研究が進んでるみたいなので(RendrとかMeteorとか)、Opalでそういった技術がRubyに持ってこれると面白いのかなと思っています。


2014-09-16

[linux] Revelationでデフォルトのパスワードファイルを指定する

Ubuntu上ではパスワードの管理にRevelationを使っている。
デフォルトだと起動するたびに手動でパスワードファイルを読み込む必要があって面倒なのだけど、実は設定でデフォルトのパスワードファイルを指定できるのでメモしておく。

Edit→Preferencesを開いて、"Open file on startup"に適当なファイルを指定すれば良い(僕は~/.revelationにしている)。

[ruby][event] RubyHiroba (RubyKaigi後夜祭) で喋ります

今年のRubyKaigiでは、メインセッションで30分話すようなまとまったネタも、LTの5分で話すようなインパクトあるネタもないので喋る予定はなかったのだけど、なんかRubyHirobaのトークを募集していますというメールが来て、見てみると「生活発表会」というタイトルで、その括りなら話ができそうな気がして申し込んでみた。

内容は以下です。

最近のRuby関係の雑多なトピックについて紹介・解説します。以下のコンテンツを含む可能性が高いです。「Rubyとパターンマッチについて」「Enumerable#hash_by(仮)について」「Opal(Ruby to Javascriptコンパイラ)について」

[http://rubyhiroba.org/2014/presentation.htmlより引用]

「生活発表会」っていう名前は大江戸Ruby会議由来だそうですが、考えてみるとなかなか間口が広くて良いタイトルですね。

そういうわけで今年もまた会期中にスライドを作る日々が始まりそうです


2014-09-12

[github] GitHubのmasterブランチを削除する

githubに静的HTMLなコンテンツを上げようと思ったのだけど、gh-pagesブランチだけ作るつもりが、うっかりしてgit push origin masterしてしまった。
仕方ないので「github ブランチ 削除」とかで検索するも、

$ git push --delete origin master
remote: error: refusing to delete the current branch: refs/heads/master
To git@github.com:yhara/apple_catcher_yeah.git
 ! [remote rejected] master (deletion of the current branch prohibited)
error: failed to push some refs to 'git@github.com:yhara/apple_catcher_yeah.git'
reposh: failed to exec 'git push --delete origin master': status 1

というエラーが出て削除ができない。

リモートのcurrent branchって何なんだろ、と思ったけど、githubリポジトリの設定の「default branch」という項がそれらしい

ということで、github上にgh-pagesブランチを作って、defualt branchをそっちにしたら、masterを削除することができた。

$ git checkout -b gh-pages
$ git push origin gh-pages
(ブラウザ上でdefault branchをgh-pagesに変更する)
$ git push --delete origin master

2014-08-27

[ubuntu] Ubuntu 14.04でAltを押すとHUDメニューが出てしまう現象を直す方法

最近のUbuntuではAltを押すと検索ウィンドウみたいなやつが出るようになっているが、極めて鬱陶しいので、オフにする方法を調べた。

方法1

システム設定→キーボード→ショートカット→Launchers→「HUDを表示するキー」をクリック→Backspaceを押す→割り当てキーが「無効」になったらOK

環境によってはこれで治る。

方法2

「CompizConfig設定マネージャ」を入れる。

$ sudo apt install compizconfig-settings-manager

Altを押して検索ボックスにcompizとか入力するとCompizConfig設定マネージャが出てくるので、起動する。
「これはadvanced toolなので気をつけて使ってくださいね」というダイアログが出るのでOKを押す。

Ubuntu Unity Plugin→Launcher→一番上のKey to show Dash, Launcher and Help Overlayのキー設定を「無効」にする

僕の環境(MacBook+VMWare Fusion+Ubuntu 14.04)ではこれでいけた。

Ubuntu Unity Plugin→General→Key to show the HUD when tapped という項目もあるけど、これは最初から「無効」になってたと思う。キーを割り当てると検索ボックス部分だけを表示させることができる。

ところで

aptでcompiz-pluginsを入れるとCompizConfig設定マネージャにStatic Application Switcherという項目が追加されて、これを愛用してたのだけど、14.04で使おうと思ったらControl-Tabに割り当てても反応しないようになってて悲しい。

キーボードでアプリケーションをスイッチする場合、「アプリ単位のスイッチ」と「ウィンドウ単位のスイッチ」(つまり、あるアプリのウィンドウが複数ある場合にそれらを区別して表示するもの)の2種類がある。MacのCmd-tabとか、UbuntuデフォルトのAlt-tabは前者なんだけど、個人的にはLinuxのときは後者を使いたいケースが多いんだよね。


2014-08-25

[misc] Markdownで作業メモを取る

なんか社内勉強会でメモ取りの話をするらしいので、自分のやり方を書いておく。
作業メモはいつもテキストで書いてて、昔はHikiDocで書いてたんだけど、最近はMarkdownにしてる。

  • 1行目にタイトルを書く
  • ファイル冒頭にストック的な内容をまとめる
  • ファイル後半を日記として使う

具体的にはこんな感じ。

# <プロジェクト名>  ←プロジェクトごとにメモを分けてるので、ここにタイトルを書く。

## まとめ1

メモは以下のように日付と時刻を入れて記録するが、これはフローとして流れてしまうので、
ストック的なものはファイルの冒頭部分にまとめておく。

## TODO

このへんに直近のTODOを書いたりする。

----  ←この下から日記ですよ、という境界線

<追記するときはここに追記する>

## 2014/08/25  ← 日付。IMEで「きょう」とか変換すると出る

### vim  13:25 ← エントリタイトルと時刻。

時刻はvimのキー操作で自動入力できるようにしている。日付も同時に入れるやつと入れないやつを
<space>imと<space>inに割り当てているが、しょっちゅう間違うのであまり良い割り当てではない。

" input current time
nmap <Space>im  O## <C-R>=strftime("%Y/%m/%d")<CR><CR><CR><ESC><Space>in
nnoremap <Space>in  O###  <C-R>=strftime("%H:%M")<CR><ESC>F<Space>

↑Markdown的にはソースコードは字下げなり```で囲むなりすべきだが、別にHTMLに変換したい
わけではないので、このへんは適当にしている。

### リストについて  12:00

Markdown的に正しい方法で箇条書きを書くと、エディタのハイライトが効くので見やすくなる。

* aaa
* bbb

Markdownでは+, -も箇条書きに使える。HTMLに変換する場合はどちらも同じだが、テキスト上では
+/-をpros/consとして使ったり、TODOとして使ったりすると便利。

+ メリット1
+ メリット2
- デメリット1

- TODO1
  - TODO2
  + DONE1
  + DONE2

仕事だけじゃなくて、趣味のプロジェクトも同様に作業メモを書いている。
ていうかプログラミングに限らず、ゲームのプレイメモとかもこの形式で書いてたりする(ingress.mdとか)。
フロー部分とストック部分を分けておくと、「今考えてることをとりあえず日記として出力する」ということができて便利。


過去の日記