투명한 "통과"함수 래퍼를 작성하는 방법은 무엇입니까?


10

"투명한 '통과'함수 래퍼"가 의미하는 것은 함수입니다. 호출 해 봅시다. wrapper모든 인수를 다른 함수에 전달한 결과를 반환합니다 wrappee.

Emacs Lisp에서이 작업은 어떻게 이루어 집니까?

주의 : 이상적인 wrapper함수는 함수의 서명 에 대해 불가지론 적 입니다 wrappee. 즉, 그것은 wrappee논증 의 수, 위치, 이름 등을 전혀 모른다 . wrappee마치 wrappee원래 호출 된 것처럼 모든 인수를에 전달합니다 . (필요에 전화를 대체하기 위해 호출 스택 엉망에, 그러나, 없다 wrapper를 호출 wrappee.)

내 질문에 부분 답변을 게시했습니다 .

(defun wrapper (&rest args) (apply 'wrappee args))

대화 형 wrappee아닌 경우에만 작동합니다 . 분명히 대화 형 함수가 인수를 얻는 방식은 (&rest args)주문 에서 다루는 것과 다른 "채널"을 나타냅니다 . 그러므로, 여전히 필요한 것은 대화식 기능 이있는 경우 wrappee에 대한 (&rest args)서명 과 동일하게 독립적 입니다.wrappee

(이 질문은이 이전 질문 에서 설명한 문제에 의해 유발되었습니다 .)


내가 요구하는 것에 대한 추가 설명이 필요한 경우, 다음은 몇 가지 예입니다. 필요한 파이썬과 JavaScript를 보여줍니다.

파이썬에서 이러한 래퍼를 구현하는 몇 가지 표준 방법은 다음과 같습니다.

def wrapper(*args, **kwargs):
    return wrappee(*args, **kwargs)

# or

wrapper = lambda *args, **kwargs: wrappee(*args, **kwargs)

여기 *args에서 "모든 위치 인수"및 **kwargs"모든 키워드 인수"를 나타냅니다.

JavaScript와 동등한 것은 다음과 같습니다.

function wrapper () { return wrappee.apply(this, arguments); }

// or

wrapper = function () { return wrappee.apply(this, arguments); }

기록을 위해이 질문은 여러 인수가있는 함수에 mapcar를 적용하는 방법과 중복 됩니다 . 두 가지 질문이 저에게 분명히 다르게 보이기 때문에 이유를 설명하지 못했습니다. "사과가 오렌지와 동등한 것으로 간주되어서는 안되는 이유를 설명하십시오"라는 질문을받는 것과 같습니다. 단순한 질문은 너무나 미치므로 질문하는 사람을 만족시킬 수있는 답을 생각 해낼 수 있을지 의심 스럽다.


조언 / 조언 사용을 고려 했습니까?
wasamasa

@wasamasa : 아닙니다. 또한 조언 / 어드바이저가이 질문에 어떻게 적용되는지 보지 못했습니다. 어쨌든, 나는 그것을 advice멀리하기에 충분히 문제가있는 것을 발견 합니다. 실제로,이 질문에 대한 동기는
조언 된

1
@wasamasa : 조언은 같은 문제를 제시합니다. args로 무엇을해야하는지 말해 줄 수 있지만 대화식으로 만들려면 인수를 제공하는 방법을 지정해야합니다. IOW, 당신은 interactive사양 을 제공해야합니다 .
Drew

1
내가 의미 한 바는 대화식 사양에 대해 걱정할 필요가 없도록 원래 대화식 기능을 사용하여 전후에 발생하는 모든 작업을 수행하는 것입니다.
wasamasa

2
@wasamasa : 예,하지만 다릅니다. 조언은 대화식이든 아니든 항상 특정 기능에 대한 것 입니다. 그리고 그것이 명령이라면 아무런 문제가 없습니다-조언이 대화 형 행동을 재정의하지 않는 한 대화 형 행동은 권고 된 명령에 대해 상속됩니다. 이 질문은 특정 기능 / 명령이 아닌 임의의 기능 / 명령에 관한 것입니다.
Drew

답변:


11

물론 interactive사양을 포함하는 것이 가능하다 . 우리는 여기 elisp 다루고 있습니다 ! Lisp는 가장 중요한 구문이 목록 인 언어입니다. 호출 가능한 양식은 목록 일 뿐이므로 원하는대로 구성 할 수 있습니다.

응용 : 자동으로 일부 기능에 일부 기능을 추가하려고합니다. 확장 기능 defadvice에는 적용 할 수없는 새로운 이름을 부여해야합니다 .

먼저 목적에 맞는 버전입니다. fset심볼 의 함수 셀 ( )을 wrapper필요한 모든 정보로 설정 wrappee하고 추가 항목을 추가합니다.

두 가지 wrappee정의 모두에서 작동합니다 . 첫 번째 버전 wrappee은 대화식 이고 두 번째 버전 은 대화식이 아닙니다.

(defun wrappee (num str)
  "Nontrivial wrappee."
  (interactive "nNumber:\nsString:")
  (message "The number is %d.\nThe string is \"%s\"." num str))

(defun wrappee (num str)
  "Noninteractive wrappee."
  (message "The number is %d.\nThe string is \"%s\"." num str))

(fset 'wrapper (list 'lambda
             '(&rest args)
             (concat (documentation 'wrappee t) "\n Wrapper does something more.")
             (interactive-form 'wrappee)
             '(prog1 (apply 'wrappee args)
            (message "Wrapper does something more."))))

그러나 확장 함수를 구성하는 매크로를 정의하는 것이 더 편리합니다. 따라서 나중에 함수 이름을 지정할 수도 있습니다. (자동 버전에 적합합니다.)

아래 코드를 실행 한 후 wrapper-interactive대화식 및 wrapper-non-interactive비 대화식으로 호출 할 수 있습니다 .

(defmacro make-wrapper (wrappee wrapper)
  "Create a WRAPPER (a symbol) for WRAPPEE (also a symbol)."
  (let ((arglist (make-symbol "arglist")))
  `(defun ,wrapper (&rest ,arglist)
     ,(concat (documentation wrappee) "\n But I do something more.")
     ,(interactive-form wrappee)
     (prog1 (apply (quote ,wrappee) ,arglist)
       (message "Wrapper %S does something more." (quote ,wrapper))))))

(defun wrappee-interactive (num str)
  "That is the doc string of wrappee-interactive."
  (interactive "nNumber:\nsString:")
  (message "The number is %d.\nThe string is \"%s\"." num str))

(defun wrappee-non-interactive (format &rest arglist)
  "That is the doc string of wrappee-non-interactive."
  (apply 'message format arglist))

(make-wrapper wrappee-interactive wrapper-interactive)
(make-wrapper wrappee-non-interactive wrapper-non-interactive)
;; test of the non-interactive part:
(wrapper-non-interactive "Number: %d, String: %s" 1 "test")

지금까지 선언 양식을 전송하는 방법을 아직 찾지 못했지만 가능해야합니다.


2
흠, 누군가 가이 답변을 다운 투표했습니다. 나는 실제로 점수에 신경 쓰지 않지만 내가 관심있는 것은 대답을 다운 투표하는 이유입니다. 당신이 투표를하는 경우에도 의견을 남겨주세요! 이것은 대답을 향상시킬 수있는 기회를 줄 것입니다.
Tobias

확실하지는 않지만 패키지를 사용하는 사람이 WTF로 이동하게해야합니다. 대부분의 경우보다 합리적인 옵션은이를 처리하고 수동으로 줄 바꿈을 수행하는 기능을 작성하는 것입니다 (대화 형 사양의 일부를 적용하거나 재 작성하는
것임)

2
@wasamasa 나는 부분적으로 동의합니다. 그럼에도 불구하고 자동 계측이 필요한 경우가 있습니다. 예는 edebug입니다. 또한, interactive사양이 기능 본문보다 상당히 큰 기능이 있습니다. 이러한 경우 interactive사양을 다시 작성하는 것은 매우 지루할 수 있습니다. 질문과 답변은 필수 원칙을 다룹니다.
Tobias

1
개인적으로, 나는이 답변이 질문의 범위와 관련하여뿐만 아니라 매크로의 자연스러운 적용과 defun에서 macro로 한 단계 씩 나아가는 것을 보여주기 때문에 매우 유익합니다. 감사!
gsl

11

에서 매우 비슷한 문제를 해결해야 nadvice.el했으므로 해결책은 다음과 같습니다 (nadvice.el의 일부 코드 사용).

(defun wrapper (&rest args)
  (interactive (advice-eval-interactive-spec
                (cadr (interactive-form #'wrappee))))
  (apply #'wrappee args))

지금까지 게시 된 다른 솔루션과 비교하여 wrappee다른 대화 형 사양으로 재정의 하면 올바르게 작동 할 수 있다는 장점이 있습니다 (즉, 기존 사양을 계속 사용하지는 않음).

물론 래퍼를 투명하게 만들고 싶다면 더 간단하게 할 수 있습니다.

(defalias 'wrapper #'wrappee)

이것은 런타임에 랩핑되는 것을 찾는 랩퍼를 정의 할 수있는 유일한 응답입니다. 예를 들어 런타임에 조회되는 일부 명령으로 정의 된 작업을 수행하는 바로 가기를 추가하고 싶습니다. advice-eval-interactive-spec여기에 제안 된대로 사용 하여 해당 동적 래퍼에 해당하는 대화 형 사양을 구성 할 수 있습니다.
Igor Bukanov

그것을 만들 수 있나요 called-interactively-p반환 twrappee? 아직 funcall-interactively없습니다apply-interactively
clemera

1
@compunaut : 물론 (apply #'funcall-interactively #'wrappee args)원한다면 할 수 있습니다 . 그러나 함수가 대화식으로 호출되는 경우에만 수행해야합니다 (apply (if (called-interactively-p 'any) #'funcall-interactively #'funcall) #'wrappee args).
Stefan

하 고마워! 어떻게 든 내 상자 밖에서 생각할 수 없었습니다.
clemera

1

편집 : Tobias의 대답은 래핑 된 함수의 정확한 대화 형 양식과 docstring을 얻으므로 이것보다 좋습니다.


Aaron Harris와 kjo의 답변을 결합하면 다음과 같은 것을 사용할 수 있습니다.

(defmacro my-make-wrapper (fn &optional name)
  "Return a wrapper function for FN defined as symbol NAME."
  `(defalias ',(or (eval name)
                   (intern (concat "my-" (symbol-name (eval fn)) "-wrapper")))
     (lambda (&rest args)
       ,(format "Generic wrapper for %s."
                (if (symbolp (eval fn))
                    (concat "`" (symbol-name (eval fn)) "'")
                  fn))
       (interactive)
       (if (called-interactively-p 'any)
           (call-interactively ,fn)
         (apply ,fn args)))))

용법:

(my-make-wrapper 'find-file 'wrapper-func)

다음 중 하나를 사용하여 랩퍼를 호출하십시오.

(wrapper-func "~/.emacs.d/init.el")

M-x wrapper-func

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.