web-dev-qa-db-ja.com

clojureのif-else分岐

私は自分に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」部分にステートメントを追加するには、追加の調整が必要になります。これに対するエレガントな解決策はありますか?

28
Kricket

これは、命令型と機能型のアプローチの微妙な違いです。命令型では、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))
33
Konrad Garus

命令型言語にはif this then do that else do thatというifステートメントがあり、関数型言語にはif this return that else return thisというif式があります。それは同じ考えを見る別の方法であり、問​​題を表現するための非常に異なるアプローチを反映しています。関数型言語で すべてに価値がある、その値で何もしなくても、本当にすべて。

移行するときに、慣れ親しんでいた「この関数が何をすべきか」という質問ではなく、「この関数が返す結果は何か」を自分自身に尋ねることが非常に役立ちました。

15
Arthur Ulfeldt
(if (and (cond1) (cond2))
     something
     somethingelse)

(cond 
    (and (cond1) (cond2)) something
    :else somethingelse)

condは、同じものを比較したい場合にこれを行います。スイッチケースではcondpを使用できます。

そのようなコードはあまり見かけませんが、それがその方法です。

15
nickik

Clojureには明示的なreturnステートメントはありませんが、コードは "something"で "return"します。その理由は、そのifの後に式がなく、Clojureで最後の式の結果が関数の戻り値として使用されます

8
Nevena

この問題を解決するための(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))

その他

0
Thomas Moerman

(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")))
0
amirt