在 ClojureScript 程序中使用 Cuerdas 解析十进制数

Parse decimal number using Cuerdas in a ClojureScript program

我正在使用 Cuerdas library 解析浮点数的 ClojureScript 程序。有时输入来自 用户缺少组成部分,例如 .1 对应 0.1。我需要处理这个。 但是当比较解析的数字时,我 运行 进入 st运行ge 结果:

(= 0.1 (parse-number "0.1"))
;; => true

(= .1 (parse-number "0.1"))
;; => true

(= 0.1 (parse-number ".1"))
;; => false

(= .1 (parse-number ".1"))
;; => false

上面最后两个结果让我很惊讶。比较文字数字时我 得到预期的结果:

(= 0 .0)
;; => true

当我只解析没有整数的十进制数时,st运行ge 行为也可见 部分,如下所示:

(parse-number ".1")
;; => .1

.1
;; => 0.1

我发现我可以使用 js/parseFloat 得到我想要的东西,但我很好奇 使用 Cuerdas 时出现这种行为的原因是什么?这个 .1 值是多少 它与 0.1 有何不同?

parse-number 来源如下所示:

(defn parse-number
  "General purpose function for parse number like
  string to number. It works with both integers
  and floats."
  [s]
  (if (nil? s)
    #?(:cljs js/NaN :clj Double/NaN)
    (if (numeric? s)
      (edn/read-string s)
      #?(:cljs js/NaN :clj Double/NaN))))

".1""0.1" 都被认为是 numeric?(通过正则表达式检查在 cuerdas 中实现):

cljs.user=> (def re #"^[+-]?([0-9]*\.?[0-9]+|[0-9]+\.?[0-9]*)([eE][+-]?[0-9]+)?$")
#'cljs.user/re
cljs.user=> (boolean (re-matches re "0.1"))
true
cljs.user=> (boolean (re-matches re ".1"))
true

所以它们将被 cljs.reader/read-string 阅读。

读取字符串 "0.1" 时,return 类型是 js/Number,但 ".1"cljs.core/Symbol:

类型
cljs.user=> (cljs.reader/read-string ".1")
.1
cljs.user=> (cljs.reader/read-string "0.1")
0.1
cljs.user=> (type (cljs.reader/read-string "0.1"))
#object[Number]
cljs.user=> (type (cljs.reader/read-string ".1"))
cljs.core/Symbol

因此,虽然它看起来正确地解析了 .1,但实际上它把它变成了一个符号。符号 .1 不等于数字 .1.

请注意,parse-number 是 cuerdas 的 no longer available in newer versions,因为它“是一个字符串处理库,而不是数字解析库”。

我有一些 numeric parsing functions 适用于 CLJ 和 CLJS。从单元测试中,我们有

headers:

(ns tst.tupelo.parse
  ;---------------------------------------------------------------------------------------------------
  ;   https://code.thheller.com/blog/shadow-cljs/2019/10/12/clojurescript-macros.html
  ;   http://blog.fikesfarm.com/posts/2015-12-18-clojurescript-macro-tower-and-loop.html
  #?(:cljs (:require-macros
             [tupelo.misc]
             [tupelo.testy]))
  (:require
    [clojure.test] ; sometimes this is required - not sure why
    [tupelo.parse :as tpar]
    [tupelo.misc :as misc]
    [tupelo.core :as t :refer [spy spyx spyxx spyx-pretty]]
    [tupelo.testy :refer [deftest testing is dotest isnt is= isnt= is-set= is-nonblank=
                          throws? throws-not? define-fixture]])
  #?(:clj (:import [java.lang Math]))
  )

解析整数:

#?(:cljs
   (do
     (dotest
       (is= 0 (tpar/parse-int "0"))
       (is= 15 (tpar/parse-int "15"))
       (is= -5 (tpar/parse-int "-5"))
       (is= 99999 (tpar/parse-int "99999"))

       (throws? (tpar/parse-int ""))
       (throws? (tpar/parse-int "05"))
       (throws? (tpar/parse-int "123xxx"))
       (throws? (tpar/parse-int "12x3"))
       (throws? (tpar/parse-int "12.3"))
       (throws? (tpar/parse-int "xxx123")))

解析浮点数

     (dotest
       (is= 0 (tpar/parse-float "0"))
       (is= 0 (tpar/parse-float "0.0"))
       (is= 12.345 (tpar/parse-float "12.345"))
       (is= -5.1 (tpar/parse-float "-5.1"))
       (is= 42 (tpar/parse-float "42.0"))
       (is= 42 (tpar/parse-float "42"))
       (is= 123.45 (tpar/parse-float "1.2345e2"))

       (throws? (tpar/parse-float ""))
       (throws? (tpar/parse-float "xxx1.23"))
       (throws? (tpar/parse-float "1.23xxx"))
       (throws? (tpar/parse-float "1.2xx34")))
     ))