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