l'essentiel est invisible pour les yeux

Thursday, May 31, 2007

全く、お前の頭はイカれてるぜ!

  • MicrosoftによるaQuantiveの買収価格は$6B(7292.17307億円)
  • EbayによるSkypeの買収価格は$2.6B(3159.94166億円)。
  • GoogleによるYoutubeの買収価格は$1.65B(2005.34759億円)

Microsoftが買収したaQuantiveは時価総額が$2.8B 2006年度の収入高は$442M 純利益は約$54Mだが2倍のプレミアムがつき$6Bでの全額現金による買収となった。(現金による買収と株式交換による買収のメリット・デメリットや株主や市場への影響についてはバフェットからの手紙が勉強になる。)GoogleのDoubleclickの買収に慌てたYahoo!のRightMediaの買収に後を追った形ではあるが大きな買収額だ。


上記の例はアメリカンドリームだ。
ジャパニーズドリーム?なんだそれは?新興市場に上場することか?

日本がSkypeを作れない事が納得できない。
ここ最近の私の興味はこれに尽きる。Skypeとういのはあくまでメタファだが、グローバル戦略をしっかりと持ち自分達が素晴らしいと狂信するサービスを初期段階からグローバルで展開していくことがなぜできないのか。グローバルな展開を進めるとしても日本でヒットした後どうするかを考え、「(WEBアプリなら)Gettextで国際化してはい終わり。」と考えているところも多いはずだ。

動画関連にしてもP2Pにしても携帯関連にしても技術は日本にはある。
ただ、グローバルでサービスを展開するのが下手だ。iModeも失敗している。


海外の企業による日本のIT系企業の買収話はあまり聞かない。

UIEJで働くMicrosoftのIE TeamでProgram Mannagerを勤めた人とランチのときに話をした。
日本のマーケットは大きいしヒットすればそこそこの収益が出る。日本内でヒットするところをゴールに設定することで目的は達成される。グローバル戦略のプロを雇い初期からグローバル展開するサービスなどほとんどない。グローバル戦略はまるでおまけのような扱いだ。

しかし、先日の判決(PDF)を踏まえるなら、GoogleもYoutubeもSkypeさえ国内からは生まれないかもしれない。

日本人の国民性も大きく影響するようだ。
シリコンバレーのJTPAのような日本人ネットワークにみるように、インド人・中国人・韓国人と比べて日本人は世界でも日本人コミュニティなどに集結することが多いと言う。

だからこそ私は、何人だとか関係無しに実力一本・頭脳一つで戦う中島聡のような人間に惹かれるのかもしれない。

日本の企業は契約社会でもなくアメリカほど資本主義が徹底しているわけではない。組織の協調性や仲間意識などでバ ランスよく成り立っている。企業間でお互いの顔色を伺う。相手が動けば、合わせて動く。外部のテクノロジーを買収して自社に吸収したりチーム丸ごと引き抜くよりも、余計なコストをかけて”自社で同じ技術を作りたが る"ことが日本企業には良く見られると言う話を聞いた。


マーケティングとブランティング

多くのITベンチャーでは海外におけるマーケティング手法も確立していないしノウハウもない。
友達や近い人間だけで立ち上げる家族経営的な日本企業のベンチャーにおいてグローバル戦略のプロフェッショナルはいない。VCのアドバイスを受けることはあっても、自社に足りない部分を補足するため外部のプロフェッショナルを迎えいれるという文化は少ない。

もちろんMicrosoftなどの大企業においてもBtoC事業のマーケティングはまだ模索しているという。
MSに対して痛烈な意見を書くMicrosoft社員のアルファブロガーに自社の新プロダクトの記事を書いてもらうマーケティングもしているようだ。BtoCのマーケティングにおいてはAppleが郡を抜いている感がある。


ベンチャー
ITベンチャーに良く見るビジネスモデルは、SIerで最低限の収益を稼ぎつつ自社サービスをこつこつ開発するというものや既に確立しているサービスの隙間を狙いニッチな市場を狙い車輪の再開発を行うものだ。

プログラマの私はこんなものは全くつまらないと思う。
お小遣い稼ぎしながら片手間でpretty coolな物など作れない。本気でそのサービス・プロダクトの未来を信じ熱く語れるだけのビジョン(ここではお金を意味しない)を持ち魂をかけれるなら、SIerで小遣い稼ぎをして資金集めをするアプローチなど取らない。それは自信が無い事の裏返しでしかない。そのアイデアを信じ明確なプランを持ち何時間でも熱く話せるなら自分達にとって優位となるキャピタルを選ぶ立場に立つだろう。無理ならそのアイデアは捨てるまでだ。もしかすると、時代が早すぎたのかもしれない。

会社としては普通の物を作り出して収めてそこそこの収益をあげていく事が成功なのかもしれないが、一度しか無い人生においてそんなつまらない事に時間を費やすなんて馬鹿げている。絶対的に信じれる物を全力で作りダメだったなら、あきらめ次のものを生み出すまでだ。

世の中が便利になった副作用で大勢の人が数年先の未来を想像する力を無くし、現状に満足している。水平線の先が何があるのか見えない。

最も大事な事は未来を予測することではなく、技術動向を注意深くモニタリングすることだ。
流行に踊らされているだけでは、結果何も作ることができない。


面白いアイデア?
もし、自分のアイデアが「面白いアイデアだね」と周囲の人間が10人中8人以上評価するようなアイデアならそんなアイデアは腐っている。既に誰かがやっているし何ヶ月かかけて出来上がった成果物など最初のサービスを考えた当初の興奮は消え去り、たいして面白くもないものになる。

逆に頭がきれると自分が思う人間にそのアイデアを話して「お前は狂ってる。辞めとけ。」と言われた場合はチャンスかもしれない。Steve Jobsのような人間なら本当に素晴らしいアイデアならボロクソに罵るだろう。しかし彼の頭の中にはその狂ったアイデアがずっと残っているだろう。


「全く、お前は狂ってるぜ!」「お前の頭はイカれてる。」

あなたはそんな最高の賛辞を最近受けただろうか?
歴史は一人とか数人といった"常識に支配されないクレイジーな人間"によって切り開かれる。そして、マジョリティがそれに続き(市場・研究分野・技術)確立される。80年という短い生涯において歴史を変えるタイミングに出会えるのは1回もしくは2回程度だろう。チャンスを逃がしてはいけない。

Albert Einstein, (attributed) US (German-born) physicist (1879 - 1955)
Not everything that counts can be counted,and not everything that can be counted counts.
重要なものが捉えることが出来るすべてものの中にあるとは限らず、重要なものすべてを捉えられるわけでない。


この言葉はAppleのCM中でも現れるアインシュタインの有名な言葉だ。アインシュタインは「わたしには特別な才能はありません。 好奇心がとても強かっただけです」とも語っている。桁外れな好奇心こそフラット化する世界では重要な力だ。好奇心の芽を育てておけば歴史を変える事になるその瞬間を感じることができるだろう。大抵の人はそれに気づかない。

Think Different.
英文法的に間違っているとかJobsにとってはどうでもいいのだ。「だってこっちの方がCoolだろう?」彼のものさしは英文法ではない。言葉の持つカッコよさである。「常識にとらわれるな。常識を超えたところに解がある。目の前に見える解は本当の解ではない。」そんなメッセージをこの言葉に感じる。

Think Differenct (CM)
Here’s to the crazy ones.
The misfits.The rebels.The troublemakers.
The round pegs in the square holes.
The ones who see things differently.
They’re not fond of rules.
And they have no respect for the status quo.
You can praise them, disagree with them, quote them, disbelieve them, glorify or vilify them.
About the only thing you can’t do is ignore them.
Because they change things.
They push the human race forward.
While some see them as the crazy ones,
we see genius.
Because the people who are crazy enough to think
they can change the world, are the ones who do.

「常識を無視した好奇心」・「周囲からは狂ったように見える熱意・興味」
彼/彼女らはいつの時代も少数派だ。だから何だ?「いい人だった」とか「やさしい人だった」とかそんなものは100年後には残らない。10年後でさえ忘れ去られているだろう。

歴史に残るのは偉業である。
織田信長やチンギス・ハン・Steve Jobsがどんなに残忍だったり、冷酷かなんて対して興味をもたれない。モンゴルを統一したこと、Macを作ったこと。iPodを作ったこと。それらが歴史に残り語り継がれる。

このすばらしいCMを作ったAppleとジョン・フォン・ノイマンに感謝したい。

Monday, May 28, 2007

Comet勉強会第1回

Comet勉強会 第一回@ドリコムが開催された。
次回にもつながるいい勉強会だった。

でも、推測で話が進み、「こうだったらいいよねー」・「こうなってるに違いない」と話が進んでしまった。勉強不足でした。予習してませんでした。

Cometアプリってまだチャットだけ。
Cometキラーアプリそろそろ誰か出しておくか。

第二回も開いて欲しい!
以下次回ネタ。

  • AIO
  • MessageQueue
  • qbdm
  • SoftwareTransactionMemory
  • ・死んだ言語と言われてもC言語だよ。
  • epoll/kqueuで実装されていないイベントとかある


素晴らしいまとめおつです!
Comet勉強会#1発表資料 (ドリコム 瀧内さん)
Comet 勉強会に参加した (ドワンゴ 溝口さん)
rubyにFiberが... (スケールアウト yamazさん)
Comet 勉強会 (コミュニティエンジン 加藤さん)

ERTS読むぞ。

Friday, May 25, 2007

[Ruby] MemoizationのRubyによる高機能な実装Memoizeを作った。

Memoize

(src) memoize-0.1.0.tgz

gemだと敷居が高い事も十分に承知なのでページ下部にソースコードも晒しておきます。ご自由にご利用ください。

MemoizationはDonald Michieにより1968年に作られた言葉でその歴史は古い。最初の実装はCommon Lispのようだ。(参照: Wikipedia - Memoization) 既に十分に古い概念なのでRubyにもMemoizationの実装は存在するが随分と低機能で無駄なIOが発生するので高機能なMemoizationの実装をつくった。

Memoizationは簡単に言えば次のような機能である。

  • 関数呼び出しの結果を引数をキーにして保存する。
  • 元の関数をキャッシュが存在すればキャッシュを返しキャッシュが存在しない場合には値を導出してからキャッシュするように再定義する。


つまり、グローバル変数の値に実行結果が依存したり同じ引数を与えても実行の度に結果が変化するような副作用を伴う関数にはmemoizationを適用することはできない。また、通常のメソッドに余分なロジックを付加してメソッドを再定義するため極端にロジックの少ないメソッドでは逆に速度が遅くなる。

Memoizedするのに向いている関数(メソッド)は次のような性質を持つ。
  • 深い再帰を持つなど同じ引数で何度も同じ関数の呼び出し行われる。

  • 副作用を持たず、関数の実行コストが高いような関数。


Memoizationを実装するには、動的な関数定義を言語レベルでサポートしている必要がある。VHLLなRubyなら何の問題もない。今回実装したMemoizeは次のような機能をサポートしている。

  • memoizedデータを保存するストレージをプログラマが独自に実装できる。
  • トップレベル(main)だけでなく任意のスコープのメソッドをMemoizedできる。
  • 永続化させるためのmemoizeデータのファイルへの書き出しはプログラム終了時だけである。


TODO
  • スレッドセーフでない。


インストール

% wget http://rubyforge.org/frs/download.php/21051/memoize-0.1.0.gem
% sudo install Memoize-0.1.0.gem


Memoizedを使用したサンプルコード
Memoize.registerやMemoize.unmemoizeの第一引数に渡すselfはおまじないだと思ってください。トップレベルのmainへの参照を別のスコープから取得する方法がわからなかったのでこうなっています。知っている方がいたら教えていただけると助かります。

memoizeした関数を別の名前で参照したい際には、Memoize.registerの第三引数に:as => newmethodを指定する。


require 'memoize'
require 'benchmark'

def fib(n)
return 1 if n < 2
fib(n-1) + fib(n-2)
end

class Math2
def self.fib(n)
return 1 if n < 2
fib(n-1) + fib(n-2)
end
end

Benchmark.bm do |b|
b.report("fib(30): ") { fib(30) }
Memoize.register(self, 'fib')
b.report("fib(30) with memoize") { fib(30) }
b.report("fib(30) with memoize") { fib(30) }
Memoize.unmemoize(self, 'fib')
end

Benchmark.bm do |b|
Memoize.register(self, 'fib', :as => 'fastfib')
b.report("fastfib(30) with memoize") { fastfib(30) }
b.report("fastfib(30) with memoize") { fastfib(30) }
Memoize.unmemoize(self, 'fastfib')
end

Benchmark.bm do |b|
b.report("fib(30): ") { Math2.fib(30) }
Memoize.register(self, 'Math2.fib')
b.report("fib(30) with memoize") { Math2.fib(30) }
b.report("fib(30) with memoize") { Math2.fib(30) }
end


実行結果

% ruby test_memoize.rb
user system total real
fib(30): 7.060000 3.320000 10.380000 ( 10.376117)
fib(30) with memoize 0.000000 0.000000 0.000000 ( 0.000043)
fib(30) with memoize 0.000000 0.000000 0.000000 ( 0.000065)
user system total real
fastfib(30) with memoize 0.000000 0.000000 0.000000 ( 0.000024)
fastfib(30) with memoize 0.000000 0.000000 0.000000 ( 0.000023)
user system total real
fib(30): 6.380000 3.140000 9.520000 ( 9.520442)
fib(30) with memoize 0.000000 0.000000 0.000000 ( 0.000029)
fib(30) with memoize 0.000000 0.000000 0.000000 ( 0.000024)
%


meoizedデータを保存するストレージの仕様
初めに書いたようにユーザ定義のストレージも使用できる。Memoize.registerに何も指定しない場合は、メモリ領域でデータを管理し、データを永続化させるために、プログラムの終了時にローカルのファイルシステムを使用してファイルとして書き込む。このファイルは/tmp以下に関数名をBase64エンコードしたファイル名.cacheで書き出される。

独自のストレージの実装
独自ストレージの実装はMemoize::Storableクラスを継承して以下の用に必要なメソッドを定義すればよい。詳しくは、lib/memoize.rb中のコメントを参照してください。MemCacheをストレージとして利用するクラスは次のように実装できる。

定義したストレージを使用するには、Memoize.registerの第三引数に:storeオプションを指定する。


class MemCacheStore < Memoize::Storable
attr_accessor :cache, :keys
def initialize(name)
@keys = []
@cache = MemCache.new 'localhost:11211', :namespace => 'rakuto.blogspot.com'
end

def get(key)
@cache.get(key)
end

def set(key, value)
@keys << key unless @keys.include?(key)
@cache.set(key, value)
end

def delete(key)
@cache.delete(key)
end

def delete_all
@keys.each { |key| @cache.delete(key) }
end
end


Memoize.register(self, 'fib', :store => MemCacheStore)
fib(30)


ソースコード: memoize.rb
memoize-0.1.0.tgz

# Memoize is implementation Memoization for Ruby, this techinique to make functions faster.
#
# == Caveats:
# * Do not memoize a function whose behaviou depends on program state.
# * Do not memoize a function with side effects.
# * Do not memoize a function that returns a data structure that is modified by it's caller.
#
# Copyright 2007 rakuto under MIT licence.
# Rakuto Furutani <rakuto at gmail.com>
#
# == See:
# Memoization - http://en.wikipedia.org/wiki/Memoization
#
module Memoize
VERSION = '0.1.0'
@@stores = {}

# Memoize::Storable class is abstract class. You must implement class which memoization data is saved.
# This module offer two storage class.
#
# * Memoize::MemoryStore -
# * Memoize::PStore - Persisten cache support.
#
class Storable
# {{{
attr_accessor :store

def initialize(name)
raise "Memoize::Strable don't instantiage for abstract class."
end

def set(key, value)
end

def get(key)
end

def update(key)
end

def delete(key)
end

def delete_all
end
# }}}
end

# MemoryStore is class which store memoization data to memory.
class MemoryStore < Memoize::Storable
# {{{
attr_accessor :store

def initialize
@store = {}
end

def get(key)
@store[key]
end

def set(key, value)
@store[key] = value
end

def delete(key)
@store.delete(key)
end

def delete_all
@store = {}
end
# }}}
end

# PStore is class which store memoization data with local file system.
# This store class be able to use persistent cache, because this class write out
# cache data on file when process is exit.
class PStore < Memoize::MemoryStore
# {{{
attr_accessor :store
PREFIX_SAVE_FILE_DIR = "/tmp"
SUFFIX_SAVE_FILE_EXT = ".cache"

def initialize(name)
@name = name
filename = encoded_filename(@name)
begin
if File.exists?(filename)
File.open(filename, "rb") do |io|
@store = Marshal.load(io)
end
else
@store = {}
end
rescue
@store = {}
end
# Write out cache
at_exit {
File.open("#{filename}", "wb+") do |io|
io.flock(File::LOCK_EX)
Marshal.dump(@store, io)
io.flock(File::LOCK_UN)
end
}
end

# Delete all memoized data and persisten cache fille
def delete_all
filename = encoded_filename(@name)
File.exists?(filename) && File.unlink(filename)
@store = {}
end

private
# Get the encoded file name
def encoded_filename(name)
File.join(PREFIX_SAVE_FILE_DIR, [name].pack('m').chomp+SUFFIX_SAVE_FILE_EXT)
end
# }}}
end

module_function
# Make memoized function.
#
# Parameters:
# * klass - You must surely specify 'self'. This parameter used to overwrite Toplevell function and
# register as toplevel function. I need to think better the means ;)
# * name - an memorized function name.
# * options - an specify options. <tt>:as</tt> is memorized function name, default is the same second arguments value.
# <tt>:store</tt> is specify class which store memorized data.
#
# == Memoized function examples
# def fib(n)
# return 1 if n < 2
# fib(n-1) + fib(n-2)
# end
#
# end
# Memoize.register(self, 'fib')
# fib(30) # => 1346269 fast
# fib(30) # => 1346269 very fast
#
# # Unmemorize
# Memoize.unmemoize(self, 'fib')
# fib(30) # => very slow
#
# == Memoized one Object methods
# class Math2
# def self.fib(N)
# return 1 if n < 2
# fib(n-1) + fib(n-2)
# end
# end
#
# Memoize.memorize(self, 'Math2.fib')
# Math.fib(30) # => fast
# Math.fib(30) # => very fast
# Memoize.unmemoize(self, 'Math2.fib')
#
# == Custom storage
# You can use own memoized data storage, in doing so you must implementaion following method
#
# Method/Arity:
# * initialize/1 - this is constructor
# * get/1 - an get the cache data with key
# * set/2 - an set the cache data with key and value
# * delete/1 - an delete the cache data with key
# * delete_all/0 - an delete all cache
#
# Following class is MemCache storage sample.
#
# # Memoized data storage using MemCache
# require 'memcache'
#
# class MemCacheStore < Memoize::Storable
# attr_accessor :cache, :keys
# def initialize(name)
# @keys = []
# @cache = MemCache.new 'localhost:11211', :namespace => 'rakuto.blogspot.com'
# end
#
# def get(key)
# @cache.get(key)
# end
#
# def set(key, value)
# @keys << key unless @keys.include?(key)
# @cache.set(key, value)
# end
#
# def delete(key)
# @cache.delete(key)
# end
#
# def delete_all
# @keys.each { |key| @cache.delete(key) }
# end
# end
#
# Memoize.register(self, 'fib', :store => MemCacheStore)
# fib(30) # => fast
# fib(30) # => fast
#
def register(klass, name, options={})
ns = name.split(/::|\./)
method, klass = ns.pop, (ns.empty? ? klass : Object.const_get(ns.join("::")))
store = options[:store].nil? ? PStore.new(name) : options[:store].new(name)
as_method = options[:as] || method
Memoize._set_store(name, store)
(class<<klass;self;end).instance_eval do # for Ruby1.9
method_name = "#{as_method}_without_memoize"
memoized_method_name = "#{as_method}_with_memoize"
define_method(memoized_method_name) do |*args|
store = Memoize._get_store(name)
ret = store.get(args)
if ret.nil?
ret = send(method_name, *args)
store.set(args, ret)
end
ret
end
alias_method method_name, method
alias_method as_method, memoized_method_name
end
end

# Specify unmemoize method.
# If you delete persistent cache when set delete_all true.
#
# == Example
# Memoize.memoize(self, 'slow_func')
# slow_func(arg)
# Memoize.unmemoize(self, 'slow_func')
#
def unmemoize(klass, name, delete_all=false)
(class<<klass;self;end).class_eval do
undef_method("#{name}_with_memoize")
alias_method name, "#{name}_without_memoize"
end
delete_all && @store.delete_all
end

# This method is public, but you don't need call directly.
def _set_store(name, store)
@@stores[name] = store
end

# This method is public, but you don't need call directly.
def _get_store(name)
@@stores[name]
end

# Not implemented
module Expire
end
end

if $0 == __FILE__
def fib(n)
return 1 if n < 2
fib(n-1) + fib(n-2)
end

class Math2
def self.fib(n)
return 1 if n < 2
fib(n-1) + fib(n-2)
end
end

class MemCacheStore < Memoize::Storable
attr_accessor :cache, :keys
def initialize(name)
@keys = []
@cache = MemCache.new 'localhost:11211', :namespace => 'rakuto.blogspot.com'
end

def get(key)
@cache.get(key)
end

def set(key, value)
@keys << key unless @keys.include?(key)
@cache.set(key, value)
end

def delete(key)
@cache.delete(key)
end

def delete_all
@keys.each { |key| @cache.delete(key) }
end
end


Memoize.register(self, 'Math2.fib', :store => MemCacheStore)
Benchmark.bm do |b|
b.report("fib(30) with memoize: ") { Math2.fib(30) }
#Memoize.unmemoize(self, 'fib')
b.report("fib(30)") { Math2.fib(30) }
end
end


考察
  • Memoizaionのテクニックは関数型言語と非常に相性がよさそう。
  • Railsのルーティング部分は副作用を含まないメソッド単位に分解して、Memoizedすることで高速化できないだろうか?

Wednesday, May 23, 2007

[Ruby] EXIFのGPS情報も取得できるExtExif 1.0をリリースした

extexif

RubyにはEXIF(Exchangeable image file format)を扱うための実装がいくつかあるが、全て開発がとまっている。Pure Rubyで実装されたruby-exifは、Ruby1.8だとパッチを当てないとコンパイルが通らない。二つ目はCで書かれたlibexifをRubyから呼び出せるように拡張ライブラリで実装されたruby-libexifであるが、2007年5月23日現在サーバが落ちている。幸いローカルにキャッシュがあったためコンパイルを試みたが、Ruby1.8.5ではコンパイルが通らない。Debainパッケージではコンパイルが通るように修正が施されているが、このライブラリはEXIFのGPSデータの取得をサポートしていない。

exif.c


#if 0
/* GPS tags are not supported in this library */
#endif


RubyのEXIF関連ライブラリはどれも開発がとまっている状況みたいなので、EXIFのGPS情報の取得もサポートするExtExifを作った。(RubyForge申請中なのでもう少し時間がかかりそう。)

インストール

% sudo gem install extexif


ドキュメント
ruby-libexifに実装されているExifクラスはそのまま利用可能であるが、もう一段抽象的な操作を提供するクラスとしてExtExifを実装している。現在のところは機能は少ないがこちらにEXIF関連で便利なメソッドとかを実装していく。

画像のパスを与えてクラスを生成して配列のようにEXIF Tag IDを使用してアクセスするだけです。

require 'rubygems'
require 'extexif'

# print EXIF data
IMG_FILE = 'image_with_exif.jpg'
image = ExtExif.new(IMG_FILE)
puts IMG_FILE
print "%-15s: %s\n" % ["Make", image["Make"]]
print "%-15s: %s\n" % ["GPSLatitude", image["GPSLatitude"]]
print "%-15s: %s\n" % ["GPSLongitude", image["GPSLongitude"]]



% ruby test_extexif.rb
image_with_exif.jpg
Make : KDDI-CA
GPSLatitude : 35.00, 39.00, 45.38
GPSLongitude : 139.00, 41.00, 16.89
%


EXIF Tag IDについては以下のサイトを参考に。
ExifTAG

exif.cに対するパッチのみいる人は以下。

< #include <ruby.h>
< #include <libexif/exif-tag.h>
---
> #include "ruby.h"
> #include <libexif/exif-ifd.h>
15a14
> #include <libexif/exif-tag.h>
137d135
< char value[1024];
142c140
< v = rb_str_new2(exif_entry_get_value(entry, value, sizeof(value)));
---
> v = rb_str_new2(exif_entry_get_value(entry));
219d216
< char value[1024];
246c243
< return rb_str_new2(exif_entry_get_value(e, value, sizeof(value)));
---
> return rb_str_new2(exif_entry_get_value(e));
253c250
< found = exif_entry_get_value(e, value, sizeof(value));
---
> found = exif_entry_get_value(e);
356c353
< rb_funcall(dest, rb_intern("<<"), 1, rb_str_new((char *)exif->ed->data, exif->ed->size));
---
> rb_funcall(dest, rb_intern("<<"), 1, rb_str_new(exif->ed->data, exif->ed->size));
379d375
< #define GPS_SUPPORT
457,488c453,454
< #ifdef GPS_SUPPORT
< {EXIF_TAG_GPS_VERSION_ID, "GPSVersionID", N_("GPS tag version")},
< {EXIF_TAG_GPS_LATITUDE_REF, "GPSLatutideRef", N_("North or South latitude")},
< {EXIF_TAG_GPS_LATITUDE, "GPSLatitude", N_("Latitude")},
< {EXIF_TAG_GPS_LONGITUDE_REF, "GPSLongitudeRef", N_("East or West longitude")},
< {EXIF_TAG_GPS_LONGITUDE, "GPSLongitude", N_("Longitude")},
< {EXIF_TAG_GPS_ALTITUDE_REF, "GPSAltitudeRef", N_("Altitude reference")},
< {EXIF_TAG_GPS_ALTITUDE , "GPSAltitude", N_("Altitude")},
< {EXIF_TAG_GPS_TIME_STAMP, "GPSTimeStamp", N_("GPS time (atomic clock)")},
< {EXIF_TAG_GPS_SATELLITES, "GPSSatellites", N_("GPS satellites used for measurement")},
< {EXIF_TAG_GPS_STATUS, "GPSStatus", N_(" GPS receiver status")},
< {EXIF_TAG_GPS_MEASURE_MODE, "GPSMessureMode", N_("GPS measurement mode")},
< {EXIF_TAG_GPS_DOP, "GPSDOP", N_("Measurement precision")},
< {EXIF_TAG_GPS_SPEED_REF, "GPSSpeedRef", N_("Speed unit")},
< {EXIF_TAG_GPS_SPEED, "GPSSpeed", N_("Speed of GPS receiver")},
< {EXIF_TAG_GPS_TRACK_REF, "GPSTrackRef", N_("Reference for direction of movement")},
< {EXIF_TAG_GPS_TRACK, "GPSTrack", N_("Direction of movement")},
< {EXIF_TAG_GPS_IMG_DIRECTION_REF, "GPSImgDirectionRef", N_("Reference for direction of image")},
< {EXIF_TAG_GPS_IMG_DIRECTION, "GPSImgDirection", N_("Direction of image")},
< {EXIF_TAG_GPS_MAP_DATUM, "GPSMapDatum", N_("Geodetic survey data used")},
< {EXIF_TAG_GPS_DEST_LATITUDE_REF, "GPSDestLatitudeRef", N_("Reference for latitude of destination")},
< {EXIF_TAG_GPS_DEST_LATITUDE, "GPSDestLatitude", N_("Latitude of destination")},
< {EXIF_TAG_GPS_DEST_LONGITUDE_REF, "GPSDestLongitudeRef", N_("Reference for longitude of destination")},
< {EXIF_TAG_GPS_DEST_LONGITUDE, "GPSDestLongitude", N_("Longitude of destination")},
< {EXIF_TAG_GPS_DEST_BEARING_REF, "GPSDestBearingRef", N_("Reference for bearing of destination")},
< {EXIF_TAG_GPS_DEST_BEARING, "GPSDestBearing", N_("Bearing of destination")},
< {EXIF_TAG_GPS_DEST_DISTANCE_REF, "GPSDestDistanceRef", N_("Reference for distance to destination")},
< {EXIF_TAG_GPS_DEST_DISTANCE, "GPSDestDistance", N_("Distance to destination")},
< {EXIF_TAG_GPS_PROCESSING_METHOD, "GPSProcessingMethod", N_("Name of GPS processing method")},
< {EXIF_TAG_GPS_AREA_INFORMATION, "GPSAreaInformation", N_("Name of GPS area")},
< {EXIF_TAG_GPS_DATE_STAMP, "GPSDateStamp", N_("GPS date")},
< {EXIF_TAG_GPS_DIFFERENTIAL, "GPSDifferential", N_("GPS differential correction")},
---
> #if 0
> /* GPS tags are not supported in this library */
490d455
<
567c532
< {-1, NULL, NULL}
---
> {0, NULL, NULL}
592c557
< for (i = 0; ExifTagTable[i].tag != -1; i++) {
---
> for (i = 0; ExifTagTable[i].tag != 0; i++) {


gemパッケージの中の写真はGOLDRU$Hの金曜限定ランチ1ポンドバーグ(450g)だというのは秘密。

Monday, May 21, 2007

[Ruby and Erlang] そろそろRBridge::Erlangについて一言いっておくか

RBridge::Erlang


を作った。

弾さんもErlangについて一言言うなどここ最近のErlangの盛り上がりは凄い。

Erlangを1週間ほどさわってみて思ったのは次の二つ。
  • Erlangの軽量なプロセスをRubyから扱いたい。
  • Erlang/OTPが実績もありすばらしい(そう)なのでRubyからその恩恵にあずかる


とりあえず、はじめてのErlanモジュール作成ということで悩んだ結果、RubyからErlangのメソッドを呼び出すRBridge::Erlangを作った。現在同期呼び出しのみサポートしている。ErlangとRuby間の通信におけるプロトコルはJSON-RPCである。


ダウンロード
rbridge-0.0.1.tar.gz

デモの動かし方。
コンパイル

% tar xvzf rbridge-0.0.1.tar.gz
% cd rbridge-0.0.1.tar.gz
% make


Erlang側のサーバを起動する。

% erl
1> c(geometry).
2> geometry:start().


後は、Ruby側から呼び出すだけ。

require 'rbridge'
geo = RBridge::Erlang.new(:geometry, "127.0.0.1")
geo.area({:x => 100, :y => 200}) # => 20000

ポートの変更
デフォルトのポートは3793番を使用しているので、使用している際は次のファイルのPortの設定を変更してください。その際は、Ruby側のRBridge::Erlang.newの第三引数に指定したポートを渡す必要あり。

% cat server_root/conf/httpd.conf
ServerName localhost
ServerRoot server_root
DocumentRoot server_root/htdocs
Port 3793
Modules mod_alias mod_auth mod_jsonrpc mod_actions mod_cgi mod_responsecontrol mod_trace mod_range mod_head mod_include mod_dir mod_get mod_log mod_disk_log
DirectoryIndex index.html
JsonRpcAlias /rpc
ErrorLog logs/error_log
TransferLog logs/access_log
SecurityLog logs/security_log


Erlang側のソース
関数を定義して、rbridge:start関数の引数にリストで渡すだけ。
rbridge:startの第一引数はお約束。?MODULE(現在のモジュール名)を渡してください。
第二引数にservice_procレコードのリストを渡す。idempotent=true or falseはGETリクエストによるJSON-RPCを受け付けるかどうか。POSTは現在実装していないのでtrueにしてください。paramsはサービスの引数を定義する。引数のキーの名前と型("bit" | "num" | "str" | "arr" | "obj" | "any" | nil)のいずれかを指定します。


-module(geometry).
-export([area/2, start/0, stop/0]).

-include("include/rbridge.hrl").

%% Get area
area(X, Y) when is_number(X), is_number(Y) ->
X * Y.

%% service_proc#type ::= "bit" | "num" | "str" | "arr" | "obj" | "any" | nil
start() ->
rbridge:start(?MODULE,
[#service_proc{name = <<"area">>,
idempotent=true,
params=[#service_proc_param{name = <<"x">>, type = <<"num">>},
#service_proc_param{name = <<"y">>, type = <<"num">>}]}]).

% Stop RBrige server
stop() ->
rbridge:stop().



TODO:
「RubyからErlang/OTPの恩恵をどうやって授かるか?」について色々と考えてみる。


追記
id:sfujiwaraさんがPerlから呼び出すコードを書いてくれています。
[Perl][Erlang] RBridge::Erlang を Perl から呼ぶ

Comet&Erlang勉強会@ドリコムのお知らせとRails勉強会@東京

Rails勉強会@東京 第18回に参加した。
参加したセッションは
ActiveRecord::QueryCacheMagic Multi Connection
舞波さん・最速配信研究会さん・瀧内さん・Yuguiさん(順不同)といったメンバーが参加されていた。細かいところでの主な論点はRailsのパーフォーマンスの最適化について。大きな部分(Page Cache)での最適化は使いどころが難しい→小さな部分での最適化を積もらせて全体としてパフォーマンスを上げたいが、Railsはそのへんが弱いという話。

・ActiveRecord::QueryCacheは関連についてもキャッシュを管理できるようにしたい。(舞波)
・ActiveRecord::with_scopeはRails2.0で内部的に使用されるようになり外部に公開されなくなる。(日本人は困る人大勢。。)
・バウンスメール処理や大量のメール配信の問題。(ミドルウェアで処理するかDRb)
・ERBが遅い。Cで書かれたERB実装を使用すると早くなる。Erubisとか。
・Routeが複雑&高機能すぎて遅い。ActionCacheは多用するのでRouteが遅いと困る。Routeは基本的な機能だけで十分なのでCで実装したいなぁ。(瀧内)


ShootingStarとErlang
に参加した。
ShootingStar2.0が出ました。
・クラスタリング機能をサポートしています。
・隠しAPIがいっぱいあるよ。問い合わせが多いと瀧内さんが大変そうなのでソース読んでくださいというのがいいのかな?
・ShootingStarのプロジェクトに新たにDoc Writeの安藤さんが追加されたので急速にドキュメント周りが整備されるかも。
・ErlangはRBridge::Erangを紹介。
・メニーコアが進む現在においてErlang+Rubyでどのような可能性があるかについて話し合う。
・画像処理や3Dなどのギリギリまでパフォーマンスが要求されるところでErlangは実用レベルで耐えれるだろうか。
・Erlangの処理系のソース読んで仕組みを理解しないと安心できない。
・Erlangは本当に早いの?


Comet&Erlang勉強会@ドリコム
日時: 5月27日
時間: 13時~
場所: ドリコム 

数少ないErlang勉強会がドリコムで開かれます。
Cometと絡めてC10K問題やErlang/OTPやHiPEについて実際に手を動かしながらプログラムを書くセッションがやりたいなぁ。

---------------------------------------------------
懇親会

結婚されてたとは知らなかったです。

だって、24時間オンラインだから。
どんな生活かと思いきや9時出社20時帰宅してご飯。22時にまた出社。始発で帰宅。一日二度顔合わせれば十分・・・凄い。

数年内にアメリカに渡るので、24時間アドレナリンでっぱなし・トランスしっぱなしとなるような、未来に残る研究開発・サービスつくりを一緒にやりたいです。同じ職場で働く日を楽しみにしてます。 > Hello, world!

Thursday, May 17, 2007

[Rails] Swiftiplyのアーキテクチャとベンチマーク

Swiftply(日本語で何て読む?)はRubyで書かれたHTTPに特化したプロキシフレームワークである。ApacheのModProxyBalancerやHAProxyなどのプロキシといったフレームワークと大きく異なる点として、プロキシとアプリケーションサーバの関係が上げられる。Swiftplyではブラウザからのリクエストを受け付けるSwiftplyサーバとリクエストを処理する複数のSwifplyクライアントから構成される。Swiftplyクライアントは全て同じアドレス・ポートでリクエストを待ち受ける。

Swiftiplyサーバーの設定ファイルの作成
config/swiftiply_config.ymlにサーバの設定ファイルを設置する。(パスは任意でOK)


cluster_address: 0.0.0.0
cluster_port: 80
#daemonize: true
timeout: 3
map:
- incoming:
- www.yourdomain.com
- www2.yourdomain.com
outgoing: 127.0.0.1:8000
default: true


設定項目
  • cluster_address: ブラウザからのトラヒックをListenするアドレス (String)
  • cluster_port: ブラウザからのトラヒックをListenするポート (Integer)
  • daemonize: デーモンで起動する時はtrue (true or false)
  • timeout: Swiftiplyがブラウザからのリクエストを保持する秒数(sec)。expireしたコネクションの場合は503が返される。 (Integer)
  • map: ドメイン(or IP)別にリクエストの宛先を定義するセクション (Hash)
  • incoming: ホスト名を指定する。配列で複数指定することも可能。(String or Array)
  • outgoing: リクエストの宛先を指定する。Swiftplyサーバからのリクエストは同じアドレス&ポートで待ち受ける複数のSwiftplyクライアントによって処理されるのでここで複数の宛先を指定する必要はない。(String)
  • default: ブラウザからのリクエストがどのincomingにもマッチしなかった際にそのパターンが適用される。(true or false)

Swiftply Mongrel単体での起動
swiftiply-X.X.X/bin/mongrel_railsというMongrelの起動スクリプトが用意されているのでそのスクリプトを使用する。

% SWIFT=1 /usr/bin/mongrel_rails start -e production -p 10000
% EVENT=1 /usr/bin/mongrel_rails start -e production -p 10000

SWIFTまたはEVENTという環境変数を指定することでMongrelをそれぞれのモードで起動できる。SWIFTモードではSwiftplyサーバを立ち上げていないと動作しない。

クラスターとして使用する
初めにサーバを起動する。
初めに示した上記の設定ファイルを使用した場合、80番ポートと8000番ポートがListen状態になる。

% swiftiply -c config/swiftiply_config.yml

8000番ポートに転送されたリクエストのデータを処理するMongrelを起動する。swiftiply_mongrel_raiilsというスクリプトが用意されているのでこれを利用する。(環境は強制的にenvironmentになる)

$ swiftiply_mongrel_rails start -n 10 -p 8000 -d


プロセスIDファイルが#{RAILS_ROOT}/log/dog#{n}.pidに生成されるので再起動する時はmongrel_railsスクリプトを使用する。(swiftiply_mongrel_railsは現在起動のみ)

% mongrel_rails restart -P log/dog1.pid


Apache + Mongrel Cluster環境とSwiftiplyでabを使用しベンチマークを取得した。
リクエスト数を500で固定し同時接続数を10~200まで上げて静的ページと動的ページのRequest per secoundの値を比較した。
CPUはIntel(R) Celeron(R) CPU 2.66GHz, メモリは2GB構成。



静的ページにおいてはかなり高速化している。

TODO
ソースリーディングとアーキテクチャについて理解を深める。
mongrel_clusterのように簡単に扱えるスクリプトを作成し実用で使えるかどうか検討する。

Wednesday, May 16, 2007

[Ruby] Ruby/EventMachineでネットワークプログラミング

Ruby/EventMachine

Ruby/EventMachineはシングルスレッドで高速に動作するネットワークプログラミングのためのライブラリでIPレイヤを扱うコア部分はC++で実装されている。(Pure Ruby版も用意されている。)

イベントドリブンなアーキテクチャで実装されており、ユーザはIP層を全くほとんど意識することなくプロトコルの実装に集中できるように設計されている。ユーザが書くソースコードでは決まったクラスを継承し決まったメソッドをオーバーライトするだけでよい。

現在の実装は、Unix系のシステムを使用している場合はslect(2)システムコールで実装されている。将来のバージョンではepoll(4)に置換したいとのこと。

文字数を返すサーバ


require 'rubygems'
require 'eventmachine'

module CharacterCount < EventMachine::Connection
def post_init
puts "Received a new connection"
@data = ""
@line_count = 0
end

def receive_data(data)
puts "Received data: #{data}"
send_data "#{data.length} (characters)\r\n"
close_connection_after_writing
end
end

EventMachine::run do
host, port = "127.0.0.1", 3793
EventMachine.start_server(host, port, CharacterCount)
puts "Now accepting connections on address #{host}, port #{port}"
EventMachine.add_periodic_timer(10) {$stderr.write '*'}
end



EventMachine::Connection#post_initがクライアントからの接続を受け付けた時に呼び出され、受信したデータはEventMachine::Connection#received_dataの引数に渡される。post_initはEventMachine内のループでリクエストを受け付けるたびにクラスがインスタンス化され呼び出される。ユーザのコード内でこのクラスをインスタンス化することは決してない。

日付を返すメソッド(get_date)を定義したサーバ。

require 'rubygems'
require 'eventmachine'

# This is simple protocol which get date.
class GetDateProtocol < EventMachine::Connection
def receive_data(data)
puts "Received data: #{data}"
if pattern = data.split("get_date: ")[1]
send_data Time.now.strftime(pattern)
elsif data.index("exit") == 0
close_connection_after_writing
else
send_data "ERROR: This method isn't implemented.\r\n"
end
end

def unbind
puts "Connection is closed."
end
end

EventMachine::run do
host, port = "127.0.0.1", 3793
EventMachine.start_server(host, port, GetDateProtocol)
puts "Now accepting connections on address #{host}, port #{port}"
end

unbindは接続が切断された時に呼び出される。その他のメソッドについてはeventmachin.rbを参照。

ネットワーク部分の大部分がC++で書かれており高速に動作しかつ、プロトコルの実装に集中することができる。Ruby/EventMachineのドキュメント中ではC10K問題がライブラリを作るきっかけとなったことについて触れられているが、大本命はやはりErlangかな。

Wednesday, May 09, 2007

[Erlang] 実行時間計測

Eralngで簡単な実行時間計測を行うモジュールを作った。

statistics(runtime), statistics(wall_clock)の一度目の呼び出しはそれぞれCPU時間・実経過時間を0に初期化する。返り値は次の要素から成るタプル(引数にwall_clockを指定)で構成される。


{Total_Wallclock_Time, Wallclock_Time_Since_Last_Call}


prof.erl

-module(prof).
-export([start/0, stop/1, test/0]).

%% prof:start() -> B.
%% Start the profiler.
start() ->
statistics(runtime),
statistics(wall_clock),
{_, T1} = statistics(runtime),
{_, T2} = statistics(wall_clock),
{ok, {T1, T2}}.

%% return -> {Runtime, Wall-clock time}
stop(B) ->
case B of
{ok, {OldT1, OldT2}} ->
{_, T1} = statistics(runtime),
{_, T2} = statistics(wall_clock),
{T1- OldT1, T2 - OldT2};
Other ->
{error, Other}
end.

%% test()
%% Profile test
test() ->
B = start(),
lists:foreach(fun (X) -> math:pow(X, 10) end, lists:seq(1, 1000000)),
{Time1, Time2} = stop(B),
io:format("time=~p (~p) msec~n", [Time1, Time2]).



実行結果

1> c(prof).
{ok, prof}
2> prof:test().
time=300 (1194) msec
ok
3> prof:test().
time=370 (1238) msec
ok


症状
プロセス数を増やしすぎて、CPU時間が短いのに実経過時間が極度に大きくなる場合は、物理メモリが不足しスワップが発生しているサインである。

Tuesday, May 08, 2007

株主への理解

ドリコム内藤社長と対談13(株式分割と資本政策)

とりあえず、「バフェットからの手紙」第三章 普通株を読み、もう一度株価と株式分割について熟考していただきたい。

現状の株主戦略のままでは、中長期的なオーナーの考えを持って株式を購入する投資家を獲得するのは無理である。投機家により株式の売買が繰り返され、企業の内在価値を反映しない株価が続く限り中長期的な株主は損害を被ることになる。

ウォーレンバフェットが示すコーポレートガバナンスを引用すれば、現状の第二形態から第三形態にシフトする改革の時ではないだろうか。

Friday, May 04, 2007

[Erlang] listsモジュールの関数一覧 1



Programming Erlang(PDF)を購入した。

形式コメントからドキュメントを生成するErlang Docのような物が存在するかも知れないが、知らないのでリストに関する操作を提供するモジュール/usr/lib/erlang/lib/stdlib-1.14.2/src/lists.erlからリスト関数を一つずつ見ていきここにまとめることにした。Erlangの規約にのっとり関数名/Arity(関数の引数の数)で示す。

追記
Programming ErlangのAppendix A "Documenting Your Program"を参照。


また、Erlangではモジュールは自作のモジュールも含めてコンパイルされる。標準ライブラリとして提供されているモジュールは/usr/lib/erlang/lib/stdlib-1.14.2/ebin/いかに(環境によって異なる).beam(Bogdan's Erlang Absctract Machine)拡張子で保存されている。

Tips
Erlangインタプリタ上で一度束縛した変数の束縛を解除するにはf().関数を使用する。

listsモジュールの関数一覧

append/2
二つのリストを連結する。

13> lists:append([1,2], [2,3]).
[1,2,2,3]


subtract/2
集合Aから集合Bを引いた差集合を返す。

14> lists:subtract([1,2], [2,3]).
[1]


reverse/1
リストを逆順にする。

15> lists:reverse([1,2,3,4]).
[4,3,2,1]


nth/2
リストの先頭からN番目の要素を取り出す。

6> lists:nth(3,["UIE", "Japan", "Inc"]).
"Inc"


nthtail/2
リストの先頭N要素を取り除いた残りのリストを返す。

20> lists:nthtail(1, ["UIE", "Japan", "Inc"]).
["Japan","Inc"]


prefix/2
リストAがリストBの先頭要素と等しければtrue, 異なればfalseを返す。

35> lists:prefix(["UIE"], ["UIE", "Japan", "Inc"]).
true
36> lists:prefix(["UIE", "Japan"], ["UIE", "Japan", "Inc"]).
true


suffix/2
リストAの末尾要素がリストBの末尾要素と等しければtrue, 異なればfalseを返す。

38> lists:suffix(["Inc"], ["UIE", "Japan", "Inc"]).
true


last/2
配列の最後の要素を返す。

44> lists:last(["UIE", "Japan", "Inc"]).
"Inc"


seq/2 :: seq(MIN, MAX)
整数値MINからMAXまでの順序数を返す。

45> lists:seq(10,20).
[10,11,12,13,14,15,16,17,18,19,20]


seq/3 :: seq(MIN, MAX, Incr)
整数値MINからMAXまでの順序数を返すが増分値を整数Incrの値とする。

48> lists:seq(10,20,3).
[10,13,16,19]


sum/1
リストの合計を返す。

51> lists:sum([10,20,30,40]).
100


duplicate/2 :: duplicate(N,X) XをN個持つリストを返す。

55> lists:duplicate(10, 2).
[2,2,2,2,2,2,2,2,2,2]


min/1
リスト中の最小値を返す。

56> lists:min([1,2,3,4,5]).
1


max/1
リスト中の最大値を返す。

57> lists:min([1,2,3,4,5]).
5


list/2
リスト中の部分リストを返す。(開始要素は先頭要素)

60> lists:sublist([1,2,3,4,5], 3).
[1,2,3]


list/3 :: sublist(List, Start, Length)
リスト中の部分リスト(Start..Start+Length)を抽出する。

58> lists:sublist([1,2,3,4,5], 2, 3).
[2,3,4]


delete/2 :: delete(Item, List) -> List
リスト中からItemを削除する。

62> lists:delete(3, [1,2,3,4,5]).
[1,2,4,5]


zip/2
引数に渡した各リスト(要素数は2つ)の要素からなるタプルのリストを返す。
 
63> lists:zip([1,3,5,7], [2,4,6,8]).
[{1,2},{3,4},{5,6},{7,8}]


unzip/2
zip/2と可逆の操作を行い結果をタプルで返す。

64> lists:unzip([{1,2},{3,4},{5,6},{7,8}]).
{[1,3,5,7],[2,4,6,8]}


zip/3
引数中のリストからタプルの組を生成してリストで返す。

69> lists:zip3([1,4,7], [2,5,8], [3,6,9]).
[{1,2,3},{4,5,6},{7,8,9}]


unzip/3
zip/3と可逆の操作を行い結果をタプルで返す。

70> lists:unzip3([{1,2,3},{4,5,6},{7,8,9}]).
{[1,4,7],[2,5,8],[3,6,9]}


zipwith/3 :: zipwith(F, X, Y)
XとYの要素を順に抽出しF(X1,Y1), F(X2,Y2)...なるリストを返す。

76> Mod = fun(X, Y) -> X * Y end.
#Fun
77> lists:zip(Mod, [1,3,5,7], [2,4,6,8]).
[2,12,30,56]


zipwith3/3 :: zipwith(F, X, Y, Z)
X, Y, Zの要素を順に抽出しF(X1,Y1, Z1), F(X2,Y2, Z2)...なるリストを返す。

82> Volume = fun(X, Y,Z) -> X * Y * Z end.
#Fun
83> lists:zipwith3(Volume, [1,4,7], [2,5,8], [3,6,9]).
[6,120,504]


まだまだあるが、長くなったのでとりあえずココまで。

Thursday, May 03, 2007

GW中に読む4冊

「Life is beautiful: 21世紀の錬金術:Web2.0バブルで一儲けする方法」が人気エントリになることや、このブログでもGoogle Analysticsでの解析結果でコンテンツ上位5位以内に「IPOから学んだ事柄」が必ず入っており、IPOという言葉がITベンチャーで働く者にとって考えさせられる大きな問題となっていることはほぼ間違いない。

真にコーポレートガバナンスを理解しオーナーの利益と従業員含めた企業の利益の両方を追求するための哲学なき経営者が上場すべきでは無いと思う。そこでGW中に読む本として、オハマの賢人と敬愛を込めて呼ばれる、ウォーレン・バフェット氏に関する本を中心として4冊を購入した。金融・会計・経済と言った(私にとっては)専門分野とは異なる分野のため、多くの専門用語に時間を割かれながらもどれも非常に勉強になる本である。

尊敬できる経営者が運営する「企業を買う」(数十年単位の)バリュー投資により富を築いたバフェット氏の本と世界トップの頭脳を集めコンピュータ武装した天才集団LTCMによる裁定取引による荒稼ぎから破綻まで。この二冊を読み比べるだけでも学ぶべきことは非常に多い。









バークシャー・ハザウェイ社の年次報告書にて株主に向けて書かれる投資家必読ともいわれる「会長からの手紙」。それを聞きたいがばかりにバークシャーの株主になる人もいるというから驚きだ。その「会長からの手紙」を一冊にまとめたのが「バフェットからの手紙」であるが会計に関する用語が多く出てくるため、まずは上に示した順序で「最強の投資家バフェット」を読んでから、時間をかけてじっくりと「バフェットからの手紙」を購読するのがおすすめ。

バフェットからの手紙は、哲学書として一生手元においておき繰り返し参照したい一冊である。