2016-06-17 8 views
3

私は別の関数をラップする関数を書こうとしていますが、わかりやすいラムダリストを維持しながらパラメータを正しく渡す方法がわかりません。引数を指定して関数を呼び出す関数の記述方法は?

など。私は機能

(defun f (x &key y z) ...) 

を持っている場合、私は、私は「doesnので呼び出されたg正確な引数とgからfを呼びたいので、これは満足のいくものではない

(defun g (x &key y z) 
    (h (f x :y y :z z))) 

のようなものを書きたいです(例えば、私はfにキーワード引数を提供したいが、それは呼び出し側によってgに供給されなかった)。

私が最初のようなものを書いた:

(defun g (&rest f-args) 
    (apply #'f f-args)) 

をそして、それは私が欲しい効果だ、しかしgのためのラムダリストは現在、非常に不可解であり、私は引数がどうあるべきかを見るためにfに移動した保ちます。

私は解決策を思いつきました(それは答えとして投稿したのでほとんど満足です)。しかし、私はすべての単一のキー引数と大きなラムダリストを明示する必要があります。 drakma:http-request)、それは痛みになります。多分もっと良い方法があることを願っています。

+0

キーワードの引数には、次のような形式を使用できます。 '(y nil y-p)'引数は引数が与えられていれば 't'、省略された場合は' nil'となります。これからラムダリストを再構成する必要があります。 – mobiuseng

答えて

3

ラムダリストを別の関数からコピーして関数を定義するマクロを書くことができます。問題はラムダリストを取得する標準的な方法がないことですが、SBCLの場合はSB-INTROSPECT:FUNCTION-LAMBDA-LISTを使用できます(ただし、(declaim (optimize (debug 0)))では機能しません)。スワンクのソースコードを読んで、さまざまな実装のラムダリストを取得する方法を調べることができます。コードは変数定義を示していないので、少し醜い

(defmacro define-wrapper (name lambda-source &body body) 
    `(defun ,name ,(sb-introspect:function-lambda-list lambda-source) 
    ,@body)) 

(defun f (x &key (y 3) (z 4)) 
    (+ x y z)) 

(define-wrapper g f 
    (* 2 (f x :y y :z z))) 

(f 2) ;=> 9 
(g 2) ;=> 18 

。もう少し複雑なソリューションは、ラッパーで_が与えられた引数を持つ関数Fへの呼び出しに置き換えられ

;; Requires Alexandria. 
(defmacro define-wrapper (name lambda-source &body body) 
    (let ((lambda-list (sb-introspect:function-lambda-list lambda-source))) 
    (multiple-value-bind (required optional rest keywords) 
     (alexandria:parse-ordinary-lambda-list lambda-list) 
     (declare (ignore rest)) 
     `(defun ,name ,lambda-list 
     ,@(sublis `((_ . (,lambda-source ,@(loop for r in required collect r) 
              ,@(loop for (name init suppliedp) 
                in optional collect name) 
              ,@(loop for ((k-name name) init suppliedp) 
                in keywords 
               append (list k-name name))))) 
        body))))) 

(defun f (x &key (y 3) (z 4)) 
    (+ x y z)) 

(define-wrapper g f 
    (* 2 _)) 

のような何かをするかもしれません。引数変数が存在し、自分で定義した変数と競合する可能性があることを覚えておく必要があります。

これは、与えられたかどうかにかかわらず、関数にすべての引数を渡します。それは、引数が与えられたかどうかに応じて異なる振る舞いをする関数を混乱させる可能性があります。 APPLYを使用して回避することはできますが、もう少し複雑です。

(defmacro define-wrapper (name lambda-source &body body) 
    (let ((lambda-list (sb-introspect:function-lambda-list lambda-source))) 
    (alexandria:with-gensyms (deparsed-arglist-sym 
           key-sym val-sym suppliedp-sym) 
     (multiple-value-bind (required optional rest keywords) 
      (alexandria:parse-ordinary-lambda-list lambda-list) 
     (declare (ignore rest)) 
     (multiple-value-bind (body declarations docstring) 
      (alexandria:parse-body body :documentation t) 
      `(defun ,name ,lambda-list 
      ,@(when docstring (list docstring)) 
      ,@declarations 
      (let ((,deparsed-arglist-sym 
        (nconc (loop for ,val-sym in (list ,@required) collect ,val-sym) 
          (loop for (,val-sym . ,suppliedp-sym) 
            in (list ,@(loop for (name init suppliedp) 
                 in optional 
                collect (list 'cons name 
                    (or suppliedp t)))) 
            when ,suppliedp-sym collect ,val-sym) 
          (loop for (,key-sym ,val-sym ,suppliedp-sym) 
            in (list ,@(loop for ((kname name) init suppliedp) 
                 in keywords 
                collect (list 'list kname name 
                    (or suppliedp t)))) 
            when ,suppliedp-sym append (list ,key-sym ,val-sym))))) 
       ,@(sublis `((_ . (apply #',lambda-source ,deparsed-arglist-sym))) 
         body)))))))) 

(define-wrapper bar drakma:http-request 
    "Return the length of a response to http-request." 
    ;; HTTP-REQUEST has some &aux variables. 
    (declare (ignore drakma::unparsed-uri 
        drakma::args)) 
    (length _)) 

(bar "http://www.google.com") ;=> 11400 (14 bits, #x2C88) 
+0

* lambda-from * ...最初は私はそれがタイプミスだと思った:-) – coredump

+0

@coredumpええ、それは良い名前を使用することができますね:) – jkiiski

2

私はこの思い付いた:

(defun g (x &rest f-keys &key y z) 
    (declare (ignorable y z)) 
    (apply #'f x f-keys)) 

それは小さなラムダリストのための素晴らしいことだが、私は私がやれることを願っています。
明示的に入力しない限り、デフォルト値が表示されません。

関連する問題