トップ 最新 追記

Route 477



2008-01-03

[misc] あけましておめでとうございました

新年らしいですね。

なんか2/1に修論締め切りがあり、その約一週間後に発表があるため、この一ヶ月は必死であること必至であり、 気とか胃とかが重く、あと〇〇とか××のタスクが重なっていて、非常に痩せそうな気分です(物理的な意味で)。

(今月を乗り切れたら)今年もよろしくお願いします。

[ruby] Ruby1.9.0-0で増減したメソッド一覧

Ruby1.8.6-p111とRuby1.9.0-0のメソッドを比較してみました。

Kernelに__callee__という謎のメソッドが入ってたり、いろいろ発見があって面白かったです:-)


2008-01-04

[misc] そろそろ本気出す

  • 禁チャット
  • 禁LDR
  • 禁テトリスDS

最後のが一番危険

[biwascheme] let*の変換

let*はletのネストに変換できる。*1

(let* ((a 1) (b a)) (print a) (+ a b))
  ↓
(let ((a 1))
  (let ((b a))
    (print a) (+ a b)))

ということで、

  1. バインディングを最後から順番に取って、
  2. body = (let (bind) (body)) とする

という操作を繰り返せばよい。

…のだが、biwaschemeではbodyに複数の式を許しているので、最初だけ特別扱い(Pairで包まない)しなければならない。

    var ret = null;
    binds.to_array().reverse().each(function(bind){
      ret = new Pair(Sym("let"), 
               new Pair(new Pair(bind, nil),
                 ret == null ? body : new Pair(ret, nil)));
    })
    return ret;

というのでちょっと悩んだ。

再帰的に書けばこんな罠にははまらないっぽいんだが。

syntax-rulesで書いてみた例。

(define-syntax letstar
  (syntax-rules ()
    ((letstar ((var val)) body ...)
     (let ((var val)) body ...))
    ((letstar ((var val) (var2 val2) ...) body ...)
     (let ((var val))
       (let* ((var2 val2) ...) body ...)))))

(print (let ((x 2) (y 3)) (let* ((x 7) (z (+ x y))) (list x y z))))

*1 letは最終的にlambdaに変換できるので、let*から直接lambdaに変換しても良いと思う

[scheme] syntax-rulesでdefine-structをつくる

去年書き忘れた記事。

MzSchemeにはdefine-structという構文(Common Lispのdefstructみたいなもの)があるらしくて、 Gaucheにもあったらいいなぁと思ったので作ってみた。

…とさらっと書いたけど非常に苦労しましたw

;; string-appendのシンボル版
(define (symbol-append . syms)
  (string->symbol 
    (apply string-append (map symbol->string syms))))

;; define-structという構文を定義
(define-syntax define-struct 
  (syntax-rules ()
    ((define-struct name props)
     (eval 
       `(begin
          (define-class 
            ,(symbol-append '< 'name '>) 
            () 
            ,(map 
               (lambda (sym)
                 `(,sym :init-keyword ,(make-keyword sym)
                        :accessor ,(symbol-append sym '-of- 'name)))
               'props))
          ,@(map
              (lambda (sym)
                `(define-method ,(symbol-append 'name '- sym '-set!)
                                ((obj ,(symbol-append '< 'name '>)) ,sym)
                   (slot-set! obj ',sym ,sym)))
              'props))
       (interaction-environment)))))

使用例。

(define-struct book (title jp-title))

(let1 bk (make <book> :title "SICP")
  (print (title-of-book bk))
  (book-jp-title-set! bk "(前略)構造と解釈")
  (print (jp-title-of-book bk))
  )

はっきり言ってsyntax-rules使う意味がない(パターンマッチ使ってない)。 define-macroで書き直してみようかと思ったけど気力が尽きたので省略。 あとevalをなんとかしたかったけど無理だった。

先輩はevalなし(syntax-rulesだけ)でできるって言うんだけどさ、無理じゃね? syntax-rulesじゃマクロ展開時にsymbol-appendを実行することができないんだし。

syntax-rulesでsymbol-appendが実装できれば別だけど…、いくらチューリング完全と言っても、 それはリスト操作についてであって、symbol->stringが実装できるわけではない。よね。


2008-01-06

[biwascheme] letrecとletrec*

3行でまとめると

  • letrecは相互再帰関数を定義するためのlet
  • letrec*はトップレベルの意味を明確にするために導入されたもの
  • letrec*はバインディングの値部分で、それより前の変数を参照してよい。letrecはダメ

ということみたい。

letrec*はR6RSで正式導入されたものなので、R5RSにはない。

以下R6RSのてきとーな概訳:

11.4 - letrec

  • (letrec <bindings> <body>) syntax
    • <bindings> は ((<variable1> <init1>) ...), initは式
    • <body>は11.3で説明されている
    • variableには同じ変数が複数回出てきてはいけない
  1. 各variableが新しい場所に束縛される
  2. その環境下で、各initが評価される(順序は不定)
  3. 各variableが対応したinitの評価結果に束縛される
  4. その環境下で、bodyが評価される
  5. body中の最後の式の評価結果が返される

各variableの有効範囲はletrec式の全体であり、これによって相互再帰関数を定義することができる。

(letrec ((even?
          (lambda (n)
            (if (zero? n)
                #t
                (odd? (- n 1)))))
         (odd?
          (lambda (n)
            (if (zero? n)
                #f
                (even? (- n 1))))))
  (even? 88))   
                        ⇒  #t

各initは、どのvariableの値も代入・参照することなしに評価できるべきである。 letrecの最もよくある使い方においては、全てのinitはlambda式なので、この条件は自動的に満たされる。 また、各initの継続は一度以上実行されるべきではない。

処理系の責任:処理系は、initの評価中にvariableが参照されたことを検出しなければならない。もし処理系がこのような違反を検出したなら、 例外&assertionを投げるべきである。initの継続が2回以上呼び出されたかどうかは、検出してもしなくても良い。が、検出するならば 例外&assertionを投げなければいけない。

11.4 - letrec*

  • (letrec* <bindings> <body>)
    • <bindings>は((<variable1> <init1>) ...), initは式、<body>は11.3参照。各variableは被っちゃダメ
  • 各variableが新しい場所に束縛される
  1. 各variableが左から順に、対応したinitの評価結果に束縛される
  2. この環境下でbodyが評価され、bodyの最後の式の評価結果が返される

評価・代入は左から順に行うが、各variableの束縛自体はletrec*式全体で有効であり、これによって相互再帰関数が定義できる。

(letrec* ((p
           (lambda (x)
             (+ 1 (q (- x 1)))))
          (q
           (lambda (y)
             (if (zero? y)
                 0
                 (+ 1 (p (- y 1))))))
          (x (p 5))
          (y x))
  y)
                        ⇒  5

各initは対応するvariableや、それ以降のvariableの値を参照・代入することなしに評価できなければならない。 また、各initの継続は2度以上実行されるべきではない。

処理系の責任:処理系はinitの評価中に、対応するvariableやそれ以降のvariableの値の参照を検出しなければならない。検出したら、 例外&assertionを投げなければならない。継続の方はチェックしてもしなくてもいいが、するなら例外&assertionを投げなければならない。


2008-01-07

[biwascheme] write_ssを実装した

相互再帰関数の実行をデバッグしようとしたら、循環構造を文字列化しようとして無限ループになることが分かったので、 write/ssのようなものを実装した。

gosh> (let ((x (list 'a))) (list x x))    
(#0=(a) #0#)

こういうやつ。「#n=」で名前を付け、「#n#」で参照する感じ。

どうやればいいか分からなかったので、とりあえず2パスで実装することにした。

  1. まずダンプしたいオブジェクトを再帰的に辿り、複数回出現するオブジェクトを全てメモする。
  2. このメモを見ながら、オブジェクトを再帰的に文字列化していく。
    • 2度以上出現するオブジェクトで、初回の出現なら、#n= を頭に付ける。
    • 2度以上出現するオブジェクトで、2回目以降の出現なら、単に #n# と出力する。

とりあえずletrec*を使ったコードのデバッグに使ってみたら、こんなんが出てきて吹いたw

#0=[[#1="frame", [#1#, [#2="constant", #3=1, [#4="argument", [#5="refer-local", #6=0, [#7="indirect", [#4#, [#8="refer-global", #9="-", [#10="apply", #11=2]]]]]]], [#4#, [#12="refer-free", #6#, [#7#, [#10#, #3#]]]]], [#4#, [#2#, #3#, [#4#, [#8#, #13="+", [#14="shift", #11#, #3#, [#10#, #11#]]]]]]], [[[#1#, [#5#, #6#, [#7#, [#4#, [#8#, "zero?", [#10#, #3#]]]]], ["test", [#2#, #6#, ["return", #3#]], [#1#, [#1#, [#2#, #3#, [#4#, [#5#, #6#, [#7#, [#4#, [#8#, #9#, [#10#, #11#]]]]]]], [#4#, [#12#, #3#, [#7#, [#10#, #3#]]]]], [#4#, [#2#, #3#, [#4#, [#8#, #13#, [#14#, #11#, #3#, [#10#, #11#]]]]]]]]], [#15=#<undef>], [#0#]]], [#15#]]

あー、数値はimmutableだからタグ付ける意味ないのか。 (readしたときに同型の構造ができる、ってのがこの表記の目的らしいので。) 文字列もJavaScriptレベルではimmutableなんだから タグ付けるのやめようかなぁ。

Gaucheの場合

write.cを見てみたけど、Gaucheも2 passなんだな。

/* We need two passes to realize write/ss.

   The first pass ("walk" pass) traverses the data and finds out
   all shared substructures and/or cyclic references.  It builds a
   hash table of objects that need special treatment.

   The second pass ("output" pass) writes out the data.

ただ、発見したオブジェクトのテーブルは配列じゃなくハッシュテーブルを使ってるらしい。 そりゃそうか。


2008-01-08

[biwascheme] equal?

現実逃避にも熱が入る今日この頃ですが、皆様いかがお過ごしでしょうか。

write/ssができたので、これそのままequal?にも使えんかなーと思ったのだが…

(let* ((x (list 'a)) (y (list 'a)))
  (equal? (list x y) (list x x)))

というコードを考えると、(list x y)は "((a) (a))"で、 (list x x)は "(#0=(a) #0#)" になるから駄目なのであった。残念。

ちなみにwriteでなくwrite/ssなのは、R6RSのequal?は循環構造でも停止しないといけないから (参考)。

無いよりはマシかということで、writeを使った実装をコミットした。 あとで書き直す。

equal?の実装とかめんどいなーと思って今まで放置してたのだけど、write/ssに比べたら全然難しくないと思えるようになった (特に停止性を考えなければ)。なんとなく成長している感があって良い。

[biwascheme] 速度と可読性について悩む

例えばPrototype.jsのeachを使うか使わないか。

  • eachを使う
    • メリット
      • 書くのが楽
      • 読みやすい。あとからbiwaschemeのソースを読む人にとって優しい。
    • デメリット
      • 遅い。
  • for文手書き
    • メリット
      • 速い。
    • デメリット
      • eachより読みにくい。
      • 書くのがだるい(おれが)

まぁほんの少し速いことにどれくらい意味があるかは処理系のどの部分かによって違ってくるわけだけど、 いちいちこれはeachでいいかとか考えるのが面倒になってきたので全部eachにしちゃえという悪魔の囁きが。

自明な例:

インタプリタのコア部分(中間言語を実行していくところ)
ここはさすがにeachは避けたい。
write/ss
多分そんなに多用しないのでeachでいい。

いま、ソースコードのトップレベルの各式に対して繰り返す部分をeachにしようかforにしようか悩んでいるところ。

ていうか、個数がいくつから有意な差が出るのか一度ベンチマーク取らんとな。判断基準が。勘で決めている場合ではない。

あとfor文にしても、lengthをキャッシュするか否かとかー

別解

全てのeachをfor文に展開するプリプロセッサを書く。

[biwascheme] 全体をlambdaでくくるのを止めた

ソースコードに複数の式があった場合、それらを全部つなげてlambdaでくるむという手抜きをしてたのだが、それを止めた。

なんでかというと、以下のようなのがうまく実行できないからだ。

(define a "global var")
(set! a 1) ;unbound symbol 'a'

でどうしたかというと、とりあえずソースコードを全部パーズし、インタプリタが式の配列とどこまで実行したかを 状態として持つようにした。

まあPauseが絡まない限り状態変数とか要らないんで、Pauseオブジェクト側に持たせても良かったんだけど… というかそうした方が良いか?うーん。 でもスタックはインタプリタ側が持ってるんだよなー。

整理しよう。

状態変数:

  • stack
  • a,x,f,c,s
  • forms (パーズした式たち)
  • forms_ct (いま何番目の式を実行中か。Array#popで削っていけば不要なんだが、実行速度に配慮したとかなんとか)

今の実装だと、stack, forms, forms_ctはインタプリタが持ってて、x,f,c,sはPauseが持ってる。aはインタプリタを再開するときに外部から与えられる (例えばconfirmにおけるユーザの入力とか)。

とりあえずこのままにしとくか。

(define c 1)
(set! c (confirm "hoge"))
c

こんなプログラムも問題なく実行できるようになった。

[junk] ふと

もう一年修士やった方が全体として幸せかもなぁなんて考えが頭をよぎる。

卒業と留年を同時に行うことはできないので、どちらが幸せかなんて分からないわけだが。

[biwascheme] jsSchemeがCoreEnvとTopEnvを分けていた理由

jsSchemeのREPLにはユーザが定義した変数のインスペクタみたいのがあったので、そのためだったらしい。最近気付いた(遅いよ)。

[biwascheme] lambda式中でグローバル変数をset!できないバグ

以下のプログラムの結果が1になる。

(define a 1)
((lambda () (set! a 3)))
a

2番目の式のコンパイル結果はこう。

['frame', ['refer-global', 'a', ['argument', ['close', 1, ['constant', 3, ['assign-free', 0, ['return', 0]]], ['apply', 0]]]], ['halt']]

assign-globalになるべきところがassign-freeになってしまっている。 あとaの参照が自由変数と見なされてcollect_freeの対象になっているっぽい(→refer-global)。

ひげぽんさんが11月に遭遇したバグと同じか?

自由変数とかset!のあたりは、ちゃんと理解せずに適当に済ました感があるので、そのツケが来たともいえる(^^;

作業ログ

直すべきこと:

  • aが自由変数と判定されるのを直す。

どこを直すか:

  • find_freeを直せばいいはず

どう直すか:

  • 元の3impの実装にはグローバル変数がないので、「自由変数:ローカル変数でない変数」
  • これを、
    • 「ローカル変数:lambda式の仮引数」
    • 「自由変数:参照or代入されていて、ローカル変数ではなくて、外側のlambdaの仮引数になっているもの」
    • 「グローバル変数:それ以外」
    • のようにしたい。
  • 変数eの中身が上のような定義を満たすようになればOK

やること:

  • 変数fの内容が正しいかチェックする
    • てかいつのまにこんな変数増やしたんだっけ(汗
    • 変数fは「外側のlambdaの仮引数になっているもの」を表す。
    • fをいじるのはlambda式の本体をコンパイルするとき(スコープが深くなるとき)だけ。
      • このとき、fにlambda式の仮引数を加える。加えた情報は、さらに内側のlambda式をコンパイルするときに役に立つ。
  • 変数fの使い方が正しいかチェックする
    • 変数fを使うのはfind_freeだけ。
      • 案の定コメントに書いてあることがとんちんかんだw
      • あとローカル変数の集合に「b」とかいう名前付けるのやめてください(><)
      • もしかして、フランス語で'local'がbで始まる単語になる?
    • 参照時:
      • 変数がfに含まれていたら自由変数。これは正しい。
    • 代入時:
      • 変数がfに含まれていたら自由変数…ってf見てねえじゃん。これのせいか。
      • fを見るように直した。
(define a 1)
((lambda () (set! a 3)))
a

結果:3

やったね!!

困ったこと:

  • 「現在のローカル変数」と「外側のローカル変数」の区別がややこしい。
    • だって変数名が1文字なんだもん
    • そのうちリネームしたい。
  • sをsets.union(s.intersect(free))としてるのがよく分からない。
    • あ、setsはfind_setsの返り値、freeはfind_freeの返り値ね。
    • setsをそのまま渡すんじゃ駄目なん?
    • そもそもsは何に使われるのか
      • 変数参照のとき、その変数がsに含まれていたら、indirect命令を挟む
      • つまり、sはboxingされた変数の一覧。
    • とすれば、sets.union(s) まではよく分かる。現在のlambda中でset!される変数だけでなく、外側でset!されるものもboxingされた変数だからだ。
    • じゃああとは s.intersect(free) の意味を考えれば良い。
      • これは「sのうち、freeにも含まれるもの」という意味。
      • 逆に考えて、「sのうち、freeに含まれないもの」とは何か。これはつまり「sのうち、varsに含まれるもの」と、「sのうち、グローバル変数であるもの」。
      • これらがsとして渡るとなにがマズいか?
        • sに含まれる変数はindirect経由で参照されるのだった。
        • varsに含まれるもの(ローカル変数)はindirectしてはならない…のかな。どうだっけ。
        • グローバル変数であるものはindirectしてはならない。これは確か。
      • つまり、「indirectすべきなのは自由変数だけ」ということらしい。なるほど。
  • いきなりトップレベルで(set! a 1)とやってもエラーにならない。
    • TopEnvにエントリがないものをassign-globalしようとしたら例外を投げればいい?
    • いや、これじゃdefineもエラーになってしまうな。
      • define-global命令を追加する?
      • コンパイル時に、define対象のグローバル変数に仮の値を入れておく?
        • これで何か問題があるかな。
        • てか a["foo"] = undefined でも a.hasOwnProperty("foo") == true なのか
        • とりあえず現在のテストは通った。

2008-01-09

[scheme] SRFI萌え

SRFIってのはSchemeの拡張ライブラリを定めたものなんだけど、リスト を見ると結構言語機能のコアに近いものが並んでいたりする。

で、各SRFIにはリファレンス実装のコードが載ってるんだけど、これがほとんどScheme自身で書いてあるのね。

言語コアはすごく小さいけど言語の拡張がユーザレベルでできるってのはやっぱ格好いいなぁと。

srfi-6(文字列を入出力ポートとして扱うライブラリ)とかSchemeレベルでどうやんの?と思ったら、 read-charとかwrite-charとかを全部再定義してた*1。なるほどなぁ。

*1 正確には、再定義というかラップしてる

[biwascheme] ポートを導入した

Schemeでは入出力は全部「ポート」を介して行うことになっているので、それっぽいコードを書いてみた。

(define sp (open-output-string))
(write '(1 2 3) sp)
(display "hoge" sp)
(print (get-output-string sp))  ;=> (1 2 3)hoge

こんなコードが動くようになった。

そのうち、ネットワーク上のファイルをopenしたり、ローカルのファイルにwriteしたり*1できるようにしたい。

*1 TiddlyWikiがやってるんだから、OS/ブラウザ依存な方法を使えばできるはず


2008-01-10

[biwascheme] 文字を実装した

Char関係のコードが動かなくなっていたのを修正。 ついでに文字を扱う関数(string->listとか)も実装。ああ素晴らしき現実逃避。

てかさ、

 >>> "Σ".toLowerCase();
 "σ"

JavaScriptすごくね?

[javascript] 関数を返す関数っていつ使うの?

JavaScriptでは関数を返す関数を書くことができる。

そんなのいつ使のかって?例えばこんな時に。

例えば俺がJavaScriptでScheme処理系を作っていたとしよう。

Schemeでは「string<?」という(変な)名前の関数を使って文字列の比較ができる。

(string<? "a" "b" "c")  ;=> 真を返す

もちろん「<」だけじゃなくって、「string>?」「string<=?」「string>=?」「string=?」もあるよ。

さて、この機能をJavascriptで実装するとどうなるだろうか?

こんな感じかな。

  Env["string<?"] = function(ar){
    for(var i=1; i<ar.length; i++){
      if(!(ar[i-1] < ar[i])) return false;
    }
    return true;
  }

ユニットテストも通った、大丈夫そうだ。 じゃあ次は「>」だ。

  Env["string>?"] = function(ar){
    for(var i=1; i<ar.length; i++){
      if(!(ar[i-1] > ar[i])) return false;
    }
    return true;
  }

よしできた。

…って、ちょっと待って。これ、ほとんど上のコードのコピペじゃないかね?「>=」や「<=」に対しても同じことを繰り返すわけ?

もし俺がもっと貧弱な言語を使ってるならそれも致し方ないかも知れない。でもこれはJavascriptだ。 似たような関数がいくつもあるなら、自動生成すればいい。どうやるかって?そう、関数を返す関数を書けばいいんだ。

やってみよう

まず、それぞれの関数でどこが違うのか考えてみる。上の例なら、文字列どうしを「<」で比較するか、「>」で 比較するかが違うね。

というわけで、この比較処理から、上のような関数を自動生成するmake_string_comparerという関数を書いてみる。

  function make_string_comparer(test){
    return function(ar){
      for(var i=1; i<ar.length; i++){
        if(!(test(ar[i-1], ar[i]))) return false;
      }
      return true;
    }
  }

「return function(){...}」ってのがポイントだね。

これを使うと、「string>?」や「string<?」は以下のようにとってもシンプルに書ける。

  Env["string>?"] = make_string_comparer(function(a,b){ return a > b });
  Env["string<?"] = make_string_comparer(function(a,b){ return a > b });

なにが嬉しいか

嬉しいこと一つ目。まず、単純に、コードの行数が減る。

まだ「string>=?」「string<=?」「string=?」を実装してないけど、自動生成方式ならたった3行追加するだけで済む。

  Env["string>=?"] = make_string_comparer(function(a,b){ return a >= b });
  Env["string<=?"] = make_string_comparer(function(a,b){ return a <= b });
  Env["string=?"]  = make_string_comparer(function(a,b){ return a = b });

関数全体をコピペする方式なら、何行増えるか…考えたくもないね。 一般に、コードが増えるほど全体を把握するのが難しくなり、メンテが大変になり、バグの温床が増える。 コードをシンプルに保てるなら、それに越したことはない。

嬉しいこと二つ目。機能の追加が容易になる。

例えば、引数が文字列じゃなかったらエラーを出すように改造したくなったとしよう。 自動生成方式なら、おおもとの関数、make_string_comparerを書き換えるだけで済む。

  function make_string_comparer(test){
    return function(ar){
      assert_string(ar[0]);       //←型チェック
      for(var i=1; i<ar.length; i++){
        assert_string(ar[i]);     //←型チェック
        if(!(test(ar[i-1], ar[i]))) return false;
      }
      return true;
    }
  }

コピペ方式だと、5つの関数全てについて型チェックを行うように書き換えないといけない。

まとめ

  • 似たような関数は自動生成することで、コードをシンプルに保てる
  • JavaScriptでは、関数を返す関数を書くことによって、関数の自動生成ができる

蛇足

というようなテクニックはLisp/Schemeではごく普通の技だけど、 Rubyでは「関数の自動生成」って(できるけど)あんまりやらない気がしね?という話に 繋げたかったんだけど力尽きたので略。

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

ujihisa [修論のプログラムで、Rubyで 関数の自動生成使いまくってます。]


2008-01-11

[tetris] Re: 久しぶりに出題

そういやテトリス図プラグインとか作ったなぁ、とか思い出してみる。

良い悪いは別にして、こう置いてしまいそうな気がします。

うーむ。

もしくは、こう?

一番人気はそのまま中央に刺すという手の模様。水色を入れる操作が難しいので敬遠したんですが…いけませんね、 それぐらい自信を持ってできるようにしないと。

ツモ次第では右を平らにしてから緑で埋める方法も。

  • 1/16追記:この前emaさんに聞いたんですが、むしろ「水色待ち」と考えるようでは駄目で(ぬりかべは操作が煩雑だから)、緑で埋めるか黄を使う方法を考えないといけないらしいです^^;
  • 後者はこの場合「橙+水色」でしょうか。


2008-01-12

[tetris] 〇手詰め

以下の状況で、あなたならどう置きますか?という問題。20G (SHIRASE後半)で。

詳細は、冒頭の記事を参照して欲しいのですが…

ちょっと感動してしまったので、下にも回答を貼っておきます:-)

評価基準は、

  • 操作の簡単さ(コンコン無し、2回の横タメなし、回転を少なく)
  • 残りの地形
  • HOLDは、使わなくて済むなら使わない方が良い(操作の簡略化、赤の温存)

です。

模範解答はこちら。

その発想は無かった!

上手い人は、「赤のパタパタ」「青の飛ばし(陰謀)」のような1ブロックの操作だけじゃなくて、 こういう2ブロック以上の操作(「2手詰め」「3手詰め」)のようなパターンも頭に入ってるんだろうな、と思った記事でした。

[ruby] Rubyと高階関数

そうそう、そういうことが書きたかったのですよー。 ありがとうございます。

Rubyでも関数型言語のように高階関数(的なもの)は使えるんだけど、

  • 「関数を引数に取る関数」は通常、ブロックを使って実装される。(Rubyプログラマは多分、それが「高階関数」であるとはあまり意識してない)
  • 「関数を返り値として返す関数」はProcオブジェクトを使って実装できるが、あまりしない
    • 似たような関数を自動生成するときも、ブロックを使って定義することが多い

という違いがあって面白いなぁ、という。

やっぱり関数そのものがファーストクラスではないという点が影響してるんですかねぇ。 もしくは、これもまつもとさんの思想に誘導されているのか!?

#例えば、「関数が返り値として返す関数」をそのままメソッドにすることって「普通の」方法ではできませんよね。「ふつうの」Rubyプログラマならできると思いますが(笑)。

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

yowa [元図と段差が一段違ってますよー。 (この図だと紫で2列消し→青を無回転でスライド入れが成立しそうな。)]

yhara [直しました。ご指摘ありがとうございます。 (段差が4段だと赤を立てられるなぁと思って削ったんですが、たしかにその手順..]


2008-01-13

[rails] 1.2.3から2.0.2に

バイトとかそのへんのアレ。

参考になったリンク。

基本的にはscaffoldしなおしただけかな。2.0のscaffoldの方が、後からいじりやすくて良いです。

ただ script/generate scaffold person カラム名:型〜 とかするときに db/migrate/002_create_person.rb が既にあると そこでscaffoldの作成が止まってしまう(コントローラを作ってくれない)ので、事前にリネームしておくのが吉。

[softs] WinDeskWide

画像の説明

Windows用の仮想画面ソフト。最近のMacのSpacesみたいなやつ。 Spacesほど派手ではないけれど、画面切り替えがかなり軽くて驚いた。これなら ストレスなく常用できそうだなぁ。

とりあえず4画面作って、プロジェクトごとに分けて使っている。

  • gvim開きすぎて「どれだっけ?」と迷うことが無くなった。:-)
  • ターミナル(nyacus)も同じく
  • ファイラはまめFileの子ウィンドウを画面ごとに配置。
  • ブラウザはまだどうするのがいいか検討中。
    • 今日はとりあえずfub/Firefox/Operaを各画面で使い分けるようにしてみた(笑)。

あとWindowsでエクスプローラ使ってるプログラマは人生のn%を無駄にしているので、もっと高機能なファイラを 試してみるべきだとおもうよ!


2008-01-14

[event][javascript] Kanasan.JS Javascript第5版読書会#2いってきた

Shibuya.js meets Kansai.

もし、日本のJavascript界の歴史にいくつかの転機を見出すとしたら、今日は 確実にその内の一つに選ばれるのではないか。 前代未聞、50人弱で輪になっての読書会。もちろん、グループ分けは無し。 果たして読書会として成立するのか? Shibuya.jsからの使者amachangはLTに間に合うのか? 博多行きの新幹線に乗ったnaoya_tは無事新大阪で降りられるのか!? 続報、刮目して待て!

(訳:例によってやたら長いログを書いてるのですが編集中なので待って)

[javascript] Javascriptで無限ループを実現する5つの方法

「Javascriptで無限ループを実現する5つの方法」というLTをしました。

内訳を先に書いてしまうと、

  1. 普通に書く
  2. setTimeout
  3. jsThread
  4. JSDeferred
  5. オレオレ言語

の5つです。

同期処理したいのにsetTimeoutとかonSuccessだとかうぜえ、同期処理は同期処理っぽく書かせろ! という人のために。


2008-01-15

[english] うちのWikiを英語になおすと

trackfeedを見てたらこんなURLが。

SVKを使ってみよう
Try using SVK
Vim覚え書き
Vim Note
Screenをはじめよう
Getting Started on Screen
ZenTestまとめ
ZenTest at once
びわこ開発合宿#2
BIWAKO development camp # 2

参考になる。

[javascript] Javascriptとマクロ

Javascriptのすごいところは、C言語風にも書けるし、関数型言語風にも書けるという柔軟性の高さだ。 だからこそ、10年以上経った今なお「最先端」にありつづけるのだろう。

BiwaSchemeを作ってて思うのは、JavaScriptが予想よりずっとSchemeに近いこと。 整数や文字の仕様とかね。(実は参考にした?)

あとはマクロさえあれば、本当にLispになっちゃうんじゃない?と思うわけで、↓の記事にはやられた、と思った。

Narcissus(Javascript on Javascript) ってのは 知らなかったけど、ネーミングセンスがいいね(笑)。 これ、どれくらいまで実装してるんだろう。

jsthreadパーザは Rhinoのparser.javaの移植で、1300行くらいの模様。これを使うという手もある。

言語マニア的な視点

さらに未来のことを考えてみる。

Schemeの信念である「シンプルさ」は、(R6RSが実用方面に舵を切ったとはいえ) これからも保たれるだろう。 一方のJavascriptはどうか?ECMAScript4では、「互換性を継続しつつ、より大規模なコードや複雑なアプリケーション構築にも対応できるようにしていきたい」という (@IT)。

機能を強化することは、言語の寿命を延ばすのか、縮めるのか。 言い換えれば、言語が長生きするために必要な機能とは何だろう。 Java/C#にシェアを奪われたC++のことを考えると、少なくとも「機能は多ければ多いほど良い」というものではないようだが。

追記

構文の拡張に必ずjsパーザが要るわけではないという例。うわーうわー。

<script src="heredoc.js">
str1 =<<EOT
複数行の文字列です。
下の行でおわりです。str1はグローバルになります。
EOT
</script>

しかも、scirptタグの使い方を工夫することによって、普段js書くときと「変わらない感じ」で使える。やばい。

[misc] ある分野に特化することによって高い効果を得る

髭よりも、顔写真が動くところに驚いたんだが。

Lingrで教えてもらった。

おもろいのう。

「2D画像を3D化する」という研究はいくつもありそうだけど、それを「顔」に特化させることで面白いものができる、と。


2008-01-16

[ruby] おまいらはもっと型が無くても読みやすいソースの書き方について考察するべき

後、この後の話は、RailsというよりRuby、Rubyというよりスクリプト言語のことなんだけど、他人の作ったソースコードを読んだり、利用したりするのは、スクリプト言語は、Javaと比べてつらいと思う。型の情報がないから。標準ライブラリのように使い方が判っているやつならいいんだけど。

[2008-01-11 - ひがやすを blogより引用]

もう少し大きいので、Railsのソースなんかを読んでも「型宣言がないために読みづらい」という感じはしません。このへんはやはり慣れと作り方(命名の仕方とか、イディオムの統一とか)ではないでしょうか*3。

[Re: そろろろRailsについて本音を書いてみるか - moroの日記より引用]

この前bitclustのコードを読むのに苦労した俺としては、 型宣言がない言語は型宣言がある言語より他人のソースを読むのが大変、というのは、 (不等号でいえば)確実に真であると思います。 で、問題は「どのくらい」大変になるのかであって、 moroさんは命名の仕方とか、イディオムの統一でなんとかなるレベルである、と書いている。

「型宣言がないことを補うような命名の仕方とか、イディオム」についてもっと考えてみたいところ。

関連URL

とか、そういうことを書こうと思っていたのですが、元となったmixi日記を読んでみたらいろんなことを考えてしまって まとまらなくなったのでURLだけ貼って終わる。

あと大田@mixiさんの日記はすごく面白いので、ぜひ外部ブログを開設してほしいです:-) mixi会員にしか読めないなんて、世界(と太田さん)にとって損失だと思います。

考えたこと:

  • Seasar2でLightweightな方向に近づくJavaと、Railsでエンタープライズに近づくRuby。両者はこれからどうなっていくのだろう?
  • 「いわゆるエンタープライズ」で作られるプログラムは特徴があるらしい。
    • とりあえずクラス数が死ぬほど多いらしい
    • 登場人物と分岐と例外が多くて大変
    • 複雑と煩雑の違い(深さが大きくて難しいのと、平面的だけどどにかく数が多くて難しいのと)
  • Rubyは(もしくはLLは、もしくは関数型言語は) そういうプログラムに対して太刀打ちでき得るか?どのような機構/手法がそれを可能にするか?
    • ポールグレアムのYahoo! Storeのことを考えたんだが、あれは煩雑ではなく複雑の方か

[prog] nanto_viさんに学ぶプログラミング言語の学習法

Kanasan.JS参加者なら既にご存知だと思いますがwnanto_viさんとはJavaScript(ECMAScript)の仕様に最強に詳しい方です。

JavaScriptの仕様をすらすらと語るnantoさんに「どうやって勉強したんですか?」と聞いてみたところ「2chのJavaScriptスレに張り付いてあがる質問を自分で解いていた。レスするわけではなかったけど。一通り行けるかなとおもったころで仕様書を読み始めた。」と感動する答えをいただく。

[wwwakuより引用]

なんと!

新しい言語を学ぶのに「問題集を解く」のは非常に有効だと思うんですが、 2chスレの質問を利用するという発想は無かったです。

「問題集」のこと

最近だとあなごるとかどう書くとか SPOJとかあって便利ですよね。

L-99http://suakx.com/node/218P-99(Prologのための99の問題)というのも非常に気になります。

これからは「〇個の方法」より「〇個の問題」の時代か!?

さらに飛躍するけど

こういう時にありがちなのが、「やってみようと思ったけど、既に誰かがやってるみたいだからいいや」という考え。 俺もよくあります^^;;

でも、時が経てば経つほど、解決済みの問題ってのは増えていくわけで…。 面白そうな問題だけど、人がやってるからやらない!ってのは勿体ないですよね。

それに、別の回答はそれはそれで回答者の個性が現れて面白いもの。意味がないわけではありません。

問題も回答も山のように手に入る時代、これからプログラマにおいては、 「似たようなものが既にある、でもそんなの関係ねえ!」というスルー力が重要になってくるんじゃないかな、 とそんなことを思った冬の夜。

---

[この段落は以下の記事にインスパイアされました] (結論が出るまで3ヶ月かよ、という)

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

hoge1e3 [この前はお疲れ様でした. Eclipseに毒されて型のある言語しか触れなくなった者です. やっぱりここはRubyに..]

uehaj [はじめまして。「型宣言があるほうが良いのか無いほうが良いのか」について記事を書いてみました。Groovyの「オプショ..]


2008-01-17

[event][scheme] gauche.night 第2夜

http://practical-scheme.net/wiliki/wiliki.cgi?gauche.night

明らかに行くしかない

LTの募集は2/17まで.

[event][javascript] Kanasan.JS Javascript第5版読書会#2 のまとめ

Kanasan.JSサイ本読書会 #2 におじゃましてきました。

今回は読書会としては前代未聞の規模で正直どうなるかと思ったんですが、意外となんとかなって感激。 他言語(Javascript以外)の話題を制限したことも、今回は有利に働いたと思います。

リンク集

記録

以下、読書会のログです。なるべく会場の雰囲気を再現するように心がけてみました。

10:15

全員、順番に自己紹介する。俺到着。(迷ったorz)

10:23

amachang到着!

10:39 LTのターン

LTは、自分のスライドをいじってたりであまり聞けませんでした…_|‾|○ tyoroさんのブログが よくまとまっているので、 そちらを参照すると良いかと思います。

  • 長さん(hoge1e3さん)
    • 関東から
    • Web上でのゲーム製作
  • nanto_viさん
  • amachang
    • JSDeffered
  • 岩田さん
  • AWAWAさん
    • http://stack.nayutaya.jp/user/AWAWA
    • 自己紹介LT :-)
    • 仕事でJava + Javascript
    • 1年目:「Javascript?Javaの簡易版?」
    • 2年目:変数宣言とfunctionくらい
    • 3年目:document.write, innerHTMLを知る。JSで背景色が変更できることも知る
    • 4年目:thisの存在を知る。newでクラスっぽいものが作れることに気付くが、裏技だと思っている
    • 5年目:prototype.jsを知る
    • 6年目:Kanasan.JS ←いまここ!
    • 自作フレームワーク
  • emaさん
  • yhara
    • 非同期処理を同期っぽく書く方法について
    • 20080114#p02
  • ujihisaさん
  • deqさん
    • Flexでリアルタイム音生成
    • Flexの認知度:会場の6〜7割
    • 修論で、人間の喉から音が出る仕組みを物理シュミレーションを使って解析している
    • マシンから「い〜〜い」という声っぽい音がw
    • 秒間36000回くらいの計算をしているが、AS3でこなせてしまう
    • アイデア:ASで初音ミク(笑)
    • アイデア:物理シミュレーションのFLASHに、音もシミュレーションして鳴らす

12:24 休憩

昼飯。近所のラーメン屋へ。

  • amachangからサイボウズラボのここに書けない話を聞く(笑) *1
  • 関東のイベントは学生が少ない気がする。もっと来てほしい
  • JSで書くと使ってもらいやすいのが良い。
  • JSRubyHotRuby。つまり、パーザとVMはあるので、構文木からYARVのバイトコードを生成する部分をjsで書けばPure Javascriptかつ高速実行できるRuby処理系がwww
  • amachangにhttp://stack.nayutaya.jp/を宣伝した
  • amachangに「ふつける」をおすめした

4797336021

  • 「読書会ってこんな感じなのか」と聞かれたんですが、関西の最近の読書会がこんな感じ(Extreme reading)なだけで、普通の読書会(?)はこうではないかも。
    • 他には、毎回一人が決められた範囲を読んでみんなに説明する輪講形式とか。

13:30ごろ

机を並べ替え、大ーーきな長方形を作った。

…あの、対面がものすごく遠いんですが…w

13:36 読書会のターン

3つの重要なURL:

今回のルール:

  • 他言語トークは控えめに (いつもJavascript以外の言語の話に脱線するのでw)
  • イニシャルトークとかしない、全員で話題を共有する

13:48

ネットワークの設定に時間を取られる。

その間の繋ぎとして、cuzicさんによる自著宣伝のコーナー(笑)。

4839926689

naoya_tさんにLTしてもらうか?という案もあったけど、 ネットワークの設定が終わったので幻のLTに。

13:51

iyou.png

13:52

Jashの使い方の説明。ブックマークレットなので、D&Dとか「お気に入りに登録」するのがインストール。

14:03 p81〜p103

しばらく、誰も喋らずチャットのログだけが伸びる異様な雰囲気にw

14:11 if文のカッコ

  • Q. p83のカッコは必要?:
    • カッコなし: if(address == null || address == "") {
    • カッコあり: if((address == null) || (address == "")) {
  • A. 中身が長いときは下の方が読みやすい。
    • 中身がとっても長いときは||のあとに改行入れるよね

14:13 空チェック

  • Q. 「nullまたは""か」のチェックって、ライブラリで提供されてたりしない?(Javaでいう)ApacheCommonのStringUtilみたいな。
  • A. Prototype.jsを使うと str.empty()で""かどうかはチェックできる。nullはチェックできないけど

14:14 caseラベル

  • caseラベルに(リテラルだけでなく)任意の式が書けるのか
    • これはC言語使いから見たら衝撃
    • p87の脚注にそんな話が。
  • switch(true){ case 条件式: ... } みたいに書けて便利、らしい
    • if elseを使えという話もあるが(どちらが綺麗か?)

14:16

shabereyo.png

14:20 式と文について

  • Q. function なんちゃら〜が式になるか、文になるかはどうやって決まる? 関数名が付いてるかどうか?
  • A. いや、関数名が付いてるかどうかは関係ない。function〜の前が文の終わりかどうかによって決まる。(後ろは関係ない)

というamachangとnanto_viさんの議論。

正確なところはnanto_viさんのログに期待^^;;

以降、式と文について喧々諤々。

  • Q. PrimaryExpressionてなに?
  • A. ECMAScriptの文法定義の一部。「リテラルか、変数参照か、式を()で括ったもの」とされている。
  • Q. なんで関数適用はPrimaryExpressionじゃないの?(amachang)

PrimaryExpressionって概念的になんなの?という話。

  • それ以上分割できないexpressionという意味とかではないのかな (hakobe説)
  • 文法の一番小さいやつがPrimaryExpression?? (kanasan説)
  • 実は「Primary」というネーミング自体にそんなに深い意味はないんじゃない? (yhara説)
  • Q. そもそも式と文の違いってなんなの?
  • A. 評価して値になるのが式。値にならないのが文。
    • 例えば、ifの条件のところには値が必要なので、if(式) はOKだけど、if(文)はNG。
    • というようなことは、文法定義を見れば分かる。(英語では、式=expression, 文=statement)

14:40

  • 脱線が元に戻らないので、この話はここで終わりに
  • 文と式が禁止ワードにw

14:44 これはひどい

p91の「こんなこともできます」の例がひどいwww

var o = {x:1, y:2, z:3}
var a = new Array();
var i = o;
for(a[i++] in o)
  ;

これを実行すると a = ["x", "y", "z"] になる(笑)

14:47 ラベル文の話

  • Q. なんか「6.10 ラベル文」がよくわかんないんだけど…
  • A. ラベルは主にbreakとcontinueで使うものだから、「6.11 break文」「6.12 continue文」を先に読むべき。というか章としてこっちが先に来るべき

ちなみに、JavaScript において、break 文は continue 文とは違い指定されるラベル名がループ文のものである必要はない。なので、

(function () {

...

if (shouldExit) return;

...

})();

と書く代わりに、

main: {

...

if (shouldExit) break main;

...

}

と書くこともできる。

[userChrome.js 用スクリプト: Days on the Moonより引用]

  • Q. ここでcontinue main; とするとどうなる?
  • A. p94真ん中 breakと違い、continueはループの中でしか使えない。
  • Q. 初心者的には、入り組んだループの中でどのループに対するbreak/continueなのかを指示するための記述ってことでいいですよね<label
  • A. そうだと個人的にはおもうんですけど、実際はどうなんでしょうね(kanasan)
    • 多重ループのときに使うのが一般的ですな。>ラベル (cuzic)
  • Q. break if(a){ label1 }else{ label2 } みたいに、動的にラベルを変えることはできるか?
  • A.できない。breakのあとにはラベル名のリテラルしか書けない。
  • Q. eval("break " + jumpto ); とかしたら、動的にbreak先を変えられたりしない?
  • A. eval文字列の外には飛べないっぽい。

15:00 functionがどこに書けるかの話

  • Q. Javascript では、最初に function 定義をぜーんぶ拾って、それから実行がはじまるんですか?<P.97
  • A. そうです

下にある関数も呼び出せるよ、という例。そういやRubyは下にある関数は呼び出せないんだっけか。なんかびっくりした。

なぜかAST(抽象構文木)の話に。抽象構文木は、プログラムを解析して木にしたもの(構文木)から余計なカッコとかの情報を取り除いたものらしい。(Wikipedia)

if文で関数定義を切り替えようとして嵌った話。

if(typeof(WScript) == 'object'){ //for cscript.exe(WSH)
  function puts(str){ WScript.Echo(str+"\n") }
}
else{ //for SpiderMonkey 
  function puts(str){ print(str+"\n"); }
}

これで環境によらないputs関数ができたお!!と思いきや、「if文の分岐結果によらず」下のputsの定義が 採用されてしまうという結果に。

そもそも、仕様上はif文の中で関数宣言(名前つき)してはいけない。無名関数ならOK。

if(typeof(WScript) == 'object'){ //for cscript.exe(WSH)
  var puts = function(str){ WScript.Echo(str+"\n") }
}
else{ //for SpiderMonkey 
  var puts = function(str){ print(str+"\n"); }
}
  • Q. じゃあfunction hoge(){}より var hoge = function(){}の方が安全という事ですね?
  • A. そうですね。
    • 後者ならif文の中にも(合法的かつ安全に)書けるので。
    • 名前が重複したときの動作を予測しやすいので。
    • 関数を使えるようになるタイミングが予測しやすいので。

15:13 空文の話

p.102では、空文を書く時はコメント入れましょうという提案がなされている。

  • サイ本方式: for(...) /*空文*/ ;
  • kanasan式: for(...){ ; }
  • tyoro式:
for(...)
  ;

それとは別に、「うっかりセミコロン問題」が紹介されているのだが、こっちは有効な解決策が 提案されていない(「注意しましょう」と書かれているだけ)。

if(...);          //ここにうっかりセミコロンが付いているので
  alert("hoge")   //この行が必ず実行されてしまう!

まあでも、「手がすべってセミコロンが入ってしまった」とか、注意する以外に解決策ないよねw

  • vimやemacsを使っていれば、インデントが狂うのでわかるはず
    • いや、人によってはインデントが狂うのが普通の状態のこともあるので、気付かない可能性も…
    • Q. それってどういう時よ?
    • A. Prototype.jsによるクラス指向など、Javascriptの新しい書き方がどんどん出てきていて、エディタが対応しきれてないのです

端的には、JSDefferedの「行末ドット」とか

行末派:

method.
chain()

行頭派:

method
.chain()
  • nanki: セミコロンをEnterに割り当ててるからそもそも押し間違えないよ派

15:22

あまりに人数が多いため、進捗状況が揃わなくて休憩が取りづらい罠。

でもあくまで全員が読み終わるまで待ちたいkanasan++

15:23 varが式でも…

  • Q. varが式でもいいんじゃない?(amachang)
    • whileの条件文とか三項演算子の中で新しい変数を導入したいときがある。
  • A. while(var a = ...){ ...} で a のスコープをwhileの中に限定するには、やっぱforみたいに特殊な構文で扱わないといけない気がする
  • Q. あれ、でもforにはvarが書けるじゃん。for(a;b;c) のaのところって文が書けるの?
  • A. p90 書けない。式しか書けない。varだけ、特例で許されている

forの構文の定義。まさにvarだけ特別扱いされている!

  • ちなみに "StatementNoIn" なのは、ここに「in」というキーワードが来るとfor〜in文と間違うからでは?という説。 StatementNoIn = 「in」以外の全ての式
  • せめて "StatementWithoutIn" だろ、というツッコミw

15:36

休憩を挟んで、サイ会再開。

nasrin.png

nasrin2.png

7章(p.105〜p.122)に突入。

15:41 配列の生成方法

  • Q. var ary = {} と var ary = new Array(); は同じ?
  • A. amachang: var ary = []; と var ary = new Array(); と var ary = Array(); は同じ

15:43 「.」は演算子か

  • Q. 「.」って「演算子」なのか?(amachang)
    • 日本語のecmascript仕様には演算子とかいてあるけど…、英語では別の表現(accessor)になっている。(演算子は"operator")
  • amachang:演算子ではないと思うよ派
  • nanto_vi:演算子の定義によるよ派
  • ema:「.」は「[]」の別名なので、演算子というよりシンタックスシュガーだよ派
    • シンタックスシュガーとは: 文法上、他の書き方ができるが、簡便のために用意されている構文のこと。
  • hakobe:でも.の後は識別子しか与えられないので厳密にはちがいそうだよ派
  • yhara:君が演算子だと思うものが演算子だよ派 (嘘)

15:58 「.」と「[]」はどっちが速い?

  • Q. x.aとx["a"]はどっちが速い?
  • A.ドットの方が速「そう」ではある。実際にどうなのかはベンチマーク取らないと分からない
    • nanto_vi: spidermonkeyでは、[]の中が単純な文字列なら、どちらも同じ中間コードにコンパイルされるので、速度に違いはない。
    • naoya_t: いやパースが3文字分短くなるからその分だけx.aの方が速い (※ミクロなレベルで)
    • ema: 100000 回ループで参照だけしてみましたが、たしかに差はなかったです @ firebug
    • yhara: あまり話題にならないってことは、あまり差がないんだろうなぁ、と想像。
    • yuya: キータイプ的には「.」の方が断然速い(笑)

16:03 jsインタプリタ

  • Q. 全然関係ないんですけど、jsrun(コンソールでファイルを実行)とかできたりしないですかね?

この質問、本番では拾えなかったんですが、

  • spidermonkey (Cによるjsインタプリタ)
  • rhino (javaによるjsインタプリタ)
  • cscript.exe (windowsのみ)

など、コマンドラインで実行できるjsインタプリタはいくつか存在します。

16:03 カンマだけで値を省略した場合は、未定義の要素が生成されます

p.113

var a = [undefined, undefined, undefined]
var b = [,,,]; //カンマ3つなのに注意!
  • Q. これらは同じか?
  • A. nanto_vi: 厳密には異なる(!)
    • 上の場合a[0]は存在してundefinedが入るが、下の場合b[0]がそもそも存在しない

Operaで試してみました。(Firefoxは挙動が違うかんじ)

 >> a.hasOwnProperty("0")
 false
 >> b.hasOwnProperty("0")
 true

16:04 undefinedとnullの違い

  • Q. undefinedとnullの違いは?
  • A. いろいろあるけど、例えばnullは予約語だが、undefinedは予約語ではない(単なるグローバル変数)。

なので、undefined = 100 みたいなコードも許される! undefinedを扱う時は、上書きされ得ることを想定していないといけないw

ただしundefinedオブジェクト(って言うのかな?)そのものが変更されるわけではないので、

var u = void 0; //参照:p78 void演算子
var u = (function(){})()

とかすることで元のundefinedオブジェクトを取得することができる。

16:08 プロトタイプ

hoge1e3さんの質問:

A=function(){}
A.prototype = {
   get: function() {return this.x*2;}
}
a=new A();
alert(a.constructor==A);

A=function(){}
/*A.prototype = {
   get: function() {return this.x*2;}
}*/
a=new A();
alert(a.constructor==A);

(以降、hoge1e3/amachang/nanto_viの3名による議論が行われたが、会場のほとんどがついていけなかったw)

プロトタイプチェインの話は9章なので、「続きは9章で」ということに。

  • Q. hoge1e3: (最初に意図していた質問) Perlのように、後からクラスを変えることは可能か?
  • A. nanto_vi: できない。

16:17 「0」というプロパティ

  • Q. 7.6.1と7.6.5の「配列の要素」とオブジェクトの話なんですけど「0」というプロパティをオブジェクトに追加したとすると配列の添字としては「0」は使えない、んですかね?
  • A. kanasan: どこかで暗黙の型変換が発生しするため、0という名前のpropartyと配列は同時には使えなかったはずです。
    • ema: 以下はtrueになります。
 var a = [{},{}];
 debug(a[0] === a['0']);

16:19 プロトタイプベースOOPについて

  • Q. 「クラスを実体化したものがインスタンス」としたら、プロトタイプはどう説明する?
  • A. amachang: jsでは「Array」もオブジェクトと同じように扱える。それがプロトタイプベースの世界。と自分は捉えている

参考? プロトタイプベース・オブジェクト指向

  • プロトタイプベースの元祖はSelfという言語らしい。

16:30 勉強会終了!

次回は7.5「配列」から。(既に7.5に入ってる人もいたが、きりが良いため&足並みを揃えるため、復習も兼ねて)

まとめ

ここまで読んだ人++。

Kanasan.JSは初中級者向けを標榜しつつ、実際にはちょくちょく高度な会話が飛び交い、しかし 初心者も上級者も「楽しかった」と言って帰って行くという非常に変わった集まりである。 勉強会における「初心者向けと上級者向けの兼ね合い問題」*2 に対し、一つの理想形を示したと言えそうだ。

もちろん、そのような雰囲気が自動的にできたわけではなく、その裏では 主催であるKanasanの「グループ分けはしない」「質問は全員で共有する」「全員が読み終わるまで待つ」 といった、場の一体感を重視する強い意志が働いていたことをここに記しておく。

Kanasan.JSは関東のイベントへの憧れから始まった勉強会だが、 関東とは別の価値を提示することによって、 いずれは逆に関東の人が来たくなるような勉強会になればいいなぁと思う。

*1 いや、個性的なメンツが多くて事務の人も大変〜みたいな他愛のない話ですけど

*2 Ruby関西も苦労している

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

ema [長文まとめお疲れ様です!! サイ本 p.113 にも書かれているのですが、[,,].length == 2 だった..]

yhara [しまった。カンマ3つに直しておきました。どうもです。 「省略なんて使わない」には同意w]


2008-01-18

[ruby] シンタックスハイライトを行うライブラリ、Text::VimColorがRubyに移植されたようです

Ruby-VimColorキター!

誰か移植しないかなーと思ってたところだったので嬉しい。CodeReposにリポジトリがあったので、 とりあえずREADMEに使い方とかを追記してみた。

で、ソース見たんだけど、これvimをforkで起動してるのなwww てっきりシンタックスファイルをパーズしてるのかと思っていた。

元となったPerl版はこちら。

シンタックスハイライトを行うライブラリっていろいろあるんだけど、 対応フォーマットの数では多分これより多いものって無いんじゃないかな。 Vimをインストールすると分かるんだけど、 RubyやPerlはもちろん、Haskell, OCamlからZ80に至るまで、実に400を越えるシンタックスファイル(*.vim)が 同梱されてるんですな。 はてなダイアリーで使ってるのも多分これじゃないかと 思われ。

まぁ各言語の厳密なパーザを使うわけじゃないんで、専用のライブラリと比べれば精度は落ちると思うけど、 「多言語」と「手軽さ」がVimColorファミリーの一番の魅力かなと思います。

試してみよう

とりあえずチェックアウト。(svnない人はhttp://svn.coderepos.org/share/lang/ruby/vimcolor から)

svn co http://svn.coderepos.org/share/lang/ruby/vimcolor ruby-vimcolor

まず、同梱のコマンドラインツールを試してみる。

$ ruby -Ilib vimcolor-embed lib/vimcolor.rb -f html > a.html

とするとHTMLの断片が出力されます。

これに、適当なCSSを加え、全体を<pre>でくくってやると…

vimcolor

おおお。こいつ、動くぞ!

使ってみよう

Rubyから使うにはこんな感じ。

$LOAD_PATH << "./lib"
require 'vimcolor'

vc = VimColor.new
html = vc.run_file("lib/vimcolor/mark.vim", {}, :html)
puts <<EOD
<html>
        <head>
                <link rel="stylesheet" href="light.css" type="text/css"><link>
        </head>
        <body>
                <pre>
#{html}
</pre></body></html>
EOD

非常に簡単なのです。

Rubyでシンタックスハイライトしたくなったら、ぜひ一度お試しください。

余談

コード書いてみて思ったんだけど、run_fileの3つめの引数はいらなくね?

vc.run_file("foo.rb", {:format => :html})

とか書きたいです(><)

(こういうときに、「じゃあコミットしといて下さい」という返しができるのがCodeReposの良いところですね:-)


2008-01-19

[biwascheme] クロージャを実行するような関数をjsレベルで定義できるようにした

BiwaSchemeでは、基本的にライブラリは全てJavaScriptで書くことにしている。

が、ここで問題になるのが、mapのように中でSchemeのクロージャを呼ぶような関数だ。 クロージャが実行中にスタックを参照することもあるので、単純に新しいインタプリタを起動するわけにはいかない。

とすると動的に中間コードを生成する方法が思い浮かぶが、単純にクロージャを実行する中間コードを返してはいおしまい、というわけにもいかない。 mapのようにクロージャを何回も呼ぶ場合があるからだ。

悩んだ末に、こんな感じに書けるようにしてみた。

  define_libfunc("map", 2, null, function(ar){
    var proc = ar[0], ls = ar[1];
    var a = [];
    
    if(ls == nil)
      return nil;
    else{
      return new Call(proc, [ls.car], function(ar){
        a.push(ar[0]);
        ls = ls.cdr;
        if(ls == nil)
          return a.to_list();
        else
          return new Call(proc, [ls.car], arguments.callee); //無名再帰。JavaScriptは便利である
      })
    }
  })

まず、ライブラリ関数の中で、呼びたいクロージャ、それへの引数、クロージャを呼んだあとの処理(after)をまとめたオブジェクト (Callというクラスのインスタンス)を作り、それをreturnで返してやる。

インタプリタは、ライブラリ関数がCallを返したら、「クロージャを実行したあと、その結果を引数にして afterを呼び出す」という処理を表す中間コードを生成し、それを実行することにする。

これで、今のところちゃんと動いているようだ。

(let1 a 1
  (map (lambda (x) a) '(1 2 3)))  ;=> (1 1 1)

考えること:

  • これで本当に大丈夫か?おかしくなるケースはないか?
  • JavaScriptで書けるっていうけど、必ずCPS形式で書かんといかんよね?それはもうSchemeで書いた方が楽では?w
  • 変数のスコープとか大丈夫か?そのaが外から破壊されることはないか?
  • スタック溢れたりしない?
    • Callを返す→そのafterを呼ぶ→Callを返す→afterを…なので、after自体がネストして呼ばれるわけではない。ので大丈夫。

[biwascheme] BiwaScheme 0.3をリリースしました

びわこ開発合宿#2に来ています。本当は修論書きを頑張るはずだったんですが… どうしちゃったんでしょう。

0.2からだいぶ機能が増えたので、BiwaScheme 0.3をリリースしました。 ダウンロードはこちらから→BiwaScheme

変更点は以下です。

new

  • 関数が増えた
    • $ (getelemと同じ、($ "hoge")とか)
    • vector関係
    • 文字関係
    • リスト関係(append, list?, for-each, write/ss, equal?)
    • map系、for-each系(まだ複数のリストは未対応)
  • 構文展開を実装し、構文が増えた
    • and, or, let*, when, unless, let1, letrec, letrec*(このへんちょっとあやしい)
  • quasiquote, unquote (vectorはまだ)
  • ポートの導入
    • 文字列ポート(open-output-string, get-output-string)とか

change

  • ディレクトリ構成を変更 (lib/, test/)

fix

  • caarからcadddrまでの実装が逆になってた(cadrがcdarの意味になってた^^;)のを修正
  • to_scmという変な関数を、to_writeとto_displayに分離(それぞれwrite/display的に文字列化)

気がついたら、R6RSの標準関数が8割くらい実装できていました。 そろそろ標準ライブラリにも手を出すかなぁ。Chapter 3 List utilitiesとか。

[misc] ソリティアとマインスイーパーにつぎ込まれる労力を有効活用できないか

風呂場で、「現実逃避にソリティアとかついやっちゃいますよねー」という話をしたんですが。

ruby-minisatというライブラリがあります。

数独などのパズルの盤面を、SATという単純な問題に落とし込むことで、SAT用の高速なソルバがそのまま利用できてウマーというものです。 SATをパズル問題の中間言語として使うわけですね。

ということは、SETI@home などで必要な計算をSATに変換して、さらにそれをマインスイーパーの問題に落とし込めば、 ゲームで遊びながらSETI@homeに協力することが可能なのではないでしょうか?

ってもちろん、人間がマインスイーパーを解くより、コンピュータがSATの解を見つける方がはるかに速いので、 できたところであまり意味はないんですが(^^;。

でも画像認識のように人間の方が優れている問題もあるので、うまくそういう問題に変換することができたら…。 「マインスイーパーを解き続けるだけの簡単なお仕事です」なんてバイトができる時代になるかも。

cf. エ□グリッドコンピューティング


2008-01-20

[biwascheme] クロージャを実行するような関数をjsレベルで定義できるようにした(2)

JavaScriptで書けるっていうけど、必ずCPS形式で書かんといかんよね?それはもうSchemeで書いた方が楽では?w

と書いたのだが、よく考えるとリストやベクタの各要素について一度ずつ繰り返すものばっかりなので、 そういうループをCPS化したものを用意し、コールバックでカスタマイズできるようにしてみた。

mapがこんな風に書ける。

  define_libfunc("map", 2, null, function(ar){
    //TODO: multi lists support
    var proc = ar[0], ls = ar[1];
    assert_pair(ls);

    var a = [];
    return Call.foreach(ls, {
      call: function(x){ return new Call(proc, [x]) },
      result: function(res){ a.push(res); },
      finish: function(){ return a.to_list(); }
    })
  })

remp (Rubyでいうreject) はこんな風に書ける。

  define_libfunc("remp", 2, 2, function(ar){
    var proc = ar[0], ls = ar[1];
    assert_pair(ls);

    var ret = [];
    return Call.foreach(ls, {
      call: function(x){ return new Call(proc, [x]) },
      result: function(res, x){ if(!res) ret.push(x); },
      finish: function(){ return ret.to_list(); }
    })
  })

実装

Call.foreachは昨日書いたCallのラッパーで、以下のような定義になっている。Iteratorは自分で書いた外部イテレータで、配列、文字列、リストに対応している。

  WebScheme.Call.foreach = function(obj, callbacks){
    var iterator = null;
    var x = null;

    var loop = function(ar){
      if(iterator)
        callbacks["result"](ar[0], x);
      else
        iterator = Iterator.of(obj);

      if(!iterator.has_next())
        return callbacks["finish"]();
      else{
        x = iterator.next();
        var result = callbacks["call"](x);
        result.after = loop;
        return result;
      }
    }
    return loop(null);
  }

クロージャ呼び出しの前後で分断されるので、loopはリストの長さより1回多く実行されることに注意。例えば3要素のリストに対して繰り返す場合、

loop 1 : call
loop 2 : result
         call
loop 3 : result
         call
loop 4 : result
         finish

のように、4回loop関数が実行される。

さて、あとは、もうちょっと良いコールバック名を考えるべきか(^^;

[biwascheme] ネットワークからソースを読み込む機能を実装した

ネットワークから別のソースを取ってきて実行する機能を実装しました。

(load "plapla.scm")
(plapla "hitode909")

例。REPLで上のソースを実行すると、サーバ上に置かれたplapla.scmの内容がロードされて、

"hitode909++"

という結果になります。:-)

plapla.scmの中身が見たい場合は

(display (http-request "plapla.scm"))

など。


2008-01-21

[event][haskell] Re: Haskell Hackathon やりたいね! - yukobaの日記

BiwaSchemeの人とか、来てくれたら嬉しいなぁ。

と言われたらもう行くしかない!

Rubyで書こうかと思ったけど、ささださんがいるならSchemeにしようかなぁ。(いろんな言語がある方が面白いですよね?) もしくは僕がRuby 1.8でささださんがRuby1.9とか(笑)

ただいま関西会場を検討中。

[event][haskell] Haskell Hackathonがいかに革新的なイベントであるかについて

  • 〇〇勉強会:みんな言語もやることも同じ
  • LiveCoding:みんな言語もやることも違う
  • GaucheFestとか: みんな言語は同じでやることが違う
  • 今回:みんな言語は違うがやることが同じ!

これはあたらしい


2008-01-22

[prog] 型についてもうすこし考えてみた

前回の話題について。言及してくださった方ありがとうございます。

んで、あれからちょっと考えてみたんだけど、全く考えがまとまらない罠。

型の効果

とりあえず、変数の型を明示することにはいろいろな効果がある。

  • (A) コードを読むのに役立つ
  • (B) 型チェックに役立つ (バグのないプログラムを書くのに役立つ)
  • (C) 最適化に役立つ

変数の型、データの型

これは昔大林さんに聞いた話で、どっかに書いてあるかもしれないけどもっかい書く。

- 変数に型がある変数に型がない
データに型があるJavaとか(静的型)Rubyとか(動的型)
データに型がないC/C++ Forthとか(昔の言語)

型ありと型なしのミックス

Javaは「全部、型を書く」で、Rubyは「全部、型を書かない」なんだけど、その中間のアプローチもある。

これには2種類あって、まず「基本は型ありで、省略可能」なもの。いわゆる型推論ですね。Haskellとか。

もう一つは、「基本は型なしで、付加可能」なもの。 Lisp系は一般的に変数に型がないけど、Common Lispはオプションで型を指定できることが仕様で規定されているらしい。 id:uehajさんが書いてらしたGroovyもそうだとか。

両者は似ているようで、決定的な違いがある。前者は型を省略した変数も型を持つ(型チェックされる)が、 後者は型を省略した変数には型がない(型チェックされない)。

というのを踏まえて

型の効果を見直してみる。

  • (A) コードを読むのに役立つ
    • このための型は省略不能。(省略したら意味がないw)
  • (B) 型チェックに役立つ (バグのないプログラムを書くのに役立つ)
    • このための型は省略可能。できれば書かない方が楽。
  • (C) 最適化に役立つ
    • このための型は省略可能。できれば書かない方が楽。

すごい中途半端なところで終わる(笑)。

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

Before...

ikegami-- [(A)の場合でも型は省略してもいいんじゃないでしょうか。もしも、型推論の結果を反映したドキュメントを自動生成できれば..]

yhara [そうそう、ツールの補助によるアプローチについてもう少し考えたかった。のです。かも。]

uehaj [(A)(B)は「コメント」の話に構図が似てるかもしれませんね。 (A)は「適切にコメントをつけると読みやすい」とい..]


2008-01-23

[biwascheme] BiwaSchemeでプレゼンツールを実装しました

先日のびわこ開発合宿#2の成果発表のときに、なんとなくプレゼンツールが作りたくなって、 突発的に作ってみた。

ソースはこんな感じ(笑)。

<script src="../lib/biwascheme.js">
(load "presen.scm")
(define *presen*
  '#(("自己紹介" 
      ("yharaです"
       "y y hara"))
     ("やろうと思ってたこと"
      ("修論"))
     ("やったこと"
      ("プレゼンツール"
       "いまつくった"
       "applyがない"
       "string-concat"))
     ("やったこと"
      ("BiwaSchemeの改良"
       "mapとかつくった"
       "ネットワークロード"))
     ("どう見ても現実逃避です"
      ("本当にありがとうございました"))
      ))
(presen-start)
</script>

本体はpresen.scm。作り始めてからapplyが未実装なことに気付き、時間がないので ライブラリ側にstring-concatを実装してその場を凌いだ(ひどい)。

というのも含めて30分くらいで書いたんだが、Schemeのボトムアップなプログラミングは超短期での開発に向いてるなぁと 思った。 要するに小さい関数一つ一つを動かしながら確かめるので、それが簡易的なユニットテスト代わりになるという。

[biwascheme] define-macroを実装した

いわゆる「古典的なマクロ」を実装した。

(define-macro (foo x y) `(+ ,x ,y))
(foo 8 9)

というコードが17を返すことしか試してないので、まだバグがあるかも知れないが。

なんか15分で実装する流れだったのに、結局2日かかってしまい非常に残念な感じになった。 マクロ展開器のコンパイル結果をキャッシュするとか考えなければもうちょっと早かったかも知れないけど。

インタプリタの実行開始回りがぐちゃぐちゃになったのでどげんかせんといかん。

[tetris][prog] テトリスをプレイすることによって体調を測る

やはり夜中の3時とかにコードを書くのは(僕にとっては)良くないらしい。テンションは高いが頭がうまく回らない。 単純なバグに気付かない。等。

自分の脳がちゃんと動いているかを調べるのはとても簡単で、Tiを一回プレイしてみれば良い(笑)。 適切な睡眠を取った日はm6〜m7前後が出るし、睡眠時間が極端に短いか長い日はカンストすら出来ずに死ぬ。

10月くらいからの統計では、

  • 長さは8時間程度がベスト。6時間を切ると少ない。10時間を越えると多い(多すぎても良くない。)
  • 就寝時間が3〜4時を越えると、たとえ8時間寝てても駄目

ということがなんとなく分かっている。


2008-01-24

[softs] AutoPagerizeのOpera版,oAutoPagerizeの使い方

という重要な情報がどうググっても出てこなかったので,かっとなってエントリを書いた.反省はしていない.

AutoPagerizeはもとはFirefoxの拡張で,ページの一番下までスクロールしたら自動的に次のページを「継ぎ足して」表示するものです. twitterとか,はてなダイアリーとか,@ITとか,「全10ページってなんだよめんどくせー!!」な時に絶大な力を発揮します.

Operaへの導入

非常に簡単でした.

  1. ツール→設定→詳細設定→コンテンツ→javascriptオプション→一番下の「ユーザーJavascriptファイル」にてきとうなフォルダ(たとえばC:\Program Files\Opera8\scripts)を設定する
  2. oAutoPagerize.js を http://d.hatena.ne.jp/os0x/20071202/oautopagerize からダウンロードし,上で指定したフォルダに保存する

以上.

参考

Operaのuser.jsは「ページを読み込んだら自動的に実行されるbookmarkletだと思えばいい」そうです(わかりやすい説明!).

参考にさせていただいたリンクはこちら.

オリジナルのFirefox版と違って,SITEINFOが自動でアップデートされたりはしないそうなんですが, 1クリックで更新するためのツールがあるようです.oAutoPagerize.jsと同じディレクトリに置きましょう.

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

カーディナル [ こんにちは。  価格.comのクチコミからOpera 9.64へoAutoPagerizeを導入してみました。 ..]


2008-01-25

[event][scheme] gauche.night#2のチケットを入手した

今日から発売.ローソンで買える.15時ごろに行ったので,もう売り切れてたらどうしようかと一瞬どきどきした (大げさ?w)

ともあれ,行きたい人は早めに購入した方が良いかも.

[biwascheme] mapの実装とか,dynamic-windのこと

先輩にこの前実装したmapの話をしたら,「tut-schemeも同じような実装だよ」と言われた. ちょっと嬉しい.

ただTUSは「関数のリターン時に実行するコード」が指定できるようになっていて, mapなどを呼び出すときにもうこの情報をスタックに置いてしまうとか.

これがdynamic-windのときにも使われているらしい,というつながりでdynamic-windの話を聞いた. dynamic-windは3つのクロージャを引数にとる関数で,ただbefore→thunk→afterの順に実行するだけ,というもの.

ただし,thunkの実行途中で継続オブジェクトを実行して飛び出すときは,その前にafterを実行しなければいけないし, 逆にthunkの中に飛び込むときは,その前にbeforeを実行しなければならない. (try〜catchみたいなものだとイメージすると良いかも)

さらにdymanic-windがネストしている場合もあって,何がなんだか(笑). とりあえずホワイトボードの図をデジカメで撮ったので,実装するときに参考にしよう(という俺メモ).

[biwascheme] sortの実装とか

んで,ついでに「sortも同じような感じでできませんかね?」と聞いたら「無理でしょ」と即答(笑). JavaScriptのsortが使えれば(最終的にCのqsortあたりに落ちると思うので,速度的に)いいなぁと思ったのだが, qsortのループとインタプリタのループを同時に回すことはできないので無理,と.

もちろん,qsortの比較関数のところで毎回新しいインタプリタを作ってクロージャを実行することは可能なのだが, これだとクロージャ内で継続を生成したり実行したときにうまくいかない.(^^; というのもBiwaSchemeでは スタックを自前で持っているからだ.GaucheはCのスタックをそのままコピーして継続を作るので,この方式で問題ない (らしい)んだけど.

ということで,考えられる実装は

  • Schemeでクイックソートを書く
    • 多分一番ありがち?
  • JavaScriptでクイックソートを書く
    • CPS形式で書かないといけないのでちょっと面倒
  • JavaScriptのsortを利用し,継続との絡みは諦める

の3択.

さて,そろそろ修論の締め切りだが,1日8ページ書けば間に合う感じらしい.8ページ…8ページ…(思考停止) 日記のエントリを書くくらい適当に書ければいいんだけど.

[web] iPhone欲しい!

だそうです.せっかくなので応募してみる.

[biwascheme] 多値を実装した…?

Valuesというクラスを新しく作り,valuesとcall-with-valuesを実装した.

本当は,

わざわざ多値を持ち込む「実用上の」理由は、性能だ。 valuesで返される多値はそれ自体はfirst classじゃない。 (各値はfirst classだけど、多値全体としてはそうじゃない)。多値は、それがvaluesで作られてからcontinutationに渡されるまでしか総体として存在しないのだ (立場によっては、全く存在しないと言っても良いだろう)。

[Scheme:多値より引用]

とのことなので,もっと効率の良い実装を考えるべきなのだろうけど….とりあえずはこれで.

なんで多値が欲しかったかというと,partitionを実装するためで,これで Chapter 3 List utilitiesの関数が 全て実装できた.わあい.


2008-01-27

[vim] "funciton" "incldue" など,単純な打ち間違いを防ぐ.vimrcの設定

だいぶ前の話なんですけど,twitterで「lambdaを20回に1回くらいlamdba(らむどば)と打ち間違えて困る」的な話をしたら, abbrというコマンドがあると教えてもらいました. motemenさんに感謝.

.vimrcに

abbr lamdba lambda      #λな人向け
abbr funciton function  #JavaScripter向け
abbr reuqire require    #Rubyist向け
abbr incldue include    #C/C++使い向け
abbr improt import      #Haskeller/蛇遣い向け

などと書いておくと,「lamdba{ 」の「{」のタイミングで自動的にlambdaに直してくれます.これは嬉しい.

(んで,useは打ち間違える要素がないという意味でPerl最強説)


2008-01-29

[web] companulaを設置してみた

なんとなく,自分で試してみないと面白さがわからなさそうな予感がしたので貼ってみる. 左のサイドバーからどうぞ.

  • おもしろい記事だと思ったら「キープ」を押す
  • 特におもしろい記事だと思ったら「投資」を押す

といいらしいよ.

てか,何をどこに「キープ」するのか,何を何に対して「投資」するのかが全くわからんのだけど(笑). もうちょっと人が増えたらどういうものか分かるのかな?

[junk] さーて投資してもらうために面白い記事を書くぞ!と思ったが

修論真っ最中でそんな暇がありません⊂二二二( ^ω^)二⊃

ブーンもあれほど流行ったわりに,今では単なる一AAになったみたいですね. 最近の流行はやる夫・やらない夫なのかな.