假设我想实现Fermi函数(逻辑曲线的最简单示例),这样如果它传递了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
。有没有一种很好的方法来为这两种类型编写函数?
答案 0 :(得分:8)
假设您想要浮点指数,那就是(**)
。 (^)
是积分指数。重写你的函数以使用(**)
并让GHC推断出类型给出:
fermiFunc x = 1/(1 + e ** (-x))
和
> :t fermiFunc
fermiFunc :: (Floating a) => a -> a
由于Float
和Double
都有Floating
个实例,fermiFunc
现在具有足够的多态性,可以同时使用这两个实例。
(注意:您可能需要声明e
的多态类型以绕过单态限制,即e :: Floating a => a
。)
一般来说,答案是"如何编写适用于多种类型的函数?"是"写它以便它适用于所有类型。" (参数多态,如map
),"查找(或创建)他们共享的一个或多个类型,提供您需要的行为。" (ad hoc多态,如show
),或"创建一个新类型,即这些类型的总和。" (如Either
)。
后两者有一些权衡。例如,类型类是打开的(您可以随时添加更多),而总和类型是关闭的(您必须修改定义以添加更多类型)。求和类型要求您知道要处理的类型(因为它必须与构造函数匹配),而类型类允许您编写多态函数。
您可以在GHCi中使用:i
列出实例并列出实例方法,这可能有助于您找到合适的类型类。
答案 1 :(得分:8)
不要用任何语言写global $post;
。这不是指数函数,而是幂函数。
指数函数被称为e^x
,其定义实际上与动力操作无关 - 它根据您的喜好定义为泰勒级数或the unique solution至普通微分方程 d / d exp = exp,边界条件exp 0 = 1.现在,对于任何理性 n ,我们都会发生这种情况exp n ≡(exp 1) n 这也激励着为ℚ或ℂ中的数字定义幂运算,即为< / p>
a z := exp( z ·ln a )< / p>
...但 e 应该被理解为只是编写exp()本身的快捷方式。
因此,不应在某处定义exp
并尝试利用它的某些功能,而应该使用exp
。
e
......或者确实
fermiFunc :: Floating a => a -> a
fermiFunc x = 1/(1 + exp (-x))