Что самый легкий путь состоит в том, чтобы проанализировать числа в clojure?

Я использовал Java для парсинга чисел, например.

(. Integer parseInt  numberString)

Существует ли больше clojuriffic пути, который обработал бы и целые числа и плавания, и возвратил бы clojure числа? Я особенно не волнуюсь по поводу производительности здесь, я просто хочу обработать набор разграниченных чисел пробела в файле и сделай что-то с ними самым простым возможным способом.

Таким образом, файл мог бы иметь строки как:

5  10  0.0002
4  12  0.003

И я хотел бы смочь преобразовать строки в векторы чисел.

54
задан Rob Lachlan 14 April 2010 в 07:54
поделиться

3 ответа

Для анализа чисел можно использовать программу чтения edn . Это дает вам то преимущество, что при необходимости вы получаете поплавки или Bignum.

user> (require '[clojure.edn :as edn])
nil
user> (edn/read-string "0.002")
0.0020

Если вам нужен один огромный вектор чисел, вы можете схитрить и сделать следующее:

user> (let [input "5  10  0.002\n4  12  0.003"]
        (read-string (str "[" input "]")))
[5 10 0.0020 4 12 0.0030]

Хотя это вроде хакерство. Или есть re-seq :

user> (let [input "5  10  0.002\n4  12  0.003"]
        (map read-string (re-seq #"[\d.]+" input)))
(5 10 0.0020 4 12 0.0030)

Или один вектор на строку:

user> (let [input "5  10  0.002\n4  12  0.003"]
        (for [line (line-seq (java.io.BufferedReader.
                              (java.io.StringReader. input)))]
             (vec (map read-string (re-seq #"[\d.]+" line)))))
([5 10 0.0020] [4 12 0.0030])

Я уверен, что есть другие способы.

64
ответ дан 7 November 2019 в 07:37
поделиться

Не уверен, что это «самый простой способ», но я подумал, что это было весело, так что ... С помощью хака отражения вы можете получить доступ только к части чтения чисел Clojure's Reader:

(let [m (.getDeclaredMethod clojure.lang.LispReader
                            "matchNumber"
                            (into-array [String]))]
  (.setAccessible m true)
  (defn parse-number [s]
    (.invoke m clojure.lang.LispReader (into-array [s]))))

Затем используйте примерно так:

user> (parse-number "123")
123
user> (parse-number "123.5")
123.5
user> (parse-number "123/2")
123/2
user> (class (parse-number "123"))
java.lang.Integer
user> (class (parse-number "123.5"))
java.lang.Double
user> (class (parse-number "123/2"))
clojure.lang.Ratio
user> (class (parse-number "123123451451245"))
java.lang.Long
user> (class (parse-number "123123451451245123514236146"))
java.math.BigInteger
user> (parse-number "0x12312345145124")
5120577133367588
user> (parse-number "12312345142as36146") ; note the "as" in the middle
nil

Обратите внимание, что это не вызывает обычное NumberFormatException , если что-то пойдет не так; вы можете добавить проверку на nil и бросить ее сами, если хотите.

Что касается производительности, давайте устроим ненаучный микробенчмарк (обе функции были «подогреты»; начальные запуски, как обычно, были медленнее):

user> (time (dotimes [_ 10000] (parse-number "1234123512435")))
"Elapsed time: 564.58196 msecs"
nil
user> (time (dotimes [_ 10000] (read-string "1234123512435")))
"Elapsed time: 561.425967 msecs"
nil

Очевидный отказ от ответственности: clojure.lang.LispReader.matchNumber является частным статическим методом clojure.lang.LispReader и может быть изменен или удален в любое время.

25
ответ дан 7 November 2019 в 07:37
поделиться

Если вы хотите быть в большей безопасности, вы можете использовать Float / parseFloat

user=> (map #(Float/parseFloat (% 0)) (re-seq #"\d+(\.\d+)?" "1 2.2 3.5"))
(1.0 2.2 3.5)
user=> 
25
ответ дан 7 November 2019 в 07:37
поделиться
Другие вопросы по тегам:

Похожие вопросы: