2008-08-07
■ [ruby] RubyでZipperを実装してみた
Zipperについては
を見てほしいのですが、簡単に言うと副作用を使わずに実装された双方向リストみたいな感じです。
例。
# zipperを作る zipper = Zipper.make(1, 2, 3) # 最初は、0番目にカーソルがある zipper.get #=> 1 # nextを呼ぶと、1番目にカーソルが移動する zipper.next.get #=> 2
んで書き換えもできるのですが、「副作用なし」なので、書き換えると丸ごと別のzipperができます。
orig = Zipper.make(1, 2, 3) copy1 = orig.next.set(100) copy2 = orig.next.set(200) copy1.to_a #=> [1, 100, 3] copy2.to_a #=> [1, 200, 3]
「なんだか遅そう」って?それがO(1)、つまりどんなに長くなっても同じ時間で行えるのがZipperの凄いところなんです。
試しにこんなテストを書いてみました。
it "Zipperは、大量にコピーしてそれぞれ書き換えたりしてもへっちゃら!" do original = Zipper.make(*(0..10000).to_a) # 長さ10000のzipperを作って、 zippers = (0..10000).map{|i| # それを10000個、 original.next.set(i) # 1箇所ずつ書き換えて複製する } zippers[99].get.should == 99 zippers[1234].get.should == 1234 end
二次元配列でこれをやると10000 * 10000なので凄い時間がかかりそうですが、Zipperでは0.5秒で終わります(僕のMacBook調べ)。
まあ、Rubyのライブラリとして何に使えるかというと、ぱっと思いつかないのですが(笑)、なかなか面白かったです。
ソースは最近話題の(って遅いけど) gist にアップロードしてみました。間違ってゲストで投稿しちゃったんだけど、その場でログインしたらownerがyharaになるとか気が利いてますね。
2008-08-08
■ [junk] 「ピタゴラスイッチの人ってSKKも作ってたのか」
と勘違いしてる人がいたら面白いな。
という僕も、完全に同姓同名であることは知らなかったわけですが。
あと、大文字区切りの単文節変換というアイデア自体はSKKより先にあったんですねぇ。
Lispマシン!"本ページは,DOS 用エディタ Elis とは関係ありません" の一言が懐かしすぎる。
いや、そんなことが書きたかったんじゃなくて、、
GoogleでSKKを検索すると予想外の結果になるんですが。
2008-08-09
■ [junk] Wikipediaはyak shavingのメッカ [EOM]
参考
- yak shaving で人生の問題の80%が説明できる問題 - bkブログ
- 件名だけで用件を終わらせる「EOM」とは : ライフハッカー[日本版], 仕事も生活も上手くこなすライフハック情報満載のブログ・メディア
ヤック・シェイビングってヤック・デカルチャーとちょっと似てるよね。
2008-08-10
■ [haskell] Linspire(旧Lindows)がHaskellを使ってるらしい
Linspire GNU/Linux はHaskellをシステムツール開発に選択した。
[Haskell - Wikipediaより引用]
mjd?
検索するとこんなのが出てくる。
パッケージツールやシェルスクリプトをHaskellでリプレースしたい、みたいな。
このメールは2006年だけど、それからどうなったんだろう。
2008-08-13
■ [ruby] ruby Functor gem
RubyでHaskellやOCamlのようなパターンマッチを実現しようという試みはいくつもありますが、それらの中では一番綺麗なんじゃないでしょうか(見た目的な意味で)。
fib = Functor.new do given( 0 ) { 0 } given( 1 ) { 1 } given( Integer ) { |n| self.call( n - 1 ) + self.call( n - 2 ) } end
どうでしょうか?
ところでFunctorって一体なんなんでしょう。いろんなところで名前だけは聞くのですが…。
関手(かんしゅ、functor)とは、圏論における一つの圏から別の圏への対応でその構造と両立するようなものである。関手によって一つの数学体系から別の体系へのシステマティックな対応が定式化される。関手は「圏の圏」における射と考えることもできる。
[関手 - Wikipediaより引用]
分かる要素がないwww
■ [ruby] RubyTreemap
ハードディスクの使用状況を、地図のように分かりやすく表示してくれるソフトウェアがあります。例えばSpaceMongerとか。
RubyTreemapは、このような画像を簡単に作るためのライブラリだそうです。PNG, SVGのほか、htmlでも出力できる模様。
しかし、ハードディスクの使用状況以外でどういう用途に使えるんでしょう?ツリーの各節点が何かの大きさを持つような構造といえば…。うーん、何だろう。
(追記: 例えば家計簿の支出を階層構造のカテゴリに分けると、「今月本にはこれくらい使って、そのうち漫画はこれくらい」とか分かっていいかも。)
■ [haskell] HUnitを使ってみた
(追記:Test.HUnitはGHCだと標準で入ってます。インストールは不要。)
一応AssertEqualとかもあるんですけど、そうじゃなくて演算子の方を使うのが玄人らしい。
僕はRSpecが好きなので~?= を使ってますが、 ~=?を使えば逆順 (つまり <expected> ~=? <given>) で書くこともできます。
plus3.hs:
module Plus3 where plus3 :: Int -> Int -> Int -> Int plus3 x y z = x + y + z
test_plus3.hs:
import Test.HUnit import Plus3 simpleTest = [ "plus3 should plus 3 integers" ~: (plus3 1 2 3) ~?= 6 , "plus3 should plus 3 negative integers" ~: plus3 (-1) (-2) (-3) ~?= -6 ] largeTest = [ "plus3 should plus 3 large integers" ~: (plus3 1000000000000000000000000 2000000000000000000000000 3000000000000000000000000) ~?= 6000000000000000000000000 ] testData = [ "simple" ~: simpleTest , "large data" ~: largeTest ] main = runTestTT (test testData)
実行結果:
/Users/yhara/proj/babel21/haskell % runghc test_plus3.hs Cases: 3 Tried: 3 Errors: 0 Failures: 0
MacPortsで入れたGHC 6.8.2で確認。
■ [haskell] Haskellで外部プログラムとパイプ経由でやりとりしてみた
Rubyでいうopen3である。
import System.Process import System.IO main :: IO () main = do (stdin, stdout, stderr, processHandle) <- runInteractiveCommand "sort" hPutStr stdin "foo\nbar\nbaz" result <- hGetContents stdout putStr result
どう見ても手続き型ですw 思ったより簡単だな。
実行結果:
/Users/yhara/proj/babel21/haskell % runghc process_sample.hs bar baz foo
2008-08-19
■ [ruby] Rubyの意外な機能とか
いま「プログラミングRuby」を読んでるんだけど、知らんかったことが多いなー。
4274066428
多重代入は括弧でグルーピングできる
irb(main):010:0> b, (c, d), e = [1, [2, 3], 4] => [1, [2, 3], 4] irb(main):011:0> [b, c, d, e] => [1, 2, 3, 4]
notが警告を出すことがある
irb(main):012:0> not "foo" (irb):12: warning: string literal in condition => false irb(main):013:0> not /foo/ (irb):13: warning: regex literal in condition => true
「!」でも同じ。
defined? は文字列を返す
irb(main):018:0> defined? 1 => "expression" irb(main):019:0> defined? 1+2 => "method" irb(main):020:0> defined? :foo => "expression" irb(main):021:0> defined? "foo" => "expression" irb(main):022:0> defined? Array => "constant"
どういう時に使うんだ。
whenで配列の展開が使える
irb(main):023:0> case 1 irb(main):024:1> when *[1,2,3] irb(main):025:1> puts "ok" irb(main):026:1> end ok => nil
まぁ確かに自然ではある。
while〜do〜endだけでなく、while〜:〜endも許される
irb(main):036:0> n = 0 => 0 irb(main):037:0> while n < 3: irb(main):038:1* puts n irb(main):039:1> n += 1 irb(main):040:1> end 0 1 2 => nil
Python使いも安心 追記:1.9では無くなったそうです。
forはeachと同じだが、スコープが違う
for x in [1,2,3] ... end # ここでxは参照できる [1,2,3].each do |y| ... end # ここでyは参照できない!
補足。eachの呼び出しより前に y=0 などと初期化してあった場合は:
- 1.8
- yはeachより後でも参照でき、3になる。
- 1.9
- yはeachより後でも参照できるが、0のまま。
2008-08-20
■ [misc] 適当な英語でも書いてみるといいよという話 (1)
githubには、リポジトリのトップにREADME.rdocのようなファイルを入れておくとHTMLに変換して表示してくれる機能があるんですけど、 ヘルプのどこを見ても「どんなフォーマットが使えるか」が載ってない。
んで仕方ないからGuidesに項目を追加したんです。適当な英語で。これが一ヶ月くらい前。
そのあとしばらく特に反応が無かったんですが、最近になっていろいろ項目が追加されて、今はこんな感じに。
何でも書いてみるもんですねぇ。特に*.rstがUnsupportedに追加されて、そのあとSupportedに移動したあたりが良かった。
ついでにrdもサポートされると嬉しいんですが(・∀・)
2008-08-21
■ [ruby][mac] RubyからGrowlに通知するためのライブラリ、Meow (とGrowlNotifier)
ruby-talkを眺めてたらMeowというライブラリのリリース案内が。なんでもGrowlにRubyから通知を送れるらしい。
というわけでやってみた。
ちょwwwこれ簡単すぎるんだがwww
インストール
とりあえずRubyCocoaが必要。
==MacPortsでrubyを入れてる場合、RubyCocoaもportで入れたいわけだが(rb-cocoa)、「ドキュメントがもう入ってるよ」とか言われてインストールできない。 仕方ないので僕は元のドキュメントをリネームして対処した(いいのかそんなので)。== (8/25追記: 普通に port install rb-cocoa で入るようになったようです!)
Meowの方は普通に gem install meow でOK。
使ってみる
基本的な使い方は
meep = Meow.new('Meow Test') meep.notify('Title', 'Description')
こんだけです!
通知をクリックしたときの処理を指定したい場合は、
meep.notify('Click Me', 'Do it!') do open "http://example.jp" # open browser end
こんな感じで。
ライバルとか
某所でGrowlNotifierというライブラリもあるよと教えてもらった。 Mac版LimechatからGrowl通知部分を切り出してライブラリにしたものらしい。
Meowよりは複雑だけど、タイムアウトしたときの処理も書けるとかいろいろ高機能っぽい。 あと、RubyCocoa的にちゃんとしてそう(笑)。
自作のRubyCocoaアプリに組み込むならGrowlNotifier、ちょっとしたスクリプトを書くだけならMeowがいいんじゃないかな。
作ってみる
せっかくなので、こっそりアンケートβに新着アンケートが来たらgrowlに通知するスクリプトを書いてみた。
~/.kosoanというファイルに
mail: foo@example.jp password: asdffdsa
のように書いて、ruby kosoan_watch.rbで起動。10分ごとに新着チェックして、答えられるアンケートが来たらGrowlに通知される。
WWW::Mechanizeを使ってるので、gem install mechanizeが必要。
誰かはてなアンケート版も書いてくれると嬉しいなぁ。「20代男性」とか条件を設定できる感じで。
2008-08-23
■ [ruby] bitclustに関するメモ
Ruby合宿中に聞いとけばよかった…。
ディレクトリ構成はとりあえず
% ls bitclust/ doctree/ db-1_8_7/
みたいにする。
まずDBフォルダを作る。
% ./bitclust/bin/bitclust.rb -d ./db-1_8_7 init version=1.8.7 encoding=euc-jp
encoding=utf8とかしても、入力を自動的にUTF8に変換してくれたりはしないので注意(今のところ)。
次にリファレンスを読み込む。svn upを忘れずにな。
% ruby -I bitclust/lib/ bitclust/bin/bitclust.rb -d ./db-1_8_7/ update --stdlibtree=./doctree/refm/api/src
末尾に「&& echo ^G」(^GはC-v C-g)とかしとくと終わったときにベルが鳴って良い。「&& say 'finished'」でもいいけど(Mac限定)。
んでサーバ起動。
% ./bitclust/standalone.rb --debug --database=db-1_9_7 --port=10081 --baseurl=""
--debugを付けないと^Cで止められないので注意。
ここまではいいんだ。ここまでは。
ツリーの一部分だけ更新したい
えーと
一部分だけやろうとするとOpenSSLあたりのドキュメントでエラーになるんでDB作るところからやり直さんといかんのだが、 リファレンス書くだけならbc-tohtmlで部分的にHTML作ればいい気がしてきた。
Tips
bitclust/theme/default/style.cssで s/33a/3a3/g とかすると、デザインが青から緑になるのでローカルだとわかりやすくなって良い。
■ [haskell] ParsecでS式をパーズしてみた
てか48時間Scheme(via http://kzk9.net/column/haskell_parsec.html)そのままだが。
import Text.ParserCombinators.Parsec data Value = IntValue Int | StrValue String | ListValue [Value] deriving (Eq, Show) parseSexp :: Parser Value parseSexp = parseAtom <|> parseList parseAtom :: Parser Value parseAtom = parseInt <|> parseStr parseInt :: Parser Value parseInt = do s <- many1 digit return $ IntValue (read s) parseStr :: Parser Value parseStr = do char '"' x <- many stringParts char '"' return $ StrValue $ concat x where stringParts :: Parser String stringParts = do c1 <- char '\\' c2 <- anyChar return $ c1:[c2] <|> do c <- try $ noneOf "\"" return $ [c] parseList :: Parser Value parseList = do char '(' ls <- sepBy parseSexp (skipMany1 space) char ')' return $ ListValue ls main = print $ parse parseSexp "" "(\"1\" (2) 3)"
値をValueで包まんといかんのが嫌なんだが、なんとかならんかなぁ。
IntとStringだけでよければ型クラス?でなんとかなりそうな気がするが、それらのリスト(とリストのリストとか)をどうしたらいいのかわからん。
■ [ruby] Rubyライブラリのサンプルコードを書くときのTips
Rubyのライブラリは大抵、
bin/ (あれば) lib/foo.rb lib/foo/bar.rb test/test_foo.rb example/baz.rb
みたいになるわけだが、example/baz.rbからlib/foo.rbをrequireするにはどうすればいいか?という問題がある。
もちろん、インストールしてしまえば
require 'foo'
でいいわけだが、開発中はそういうわけにもいかない。
「サンプルコードはトップレベルで実行してね」(または、example以下で実行してね)と決めてしまえば、
require 'lib/foo'
とか
require '../lib/foo'
でrequireできるが、これだと決められたディレクトリで実行しないとLoadErrorになってしまう。
そこで、example/baz.rbの最初に以下のように書いておくと、どこから実行しても require 'foo' でいけるようになる。
$LOAD_PATH << File.expand_path("../lib", File.dirname(__FILE__))
何をしているかというと、__FILE__で自分自身(つまりexample/baz.rb)のパスを取得して、baz.rbのある ディレクトリから ../lib を相対的にたどり、requireがライブラリを探す対象として登録している。
(8/26追記: 場合によっては、$LOAD_PATH << より $LOAD_PATH.unshift の方が良いかも。<<は末尾に追加するので、fooというライブラリが既にインストールされてたりするとそっちを読みこんでしまう。)
ちなみにこのTipsはサンプルコードだけでなく、ユニットテストにも使える。
$LOAD_PATH << File.expand_path("../lib", File.dirname(__FILE__)) require 'rspec' require 'foo'
とかね。
■ [haskell] HaskellでS式のライタを書きたいんですけど
とりあえず IntとStringと[a]をSexpValueのインスタンスにしようとしたんですけど、String(つまり[Char])と[a]がバッティングして動いてくれません(><)
{-# OPTIONS_GHC -XTypeSynonymInstances #-} {-# OPTIONS_GHC -XFlexibleInstances #-} {-# OPTIONS_GHC -XFlexibleContexts #-} import Data.List takeChars :: String -> [Int] -> [String] takeChars str ns = map (\n -> [str !! n]) ns class SexpValue a where toSexp :: (SexpValue a) => a -> String instance SexpValue Int where toSexp n = show n instance SexpValue String where toSexp s = show s instance SexpValue a => SexpValue [a] where toSexp ls = "(" ++ (concat $ intersperse "," (map toSexp ls)) ++ ")" main = putStr $ toSexp $ takeChars "abcde" [1,3]
どうしたらいいんだろう。Showとかはどうやってるのかな。
2008-08-25
■ [python] Pythonいみわからん→わかりました
>>> re.compile("foo").match("foo\nbar\nbaz") <_sre.SRE_Match object at 0x6ebb8>
>>> re.compile("bar").match("foo\nbar\nbaz") >>> re.compile("bar", re.M).match("foo\nbar\nbaz") >>>
マッチしろよ
(9/4追記) こうですね、わかりました!
>>> re.compile("foo").search("foo\nbar\nbaz") <_sre.SRE_Match object at 0x3fc98> >>> re.compile("bar").search("foo\nbar\nbaz") <_sre.SRE_Match object at 0x716e8>
ありがとうございました>Dubheadさん、suztomoさん
python自体の感想についてはあとで書くかも。
2008-08-30
■ [unbabel] LLにLLを埋め込むためのライブラリ、Unbabelについて発表しました
LL FutureのLTで、Unbabelというプロジェクトについて発表させていただきました。
右上のボタンを使うか、j/kでスライド間を移動できます。oとかaとかzを押すと何かが起きます。
Unbabelって何ですか
LLにLLを埋め込むためのフレームワークです。例えば
require 'unbabel' fib = Unbabel::Scheme.new(<<EOD) ;; fib :: Int -> Int (define (fib x) (case x ((0) 0) ((1) 1) (else (+ (fib (- x 2)) (fib (- x 1)))))) EOD p fib[2] #=> 1 p fib[3] #=> 2 p fib[10] #=> 55
のように、RubyスクリプトからSchemeの関数を呼んだり、PythonからRubyの関数を呼んだり、HaskellからPerlの関数を呼んだりなどできます。 まるで魔法のような話ですが、実際にはfib関数を呼ぶたびにSchemeのインタプリタが走ります。富豪的プログラミング(笑)です。
より詳しい仕様については SPEC.ja を参照してください。
名前はUnlambda へのリスペクトを込めて命名されました。あとバベルの塔のごとくバラバラな言語を 使ってる俺たちも通訳さえいれば話が通じたりするよね、みたいなそういう。
効用・効能
- 「欲しいライブラリがCPANにはあるんだけど、Rubyにはないんだ」
- それUnbabelで (RubyスクリプトにPerlのコードを埋め込みましょう)
- 「アルゴリズムはHaskellで書きたいんだけど、GUIとか大変そうだし…」
- それUnbabelで (GUI部分はRuby等で書いて、Haskellのコードを埋め込みましょう)
- 「仕事でRuby 1.9が使いたいけど上司が許してくれません」
- それUnbabelで (Ruby 1.8にRuby 1.9を埋め込みましょう)
- 「Ruby 1.9に移行したいけど、1.8のスクリプトの移植が面倒で…」
- それUnbabelで (Ruby 1.9にRuby 1.8を埋め込みましょう)
- 「Schemeはまだまだライブラリの数が少ないよねえ」
- それUnbabelで (CPANとrubygemsの全てのライブラリが使い放題です、もうライブラリがないなんて言わせません)
- 「Rubyにもパターンマッチが欲しいですよね」
- それUnbabelで (Haskellを埋め込むとRubyスクリプトなのにパターンマッチが使えます)
- 「なんでPythonにはswitchがないの!」
- それUnbabelで (Javaを埋め込むとPythonスクリプトなのにswitch文が使えます)*1
えっと、あまり本気にしないように…。
インタプリタ起動のオーバーヘッドは確かにあるので埋め込む関数を何百回も呼ぶような使い方には向いてないと思いますが、 そうでなくて、小規模なスクリプトや、大きなデータを一度だけ別言語に渡すような使い方なら意外と実用に耐えるんじゃないかと考えています。 例えばオセロゲームの思考ルーチンだけHaskellで書いて、CPUの番が来るごとにRubyからそれを呼び出すとか。
ダウンロード
ソースコードはCodeReposに置かせてもらいました。
チェックアウトは
svn co http://svn.coderepos.org/share/platform/unbabel/trunk unbabel
でどうぞ。
どうぞご自由に書き換えたり、言語を追加したり、添削したりしてくれると嬉しいですm(__)m
*1 現状、Javaの埋め込みはRuby版しか対応できていませんが、PythonにRubyを埋め込むことはできるので、Java in Ruby in Python という構成にすれば実現可能です。
2008-08-31
■ [perl][ruby][english] I don't know what to do, とLarryは言った
LL Futureの懇親会で、Larry Wallに気になってたことを質問してみた。 「Perlユーザってモダンな人たち(use Mooseとか、use strictとかuse warningsとか)と古い人たち(Perl5のコードをPerl4のスタイルで書いてるような)に分断されてるように 見えるんですが、それについてはどう考えていますか?」
I don't know what to do というのがLarryの答えだった。新しいバージョンを使うには、どうしても何か新しいことを学ぶ必要がある。 今までに学んだことで十分満足している人もいる。
僕はなんとなくもっと楽観的に捉えているものかと思っていたのだが、Larryはどことなく悲しそうに見えた。気のせいかも知れんけど。僕はしんみりした。
これはPerlだけの話じゃない、Rubyだってそうだ。この冬に1.9がリリースされるが、Ruby 1.8ユーザとRuby 1.9ユーザが分断されることはないと誰が言える?
だがLarryによれば、メジャーリリースではそういうことは常に起こるものだそうだ。ある人は楽しみのためにプログラミングをする。 また別の人は自分の仕事を片付けるためにプログラミングをする。
ここからは蛇足。
いつもなら英語で話しかけるとかたぶんぜったいできないんだが、今回は前日夜に ujihisaやhajimehoshiと夜通し英語(風の何か)で会話してたので、その勢いで喋ることができたのではないかと考えている。 昔、はてな社員が朝会で英会話してるという話(2005年か)を見たときは効果あんのかなと思ったが、 あるわ。あるある。
英語が喋れない日本人のいくらかはたぶん能力というより心理的な抵抗が大きくて、つまり、 なんか中学高校の英語教育の間に英語で喋るのは気恥ずかしいことという概念を植え付けられる気がするので(俺だけ?)、英語を発話することに対する 心理的な抵抗を取り除くという意味では日本人同士でもかなり効果はあるんじゃないかと思った。
というわけでまず友達を酔わせて英語で話しかけてみるところから始めるといいです。
□ soutaro ["Introducing Functor"の"Functor"はちょっとわからなかったのですが、MLやC++のfun..]
□ minke [functorという言葉は、プログラミング言語界隈では圏論の関手の意味を超えてもっと広くゆるく使われてますね。 圏論..]
□ yhara [soutaroさん、minkeさん: ありがとうございます。必ずしも「Functor == 関手」の意味で使われてる..]