2017-11-01 9 views
0

사용자 정의 이름을 가진 다른 함수를 리턴하는 Clojure 함수를 작성하려고합니다. 내 시도까지 :Clojure : 런타임에 매개 변수로 주어진 이름의 함수를 작성하십시오.

(defn function-with-custom-name [name] (fn name [] 42)) 
(function-with-custom-name "hello") 
# --> #object[lang.main$function_with_custom_name$name__4660 0xa6afefa "[email protected]"] 
# syntactically ok, but its name is 'name' and not 'hello' 

(defn function-with-custom-name [name] (fn (symbol name) [] 42)) 
# --> CompilerException java.lang.IllegalArgumentException: Parameter declaration symbol should be a vector, compiling:(/tmp/form-init3365336074265515078.clj:1:40) 

(defn function-with-custom-name [name] (fn '(symbol name) [] 42)) 
# --> CompilerException java.lang.IllegalArgumentException: Parameter declaration quote should be a vector, compiling:(/tmp/form-init3365336074265515078.clj:1:40) 

내가 fn 매크로, 따라서 적절한 인용이 매개 변수 아마 중요하지만, 위의에 따라, 나는 바로 그것을 얻을 수 없다는 것을 이해하지만 나는 99 % 확신 (fn의 출처를 살펴보면) 첫 번째 매개 변수가 기호로 인식되어야한다는 유일한 기준이 있기 때문에 방법이 있습니다.

이 작업을 수행하는 방법에 대한 힌트가 있습니까?

EDIT : 의견에서 묻는대로 유스 케이스 : 저는 Clojure에서 간단한 언어 해석기를 작성하고 있습니다 (다른 것들 중에서도) 기능을 만들 수 있습니다. 내 언어의 함수는 현재 익명의 Clojure 함수로 표현됩니다. 그러나 Clojure 함수에도 이름이 있으면 인터프리터 디버깅이 훨씬 쉬워집니다.

EDIT2 : 첫 번째 편집을 통해 나는이 문제에 대한 매크로 기반 솔루션을 사용할 수 없다는 결론을 내 렸습니다. 런타임에 함수를 만들 필요가 있기 때문에 (그리고 지금까지는 매크로는 컴파일시에만 작동한다는 것을 기억하십시오.) -> 명확성을 위해 질문 제목을 변경했습니다. 매크로 기반 응답은 유용한 통찰력을 제공하므로 삭제하지 마십시오.

+0

이에 대한 사용 사례를 명확히 수 (즉 런타임에 fn을 작성하는 이유와 사용 방법). –

+0

@AlanThompson : 편집을 참조하십시오. – Attilio

답변

0

업데이트 : 간단한 버전에 대한

, 당신은 defmacro를 사용할 수 있습니다. 좀 더 복잡한 버전 (예 : by the potemkin library)을 사용하려면 var을 생성하고 클로저 이름 공간에 "interning"해야합니다. 이 clojure.core/intern를 통해 이루어집니다 출력에서 ​​

(ns tst.demo.core 
    (:use demo.core tupelo.test) 
    (:require [tupelo.core :as t])) 
(t/refer-tupelo) 

(defmacro make-fn-macro-unquote [name] 
    (spyxx name) 
    `(defn ~name [] 42)) 

(defn make-fn-func-quote [name2] 
    (spyxx name2) 
    (intern 'tst.demo.core (symbol name2) (fn [] 43))) 

(dotest 
    (make-fn-macro-unquote fred) 
    (spyxx fred) 
    (is= 42 (spyx (fred))) 

    (let [wilma-var (make-fn-func-quote "wilma")] 
    (spyxx wilma-var) 
    (is= 43 (spyx (wilma-var))))) 

봐 : wilma-var는 Clojure의 var에있는 동안 fred가하는 Clojure의 함수이다

name => clojure.lang.Symbol->fred 
fred => tst.demo.core$fn__38817$fred__38818->#object[tst.demo.core$fn__38817$fred__38818 0x5f832a1 "[email protected]"] 
(fred) => 42 

name2 => java.lang.String->"wilma" 
wilma-var => clojure.lang.Var->#'tst.demo.core/wilma 
(wilma-var) => 43 

하는 것으로합니다. foo과 같은 기호와 var 및 함수 간의 관계에 대해 see this post을 입력하십시오.

또한 매크로 버전은 인용 부호가없는 기호 fred을 입력으로 사용하며 함수 버전은 큰 따옴표로 묶은 입력을 입력으로 사용합니다.

그래서 fred은 함수를 가리키는 기호이고 wilma-var은 var (함수를 가리키는)를 가리키는 기호입니다. 두 경우 모두 Clojure를 사용하면 (fred) 또는 (wilma-var)을 입력하여 함수 호출을 할 수 있으며 결과적으로 42 또는 43이됩니다.

+0

'dotest'와'is ='는 무엇입니까? – amalloy

+0

그들은 'tupelo.test' (편의상 이름 공간 선언 추가)의 편리한 함수입니다. –

+0

감사합니다. 나는 당신의 솔루션을 시험해 보았습니다. 왜 함수 호출로 작동하지 않는지 설명해 주시겠습니까? – Attilio

1

defmacro를 사용할 수 있습니다.

(defmacro function-with-custom-name [name] 
    `(fn ~(symbol name) ([] 42))) 
1

매크로를 사용하지 않고도 런타임에 네임 스페이스 기능을 사용할 수 있습니다.그것은 당신에게 예를 들어 일부 입력에서 함수를 등록하는 방법 제공 할 수 있습니다 (난 정말이 생각에 대한 이유를 찾을 수없는,하지만 어쩌면 그것은 단지 나)

user> (defn runtime-defn [f-name f] 
     (intern (ns-name *ns*) (symbol f-name) f)) 
#'user/runtime-defn 

user> (runtime-defn "my-fun" #(* 100 %)) 
#'user/my-fun 

user> (my-fun 123) 
;;=> 12300 

user> (runtime-defn (read) #(* 200 %)) 
#input "danger!!!" 

#'user/danger!!! 

user> (danger!!! 1) 
;;=> 200