Как сделать рекурсивную функцию локальной для тела let - PullRequest
4 голосов
/ 07 ноября 2010

Я пытаюсь создать функцию в Clojure, которая является локальной для тела функции (let ...). Я попробовал следующее, но (defn ...) определяет вещи в глобальном пространстве имен.

(let [] (defn power [base exp]
      (if (= exp 0)
        1
        (if (> exp 0)
          ; Exponent greater than 0                                           
          (* base (power base   (- exp 1)))
          ; Exponent less than 0                                              
      (/ (power base (+ exp 1)) base))))
 (println (power -2 3)))

; Function call outside of let body
(println (power -2 3))

Теперь я тоже попробовал:

(let [power (fn [base exp]
      (if (= exp 0)
        1
        (if (> exp 0)
          ; Exponent greater than 0                                           
      (* base (power base (- exp 1)))
          ; Exponent less than 0                                              
      (/ (power base (+ exp 1)) base))))]
 (println (power -2 3)))

; Function call outside of let body
(println (power -2 3))

Но тогда я получаю ошибку:

Exception in thread "main" java.lang.Exception: Unable to resolve symbol: power in this         context (math.clj:6)

Как создать функцию, пространство имен которой является локальным для тела let и может рекурсивно вызывать себя?

Ответы [ 4 ]

10 голосов
/ 07 ноября 2010

Для этого вы можете использовать letfn:

 (letfn [(power [base exp]
               (cond 
                 (= exp 0) 
                 1
                 (> exp 0) ; Exponent greater than 0                              
                 (* base (power base (dec exp)))
                 :else ; Exponent less than 0                                     
                 (/ (power base (inc exp)) base)))]
      (print (power -2 3)))

Обратите внимание, что я также изменил вашу вложенную if-конструкцию на cond, я думаю, что она более читабельна. Также я изменил (+ exp 1) и (- exp 1) на (inc exp) и (dec exp) соответственно. Вы даже можете улучшить свою функцию, например, используя recur и аргумент-накопитель, но, возможно, это выходит за рамки вашего вопроса. Также см. Комментарий Брайана Карпера ниже.

3 голосов
/ 08 ноября 2010

letfn - лучшее решение для вашего конкретного случая.Однако вы также можете создать именованную «анонимную» функцию следующим образом:

(let [power (fn power [base exp] ...)]
  (println (power -2 3)))

Однако этот стиль не допускает взаимно рекурсивные функции, что letfn делает.

2 голосов
/ 08 ноября 2010

Помимо хорошего ответа Мишеля: использование функции высокого порядка на ленивых последовательностях часто позволяет получить точные решения по сравнению с явной рекурсией:

(defn power [base exp]
  (reduce * (repeat exp base)))
1 голос
/ 08 ноября 2010

Вот решение, которое является примером последнего утверждения Михеля об использовании аккумулятора.Это позволяет вам использовать recur, чтобы получить преимущество оптимизации tail call .Это дает вам преимущество в том, что при каждой рекурсии не используется пространство стека.

(defn pow [base exp]
 (letfn [(power [base exp accum]
                (cond
                  (= exp 0) accum
                  (> exp 0) (recur base (dec exp) (* accum base))
                  :else (recur base (inc exp) (/ accum base))))]
 (power base exp 1)))

user> (pow -3 2)
9
user> (pow -3 3)
-27

Если вы просто хотите написать функцию, которая поднимает базовое число до степени, не забудьте, что вы можете вызватьметоды, которые уже существуют в Java.java.util.Math может помочь вам здесь.

(Math/pow -2 3)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...