簡體   English   中英

Clojure:創建函數,其名稱在運行時作為參數給出

[英]Clojure: create function whose name is given as a parameter at run-time

我正在嘗試創建一個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 :第一次編輯使我想到,我得出的結論是我無法使用基於宏的解決方案來解決此問題,因為我需要創建運行時函數(據我所記得,宏只能工作在編譯時)。 ->為了清楚起見,更改了問題標題。 不過,請不要刪除基於宏的答案,因為它們會提供有用的見解。

您可以使用defmacro。

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

您也可以在運行時執行此操作,而無需使用宏,而是使用名稱空間函數。 例如,它可以為您提供從某些輸入中注冊函數的方式(盡管我真的找不到任何好的理由,但也許只是我一個人)

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

更新

對於簡單版本,可以使用defmacro 對於更復雜的版本(例如potemkin庫使用版本),您需要弄亂創建一個var並將其“ intern-ing”到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。 參閱這篇文章 ,了解foo等符號與var和函數之間的關系。

還要注意,宏版本使用未加引號的符號fred作為輸入,而函數版本則使用雙引號的純字符串作為輸入。

因此, fred是指向函數的符號,而wilma-var是指向var(然后指向函數)的符號。 無論哪種情況,Clojure都允許我們鍵入(fred)(wilma-var)進行函數調用,結果是42或43。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM