Ebe*_*ley 3 floating-point haskell function
假设我想实现Fermi函数(逻辑曲线的最简单的例子),这样如果它被传递,Float它返回一个Float,如果它被传递,Double它返回一个Double.这是我得到的:
e = 2.7182845904523536
fermiFunc :: (Floating a) => a -> a
fermiFunc x = let one = fromIntegral 1 in one/(one + e^(-x))
Run Code Online (Sandbox Code Playgroud)
问题是ghc说的e是Double.定义变量one也有点粗糙.我想到的另一个解决方案就是定义双精度函数:
e = 2.7182845904523536
fermiFuncDouble :: Double -> Double
fermiFuncDouble x = 1.0/(1.0 + e^(-x))
Run Code Online (Sandbox Code Playgroud)
然后使用Either:
fermiFunc :: (Floating a) => Either Float Double -> a
fermiFunc Right x = double2Float (fermiFuncDouble (float2Double x))
fermiFunc Left x = fermiFuncDouble x
Run Code Online (Sandbox Code Playgroud)
这不是很令人兴奋,因为我可能只是为Float处理转换和调用的情况编写了一个单独的函数fermiFuncDouble.有没有一种很好的方法为这两种类型编写函数?
假设您想要浮点指数,那就是(**).(^)是积分指数.重写你的函数使用(**)并让GHC推断出类型给出:
fermiFunc x = 1/(1 + e ** (-x))
Run Code Online (Sandbox Code Playgroud)
和
> :t fermiFunc
fermiFunc :: (Floating a) => a -> a
Run Code Online (Sandbox Code Playgroud)
由于Float并且Double两者都有Floating实例,fermiFunc现在具有足够的多态性以与两者一起工作.
(注意:您可能需要声明一个多态类型e以绕过单态限制,即e :: Floating a => a.)
一般来说,"如何编写适用于多种类型的函数?"的答案.是"写它,以便它适用于所有类型." (参数多态,如map),"查找(或创建)他们共享的一个或多个类型类,提供您需要的行为." (ad hoc polymorphism,like show),或"创建一个新类型,即这些类型的总和." (像Either).
后两者有一些权衡.例如,类型类是打开的(您可以随时添加更多),而总和类型是关闭的(您必须修改定义以添加更多类型).Sum类型要求您知道要处理的类型(因为它必须与构造函数匹配),而类型类允许您编写多态函数.
您可以:i在GHCi 中使用列出实例和列出实例方法,这可能有助于您找到合适的类型类.
不要e^x用任何语言写作.这不是指数函数,而是幂函数.
指数函数被调用exp,其定义实际上与幂操作无关 - 它根据您的喜好定义为泰勒级数或作为常微分方程的唯一解,d/d exp = exp与边界条件EXP 0 = 1.现在,它恰巧,对于任何合理的ñ,我们有EXP ñ ≡(EXP 1)ñ和激励也限定了功率操作用于ℝ或ℂ除了ℚ号码,即如
a z:= exp(z ·ln a)
......但是e 应该被理解为只是编写exp()本身的捷径.
因此,不应该定义e某个地方并试图利用它的某些功能,而应该使用exp原样.
fermiFunc :: Floating a => a -> a
fermiFunc x = 1/(1 + exp (-x))
Run Code Online (Sandbox Code Playgroud)
......或者确实如此
fermiFunc = recip . succ . exp . negate
Run Code Online (Sandbox Code Playgroud)