Clojure 可为空的双类型提示

Clojure 可为空的双类型提示,clojure,nullable,type-hinting,Clojure,Nullable,Type Hinting,我在写一个神经网络,节点的定义如下: (defrecord Node [^double input-sum ^double last-output]) 输入和是其输入的运行和;预激活上次输出是它的激活值 我最初的想法是创建一个初始节点,如(>node 0 nil)。我的理性是,在最后一次输出被激活之前,给它一个真实的值是没有任何意义的 不幸的是,创建上述节点会产生NPE;显然是因为它试图将nil转换为双精度: (->Node 0 nil) NullPointerException c

我在写一个神经网络,节点的定义如下:

(defrecord Node [^double input-sum ^double last-output])
输入和
是其输入的运行和;预激活<代码>上次输出是它的激活值

我最初的想法是创建一个初始节点,如
(>node 0 nil)
。我的理性是,在
最后一次输出被激活之前,给它一个真实的值是没有任何意义的

不幸的是,创建上述节点会产生NPE;显然是因为它试图将
nil
转换为双精度:

(->Node 0 nil)
NullPointerException   clojure.lang.RT.doubleCast (RT.java:1298)
如果我删除上次输出上的类型提示,它可以正常工作


我试着养成打字暗示的习惯。有没有办法在上次输出时提供类型提示,但同时指出
nil
是一个可接受的值?

^double
是Java原语double类型的类型提示。请尝试
^Double

^Double
是Java原语Double类型的类型提示。请尝试使用
^Double
如果确实需要使用基元类型键入提示,并且使用对象类型
Double
不是一个选项(与性能相关的原因类似),通常的做法(据我所知)是创建自己的自定义构造函数:

user> (defrecord Node [^double input-sum ^double last-output])
user.Node

user> (defn make-node [^Double input-sum ^Double last-output]
        (->Node (or input-sum 0) (or last-output 0)))
#'user/make-node

user> (make-node nil 0)
#user.Node{:input-sum 0.0, :last-output 0.0}

user> (make-node 10 nil)
#user.Node{:input-sum 10.0, :last-output 0.0}

user> (make-node 1 2)
#user.Node{:input-sum 1.0, :last-output 2.0}
这种方法适用于所有需要复杂逻辑来构建实体的情况

更新

如果您确实需要一个基本值以及区分值和“无值”的方法,您可以使用
NaN
来实现这一点:

user> (defn make-node [^Double input-sum ^Double last-output]
        (->Node (or input-sum Double/NaN) (or last-output Double/NaN)))
#'user/make-node

user> (make-node 1 nil)
#user.Node{:input-sum 1.0, :last-output NaN}

因此,它是值,也是
nil

的基本类似物。如果您确实需要使用基本类型键入提示,并且使用对象类型
Double
不是一个选项(与性能相关的原因类似),通常的做法(据我所知)是创建您自己的自定义构造函数:

user> (defrecord Node [^double input-sum ^double last-output])
user.Node

user> (defn make-node [^Double input-sum ^Double last-output]
        (->Node (or input-sum 0) (or last-output 0)))
#'user/make-node

user> (make-node nil 0)
#user.Node{:input-sum 0.0, :last-output 0.0}

user> (make-node 10 nil)
#user.Node{:input-sum 10.0, :last-output 0.0}

user> (make-node 1 2)
#user.Node{:input-sum 1.0, :last-output 2.0}
这种方法适用于所有需要复杂逻辑来构建实体的情况

更新

如果您确实需要一个基本值以及区分值和“无值”的方法,您可以使用
NaN
来实现这一点:

user> (defn make-node [^Double input-sum ^Double last-output]
        (->Node (or input-sum Double/NaN) (or last-output Double/NaN)))
#'user/make-node

user> (make-node 1 nil)
#user.Node{:input-sum 1.0, :last-output NaN}

因此,它是值,也是与
nil

的原始类似物,但这不是他所要求的。。。将
nil
转换为
0.0
与“表明
nil
是一个可接受的值”不同。对,它不是。但是,如果您有一个基元类型提示,如果您不想每次使用
->Node
时都检查所有参数,那么这是您唯一的选择。不过,他在问题的任何地方都没有说“基元”。此外,他说“在
最后一次输出被激活之前给它一个实际值没有任何意义”,这似乎意味着他真正想要的是一个实际的
nil
值,而不是
0.0
(从数学上讲,这是“真实的”)。但是使用类型提示来“指示”某个东西(如果你的意思是“记录”)看起来真奇怪。还有其他用于类型注释的工具,如
schema
spec
core.typed
OK,cool。对我来说有点像C语言,但在性能确实需要原语的情况下,它是有效的但这不是他想要的。。。将
nil
转换为
0.0
与“表明
nil
是一个可接受的值”不同。对,它不是。但是,如果您有一个基元类型提示,如果您不想每次使用
->Node
时都检查所有参数,那么这是您唯一的选择。不过,他在问题的任何地方都没有说“基元”。此外,他说“在
最后一次输出被激活之前给它一个实际值没有任何意义”,这似乎意味着他真正想要的是一个实际的
nil
值,而不是
0.0
(从数学上讲,这是“真实的”)。但是使用类型提示来“指示”某个东西(如果你的意思是“记录”)看起来真奇怪。还有其他用于类型注释的工具,如
schema
spec
core.typed
OK,cool。对我来说有点像C语言,但在性能确实需要原语的情况下,它是有效的你能更详细地解释一下你为什么“试图养成打字暗示的习惯”吗?这样做是一种过早的优化,我相信你已经听过很多次了,这是万恶之源。@SamEstep我认为在我定义类型时,从一开始就进行优化会更容易,而不是从头开始,但你不知道是否真的需要这样做。如上所述,类型提示纯粹是针对“代码的性能关键区域”的性能优化,因此您所做的只是一个教科书上的过早优化示例。如果您还没有读过的话,我强烈建议您阅读的答案。@SamEstep OK谢谢。也许我只需要启用反射警告并处理它,然后查看上面提到的文件包。在您已经编写代码、对其进行基准测试并发现反射是一个瓶颈之后,添加类型提示没有什么错。问题在于,当您在知道需要它们之前就使用它们,从而妨碍了您的设计。至于文件,我个人建议;它在标准库中,因此它将是未来最受支持的选项,而且它有非常坚实的设计和许多非常棒的功能。你能更详细地解释一下为什么你“试图养成打字暗示的习惯”吗?这样做是一种过早的优化,我相信你已经听过很多次了,这是万恶之源。@SamEstep我认为在我定义类型时,从一开始就进行优化会更容易,而不是从头开始,但你不知道是否真的需要这样做。如上所述,类型提示纯粹是针对“perform”的性能优化