83's

Top > Tags > Gauche

Gauche

タイトル一覧を表示 | 本文を表示

timeoutのやつ August 31, 2008 01:21

ググったらGaucheクックブックにあった (長時間かかる処理にタイムアウトをつける)。 リファレンスマニュアルしか探してなかったよ。 なるほどalarmってこういうふうに使うのか……。

ちなみにalarmだとうまく行かない正規表現のやつは、 スレッド版だとしっかりタイムアウトして返ってきました。 Rubyからパクったかいがあったよ。

(print (current-date))
(guard (e (else (print (current-date))))
  (timeout  1
            (lambda () (#/a+*$/ "aaaaaaaaaaaaaaaaaaaaaaaaaaaab"))))
% gosh timeout.scm
#<date 2008/08/31 01:23:14.839329000 (32400)>
#<date 2008/08/31 01:23:15.845366000 (32400)>

ところでクックブック再開キボン……(´・ω・`)

いいこと思いついた。お前testのケツのところでapplyしろ August 20, 2008 22:55

述語を使ったテストって毎回こう……

(test* "3 is number" #t (number? 3))

expectに#tって書くのでめんどくさい。

(define-macro (test-true msg form)
  `(test* ,msg #t ,form))

という感じでマクロを書いてしまえば

(test-true "3 is number" (number? 3))
;;=> test 3 is number, expects #t ==> ok

となるわけだけど、「expects #t」ってなんだかなぁと思ってちょっとこうしてみる。

(define-macro (test-pred msg pred form)
  `(test* ,msg ,pred (list ,form) apply))

(test* msg expect form compare)は中で(compare expect form)を評価してるので、 compareapply渡してformをlistでくくると(apply pred (list form))つまり (pred form)が#tならpassするテストが書ける。

すると

(test-pred "3 is number" number? 3)
;;=> test number, expects #<subr number?> ==> ok

てな具合にちょっとすっきり書けつつ、「expects #<sub number?>」ってあたりが惜しい感じ。 「expects number」とかになればいいんだけど、さすがにそれはgauche.testのメソッド上書き しないとダメっぽいなぁ。

ただまあ、処理とテストの式が分離できて見栄えはいい感じがする。ホントか?

(test-pred "returns number" (cut is-a? <> <number>) (do-something))
;;=> test returns number, expects #<closure #f> ==> ok

表示はイケてない。

timeout August 20, 2008 02:29

RubyのTimeout.timeoutぽいのをGaucheで書いてみた。 こういうの結構必要な場面あると思うんだけど、毎回書くのかなぁ。 まあほんの数行だけどさ。それともなんかもっとイカすやつがあるのかなぁ。

(use gauche.threads)

(define (timeout sec proc)
  (guard (e ((<uncaught-exception> e) (raise (uncaught-exception-reason e))))
    (thread-join! (thread-start! (make-thread proc))
                  sec)))

;;--------------------------------

(print (timeout 5 (lambda ()
                    (sys-sleep 3)
                    "foo")))
;;=> foo

(guard (e (else (print e)))
  (timeout 1 (lambda () (sys-sleep 5))))
;;=> #<join-timeout-exception #<thread #f runnable 0x80ec920>>

(timeout 10 (lambda () (error "error occured in proc")))
;;=> gosh: "error": error occured in proc

thread-join!は待ち時間が指定した秒数を越えると join-timeout-exceptionを投げるらしいのでタイムアウトのエラーはそれをそのまま使ってる。 timeout用のエラーを作った方がいい気がするけどもう寝る。 どうせその次はモジュールにしたくなったりするんだ。 寝るべきだ。

GaucheでYAML August 02, 2008 00:01

YAMLが読めるよ!! やったね たえちゃん!

lequeから Gauche-YAML-0.0.tgzを入手。バージョンがすごい……。ダブルオー。

$ sudo aptitude install libsyck-dev
$ sudo gauche-package install Gauche-YAML-0.0.tgz

(use text.yaml)したらSEGVした。

GaucheのAPIが変わってるみたい。yaml.cを修正。

--- Gauche-YAML-0.0.orig/yaml.c 2006-09-03 00:04:40.000000000 +0900
+++ Gauche-YAML-0.0/yaml.c      2008-08-01 23:05:12.000000000 +0900
@@ -450,7 +450,7 @@
     sym_merge = SCM_INTERN("merge");
     sym_default = SCM_INTERN("default");
     make_date_proc =
-       Scm_EvalCString("make-date", SCM_OBJ(SCM_FIND_MODULE("srfi-19", 0)));
+       Scm_EvalCStringRec("make-date", SCM_OBJ(SCM_FIND_MODULE("srfi-19", 0)));
 
     /* Register stub-generated procedures */
     Scm_Init_yamllib(mod);

(use text.yaml)するとこんな関数が使える。おおお。

  • yaml-parse-string yaml-string
  • yaml-load port
  • yaml-dump obj &optional port
  • x->yaml obj

省略可能な引数のためのマクロ February 20, 2008 23:09

省略可能引数のパージング に便利なマクロが! というか無いと世知辛すぎる(´Д⊂

# Ruby
def foo(x, y = 0, z = 0)
  [x, y, z]
end


;; Gauche
(define (foo x . args)
  (let-optionals* args ((y 0) (z 0))
    (list x y z)))

省略が1個の場合はこんなのも。

# Ruby
def foo(x, y = nil)
  bar(x, y)
end


;; Gauche
(define (foo x . args)
  (bar x (get-optional args '())))

そしてキーワード引数も。

# Ruby
def foo(options = {})
  options[:x] ... 
  options[:y] ...
    ...
end
foo(:x => "ルビーの指輪")


;; Gauche
(define (foo . options)
  (let-keywords options ((x '()) (y '()))
    (... x ... y ...)))

(foo :x "main gauche")

おお。

まあこういうのは自分でマクロ書けばいかようにもなるけど、 超頻出なモンはオレオレマクロよりも合意のとれたものを使いたいんだよなぁ。 普通(≠ふつう)の趣味プログラマの心境としてはさ、 巻かれたくなるよね長いものに。そろそろ趣味グラマじゃなくなるけど。

あと関係ないけど

(define-method move ((self <dog>) x y)
  ...)

この定形パターンが結構めんどくさい。

extend February 16, 2008 18:10

GaucheでOOP中。

;; foo.scm
(define-module foo
  (export <foo> say))

(select-module foo)

(define-class <foo> () ())

(define-method say ((self <foo>))
  (print "hello"))

(provide "foo")
;; bar.scm
(define-module bar
  (use foo)
  (export <bar>))

(select-module bar)

(define-class <bar> (<foo>) ())

(provide "bar")
;; test.scm
(use bar)

(say (make <bar>))

test.scm実行するとunbound variable: sayっていうエラーになる。 useしたbarモジュールでfooのインタフェースがexportされていないから見えないってことか。

bar.scmを

- (use foo)
+ (extend foo)

したら通った。なるほど。 extend使うとbarをuseした文脈からfooモジュールでexportしたやつを見に行けるようになるわけか。

<bar>は<foo>を継承してるから初めのでいくかと思ってたけど、 まあよくよく考えてみると、そうか、そうだよね、という具合。

しっかし普段requireさえ書かなくて良いようなぬるい環境(= Rails)にいると、 こういうのがいろいろ面倒くさくていけない。 とりあえずdefine-moduleとselect-moduleとprovideに重複が! そうか、そこでマクロか。

GaucheでMySQL/SQLiteの個人的まとめ February 05, 2008 21:49

大体使い方がわかってきた。Gauche:DBI/DBD見れば わかるけど、一応流れをメモっておく。

MySQLのドライバはDBI | dev | Kahua Projectから、SQLite3のはそのページに見当たらないのでuchizonoにあったものを使わせてもらた。

レッツ・コネクト。

(use dbi)

(define *mysql-con* (dbi-connect "dbi:mysql:db=test;host=localhost" :username "root" :password "himitsu"))

(define *sqlite-con* (dbi-connect "dbi:sqlite3:path/to/db/test.db"))

クエリ。

(let1 q (dbi-prepare *mysql-con* "insert into books (name, price) values (?, ?)")
  (for-each (pa$ apply dbi-execute q) 
            '(("JavaScript" 4200) ("On Lisp" 3800))))

?に値を入れられるのがイカス。つまり

(dbi-execute q "JavaScript" 4200)

で1つめの?に"JavaScript"、2つめの?に4200。

手軽にやりたければ

(dbi-do *mysql-con* "insert into books (name, price) values (\"SICP\", 4600)")

次は結果の取り出し。

dbd/mysql.scmを見ると、クエリ結果のクラス<mysql-result-set><relation><sequence>を継承してる。

gosh> (use gauche.sequence)
gosh> (for-each print (dbi-do *mysql-con* "select * from books"))
#(1 4200 JavaScript)
#(2 3800 On Lisp)
#t

各レコードはベクタになってた。

ちなみにSQLiteのほうの<sqlite3-result-set><collection>を継承してる。

gosh> (use gauche.collection)
gosh> (for-each print (dbi-do *sqlite-con* "select * from books"))
(1 4200 JavaScript)
(2 3800 On Lisp)
#t

こっちはリストだった。

Gaucheのロードパス January 29, 2008 03:16

環境変数GAUCHE_LOAD_PATH、goshコマンドの-Iオプション、あとは大域変数*load-path*set-car!でもしていじくる。

*load-path*いじるよりadd-load-pathを使うべきらしい。

add-load-path path &optional (afterp #f)

パスpathをライブラリロードパスのリストに加えます。 afterpに真の値が与えられていればpathは既存のリストの末尾に追加されます。そうでなければpathは既存のリストの先頭に追加されます。

ロードパスを変更したい場合、*load-path*を直接替えずにこのフォームを使って下さい。このフォームはコンパイル時に解釈されるのに対し、*load-path*を書き換えるコードは実行時に解釈されます。"use" や "require" はコンパイル時のロードパスを使うので、*load-path*への変更は反映されないかもしれません。

ファイルのMD5 January 27, 2008 18:53

% md5sum ~/gauche-refj.txt.gz
31970f62bbb827c8fc14ebae6a132b25  /home/yz/gauche-refj.txt.gz

これをやりたいんだ。

Rubyだと

require 'digest/md5'
puts Digest::MD5.hexdigest(File.read("/home/yz/gauche-refj.txt.gz"))
#=> "31970f62bbb827c8fc14ebae6a132b25"を表示

で済むんだけど、Rubyでぬくぬくしてるとそろそろゆとり脳って言われそうなのでひさしぶりにGaucheで頑張る。

(use rfc.md5)
(use file.util)
(print (digest-hexify (md5-digest-string (file->string "/home/yz/gauche-refj.txt.gz"))))
;=> "6ff4b0c6979cb79d21e355d338769d5b"を表示

なんか違う。file->stringFile.readとは違うみたい。

rfc/md5.scmから適度にパクってこうなった。

(use rfc.md5)
(use gauche.uvector)

(define *buffer-len* (* 1024 10)) ; 10KB

(define (file->md5-digest path)
  (let ((buf (make-u8vector *buffer-len*))
        (md5 (make <md5>)))
    (call-with-input-file path 
                          (lambda (port)
                            (until (read-block! buf port) eof-object? => cnt
                              (digest-update! md5
                                              (u8vector->string (if (< cnt *buffer-len*)
                                                                  (uvector-alias <u8vector> buf 0 cnt)
                                                                  buf))))
                            (digest-final! md5)))))

(print (digest-hexify (file->md5-digest "/home/yz/gauche-refj.txt.gz")))
;=> "31970f62bbb827c8fc14ebae6a132b25"を表示

なげぇw けどdigest関係の必要な関数が把握できた。ウフフ