トップ 最新 追記   RSS 1.0 FEED  

Journal InTime


2011-10-08 (Sat) [長年日記]

_ [ソフトウェア] Oracle Presenter Console

RubyConf Argentinaに向けてスライドの準備をはじめているが、今回はRubyConfの反省を活かすため、ノートに原稿を書いておくことにした。 ということで、Rabbitを捨ててOpenOfficeで書いている。

問題は本番中にどうやって見るかだが、紙で出力するのは、「どこ読んでるかわからなくなるからだめだ」とのまつもとさんのアドバイスにより却下。 といって昔のまつもとさんみたいに2台PC用意するというのは現実的でない。

調べてみると、OpenOfficeにはOracle Presenter Consoleという拡張があるようなので、それを使ってみることに。

インストールしたのはいいが、使い方がよくわからず、はまってしまったので、以下にまとめておく。

  1. 外部ディスプレイを接続する。
  2. ミラーリングではなく、別の画面を表示できるよう設定する。
  3. スライドを表示したい方のディスプレイでOpenOfficeを立ち上げる。 または、すでに起動しているOpenOfficeをスライドを表示したい方のディスプレイに移動する。
  4. [スライドショー]→[スライドショーの設定]で、[プレゼンテーションディスプレイ]をスライドを表示したい方のディスプレイに設定する。
  5. F5でスライドショーを開始する。
  6. もう一つのディスプレイにコンソールが表示される。

はまったのは3をずっと逆のディスプレイでやろうとしていたため。

これでようやくちゃんと使えるようになったが、ノートに文字がいっぱい書いてあるとスライドの切替えが妙に遅いようだ。うーん。

本日のツッコミ(全2件) [ツッコミを入れる]

_ まつもと [中村くんがrabbitでノートを表示するパッチを書きましたよ]

_ shugo [昨日見せてもらいました。もうちょっと早く作ってくれていれば。 でもコンソールでスライドの進捗と時間表示ができればウ..]


2011-10-22 (Sat) [長年日記]

_ [Ruby][Haskell][Scala] flat_map、非決定性計算、リスト内包表記

Ruby 1.9では、Enumerable#flat_mapというメソッドが追加されている。

%w(ruby perl python).flat_map {|i| i.chars.to_a}
#=> ["r", "u", "b", "y", "p", "e", "r", "l", "p", "y", "t", "h", "o", "n"]

といった動作をするもので、要はブロックが返す配列を連結した配列を返す。

%w(ruby perl python).map {|i| i.chars.to_a}.flatten(1)

とすれば同じことができるのにどうしてわざわざ追加したのか疑問に思われるかもしれないが、 実はflat_mapが追加されたということは、Rubyの配列がHaskellのリストモナドのように使えることを意味している。

以前の日記で非決定性計算について書いたが、 Ruby 1.9では継続が拡張ライブラリに格下げになっているので、代りにflat_mapで書いてみよう。

p (1..5).flat_map {|baker|
    (1..5).flat_map {|cooper|
      (1..5).flat_map {|fletcher|
        (1..5).flat_map {|miller|
          (1..5).select {|smith|
            [baker, cooper, fletcher, miller, smith].uniq.length == 5 &&
              baker != 5 &&
              cooper != 1 &&
              fletcher != 1 && fletcher != 5 &&
              miller > cooper &&
              (smith - fletcher).abs != 1 &&
              (fletcher - cooper).abs != 1
          }.map {|smith|
            [baker, cooper, fletcher, miller, smith]
          }
        }
      }
    }
  }

確かに計算できる。でもこれってただの全探索なんじゃ…と疑問に思われる方もいるかもしれない。そのとおり。

以前の日記でHaskell版のポイントは遅延評価にあると書いた。上の例は有限集合を扱っているので全探索でもいいわけだが、無限集合を相手にするとそうはいかない。例えば以下のように、x**2 + y**2 == z**2を満たすような整数x,y,zを求めるプログラムを書いたとする。

def pythagorean_triples
  (1..Float::INFINITY).flat_map {|z|
    (1..z).flat_map {|x|
      (x..z).select {|y|
        x**2 + y**2 == z**2
      }.map {|y|
        [x, y, z]
      }
    }
  }
end
p pythagorean_triples.take(10)

このプログラムを実行すると、返って来ない。flat_mapが無限に長い配列を生成しようとしてしまうからだ。 そこで、yhara先生が提案しているEnumerable#lazyを使ってみる。

require_relative "lazy"

def pythagorean_triples
  (1..Float::INFINITY).lazy.flat_map {|z|
    (1..z).lazy.flat_map {|x|
      (x..z).lazy.select {|y|
        x**2 + y**2 == z**2
      }.map {|y|
        [x, y, z]
      }
    }
  }
end
p pythagorean_triples.take(10)

今度はちゃんと以下のような解が得られる。

[[3, 4, 5], [6, 8, 10], [5, 12, 13], [9, 12, 15], [8, 15, 17], [12, 16, 20], [7, 24, 25], [15, 20, 25], [10, 24, 26], [20, 21, 29]]

ちなみにHaskellではリスト内包表記を使ってもっとかっこよく書けるが、意味的には上記のコードと同じだ。

pythags = [ (x, y, z) | z <- [1..], x <- [1..z], y <- [x..z], x^2 + y^2 == z^2 ]

Haskellに比べるとちょっと冗長だが、Scalaでは以下のように書ける。

import scala.math.pow

def from(i: Int): Stream[Int] =
  Stream.cons(i, from(i + 1))

def pythagoreanTriples(): Stream[(Int, Int, Int)] =
  for (z <- from(1);
       x <- 1 to z;
       y <- x to z;
       if pow(x, 2) + pow(y, 2) == pow(z, 2))
    yield (x, y, z)

for (i <- pythagoreanTriples().take(10))
  println(i)

実は、Scalaのfor式はリスト内包表記と同等で、

for (z <- from(1);
     x <- 1 to z;
     y <- x to z;
     if pow(x, 2) + pow(y, 2) == pow(z, 2))
  yield (x, y, z)

from(1).flatMap(z => 
  (1 to z).flatMap(x =>
    (x to z).withFilter(y =>
      pow(x, 2) + pow(y, 2) == pow(z, 2)).map(y =>
        (x, y, z))))

のような形に変換される。 Ruby版とHaskell版、Scala版は、見た目が違うだけで意味的にはほとんど同じ。

Rubyのfor式も

for (z in 1..Float::INFINITY;
     x in 1..z;
     y in x..z;
     if x**2 + y**2 == z**2)
  [x, y, z]
end

と書けるように拡張したら面白いかもしれない。

ただ、Scalaではyieldの有無でflatMap/mapかforeachかを切り替えているようだが、Rubyの場合yieldは他の意味に使っているので、もうちょっと工夫が必要そう。

x = for (...) do ... end

のように値が要求される場合はflat_map/mapを使って、

for (...) do ... end

のように使ってないように見える場合はeachを使う、というのを考えたが、

def foo
  for (...) do ... end
end

だと値がほしいのかどうか曖昧なので

def foo
  return for (...) do ... end
end

のようにreturnを書かないといけなくなるのがちょっと気持ち悪い。

ここで静的型があればfooの戻り値の型から判断できるのに…とか思ってしまったら(Ruby的には)負け。

本日のツッコミ(全1件) [ツッコミを入れる]

_ shugo [この構文だと、今の構文のfor (x,y),z in [[1,2],3], ...]みたいなのと衝突しそうだな。]


2011-10-25 (Tue) [長年日記]

_ [Ruby] 助成金公募

Rubyビジネスフォーラムでも紹介したが、Rubyアソシエーションで 助成金公募を始めた。 個人的にずっとやりたかったことなので、やっと実現できてうれしい。

助成委員会には入らない予定なので、自分も応募してみようかな、と思ったが万一採択されちゃったりすると微妙なことになりそうなので自重することにしよう。

だれかScalaのfor式みたいなのをRubyに導入しませんかね。

本日のツッコミ(全2件) [ツッコミを入れる]

_ まつもと [for i in v1 for j in v2 for k in v3 if cond1 if cond2 yiel..]

_ shugo [ご応募お待ちしております:) 複数行書いた時にネストが深くならないようにしたいんですが、↑だとruby-modeと..]