Shiro Kawai
shiro****@lava*****
2003年 2月 19日 (水) 09:57:47 JST
From: Kimura Fuyuki <fuyuk****@hadal*****> Subject: [Gauche-devel-jp] 省略可能引数とデフォルト値 Date: Wed, 19 Feb 2003 09:15:49 +0900 > 次のような二つのオプショナル引数を取る関数があったとして、 > > (define (do-something . args) > (let-optionals* args ((host "www.yahoo.com") > (port 80)) > (print host ":" port))) > > この関数を呼ぶだけのコマンドを書け。コマンドラインオプション(-h, -p)で > hostとportを指定できるようにすること。 optional引数の欠点は、前の方の引数だけ「無指定」という指定が できないことでしょうね。必然的に、「後のほうの引数は前の方の 引数が指定されて初めて意味を持つ」といった暗黙の制約がかかります。 どの引数も等分に出てくる可能性がある場合は、キーワード引数で 渡した方がすっきりするかもしれません。 (define (do-something . args) (let-keywords* args ((host "www.yahoo.com") (port 80)) ...)) 渡すほうは引数リストを用意してやればいいので。 (define (main args) (let ((do-args '())) (parse-options (cdr args) (("h=s" (host) (push! do-args `(,host :host))) ("p=i" (port) (push! do-args `(,port :port))))) (apply do-something (reverse do-args)))) parse-optionsは副作用べったりでやだ、というならsrfi-37を使って (use srfi-37) (define (main args) (let ((do-args (args-fold (cdr args) `(,(option '(#\h) #t #f (lambda (option name arg do-args) (list* :host arg do-args))) ,(option '(#\p) #t #f (lambda (option name arg do-args) (list* :port (x->integer arg) do-args)))) (lambda (option name args . seeds) (error "Unknown option" name)) (lambda (operand . seeds) (errorf "Usage: ~a [-h host][-p port]" (car args))) '()))) (apply do-something do-args))) どうしてもoptional引数でやるなら、#fが渡された場合は デフォルト値を使う、みたいなAPIにしておいて、次のように 2段構えにするとか。 (define (do-something . args) (let-optionals* args ((host #f) (port #f)) (let ((host (or host "www.yahoo.com")) (port (or port 80))) ...))) 欠点: (1) 「無指定」を指定するための値は有効な値として渡せない (2) do-somethingが長くなる do-something自体を変更できない場合は、呼び出し側で引数 リストを作ってapplyするしかないでしょう。その場合、portだけが 指定された場合のhostの値はやっぱり呼び出し側で用意しとかないと ならないですね。 一般的に使える「無指定」を指定する値があると便利そうなんですが、 Scheme的には「無指定」値を決めた途端、その「無指定」値を 引数として受け渡したくなる場面が出て来るので、実際には使えない んですよね。 let-optionals* マクロを拡張して、「この値が来た場合は デフォルト値を使う」というふうに指定できるようにすることは 可能かな。それもなんとなくすっきりしない気がしますが。 --shiro