簡體   English   中英

如何在 Clojure 中為函數參數創建默認值

[英]How to create default value for function argument in Clojure

我帶着這個:

(defn string->integer [str & [base]]
  (Integer/parseInt str (if (nil? base) 10 base)))

(string->integer "10")
(string->integer "FF" 16)

但它必須是一個更好的方法來做到這一點。

如果簽名的數量不同,一個函數可以有多個簽名。 您可以使用它來提供默認值。

(defn string->integer 
  ([s] (string->integer s 10))
  ([s base] (Integer/parseInt s base)))

請注意,假設falsenil都被視為非值, (if (nil? base) 10 base)可以縮短為(if base base 10) ,或者進一步縮短為(or base 10)

自 Clojure 1.2 [ ref ] 起,您還可以將rest參數解構為映射。 這使您可以為函數參數命名並提供默認值:

(defn string->integer [s & {:keys [base] :or {base 10}}]
    (Integer/parseInt s base))

現在你可以打電話

(string->integer "11")
=> 11

或者

(string->integer "11" :base 8)
=> 9

你可以在這里看到這個: https : //github.com/Raynes/clavatar/blob/master/src/clavatar/core.clj (例如)

此解決方案更接近原始解決方案精神,但略微清潔

(defn string->integer [str & [base]]
  (Integer/parseInt str (or base 10)))

一個類似的模式,可以方便地使用orlet結合使用

(defn string->integer [str & [base]]
  (let [base (or base 10)]
    (Integer/parseInt str base)))

雖然在這種情況下更詳細,但如果您希望默認值依賴於其他輸入值,它會很有用。 例如,考慮以下函數:

(defn exemplar [a & [b c]]
  (let [b (or b 5)
        c (or c (* 7 b))]
    ;; or whatever yer actual code might be...
    (println a b c)))

(exemplar 3) => 3 5 35

這種方法也可以很容易地擴展到使用命名參數(如 M. Gilliar 的解決方案中):

(defn exemplar [a & {:keys [b c]}]
  (let [b (or b 5)
        c (or c (* 7 b))]
    (println a b c)))

或者使用更多的融合:

(defn exemplar [a & {:keys [b c] :or {b 5}}]
  (let [c (or c (* 7 b))]
    (println a b c)))

您可能需要考慮另一種方法:函數。 這些可以說是為函數指定默認值的更“功能性”和更靈活的方式。

首先創建(如有必要)一個函數,該函數具有您希望作為默認值提供的參數作為前導參數:

(defn string->integer [base str]
  (Integer/parseInt str base))

這樣做是因為 Clojure 版本的partial允許您僅按照它們在函數定義中出現的順序提供“默認”值。 根據需要對參數進行排序后,您可以使用partial函數創建函數的“默認”版本:

(partial string->integer 10)

為了使該函數可多次調用,您可以使用def將其放入 var 中:

(def decimal (partial string->integer 10))
(decimal "10")
;10

您還可以使用let創建“本地默認值”:

(let [hex (partial string->integer 16)]
  (* (hex "FF") (hex "AA")))
;43350

部分函數方法與其他方法相比有一個關鍵優勢:函數的使用者仍然可以決定默認值是什么,而不是函數的生產者而無需修改函數定義 這在帶有hex示例中進行了說明,其中我決定默認函數decimal不是我想要的。

這種方法的另一個優點是您可以為默認函數分配不同的名稱(十進制、十六進制等),這可能更具描述性和/或不同的范圍(var、local)。 如果需要,部分函數也可以與上述一些方法混合使用:

(defn string->integer 
  ([s] (string->integer s 10))
  ([base s] (Integer/parseInt s base)))

(def hex (partial string->integer 16))

(請注意,這與 Brian 的回答略有不同,因為由於此回復頂部給出的原因,參數的順序已顛倒)

與 Matthew 的建議非常相似的方法是不執行& rest args,而是要求調用者提供靈活(和可選)鍵的單個額外 map 參數。

(defn string->integer [s {:keys [base] :or {base 10}}]
    (Integer/parseInt s base))

(string->integer "11" {:base 8})
=> 9

(string->integer "11" {})
=> 11

這樣做的好處是選項是單個參數映射,調用者不必非常小心傳遞偶數個額外參數。 另外,linters 可以用這種風格做得更好。 與每行成對的 args 相比,編輯器還應該在地圖鍵值表格對齊方面做得更好(如果您願意的話)。

稍有不利的一面是,在沒有選項的情況下調用時,仍必須提供空映射。

這在這個位置參數部分(代碼氣味文章)中有所涉及。

更新:現在 Clojure 1.11 支持傳遞可選參數的映射(使用& )。

暫無
暫無

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

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