齊藤

趣味プログラマ。 気の向くままに思い付きのままにコードをいじって遊んでます。

中学生時代に部活でN88-BASICに触れたのが始まり。 高専に進学してからはC,Rubyを主に使ってました。 後に C++, JavaScrip tに興味が向いて、 JavaScript のセマンティクスでどうにも理解できない点があって、関数型の源流を求めて Scheme に至った次第。

ウェブサイトはここ。

http://saito.s4.xrea.com/wiliki.cgi

Twitterにも参加してるですよ。

私の今迄の疑問。

そして要望。(解決済み)


マクロ中のトップレベル define について

トップレベル define について以前に話題になった部分を読み返していました。

http://practical-scheme.net/wiliki/wiliki.cgi?Scheme%3A%E5%88%9D%E5%BF%83%E8%80%85%E3%81%AE%E8%B3%AA%E5%95%8F%E7%AE%B1%3Alog00#H-11dj7n5q4m01b

"may or may not introduce binding" というのは、その後の "see section 5.2." と併せて考えれば「define が set! の意味に扱われることもあるから新しい変数が作られないかもしれない」という意味にとれます。 define が新しい変数を作らないこともあるという (5.3 でも説明されている) 性質について注意を促しているものであって、この文が何かを規定しようとするものではないと考えれば、変数が既に導入されていない限り、つまり set! として解釈される場合を除いてリネームされるべきなんじゃないかと思えます。

つまり

(define-syntax foo (syntax-rules () ((foo) (define x 100))))

というマクロがあったとき、事前に (define x 1) といった定義があれば (foo) は (set! x 100) と扱われ、 x が定義されていないならばリネームされた変数が作られるというのが私が考える解釈です。

しかし、後から定義が追加される可能性 (REPL) を考えるとそう単純でもないのかなという気持ちもあって難しいところです。 R6RS のように define と set! をスパッと分けるのは、意味論を明確にするためにはどうしても必要だったんでしょうね。 (しかし、 REPL を無視すると使い勝手は悪い。)

Document ID: 2ad7057eea65d615fa0d26635357454c

(2015/08/09 21:11:22 UTC)

html-parser

Gauche が持っているライブラリの中に (不正確な場合も多い) HTML のパースに使える寛容なパーサは含まれていない。 よく紹介されるのは html-prag なのだが、ライセンスが LGPL なのが好きではなく、もっと緩いのがないかと思っていた。

そんな折に CHICKEN Scheme 用のライブラリ html-parser紹介するブログ記事が投稿されたのを見て、さっそくライセンスを確認すると BSD スタイルだったので、これはよいと Gauche 向けに移植してみた。

http://saito.s4.xrea.com/software/Gauche-html-parser-0.2.tar.gz

移植とは言っても、元々かなり移植性の高い感じになっていたので、移植にあたって構造を理解する必要さえなかった。 import するライブラリをちょろっと Gauche 用に置換えるくらいのものだ。 Gauche が持っているライブラリ (ssax) と機能が重複する手続きはばっさりと削除した。

Document ID: a6a0563045d6a3ca679eacd2cc0009f4

2014/09/13 16:11:00 UTC

Picrin のリストモナドに関する文書の翻訳

picrin:モナド的リストオペレータの Scheme への導入 (限定継続を用いて)

Practical

Scheme をこれから学ぼうという人が SICP を教科書にする事例をよく見掛けるのだけれど、それは良い選択とは言えないんじゃないだろうか。

SICP で Scheme を学ぶというのは、喩えて言うなら、これから英語を学ぼうという人が (英語での) 小説の書き方を解説した本を開くようなもので、知識のレイヤが違うと思う。 SICP は Scheme を学ぶための本ではないし、 Scheme を使いつくしているわけでもなく、説明に Scheme を使っているというだけのものだからだ。 あくまで計算機科学の教科書なのだ。

もちろん SICP はとても良い本で、読むべき本だ。 Scheme を使うにせよ、他の言語を使うにせよきっと訳に立つ。 でも、先程の例で言うなら、英語で小説を書けなくても英語を使えることには充分な意味がある。 SICP の解説を大して理解できなくても Scheme は充分に面白くて使える言語だ。

そんなわけで、私が言いたいことは、初心者にもうちょっと気楽に Scheme 入門して欲しいってこと。

(2013/03/19 08:44:05 UTC)

zip アーカイブ

zip アーカイブに関する Gauche 用ライブラリを作って公開している。

zip アーカイブから直接ロードするためのライブラリと、 zip アーカイブを作成するためのライブラリだ。

私自身が使うために作ったので、自分で使う分には不足はないのだけれど、 API が綺麗じゃないという気がしている。 特にアーカイブに入れる内容を文字列で渡すところとか。

アーカイブファイルに格納するという性質上、あまりファイルの中をいったりきたりもしたくないという理由もあって一度に渡してしまえば簡単という割切りでこうしたものの、巨大なデータを扱いたいときに一度は全て (文字列として) メモリ上に存在しなければならないというのがダサく見える。

それと、 zip アーカイブのロードと生成を別ライブラリにしてしまったのも良くない。 ヘッダを表すクラス等、共通化できる部分がある。 でも、そのあたりを綺麗に統合しようと思うとアーカイブの読込み部分も作ってからだろうという気持が起こり、しかし、今のところそれを必要とする事態がないので先送りにされているという状況だったりする。

実際に使ってみないと API に求められるものってわからないから、必要ドリヴンはそう間違った戦略ではないと思うのだけれど、なんだかんだで先送りになりがちだよなーなんてことも思う。

(2013/03/17 14:22:29 UTC)

目的は決めるしかない

学生のときに授業で使っていた数式処理システムは物理では Mathematica だったし、制御工学では Maple や Matlab だったし、数学では Risa/Asir だった。 卒業研究は当初は GAP で多項式環の概念を扱えるように拡張するということになっていたが、 GAP のマニュアルが英語しかなかったのですぐにめげて結局は GAP を使わずに C で書いた。

そうやっていくつかの数式処理システムを使っていたわけだが、今となっては何ひとつ覚えていない。 それぞれを使う時間が短かかった分、どれにも充分に慣れることができなかった。

もちろん、それぞれの数式処理システムはそれぞれの分野に適しているのだろう。 適材適所というやつだ。 でも、学校でそれらを指導するのは「教育」という目的があってのことだ。 少しばかり無理があってもどれかひとつに絞って指導すればひとつは身に付いたかもしれない (教育と云う目的に沿う) という意味では適材適所ではなかったと言える。

つまり、ある目的に最適な手段が別の目的では最適ではないのが普通だ。

さて、先月のことだが、にちゃんねるにある母校のスレッドでこんな質問があった。

どれほどの英語力を社会に出たら要求されるのですか?

必要な技能は職業によってまるっきり違う。 当然のことだ。 極端に言えば英語が出来ない方がいい場合だってあるかもしれない。 (具体事例は思い付かないけれど。) あるいはそれに費やす時間を別のことに振り向けた方がいいかもしれない。

私はこれを線形計画法を引き合いに出して説明した。 線形計画法は目的関数を最大にするパラメータを決定する手法だ。 自分が望む良い人生 (目的関数) を満たす合理的なパラメータは理屈の上では存在し得る。 (現実にはひとりの人生は複雑すぎて計算することは出来ないけれど。)

だが、目的関数は自分が「決める」しかない。 この決定には正しいということも間違っているということもない。 ただの前提条件だ。

理論がどれだけ発展しても、コンピュータの演算能力がどれほどパワフルになろうとも、それらは手段でしかあり得ない。 会社の経営方針なら経営者が、国の運営方針は政府 (国民) が「決める」しかないことだ。

合理的に答えを探そうと思ったら自分が本当に望んでいることは何なのかをまず考える必要がある。 それって結局は哲学の問題だよね。

(2011/06/18 01:23:49 PDT)

混ぜるな危険

数値文字参照の扱いが htmlprag と ssax で違ってた。

htmlprag ではこんな感じ。

(html->sxml "<html><body><div>&#12940;</div></body></html>")
=> (*TOP* (html (body (div (& 12940)))))

ssax ではこんな感じ。

(ssax:xml->sxml
 (open-input-string "<html><body><div>&#12940;</div></body></html>")
 '())
=> (*TOP* (html (body (div "㊌"))))
;; gauche-charactor-encoding が utf-8 以外のときはどうなるんだろう?

htmlprag で作った sxml を sxml:sxml->xml で変換しようとするとエラーになってしまう。 shtml->html を使えばいいだけの話ではあるけど、同じ sxml 関連だからと思ってこういう風に出自の違うライブラリを混ぜて使ってると予想外のところで躓いてしまうこともあるという教訓。

(2011/02/11 07:39:49 PST)

文字列ポート

http://practical-scheme.net/chaton/gauche/a/2011/02/10#entry-4d542b4d-9493d

齊藤 2011/02/10 10:15:41 PST ところで話は変わりますが、 output string port に蓄積したものを今度は input string port として読み出したいってケースは珍しくないと思うんですが、 get-output-string で取り出して open-input-string するしか無いですか? なんとなく非効率な気がするんですが、内部的には共有されてたりするのかな。

shiro 2011/02/10 10:53:36 PST ああ、get-output-stringはコピーが発生しちゃいますね。output string portは最終的な長さがわからないのでチャンクのリストの形でデータを保持してるので。今のところその蓄積されたデータを得る方法はget-output-stringだけです。

procedural buffered portを使ったらパイプのような対のポートを作れるんじゃないかな。

齊藤 2011/02/10 11:15:24 PST <buffered-output-port> を使えば案外簡単に出来そうな予感。

と言うわけでやってみた。

まずは出力ポートから。

(use gauche.vport)
(use gauche.uvector)

(define-class <ostring> (<buffered-output-port>)
  ((chunks :init-keyword :chunks)
   (last-chunk :init-keyword :last-chunk)))

(define-method initialize ((obj <ostring>) initargs)
  (let1 chunks (cons #f '())
    (next-method obj
      (list*
       :chunks chunks
       :last-chunk chunks
       :flush (lambda (buffer flag)
                (let1 lc (~ obj 'last-chunk)
                  (set-cdr! lc (cons (u8vector-copy buffer) '()))
                  (set! (~ obj 'last-chunk) (cdr lc))
                  (u8vector-length buffer)))
       initargs
       ))))

(define (make-open-ostring . opt) (apply make <ostring> opt))

試しに動作させてみると…

(define a (make-open-ostring :buffer-size 2)) ;; ←効果を見易くするためバッファを小さく
(write '(hoge huga hige) a)
(flush a)

このとき a に蓄積されているデータの様子は

(~ a 'chunks)
;; => (#f #u8(40 104) #u8(111 103) #u8(101 32) #u8(104 117) #u8(103 97) #u8(32 104) #u8(105 103) #u8(101 41))

入力ポートを作るコードはこんな感じ。

(define-class <istring> (<buffered-input-port>)
  ((chunks :init-keyword :chunks)))

(define-method get-input-port ((obj <ostring>))
  (let1 x (make <istring> :chunks (cdr (~ obj 'chunks)))
    (set! (~ obj 'chunks) (cons #f '()))
    x))

(define-method initialize ((obj <istring>) initargs)
  (next-method obj
    (list* :fill
           (lambda(buffer)
             (let* ((obl (u8vector-length buffer))
                    (chunks (~ obj 'chunks))
                    (ib (if (null? chunks) #f (car chunks))))
               (if (not ib)
                   0
                   (let ((ibl (u8vector-length ib)))
                     (cond ((= ibl obl)
                            (u8vector-copy! buffer ib)
                            (set! (~ obj 'chunks) (cdr chunks))
                            ibl)
                           ((< ibl obl)
                            (u8vector-copy! buffer ib)
                            (set! (~ obj 'chunks) (cdr chunks))
                            ibl)
                           ((> ibl obl)
                            (u8vector-copy! buffer ib)
                            (set-car! (chunks (u8vector-copy ib obl)))
                            obl))))))
           initargs
           )))

先程の出力ポートから入力ポートを作って入力してみる。

(define b (get-input-port a))
(read b) ;; => (hoge huga hige)

入力ポート内での状態管理が手抜きなので無駄にコピーしちゃってるけど、とりあえずは機能してるみたいなので良しとしよう。

(2011/02/10 13:12:25 PST)

マシンの命名法

Island Life:マシンの命名法 を読んで、そういえば私の場合はどうかと言うとメインマシンは longarch で、サブマシンは stars である。 ついでにあまり使ってない予備マシンは lightning だったりする。 わかる人にはわかると思うが、某分隊の名前から借用した名前だ。 数が多くなってくると連番とかにせざるを得なくなってくるんだろうけど、個人所有の物だとやはり本人の思い入れが反映されがちだろう。

私の母校だとワークステーションやサーバは科学者の名前が与えられていて端末は連番だった。 しかし、研究室にあるマシンは担当教員の好みで決めていたようだ。 隣の研究室のマシンにはトリトンとかレイという名前が付いていた。 「レイって綾波ですか?」と聞いたら「アムロだ」と言われて、そっちか~と思った覚えがある。

まぁ、名前を借用するって言ったらアニメとかゲームからっていうのは鉄板だよねという話。

(2011/02/04 06:34:19 PST)

結局それは何なのか

説明は理解したけど結局それは何なのかというのがわからないということがある。 例えば Haskell なんかだと、モナドって結局何なのかという疑問を持つ人は結構多い。 答えとしてはモナド則を満たすものだとしか言いようが無いのだけれど、なかなかそれで納得いかないみたいだ。

プログラミングに限らなくてもいい。 数学なんかでも最初に行列式を学んだときは、その使い方というか在り方に自分で至るのは難しいと思う。

こういったことは一本の棒に例えられる。 その棒が何なのかと問われたら、棒は棒だとしか言いようがない。 その棒は物干し竿かもれないし、杖かもしれないし、あるいは棍棒なのかもしれない。 けれど、物干し竿だと言ってしまうと棒の本質から外れてしまう。 色々なものに成れる抽象的な存在に「何なのか」というのを問うても意味がない。 一旦は「そういうものだ」という思考停止が必要なんじゃなかろうか。 その上で、典型的な利用例を見ていくのが理解への早道なのではないかと考えている。

Haskell のモナドや C のポインタ等と違って Scheme の第一級継続は無ければ無いなりで使えてしまうから、その点は理解に至るのには不利な気がするなぁ。

(2011/01/08 20:32:09 PST)

モジュールとメソッド

curl バインディングを使ってみた。

<curl> は url というスロットがあり、そのアクセサは url-of なのだけれど、 url-of は export されていないせいか curl モジュールを use しただけでは見えない。 (define-generic url-of) (use curl) としてみても url-of ⇒ #<generic url-of (0)> だった。

curl モジュールの中では initialize メソッドを定義しているようで、これも export はされていないのに curl モジュールを use しただけでちゃんと機能しているようだ。 initialize は特別扱いなんだろうか。

メソッドとモジュールの関係がよくわからない感じ。

 (2010/01/23 08:48:23 PST)

ユニークなシンボル

CommonLisp には gensym があって、gensym は Gauche でも使える。 r6rs の範囲内で gensym を書くことは出来るだろうかということを考えた。

本来の gensym は intern されないシンボルを作るものだ。 さすがに言語組込の拡張機能として用意しなければ書けないだろうとは思ったのだが、色々と考えている内にこんなものが出来上がった。

(define (my-gensym)
  (car (syntax->datum
        ((lambda(x)
           (syntax-case x ()
             ((_ a)
              (generate-temporaries (syntax (a))))))
         #'(_ _)))))

これが gensym として使えるとは思ってない。 しかし、遊んでいる内に気付いたことがある。

(define g (my-gensym))
(define h (string->symbol (symbol->string g)))
(write (eq? g h))

r6rs 的にはスペルが同じシンボルを比較すれば eq? になるはず。 なので、ここでは #t が返るのが正当だと私は思う。 実際、Mosh, plt-scheme, petite-chez-scheme ではそうなる。 でも、 Ypsilon では #f だった。

 (2009/12/08 03:10:48 PST)

every-pred

私は未だオライリーの「プログラミングGauche」を入手してはいないが、その中に用意されている演習問題にとりくんでいるのをウェブ上でたまに見掛ける。 その演習問題のひとつが every-pred だ。

Gauche には every-pred は用意されており、ドキュメントで以下のように説明されている。

 Function: every-pred pred …
 与えられた引数をそれぞれ述語 pred に適用する手続きを返します。 全ての pred が #f でない値を返す場合、戻り値は最後の pred の戻り値になります。 いずれかの pred が #f を返す場合、 every-pred はそれ以降の pred を呼び出さずに #f を返します。

Scheme でよく用いられる高階関数の典型的な例と言ってよいと思う。 演習問題としては手頃だ。 試しに私もやってみた。

(use srfi-1)
(define ((my-every-pred . pred-list) . x)
  (every (cut apply <> x) pred-list))

典型的なパターンは既存の関数で解決出来る場合が多い。 every-pred がやっていることは本質的に every そのものだ。 少しばかり引数の順序を入れ替えているに過ぎない。

で、 Gauche が用意している every-pred はどういう定義なのだろうとソースコードを見てみるとこんな感じだった。

(define (every-pred . preds)
  (if (null? preds)
      (lambda args #t)
      (lambda args
        (let loop ((preds preds))
          (cond ((null? (cdr preds))
                 (apply (car preds) args))
                ((apply (car preds) args)
                 (loop (cdr preds)))
                (else #f))))))

依存関係を作りたくないからかとも思ったけれど、 every-pred が定義されているモジュール gauche.procedure は (use srfi-1) としてしまっているので、そういうわけでもないらしい。 おそらく効率化のためだと思って簡単に速度比較してみた。

(define test1 (every-pred odd? positive? number?))
(define test2 (my-every-pred odd? positive? number?))
(time (dotimes (i 10000000) (cut test1 10)))
(time (dotimes (i 10000000) (cut test2 10)))
;(time (dotimes (i 10000000) (cut test1 10)))
; real   8.781
; user   7.766
; sys    0.969
;(time (dotimes (i 10000000) (cut test2 10)))
; real   8.766
; user   7.703
; sys    1.015

とりたてて差はないっぽく見える。 差が出るケースがあるのだろうか? それとも単に私の深読みしすぎで、そこまで細かなところに気を回していないだけだろうか。

 (2008/08/02 06:02:44 PDT)

contextual information

schemeのマクロは難しい。datum->syntaxの第一引数の意味がまだ良くわからない。文脈情報ってなんやねんという感じ。今は名前解決の起点と理解しているけれど、これでいいのだろうか。 (2008/04/08 07:07:16 PDT)

define-macro

syntax-caseマクロはハイジニックなマクロもそうでないマクロも書けるということで、そんならsyntax-caseでdefine-macroを模倣できるんではないかとふと意味も無く思いついて書いてみた。

(define-syntax define-macro
  (lambda(x)
    (syntax-case x ()
      ((_ name body)
       (with-syntax ((syntax-body
                      (datum->syntax-object
                       (syntax k)
                       (syntax-object->datum (syntax body)))))
         (syntax
          (define-syntax name
            (lambda(y)
              (syntax-case y ()
                ((name a (... ...))
                 (let* ((m (syntax-object->datum (syntax (a (... ...)))))
                        (n (eval `(syntax-body ,@m) (interaction-environment))))
                   (with-syntax ((r (datum->syntax-object (syntax k) n)))
                     (syntax r))))))
            )))))))

なにやらあまりにもごちゃっとしているし、途中でevalしてるどころか(r6rsでは廃止されたはずの)interaction-environmentを使ってるしで無様なものになってしまった。既存の似たようなことをしたコードが無いものかと"(define-syntax (define-macro"を検索ワードとしてぐぐってみると、まさに同じことをやろうとしているコードが見つかった。

http://c2.com/cgi/wiki?DefineSyntax

こういう書き方もあるんだなぁ。 datum->syntax-objectの第一引数の意味をさっぱり理解できてなくてSchemeを使用したメタプログラミングの記事にある「ちょっとした魔法の式です」をとりあえず鵜呑みにして(syntax k)と書いているので、まじめに理解に努める必要アリと思われる。(2008/01/09 07:13:40 PST)

グタ

閉じ括弧をコッカと呼ぶのがアリならXMLの閉じタグはグタと読んでもよさそうなもんだ。

(2006/11/02 21:57:20 PST)

More ...