15

OK、今は不正行為はありません。

いいえ、実際には、1、2分かかり、これを試してみてください。

「ポジション」は何をしますか?

編集:cgrandの提案に従って簡略化。

(defn redux [[current next] flag] [(if flag current next) (inc next)])

(defn positions [coll]
  (map first (reductions redux [1 2] (map = coll (rest coll)))))

さて、このバージョンはどうですか?

def positions(coll) {
  def (current, next) = [1, 1]
  def previous = coll[0]
  coll.collect {
    current = (it == previous) ? current : next
    next++
    previous = it
    current
  }
}

私はClojureを学んでいて、関数型プログラミングをいつも楽しんでいるので、それが大好きです。Clojureソリューションを思いつくのに時間がかかりましたが、エレガントなソリューションを考えなければならないことを楽しんでいました。Groovyのソリューションは問題ありませんが、私はこのタイプの命令型プログラミングが退屈で機械的であることに気付いたところです。Javaを12年間使用した後、私は轍を感じ、Clojureを使用した関数型プログラミングが必要な後押しになりました。

そうです、要点を理解してください。正直に言うと、数か月後に戻ったときにClojureコードを理解できるのではないかと思います。確かに私はそれをコメントすることができますが、それを理解するためにJavaコードにコメントする必要はありません。

だから私の質問は:関数型プログラミングパターンにもっと慣れることの問題ですか?関数型プログラミングの達人はこのコードを読んで、理解するのが簡単だと思っていますか?どのバージョンがわかりやすいと思いましたか?

編集:このコードが行うことは、同点のプレイヤーを追跡しながら、ポイントに応じてプレイヤーの位置を計算することです。例えば:


Pos Points
1. 36
1. 36
1. 36
4. 34
5. 32
5. 32
5. 32
8. 30
4

7 に答える 7

22

本質的な可読性などはないと思います。慣れているものと、慣れていないものがあります。コードの両方のバージョンを正常に読み取ることができました。私もCとJavaを見て10年、Clojureを見てたった1年だったので、Groovyを知らなくても、Groovyバージョンを実際にはもっと簡単に読むことができました。それは言語については何も言っていません。それは私について何かを言っているだけです。

同様に、英語はスペイン語よりも簡単に読むことができますが、これらの言語の本質的な読みやすさについては何も言いません. (スペイン語は、単純さと一貫性の点で、おそらく 2 つの言語の中で「より読みやすい」言語ですが、私はまだそれを読むことができません)。私は今日本語を勉強していて大変苦労していますが、ネイティブの日本人は英語についても同じことを言っています。

人生のほとんどを Java を読むことに費やした場合、当然、Java に似たものはそうでないものよりも読みやすくなります。C に似た言語を見るのと同じくらい Lisp 言語を見ることに時間を費やすまで、これはおそらく真実であり続けるでしょう。

言語を理解するには、とりわけ次のことに精通している必要があります。

  • 構文 ([vector](list), hyphens-in-names)
  • 語彙(どういうreductions意味ですか?どのように/どこでそれを調べることができますか?)
  • 評価規則 (関数をオブジェクトとして扱うことはできますか? ほとんどの言語ではエラーです。)
  • などのイディオム(map first (some set of reductions with extra accumulated values))

これらはすべて、学習して内面化するために時間と練習と繰り返しが必要です。しかし、次の 6 か月間で Clojure をたくさん読んだり書いたりすれば、6 か月後にその Clojure コードを理解できるようになるだけでなく、おそらく今よりもよく理解できるようになるでしょう。それ。これはどう:

(use 'clojure.contrib.seq-utils)                                        ;;'
(defn positions [coll]
  (mapcat #(repeat (count %) (inc (ffirst %)))
          (partition-by second (indexed coll))))

1年前に書いたClojureのコードを見ると、その出来の悪さにぞっとしますが、まあまあ読めます。(あなたの Clojure コードがひどいと言っているわけではありません。私はそれを読むのに何の問題もありませんでしたし、私は教祖でもありません。)

于 2009-11-13T04:52:17.063 に答える
8

編集:もう関係ないかもしれません。

Clojureのものは私には複雑です。理解する必要があるより多くの抽象化が含まれています。これは、高階関数を使用することの代償です。それらが何を意味するのかを理解する必要があります。したがって、孤立したケースでは、命令はより少ない知識を必要とします。しかし、抽象化の力はそれらを組み合わせる手段にあります。すべての命令型ループは読んで理解する必要がありますが、シーケンスの抽象化により、ループの複雑さを取り除き、強力な操作を組み合わせることができます。

さらに、Groovy バージョンは、実際には map である高階関数である collect を使用しているため、少なくとも部分的に機能していると主張します。中には状態もあります。

Clojure バージョンの書き方は次のとおりです。

(defn positions2 [coll]
  (let [current (atom 1)
        if-same #(if (= %1 %2) @current (reset! current (inc %3)))]
    (map if-same (cons (first coll) coll) coll (range (count coll)))))

これは、変更可能な「現在」を使用するという点でGroovyバージョンと非常に似ていますが、次/前の変数がなく、代わりに不変のシーケンスを使用するという点で異なります。ブライアンが雄弁に言ったように、可読性は本質的なものではありません。このバージョンは、この特定のケースでの私の好みであり、中間のどこかにあるようです。

于 2009-11-13T07:12:20.917 に答える
8

私はティモシーに同意します: あなたはあまりにも多くの抽象化を導入しています. 私はあなたのコードを作り直して、次のように終了しました:

(defn positions [coll]
  (reductions (fn [[_ prev-score :as prev] [_ score :as curr]] 
                (if (= prev-score score) prev curr))
    (map vector (iterate inc 1) coll)))

あなたのコードについて、

(defn use-prev [[a b]] (= a b))
(defn pairs [coll] (partition 2 1 coll))
(map use-prev (pairs coll))

次のように簡単にリファクタリングできます。

(map = coll (rest coll))
于 2009-11-13T12:50:23.323 に答える
4

Clojure のものは、一見するとより複雑です。もっとエレガントかもしれませんが。OO は、言語をより高いレベルでより「関連性の高い」ものにするための結果です。関数型言語には、より「アルゴリズム」(プリミティブ/エレメンタリー) な感覚があるようです。まさにその瞬間に感じたことです。Clojure の使用経験が増えると、状況が変わるかもしれません。

残念ながら、どの言語が最も簡潔であるか、または最小のコード行で問題を解決できるかというゲームに陥っています。

問題は私にとって2つの折り目です:

  1. 一見しただけで、コードが何をしているのかを簡単に把握できますか?. これはコード管理者にとって重要です。

  2. コードの背後にあるロジックを推測するのはどれくらい簡単ですか?. 冗長/長すぎる?. 簡潔すぎる?

「すべてを可能な限りシンプルにしますが、シンプルにしないでください。」

アルバート・アインシュタイン

于 2009-11-18T02:44:15.203 に答える
3

私もClojureを学んでおり、それを愛しています。しかし、開発のこの段階では、Groovy バージョンの方が理解しやすかったです。私がClojureで気に入っているのは、コードを読んで「あはは!」となるところです。何が起こっているのかを最終的に「理解」したときの経験。私が本当に楽しんでいるのは、コードを変更せずにコードを他のタイプのデータに適用できるすべての方法を理解した数分後に起こる同様の経験です. Clojure でいくつかの数値コードを処理した回数のカウントを失い、しばらくして、同じコードを文字列、シンボル、ウィジェットなどで使用する方法を考えました...

私が使用する類推は、色を学ぶことです。赤い色を知ったときのことを覚えていますか? あなたはすぐにそれを理解しました-世界にはこの赤いものがすべてあります。その後、マゼンタという用語を聞いて、しばらく迷子になりました。しかし、もう少し経験を積むと、コンセプトが理解でき、特定の色をより具体的に表現できるようになります。概念を内面化し、頭の中にもう少し情報を保持する必要がありますが、最終的にはより強力で簡潔なものになります。

于 2009-11-13T14:58:35.243 に答える
3

Groovy は、この問題を解決するさまざまなスタイルもサポートしています。

coll.groupBy{it}.inject([]){ c, n -> c + [c.size() + 1] * n.value.size() }

きれいにリファクタリングされていませんが、理解するのが難しくありません。

于 2010-06-27T06:28:27.360 に答える
3

これが質問に対する答えではないことはわかっていますが、次のようなテストがあれば、コードをよりよく「理解」できます。

assert positions([1]) == [1]
assert positions([2, 1]) == [1, 2]
assert positions([2, 2, 1]) == [1, 1, 3]
assert positions([3, 2, 1]) == [1, 2, 3]
assert positions([2, 2, 2, 1]) == [1, 1, 1, 4]

それは、今から 1 年後に、コードが何をすることが期待されているかを教えてくれます。ここで見たコードのどの優れたバージョンよりもはるかに優れています。

私は本当に話題から外れていますか?

もう一つは、「読みやすさ」は文脈によると思います。誰がコードを保守するかによって異なります。たとえば、Groovy コードの「機能的」バージョンを維持するためには (どんなに優れていても)、Groovy プログラマーだけでなく、機能的な Groovy プログラマーも必要になります。コードは「初心者」の Clojure プログラマーにとって理解しやすくなり、コードはより大きなコミュニティによって理解されるため、全体的に読みやすくなります。コードを把握して編集できるようになるために Clojure を 3 年間勉強する必要はありません。それに。

于 2011-11-18T23:36:20.923 に答える