読者です 読者をやめる 読者になる 読者になる

dilute.rb@東京Rubykaigi 11 について

ruby

tenderloveさんが発表されていた摩訶不思議な dilute.rb の原理について
発表では種明しなしでモヤモヤが止まらないので調べてみました。

動画はこの辺りから。


【招待講演】Image Recognition and Code that shouldn't exist Aaron Patterson氏

デモ概要

まずは何の変哲もない fibonacci スクリプトがあります。
f:id:rochefort:20160605004924p:plain

次にこれに dilute.rb をかませると、スクリプトの一部が空白に置き換わりますが
最後に pipe で ruby に渡すと fib.rb を実行した時と同様の結果になります。
f:id:rochefort:20160605004935p:plain

え?どういうこと?

当日会場で見てたのですが、さっぱりわかりませんでした。

あまりにわからないので

ググってみると以下の記事を見つけました。
Demystifying @tenderlove's homeopathic code optimizations
どうやら、新ネタではなく 2014年の別カンファレンスでの発表のようです。
そしてヒントとなるコードが示されていました。

#! /usr/bin/env ruby
probability = $stdout.tty? ? (ENV["D"] || 10).to_i : 0
puts ARGF.read.chars.map { |c| rand(100) + 1 <= probability && c !~ /\s/ ? " " : c }.join

$stdout.tty?

標準出力が terminal かどうかを判定することができます。
これは初めて知りました。なるほど、これを利用していたのですね。

catを使って確認すると、以下のようになるわけです。

$ cat a.rb
puts  "stdin : #{$stdin.tty?}"
puts  "stdout: #{$stdout.tty?}"
$ ruby ./std_example.rb
stdin : true
stdout: true

$ cat ./std_example.rb | ruby
stdin : false
stdout: true

$ ruby std_example.rb | cat
stdin : true
stdout: false

少し足りない

上記コードは、一見 dilute されていますが、dilute を複数回実行した時も
1回実行した時と同様の動作となっています。
つまり、dilute率が20%なら 5回実行すると全て空白で置き換えられなければなりません。
何回目の実行なのか(または、前回のdilute率)というような値をどこかに保持しないいけません。
どこに持たせるのか悩んだのですが、結局、標準出力に含めちゃえばいいのかと気づいて
以下のようなコードになりました。

できた

#!/usr/bin/env ruby
data, called_count = ARGF.read.split("\t")
called_count = called_count.to_i + 1

if $stdout.tty?
  probability = (ENV['D'].to_i | 20) * called_count
  puts data.chars.map { |c| rand(100) + 1 <= probability && c !~ /\s/ ? ' ' : c }.join
else
  puts "#{data}\t#{called_count}"
end
$ ./dilute.rb fib.rb
 ef  ib
   f n   3
    1
  else
    f b( -1  + fib(n-2
  end
e d

p fib(3
$ ./dilute.rb fib.rb | ./dilute.rb
 e  fib n
   f n <

  else
      b( -1    f  (n-2)
  end
e

  fib 33
$ ./dilute.rb fib.rb | ./dilute.rb | ./dilute.rb | ./dilute.rb | ./dilute.rb








$ ./dilute.rb fib.rb | ./dilute.rb | ./dilute.rb | ./dilute.rb | ./dilute.rb | ruby
3524578

See Also

東京RubyKaigi 11 に参加してきました - rochefort's blog