[Gauche-devel-jp] 省略可能引数とデフォルト値

Back to archive index

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



Gauche-devel-jp メーリングリストの案内
Back to archive index