Clojureで関数の引数のデフォルト値を作成する方法


127

私はこれが付属しています:

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

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

しかし、それはこれを行うためのより良い方法でなければなりません。

回答:


170

シグネチャのアリティが異なる場合、関数は複数のシグネチャを持つことができます。これを使用して、デフォルト値を指定できます。

(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)


3
私はそれを言うために二行目のために良いだろうと思う(recur s 10)使用して、recur代わりに関数名を繰り返しますstring->integer。これにより、将来関数の名前を変更しやすくなります。誰かがrecurこれらの状況で使用しない理由を知っていますか?
Rory O'Kane 14

10
recur同じアリティでしか機能しないようです。上記を再試行した場合の例:java.lang.IllegalArgumentException: Mismatched argument count to recur, expected: 1 args, got: 2, compiling:
djhaskin987

この同じ問題に遭遇しました。関数自体を呼び出すことは意味があり(string->integer s 10)ますか?
カートミューラー

155

restClojure 1.2 [ ref ] 以降、引数をマップとして分解することもできます。これにより、関数の引数に名前を付けてデフォルトを提供できます。

(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(例)


1
Pythonのバックグラウンドから来た場合は非常に理解しやすい:)
Dan

1
これは受け入れられた答えよりもはるかに理解しやすいです...これは受け入れられた「Clojurian」の方法ですか?このドキュメントへの追加を検討してください。
Droogans 2013

1
私がしている問題を追加ヘルプアドレスこれに非公式のスタイルガイドに。
Droogans 2013

1
この回答は、受け入れられた回答よりも「適切な」方法をより効果的に捉えていますが、どちらも正常に機能します。(もちろん、Lisp言語の優れた能力は、通常、同じ基本的なことを実行するためにさまざまな方法があることです)
johnbakers

2
これは少しわかりづらいようで、しばらく覚えていられなかったため、少し冗長なマクロを作成しまし
akbiggs 2014年

33

このソリューションは元のソリューションの精神に近いですが、ややクリーンです

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

同様のパターン便利使用することができるorと組み合わせますlet

(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)))

他のデフォルトに依存するデフォルトが必要ない場合(または必要な場合でも)、上記のMatthewによるソリューションでは、異なる変数に複数のデフォルト値を使用することもできます。これは、通常の使用よりもはるかにきれいですor
johnbakers

私はClojureの初心者なので、OpenLearnerが正しいかもしれませんが、これは上記のMatthewのソリューションの興味深い代替手段です。最終的にそれを使用するかどうかを決定するかどうか、これについて知ってうれしいです。
GlenPeterson 2014

or異なっている:orので、orの違いを知らないnilfalse
Xiangru Lian 2015

@XiangruLian:を使用する場合、またはfalseを渡すと、デフォルトの代わりにfalseを使用することがわかると言いますか?ながら、またはfalseが渡されたときにデフォルトを使用し、false自体は使用しませんか?
Didier A.

8

検討する必要があると思われる別のアプローチがあります。部分関数です。これらは間違いなく、関数のデフォルト値を指定するためのより「機能的」でより柔軟な方法です。

最初に、必要に応じて、主要パラメータとしてデフォルトとして提供するパラメータを持つ関数を作成します。

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

これは、Clojureのバージョンでpartialは、「デフォルト」値を関数定義に現れる順序でのみ提供できるためです。パラメータが必要に応じて順序付けられたら、関数を使用してpartial関数の「デフォルト」バージョンを作成できます。

(partial string->integer 10)

この関数を複数回呼び出し可能にするために、次のコマンドを使用して変数に配置できますdef

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

次を使用して「ローカルデフォルト」を作成することもできますlet

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

関数の部分的なアプローチには、他のものよりも1つの重要な利点があります。関数のプロデューサーではなく、関数のコンシューマー関数の定義を変更する必要なく、デフォルト値を決定できます。これは、デフォルトの機能が私が望むものではないと決定した例で示されています。hexdecimal

このアプローチのもう1つの利点は、デフォルトの関数に、よりわかりやすい別の名前(10進数、16進数など)を割り当てたり、スコープ(var、local)を変えたりできることです。必要に応じて、部分関数を上記のアプローチのいくつかと組み合わせることができます。

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

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

(この応答の上部にある理由により、パラメーターの順序が逆になっているため、これはブライアンの回答とは少し異なります)


1
これは質問が求めているものではありません。でも面白いです。
Zaz

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.