StackOverFlow при подсчете цифр - PullRequest
9 голосов
/ 21 января 2010

Я пытаюсь посчитать количество цифр в числе в Clojure следующим образом: я получаю StackOverflowError даже для двухзначных чисел

(defn num-digits [n]
   (if (= 0 n)
   0
   (inc (num-digits (/ n 10)))))
(println (num-digits 93))

Но если я заменю / на непроверенное деление, то это будет работать как минимум 93. Но ни один из методов не работает для:

93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

Во-первых, я хотел бы знать, как выполнить деление С-стиля в Clojure. Всякий раз, когда я делаю (/ х у), я получаю соотношение, а не целое число. Как это сделать?

Во-вторых, есть ли способ API для преобразования этого числа в вектор цифр и подсчета вызовов на него.

Спасибо
Ajay G

Ответы [ 4 ]

8 голосов
/ 10 марта 2010

Вот почему у вас проблема:

user> (take 10 (iterate #(/ % 10) 10923))

(10923 10923/10 10923/100 10923/1000 10923/10000 10923/100000 10923/1000000 10923/10000000 10923/100000000 10923/1000000000)

Это исправление:

user> (take 10 (iterate #(quot % 10) 10923))

(10923 1092 109 10 1 0 0 0 0 0)

Это выражение, которое вы ищете:

user> (count (take-while #(not (zero? %)) (iterate #(quot % 10) 10923)))
5

Это обман:

user> (count (str 10923))
5

Это функция, которую вы пытались написать (но будьте осторожны, она будет переполняться при большом количестве):

user> (defn num-digits [n]
        (if (= 0 n)
          0
          (inc (num-digits (quot n 10)))))

#'user/num-digits
user> (num-digits 10923)
5

Однако, это вызов:

user> (num-digits 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000)

158

Эта версия этой функции не создает стек:

user> (defn num-digits-tail-recursion 
        ([n count]
           (if (= 0 n)
             count
             (recur (quot n 10) (inc count))))
        ([n] (num-digits-tail-recursion n 0)))
#'user/num-digits-tail-recursion
user> (num-digits-tail-recursion 10923)
5

Все версии интересны по-своему. Хороший вопрос!

4 голосов
/ 22 января 2010

В Clojure нет оптимизации хвостового вызова. Вы должны использовать специальную форму recur.

например:.

(defn num-digits [n]
  (loop [n n
         cnt 0]
    (if (= 0 n)
      cnt
      (recur (quot n 10) (inc cnt)))))

Но в ответ на ваш второй вопрос: да, и вот как:

(defn num-digits [n] (count (str n)))
3 голосов
/ 21 января 2010

Clojure пытается «делать правильные вещи» с помощью числовых операций, и никогда не теряет точность . поэтому, когда вы говорите устройство 17/10, результатом будет дробь 17/10 (семнадцать десятых), а не 1. По умолчанию никакая информация не будет потеряна ни в одной из числовых операций. В таких случаях вы можете явно отбросить дополнительную точность с помощью (quote x 10) или вы можете привести результат к int (int (/ 17 10))

для второго вопроса вот небольшой взлом:

(count (str 257))

Хороший способ избежать взрыва стека с помощью рекурсии в Clojure - это использовать другие функции более высокого порядка вместо рекурсии.

(count (take-while pos? (iterate #(quot % 10) 257))))
3 голосов
/ 21 января 2010

Согласно этой странице , вы можете выполнить целочисленное деление в Clojure, используя quot:

(quot n 10)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...