Clojure'da fonksiyon argümanı için varsayılan değer nasıl oluşturulur


127

Ben bununla geliyorum:

(defn string-> tamsayı [str & [taban]]
  (Integer / parseInt str (if (nil? Base) 10 base)))

(dize-> tam sayı "10")
(dize-> tamsayı "FF" 16)

Ama bunu yapmanın daha iyi bir yolu olmalı.

Yanıtlar:


170

İmzalar birbirlerinden farklıysa, bir işlevin birden çok imzası olabilir. Varsayılan değerleri sağlamak için bunu kullanabilirsiniz.

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

Varsayımın falseve nilher ikisinin de değer dışı olarak kabul (if (nil? base) 10 base)edildiğini (if base base 10), kısaltılabileceğini veya daha ileriye götürülebileceğini unutmayın (or base 10).


3
Sanırım ikinci satırda fonksiyon adını tekrarlamak yerine (recur s 10)kullanmak daha iyi olur . Bu, gelecekte işlevi yeniden adlandırmayı kolaylaştırır. Bu durumlarda kullanmamak için herhangi bir sebep bilen var mı ? recurstring->integerrecur
— Rory O'Kane

10
Görünüşe göre recursadece aynı arity üzerinde çalışıyor. yukarıda java.lang.IllegalArgumentException: Mismatched argument count to recur, expected: 1 args, got: 2, compiling:
— yinelemeyi

Aynı sorunla karşılaştım. Fonksiyonun kendisini çağırması mantıklı olur (string->integer s 10)mu (yani )?
— Kurt Mueller

155

restClojure 1.2 [ ref ] sürümünden bu yana argümanları harita olarak da yok edebilirsiniz . Bu, işlev bağımsız değişkenleri için varsayılanları adlandırmanıza ve sağlamanıza olanak tanır:

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

Şimdi arayabilirsin

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

veya

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

Bunu burada eylem halinde görebilirsiniz: https://github.com/Raynes/clavatar/blob/master/src/clavatar/core.clj (örneğin)


1
Python arka planından geliyorsa anlaşılması çok kolay :)
— Dan

1
Bunu anlamak, kabul edilen cevaptan çok daha kolay ... Kabul edilen "Clojurya" yolu bu mu? Lütfen bu belgeye eklemeyi düşünün.
— Droogans

1
Ben var olan bir sorunu eklendi yardım adresine Buna gayri resmi stil kılavuzuna.
— Droogans

1
Bu cevap, kabul edilen cevaptan daha "doğru" yolu yakalar, ancak her ikisi de iyi sonuç verir. (elbette, Lisp dillerinin büyük bir gücü, genellikle aynı temel şeyi yapmanın birçok farklı
— yolunun olmasıdır

2
Bu bana biraz kelime gibi geldi ve bir süre hatırlamakta zorlandım, bu yüzden biraz daha az ayrıntılı bir makro yarattım .
— akbiggs

33

Bu çözüm, orijinal çözümün ruhuna daha yakın , ancak marjinal olarak daha temiz

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

Bir Benzer bir model kullanışlı kullanımları olabilir orkombinelet

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

Bu durumda daha ayrıntılı olsa da, varsayılanların diğer girdi değerlerine bağlı olmasını istiyorsanız yararlı olabilir . Örneğin, aşağıdaki işlevi düşünün:

(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

Bu yaklaşım, adlandırılmış argümanlarla (M. Gilliar'ın çözümünde olduğu gibi) çalışacak şekilde kolayca genişletilebilir :

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

Veya daha fazla füzyon kullanarak:

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

Varsayılanlarınıza diğer varsayılanlara bağlı olarak ihtiyacınız yoksa (veya belki de olsa bile), Matthew'un yukarıdaki çözümü, farklı değişkenler için birden çok varsayılan değere de izin verir. Normal kullanmaktan çok daha temizor
— johnbakers

Ben bir Clojure noob'um, bu yüzden belki OpenLearner haklıdır, ancak bu Matthew'un yukarıdaki çözümüne ilginç bir alternatiftir. Sonunda kullanmaya karar versem de kullanmasam da bunu bildiğime sevindim.
— GlenPeterson

orfarklıdır :orçünkü orfarkını bilmiyor nilve false.
— Xiangru Lian

@XiangruLian Kullanırken mi söylüyorsunuz: veya, eğer yanlış geçerseniz, varsayılan yerine yanlış kullanmayı bilecek mi? İle ya da yanlış geçtiğinde varsayılanı kullanır ve kendisi yanlış değil mi?
— Didier A.

8

Göz önünde bulundurmak isteyebileceğiniz başka bir yaklaşım daha var: kısmi işlevler. Bunlar, muhtemelen işlevler için varsayılan değerleri belirlemenin daha "işlevsel" ve daha esnek bir yoludur.

Baştaki parametre (ler) olarak varsayılan olarak sağlamak istediğiniz parametre (ler) i içeren bir işlev oluşturarak başlayın (gerekirse):

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

Bu, Clojure'un sürümü partial"varsayılan" değerleri yalnızca işlev tanımında göründükleri sırada sağlamanıza izin verdiği için yapılır . Parametreler istendiği gibi sıralandıktan sonra, işlevi kullanarak işlevin "varsayılan" bir sürümünü oluşturabilirsiniz partial:

(partial string->integer 10)

Bu işlevi birden çok kez çağrılabilir hale getirmek için, şunu kullanarak bir var'a koyabilirsiniz def:

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

Şunları kullanarak da bir "yerel varsayılan" oluşturabilirsiniz let:

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

Kısmi fonksiyon yaklaşımının diğerlerine göre bir önemli avantajı vardır: fonksiyonun tüketicisi , fonksiyon tanımını değiştirmeye gerek kalmadan fonksiyonun üreticisi yerine varsayılan değerin ne olacağına hala karar verebilir . Bu, varsayılan işlevin istediğim şey olmadığına karar verdiğim örnekte gösterilmiştir .hexdecimal

Bu yaklaşımın bir başka avantajı, varsayılan işleve daha açıklayıcı ve / veya farklı bir kapsam (var, yerel) olabilecek farklı bir ad (ondalık, onaltılık vb.) Atayabilmenizdir. Kısmi işlev, istenirse yukarıdaki bazı yaklaşımlarla da karıştırılabilir:

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

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

(Bu yanıtın üst kısmında verilen nedenlerle parametrelerin sırası tersine çevrildiğinden, bunun Brian'ın yanıtından biraz farklı olduğunu unutmayın)


1
Sorunun sorduğu bu değil; yine de ilginç.
— Zaz

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.