如何最好地编写一个接受浮点数或双精度浮点数作为输入的函数?

3

假设我想要实现费米函数(逻辑曲线的最简单的例子),使得如果传入一个Float,它将返回一个Float,如果传入一个Double,它将返回一个Double。以下是我的代码:

e = 2.7182845904523536

fermiFunc :: (Floating a) => a -> a
fermiFunc x = let one = fromIntegral 1 in one/(one + e^(-x))

问题在于 ghc 说 e 是一个 Double 类型。定义变量 one 也有点复杂。我想到的另一个解决方案是仅为双精度数定义该函数:

e = 2.7182845904523536

fermiFuncDouble :: Double -> Double
fermiFuncDouble x = 1.0/(1.0 + e^(-x))

然后使用 Either:
fermiFunc :: (Floating a) => Either Float Double -> a
fermiFunc Right x = double2Float (fermiFuncDouble (float2Double x))
fermiFunc Left x = fermiFuncDouble x

然而,这并不是很令人兴奋,因为我可能会写一个单独的函数来处理Float类型的数据并调用fermiFuncDouble函数。有没有一种好的方式来编写适用于两种类型的函数呢?


1
最小的更改是给 e 一个多态类型签名,就像你为 fermiFunc 做的那样。根本不需要定义 one;只需使用 1 即可。但是这里建议更彻底的更改的答案也很好。 - Daniel Wagner
2个回答

8
假设您需要浮点数幂,那么使用(**)(^) 用于整数幂。将您的函数重写为使用(**)并让 GHC 推断类型,如下所示:
fermiFunc x = 1/(1 + e ** (-x))

并且

> :t fermiFunc
fermiFunc :: (Floating a) => a -> a

由于FloatDouble都有Floating实例,因此fermiFunc现在具有足够的多态性,可以与两者一起使用。

(注意:您可能需要为e声明一个多态类型,以避免单态限制,即e :: Floating a => a。)

通常,“如何编写适用于多个类型的函数?”的答案是:“编写通用于所有类型的函数。”(参数多态,例如map),“查找(或创建)一个或多个它们共享的类型类,并提供所需的行为。”(临时多态,例如show),或“创建一个新类型,该类型是这些类型之和。”(例如Either)。

后两种方法都有一些权衡。例如,类型类是开放的(您可以随时添加更多类型),而总和类型是封闭的(您必须修改定义以添加更多类型)。总和类型要求您知道正在处理哪种类型(因为它必须与构造函数匹配),而类型类允许您编写多态函数。

您可以在GHCi中使用:i列出实例和实例方法,这可能有助于您找到合适的类型类。


是的,然而 e**(-x) 被翻译为 exp ((-x) * log e),其中 log e 是一种非常低效的写法,等同于 1。(虽然这并不像定义 one = fromIntegral 1 那样低效,可以承认...) - leftaroundabout
1
没错,我很高兴你的回答纠正了这一点,但我认为我的回答的其余部分仍然有用。在我看来,对 e 的误用有些离题,真正的问题是关于特定多态性的。 - Rein Henrichs
fermiFunc = recip . succ . exp . negate 虽然很美。 - Rein Henrichs

8
不要在任何语言中写 e^x,因为那不是指数函数,而是幂函数。指数函数被称为 exp,它的定义实际上与幂运算无关 - 它被定义为 Taylor 级数或者作为满足普通微分方程 dd exp = exp 的唯一解,具体取决于你的喜好,其中边界条件为 exp 0 = 1。现在,对于任意有理数 n,我们都有 exp n ≡ (exp 1)n,这也激发了我们定义 ℝ 或 ℂ 中的数字的幂运算,除了 ℚ 以外,即为:

az := exp (z · ln a)

...但应该将 e 理解为只是写 exp() 的快捷方式。

因此,您不应该在某个地方定义 e 并尝试对其进行操作,而应该直接使用 exp

fermiFunc :: Floating a => a -> a
fermiFunc x = 1/(1 + exp (-x))

...或者确切地说

fermiFunc = recip . succ . exp . negate

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接