clojure: что эквивалентно схеме `set!` - PullRequest
1 голос
/ 15 апреля 2019

Я пытаюсь выполнить упражнения Структура и интерпретация компьютерных программ в clojure.Однако в нескольких местах используется схема set!, которая изменяет значение переменной внутри замыкания.Например,

(define (stash x)
  ;; return a procedure which returns a value, which
  ;; you can change by providing an argument
  (lambda (nv-maybe)
    (if (null? nv-maybe)
        x
        (begin
          (set! x (car nv-maybe))
          x))))

Я пытался использовать функцию Clojure set!, но, похоже, она работает по-другому.Каков эквивалент (или ближайшая альтернатива) схемы set! в ближайшем будущем?

Ответы [ 2 ]

3 голосов
/ 16 апреля 2019

Это упражнение кажется мне более чем надуманным (хорошо, очень надуманным). Вот как я бы это сделал в Clojure (если бы меня заставили):

(ns tst.demo.core
  (:use demo.core tupelo.core tupelo.test) )

(defn new-stash
  "Saves an initial value and returns a function `stash` that can retrieve the 
   value via `(stash)`. Value can be mutated via `(stash <new-val>)` "
  [some-val]
  (let [local-state (atom some-val)]
    (fn
      ([] @local-state)
      ([new-val] (reset! local-state new-val)))))

(dotest
  (let [stash (new-stash 5)]
    ; stash saves the value 5
    (is= 5 (stash))
    (is= 5 (stash))

    ; mutate value in stash to 7
    (is= 7 (stash 7))
    (is= 7 (stash)) ; stash now contains 7
    (is= 7 (stash))))

Конечно, вышеизложенное - это неидеоматический код Clojure. Как правило, вы просто используете atom напрямую и избегаете всего ненужного закрытия:

(def stash (atom :uninitialized)) ; could use `nil`

(dotest
  (reset! stash 5) ; save the initial value
  ; stash saves the value 5
  (is= 5 @stash)
  (is= 5 @stash)

  ; mutate value in stash to 7
  (is= 7 (reset! stash 7))
  (is= 7 @stash) ; stash not contains 7
  (is= 7 @stash))

Вы можете достичь того же результата другими способами (например, с помощью var), но atom - это самый простой способ обработки изменяемого состояния.

1 голос
/ 16 апреля 2019

Примечание : Этот ответ устарел, потому что ОП отредактировал вопрос, включив в него "почему", которое я просил. Я оставляю это для контекста, но ответ Алана Томпсона намного лучше для текущей версии вопроса.


Это зависит от того, что вы мутируете и почему. В некоторых стилях Scheme мутация является обычным явлением, и поэтому вы можете спросить «как мне изменить объект в Clojure», потому что ваш алгоритм Scheme включает в себя мутацию. Лучше задать вопрос: «Каков правильный алгоритм для решения этой проблемы в Clojure». Задавать очень широкие вопросы об очень узких фрагментах кода не будет продуктивно.

Например, ваш фрагмент кода на самом деле вообще бесполезен, потому что даже в Scheme он аналогичен:

(define (f x)
  (lambda (newvalue) '()))

То есть мутация x несущественна в вашей функции, потому что невозможно прочитать x! Таким образом, сказать вам, что это Clojure, не очень интересно: просто

(defn f [x]
  (fn [newvalue] nil))

но, конечно, это не отвечает на ваш настоящий вопрос. Итак, дело в том, чтобы поддержать: что является вашим настоящим вопросом? Почему Вы мутируете с этим? Затем респонденты могут предложить тип мутации, которая подходит для вашего сценария, или (возможно) альтернативный подход, который в конце концов не требует мутации.

...