1

私はclojureを初めて使用し、PracticalCommonLispからcdプロジェクトを再作成して学習しています。where'clause'セレクターを使用して更新関数を実装するのに問題があります。(update(where:artist "AC / DC"):rating 10)を実行すると、データベースが吹き飛ばされ、空のリストだけが返されます。これが私のコードです:

(defn where [& {:keys [title artist genre rating]}]
(fn [cd]
    (and
        (if title (= (get cd :title) title) true)
        (if artist (= (get cd :artist) artist) true)
        (if genre (= (get cd :genre) genre) true)
        (if rating (= (get cd :rating) rating) true))))

(defn update [selector-fn & {:keys [title artist genre rating]}]
    (def ^:dynamic *db*
        (map (fn [row]
            (when (apply selector-fn row)
                    (if title (def row (assoc-in row [:title] title)))
                    (if artist (def row (assoc-in row [:artist] artist)))
                    (if genre (def row (assoc-in row [:genre] genre)))
                    (if rating (def row (assoc-in row [:rating] rating))))
            row)
        *db*)))

各CDをハッシュマップとして実装しました。

(defn make-cd [title artist genre rating]
{:title title
:artist artist
:genre genre
:rating rating
})

ですから、私のassoc-inの使い方は正しいと思っています。私が間違っていることについてのアイデアは大歓迎です。

ありがとう...

MZ

Ok。アーサーのコメントに基づいて、ここに私が今更新機能のために持っているものがあります:

(defn update [selector-fn & {:keys [title artist genre rating]}]
        (map (fn [row]
            (when (apply selector-fn row)
                (-> row
                    (#(if title (assoc-in % [:title] title) %))
                    (#(if artist (assoc-in % [:artist] artist) %))
                    (#(if genre (assoc-in % [:genre] genre) %))
                    (#(if rating (assoc-in % [:rating] rating) %)))))
        *db*))

map繰り返し処理する必要があるため、フォームはまだ必要だと思います*db**db*アーティストがAC/DCであるCDだけですべてのCDの評価を変更したくありません。したがってmap、各cdを繰り返し処理し(にバインドrow)、where関数を呼び出してタイトルが一致するかどうかを確認します。その場合、trueが返され、評価を更新できます。

残念ながら、これはまだ機能しません。

ArityException Wrong number of args (4) passed to: core$where$fn  clojure.lang.AFn.throwArity (AFn.java:437)
4

2 に答える 2

3

関数で使用defしても、意図した結果が得られることはめったにありません(スレッドセーフではありません)。各操作はマップを取得して変更し、変更されたものを返すため、最後の操作の戻り値がすべての変更の影響になるように、それぞれを次の操作にスレッド化できます。このパターンはClojureで非常に一般的であるため、非常に便利な2文字のマクロがあります。

user> (-> {:a {:k 1} :b {:k 2} :c {:k 3}} 
          (assoc-in [:a :k] 8) 
          (assoc-in [:b :k] 9))
{:a {:k 8}, :c {:k 3}, :b {:k 9}} 

「スレッドファースト」のmaco->は、各式を次の引数の最初の引数として挿入するだけです。したがって、上記の式は(芸術的なライセンスを使用して)次のように拡張されます。

(assoc-in (assoc-in {:a {:k 1} :b {:k 2} :c {:k 3}} [:a :k] 8) [:b :k] 9)) 

あなたの文脈では、これは次のようになります。

(-> row
    (#(if title (assoc-in % [:title] title) %))
    (#(if artist (assoc-in % [:artist] artist) %))
    (#(if genre (assoc-in % [:genre] genre) %))
    (#(if rating (assoc-in % [:rating] rating) %)))

各行は、引数の変更されたバージョンを返すか、変更されていないバージョンを返す関数を作成してから、それを呼び出します。row引数は、行末の2つの親の間に挿入されます。それが視覚的に魅力的でない場合は、関数に名前を付けて、呼び出しdefnに名前だけをリストすることができます。->

于 2012-10-29T18:44:18.180 に答える
0

更新関数内の行を変更しようとしているように見えますが、実際に行っているのは、すべてのステップで行を再定義することです。

しかし、map関数は新しい行を返していません。

指定するとreduce、行自体がプロパティを変更します。

于 2012-10-29T18:40:09.247 に答える