Clojure でベクターに結果を蓄積する最良の方法は? (純粋な関数型コードは見苦しく冗長に思えます) 質問する

Clojure でベクターに結果を蓄積する最良の方法は? (純粋な関数型コードは見苦しく冗長に思えます) 質問する

...可変データを使った命令型プログラミングが私の脳に深く刻み込まれているだけかもしれませんが、Clojure でデータのベクトルを構築するコードは冗長で扱いにくく、複雑だと感じています。もっと良い方法があるはずです!

Ruby では次のようなコードを書くかもしれません:

results = []
a_collection.each do |x|
  x.nested_collection.each do |y|
    next if some_condition_holds
    results << y
  end
end

Clojure では、おそらく次の (恐ろしい) コードのような再帰関数を使用する以外に、これを行うためのより良い方法は知りません。

; NEWBIE ALERT! NEWBIE ALERT!
(loop [results   []
       remaining a_collection]
  (if (empty? remaining)
      results
      (recur
        (loop [results results
               nested  (nested_collection (first remaining))]
           (if (empty? nested)
               results
               (if (some_condition_holds)
                   (recur results (rest nested))
                   (recur (conj results (first nested)) (rest nested))))) 
        (rest remaining))))

可変データと反復ループがなければ、コレクションを構築するには再帰を使用する必要があります。このような再帰関数にはそれぞれ(empty?)ガード句などが必要です。全体があまりにも反復的であるため、叫びたくなります。

単純なケースではmapこれで十分ですが、ネストのレベルが複数あり、各レベルで反復をスキップする必要がある条件が存在するケースを考えています。

Common Lisp ではloop、マクロ または を使用する場合がありますmapcan。Clojure には のようなものはありませんかmapcan?

ベストアンサー1

オプションの見た目が優れていると思う順に並べると次のようになります。

(for [x coll,
      y (nested-collection x)
      :when (not (some-condition-holds y))]
  y)

あるいは、次の構文を使用する代わりにmap、 やなどの関数を使用して構築することもできます。mapcatfor

(mapcat (fn [x]
          (remove some-condition-holds
                  (nested-collection x)))
        coll)

本当に興味がある場合は、部分関数の適用と合成を使用して構築することもできます。

(mapcat (comp (partial remove some-condition-holds)
              nested-collection)
        coll)

この 3 番目のスタイルは Clojure ではあまり読みやすくありませんが、他の言語では同等のコードが非常に読みやすいです。たとえば Haskell では次のようになります。

coll >>= (filter (not . someConditionHolds) . nestedCollection)

おすすめ記事