2016-03-03 28 views
0

我想1)用下面的函數創建一個符號列表;然後2)使用這些符號/名稱創建原子,以便可以從其他函數修改原子。這是爲了產生符號/名稱的功能:用函數創建clojure原子

(defn genVars [ dist ] 
    (let [ nms (map str (range dist)) neigs (map #(apply str "neig" %) nms) ] 
     (doseq [ v neigs ] 
      (intern *ns* (symbol v) [ ])) 
    )) 

如果DIST = 3,則3個符號,neig0,... neig2被創建的每個用空載體結合。如果可以使用這些符號在功能上創建原子,以便可以從其他函數訪問它們。任何幫助都非常感謝,即使有其他方法可以完成此任務。

+0

但是,你的功能的實際問題是什麼?你在當前命名空間中生成符號,所以你只需要用'atom'調用來包裝你的向量。 – leetwinski

回答

1

生成名稱的願望,意味着你將會通過一個單一的地圖,而不是得到更好的服務:

(def neighbours (atom (make-neighbours))) 

make-neigbours的定義可能是這個樣子:

(defn make-neighbours [] 
    (into {} (for [i (range 10)] 
      [(str "neig" i) {:age i}]))) 

當其他命名空間會看起來像使用類似的值:

(get-in @data/neighbours ["neig0" :age]) 

習慣性Clojure傾向於避免創建許多命名的全局變量,而寧願將狀態並置到由Clojure的併發原語(atom/ref/agent)控制的一個或幾個變量中。我鼓勵你想想你的問題是否可以用這種方式用單個原子來解決,而不是要求定義多個變量。儘管如此,如果您真的需要多個原子,請考慮將它們全部存儲在一個地圖var中,而不是創建許多全局變量。就我個人而言,我從來沒有遇到過創建多個原子比單個大原子更好的情況(所以我會有興趣知道這種情況很重要)。

如果您確實需要很多變量,請注意,在函數中定義變量實際上是不好的樣式(https://github.com/bbatsov/clojure-style-guide#dont-def-vars-inside-fns)。也有很好的理由!使用功能和數據的美妙來自於功能的純粹性。 def函數內部特別討厭,因爲它不僅是一種副作用,而且是一種潛在的執行流改變副作用。

當然是有一種方法可以實現它,正如另一個答案指出的那樣。

說到定義超出defdefn的東西,使用宏有很多優先權。例如來自compojure的defroutes,來自Schema的defschema,來自clojure.test的deftest。一般來說,任何創建變量的方便形式都可以。你可以使用宏的解決方案,爲您創造原子DEFS:

(defmacro defneighbours [n] 
`(do 
    [email protected](for [sym (for [i (range n)] 
       (symbol (str "neig" i)))] 
    `(def ~sym (atom {})))) 

在我看來,這是不是一個功能版本的攻勢居然少,不僅因爲它是創造全球DEFS。通過使用常規的def語法來創建全局視圖更爲明顯。但我只是把它作爲一個稻草人來提出來,因爲這仍然很糟糕。

功能和數據效果最好的原因是因爲它們組成。

有一些有形的考慮因素使得單個原子控制狀態非常方便。您可以方便地遍歷所有鄰居,您可以動態添加新鄰居。你也可以做一些事情,如連接鄰居和其他鄰居等。基本上有很多功能/數據抽象,如果你創建了許多全局變量,你就會鎖定自己。

這就是宏一般認爲對語法技巧有用的原因,但最好避免使用函數和數據。它對代碼的靈活性有着實際的影響。例如回到組合;宏語法實際上是非常有限的,因此我寧願不使用defroutes

總結:

  1. 不要讓許多全球DEFS的,如果你能避免它。
  2. 在可能的情況下,優先選擇多個原子上的1個原子。
  3. 不要在函數內部定義。
  4. 最好避免使用宏來支持函數和數據。
  5. 無論這些指導方針如何,探索什麼是可能的,並且我無法瞭解您的情況總是很好的,所以我希望您最終克服了您的直接問題,並找到了一種令人愉快的語言來使用Clojure。
+2

不需要這是一個宏,它可以只是一個函數。 –

+0

優秀!謝謝您的回答。我相信要麼是合適的。原子矢量是否有變化,以便我可以用名稱來引用原子,就像在第一個解決方案中那樣,而不是通過索引到矢量中? – Brian

+0

如果您使用地圖而不是矢量,您可以按名稱查找:'(def atoms(into {}(for [n names] [n(atom {})]))' –

2

你的功能似乎是正確的,只需在intern呼叫中用​​呼叫打包呼叫。另外我寧願使用dotimes

user> 
(defn gen-atoms [amount prefix] 
    (dotimes [i amount] 
    (intern *ns* (symbol (str prefix i)) (atom [])))) 
#'user/gen-atoms 
user> (gen-atoms 2 "x") 
nil 
user> x0 
#atom[[] 0x30f1a7b] 
user> x1 
#atom[[] 0x2149efef]