私は自分にClojureを教えています。
非FP言語では、ネストされたifを簡単に作成でき、特にelseを指定しなかった場合は、ifブロックから制御が流れます。例えば:
Thing myfunc()
{
if(cond1)
{
if(cond2)
return something;
}
return somethingelse;
}
ただし、Clojureには(私が知っている)returnステートメントがないため、次のように記述します。
(defn myfunc []
(if (cond1)
(if (cond2) something))
somethingelse)
そうすれば、「何か」の「返却」はありません。ちょっと言ってみて、わかりました、ここに値があります。実行を続けましょう。明白な解決策は、条件を組み合わせることです、すなわち:
(if (and (cond1) (cond2))
something
somethingelse)
しかし、これは大きな条件では扱いにくく/醜くなります。また、cond1の「else」部分にステートメントを追加するには、追加の調整が必要になります。これに対するエレガントな解決策はありますか?
これは、命令型と機能型のアプローチの微妙な違いです。命令型では、return
を関数の任意の場所に配置できますが、関数型では、明確で明示的な実行パスを用意するのが最善の方法です。一部の人々(私を含む)は、命令型プログラミングでも後者のアプローチを好み、それをより明白で管理しやすく、エラーが発生しにくいと認識しています。
この関数を明示的にするには:
Thing myfunc() {
if(cond1) {
if(cond2)
return something;
}
return somethingelse;
}
次のようにリファクタリングできます。
Thing myfunc() {
if(cond1 && cond2) {
return something;
} else {
return somethingelse;
}
}
Clojureでは、同等のものは次のとおりです。
(defn myfunc []
(if (and cond1 cond2)
something
somethingelse))
「else」が必要な場合、Javaバージョンは次のようになります。
Thing myfunc() {
if(cond1) {
if(cond2) {
return something;
} else {
return newelse;
}
} else {
return somethingelse;
}
}
...およびそれに相当するClojure:
(defn myfunc []
(if cond1
(if cond2 something newelse)
somethingelse))
命令型言語にはif this then do that else do that
というifステートメントがあり、関数型言語にはif this return that else return this
というif式があります。それは同じ考えを見る別の方法であり、問題を表現するための非常に異なるアプローチを反映しています。関数型言語で すべてに価値がある、その値で何もしなくても、本当にすべて。
移行するときに、慣れ親しんでいた「この関数が何をすべきか」という質問ではなく、「この関数が返す結果は何か」を自分自身に尋ねることが非常に役立ちました。
(if (and (cond1) (cond2))
something
somethingelse)
(cond
(and (cond1) (cond2)) something
:else somethingelse)
cond
は、同じものを比較したい場合にこれを行います。スイッチケースではcondp
を使用できます。
そのようなコードはあまり見かけませんが、それがその方法です。
Clojureには明示的なreturnステートメントはありませんが、コードは "something"で "return"します。その理由は、そのif
の後に式がなく、Clojureで最後の式の結果が関数の戻り値として使用されます。
この問題を解決するための(imho)最も読みやすいアプローチは、 core.match ライブラリーによって提供されます。
このコードスニペット:
(defn myfunc []
(if (cond1)
(if (cond2) something))
somethingelse)
になる:
(ns my.project
(:require [clojure.core.match :refer [match]]))
(defn myfunc []
(match [cond1 cond2]
[true true] something
[_ _] somethingelse))
ブール値以外の値、たとえばキーワード(またはそのことについての任意のclojure値)でのマッチングなど、次のようにマッチングできることに注意してください。
(defn myfunc []
(match [cond1 some-kw another-kw]
[true :a :z] foo
[true :b _] gee
[true _ _] bar
[false _ _] giz
[_ _ _] default))
その他 例 。
(cond)
マクロを使用することもできます。
(defn length-checker [a b]
(cond
(= 3 (count (str a))) (if (= 3 (count (str b)))
(println "both numbers are 3 digits long")
(println "first number is 3 digits, but the 2nd not!"))
:else (println "first- or both of the numbers are not 3 digits")))