2017-05-11 52 views
0

更新:

我重寫了這個問題,使原始問題更清晰並添加了全宏解決方案。請忽略此版本,並參閱以下內容:如何自動創建Clojure`defn`函數?

How to create Clojure `defn` functions automatically without macros?

遺憾的是,不會讓我刪除舊版本。


最初由以下問題的動機:Mapped calls to clojurescript macro


假設你想自動創建很多類似的功能(即無需手工寫他們全部)。假設我們有一些預先存在的數據,我們希望寫訪問功能,像這樣:

(def foo 
    {:able "Adelicious!" 
    :baker "Barbrallicious!" 
    :charlie "Charlizable"}) 
(def bar 
    {:able "Apple" 
    :baker "Berry" 
    :charlie "Kumquat"}) 

(defn manual-my-foo [item] (get foo item)) 
(defn manual-my-bar [item] (get bar item)) 

(manual-my-foo :able) => "Adelicious!" 
(manual-my-bar :charlie) => "Kumquat" 

所以manual-my-foo是「硬連接」使用foo全球地圖。

你可能會認爲你需要一個宏來創建這個函數,這是一個解決方案。但是,宏的一個弱點是它們不能作爲參數傳遞給另一個函數,如map。因此,我們可以寫出這樣的宏:

(generate-fn :foo) ;=> creates `my-foo` w/o hand-writing it 

但下面會失敗:

(map generate-fn [:foo :bar :baz]) 

我們如何能自動化這些功能的一代?

+0

這原本是由這個問題如果用戶試圖在CLJS自動生成一些回調函數動機:http://stackoverflow.com/questions/43897632/mapped-calls-to-clojurescript-macro?noredirect = 1#comment74871840_43897632我認爲使用'intern'而不是調用宏的宏是一個不錯的解決方案。 –

回答

-4

雖然您不能將map與宏一起使用,但您可以編寫第二個宏來執行此功能。反過來,這又可能需要編寫第三個宏等,這是Clojure for the Brave and True和其他地方描述的「宏一路下來」這個短語的起源。

一個類似的問題was answered here通過使用Clojure的intern函數。我們的問題比這個問題有點不同,因爲在這裏,我們用兩種不同的方式使用intern

  • 要像defdefn
  • 訪問使用var-get
  • 一個全局變量的值來創建一個全局變量

要減少打字,我們將使用spyspy-letfrom the Tupelo library。您需要在下面的project.clj

[tupelo "0.9.38"] 

這使我們能夠寫出下面的代碼:

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

(def foo 
    {:able "Adelicious!" 
    :baker "Barbrallicious!" 
    :charlie "Charlizable"}) 
(def bar 
    {:able "Apple" 
    :baker "Berry" 
    :charlie "Kumquat"}) 

(defn generate-event-fn 
    [event-kw] 
    (spy-let [ 
    event-str (name event-kw) 
    event-sym (symbol event-str) 
    fn-name (symbol (str "my-" event-str)) 
    new-fn (fn fn-name [item] 
       (let [the-var (intern 'tst.clj.core event-sym) ; get the var the symbol 'event-sym' points to 
         the-map (var-get the-var) ; get the map the var is pointing to 
         the-str (get the-map item)] ; get the string from the map 
        (spyx the-var) 
        (spyx the-map) 
        (spyx the-str))) 
    ] 
    (intern 'tst.clj.core fn-name new-fn) ; create a var 'fn-name' pointing to 'new-fn' 
)) 

(newline) (println "*** generating functions ***") 
(newline) (generate-event-fn :foo) ; creates and interns a function 'my-foo' 
(newline) (generate-event-fn :bar) ; creates and interns a function 'my-bar' 

(newline) (println "*** calling function ***") 
(newline) (spyx (my-foo :able)) 
(newline) (spyx (my-bar :charlie)) 

在執行時,我們看到這些結果:

*** generating functions *** 

event-str => "foo" 
event-sym => foo 
fn-name => my-foo 
new-fn => #object[tst.clj.core$generate_event_fn$fn_name__32477 0x1ebd2a1b "[email protected]"] 

event-str => "bar" 
event-sym => bar 
fn-name => my-bar 
new-fn => #object[tst.clj.core$generate_event_fn$fn_name__32477 0x4824083 "[email protected]"] 

*** calling function *** 

the-var => #'tst.clj.core/foo 
the-map => {:able "Adelicious!", :baker "Barbrallicious!", :charlie "Charlizable"} 
the-str => "Adelicious!" 
(my-foo :able) => "Adelicious!" 

the-var => #'tst.clj.core/bar 
the-map => {:able "Apple", :baker "Berry", :charlie "Kumquat"} 
the-str => "Kumquat" 
(my-bar :charlie) => "Kumquat" 

所以我們看到我們創建了功能my-foomy-bar,可以訪問全球foobar地圖,respectiv伊利。我們不需要使用宏!

在Clojure中,var有點像foomy-foo這樣的符號與它們指向的值(在本例中分別爲一個映射和一個函數)之間的不可見「中間人」。欲瞭解更多信息,請參見相關崗位:

When to use a Var instead of a function?

+2

僅供參考,促進您自己的庫 - 實際上需要的答案 - 在Stackoverflow中皺起了眉頭:(http://stackoverflow.com/search?q=tupelo+user%3A1822379+,https://meta.stackoverflow .com/questions/298734/is-it-acceptable-to-promote-my-own-library-as-part-a-real-answer) – ClojureMostly

+0

我本來可以使用'println'而不是'spy'&'間諜讓,但這會增加重複和混亂。你認爲'println'會更好嗎? –

+0

我只是不能在原始問題中看到任何地方被問到「如何調試/打印中間值」。如果有一天有調試問題,您可以提出tupelo庫。否則請回答沒有調試代碼的問題。和FWIW,一個簡單的內聯'(def xx the-val)'是我在調試時所做的,因爲'data>打印的字符串' – ClojureMostly

2

我們如何可以自動這些功能的一代?

你不需要。該foobar地圖的功能進行操作,你的願望:

(foo :able) ; "Adelicious!" 

(bar :able) ; "Apple" 
+0

這是對另一個問題的迴應:http://stackoverflow.com/questions/43897632/mapped-calls-to-clojurescript-macro?noredirect = 1#comment74871840_43897632用戶試圖自動生成回調函數的地方。我只是試圖簡化爲更通用的功能。也許我在這裏過於簡單。 –

+0

我可能是錯的,但我真的不認爲最初的動機是爲地圖中的關鍵字編寫訪問器 - 這只是一個簡單的例子。當然,在這個例子中,複雜的解決方案是不必要的,但我不願意通過道歉的方式對10個問題的複雜性進行解釋,問問什麼是最終的簡單問題。 – galdre