Clojure条件分岐まとめ

📝Clojure Flowsの中のとくに条件分岐についてまとめ.

このメモでは基礎的にシンタックスをまとめる.

発展的なポリモーフィズムは🔖Clojure Expression Problemへ.

Clojure: 真偽値(true/false)

📝Clojure フォームの一つとしての論理値, 真偽値(true/false)

💡Clojureの世界における真と偽

Clojureではfalseとnilとが偽であり. ほかは真である.

  • 空リスト[]は偽ではない. (cf. Common Lisp)
  • 0は偽ではない (cf. C言語).

Clojure: 条件式

フォームを評価する基本的なものを列挙.

  • true?
  • false?
  • nil?
    • not-nil?はないが, some?でnil判定はできる.
  • not
  • =
  • not=: not equal
  • empty?
  • seq

シーケンスに対する述語

第一引数に条件(predicate), 末尾にCollectionを取る.

(xxx pred coll)

これらはfilter関数とあわせて用いられる.

  • every?
  • not-any?
  • not-every?

これはcollのみ ([x])

  • any?

some

someはシーケンスのいづれかの値が条件を満たすかどうかを判定する.

(some pred coll)

戻り値に注意!

述語がtrueを返したら直ちにその値を返す. 全てがfalseならnilを返す.

Clojure条件分岐(if/when)

Clojure条件分岐の基礎.

Clojure: if/when

  • if/when
  • if-not/when-not
  • if-let/when-let
  • if-some/when-some

Clojureではif-elif-elseはないことに注意. condで代用する.

Clojure: switch文(cond/condp/case)

switchのシンタックスはないけどcondとcaseがある.

このswitch文の発展が📝Clojure マルチメソッド | Multimethod.

🔖Clojure Style Guideによると, cond, condp, caseの順によいとされている. 書き方がよりシンプルになるので.

GitHub - bbatsov/clojure-style-guide: A community coding style guide for the Clojure programming language

cond

https://clojuredocs.org/clojure.core/cond

  • 複数のifを書くときのわかりやすい記法.
  • 条件に当てはまらない場合の処理をelseで実施することができる.
(defn pos-neg-or-zero[n]
  (cond
    (< n 0) "negative"
    (> n 0) "positive"
    :else "zero"))

condp

condpというシンタックスもある. condよりも見やすい.

Understanding Clojure: cond and condp — Matthew Boston

case

https://clojuredocs.org/clojure.core/case

  • condにおいて判定する値が1つしかない場合.
  • case文の最後に条件なしを記述するとそれがdefaultになる.

clojure/core.match: Clojureでパターンマッチ

Clojureでパターンマッチ.

defun: Clojureでのパターンマッチマクロ

関数にパターンマッチの機能を追加するためのマクロ. もしxxならばyyするのような条件の羅列をキレイに記述.

core.matchの形式で条件を Multi-arity Functions の形式で処理を記述することができる.

killme2008/defun

ref. Clojureにおけるパターンマッチングdefunマクロ - Qiita


erlangや 📝Elixer からのインスパイヤとか.

ref. 🔗なぜ僕の中でElixirが一番であり続けるのか | さんぽしの散歩記

この記事では Exixerのパイプライン演算子とパターンマッチを組み合わせて流れるようにコードを書く魅力が語られている.

同じことを Clojureでやろうとするならば cond-> 条件つきスレッドマクロ の機能がそれに該当するかもしれないが,

長いパイプライン演算子は別関数として切り出すべき

なるほどたしかに小さな関数に切り出してパターンマッチしたほうが見た目がいい場合がある.

📝Clojure マルチメソッド(multimethod)

Clojure 条件分岐トピックス

andとorをフロー制御につかう

💡LISPの世界のand/orはフロー制御の評価器

💡whenはifにelseがないことを示す

whenはifの簡易系だが, そのキモif文にelseがないことを示す.

概してifやelseの中身の()で囲まれるような部分が長くなりがちである. さらにifとelseがどこで区切れるのか可読性の問題もある.

そういう脳内メモリを節約するという目的がある.

💡if-let/when-let は処理の結果による分岐でつかう

if-let/when-letは, letで定義したロジック判定のシンボルをifでテストする.

これは,たとえば複雑な真偽判定のロジックにシンボルを割り当てることで可読性を上げるために利用できる.


if-letはマクロでありC言語の邪悪な習慣をエレガントにしたとか😕

なにかの処理をして戻り値がnilか否かで処理を分岐するときにエレガントな書き方ができる. 具体的にはlet とifで以下なものを,

(let [result (proc)]
  (if result
    (do-something result)
    (do-else)))

if-letだと以下のようにかける.

(if-let [result (proc)]
  ((do-something result)
    (do-else)))

Java的には関数を処理して異常の場合は戻り値に -1とかfalseとか戻さずに例外を上げるのがベストプラクティスだけど関数型でも同じなのだろうか?そしてこの 関数の呼び出し元に分岐を判断させるのがC言語の邪悪な習慣といっているのだろうか?いずれにしろこういう戻り値にnilをつかって呼び出し元を困らせる邪悪な関数につかう.

ref. 💡例外がないと戻り値チェックでウンコード

まあif-letやwhen-letはnilが戻るような副作用のある関数の呼び出しの文脈で利用するものと心得ておこう.

see more. if-let or throw pattern on http request

if-let/when-let vs if-some/when-some

if-letと似たような文法でif-some/when-someというものがある.

(if-some [result (proc)]
  ((do-something result)
    (do-else)))

ここのresultの部分がnilでなければ処理をする. if-letの比較だと, if-letはtrue/falseで判定するがif-someはnilか否か.

ref. if-some - clojure.core

up: 📂Clojure Core Languages