Как переназначить переменную другое значение в clojure - PullRequest
0 голосов
/ 10 октября 2018

Я делаю эту программу Clojure для серии Фибоначчи.

(def fibonacci []
  (def a 0)
  (def b 0)
  (def c 1)
  (def n (atom 0))
  (while (<= @n 10)
    (do
      (def c (+ a b))
      (def a b)
      (def b c)
      (println '(a b c))
      )(swap! n inc)))
(fibonacci)

Я получаю эту ошибку:

CompilerException java.lang.RuntimeException: Too many arguments to def, compiling:(C:\Users\user\AppData\Local\Temp\form-init4960759414983563364.clj:1:1) 
CompilerException java.lang.RuntimeException: Unable to resolve symbol: fibonacci in this context, compiling:(C:\Users\user\AppData\Local\Temp\form-init4960759414983563364.clj:13:1) 

Я не могу понять, как переназначить переменные a, b и c.А также, пожалуйста, предложите любые исправления, необходимые в программе.

Ответы [ 2 ]

0 голосов
/ 12 октября 2018

Во-первых, давайте сделаем ваш код работающим.Первый def, для определения функции, должен быть defn:

(defn fibonacci []
  (def a 0)
  (def b 0)
  (def c 1)
  (def n (atom 0))
  (while (<= @n 10)
    (do
      (def c (+ a b))
      (def a b)
      (def b c)
      (println '(a b c))
      )(swap! n inc)))

. Компилируется.Давайте попробуем это:

> (fibonacci)

(a b c)
(a b c)
...
(a b c)
=> nil

Не то, что вы хотели.Беда в кавычке перед (a b c), которая возвращает элементы списка без оценки.Мы должны снять цитату, но мы не хотим, чтобы a рассматривался как оператор.Итак, вставьте list:

(defn fibonacci []
  (def a 0)
  (def b 0)
  (def c 1)
  (def n (atom 0))
  (while (<= @n 10)
    (do
      (def c (+ a b))
      (def a b)
      (def b c)
      (println (list a b c))
      )(swap! n inc)))

Давайте запустим это:

> (fibonacci)

(0 0 0)
(0 0 0)
...
(0 0 0)
=> nil

Ну, у нас есть числа, но не те, которые мы хотим.Проблема в том, что первый (def c (+ a b)) стирает единственное ненулевое значение.Мы должны начать с b в 1 и просто временно использовать c:

(defn fibonacci []
  (def a 0)
  (def b 1)
  (def n (atom 0))
  (while (<= @n 10)
    (do
      (def c (+ a b))
      (def a b)
      (def b c)
      (println (list a b c))
      )(swap! n inc)))

А теперь ...

> (fibonacci)

(1 1 1)
(1 2 2)
(2 3 3)
(3 5 5)
(5 8 8)
(8 13 13)
(13 21 21)
(21 34 34)
(34 55 55)
(55 89 89)
(89 144 144)
=> nil

Ура!

Но мы можем видеть, что b и c одинаковы, и просто предсказать следующее a.

Есть большая проблема.Использование изменяемых переменных не является способом Clojure.Также не идиоматично для функции печатать то, что она вычисляет: лучше развивать результат в виде последовательности, с которой мы можем делать то, что нам нравится.

Мы можем определить всю последовательность Фибоначчи следующим образом:

(defn fibonacci []
  (map second
       (iterate
         (fn [[a b]] [b (+ a b)])
         [0 1])))

И просто развивать столько, сколько мы хотим:

   (take 10 (fibonacci))

=> (1 1 2 3 5 8 13 21 34 55)
0 голосов
/ 11 октября 2018

Как уже указывалось в комментариях, на императивном языке вы обычно решаете такую ​​проблему, изменяя значения переменных.Но в функциональном языке, таком как Clojure, основное внимание уделяется неизменности, вызовам функций и рекурсии.Хотя вы можете найти решение с bindings , set! и т. П., Это не тот стиль, в котором вы должны программировать в Clojure (по крайней мере, для такого рода проблем).

Итак, функция для итеративного вычисления чисел Фибоначчи может выглядеть следующим образом (см. Также этот раздел в SICP Distilled ):

(defn fibonacci
  [n]
  (letfn [(fib-iter [a b count]
            (if (= count 0)
              b
              (fib-iter (+ a b) a (- count 1))))]
    (fib-iter 1 0 n)))

fib-iter являетсялокальная функция и рекурсивно вызывает себя (через рекурсию хвостового вызова ).Но, как написано здесь :

Поскольку Clojure использует соглашения о вызовах Java, он не может и не дает такие же гарантии оптимизации хвостового вызова.Вместо этого он предоставляет специальный оператор recur, который выполняет рекурсивное зацикливание в постоянном пространстве путем повторного связывания и перехода к ближайшему замкнутому циклу или функциональному фрейму.

Таким образом, вы можете заменить fib-iter в хвостовом вызовес recur , чтобы получить итеративный процесс, или вы можете использовать форму цикла , которая немного упрощает код:

(defn fibonacci
  [n]
  (loop [a 1
         b 0
         count n]
    (if (= count 0)
      b
      (recur (+ a b) a (- count 1)))))
...