Clojure:创建函数,其名称在运行时作为参数给出

时间:2017-11-01 19:15:31

标签: function clojure macros

我试图创建一个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 "lang.main$function_with_custom_name$name__4660@a6afefa"]
# 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的来源),唯一的标准是第一个参数应该被识别为符号。

有关如何执行此操作的任何提示?

编辑:用例,如评论中所述:我在Clojure中编写了一个简单的语言解释器,其中包括(其中包括)可以创建函数。我语言中的函数目前由匿名Clojure函数表示。但是,如果Clojure函数也有名称,它将使调试解析器更容易。

EDIT2 :第一次编辑让我思考,我得出结论,我不能使用基于宏的解决方案来解决这个问题,因为我需要创建函数运行时(和,据我记忆,宏只能在编译时工作)。 - >为清晰起见,更改了问题标题。不过,请不要删除基于宏的答案,因为它们可以提供有用的见解。

3 个答案:

答案 0 :(得分:1)

您可以使用defmacro。

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

答案 1 :(得分: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

答案 2 :(得分:0)

更新

对于简单版本,您可以使用defmacro。对于更复杂的版本(例如使用by the potemkin library),您需要在创建var和"实习生"它进入了clojure命名空间。这是通过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)))))

查看输出:

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

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

请注意,fred是一个clojure函数,而wilma-var是一个clojure var。请see this post关于foo之类的符号与var和函数之间的关系。

另请注意,宏版本采用不带引号的符号fred作为输入,而函数版本采用双引号中的纯字符串作为输入。

因此fred是指向函数的符号,而wilma-var是指向var的符号(然后指向函数)。在任何一种情况下,Clojure都允许我们输入(fred)(wilma-var)来进行函数调用,结果我们得到42或43。