Это не волшебство или что-либо... просто приятная стенография для анонимных функций.
partial(alert, "FOO!")
эквивалентно function(){alert("FOO!");}
partial(Math.max, 0)
, соответствует function(x){return Math.max(0, x);}
вызовы к частичному ( терминология MochiKit . Я думаю, что некоторые другие библиотеки дают функциям .curry метод, который делает то же самое), выглядят немного более хорошими и менее шумными, чем анонимные функции.
Как уже было сказано, использование списков - не лучшая идея, если вам нужно делать такие вещи. Произвольный доступ - это то, для чего созданы векторы. Assoc-in
делает это эффективно. Со списками вы не можете избежать рекурсии вниз в подсписки и замены большинства из них измененными версиями самих себя вплоть до самого верха.
Этот код сделает это, хотя и неэффективно и неуклюже. Заимствование из dermatthias:
(defn replace-in-list [coll n x]
(concat (take n coll) (list x) (nthnext coll (inc n))))
(defn replace-in-sublist [coll ns x]
(if (seq ns)
(let [sublist (nth coll (first ns))]
(replace-in-list coll
(first ns)
(replace-in-sublist sublist (rest ns) x)))
x))
Использование:
user> (def x '(0 1 2 (0 1 (0 1 2) 3 4 (0 1 2))))
#'user/x
user> (replace-in-sublist x [3 2 0] :foo)
(0 1 2 (0 1 (:foo 1 2) 3 4 (0 1 2)))
user> (replace-in-sublist x [3 2] :foo)
(0 1 2 (0 1 :foo 3 4 (0 1 2)))
user> (replace-in-sublist x [3 5 1] '(:foo :bar))
(0 1 2 (0 1 (0 1 2) 3 4 (0 (:foo :bar) 2)))
Вы получите IndexOutOfBoundsException
, если зададите какое-либо n
больше, чем длина подсписка. Это также не хвостовая рекурсия. Это также не идиоматично, потому что хороший код Clojure избегает использования списков для всего. Это ужасно. Я бы, вероятно, использовал изменяемые массивы Java до того, как использовал это. Я думаю, вы уловили идею.
Причины, по которым списки хуже векторов в этом случае:
user> (time
(let [x '(0 1 2 (0 1 (0 1 2) 3 4 (0 1 2)))] ;'
(dotimes [_ 1e6] (replace-in-sublist x [3 2 0] :foo))))
"Elapsed time: 5201.110134 msecs"
nil
user> (time
(let [x [0 1 2 [0 1 [0 1 2] 3 4 [0 1 2]]]]
(dotimes [_ 1e6] (assoc-in x [3 2 0] :foo))))
"Elapsed time: 2925.318122 msecs"
nil
Вам также не нужно писать assoc-in
самостоятельно, он уже существует . Когда-нибудь посмотрите на реализацию assoc-in
; это просто и понятно (по сравнению с версией со списком) благодаря векторам, обеспечивающим эффективный и легкий произвольный доступ по индексу, через get
.
Вам также не нужно цитировать векторы, как вам нужно цитировать списки . Списки в Clojure строго подразумевают: «Я вызываю здесь функцию или макрос».
Векторы (и карты, наборы и т. Д.) Могут быть перемещены через seq
s. Вы можете прозрачно использовать векторы в виде списков, так почему бы не использовать векторы и не получить лучшее из обоих миров?
Векторы также выделяются визуально. Код Clojure - это не такой большой кусок паренов, как другие Lisp, благодаря широкому использованию []
и {}
. Некоторых это раздражает, я считаю, что это облегчает чтение. (Мой редактор выделяет синтаксис ()
, []
и {}
по-разному, что помогает даже больше.)
В некоторых случаях я бы использовал список для данных:
lazy-seq
Для простых случаев функция рекурсивной подстановки даст вам именно то, что вам нужно, без особых дополнительных сложностей. когда все становится немного сложнее, самое время взломать функции open clojure, встроенные в zipper : «Clojure включает в себя чисто функциональные, общие обходы и редактирование дерева с использованием техники, называемой застежкой-молнией (в zip пространства имен) ".
адаптировано из примера в: http://clojure.org/other_libraries
(defn randomly-replace [replace-with in-tree]
(loop [loc dz]
(if (zip/end? loc)
(zip/root loc)
(recur
(zip/next
(if (= 0 (get-random-int 10))
(zip/replace loc replace-with)
loc)))))
, они будут работать с чем угодно вложенным (seq'able), даже с xmls
Сортировка of не отвечает на ваш вопрос, но если у вас есть векторы вместо списков:
user=> (update-in [1 [2 3] 4 5] [1 1] inc)
[1 [2 4] 4 5]
user=> (assoc-in [1 [2 3] 4 5] [1 1] 6)
[1 [2 6] 4 5]
Поэтому, если возможно, избегайте списков в пользу векторов для лучшего поведения доступа. Если вам приходится работать с lazy-seq из разных источников, это, конечно, не лучший совет ...
Вы можете использовать эту функцию и адаптировать ее для ваших нужд (вложенные списки):
(defn replace-item
"Returns a list with the n-th item of l replaced by v."
[l n v]
(concat (take n l) (list v) (drop (inc n) l)))
Простое предложение из арахисовой галереи:
assoc
; Это может привести к потере производительности; но если бы это была операция, чувствительная к производительности, вы бы в первую очередь работали с векторами.