![](/img/trans.png)
[英]How to pass a symbol to a function to create a function in clojure
[英]How can I get the name of a function from a symbol in clojure?
假設我將x定義為符號函數foo
(defn foo [x] x)
(def x foo)
如果僅給定x,是否可以發現名稱“ foo”?
在這種情況下,foo中是否可以找到函數x的名稱-“ foo”?
(foo x)
是否存在或可以創建一個功能,例如:
(get-fn-name x)
foo
最近在這個網站上也提出了類似的問題。 看這里
當您執行(def x foo)
,您將x定義為“ foo
的值”,而不是“ foo
本身”。 一旦foo
解析為其值,該值就不再與foo
有任何關系。
因此,也許您現在看到了一個可能的問題答案:在定義x
時不要解析foo
。 而不是做...
(def x foo)
...做...
(def x 'foo)
現在,如果您嘗試獲取x
的值,則將得到foo
(從字面上看),而不是foo
解析為的值。
user> x
=> foo
但是,這可能有問題,因為您有時可能還希望能夠獲得foo
解析為使用x
的值。 但是,您可以通過執行以下操作來做到這一點:
user> @(resolve x)
=> #<user$foo user$foo@157b46f>
如果我要描述它的作用:“獲取x
解析的值,將其用作符號,然后將該符號解析為其var( 而不是其值),然后取消引用該var以獲得值”。
...現在讓我們做些駭人聽聞的事情。 我不確定我會建議我做建議做的這兩種事情,但是您確實問Can the name "foo" be discovered if only given x?
,我可以想到兩種方法。
方法#1:正則表達式fn var名稱
請注意foo
和x
都可以解析為以下內容:
(defn foo [a] (println a))
(def x foo)
user> foo
=> #<user$foo user$foo@1e2afb2>
user> x
=> #<user$foo user$foo@1e2afb2>
現在,檢查一下:
user> (str foo)
=> "user$foo@1e2afb2"
user> (str x)
=> "user$foo@1e2afb2"
涼。 這僅適用於foo
,因為foo
解析為一個函數,該函數恰好具有類似var的名稱,該名稱對於x
將是相同的,因為它引用了相同的函數。 注意,“ foo”包含在(str x)
(以及(foo x)
)產生的字符串中。 這是因為函數的var名稱顯然是通過向后引用最初用於定義它的符號創建的。 我們將利用這一事實從任何函數中找到該符號。
因此,我編寫了一個正則表達式以在函數var name的字符串中找到“ foo”。 不是不是要查找“ foo”,而是要查找任何子字符串(以正則表達式".*"
開頭,后跟一個\\$
字符)(以正則表達式"(?<=\\$)"
-以及后跟\\@
字符-以正則表達式"(?=@)"
...
user> (re-find #"(?<=\$).*(?=@)"
(str x))
=> "foo"
我們可以通過將其簡單地包裝(symbol ...)
來進一步將其轉換為符號:
user> (symbol (re-find #"(?<=\$).*(?=@)"
(str x)))
=> foo
此外,可以將整個過程概括為一個函數,該函數將接受一個函數並返回與該函數的var名稱關聯的符號-這是在最初定義該函數時給出的符號(該過程對於匿名函數)。
(defn get-fn-init-sym [f]
(symbol (re-find #"(?<=\$).*(?=@)" (str f))))
...或者我覺得更好閱讀的...
(defn get-fn-init-sym [f]
(->> (str f)
(re-find #"(?<=\$).*(?=@)")
symbol))
現在我們可以做...
user> (get-fn-init-sym x)
=> foo
方法2:根據身份反向查找所有ns映射
這將會非常好玩。
因此,我們將獲取所有名稱空間映射,然后dissoc
'x
,然后根據每個映射中的val是否指向與x
解析的對象完全相同的對象來過濾剩余的內容。 我們將按照過濾后的順序處理第一件事,然后再針對第一件事獲取密鑰以獲取符號。
user> (->> (dissoc (ns-map *ns*) 'x)
(filter #(identical? (let [v (val %)]
(if (var? v) @v v))
x))
first
key)
=> foo
請注意,如果將x
替換為上面的foo
,將得到x
。 實際上,所有這些操作都是返回它找到的映射到與x
完全相同的值的名字。 和以前一樣,可以將其概括為一個函數:
(defn find-equiv-sym [sym]
(->> (dissoc (ns-map *ns*) sym)
(filter #(identical? (let [v (val %)]
(if (var? v) @v v))
@(resolve sym)))
first
key))
這里的主要區別是參數必須是帶引號的符號。
user> (find-equiv-sym 'x)
=> foo
這個find-equiv-sym
函數確實不是很好。 當名稱空間中有多個事物解析為相同的值時,就會發生問題。 您可以返回解析為相同事物的符號列表(而不只是返回第一個符號),然后從那里進行進一步處理。 更改當前函數以使其起作用很簡單:刪除最后兩行( first
和key
),並用(map key)
替換它們。
無論如何,我希望這對您和我都一樣有趣和有趣,但是我懷疑這些黑客中的任何一個是否都是解決問題的好方法。 我主張我的第一個解決方案。
目前尚不清楚為什么要這樣做-當您這樣做(def x foo)
時,實際上是在名稱空間中為新的var賦予名稱x
。 它恰好具有與foo
相同的值(即,它包含相同的函數),但在其他方面則完全獨立於foo。 就像使用Java類比對同一對象有兩個引用一樣。
為什么還要繼續獲取名稱foo
?
如果您確實想要執行類似的操作,則可能是在包含原始符號的函數上使用一些自定義元數據的情況:
(def foo
(with-meta
(fn [x] x)
{:original-function `foo}))
(def bar foo)
(defn original-function [v]
"Returns the :original-function symbol from the metadata map"
(:original-function (meta v)))
(original-function bar)
=> user/foo
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.