9

最近、Clojure を学び始めました。一般的には面白そうに見えますが、構文上の不便さに慣れることができません (以前の Ruby/C# の経験と比較して)。

ネストされた式の接頭表記。Ruby では、複雑な式を左から右にチェーン/パイプで記述することに慣れていますsome_object.map { some_expression }.select { another_expression }。入力値から結果に段階的に移動するため、非常に便利です。単一の変換に集中でき、入力中にカーソルを移動する必要はありません。それとは逆に、Clojure でネストされた式を作成するときは、内側の式から外側の式にコードを記述し、常にカーソルを移動する必要があります。それは遅くなり、気を散らします。マクロについては知ってい->ます->>が、慣用句ではないことに気付きました。Clojure/Haskell などでコーディングを始めたときに同じ問題が発生しましたか? どのように解決しましたか?

4

4 に答える 4

10

私は最初Lipsについて同じことを感じたので、あなたの痛みを感じます:-)

ただし、良いニュースは、少し時間をかけて定期的に使用することで、おそらく接頭辞表記が好きになることです。実際、数式を除いて、私はスタイルを中置することを好みます。

接頭表記が好まれる理由:

  • 関数との一貫性- ほとんどの言語では、中置記号 (数学演算子) と前置記号 (関数呼び出し) の表記法が混在しています。Lips では、数学演算子を関数と見なすと、一定の優雅さを備えたすべての一貫性があります。
  • マクロ- 関数呼び出しが常に最初の位置にある場合、より正気になります。
  • Varargs - ほぼすべての演算子に対して可変数のパラメーターを使用できるのは便利です。(+ 1 2 3 4 5)私見よりもいいです1 + 2 + 3 + 4 + 5

その場合の秘訣は、コードをこのように構造化することが論理的に理にかなっている場合に->and を自由に使用することです。->>これは通常、オブジェクトまたはコレクションに対する後続の操作を処理する場合に役立ちます。

(->>
  "Hello World" 
  distinct 
  sort 
  (take 3))

==> (\space \H \W)

接頭辞スタイルで作業するときに非常に役立つ最後のトリックは、より複雑な式を作成するときにインデントをうまく利用することです。適切にインデントすると、プレフィックス表記が実際には非常に読みやすいことがわかります。

(defn add-foobars [x y]
  (+
    (bar x y)
    (foo y)
    (foo x)))
于 2012-04-24T01:42:00.493 に答える
4

私の知る限り->->>Clojureでは慣用的です。私はいつもそれらを使用しています、そして私の意見では、それらは通常はるかに読みやすいコードにつながります。

Clojureの「エコシステム」周辺の人気のあるプロジェクトで使用されているこれらのマクロの例を次に示します。

例による証明:)

于 2012-04-24T01:21:54.590 に答える
3

式チェーンが長い場合は、を使用してletください。長い暴走式や深くネストされた式は、どの言語でも特に読みやすくはありません。これは悪いです:

(do-something (map :id (filter #(> (:age %) 19) (fetch-data :people))))

これはわずかに優れています:

(do-something (map :id
                   (filter #(> (:age %) 19)
                           (fetch-data :people))))

しかし、これも悪いことです。

fetch_data(:people).select{|x| x.age > 19}.map{|x| x.id}.do_something

これを読んでいる場合、何を知る必要がありますか?do_somethingのサブセットのいくつかの属性を呼び出していますpeople。このコードは、最初と最後の間に非常に距離があり、それらの間を移動するまでに見ているものを忘れてしまうため、読みにくいです。

Rubyの場合、do_something(または最終結果を生成しているものは)行の最後で失われるため、に何をしているのかを判断するのは困難ですpeople。Clojureの場合、それが私たちが行っていることはすぐにわかりdo-somethingますが、すべてを内部まで読んでいなければ、私たちが何をしているのかを判断するのは困難です。

この単純な例よりも複雑なコードは、かなり苦痛になります。すべてのコードがこのように見える場合、これらのスパゲッティのすべての行を前後にスキャンするのに首が疲れます。

私はこのようなものを好みます:

(let [people (fetch-data :people)
      adults (filter #(> (:age %) 19) people)
      ids    (map :id adults)]
  (do-something ids))

今では明らかです:私は最初にpeople、私はぐるぐる回って、それから私do-somethingは彼らに行きます。

そして、あなたはこれで逃げるかもしれません:

fetch_data(:people).select{|x|
  x.age > 19
}.map{|x|
  x.id
}.do_something

しかし、少なくともこれを実行したいと思います。

adults = fetch_data(:people).select{|x| x.age > 19}
do_something( adults.map{|x| x.id} )

またlet、中間表現に適切な名前がない場合でも、使用することは前例のないことではありません。(このスタイルは、Clojure自身のソースコードで使用されることがあります。たとえば、のソースコードdefmacro

(let [x (complex-expr-1 x)
      x (complex-expr-2 x)
      x (complex-expr-3 x)
      ...
      x (complex-expr-n x)]
  (do-something x))

これは、デバッグにおいて大きな助けになる可能性があります。これは、次のようにすることでいつでも物事を検査できるためです。

(let [x (complex-expr-1 x)
      x (complex-expr-2 x)
      _ (prn x)
      x (complex-expr-3 x)
      ...
      x (complex-expr-n x)]
  (do-something x))
于 2012-04-24T20:12:23.827 に答える
3

Lisp を初めて使い始めたときも、実際に同じハードルを目にしました。それがコードをよりシンプルで明確にする方法を理解するまでは、本当にいらいらしていました。

initial + scale + offset

なりました

(+ initial scale offset)

次に、プレフィックス表記を試し(+)て、関数が独自のID値を指定できるようにします

user> (*)
1
user> (+)
0

もっと多くの例がありますが、私のポイントは接頭表記を擁護することではありません。肯定的な側面が明らかになるにつれて、学習曲線が(感情的に)平坦になることを伝えたいだけです.

もちろん、マクロを書き始めると、プレフィックス表記は便利ではなく必須になります。


あなたの質問の2番目の部分に対処するために、スレッドの最初のマクロとスレッドの最後のマクロは、コードをより明確にするときはいつでも慣用的です:)それらは純粋な算術演算よりも関数呼び出しでより頻繁に使用されますが、方程式はより口当たりが良いです。


PS: (.. object object2 object3)-> object().object2().object3();

(doto my-object
   (setX 4)
   (sety 5)`
于 2012-04-24T00:12:21.287 に答える