LINUX.ORG.RU

Clojure persistent / transient

 


0

1

Видимо, не до конца понимаю суть persistent / transient.

Код 1:

(defn duplicate [s]
  (let [result []] ; будем заполнять persistent вектор result
    (doseq [e s]   ; для каждого элемента вектора s (аргумент)
      (println e)  ; выводим его на экран
      (conj result e e)) ; добавляем два таких элемента в result
    result)) ; возвращаем result 

(defn -main [& args]
  (println (duplicate [1 2 3])))
Выводит:

1
2
3
[]

Код 2:

(defn duplicate! [s]
  (let [result (transient [])] ; будем заполнять transient вектор result
    (doseq [e s]   ; для каждого элемента вектора s (аргумент)
      (println e)  ; выводим его на экран
      (conj! result e) ; добавляем два таких элемента в result
      (conj! result e))
    (persistent! result))) ; возвращаем persistent result 

(defn -main [& args]
  (println (duplicate! [1 2 3])))
Выводит:

1
2
3
[1 1 2 2 3 3]

Почему первый код не работает?
Плохо ли в данном случае использование transient?
Можно ли сделать без transient?

Ведь persistent != immutable, так?
В первом коде он, по идее, на каждый вызов conj должен создавать новый вектор (у которого 1 новый элемент, а остальные шарятся между предыдущими версиями вектора).
Почему в итоге возвращает пустой вектор?

★★★★★

(conj result e e)) ; добавляем два таких элемента в result

Оно не добавляет, оно возвращает новую секвенцию. result не меняется

плохо ли в данном случае использование transient?

вообще да

Можно ли сделать без transient?

да

Debasher ★★★★★
()
Последнее исправление: Debasher (всего исправлений: 1)
Ответ на: комментарий от kovrik

1. Как сделать, чтобы менялся?

Не надо чтобы менялся.

(doall (flatten (map (fn [e] (println e) [e e]) s))) наверное будет работать

2. Почему в случае с transient все таки меняется?

так задумано

Debasher ★★★★★
()
Последнее исправление: Debasher (всего исправлений: 2)
Ответ на: комментарий от Debasher

(doall (flatten (map (fn [e] (println e) [e e]) s))) наверное будет работать

почти.
Это не будет работать, если в качестве аргумента передаем, например, [[1 2] 3]. Должно получиться [[1 2] [1 2] 3 3], а получается [1 2 1 2 3 3]

kovrik ★★★★★
() автор топика
Ответ на: комментарий от kovrik

1. Как сделать, чтобы менялся?

Есть несколько способов:

  • Использовать атомы и reset!, swap!
  • Использовать рекурсию
  • Использовать map и т.п. функции
Norgat ★★★★★
()
Последнее исправление: Norgat (всего исправлений: 1)
Ответ на: комментарий от kovrik
(defn flatten-one-level [coll]  
  (mapcat  #(if (sequential? %) % [%]) coll))

(defn foo [s] (doall (flatten-one-level (map (fn [e] (println e) [e e]) s))))
Norgat ★★★★★
()
Последнее исправление: Norgat (всего исправлений: 1)
Ответ на: комментарий от Norgat

Типа такого?

(defn duprec [s r]
  (if (empty? s)
    r
    (duprec (rest s) (conj r (first s) (first s))))
Как принято избавляться от вспомогательного аргумента? Делать еще одну функцию?

kovrik ★★★★★
() автор топика
Ответ на: комментарий от Norgat

Лично я его не люблю и юзаю либо атомы, либо map и т.п.

Атомы же, все таки, не для этих целей созданы, не?

kovrik ★★★★★
() автор топика
Ответ на: комментарий от kovrik

Атомы же, все таки, не для этих целей созданы, не?

Атомы - это стейт, который можно юзать из разных потоков без возможности зафейлится. Но зачем писать, на Clojure, код, который может зафейлится в многопоточности, если сам язык создан для обратного?

Norgat ★★★★★
()
Ответ на: комментарий от Norgat

Ну, понятно.
В любом случае, всем спасибо, все понял и со всем разобрался.

Непривычно мыслить функционально после Java :)

kovrik ★★★★★
() автор топика
Ответ на: комментарий от clojure

Не помню. Как дома буду - проверю.
Но я в последнее время чаще в LightTable сижу - там REPL удобный (хотя она глюкавая немного, но мне пока хватает).

kovrik ★★★★★
() автор топика
Ответ на: комментарий от clojure

Для Clojure у меня есть только:

NeoBundle 'tpope/vim-fireplace'
NeoBundle 'guns/vim-clojure-static'

autocmd FileType clojure vnoremap <F10> :Eval<CR>
autocmd FileType *.clj   vnoremap <F10> :Eval<CR>

Вот мой .vimrc

kovrik ★★★★★
() автор топика

А я думал кложура уже сдулась. Коммитов в офф. репозитарии кложуры последнее время кот наплакал.

abc
()
Ответ на: комментарий от kovrik

Просто задача дублирования элементов сама по себе мало о чем говорит. Можно было бы подумать об оптимальном решении в контексте более более высокоуровневой задачи (мне показалось, что дублирование списка - шаг промежуточный и явно лишний).

Если это просто учебный пример без контекста - другое дело.

Kostafey
()
Ответ на: комментарий от abc

Она стабилизировалась и близка к совершенству (асимптотически приближается).

Kostafey
()
Ответ на: комментарий от Kostafey

Если это просто учебный пример без контекста - другое дело.

так и есть :)
Я Clojure относительно недавно начал изучать, после Java - непривычно.
Начал с Clojure Koans, теперь вот решаю задачки из https://www.4clojure.com

kovrik ★★★★★
() автор топика

Оба варианта неправильные. persistent это и есть immutable. conj возвращает новую коллекцию.

С transient коллекциями нужно также обращаться как и с persistent. Т. е. conj! может вернуть как ту же коллекцию, так и новую. В данном случае просто повезло, что коллекция осталась той же.

Вообще говоря, transient это инструмент оптимизации и не стоит его использовать повсеместно.

(defn duplicate [s]
  (mapcat (fn [e] [e e]) s))
k_andy ★★★
()
Ответ на: комментарий от k_andy

persistent это и есть immutable

вообще-то нет.
Immutable - это неизменяемые структуры данных
Persistent - персистентные. Те, которые всегда сохраняют предыдущую версию при изменении.

Насчет решения спасибо - действительно, просто и лаконично.

kovrik ★★★★★
() автор топика

Такой еще вариант:

(defn duplicate [coll]
  (interleave coll coll))
;; => #'user/duplicate

(duplicate [1 2 3])
;; => (1 1 2 2 3 3)

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.