2007年 11月 01日

blosxom clone ekfloras

http://coderepos.org/share/browser/lang/ruby/ekfloras

Ruby で blosxom クローンを書いてみた。他の言語と違って blosxom のプラグインの雰囲気をのこしたり、まじめに使うことをちょっと考えたりするように。

テンプレートエンジンに ERB と Erubis どっちもサポートしようとおもって書いていたけど、途中でテンプレートが Erubis 専用になってしまっていることに気付いてめんどくなったのでどっちもやめて (イミフ) 前書いた MiniERB をつかうようにした。(空白圧縮とかしないけど ERB の 2.5 倍ぐらいはやい)

いまどきブログ CGI なんて書いてもだれもつかわないんだけどなぁ。ふつうにどっかのサービスつかったほうがいい。でも blosxom 系のはブログつーるというより他の使いかたをしやすいからおもしろい。

最近の blosxom クローンのまとめ

最近の、であって、僕が書いた、じゃないですよ? (coderepos にあるもののみ)

(使えそうな順にならんでます)

Lua は付属インタプリタでは実装できない。PHP と Python はやりたいと思わない。VBScript は Windows で CGI 環境つくるのがめんどうくさい。

Haskell と C# (on mono) あたりはやってみたいけど、ここで「やってみたい」とか書いている限りやらないだろう。本当にやってみたいなら既にやっているはずだ。

2007年 11月 03日

そういえば

id:gyuque さんにカロリーメイトのパチモンをもらったのを忘れていた。というか写真も現像してない。どんだけ coderepos 中心の生活なんだ。へたなネトゲよりたのしいコミット。

IMG_61760084

id:higepon さんと mayuki さんにもわたったようだ。

あと、昨日の写真でアップロードできそうなのを探したけどみごとに人がうつっててやめた。肖像権こわい。

IMG_61500085

前半一番後ろに座ってたときのやつがあった。(後半は空気読まず一番前に座ってた)

Shibuya.js

いい空気だよなぁほんと……いごごちがいい。今回は懇親会で Perl hacker なかたがたの Perl よりの話を聴いた。じわじわ Perl の知識が増えておもしろい。

あと、学校辞めてから生き生きしてるよねとか言われたけれど、まじでその通りすぎる。やらなくちゃいけないことはあるんだけど、そういうのさしひいても毎日楽しいもんなぁ。

写真の統一感

は、彩度とかコントラストとかではなくて、なんかちょっとそういう要素とは違う部分にあると思うなぁ。フィルタのかかりぐあい。心の。フィルタの状態の統一感ではないかな

機械的に判断できる部分ではなくて、頭に思い浮かべた像が、統一されているかどうか。

GranParadiso

Firebug1.1.0b7 を入れた。うごいている。しかしいままで以上に重い。GranParadiso の重さと相乗効果

はてなのマルチログイン uc.js がないと激しく不便なのでデバッグして Fx3 向けに修正した。tumblr のやつも同じ修正が必要だけど、あんま使ってないのでそのうちでいいや。

http://coderepos.org/share/changeset/1051

ekfloras (blosxom clone) のプラグイン機構

とりあえず module/pluggable を使っていたものの、別にリロードとかできなくていい (CGI なので使い捨て) ので、もっと簡単なプラグインシステムにした。ついでに rubygems への依存もなくなったので、純粋に Ruby がインストールされていればレポジトリからチェックアウトしてすぐ動くようになった。

# プラグインファイルをロードして
# インスタンスの配列を実行すべき順にソートして返す
def load_plugins
	Pathname.glob(@opts[:plugin_dir] + "/**/*.rb") do |f|
		load f
	end
	pm = self.class.const_get(:Plugin)
	pm.constants.inject([]) {|r,i|
		r << pm.const_get(i).new(self)
	}.sort_by {|i|
		i.priority
	}
end

みたいにして Ekfloras::Plugin::* をプラグインとみなし、メソッドをよぶ。かなり簡単だけど普通は十分なプラグイン機構だと思う。(ファイル名とクラス名間の名前の制約がなくなるので、てきとーにプラグインんが書けるようになる。逆にこれのせいでリロードとかが実装できない) こんだけ単純だと特にライブラリする必要もないので欲しくなったときに書きなぐればいいのがいい。

(priority メソッドの返り値でソートしているのは、プラグイン同士で実行順に気をつかうようにするため。blosxom だと名前順になっているから、各ユーザがプラグインの仕組みを理解してファイル名を書きかえたりしないといけなかったけど、これでだいたいてきとーにプラグインディレクトリにほうりこんでも動くようになるはず)

ちゃんとインスタンス化してメソッドをよぶようになるので、各プラグイン内のメソッドの名前がコンフリクトしたり、データが共有されることがなくていい。ちゃんとメソッド間ではデータ共有できるし (こんなのあたりまえなんだけど、Ruby 系のプラグイン機構ってこういうあたりまえのことができないのが多いイメージ)

あとは設定かな。設定はできるだけ書きたくないから (blosxom のプラグインのいいところは投げこめば動くことだと思う)、最小設定と、オプションをいくつかとかになるかな。

ekfloras の速度

  1. #<Ekfloras::Plugin::Meta:0xb7bcc03c>
  2. #<Ekfloras::Plugin::Paging:0xb7bcbfec>
  3. #<Ekfloras::Plugin::Comment:0xb7bcc014>

(EntriesCache なし)

ruby 1.8.4
1159 files, 1.212316 sec
1159 files, 1.722761 sec
1159 files, 1.390812 sec
1159 files, 1.353448 sec
1159 files, 1.423345 sec

ruby 1.9.0 (2007-09-24 patchlevel 0) [i686-linux]
1159 files, 1.025227 sec
1159 files, 1.075664 sec
1159 files, 1.202309 sec
1159 files, 1.300395 sec
1159 files, 1.284752 sec
  1. #<Ekfloras::Plugin::EntriesCache:0xb7c08ae8>
ruby 1.8.4
1159 files, 0.733907 sec
1159 files, 1.166876 sec
1159 files, 1.437776 sec
1159 files, 0.857321 sec
1159 files, 0.906379 sec

ruby 1.9.0 (2007-09-24 patchlevel 0) [i686-linux]
1159 files, 0.315943 sec
1159 files, 0.301194 sec
1159 files, 0.446674 sec
1159 files, 0.254224 sec
1159 files, 0.45789 sec

EntriesCache (エントリをオブジェクト化した状態でマーシャライズしてキャッシュするプラグイン) 入れた ruby1.9 がかなりはやい。

2007年 11月 04日

IMG_61210079

IMG_61350080

libruby.1.dylib うめこむやつ

http://rubycocoa.sourceforge.net/EmbedRubyCocoa

require 'pathname'
exec = File.join(ENV['TARGET_BUILD_DIR'], ENV['EXECUTABLE_PATH'])
old = Pathname.new('/usr/lib/libruby.1.dylib').realpath
new = '@executable_path/../Resources/libruby.1.dylib'
system("install_name_tool -change '#{old}' '#{new}' '#{exec}'") or exit 1

ってうまくいかないようなきがする。Contents/MacOS/APPNAME は otool -L すると libruby にリンクしてなくて、@executable_path/../Frameworks/RubyCocoa.framework/Versions/A/RubyCocoa にリンクしてるし、実際 libruby にリンクしてるのはこっちのほう。install_name_tool の挙動を勘違いしていなければ、たんにこれは指定されたファイルの Mach-O dylib のパスを書きかえるだけだろうから、これだと意図したこと (バンドル内の libruby よむ) ようにならない?

Chemr とかでは結局 standalonefy してあつめられる .bundle とかの libruby.1.dylib へのリンクも一緒に書きかえるようにしてみた。

# in 'package' task
	# copy libruby.1.dylib
	sh %{rubycocoa standaloneify "build/#{DEFAULT_CONFIGURATION}/#{APPNAME}.app" "image/#{APPNAME}.app"}
	system_libruby = Pathname.new("/usr/lib/libruby.1.dylib").realpath
	bundle_libruby = "image/#{APPNAME}.app/Contents/Resources/libruby.1.dylib"
	linked_binary  = "image/#{APPNAME}.app/Contents/Frameworks/RubyCocoa.framework/Versions/A/RubyCocoa"
	(Pathname.glob("image/**/*.{bundle,dylib}") + [linked_binary]).each do |l|
		sh %{install_name_tool -change '#{system_libruby}' '@executable_path/../Resources/libruby.1.dylib' '#{l}'}
	end
	cp system_libruby, bundle_libruby
sudo mv /usr/lib/libruby.1.dylib /usr/lib/libruby.1.dylib_  

して起動することを確認してみた。

(たぶん) 完全にスタンドアロンな Chemr と Amalgam

起動バイナリを置きかえて DYLD_LIBRARY_PATH を設定するとかでもいいのかなぁ。この環境変数がどれぐらい強いのかよくわからないけど

マルチログイン系のを GM で書かない理由

使わないときにも実行されるのが嫌

GM で書いたほうがはやく書けるけど、日常的におもくなってストレスたまるほうがいやだな。


GranParadiso はそれでなくても重いし!!!!! というか Safari の UI を RubyCocoa で InputManager とかつかって変更できるなら乗りかえちゃうなとか思った。

>

GranParadiso

CPU つかいすぎだなぁ……あとなんかフォアグラウンドにいるときはより CPU を食う。ActivityMonitor のグラフがやたらのびてるなぁとおもってそっちにスイッチすると隠れようとする。

関係ないけど pmTool も CPU 食いまくるよなぁ。そのせいでバッテリ持たないっていうのはどうなの。

2007年 11月 06日

モナド・モナド・モナド

Haskell が理解できる気がしてこない。諦めたい気分になってくる (実際書いてるコードは理解しなくても書ける範囲でしか書いてない)。いやもちろん誰かに「やれ」なんていわれているわけじゃない。理解できなくてくやしい。lift が全くわからない。「モナドの全て」の説明を読んでもわかった気にさえなれない。頭が悪い。本当に悪い。頭悪いのを実感できる分野は苦手だ。数学とかまさに。Haskell は数学に近い。ただプログラミングということになっているから耐えられる。いややめるかも。頭のいいわるいなんて生まれつきだから、そんなのいまさらどうしよもない。理解できる人には理解できるんだろうなぁ。

IO モナドが副作用を閉じこめているのは (そういう説明がされているから) わかった気になる。それを扱うとき、そのモナドの中で (この表現が正しいかよくわからない。つまり do ブロックの中といいたい) 処理をして、モナドをはずす (この表現が正しいかよくわからない。つまり IO String を <- をつかってなかの String をとりだすといいたい) ことで、副作用を一旦どっかに置いとくことができるようなことはわかった (ほんとに?)。

この do ブロックの中でなにがおこっているのかわかってない。>> で各行が連結されている? >> はなにをする? 連結。連結ってなに。>>= の = がないバージョン。return がなんなのかわかってない。こいつはなにをするんだ。

return は return :: a -> m a すなわち a をとってモナドにくるまれた a をかえす。モナドのインスタンスをつくる。でもこれ引数に値しかわたしてないけど、どのモナドにくるまれるんだ? コンテキストモナド? コンテキストモナドとかてきとうにいったけど、よくわかってない。それが存在するかすらわかってない。

(>>=) :: m a -> (a -> m b) -> m b バインドとかいうらしい。モナドインスタンス m a と関数を一個とって新しいモナドをかえす。わけわかんない。モナドの中の値を処理してまた新しくモナドインスタンスをつくってる。なんでこんなことする必要があるんだろう。計算を合成するってどういう意味だ。Maybe があるなし一緒にして計算を継続できるようになってるのはいいことなのか。合成ってなんなんだ。関数合成と計算の合成って何がちがうんだ。

そういえば関数合成 . と $ のつかいわけもよくわからない。全部 $ でかけるような。合成のほうが気分的にすっきりするだけ?

aaa >>
bbb

aaa >>=
(\x -> bbb)

みたいなときに x には何が入ってるんだ。aaa の返り値か。まてまてモナドはどこいった。もうすこし実用に近いところでみてみよう

import Network.CGI

cgiMain :: CGI CGIResult
cgiMain =
    setHeader "Content-Type" "text/plain" >>=
    \x -> output "Hello"

main :: IO ()
main =  runCGI $ handleErrors cgiMain

もちろん >>= は >> にして \x -> は消しても動く。それはいい。\x には何が入っているんだ。setHeader の返り値のはずだ。

setHeader :: MonadCGI m => String -> String -> m ()

m () ってなんだ。() がなんだかわからない。null ? 空リスト? いやリストは [] だ。こいつはなんだ。Haskell だと print デバッグができない。\x になにが入っているか直接調べることができない。学習のさまたげだ。print デバッグは偉大だ。

この () とかいうやつは :type しても () そのままうちこんでも () のままだ。どこに定義が書いてあるんだ。探しにくい。これだから記号がいやだ。

まぁいいやもっと簡単にしていこう。こんどはちゃんと前の計算の値をつかうモナドだ。よくあるやつになった

main :: IO ()
main =  readFile "Rakefile" >>= (\x -> putStr x)

readFile は IO String をかえす。>>= で x に IO String のなかの String がバインドされる。そして計算が実行される。

実際は putStr 自体が (\x -> fun x) の形になっているからこう書く必要はなく

main :: IO ()
main = readFile "Rakefile" >>= putStr

でいい。putStr をよばずに、関数として >>= 関数にわたしてやっているだけだ。ん? なんか全く普通だ。関数の定義通りの実行されているにすぎない。モナドが見えない。putStr の返り値は IO () だから main の定義ともあっている。これはまったくもってわからないところがない。あれ?

main :: IO ()
main = readFile "Rakefile" >> putStr ""

たしかになにもでない。あたりまえだ。

もう一回 CGI の例を定義とてらしあわせてみるけど、こっちはやっぱりわからない?

class Monad m => MonadCGI m
data CGIT m a
type CGI a = CGIT IO a
runCGI :: MonadIO m => CGIT m CGIResult -> m ()
setHeader :: MonadCGI m => String -> String -> m ()
output :: MonadCGI m => String -> m CGIResult

--

import Network.CGI

cgiMain :: CGI CGIResult
cgiMain =
    setHeader "Content-Type" "text/plain" >>
    output "Hello"

main :: IO ()
main =  runCGI cgiMain

最終的には IO () を runCGI が返すはずだ。だけど runCGI は m () をかえしている。ここでの m は MonadIO だ IO は MonadIO のインスタンス? だとするとここはあってる。runCGI は CGIT m CGIResult を引数にとる。cgiMain は CGI CGIResult をかえしてる。あれ m は? とおもったら CGIT m か。CGI CGIResult は type を展開すると CGIT IO CGIResult になる。うむ CGIT m CGIResult とマッチする。

setHeader は String -> String をとって MonadCGI をかえしてる。すなわちここでは setHeader が MonadCGI の最初のインスタンスをつくっている (たぶんこのインスタンスが setHeader した結果を保持しているんだろう)。そして、output は m CGIResult をかえしている。m は MonadCGI のインスタンスだ。あれ m CGIResult と CGI CGIResult があってなくないか。data 構築子 CGIT は m a と定義されている。m は MonadCGI。ということは m CGIResult になるな。なるほど。

import Network.CGI

-- 1: cgiMain :: CGI CGIResult
-- 2: cgiMain :: CGIT IO CGIResult
-- 3: cgiMain :: MonadCGI m => m CGIResult
cgiMain :: MonadCGI m => m CGIResult
cgiMain =
    setHeader "Content-Type" "text/plain" >>
    output "Hello"

main :: IO ()
main =  runCGI cgiMain

これでもうごく。ちゃんと整合性とれてる。data CGIT m a のよみかたがわかった。data CGIT は m a と一緒ってことだな。たぶん。

とりあえず型のうごきをみていくと >> のところでモナドインスタンスがうけわたされて処理されているっぽいのがなんとなくわかった。前回の計算結果は捨てているけど、背後でうごめくモナドインスタンスは引き継がれている。ベルトコンベアの比喩も若干わかった。文章にするのはわからないところがわかってそれを理解しようとする試みができて偉大だな。

しかしモナドインスタンスがどうやって計算を保持しているかわからないな。モナドによって保持方法が違うのは想像できるけど、実装はどうなっているんだろう。

まぁそれはおいといて、return もすこし理解できた。上ではでてきていないけど、>>= の右側の関数は m a をかえさないといけないわけだ。でも内部処理した結果がただの String だったりしたときは、モナドでつつみなおさないといけない。そのときん return をつかうとそれをつつんで、次にわたせるってことかな? よしやってみよう。

import Network.CGI

cgiMain :: CGI CGIResult
cgiMain =
cgiMain =
    setHeader "Content-Type" "text/plain" >>
    return "hogehoge" >>=
    output

main :: IO ()
main =  runCGI cgiMain

こうすれば return でつつまれて次の関数に連結され、output に hogehoge がわたされるはずだ。うまくいった。うれしい。


とりあえずつかれた。ここまでやるのに時間かけすぎだ。リストモナドがなんでモナドなのか理解していない。lift は理解できそうもない。Haskell は if も case も let も全く理解していない。まだまだだ……

関数合成 .

test :: String
test = "aaa" >>=
      return . toUpper

みたいにかけるのはいいかも

test :: String
test = "aaa" >>=
      \x -> return $ toUpper x

はちょっとださい。


リストもモナドっていうのをたしかめたくて変なコード書いてる。String は Char のリストなのでモナドの演算ができるはず (直接やらないのは String のほうが出力しやすいから) うえのは単に map (\x -> toUpper x) "aaa" と一緒だから良さがよくわからないけど、リストモナドから要素を一個一個とりだして関数適用してるっぽいのはわかったかも。

2007年 11月 07日

勉強するものじゃない。

なんか

ホットエントリとか、そういうののブクマコメをよんでいて感じる気持ちって、うまく表現できない。俺がいままで思っていたよりも、俺と決定的に合わない人っていうのは、多いんだなぁと、近頃、特にここ三ヶ月ぐらいは、感じる。

Lua で Blosxom クローン

http://coderepos.org/share/browser/lang/lua/bluasxom/bluasxom.lua

ファイル列挙するのが生の Lua でできないので ruby よんで解決。他の部分は Lua だけでかいた。Class と List は前に RO 用に書いたのをそのままつかった。reverse を定義していないことにきづいたけど sort の等号変えてアドホック解決。Lua の文字列リテラルはおもしろくて

[[...]]
[=[ ... ]=]
[==[ ... ]==]
[===[ ... ]===]

みたいに = をふやして入れ子にできる。コメントなんかも複数行書きたいときは -- のあとにコレかくんだけど、これのおかげでテンプレートのコンパイルのときに楽をしてる。それとテンプレートの include (コンパイル時に処理しないといけない) とかも入れこにできるから簡単かも。実装してないし、してもあんま意味がないのだけど……

テンプレートへの値のうけわたしは setfenv (set function environment) でやってる。Lua では文字列の eval をしたいとき、 loadstring で文字列を評価する関数をつくって、それをよぶという感じなので、loadstring のかえす関数に setfenv をよんでやれば環境が変更できる (これは Ruby でいうところの binding を指定して eval してるようなものだけど、環境も Lua ではただのテーブルなのでより柔軟性がある)。

ただ、たんにテンプレートでつかう値だけを入れたテーブルを setfenv すると os.date とか組みこみの関数がテンプレ内からつかえなくて泣けるので、グローバル環境から一段階値をコピーしてからセットしてる。やっぱ環境に直接さわれるのはおもしろい。JS もさわれたらいいのになぁ。

正規表現リテラルが必要なたったひとつの理由

バックスラッシュ二個かくのめんどくさいだろ……常考……

2007年 11月 08日

HTML の class

class はまんま OOP のクラスと同じ意味でとらえることができて、

<div class="hentry">
</div>

というのはクラスが hentry である div を表わし、言いかえれば、この div 要素は hentry クラスのインスタンスといえる (hentry に分類される実体)。

二つ以上のクラスのときは多重継承したクラスをイメージすればいいと思いますよ。

モーダルだけいえば

一般的にモーダルなのはよくないことになっていて、とかいいつつメニューとかもモーダルなんだけど、どこでもつかわれているメニューでさえ慣れていない人は操作法がわからないものらしい (おれにはそれがよくわからないけど、例えば MS Office で大量にツールバーアイコンがあるのはそういう人向けらしい)。でもみんな普通にコンピュータ使うときにはモーダルなことをしてる。モードの有無はそれを想像できるならどうでもいいことだと思う。あとはモードの切替えがめんどいかめんどくないかとか、デフォルトのモードが適切かどうかとかぐらいな感じがする。


どこらへんで妥協して線をひくかがいつも重要だけれど、そういうのって頭よくないとできないんだよなぁとつくづく感じる。おれにはできる気がしない。プログラミング言語デザインとかもそうだよなぁ (S式がなんでだめかみたいな。なんかここ最近S式がなんでだめかとか考えてばっかなんだけど)。

Vim の自動補完候補表示

http://screencast.com/t/jfQ2fz8HHL (月あたりの転送量を超えたらしく見れなくなった。Jing は 1G/month らしい)

http://lab.lowreal.net/trac/browser/config/.vimrc#L225

使いはじめてしばらくたったけど、快適すぎる。けっこう重くなるから非力マシンだと使えなそうだけど、頭使わずに補完が使えるようになってほんとに楽だ。noignorecase にしないとあれになるから検索とかが不便になるけど、それを考えてもいいかんじ。

数文字うったら候補が勝手に表示されて、候補がおおかったらずっとうちつづけられるし、候補があったらすぐ TAB 連打して入力できるし……

追記
http://subtech.g.hatena.ne.jp/cho45/20071111/1194766579

http://www.vim.org/scripts/script.php?script_id=1879

っていうのが既にあるよ! オプションとかあるからこっちのほうが便利だよ!

Monad

順序処理を関数の入れ子に、また関数自体の値と平行して副値を走らせることで「副作用」をパッケージ化するというのがモナドのコンセプトである。

http://ja.wikipedia.org/wiki/%E3%83%A2%E3%83%8A%E3%83%89_(%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0)

やっとこの部分が実感できた。やっぱちゃんと書いてみるってのはてっとりばやいなぁ。IO モナド以外はうまくイメージできてないけど……

言語

Haskell の (\x -> a * x) みたいな匿名関数の表記はかなりビビっとくる。なんていうかこう、可愛い女の子を見たときの気持ちにている。腹の底がグっって持ちあがる感じ、わくわくする感じ。

JS でもこんぐらい短いといいのになぁ。というか JS は return かかないと値返さないのがダメすぎる。なんであんな仕様にしたんだろ。もったいない。Lua もだ。最後に評価した式の値返せばたくさん幸せになれるのに……

あと Lua はめっちゃ JS だよ。JS

a = List.new(1, 2, 3)
a:map(function (i)
    return i * i
end)
a = new Array(1, 2, 3);
a.map(function (i) {
    return i * i;
});
2007年 11月 09日

独りで

よく、ある程度熟したアーティストが、他のアーティストとコラボレーションして「いい刺激になった」とかいうけど、あれはそのアーティストが既に一つ山を越えていて、ある種完成されている (つまり、完成=安定の状態を崩すための刺激) からであって、その山を越えるまではずっと孤独なんじゃないかって、つまり何がいいたいかっていうと、最初っから、大したことできないくせに、他人とコラボレーションしようとするなよ、ってなんかすごく思うんだ。

http://lowreal.net/logs/2006/04/14/1

っていうのを前に書いたのを思い出した。基本的に独りでできることは独りでやるべきだと思う。

と同時というか、ここ最近なんとなく思いうかべていたのはカブスカウト (ボーイスカウトの小学生の部) の定めにある

カブスカウトは 自分のことを自分でします

カブスカウトは たがいに助けあいます

の二つ (全部で五つある)。


いろんなことは互いのバランスでできているから、例えば「していいよ」と言われてもしてはいけないことはたくさんある。こんなのすごく、すごくあたりまえのことだと思う。

2007年 11月 10日

apt-get bvi / port install bvi

vim の -b とかでやるバイナリモードは使いにくいとかいうレベルじゃないので id:amachang に Shibuya.js のときにきいた bvi をいれた。TAB で ASCII のほうと hex のほうとでいったりきたりできるのがおもしろい。

undo の挙動がちょっと違うのがきになるかも。まだあんまりつかってない

まる

http://www.nicovideo.jp/watch/sm1489464

これすごいなー。ちょっと腹の底からグっとこみあげてくる感動があるなぁ。こういう技術っていいなぁ。


こういうのみるたびに、コーディングってライブ感がなくてすげー地味だなーって思ってしまう。ライブコーディングとかも、わかる人にはわかる、っていうレベルの面白さだもんなぁ。わかるひとだけわかるっていうがどっちかっていうと好きだから (大衆受けしても大抵ろくなことにならないから) いいんだけど、たまにちょっと切なくなる。

映像的なプログラミングだってもちろんあるわけだけど、どうもそういうふつーにウケる、みたいなのをやる気にならない。表面的な面白いことと裏側的な面白いことを組みあわせたらいいんだろうけど、表面的な面白さにしか気付かない人を見るとかなり切なくなるし話かけられるのも嫌だし、それだったら最初っから難解で気付く人が気付けばいいと思えてしまう。なんかおれは気持ち悪くひねくれてるいるんだなぁ……


うえの動画だって、おれは絵のことを良く知っているわけじゃないから、知っていればわかるような面白さに気付けていないんだろうなぁ。

2007年 11月 11日

言語

言語は英知の固まりだ

勘違いだけど

時間かけてやれば Haskell だって理解できるのではないかと思ってしまう。勘違いだ。

今日は Wikipedia で圏論の項を一応じっくり読んでみたけどほんと全然理解できない (Haskell を理解する上では必ずしも必要ではないとは言われているけど、やっぱり背景をしっておかないと真に言語設計者の意図は感じとれないと思う)。というのもそもそも数学的なことを考えるうえで必要な教養を全く持ちあわせていないせいなんだけど、数学は独学すると危険な香りがするからうかつに手を出せない感 (だからといって先生に教えてもらう、っていうのは、そういうタチではないのを身を持って知っているので独学でやるしかないんだけど) なんか数学コンプレックスみたいだ。頭悪いの実感するから苦手なんだよなぁ。

  • 具体圏以外の圏がイメージできない
  • 数学的構造を理解できない
  • 写像と関数の違いがわからない
  • というか写像が理解できてない
    • map は写像の map ?
    • でも map って関数じゃん
    • 写像は一部分だけっていうのがある?
      • 制限写像とかって map だけでは書けないよね?
      • 恒等写像は map (\x -> x) だよね
    • 関数になってる写像は全射?
    • 全射でも単射でもない写像ってあるの
  • 値域ってなんだよ
    • 値のとりうる範囲?
    • どうやって指定するんだ。
    • Int = 1 | 2 | ... みたいなの?

だめだこれやめたほうがいい泥沼になっていく。数学勉強してる人にバカにされる。おめーみたいなバカが理解できるわけねーだろってゆわれる。(ぼくは高校数学すらほとんどやってない。具体的にいうと高校二年の文理選択のときに全ての数学科目を捨ててる。)

人生がたりない

時間ないよなぁ。例えば、いくら金持ちだったとしても、世界中の国のあらゆる場所のよいところを知ることはできない。住んでみないとわからないことがあまりにも多いからだ。金で時間は絶対に買えない。金で経験は買えない。

時間たりないなんていうくらいなら、ずっとやりつづけろとは思うけれど、そういうのもできない。だめ人間すぎるのだなぁ……


それにしても、本当に大学は無駄だった。ひきこもって独学してたほうが数百倍素晴らしい生活ができたよなぁ。DAIGAKUSEI むかつく。みんな死ねばいい。DAIGAKUSEI だけじゃない。もっと広い範囲で、全くあわない人たちは多すぎる。今の季節の夜に外へ出て鼻腔を広げ冷たい空気とともに秋の匂いを肺一杯に感じるようにしている人達と一緒にいたい。それができないなら死んだほうがマシだ

なんかこう

脊髄反射でニヤニヤしながら「それって違うよね~w」みたいなのをいうやつは頭が本当に悪い。たとえばおれとかだ。それを他人がやっているのを見ると自分を見てるみたいで余計むかつく。直したい。

洗濯機へ消えていった女の子

洗濯機へ消えていった女の子が悩んでいた間、君はいったい何をしていたっていうんだ?

IMG_61360081

2007年 11月 10日

Emacs lisp (elisp) による blosxom クローン

(誠に誠死ね申しわけありませんが、blosxom を便利につかえるようにする .el ではありません)

http://coderepos.org/share/browser/lang/elisp/blosxom.el.cgi/blosxom.el.cgi

一応だいたいうごくところまで書いてみました。日本語表示できないけどどこで化けてるかわからないからもういいや><

ライセンスを same as Vim とかにしたかったけど Vim のライセンスが GPL とかじゃなかったのでやめた。

実行しているところを ttyshare にあげたかったのだけど、描画させながらゆっくり実行させる方法がわからなかった (*scratch* で insert-file して eval-current-buffer すると一瞬でおわってしまう)。というかひさしぶりに Emacs 起動したら C-h とかきかなかったりして全然操作できなかった。ことあるごとに C-[ 押して混乱してしまうw

いろんな言語

少しずつプログラミングのエッセンスみたいなのが見えてくる気がする。ある言語の設計がなぜそうなっているかとか、そういうことを、他と比較することで少しずつ理解できる感じがする。

2007年 11月 11日

Enumerable#inject

実際、なんかネタメソッドとか思っている人もいくらかいるのかもしれないのだけれど、inject は慣れると使いまくるメソッドの一つである。

あと inject を知っていると関数型言語をやるときに生きてきておもしろい。fold とか

ふときづくと

Safari も Mail.app もつかっていない。

今起動してる app

  • Terminal.app
  • Chemr.app
  • Console.app
  • Ttunderebird.app
  • iTunes.app
  • GranParadiso.app

Dashboard も Expose も殆ど使わない。ぜんぜんマカーじゃないですね。

osx.iusethis.com を使わないとのろわれるといううわさなので登録してみた。(copipe)
http://osx.iusethis.com/user/cho45

単射・全射

はてキだと単射は 1:1 で対応がとれているやつって書いてあるなぁ。わかりやすいけどそういうことなのか。

http://ja.wikipedia.org/wiki/%E5%85%A8%E5%B0%84 に図があった。わかりやすい……

autocomplpop.vim

ns9tks さんが既につくっていたのか>< 恥ずかしながら知らなかった。オプションとかが充実しててすばらしすぎる。どう考えても life changing

http://www.vim.org/scripts/script.php?script_id=1879

入れた! しかしちょっと気になったので一箇所だけ修正してみた

--- autocomplpop.vim.orig       2007-11-11 16:26:40.000000000 +0900
+++ autocomplpop.vim    2007-11-11 16:22:20.000000000 +0900
@@ -192,7 +192,7 @@
 
     if pumvisible()
         " a command to restore to original text and select the first match
-        return "\<C-p>\<Down>"
+        return "\<C-p>\<Down>\<C-p>"
     elseif a:nRetry > 0
         " In case of dividing words by symbols while popup menu is visible,
         " popup is not available unless input <C-e> (e.g. 'for(int', 'a==b')

オリジナルだと表示されてすぐ C-n だけだと最初の候補を選択してくれない (二回おして最初の候補が補完される)。なんかでも環境によるのかもしれない。うちだとこうするとうまくいく。( VIM - Vi IMproved 7.1 (2007 May 12, compiled Oct 7 2007 05:51:43) ) なんか他の部分となんかなってるのかもしれない。

あとは lazyredraw だけ別設定してチラつきおさえた (Mac の gvim だと hjkl おしっぱのカーソル移動がみえなくなって不便だけど gvim とか滅多につかわないからそんなに不便じゃない) それと ignorecase を一時的に変更できるみたいなのでデフォの ignorecase を元にもどした。

あと、補完候補表示したまま RET おして改行できないので以下のコードを復活させた

inoremap <expr> <CR> pumvisible() ? "\<C-Y>\<CR>" : "\<CR>"

vim.org

vim.org のレートって &rate=... つきのリンクはれるyouna

あとスクリプトダウンロードのところで http://www.vim.org/scripts/download_script.php?src_id=n になるんだけど、これだと wget するときいちいちファイル名を変更しないといけなくてめんどい……

2007年 11月 12日

青春

なんだかんだいって、今までは青春をどこかで感じていたのか、ごくごく最近になって、青春性みたいなものを感じられなくなってきている感じがする。学校的なもの、女学生的なものを身近に感じず、遠くのどこかにあるもう絶対に手が届かない何か、ファンタジーのようなもの、という実感が心に浮いている。

二日連続で夢をみて、二日連続で学校 (高校) がでてきた。

2007年 11月 11日

Rails hype 156cm

Rails は hype っていうのを hyde と誤読して、身長を多くみつもること? 背伸び? 自分を大きくみせようとすること? とか考えてきたら、あんがいまちがってなかった。

Spotlight (Mac 的な意味じゃなくて)

Copernicus はマウストラッキングしてスクリーンとれるけど、どことってるのかよくわからない。

ので、ある範囲だけ明くする RubyCocoa スクリプトのサンプル

#!ruby -Ku

require "osx/cocoa"
include OSX

class MainView < NSView
	def drawRect(rect)
		super_drawRect(rect)

		NSColor.blackColor.set
		NSRectFill(bounds)

		NSColor.clearColor.set
		point = NSEvent.mouseLocation
		x, y = point.x, point.y

		width  = 512
		height = 384
		NSRectFill([
			x - width / 2,
			y - height / 2,
			width,
			height
		])
	end
end

class Test < NSObject
	def applicationDidFinishLaunching(n)
		screenFrame = NSScreen.mainScreen.frame
		rect = screenFrame

		@window = NSWindow.alloc.objc_send(
			:initWithContentRect, screenFrame,
			:styleMask, NSBorderlessWindowMask,
			:backing, NSBackingStoreBuffered,
			:defer, 0
		)
		@window.opaque             = false
		@window.hasShadow          = false
		@window.level              = 1000 # NSScreenSaverWindowLevel
		@window.ignoresMouseEvents = true
		@window.alphaValue         = 0.8


		@view = MainView.alloc.initWithFrame(rect)

		@window.contentView = @view

		@window.frameOrigin = [0, 0]

		# いでよ
		@window.makeKeyAndOrderFront(nil)
		@window.orderFrontRegardless

		point = NSEvent.mouseLocation
		Thread.start do
			loop do
				sleep 0.01
				np = NSEvent.mouseLocation
				unless np == point
					@view.needsDisplay = true
				end
				point = np
			#	@view.display
			end
		end
	end
end

app = NSApplication.sharedApplication
app.delegate = Test.new
app.run

これだとマウスをはやく動かしたときに Copernicus のキャプチャにうつりこんじゃうんだよなぁ (それにこのスクリプト自体がかなり重い)。やっぱ自力でキャプチャアプリ書くしかないのかなぁ。速度求められるアプリケーションはむずかしいよお

Spotlight (Mac的な意味で)

Spotlight 検索を直接使うことはないけど locate は使うなぁ。これってたぶん連動してるよね?

してないですね。なに勘違いしていやがる

2007年 11月 12日

マウスジェスチャ式のランチャ

Photoshop の拡大率をスパっと 100% がどうとかいう話をしてて、ジェスチャできたらいい気がしたので簡単にできる範囲でつくったけど肝心の拡大率 100% のぶぶんを jsx から叩けない気配がして (ヒストリに記録されない) orz

IRC で発言した時間から計算するとここまで作るのに1時間……殆どコピペでつくってるのに時間かけすぎだなぁ…… (逆に、いろいろ作っていると思いたったらコピペでつくれるのはおもしろいかも) RubyCocoa じゃなかったらもっと時間かかって、それが作る前にわかってしまうからそもそも作らないんだけどw

左下にジェスチャ用の箱があらわれて、そこから左クリックしながらドラッグでジェスチャ。ほんとはどこからでも開始したいけど、いい方法がおもいつかない。ホットキーで起動して、っていうのはできるけど、それじゃあんまり意味ないし……

http://coderepos.org/share/browser/lang/ruby/misc/cocoagesture.rb (つづきつくるかわかんないけど、バンドル形式になってない RubyCocoa スクリプトなので RubyCocoa ちょっと触ってみたい人にちょうどいいサンプルかもしれない)

http://ichiro.nnip.org/osx/Expogesture/index.html

こういうのあったんだった。これの場合はマウスの位置だけしか見てない。NSEvent.mouseLocation の監視だけしてるっぽい? 誤爆しそうだよなぁ……

あぷぷ毎に設定できるから問題にならないのかも。どうせジェスチャなんてたかだか数個しか覚えられないし、こういうので十分なのかもしれない。つかってみよう

Chemr / DRb コネクションスレッドでの GC 時におちる

外部から DRb で Chemr にアクセスしてそのうえそのスレッドで GC が発生すると落ちるんだけどどうしたらいいかサッパリわからんよ><

巨大なインデックスもってる chm をひらいて Amalgam 側から Chemr インデックスひこうとすると Chemr が落ちる。でかいインデックスもってるのを Amalgam からひかないようにすれば一応大丈夫だけど、たくさん開いてるとタイミング悪く GC がきたりするし……

該当メソッドだけ GC 禁止してもメソッドから抜けた DRb 内で GC 発生すると死ぬし、アドホックな解決はできなそう。

EXC_BAD_ACCESS (0x0001)
KERN_INVALID_ADDRESS (0x0001) at 0xc009aae4

Thread 0 Crashed:
0    _NSAddExceptionHandlerForLock + 261
1    _NSAppKitLock + 59
2    -[NSEvent dealloc] + 122
3    0x27000 + 6206
4    obj_free + 986
5    gc_sweep + 453
6    garbage_collect + 503
7    rb_gc + 11
8    rb_gc_start + 11
9    call_cfunc + 283

開放されちゃだめなのか開放されてるのかなぁ。

PHPを避ける

PHP を たにんに すすめる というのは ぜったいに ありえない。

2007年 11月 13日

再読

日記再読してたらなんかこみあがってくるものがあって、そんな自分に dnbk

2007年 11月 14日

ここ数日

デジャヴが多すぎる。そしてデジャヴのあとのあの、なんともいえない不安感、これからよくないことが起きる予感、みたいなのが、おなかのほうに流れこんでくる。

JavaScript-XPath++

すばらしいなぁほんと

CodeRepos::Share のトップページでも js-xpath が活躍しているよ! (r1409-)

$X の問題と解決と type 指定

なんで変更したか忘れそうなのでメモ ( http://coderepos.org/share/browser/websites/coderepos.org/trac/share/js/TracUtils.js#L77 )

ずっとつかってた $X はノードセットとりたいときに二回 expr.evaluate するダサい問題があるので (書いたときは XPath はやいしー GM だしーいっかーって思ってた)。js-xpath もでたことだし、他のところでも使っていきたいのでとりあえず以下の方法で解決した。

  • type 指定を導入
    • ECMAScript 側で受けとりたい型を指定する。 (Array, String...)
    • XPathResult.BOOLEAN_TYPE とか指定するのはめんどいし覚えられない。
  • type 指定なしの場合は UNORDERED_NODE_ITERATOR_TYPE をそのまま Array に変換してかえす
    • たぶんこれでも殆どの場合は問題ないと思う
    • ノード集合じゃない (number とか) ならそれぞれそのままかえす (いままでとおなじ)
  • type 指定で Array を指定した場合は ORDERED_NODE_SNAPSHOT_TYPE を Array に変換してかえす
    • UNORDERED なやつで特別問題がある場合つかう

おれは type 指定するの好きじゃないので (めんどい) 基本的に指定しなくても問題ないように

2007年 11月 15日

コメント欄の構造変更

本番反映が思ったよりはやかった。どうせやるんだから別にいいんだけどちょっとびっくり。

コミットログ

変更内容だけ書いても (そんなの diff 見ればわかるから) 意味ないんだけど、どうしてもやってしまう。理由を書くのって頭使うんだよなぁ……

2007年 11月 17日

New version of $X

コピペメモ

今までの $X は evaluate を二回する問題があってダサいので、使い勝手をそのままにしつつ新しくして使いはじめました。

  • type 指定を導入
    • ECMAScript 側で受けとりたい型を指定する。 (Array, String...)
    • XPathResult.BOOLEAN_TYPE とか指定するのはめんどいし覚えられない。
  • type 指定なしの場合は UNORDERED_NODE_ITERATOR_TYPE をそのまま Array に変換してかえす
    • たぶんこれでも殆どの場合は問題ないと思う
    • ノード集合じゃない (number とか) ならそれぞれそのままかえす (いままでとおなじ)
  • type 指定で Array を指定した場合は ORDERED_NODE_SNAPSHOT_TYPE を Array に変換してかえす
    • UNORDERED なやつで特別問題がある場合つかう

あんまり type 指定するの好きじゃないので (めんどい) 基本的に指定しなくても問題ないように

(長いのでまだ変更するかも)

// $X(exp);
// $X(exp, context);
// $X(exp, type);
// $X(exp, context, type);
function $X (exp, context, type /* want type */) {
    if (typeof context == "function") {
        type    = context;
        context = null;
    }
    if (!context) context = document;
    var exp = (context.ownerDocument || context).createExpression(exp, function (prefix) {
        var o = document.createNSResolver(context).lookupNamespaceURI(prefix);
        if (o) return o;
        return (document.contentType == "application/xhtml+xml") ? "http://www.w3.org/1999/xhtml" : "";
    });

    switch (type) {
        case String:
            return exp.evaluate(
                context,
                XPathResult.STRING_TYPE,
                null
            ).stringValue;
        case Number:
            return exp.evaluate(
                context,
                XPathResult.NUMBER_TYPE,
                null
            ).numberValue;
        case Boolean:
            return exp.evaluate(
                context,
                XPathResult.BOOLEAN_TYPE,
                null
            ).booleanValue;
        case Array:
            var result = exp.evaluate(
                context,
                XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
                null
            );
            var ret = [];
            for (var i = 0, len = result.snapshotLength; i < len; i++) {
                ret.push(result.snapshotItem(i));
            }
            return ret;
        case undefined:
            var result = exp.evaluate(context, XPathResult.ANY_TYPE, null);
            switch (result.resultType) {
                case XPathResult.STRING_TYPE : return result.stringValue;
                case XPathResult.NUMBER_TYPE : return result.numberValue;
                case XPathResult.BOOLEAN_TYPE: return result.booleanValue;
                case XPathResult.UNORDERED_NODE_ITERATOR_TYPE: {
                    // not ensure the order.
                    var ret = [];
                    var i = null;
                    while (i = result.iterateNext()) {
                        ret.push(i);
                    }
                    return ret;
                }
            }
            return null;
        default:
            throw(TypeError("$X: specified type is not valid type."));
    }
}

あーあーあーあーあーあーー

もうう、すべきことがぜんぜんできない。仕事全然すすまない……

HTML ツリービルダー (Pure DOM)

function h (str) {
	var t, cur, stack = [cur = document.createElement("div")];
	while (str.length) {
		if (str.indexOf("<") == 0) {
			if (t = str.match(/^\s*<(\/?[^\s>\/]+)([^>]+?)?(\/)?>/)) {
				var tag = t[1], attrs = t[2], isempty = !!t[3];
				if (tag.indexOf("/") == -1) {
					child = document.createElement(tag);
					if (attrs) attrs.replace(/([a-z]+)=(?:'([^']+)'|"([^"]+)")/gi,
						function (m, name, v1, v2) {
							child.setAttribute(name, v1 || v2);
						}
					);
					cur.appendChild(child);
					if (!isempty) {
						stack.push(cur);
						cur = child;
					}
				} else cur = stack.pop();
			} else throw("Parse Error: " + str);
		} else {
			if (t = str.match(/^([^<]+)/)) cur.appendChild(document.createTextNode(t[0]));
		}
		str = str.substring(t[0].length);
	}
	return stack.pop().firstChild;
}
window.onload = function () {
	var t = [
		h("<div><img src='http://mixi.jp/favicon.ico'/>hello<span style='color:red'>!</span></div>"),
		h("<div class='test'/>"),
		h("<div class='test'><ul><li>aa</li></ul></div>"),
		h("<div class='test' style='background: #f00'>hogehoe  aaa</div>")
	];
	for (var i = 0; i < t.length; i++) {
		var e = t[i];
		document.body.appendChild(e);
	}
};

jQuery でふつうに HTML かいて要素生成しはじめると、$N("div", {attr}, [childs]) みたいなのがめんどくさくてしかたないので簡単なパーサ書いて生成するようにした。innerHTML 使えよって感じですね。なんで innerHTML つかわないで書いたんだっけ……

  • 実体参照もどしてない。

たぶんおれはパーサーがかきたかったんだ。よくわかんないけど

innerHTML より遅いんだろうなぁとおもってベンチマークとってみた。http://d.hatena.ne.jp/amachang/20060906/1157571938 amachang+=100

まず innerHTML バージョンを定義 (テーブルとかいろいろ考慮してないけど)

function hi (str) {
	var t = document.createElement("div");
	t.innerHTML = str;
	return t.firstChild;
}
benchmark({
	"pure dom"  : function () {
		for (var i = 0; i < 1000; i++) {
			h("<div><img src='http://mixi.jp/favicon.ico'/>hello<span style='color:red'>!</span></div>");
			h("<div class='test'/>");
			h("<div class='test'><ul><li>aa</li><li>bb</li></ul></div>");
			h("<div class='test' style='background: #f00'>hogehoe  aaa</div>");
		}
	},

	"innerHTML" : function () {
		for (var i = 0; i < 1000; i++) {
			hi("<div><img src='http://mixi.jp/favicon.ico'/>hello<span style='color:red'>!</span></div>");
			hi("<div class='test'/>");
			hi("<div class='test'><ul><li>aa</li><li>bb</li></ul></div>");
			hi("<div class='test' style='background: #f00'>hogehoe  aaa</div>");
		}
	}
});
# GranParadiso A8
preparing ...
let's go!
.
*** pure dom ***
result : 2213.987429[ms]
.
*** innerHTML ***
result : 829.987429[ms]
.
finish!

# Safari 3
preparing ...
let's go!
.
*** pure dom ***
result : 1005.991463[ms]
.
*** innerHTML ***
result : 200.991463[ms]
.
finish

# IE 6
preparing ...
let's go!
.
*** pure dom ***
result : 1921.985521[ms]
.
*** innerHTML ***
result : 1342.985521[ms]
.
finish!

# IE 7
preparing ...
let's go!
.
*** pure dom ***
result : 1890.984139[ms]
.
*** innerHTML ***
result : 1312.984139[ms]
.
finish!

# Opera 9.24
preparing ...
let's go!
.
*** pure dom ***
result : 2353.990287[ms]
.
*** innerHTML ***
result : 474.990287[ms]
.
finish!

かなしいですが現実はこんなもんですね。

2007年 11月 18日

gerry++

やばいいたい……

2007年 11月 19日

強いたげられて生きているほうがあってるみたいだ。慣れちゃったんだなこういうのに

twitter にハマるってどういうことだろう

よくわからない。ハマるとかハマらないとかいうものなのかな。例えば「はてダにハマる」とか「LDR にハマる」とかいわないように、「twitter にハマる」っていうのには違和感を感じる。「mixi にハマる」も違和感がある。なんだろう。何にハマるんだろう。

XPathNSResolver のクロスブラウザとか

application/xhtml+xml の場合 (というより XML の場合)、XPath では namespace を持っているノードに対して prefix が必須 *1。すなわち HTML で //body と書いていたやつは //h:body とする必要があり、さらに h を http://www.w3.org/1999/xhtml (XHTML の NS URI) に関連付ける必要がある。

DOM 3 XPath では prefix の解決に XPathNSResolver というのを使っていて、これで prefix を関連づけてやる。

var context = document;
var resolver = function (prefix) {
    if (prefix == "h") return "http://www.w3.org/1999/xhtml";
    return null; # unknown
};
var expr = document.createExpression("//h:body", resolver);

で、これだと text/html のとき (全てのノードは名前空間が空) h:body はなにも選択しなくなってしまって悲しいので条件分岐する。

var context = document;
var resolver = function (prefix) {
    if (prefix == "h") return (document.documentElement.namespaceURI ? "http://www.w3.org/1999/xhtml" : "");
    return null; # unknown
};
var expr = document.createExpression("//h:body", resolver);

document.documentElement.namespaceURI でドキュメントが XML として扱われているかを判別してよさげなのをかえす。


基本的にはこれで十分だけど、XML として処理されるとき、dc: や rdf: とかもちゃんと選択できると嬉しい。毎回 resolver に条件を追加するのもいいけど、dc とか rdf とか、普通 prefix を変えたりしないので、こうする。

var context = document;
var resolver = function (prefix) {
    var c = document.createNSResolver(context).lookupNamespaceURI(prefix);
    if (c) return c;
    if (prefix == "h") return (document.documentElement.namespaceURI ? "http://www.w3.org/1999/xhtml" : "");
   return null; # unknown
};
var expr = document.createExpression("//h:body", resolver);

createNSResolver は Node をあたえると、そのノードが持っている名前 prefix と URI の対応をつかってリゾルバを作る。下位のノードは上位のノードの prefix/URI マッピングも持っている (というかスコープにある prefix は全部とってくる) ので、できるだけ下位のノードをわたす。

だいたい汎用的に使える resolver になった。ただ、xhtml の接頭辞に h を使うかはいまいち同意がとれないので、lookupNamespaceURI でみつからないなら全部 xhtml として扱うことにする。

var context = document;
var resolver = function (prefix) {
    var c = document.createNSResolver(context).lookupNamespaceURI(prefix);
    if (c) return c;
    return (document.documentElement.namespaceURI ? "http://www.w3.org/1999/xhtml" : "");
};
var expr = document.createExpression("//h:body", resolver);

割と嬉しい resolver になった。


ちなみに、いろいろ調べているうちに Opera の非互換を踏んでしまった。Opera (9.24/9.5) では resolver が "" (空文字列) をかえすと、null を返したときと同じように NAMESPACE_ERR を投げてしまう (Safari や Gecko では名前空間が空ということにしてくれる)。つまり Opera だと全適用のユーザスクリプトで XPath つかうときはページが XML か HTML かで式自体を変えないといけないのでめんどい。とかいうのを IRC でごちゃごちゃ言ってたら Kuruma さんに報告してもらった。ありがとう!

あと XML の仕様では「名前空間に関連づいていないノードの名前空間」の名前が定義されていないみたい。no value (Namespace in XML) とかだったり null namespace (XSLT) だったりする。

drry さんが document を createNSResolver にわたすと名前空間解決できないことがあるよっていっていたので (よくわかってなかたw)、さらに追試してみたところ Opera と Fx2 では document をわたしたとき documentElement に解決を委譲しないらしいことがわかった (GranParadiso では修正済らしい https://bugzilla.mozilla.org/show_bug.cgi?id=307465 あと Safari では問題ない)

     case DOCUMENT_NODE: 
          return documentElement.lookupNamespaceURI(prefix) 
http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/namespaces-algorithms.html

この部分が正しく実装されてない。Opera--

*1: 名前空間を持っているノードは prefix なしでは絶対に選択できない

Ruby, Proc#curry

引数全部カリー化したい (一回だけじゃなくて)。可変長引数は考慮しない。

Lambda = Proc
class Lambda
	def curry
		s = self
		args = []
		(1...self.arity).inject(lambda {|x| p args; s[*(args+[x])] }) {|r,i|
			lambda {|x|
				args = args + [x]
				r
			}
		}
	end
end

sum = lambda {|x, y, z| x - y + z }.curry
p sum[1][2][3] # 2
p sum[1][2][3] # args が破壊されて失敗

なんかいろいろやってみたけどうまくいかないので eval に逃げ

Lambda = Proc
class Lambda
	def curry
		s = <<-EOS.gsub(/^¥t{3}/, "")
			lambda {|al|
				args = [#{(1...self.arity).inject(""){|r,i|r<<"a#{i}, "}}al]
				ObjectSpace._id2ref(#{self.object_id})[*args]
			}
		EOS
		eval (1...self.arity).inject(s) {|r,i|
			<<-EOS.gsub(/^¥t{4}/, "")
				lambda {|a#{self.arity-1-i+1}|
					#{r}
				}
			EOS
		}
	end
end

sum = lambda {|x, y, z| x - y + z }.curry
p sum[1][2][3] # 2
p (sum1 = sum[1])
p sum1[2][3]
p lambda {|x, y, z, a| x + y + z + a}.curry[1][2][3][4]


これ GC で死ぬよね。参照保持しないと

もっと簡単に書けた。あと ObjectSpace._id2ref は $SAFE 高いとつかえない。

class Lambda
	def curry
		s = <<-EOS.gsub(/^\t{3}/, "")
			lambda {|al|
				args = [#{(1...self.arity).inject(""){|r,i|r<<"a#{i}, "}}al]
				self[*args]
			}
		EOS
		instance_eval (1...self.arity).inject(s) {|r,i|
			<<-EOS.gsub(/^\t{4}/, "")
				lambda {|a#{self.arity-1-i+1}|
					#{r}
				}
			EOS
		}
	end
end

Ruby チャーチ数

そのまま実装してみます。チャーチ数は自然数 n の場合、引数 f をとり、引数 x に対し f を n 回適用する関数を返す高階関数らしいです。

\f x -> x       -- 0
\f x -> f x     -- 1
\f x -> f (f x) -- 2

数としてもどしたいときは

-- cn はチャーチ数
cn (\x -> x + 1) 0

とかやるとたぶん数になります。

class ChurchNumber < Lambda

	# ¥m n f x -> m f (n f x)
	def +(m)
		n = self
		m = m.to_church_number
		ChurchNumber.new {|f|
			lambda {|x|
				m[f][n[f][x]]
			}
		}
	end

	# ¥n f x -> f (n f x)
	def succ
		n = self
		ChurchNumber.new {|f|
			lambda {|x|
				f[n[f][x]]
			}
		}
	end

	def to_i
		self[lambda {|x| x + 1 }][0]
	end

	def coerce(number)
		[number, self.to_i]
	end

	def inspect
		# "#<%s:0x%08x:%d>" % [self.class, self.object_id, self.to_i]
		"#<%s:%d>" % [self.class, self.to_i]
	end

	def to_church_number
		self
	end
end

class Integer
	def to_church_number
		n = self
		ChurchNumber.new {|f|
			lambda {|x|
				(1..n).inject(x) {|r,i| f[r] }
			}
		}
	end
end

p 1.to_church_number      #=> #<ChurchNumber:1>
p 1.to_church_number.succ #=> #<ChurchNumber:2>
p 2 + 1.to_church_number  #=> 3
p 2.to_church_number + 1  #=> #<ChurchNumber:3>

(0..10).each do |i|
	p [i.to_church_number, i.to_church_number.to_i]
end

ラムダ式の読みかた

わかってなくて苦労したけどようはλを \ にして . を -> にすれば Haskell の記法として読めるみたいだ (普通逆なんだろうけど)

Lazy K の IO リスト

Lazy K のプログラムは Input リストを引数にとり、Output リストを出力する関数とみることができます。でもって空の Lazy K プログラムは I として扱われるため (というか何か書くと I の引数として評価されてく) Lazy K での cat は 0 byte になります。(Input をそのまま Output するので I でいい)

Lazy K のリストは Lisp のようにペアのいれこになっていて、一つのペア (X . Y) は (lambda (f) (f X Y))) と表現するらしいです。

Ruby で Lazy K の Input をそのまんま実装すると

def inputlist(l)
	lambda {|f|
		f[(l.first || 256).to_church_number][inputlist(l[1..-1] || [])]
	}
end

il = inputlist([72, 101, 108, 108, 111])
S = lambda {|x, y, z| x[z][y[z]] }.curry
K = lambda {|x, y| x }.curry
I = lambda {|x| x }

car = il[K]    #=> チャーチ数 72 (car il)
cdr = il[S[K]] #=> (cdr il)
cdr[K]         #=> チャーチ数 101 (car (cdr il))

こんな感じになりました。 Lazy K の Input リストは無限リスト (EOF 以降は 256) になっています。

2007年 11月 20日

ウェブサービスの XHTML 化

ユーザの入力をどうするか

  1. HTML を書けない記法しか許さない
  2. HTML を書けるがフィルタでとにかく well-formed にする

hpricot つかえば簡単に well-formed で XHTML にできてしまうなぁ。しかもはやい……

Hpricot(html, :xhtml_strict => true)

一部のHTML要素のみ通すフィルタ

ちがうちがうおれはなにをやっているんだ……

require "strscan"

# HTMLFilter
# 許した要素のみ残しながら、タグの対応を補完する
class HTMLFilter
	ESCAPE = { '<'  => '&lt;', '>'  => '&gt;', '"' => '&quot;' }

	EMPTY_ELEMENTS = ["br", "hr", "img"]

	def initialize(allow, callback=proc {|x| x})
		@allow = allow
		@callback = callback
	end

	def filter(input)
		ret = ""
		pool = ""
		s = StringScanner.new(input)
		elements = []
		until s.eos?
			if s.scan(%r{<(/)?(#{@allow.keys.join("|")})}i)
				ret << @callback[escape(pool)]
				pool = ""
				name = s[2]
				if s[1]
					# end tag
					s.scan(%r{¥s*>})
					while opened_but_close = elements.pop
						ret << "</#{opened_but_close}>"
						break if name == opened_but_close
					end
					# remove the end tag if it was not opened.
				else
					# start tag
					attrs = []
					while s.scan(%r{¥s+([a-z-]+)=(?:"([^"]*)"|'([^']*)')}i)
						an = s[1]
						av = s[2] || s[3]
						attrs << "#{an}='#{escape(av)}'" if @allow[name].include?(an)
					end
					attrs = attrs.empty?? "" : " #{attrs.join(" ")}"
					empty = false
					if s.scan(%r{¥s*(/)?>})
						case
						when EMPTY_ELEMENTS.include?(name)
							empty = true
							ret << "<#{name}#{attrs} />"
						when s[1]
							empty = true
							ret << "<#{name}#{attrs}></#{name}>"
						else
							ret << "<#{name}#{attrs}>"
						end
					else
						# invalid but continue
						ret << "<#{name}#{attrs}>"
					end
					elements.push(name) unless empty
				end
			else
				pool << s.getch
			end
		end
		ret << @callback[escape(pool)]
		ret << "</#{opened_but_close}>" while (opened_but_close = elements.pop)
		ret
	end

	def escape(str)
		str.gsub(/#{ESCAPE.keys.join("|")}/) {|m|
			ESCAPE[m]
		}
	end
end

opts = {
	"a"      => ["href", "name"],
	"strong" => [],
	"br"     => [],
	"p"      => [],
	"ins"    => ["datetime"],
	"del"    => ["datetime"],
}

inputs = DATA.read
out = HTMLFilter.new(opts, proc {|str|
	str.gsub(/¥n/, "<br />¥n")
}).filter(inputs)

puts out



__END__
<script foo="<script>alert('bar')</script>">alert('foo')</script>
<script foo="<a href='link'>link</a>">alert('foo')</script>
<a href='www.g>oogle.com'>link</a>
<a href="hoge" name="aaa">hoge<strong style="">ttt</strong></a>
<a href="hoge" name="aa
{a">hoge<br><br/></a>

<ins datetime="">
<p>
aaa
</p>

<p>aaa

</ins>

<a></strong>
<p>aaa

どう書く?org のお題 をやっていたんだった。なんかだんだんズレてきたので投稿がためらわれる。改行を br にしろというお題 に対応ずみ。

Firefox 3.0 beta1

いれた!

ロケーションバーは重いままだなぁ。ヒストリがでかすぎるのかなぁ

2007年 11月 21日

Hpricot で一部の要素のみ通すフィルタ

require "rubygems"
require "hpricot"

def html_filter(parent, opts, &block)
	block = Proc.new {|x| x} unless block
	def escape(str)
		_escape = { '<'  => '&lt;', '>'  => '&gt;', '"' => '&quot;' }
		str.gsub(/#{_escape.keys.join("|")}/) {|m| _escape[m] }
	end

	ret = ""
	parent.each_child do |c|
		if c.elem?
			empty = Hpricot::ElementContent[c.name] == :EMPTY
			if opts.key?(c.name)
				ret << "<#{c.name}"
				ret << c.attributes.select {|k,v| opts[c.name].include? k}.map {|k,v| %| #{k}="#{escape v}"| }.join
				ret << "#{empty ? " /" : ""}>"
				ret << html_filter(c, opts, &block)
				ret << "</#{c.name}>" unless empty
			else
				ret << "&lt;#{c.name}"
				ret << c.attributes.map {|k,v| %| #{k}="#{escape v}"| }.join
				ret << "#{empty ? " /" : ""}>"
				ret << html_filter(c, opts, &block)
				ret << "&lt;/#{c.name}>" unless empty
			end
		else
			ret << block.call(c.to_s)
		end
	end
	ret
end

doc = Hpricot(inputs)
puts doc
puts html_filter(doc, opts) { |text|
	text.gsub(/¥n/, "<br />¥n")
}

こんなかんじかなぁ。ちょっときもいけど……


関係ないけど Mac でバックスラッシュをコピーして Fx にペーストすると円マークになってこまる。そのままペーストしてるけど、あとからコピペしようとすると使えないんだよね……

Ruby 1.9 のイテレータ (ちょーすごい)

で、そんなことより Mochkit.Iter の Synopsis の例を Ruby でやろうとおもったらいろいろ足りない感じ

(Enumerator は http://subtech.g.hatena.ne.jp/secondlife/20071104/1194144583 でちゃんと紹介されているよ! )

theSum = sum(takewhile(
        partial(operator.gt, 10),
        imap(
            partial(operator.mul, 2),
            count()
        )
    )
));

assert( theSum == (0 + 0 + 2 + 4 + 6 + 8) );

これこれ。

class Numeric
	def countup
		(self..(1.0/0)).to_enum
	end
end

module Enumerable
	def imap(&mapfun)
		Proc.new {|&block|
			self.each do |i|
				block.call mapfun.call(i)
			end
		}.to_enum(:call)
	end
end

# (sum はぬかしてる)
p 0.countup.imap {|x| x * 2}.take_while {|x| x < 10}

imap はなんか適当に書いてみたらこうなってしまったんだけどもっと簡単にかけたりするのかな…… 演算子の partial は定義しないといけないのと、ブロック書いたほうがはやいのでとりあえずとばし


countup はもっと簡単に書けないかとおもったけど微妙にめんどいっぽい。id:secondlife さんにもきいた。

# Float になっちゃう
p 0.to_enum(:step, 1.0/0).take(10)

# みじかいけど 1.0/0 (Infinity) がわかりにくい
p (0..(1.0/0)).to_enum.take(10)

# ながいよ
p lambda {|x,&block| loop { block.call(x); x += 1 } }.to_enum(:call, 0).take(10)

# Float::MAX が無限っぽくない
p 0.to_enum(:upto, Float::MAX).take(10)
2007年 11月 22日

Fx3

http://lowreal.net/logs/2004/12/04/3 がなおってる。

2007年 11月 23日

Fx3 対応の AutoPagerize

何度か AutoPagerize の書きかえに挑戦して挫折 (GP にうつってから AutoPagerize なしで生きてた) しているのでゼロから書きなおした。まだ途中だけど30%ルールぐらいでコミット (はてダ/はてグぐらいしか確認してないんだぜ……)

http://coderepos.org/share/browser/lang/javascript/userscripts/jautopagerize.user.js?

Mochikit の Deferred っぽいのをとおしてリソースのキャッシュとかを綺麗に書けるようにしてみた。コンパクトにしようとおもってたら API がちょっとキモくなってしまった。(メソッドチェインできることと、addCallback みたいに camelCase のどこを大文字にするのかわからないような名前をつけないことには気をつけた)

Fx3 の JS (JS1.8) には reduce があって嬉しいですね。あと 1.6-1.8 追加の関数は MDC に等価コードがのっているので、他のブラウザになくてもコピペすれば動くようになるので使いまくれる。

確認したかぎり最新の Greasemonkey (0.7.20071121.0) をつかってる。普通の GM だとまだ Fx3 にインストールできない。このバージョンでも locale が ja-JP だと動かないので en-US にしないとだめ。


セキュリティ関係で unsafeWindow.console.log とかできないっぽいのでデバッグがかなりめんどうくさい。深追いしてないからできるのかもしれないけど……あと GM_log も動いてないっぽい。


Safari (GreaseKit) とか Opera でも動くようにしたいのであんまり Fx 特化の技はつかいたくない (XSLT つかって HTMLDocument つくるとか……BK すぎる……)

console.log へ吐く方法はみつけた。

	function log (m) {
//		var c = unsafeWindow.console;
//		if (c) c.log.apply(c, arguments);
		var o = Array.prototype.concat.apply([], arguments);
		location.href = "javascript:(function () { if (window.console) console.log.apply(console.log, "+o.toSource()+") })();";
	}

カッコではじまる XPath はうまくいかない。根本的な解決はむずかしい。

なんで nextLink は属性値まで指定させる方法じゃないんだろ。form/@action とかのやつも siteinfo にそのまま書けるのに……

script 要素のタグは削除してるけど内容を削除してないのではてブ人気エントリとかでゴミが表示されてしまう。実害ないからとりあえず放置

AppleScript / RubyOSA++

require "rubygems"
require "rbosa"

safari = OSA.app("Safari")
safari.do_javascript("alert(location.href)", safari.documents[0])

jautopagerize の Safari 対応がなんかむりくさい感

toSource と reduce を実装して iframe で document とるようにするところまではうまくいくのだけど、そのあとなんか設定されているはずの変数が undefined になったりして意味不明すぎる (たまにうまくいく)。それにアクセスすると Safari がフリーズする。GC がおかしいのかなぁ。

なんかもっと別の方法考えないとだめなのかもしれない。(Safari は innerHTML すると html/body/link とか消してしまうので使えない)

ちなみに Fx3 で iframe つかって document とらないのはとれないからなんだけど、とる方法あるのかな。とれるならそのほうが簡単なんだけど

2007年 11月 24日

シンクロニシティ

捨てネコらしきネコが家の庭にきていすわって対処に困っているのだけれど、いろいろありつつ飼うことになりそうな流れになっている。とかいう感じで名前つけるならなんだろうなぁ、とか考えてマリーとかどうよとか思ってたんだけど、なぜか名前の話なんて全くしていないのに母親と案がまるかぶりした。

余談ですが、今メインでつかっている MacBook のマシン名が Mary です。母親はマリーゴールドからとったようです。

jautopagerize Safari (GreaseKit) 対応

かなり初期のころの挙動にもどした。innerHTML つかうのと変わらないなぁけっきょく。はてダみたいに head/link@href を見てるやつだと2ページ目以降とれない。ふつうのサイトは body 以下にも同じリンクがあるから、siteinfo のほうを変更することで対処できるけど……

あーなんかほとんどうごいてないなぁ。もういいや……Safari とか使わないし

Safari とは関係ないけどごちゃごちゃやってるうちに Fx + Google でちゃんと動くようになった……

2007年 11月 25日

デザイン (笑)

バズワード化してる。もとからかw

2007年 11月 24日

XSLTProcessor

Fx で HTMLDocument つくるのに XSLTProcessor つかうようにしたんだけど、よくわからない条件でエラーがでる。

[Exception... "Component returned failure code: 0x80600001 [nsIXSLTProcessor.importStylesheet]" nsresult: "0x80600001 ()"

div + innerHTML + XPath 改変のほうが安定しそうだなぁ……なんでこのへんマトモな実装してないんだろ。意味不明すぎ (Safari3 と Opera には createHTMLDocument がある)

XSLTProcessor つかうのやめた。はてダででるんだよなぁ (はてグなら大丈夫)
id() についてはもともと Safari 向けのコードで //*[@id=''] に文字列置換するのを書いてる。二個以上 id 指定があるとだめだけどそんな使いかた普通しないから無視

Fx に createHTMLDocument がないのは createDocument に doctype わたせばいいってことらしいけど (というか DOM の仕様にないのはそういうことなんだろうけど)、

var html4dt = document.implementation.createDocumentType("html", "-//W3C//DTD HTML 4.01//EN", "http://www.w3.org/TR/html4/strict.dtd");
alert(document.implementation.createDocument("", "html", html4dt));

とかやっても普通に XMLDocument になるよ。イミフ。これってバグじゃないのかなぁ。でも Bugzilla には関わりたくない。

memo

  1. http://mxr.mozilla.org/firefox/source/content/base/src/nsDocument.cpp#706
  2. http://mxr.mozilla.org/firefox/source/content/base/src/nsContentUtils.cpp#3319
  3. http://mxr.mozilla.org/firefox/source/content/xml/document/src/nsXMLDocument.cpp#102

普通に XML の処理しか書いてない。

2004 年報告のやつだけどまだ NEW だ。NEW の意味わすれたけど

2007年 11月 25日

Ruby のブロック

Ruby のブロックは実のところ proc {} を引数に渡すのを、その proc が一つの場合に限って簡略化して書けるだけなのだけど (実際はちょっと違うっぽいけど)、これがかなりうまくいってるのがおもしろいなぁ。

  • return を書かなくていい
  • ブロック (というか評価されないメッセージ) を簡単に渡せる

ってのはすごく重要だなぁ。

class TrueClass
    def then
        @ret = yield
        self
    end

    def else
        self
    end
end

class FalseClass
    def then
        self
    end

    def else
        @ret = yield
        self
    end
end

(foo == bar).then {
    puts "hoge"
}.else {
    puts "fuga"
}

ふつうに綺麗に書ける。

Boolean.prototype.then = function (fun) {
	// this は Object になっているので true になってしまう。
       // もとの値をとりだすために valueOf()
	if (this.valueOf()) fun();
	return this;
};

// else は予約語なのでつかえない
Boolean.prototype.els = function (fun) {
	if (this.valueOf()) fun();
	return this;
};

("foo" == "bar").then(function () {
	print("hoge");
}).els(function () {
	print("fuga");
});

ぜんぜん綺麗じゃない。


ちなみに Io だと if([pred], [then], [else]) 以外にもうえのと同じことがデフォでできる。(Io では引数は基本的に評価されない。Message オブジェクトとしてメソッドにわたる。ただし仮引数を書いた場合は自動で評価される)

("foo" == "bar") then (
	"hoge" println
) else (
	"fuga" println
)

. ではなくスペースをメッセージ送信のくぎりにすることはこういうところで生きてきますね。一見したら構文にしか見えない。(if は then/else の評価されたほうの値をかえすけど、else は nil をかえす。then はもちろん self をかえしている)

ラムダ計算 / オブジェクト指向

ラムダ計算では true/false はもちろん関数で、こんなかんじらしい (true は x と y を引数にとり x を返す関数で、false は y を返す関数)

t :: a -> b -> a
t x y = x

f :: a -> b -> b
f x y = y

main :: IO ()
main = t (putStrLn "hoge") (putStrLn "fuga")

Haskell で書いたはいいけど、あんまわかりやすくないなw (引数は評価されるけど、副作用はモナドでラッピングされてる(?)から、かえされた IO モナドインスタンスが保持してる putStrLn "hoge" しか表示されないってことだとおもうけど、というか用語がすでにあやしい。Haskell こわい)

(define (t x y) x)
(define (f x y) y)

((t
  (lambda () (print "hoge"))
  (lambda () (print "fuga"))))

Scheme で書くとこんなかんじかな。Haskell より綺麗に書けないけど、なんかまちがってるかな……(引数は評価されてしまうので lambda でかこみ、t の返り値をさらに呼びだしてる)

まぁどっちにしろこれってどことなく OOP で書いた

true then (
	"hoge" println
) else (
	"fuga" println
)

に似ていて、

true if := method(
	call evalArgAt(0)
)

false if := method(
	call evalArgAt(1)
)

("foo" == "bar") if (
	"hoge" println
,
	"fuga" println
)

とかくともっと似てますね! と最近思ったのでした。(もしかしたらあたりまえなのかもしれないけど、じぶんとしては、やっと気付けたことなのでした)

似ているからなんなの? っていうのはまだうまく言語化できない。なんというか、「ああそっかラムダ計算もオブジェクト指向も純粋だと似てくるんだなぁ」みたいな、よくわからないけど

あー Ruby で書くともっとわかりやすいかなぁ……

# lambda
t = lambda {|x, y| x }
f = lambda {|x, y| y }

t[lambda {
	puts "hoge"
}, lambda {
	puts "fuga"
}][]

# OOP
class TrueClass
	def if(x, y)
		x[]
	end
end

class FlaseClass
	def if(x, y)
		y[]
	end
end

true.if(Proc.new {
	puts "hoge"
}, Proc.new {
	puts "fuga"
})

ラムダのほうは似せるために curry 化してない。curry 化するとラムダのほうはもっと綺麗にかける。

# http://subtech.g.hatena.ne.jp/cho45/20071119/1195420784
t = lambda {|x, y| x }.curry
f = lambda {|x, y| y }.curry

t[lambda {
	puts "hoge"
}][lambda {
	puts "fuga"
}][]

curry 化バージョンは Scheme と全く同じことをやってる。

というか Ruby は表現しやすいなぁ。Io や Scheme や Haskell は初見だと読めないし……Ruby にしとくと関数系の人もOOP系の人も読めていいかんじがする。

ほげほげ

OOP ← Io - Ruby - Scheme - Haskell → ラムダ

この4つの言語やるとなんかいろいろわかる気がしてきた。JavaScript は return かかないといけないからここには入れたくないな (OOP にしたって関数にしたって return かかないといけないのは害にしかならない)

ラムダきわめると Lazy-K になるっぽいけど、OOP (メッセージセンド) を極めるとどうなるんだろう。Io でいいのかな。たしかに全部メッセージセンドだけど、違和感ある。なんでだろう。

関数型とOOPと書き順

頭にうかんだ順に書けるぶん、自分は関数的に書くより OOP で書くほうが好きだ。

# http://subtech.g.hatena.ne.jp/cho45/20071121/1195642614
0.countup.imap {|x| x * x }.take_while {|x| x < 10}
import Text.Show

countup = iterate (\x -> x + 1)

main :: IO ()
main = putStrLn . show $
       takeWhile (< 10) $ map (\x -> x * x) $ countup 0

メソッドチェインだと前から順番によんでいけるけど、関数よびだしだと頭の中のスタックに前から順につんでポップしていかないといけない。

関数系の人はたぶん違う方法で考えているんだろうなぁ……

でも関数の部分適用とかはたのしい うえのコードの (< 10) とか、countup の定義とかすばらしくたのしい。

あーいやうーん。OOP と関数の比較っていうのとは違うかんじもする。

Leopard ほしい!

http://rubycocoa.sourceforge.net/RubyInject

これのせいで俄然 Leopard が欲しくなった。

ほしい言語

たぶんプロトタイプベースな Ruby がほしいのかもなぁ。

self #=> Global env. Object

hoge = "hoge"
self.hoge = "hoge"
Global.hoge = "hoge"
Global.Global #=> Global

Global.proto #=> Object
Global.fun   #=> fun {|&block| [native] }

Object.foo = fun {
    self #=> a env. object having receiver as proto 
}

Array.map = fun {|&f|
    ret = []
    self.each {|i|
        ret << f(i)
    }
    ret
}

String.each = fun {|&f|
    split("").each(&f)
}

String.map = Array.slot("map")
String["map"] = Array.slot("map")
String.set_slot("map", Array.slot("map"))
String["[]="]("map", Array.slot("map"))

Array.clone.set(1, 2, 3) == [1, 2, 3] == Array.with(1, 2, 3)

o = Object.clone.do {
    self #=> just the `o`
    hoge = "hoge"
    self.foo = "foo"
    fuga = fun {|x|
        "fuga"
    }
}
o.hoge #=> "hoge"
o.foo  #=> "foo"
o.fuga #=> "fuga"
o.slot("fuga") #=> fun {|x| "fuga" }

Array.sum = Array.slot("inject").partial(0) {|r,i| r + i}
[1, 2, 3].sum #=> 6

括弧の省略をしても呼びだしされるあたりがなんかうまくいかなそう。文法の矛盾があったりして、寝ておきたら「だめじゃんw」と思うんだろうなぁ。

Ruby を書いててときどき、 JS みたいに関数を自由にあつかいたいと思う。Ruby はクラスベースで、メソッドはかならずどこかのクラスに属し、UnboundMethod は bind しないかぎりよべず、bind の対象ももとのクラスと関係がないといけない。だから……だからなんだろう……


うまくまとまらない。

したいこと

  • 環境 (変数をセットする場) もオブジェクトにして書きかえしたい
    • スコープチェイン == 環境オブジェクトのプロトタイプチェイン
  • 関数/メソッドはとりだしてつけかえたりしたい。
  • 関数/メソッドを部分適用したい
  • メソッドよびだしの括弧は省略したい
  • return かきたくない
  • 関数を簡単に関数にわたしたい。
  • 正規表現リテラルはほしい。
  • ダックタイピングしたい
  • if とか case とかは構文としてあっていい (無理にメッセージセンドにしない)
  • 関数はメソッドと同じように呼びだしたい。

Greasemonkey + Firebug + GM_log

あーふつうに GM_log つかえてた……

Firebug 1.0 以降では extensions.firebug.showChromeMessages を true にしないと GM_log のメッセージが表示されないらしい。true にしたらちゃんとでた……

uneval

toSource のラッパっぽい uneval っていう関数があるみたいだ (いつからあるんだろ)。toSource は null とか undefined のときエラーになるけど (オブジェクトじゃないから) これだとならない。

uneval(null); //=> "null"
uneval({1:undefined}); //=> "({1:(void 0)})"

http://malblue.tumblr.com/post/20163446

かなりまえからあるらしい。(1.5) String.prototype.quote とかいうのもあるらしい。Ruby の String#dump 相当かなぁ

2007年 11月 26日

なんもかけない

ひさしぶりに何か想像を書こうと思ったのだけれど書けなかった……

2007年 11月 25日

jAutoPagerize が Safari で動くようになったよ

贅沢言わなければ動くようになったよ。Safari の innerHTML 系メソッドは link, meta も消しちゃうので、それを nextLink に指定していなければ動くよ。ただ、なぜかユーザスクリプトが実行されないページがあるので要検証

http://userscripts.org/scripts/show/14666


「もういいや」とか書くとなんか続けたくなる謎

2007年 11月 26日

jAutoPagerize のあれそれ

position: absolute で段組されてるところだと document.documentElement.scrollHeight がすごく小くなってしまい、ページがどのぐらい残っているか取得できない。

あと GM_xmlhttprequest をつかうべきじゃないかも。ページングがクロスサイトすることなんてまずないし、無用な特権は行使しないほうがいいよね。

右クリックで XPath とるやつ userChrome.js

userChrome.js + http://www.ne.jp/asahi/nanto/moon/2006/12/31/copy-url-lite.uc.js + Firebug

  {
    label: "Copy XPath",
    accesskey: "X",
    get text () {
        return FBL.getElementXPath(document.popupNode);
    }
  }

をつけたして C-n して新しいウィンドウを開く /html/body/div[2]/div[2]/form/div/textarea

ただし、Fx の場合 thead とかが存在しなくても DOM 上では thead を補完するので、そのままでは他のブラウザでとおらないこともあるとおもう (Google のページングとかそうだけど AutoPagerize 的なときに困るのでちゃんと確認してほしい)。

libxml-ruby の名前空間の解決について

http://d.hatena.ne.jp/keisukefukuda/20071126/p1

Perl と同じように (つまり libxml そのまま) ではありませんが以下のようにしてできます。

require "rubygems"
require "xml/libxml"

require "tempfile"

f = Tempfile.new("xml")
f << <<EOS
<?xml version="1.0" encoding='utf-8'?>
<service xmlns="http://www.w3.org/2007/app"
         xmlns:atom="http://www.w3.org/2005/Atom">
  <workspace>
    <atom:title>Main Site</atom:title>
    <collection href="http://example.org/blog/main">
      <atom:title>My Blog Entries</atom:title>
      <categories href="http://example.com/cats/forMain.cats" />
    </collection>
  </workspace>
</service>
EOS
f.close

doc = XML::Document.file(f.path)

namespaces =  {
	"app" => "http://www.w3.org/2007/app",
	"atom" => "http://www.w3.org/2005/Atom"
}.to_a

p doc.find('/app:service/app:workspace/app:collection/atom:title', namespaces)
#=> <atom:title>My Blog Entries</atom:title>

namespaces にわたすのが配列なのが微妙にひっかかる。(ドキュメントよむと "prefix:uri" みたいな文字列でもわたせるけど圧倒的なキモさなので使わないとおもう)

2007年 11月 27日

jAutoPagerize / Bug in Safari

twitter とか、prototype.js をつかっているサイトだと Array#reduce がうわがきされるうえに挙動がちがっててうごかない。

こっちの prototype 拡張をやめる (関数よびだしにする) っていうの以外で解決できるならするけど、できないならやらない。

jAutoPagerize

http://coderepos.org/share/changeset/2041

ごくごく簡単な XPath ジェネレータ/チェッカをくみこんでみました (Safari でのデバッグ用途が主目的)。siteinfo つくるなら基本的に http://labs.gmo.jp/blog/ku/2007/07/autopagerizexpath_autopagerize_ide.html をつかえばいいとおもうのでいらないと思います (さっきまで AutoPagerize IDE つかったことなかったw こりゃ便利ですね><)。

jAutoPagerize のアイコンをクリックするとテキストエリアがでます。Inspect ボタンをおすと要素選択して XPath を生成します。テキストエリア編集すると選択ノードにアウトラインがつきます。Safari でも動きます。jAutoPagerize のアイコンが表示されてないところでは javascript:XPathGenerator.open(); するとでてきます。

ノードから XPath つくるやつはちょー簡単に生成するのを前書いていたらしくて (test.user.js で getXPath... までうちこんだら autocompletepop された……) それをちょい改善してつかいました (ほとんどテストしてないからバグってるかも)。

		getXPathByElement : function (target) {
			var pathElement  = "";
			var node = target;
			if (node.nodeType == 9 /*DOCUMENT_NODE=9*/) {
				return ""
			} else {
				var tagName = node.tagName.toLowerCase();
				if (node.hasAttribute("id")) {
					// pathElement = tagName + '[@id="'+node.getAttribute("id")+'"]';
					pathElement = 'id("'+node.getAttribute("id")+'")';
				} else {
					pathElement = arguments.callee(node.parentNode) + '/' + tagName;
					if (node.hasAttribute("class")){
						pathElement += '[@class="'+node.getAttribute("class")+'"]';
					} else {
						pathElement += '['+indexOf(node)+']';
					}
				}
			}
			return pathElement;

			function indexOf (node) {
				for (var i = 0, r = 1, c = node.parentNode.childNodes, len = c.length; i < len; i++) {
					if (c[i].nodeName == node.nodeName &&
						c[i].nodeType == node.nodeType) {
						if (c[i] == node) return r;
						r++;
					}
				}
				return -1;
			}
		}

Regexp $1 $2

Ruby にしても JS にしても $1 $2 は、それらがどっから沸いてきたのかソースコードをうえからよんでいったときにわかりにくいので、できるだけ使たくない。暗黙的に代入されているし、$1 $2 っていう名前には全くマッチを連想させない (慣れればわかるけど……あと JS の場合は RegExp.$1 とかになるのでいくぶんマシ)。しかも $0 は実行中のプロセス名だから $0 と $1 の間に同じ $n なのに全く関連がない。

よっぽどめんどくさくない限りは Regexp.last_match したり m = str.match(r); する。(Perl のときは気にしない)

IMG_61970088

Safari でのあれ

GreaseKit だと同じウィンドウで userjs が実行されるので、常に prototype 上書きされる危険性があって、これはどうしよもない。普通は標準の関数をわざわざうわがきしたりしないから問題ないけど、前述の Array#reduce みたいに、微妙な立場の関数でしかも名前がよくつかわれるやつは上書きされやすい。(というか実際には Array#map も上書きされているけど、こっちは挙動が同じなので問題になってないだけ)

Opera もたぶん同じウィンドウで実行されるから同じ問題をもっていると思うけど、あんまり調べてない。


解決法:

  1. 全部関数形式でよんで、全部の関数を独自で実装する (むり)
  2. JS 1.6, 1.8 の関数は関数形式にして独自で実装する (妥協案)
  3. きりがないので放置
  4. GreaseKit 側に手をいれて別ウィンドウでの実行にする (むずかしそう)


あとたぶんこういう関係 (unsafeWindow的な意味で) jAutoPagerize + Safari + GreaseKit だと XSS ふんでそうな感じがするのでチェック必要だと思う。(ちょっと考えたけどとれちゃうな…… http://coderepos.org/share/changeset/2050 修正した。ほかにもある予感……)

(Greasemonkey だとウィンドウが別れているのでだいだい問題おきない。なぜ unsafeWindow が GM にはあって、他のにはないのかをよく考えて使わないとだめですね><)

あれ r2050 じゃ解決になってないよね…… siteinfo を外からアクセスできるようにしたらだめだな。

r2051 でたぶん解決。Safari でつかうのは危険なかおりがプンプンする。

Firefox での HTMLDocument 生成 (XSLT つかう BK)

nanto_vi さんのコメントで原因が判明したので試行錯誤してコミットした。

http://coderepos.org/share/changeset/2053

var x = new XSLTProcessor();
var t = [
	"<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>",
		"<xsl:output method='html'/>",
		"<xsl:template match='/'>",
			"<html><head><title>", title, "</title></head><body/></html>",
		"</xsl:template>",
	"</xsl:stylesheet>",
].join("");
var d = document.implementation.createDocument("", "nice-boat", null);
var r = d.createRange();
r.selectNodeContents(d.documentElement);
d.documentElement.appendChild(r.createContextualFragment(t));
x.importStylesheet(d.documentElement.firstChild);
var ret = x.transformToDocument(d);
  1. DOMParser をやめた。
  2. そもそも二回 DOMParser していた部分を一個にした (かったぽの XML はなんでもよかったので XSLT つくるのに使った Document をそのままつかう)
  3. 生成結果が HTMLDocument っぽくないなら捨てる。

jAutoPagerize キャッシュ関係

なんかしばらく新しく siteinfo をとりにいってキャッシュするところがうごいてなかったっぽい。なおした。

でも Safari だといまいち挙動不審で、たまにsiteinfo 全部 ({}) になってしまうことがある。textarea.replace(//, function () {}) がへんなのかも……めんどい

uneval を適当に実装してテストしてたらきづいた。

2007年 11月 28日

Ruby1.9 のラムダさん

やばいやばい。知らなかった。手元で既に使えるシンタックスっていう! (でも将来使えるかわからない予感) id:secondlife さんに教えてもらった。id:secondlife++ あと parse.y をながめてやりかたを覚えたw

この前の true/false のラムダを書いてみる。

# curry は http://subtech.g.hatena.ne.jp/cho45/20071119/1195420784

# 1.8
t = lambda {|x, y| x }.curry
f = lambda {|x, y| y }.curry

t[lambda {
	puts "hoge"
}][lambda {
	puts "fuga"
}][]

#1.9 head
t = -> (x, y) { x }.curry
f = -> (x, y) { y }.curry

t[-> {
	puts "hoge"
}
][-> {
	puts "fuga"
}][]

# or
t.
(-> {
	puts "hoge"
}).
(-> {
	puts "fuga"
}).()

やばすぎる萌えすぎる。


メモ

t = -> (x, y) { x }
t = -> x, y { x }   # 括弧いらないらしい

(1..3).map {|x| x * x }
(1..3).map(&-> x { x * x }) # このつかいかたはあんましなそう?
(1..3).map &-> x { x * x }  # おなじ

car = -> x,*xs { x }
cdr = -> x,*xs { xs }
p car[10, 20, 30] #=> 10
p cdr[10, 20, 30] #=> [20, 30]

car = -> ((x,*xs)) { x }
cdr = -> ((x,*xs)) { xs }
p car.([10, 20, 30]) #=> 10
p cdr.([10, 20, 30]) #=> [20, 30]

どういうとき一番うれしいかなぁ。ラムダ計算のやつは普通書かないし

hoge = ->&b{ b.yield b }

hoge.() {|x|
	p x.lambda? #=> false
}

hoge.(&->x{
	p x.lambda? #=> true
})

これ記法によって挙動が変えられるってことで夢ひろがりんぐだけど、実際変わるメソッドがあったりするとびびりそう。どういう意図で入ってるんだろう。

Ruby1.9 のラムダさん 2 (SKI とチャーチ数 256)

#!ruby1.9 -v

s = -> x {-> y {-> z { x.(z).(y.(z)) } } }
k = -> x {-> y { x } }
i = -> x { x }

t = k
f = s.(k)

car = -> list { list.(t) }
cdr = -> list { list.(f) }

car = s.(i).(k.(t))
cdr = s.(i).(k.(f))

# ((lambda (n) (n n)) ((lambda (n) (n n)) (lambda (f x) (f (f x)))))
cn256 = -> n { n.(n) }.(-> n { n.(n) }.(-> f {-> x { f.(f.(x))}}))
# SII(SII(S(S(KS)K)I))
cn256 = s.(i).(i).(s.(i).(i).(s.(s.(k.(s)).(k)).(i)))

inputlist = -> list {
	-> f { f.(list.first || cn256).(inputlist.(list.drop(1))) }
}


cn2num = -> cn { cn.(-> x { x + 1 }).(0) }
num2cn = ->  n { -> f {-> x { (1..n).reduce(x) {|r,_| f.(r) } } } }

il = inputlist.([72, 101, 108, 108, 111].map(&num2cn))
p cn2num.(cn256)
p cn2num.(car.(il))
p cn2num.(car.(cdr.(il)))

Ruby のコードに見えないすぎるw

GDHM

黄金の鐘の歌詞があとかたもない。ハレルヤのところより看守は奴隷にパンを売りつけのところがなくなったのがすげー残念だなぁ。

2007年 11月 29日

門田匡陽

なんかこう、ずっとひっかかってたけど、門田氏とRitaさんが微妙に唄いかたが似てることに気付いた。黄金の鐘聴いてて思った。

2007年 11月 28日

Hash#put がほしい

class Hash
	def put(key, value)
		self[key] = value
		self
	end
end

a = { 1 => "foo", 2 => nil }
b = { 2 => "bar", 3 => "baz" }
p a.inject({}) {|r,(k,v)| r.put(k, b[k] || v) }

a.inject({}) {|r,(k,v)| r[k] = b[k] || v; r } # ← これがダサい

ML に投げるべきなんだろうなぁ……メールこわい……


というか []= が value を返すのはわかるけど、store (さっきしった) まで value かえさなくてもいいのに (Array#{push,unshift} は self をかえす)

jAutoPagerize / microformats hAtom

どういう風に入れようか悩んでて (autopagerize_insert_before は microformats っていうのかなぁ……じゃあ名前なにがいいんかなぁとかいろいろ) いれてなかったけど、コミットされてたのでまいっかー的に……(他人まかせ)

それとは別に hAtom と rel="next" による microformats のルールを入れた。link rel="next" もマッチすべきかも

, { url          : '^https?://.*'
  , nextLink     : '//a[@rel="next"]'
  , insertBefore : '//*[contains(concat(" ",@class," "), " hfeed ")]/following-sibling::node()'
  , pageElement  : '//*[contains(concat(" ",@class," "), " hfeed ")]'
  }

clear cache のメニュー追加をかかないとなぁとおもってたらコミットされてたww CodeRepos++

wiki clone

どうも wiki clone がつくれない。なんか考えることが多くて、しかもちらばってて、まとまらない。だめすぎる……

  1. 履歴管理と diff
  2. データの保存方法
  3. 記法パーサー
  1. データベースを使わない
  2. 全てのリビジョンをそのまま保存する (過去のは全部 gzip)
  3. diff は履歴表示のときだけ
  4. 記法なし

でつくってみればいいんだろうけど、いまいちやる気がわかない。こんなのつくっても、って感じ…… Ruby で blosxom クローンつくらなかったのもこういう理由なんだよなぁ……blosxom クローンを Ruby で書いても Ruby がある程度わかってるならあれじゃ学習にならないし、書くならまともなのが書きたかったから、プラグインシステムをどうするかでずっと悩んでた。

しかし wiki clone だとユーザの書きこみがあったりするから、他の言語で勉強しながらつくるっていうのは結構荷が重い……

2007年 11月 29日

JavaScript tailcall...

function Process () {
	this.init.apply(this);
}
Process.prototype = {
	init: function () {
		var self = this;
		this.stack = [];
		this.rvalue = undefined;
		setTimeout(function () { self.call() }, 0);
	},

	p: function (f) {
		this.stack.unshift(f);
		return this;
	},

	call: function () {
		var self = this;
		var f = this.stack.pop();
		var thisObj = {};
		thisObj.call = function (f, args) {
			args = Array.prototype.slice.call(arguments);
			f    = args.shift();
			self.stack.push(function () {
				return f.apply(thisObj, args);
			});
			return "this";
		};
		this._rvalue = f.call(thisObj, this._rvalue);
		if (this.stack.length) {
			setTimeout(function () { self.call() }, 0);
		}
	}
};

window.onload = function () {
	var ps = new Process();

	ps.p(function () {
		$("#content").append("<div>start!</div>");
	}).p(function () {
		function pow (x, n) {
			function _pow (n, r) {
				$("#content").append("<div>"+[n, r]+"</div>");
				if (n == 0) return r;
				return this.call(_pow, n - 1, x * r);
			};
			return this.call(_pow, n, 1);
		}
		return this.call(pow, 2, 1000);
	}).p(function (r) {
		$("#content").append("<div>end!"+r+"</div>");
	});
};

nemui

Groria Street から愛を込めて#3

なんかくせになってきた。これすごい好きだ。「捨てたものを~」のところが鳥肌たつぐらい好きだ。

CD はずすと曲目と一緒に年号(のような4桁の数字)が書いてあるけど、これなんだろう。

gerry++

きのうもだったけど……

2007年 11月 30日

Ruby1.9 のシンボルキーハッシュリテラルの簡易記法

p({ foo: "abc", bar: "def" })

こうかけるやつなんだけど、JS みたいなノリで

p({ foo : "abc", bar : "def" })

と書くとパースエラーになる。


シンボルキーと他のキーは同時に書けるけど、ちょっと混乱するかもしれない

p({ foo: "abc", bar: "def", "baaa" => "aaa" })

それに、うえのように書いても "baaa" と :baaa は違うオブジェクトなので

p foo[:baaa]  #=> nil
p foo["baaa"] #=> "aaa"

Ruby で arguments.callee ってどうやるんだろ

self == -> { self }.()

(もちろんまちがってないけど) true だしなぁ。

引数かえられないけど redo で現在のメソッドを最初からやりなおすのはできる……

Binding#callee とか実装できないかなぁ (実行中の Method/Proc をかえす)

jAutoPagerize / はてなスター

フィルタの適用方法を変えて、importNode されたノード個々に対してフィルタを適用するようにした。本家と挙動が違うかもしれない。(フィルタ書いたことなくてよくわからない)

でもってはてなスターがロードされているページではデフォでスターをロードするようにした。Hatena.Star に特定ノード以下に制限してロードするのがあったのでそれつかってみた。(はてダのクイックページャのソースからたどった)

GM の config

直書きだと、バージョンアップするたびにコピペが発生してめんどい。どうにかしたい。

最近のまいぶーむ -> MochiKit Deferred の再発明

MochiKit の Deferred がよくできてるなぁと実感する。Deferred チェイン中に Deferred return するとチェイン中断して……みたいなのがかっこいい (そういうのを Deferred 一個で統一してできるようにしているので MochiKit Deferred は結構サイズが大きい)。でも API がどうしても好きになれない。jQuery に移植したい。

でも慣れないとよくわからないよなぁ……(setTimeout の挙動 (ブラウザの実行キューのほげほげ) とかをわかってないと、Deferred つくってるところで setTimeout(, 0) してて、なにこれ? っておもうことになる。)

Deferred チェイン。

MochiKit の実装はもっとシンプルにできそうなのでつくってみた。でもいろいろ頭悪くて考えきれてないと思う……

例えばこういう風に書けるようにする。

wait(1).
next(function (e) {
	log([1, e]);
	return wait(1).next(function (e) {
		log([2, e]);
		return wait(2).next(function (e) {
			log([3, e]);
		});
	});
}).
next(function (e) {
	log(4);
	return "555";
}).
next(function (e) {
	log(e);
})

末尾再帰っぽいの (return call ってのが tailcall っぽくてよくないですか><)

next(function () {
	log("start");
}).
next(function () {
	function pow (x, n) {
		function _pow (n, r) {
			if (n == 0) return r;
			return call(_pow, n - 1, x * r);
		}
		return call(_pow, n, 1);
	}
	return call(pow, 2, 10);
}).
next(function (r) {
	log([r, "end"]);
}).
error(function (e) {
	alert(e);
})

実装

function Deferred () { this.init.apply(this) }
Deferred.prototype = {
	init : function () {
		this.callback = {
			ok: function (x) { return x },
			ng: function (x) { throw  x }
		};
		this._next    = null;
	},

	next  : function (fun) { return this._post("ok", fun); },
	error : function (fun) { return this._post("ng", fun); },
	call  : function (val) { return this._fire("ok", val); },
	fail  : function (err) { return this._fire("ng", err); },

	cancel : function () {
		this._next = null;
	},

	_post : function (okng, fun) {
		this.callback[okng] = fun;
		this._next = new Deferred();
		return this._next;
	},

	_fire : function (okng, value) {
		var self = this;
		var next = "ok";
		try {
			value = self.callback[okng].call(self, value);
		} catch (e) {
			next  = "ng";
			value = e;
		}
		if (value instanceof Deferred) {
			value._next = self._next;
		} else {
			setTimeout(function () {
				if (self._next) self._next._fire(next, value);
			}, 0);
		}
	}
};

function wait(n) {
	var d = new Deferred();
	var t = new Date();
	setTimeout(function () {
		// 実際にかかった時間をコールバック
		d.call((new Date).getTime() - t.getTime());
	}, n * 1000)
	return d;
}

function ps () {
	var d = new Deferred();
	setTimeout(function () { d.call() }, 0);
	return d;
}

function next (fun) {
	return ps().next(fun);
}

function call (f, args) {
	args = Array.prototype.slice.call(arguments);
	f    = args.shift();
	return next(function () {
		return f.apply(null, args);
	});
}

なかなかカッコいい感じがする。もうちょいいじる。

next は this ではなくて、次の Deferred (this が準備できたらよばれる Deferred // _next) をかえす。これでずっとチェインしていく。もしコールバックが Deferred をかえしたら、その Deferred に _next をわたしてあげる。_next は継続 (ていっていいのかな。継続をちゃんと理解していない)

スタック消耗してたのを修正した

Deferred loop

function loop (o, fun) {
	var begin = o.begin || 0;
	var end   = o.end;
	var step  = o.step || 1;
	var ret;
	return next(function () {
		function _loop (i) {
			if (i < end) {
				ret = fun.call(this, i, step);
				return call(_loop, i + step);
			} else {
				return ret;
			}
		}
		return call(_loop, begin);
	});
}


next(function () {
	var hoge = 0;
	return loop({end:50000, step:50}, function (n, step) {
		for (var i = 0; i < step; i++) {
			hoge += n+i;
		}
		return hoge;
	});
}).
next(function (e) {
	log(e);
	log("end");
}).
error(function (e) {
});

50000回のループを50ずつ実行する。