配列から座標を取得するClojure

配列内の文字の座標をどのようにして見つけることができるかを理解するのに問題があります。

このような小さなASCIIマップがあります。

+-----+
|XPX X|
|X   X|
|X  XX|
|X   X|
|XX DX|
+-----+

このマップは行ごとに配列に分割されているため、配列は次のようになります。

["+-----+" "|XPX X|" "|X   X|" "|X  XX|" "|X   X|" "|XX DX|" "+-----+"]

私がやろうとしているのは、 'P'などの文字を渡して、マップ内のその文字の座標を見つけることです。この場合、 'P'は座標(2,3)にあります。

出発点として、私はを使用して文字の列を把握しようとしました

(doseq [m map] (println (clojure.string/index-of m "P")))

これはの出力を返しました

nil
2
nil
nil
nil
nil
nil
nil

この時点で、 '2'とこの行が含まれている配列のインデックスを返す方法について混乱しています。

1

5 答え

そのための1つの方法 - あなたの入力があなたが提示したような文字列のベクトルであると仮定すると、そのようになります:

(def the-map ["+-----+" "|XPX X|" "|X   X|" "|X  XX|" "|X   X|" "|XX DX|" "+-----+"])

(defn get-index [item coll]
  (some #(when ((complement nil?) %) %)
         (map-indexed (fn [ix x] (let [i (clojure.string/index-of x item)]
                                  (when i [ix i])))
                      coll)))

これはあなたが探しているどんなアイテムの最初のインスタンスも返します。 "+"の場合は [0,0] になります。また、clojureはゼロから始まるインデックスを使用するため、(2,3)は実際には(1,2)です。

You can shorten this a little by using keep with identity

(defn get-index [item coll]
  (first (keep identity
               (map-indexed (fn [ix x] (let [i (clojure.string/index-of x item)]
                                        (when i [ix i])))
                            coll))))

編集:

私は、(最初(IDを保持)を持つことは冗長ですが、代わりに someidentity を使うのは冗長です)

(defn get-index [item coll]
  (some identity 
        (map-indexed (fn [ix x] (let [i (clojure.string/index-of x item)]
                                     (when i [ix i])))
                                   coll)))

以下のいくつかの回答は、最初の座標ではなく、すべての一致座標を取得する方法を示しました。これを実現するには、上のバージョンで some identityremove nil?に変更するだけです。

例えば:

(defn get-index [item coll]
  (remove nil? 
        (map-indexed (fn [ix x] (let [i (clojure.string/index-of x item)]
                                     (when i [ix i])))
                                   coll)))

最後に、インデックスを1ベースにすることが本当にあなたの意図である場合は、見つかった各インデックスを単に増やすことができます。

(defn get-index [item coll]
  (some identity 
        (map-indexed (fn [ix x] (let [i (clojure.string/index-of x item)]
                                     (when i [(inc ix) (inc i)])))
                                   coll)))
2
追加された
ああ…そうです、それは some identity の必要性を排除し、さらにコードを短くします。非常に多くの便利な機能がコアにあります私はそれらすべてを思い出すのに苦労しています!
追加された 著者 Solaxun,
keep-indexed もご覧ください。
追加された 著者 amalloy,
これは完璧に機能しました。私はamalloyが述べたようにkeep-indexedを使うことでそれをさらに短くしました。
追加された 著者 David Furnam,

私はもう少し進んで、charのすべての出現の座標を見つける関数を書きました。

(let [a ["+-----+" "|XPX X|" "|X   X|" "|X  XX|" "|X   X|" "|XX DX|" "+-----+"]]
        (letfn [(find-coords [a ch]
                    (let [op (fn [f] (comp inc #(f % (count (first a)))))]
                        (->> a
                             (clojure.string/join "")
                             (map-indexed vector)
                             (filter (comp (partial = ch) second))
                             (map first)
                             (map (juxt (op quot) (op rem))))))]
            (find-coords a \P)))

=> ([2 3])

(find-coords a \X)
=> ([2 2] [2 4] [2 6] [3 2] [3 6] [4 2] [4 5] [4 6] [5 2] [5 6] [6 2] [6 3] [6 6])
1
追加された
(def strmap ["+-----+" "|XPX X|" "|X   X|" "|X  XX|" "|X   X|" "|XX DX|" "+-----+"])

(defn find-char-2d
  [arr char-to-find]
  (some->> (map-indexed (fn [i row]
                          [i (clojure.string/index-of row char-to-find)])
                        arr)
           (filter second)
           first
           (map inc)))

(println (find-char-2d strmap "Q")) ; prints "nil"
(println (find-char-2d strmap "P")) ; prints "(2, 3)"

The map-indexed loops through the row strings, searching each one for your substring while keeping track of the row index i. The some->> threading macro passes the result (which is a LazySeq) to the filter, which removes all elements with nil columns. Since we only want the first coordinates (assuming the character you're searching for can only exist once in the map), we select the first element. If the substring doesn't exist in the map, we will get nil, and the the some->> will short-circuit so that nil is returned. Otherwise, the indices will both be incremented (since your coordinates are 1-indexed.

代わりに、あなたはあなたの地図を線形化し、線形地図の中でインデックスを見つけ、そして線形インデックスから2次元座標を計算することができます:

(defn find-char-2d
  [arr char-to-find]
  (some-> (clojure.string/join "" arr)
          (clojure.string/index-of char-to-find)
          ((juxt #(-> (/ % (count arr)) int inc)
                 #(inc (mod % (count (first arr))))))))
1
追加された

それらすべてを見つける方法を示すために、もう1つ P 文字を追加しました。これは for を使ったもっと簡単な解決策です:

(defn strs->array [strs] (mapv vec strs))

(def data ["+-----+"
           "|XPX X|"
           "|X   X|"
           "|X  XX|"
           "|X P X|"
           "|XX DX|"
           "+-----+"])
(def data-array (strs->array data))

(defn find-chars-2 [ch-array tgt-char]
  (let [num-rows (count ch-array)
        num-cols (count (first ch-array))]
    (for [ii (range num-rows)
          jj (range num-cols)
          :let [curr-char (get-in ch-array [ii jj])]
          :when (= tgt-char curr-char)]
      [ii jj])))

結果として:

(find-chars-2 data-array \P))  => ([1 2] [4 3])

Using get-in assumes you have nested vectors, hence the need for strs->array. We also assume the data is rectangular (not ragged) and haven't put in any error-checking that a real solution would need.

0
追加された

私は(もう少し一般化された)このようなもので行くだろう

(def field ["+-----+"
            "|XPX X|"
            "|X  ZX|"
            "|X  XX|"
            "|X   X|"
            "|XX DX|"
            "+-----+"])

(defn find-2d [pred field]
  (for [[i line] (map-indexed vector field)
        [j ch] (map-indexed vector line)
        :when (pred ch)]
    [i j ch]))

user> (find-2d #{\P} field)
;;=> ([1 2 \P])

user> (find-2d #{\P \Z} field)
;;=> ([1 2 \P] [2 4 \Z])

user> (find-2d #{\D \P \Z} field)
;;=> ([1 2 \P] [2 4 \Z] [5 4 \D])

user> (find-2d #(Character/isUpperCase %) field)
;;=> ([1 1 \X] [1 2 \P] [1 3 \X] [1 5 \X] 
;;    [2 1 \X] [2 4 \Z] [2 5 \X] [3 1 \X] 
;;    [3 4 \X] [3 5 \X] [4 1 \X] [4 5 \X] 
;;    [5 1 \X] [5 2 \X] [5 4 \D] [5 5 \X])

もう1つはより機能的です(ただし読みにくくなります)。

(defn find-2d [pred field]
  (filter (comp pred last)
          (mapcat #(map vector (repeat %1) (range) %2)
                  (range)
                  field)))

最初のものとまったく同じように動作します

0
追加された