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.