frequencies
的结果用于包含NaN
的序列时是错误的,例如:
=> (frequencies [Double/NaN Double/NaN])
{NaN 1, NaN 1}
而不是预期的{NaN 2}
。
此外,运行时间从预期/平均O(n)
恶化到最坏情况的O(n^2)
,例如
=> (def v3 (vec (repeatedly 1e3 #(Double/NaN))))
=> (def r (time (frequencies v3)))
"Elapsed time: 36.081751 msecs"
...
=> (def v3 (vec (repeatedly 1e3 #(Double/NaN))))
=> (def r (time (frequencies v3)))
"Elapsed time: 3358.490101 msecs"
...
即10倍的元素需要100倍的更长的运行时间。
当序列中有O(n)
个时,如何用(预期/平均)运行NaN
来计算频率?
作为旁注:
=> (frequencies (repeat 1e3 Double/NaN))
{NaN 1000}
产生预期结果,可能是因为序列中的所有元素都是同一对象的引用。
答案 0 :(得分:4)
NaN在许多编程语言中都很奇怪,部分原因是IEEE 754浮点数标准定义了NaN不应该等于任何东西,甚至不等于它。导致您看到的大多数怪异行为的是“甚至不是自身”部分。如果您感到好奇,请在此处查看更多信息:https://github.com/jafingerhut/batman
下面的示例功能可能适合您的需求。它在返回的地图中使用:nan-kw指示找到了多少个NaN。如果用## NaN替换:nan-kw,则由于## NaN的怪异,返回的映射的缺点是您无法使用(获取频率-ret-value ## NaN)来找到计数。
(defn frequencies-maybe-nans [s]
(let [separate-nans (group-by #(and (double? %) (Double/isNaN %)) s)
num-nans (count (separate-nans true))]
(merge (frequencies (separate-nans false))
(when-not (zero? num-nans)
{:nan-kw num-nans}))))
(def freqs (frequencies-maybe-nans [1 2 ##NaN 5 5]))
freqs
(get freqs 2)
(get freqs :nan-kw)
答案 1 :(得分:3)
JVM上NaN
值的一些背景:https://www.baeldung.com/java-not-a-number
您可以通过在计算频率时临时编码NaN
值来解决此问题:
(ns tst.demo.core
(:use tupelo.core
tupelo.test))
(defn is-NaN? [x] (.isNaN x))
(defn nan-encode
[arg]
(if (is-NaN? arg)
::nan
arg))
(defn nan-decode
[arg]
(if (= ::nan arg)
Double/NaN
arg))
(defn freq-nan
[coll]
(it-> coll
(mapv nan-encode it)
(frequencies it)
(map-keys it nan-decode)))
(dotest
(let [x [1.0 2.0 2.0 Double/NaN Double/NaN Double/NaN]]
(is= (spyx (freq-nan x)) {1.0 1,
2.0 2,
##NaN 3})))
结果:
-------------------------------
Clojure 1.10.1 Java 13
-------------------------------
Testing tst.demo.core
(freq-nan x) => {1.0 1, 2.0 2, ##NaN 3}
FAIL in (dotest-line-25) (core.clj:27)
expected: (clojure.core/= (spyx (freq-nan x)) {1.0 1, 2.0 2, ##NaN 3})
actual: (not (clojure.core/= {1.0 1, 2.0 2, ##NaN 3} {1.0 1, 2.0 2, ##NaN 3}))
请注意,即使计算并打印出正确的结果,单元测试仍然会失败,因为NaN
从不等于任何东西,甚至不等于任何东西。如果要通过单元测试,则需要像以下这样保留在占位符::nan
中:
(defn freq-nan
[coll]
(it-> coll
(mapv nan-encode it)
(frequencies it)
))
(dotest
(let [x [1.0 2.0 2.0 Double/NaN Double/NaN Double/NaN]]
(is= (spyx (freq-nan x)) {1.0 1,
2.0 2,
::nan 3})))