繁体   English   中英

如何在 Haskell [T = 1/x + x/2 + 3/x +x/4 +...] 中为此创建尾递归

[英]How to create a tail recursion for this in Haskell [T = 1/x + x/2 + 3/x +x/4 + ...]

我试过这个

    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约束nIntegral类型,但随后您使用x/n并且由于(/) function 具有签名(/):: Fractional a => a -> a -> a ,这意味着xn具有相同的Fractional类型 数字类型既是Integral又是Fractional是没有意义的。

您可以使用fromIntegraln转换为Fractional类型:

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)

function 的另一个问题是没有停止条件 您需要添加一个额外的子句:

recursion :: (Integral a, Floating b) => a -> b -> b
recursion n x
  | n <= 0 = 0
  | mod n 2 == 0 = x/fromIntegral n + (recursion (n-1) x)**(-1) 
  | otherwise = fromIntegral n/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 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

顺便说一句:这个显然不会在数学上收敛,所以取部分总和似乎毫无意义 - 但嘿无论如何

您看到的错误是因为编译器无法确定 function 的参数类型。 向 function 添加类型约束可以解决这个问题:

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)

现在 function 编译但不会终止,因为没有检查终端条件( 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)

现在 function 将以答案终止,但答案与问题标题中所述的公式不匹配。 要解决此问题,请删除 **(-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)

现在 function 返回正确的值。 以下主程序验证是否是这种情况:

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

function 返回正确的值,但不是尾递归的。 作为使其尾递归的第一步,将其简化为单个递归调用。 要做到这一点,请注意公式中的项成对出现,并将它们与n-2递归组合在一起。 function 现在仅适用于偶数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)

function 仍然不是尾递归的,因为在递归调用之后进行了额外的处理(添加)。 解决这个问题的一种方法是引入一个累加器参数来保存不完整的值。

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)

作为最后一步,可以引入包装器 function 来处理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项开始,go 回到第0 ,但始终使用x作为起始值,而它取决于n是偶数还是奇数。 我们通过将实际工作移至内部“工人”function 并固定迭代的起始值来解决此类问题。

第二个问题是它不是尾递归的。 我们通过向内部“工人”function 引入累积参数来解决此类问题。 一个工具解决两个问题!

-- 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)

现在它是正确的,并且可以根据需要进行尾递归。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM