3

シーケンスの最後の要素を反復処理するときに、特別な方法で処理する必要があることがよくあります。たとえば、組み込み関数を考えてみましょうinterpose

> (interpose ", " (range 4))

(0 ", " 1 ", " 2 ", " 3)

これについての考え方の1つは、次のとおりです。

  • 最初の要素を取り、「、」を追加します
  • 2番目の要素を取り、「、」を追加します
  • 3番目の要素を取り、「、」を追加します
  • ..。
  • 最後から2番目の要素を取得し、「、」を追加します
  • 最後の要素を取り、何もしません

また、シーソーを使用してミグレイアウトを構築するときは、何か特別なことをする必要があることもわかりました。たとえば、これを作成したいとします。

+---+---+---+
| 1 | 2 | 3 |
+---+---+---+
| 4 | 5 | 6 |
+---+---+---+
| 7 | 8 | 9 |
+---+---+---+

ここで、各数字はいくつかのコンポーネント(ボタンなど)です。これを行うには、パネル全体のMigレイアウト制約を「フロー」にしてから、各コンポーネントに次の制約を追加します。

  • 「成長」-コンポーネント1、2、4、5、7、8、9
  • 「成長、ラップ」-コンポーネント3、6

上記は次のように言うことができることに注意してください:

  • 行の最後のコンポーネントを除く各コンポーネントの「成長」
  • 最後の行の最後のコンポーネントを除く他のすべてのコンポーネントの「成長、折り返し」

ここでも同じ「特別な最後の要素」のテーマが表示されます。

したがって、2つの質問:

  • 上記の推論は理にかなっていますか?つまり、上記のサンプルの問題と一般的なテーマを考慮して、設計を根本的に変更し、これを別の方法で考える必要がありますか?
  • そうでない場合、これを行うための慣用的で短い方法はありますか?

はい、ヘルパー関数やマクロを作成することはできますが、これはよくあることなので、上記のいずれかである必要があると思いがちです。言い換えれば、同じタイプの「問題」に遭遇し、それらをどのように解決しますか?

4

3 に答える 3

1

あなたはシーケンスの間違った終わりについて考えています。clojure のすべてのシーケンス (実際には一般的な LISP) は、より多くの要素のシーケンスで構成された要素です。

シーケンス処理は、シーケンスの最初の要素に対して何かを行い、残りを全体として (おそらく再帰的に) 処理するように設計されています。実際、clojure には、この考え方を奨励するためにfirstandという名前の関数があります。rest

@Rafalによるコメントで指摘されているように、介入は次のようになります...

  • 最初の要素を取る
  • 2 番目の要素を取り、前に「,」を付けます
  • 3 番目の要素を取り、前に「,」を付けます ...

Clojure では、これを (ほぼ) 次のように実装できます。

(defn interpose [s]
    (cons (first s) (map #(str "," %) (rest s))))

core.clj の実際の実装はinterleave関数に基づいて構築されています。この関数は 2 つのシーケンスを処理するため、より複雑ですが、それでも最初の + 残りのイディオムに基づいて構築されています。

多くの (ほとんどの?) アルゴリズムは、この考え方に適しています。

于 2012-04-14T21:46:44.007 に答える
1

ここでの議論のほとんどは、必要な関数が「似ている」挿入であるという仮定に基づいています。いくつかの点ではそうですが、大きな違いは、interpose の使用を最適化するために、lazy に作られていることです。

遅延関数は、長さが不明 (つまり、ストリーム) または無限 (つまり、範囲) のシーケンスから要素を取得します。ルート関数 (つまり、テイク) で値を生成するために必要な要素のみが計算されます。last および last に依存する関数 (count など) を呼び出すということは、last または count を実現するには、元のシーケンスを完全にトラバースする必要があることを意味します。これは、sw1nn が警告するものです。

ただし、この場合、Davidの回答によると、要素の量、または最後の要素のインデックスはおそらくすでにわかっています。これを count を使用せずにパラメーターとして使用できるようになるとすぐに、そのような関数を非常に簡単に作成でき、遅延させることさえできます。

(def buttons [\a \b \c \d \e \f \g \h \i])

(defn partition-nth-but
  [n b coll]
  (map
    (partial map second)                            ; remove index
    (partition-by
      #(and (integer? (/ (% 0) n)) (not= (% 0) b))  ; partition by index every nth but b
      (map-indexed (fn [i v] [(inc i) v]) coll))))  ; index coll offset 1 

=> (partition-nth-but 3 9 buttons)
((\a \b) (\c) (\d \e) (\f) (\g \h \i))

(def grow str)
(def grow-and-wrap (comp clojure.string/upper-case grow))

=> (map apply (cycle [grow grow-and-wrap]) (partition-nth-but 3 9 buttons))
("ab" "C" "de" "F" "ghi")

しかし、とにかく一連の関数を適用する場合は、関数の正しい繰り返しを循環させることもできます

(defn every-but-nth
  [n rf nf]
  (concat (repeat (dec n) rf) (repeat 1 nf)))

=> (apply concat
     (every-but-nth 3
       (every-but-nth 3 "grow" "grow-and")
     (repeat 3 "grow")))
("grow" "grow" "grow-and" "grow" "grow" "grow-and" "grow" "grow" "grow")

=> (map
     #(% %2)
     (apply concat (every-but-nth
                     3
                     (every-but-nth 3 grow grow-and-wrap)
                     (repeat 3 grow)))
     buttons)
("a" "b" "C" "d" "e" "F" "g" "h" "i")
于 2012-04-15T12:03:19.140 に答える
0

入力のサイズ、たとえばnがわかっているので、最初のn-1要素に対してのみ何かを行うだけです。これは、最初の介入例に対する最も簡単な解決策です。

あなたの成長の例では、3 と 6 でラップするn-1 (または 8) 要素を成長させます。次に、最後にn (9) を付けます。

ただし、入力のサイズが常にわかっているとは限りません。この場合、最初の要素を除外し、残りの要素のみを操作することで、同じ結果を得ることができます。これはより一般的なケースであり、おそらく clojure を使用する際の考え方に近いものです。

于 2012-04-14T03:48:51.107 に答える