我试过这个
recursion n x = if mod n 2 == 0
then x/n + (recursion (n-1) x)**(-1)
else n/x + (recursion (n-1) x)**(-1)
但是有一个问题
Ambiguous type variable ‘a0’ arising from a use of ‘print’
prevents the constraint ‘(Show a0)’ from being solved.
Probable fix: use a type annotation to specify what ‘a0’ should be.
These potential instances exist:
instance Show Ordering -- Defined in ‘GHC.Show’
instance Show Integer -- Defined in ‘GHC.Show’
instance Show a => Show (Maybe a) -- Defined in ‘GHC.Show’
...plus 22 others
...plus 18 instances involving out-of-scope types
(use -fprint-potential-instances to see them all)
• In a stmt of an interactive GHCi command: print it
如何解决这个问题,我的代码有什么问题?
mod n 2 == 0
约束n
是Integral
类型,但是您随后使用x/n
并且由于(/)
函数具有签名(/) :: Fractional a => a -> a -> a
,这意味着x
和n
具有相同的Fractional
类型。数字类型既Integral
又Fractional
是没有意义的。
您可以使用fromIntegral
将n
转换为Fractional
类型:
recursion :: (Integral a, Floating b) => a -> b -> b
recursion n x = if mod n 2 == 0
then x/fromIntegraln + (recursion (n-1) x)**(-1)
elsefromIntegraln/x + (recursion (n-1) x)**(-1)
函数的另一个问题是没有停止条件。您需要添加一个额外的子句:
recursion :: (Integral a, Floating b) => a -> b -> b
recursion n x
|n <= 0 = 0
| mod n 2 == 0 = x/fromIntegraln + (recursion (n-1) x)**(-1)
| otherwise =fromIntegraln/x + (recursion (n-1) x)**(-1)
然后,这将产生例如:
Prelude> recursion 10 2
0.3481658094621751
其他答案已经解释了这里的错误。解决这个问题的一个很好的Haskell方法如下:
import Data.List (scanl')
getNthApprox n x = approximations !! n where
approximations = scanl' (+) 0 $ fmap term [1..]
term n = if n `mod` 2 == 0
then x / fromIntegral n
else fromIntegral n / x
事实证明,由于懒惰的魔力,getNthApprox
具有与尾递归相同的性能特征。这是因为scanl' (+) 0 $ fmap term [1..]
的元素仅在计算approximations !! n
时需要时才构造。
可能不是答案,但这个答案与标题更接近:
tSumToN :: (Enum a, Fractional a) => Int -> a -> a
tSumToN n = sum . take n . tSeq
tSeq :: (Enum a, Fractional a) => a -> [a]
tSeq x =
interleave odds evens
where
odds = [ o / x | o <- [1,3..]]
evens = [ x / e | e <- [2,4..]]
interleave [] _ = []
interleave (y:ys) zs = y:interleave zs ys
example :: Double
example = tSumToN 4 1.1
顺便说一句:这个显然在数学上没有收敛,所以取部分和似乎毫无意义 - 但嘿,无论什么
您看到的错误是因为编译器无法找出函数的参数类型。 向函数添加类型约束可以处理此问题:
recursion :: (Integral a, Floating b) => a -> b -> b
recursion n x = if mod n 2 == 0
then x/fromIntegral n + (recursion (n-1) x)**(-1)
else fromIntegral n/x + (recursion (n-1) x)**(-1)
现在函数编译但不会终止,因为没有检查终端条件 (n==0
)。 要解决此问题,请添加检查。
recursion :: (Integral a, Floating b) => a -> b -> b
recursion n x | n == 0 = 0.0
| mod n 2 == 0 = x/fromIntegral n + (recursion (n-1) x)**(-1)
| otherwise = fromIntegral n/x + (recursion (n-1) x)**(-1)
现在,函数将以答案终止,但答案与问题标题中所述的公式不匹配。 要解决此问题,请删除 **(-1) 。
recursion :: (Integral a, Floating b) => a -> b -> b
recursion n x | n == 0 = 0.0
| mod n 2 == 0 = x/fromIntegral n + (recursion (n-1) x)
| otherwise = fromIntegral n/x + (recursion (n-1) x)
现在,该函数返回正确的值。 以下主程序验证是否为这种情况:
main :: IO ()
main = do
print $ recursion 1 1.0
print $ 1/1.0
print $ recursion 2 1.0
print $ 1/1.0 + 1.0/2
print $ recursion 3 1.0
print $ 1/1.0 + 1.0/2 + 3/1.0
print $ recursion 4 1.0
print $ 1/1.0 + 1.0/2 + 3/1.0 + 1.0/4
该函数返回正确的值,但不是尾递归的。 作为使其成为尾递归的第一步,请将其简化为单个递归调用。 为此,请注意公式中的项成对出现,并将它们与n-2
递归一起分组。 该功能现在仅适用于n
,但以后可以修补。
recursion :: (Integral a, Floating b) => a -> b -> b
recursion n x | n == 0 = 0.0
| otherwise = fromIntegral (n-1)/x + x/fromIntegral n + (recursion (n-2) x)
该函数仍然不是尾递归的,因为在递归调用之后会完成额外的处理(添加)。 解决此问题的一种方法是引入累加器参数来保存不完整的值。
recursion :: (Integral a, Floating b) => a -> b -> b -> b
recursion n x acc | n == 0 = acc
| otherwise = recursion (n-2) x (fromIntegral (n-1)/x + x/fromIntegral n + acc)
作为最后一步,可以引入包装器函数来处理n
的奇数值并隐藏累加器参数。 使用上述测试代码的适当修改版本进行验证。
no_recursion :: (Integral a, Floating b) => a -> b -> b
no_recursion n x = if mod n 2 == 0
then recursion n x 0.0
else fromIntegral n / x + recursion (n-1) x 0.0
你的意思显然是
recursion n x = if snd (properFraction (n / 2)) > 0 -- isOdd
then x/n + recursion (n-1) (x**(-1))
else n/x + recursion (n-1) (x**(-1))
但这里有两个问题。首先是正确的,因为你从第一项开始n
然后回到第0
项,但始终使用x
作为起始值,而它根据n
是偶数还是奇数而有所不同。我们通过将实际工作移动到内部的"worker"函数中并修复迭代的起始值来解决此类问题。
第二个问题是它不是尾递归的。我们通过向内部"worker"函数引入累积参数来解决此类问题。一个工具解决两个问题!
-- T = 1/x + x/2 + 3/x + x/4 + ...
recursion :: RealFrac a => a -> a -> a
recursion n x = if snd (properFraction (n / 2)) > 0 -- n is odd
then goOdd n 0
else goEven n 0
where
goOdd n acc = goEven (n-1) (acc + n/x)
goEven n acc
| n <= 0 = acc
| otherwise = goOdd (n-1) (acc + x/n)
现在它是正确的,并且随心所欲地尾递归。