试图在haskell中将列表相乘

时间:2018-04-13 09:34:09

标签: haskell

我一直在研究Haskell,因为我的老师强迫我们去。 作为练习,我们将尝试使用其Maclaurin系列编写一个近似sin(x)函数的解决方案,直到第30个学期。

我想到了使用1和-1的交替序列的列表,x被提升为奇数,而奇数的阶乘。然后,我将乘以前两个列表并除以最后一个。到现在为止我只写了这个:

oddl = [1,3..]
powerl x = map (x^) oddl
factorial 0 = 1
factorial x = factorial (x - 1) * x
factl = map factorial oddl
alterl = scanl (*) (-1) [-1,-1..]
sined x = zipWith (*) (powerl x) alterl
sinex x = zipWith (/) sined factl
sinf x = foldl (+) (take 30 (sinex x))

然后在ghci,我会输入sinf 3.14但在我加载之后输入它之前我得到了这个:

exert.hs:31:8:
    No instance for (Enum t0)
      arising from the arithmetic sequence ‘1, 3 .. ’
    The type variable ‘t0’ is ambiguous
    Relevant bindings include oddl :: [t0] (bound at exert.hs:31:1)
    Note: there are several potential instances:
      instance forall (k :: BOX) (s :: k). Enum (Data.Proxy.Proxy s)
        -- Defined in ‘Data.Proxy’
      instance Integral a => Enum (GHC.Real.Ratio a)
        -- Defined in ‘GHC.Real’
      instance Enum Ordering -- Defined in ‘GHC.Enum’
      ...plus 8 others
    In the expression: [1, 3 .. ]
    In an equation for ‘oddl’: oddl = [1, 3 .. ]

exert.hs:31:9:
    No instance for (Num t0) arising from the literal ‘1’
    The type variable ‘t0’ is ambiguous
    Relevant bindings include oddl :: [t0] (bound at exert.hs:31:1)
    Note: there are several potential instances:
      instance Integral a => Num (GHC.Real.Ratio a)
        -- Defined in ‘GHC.Real’
      instance Num Integer -- Defined in ‘GHC.Num’
      instance Num Double -- Defined in ‘GHC.Float’
      ...plus three others
    In the expression: 1
    In the expression: [1, 3 .. ]
    In an equation for ‘oddl’: oddl = [1, 3 .. ]

exert.hs:32:18:
    Could not deduce (Integral t0) arising from a use of ‘^’
    from the context (Num b)
      bound by the inferred type of powerl :: Num b => b -> [b]
      at exert.hs:32:1-24
    The type variable ‘t0’ is ambiguous
    Note: there are several potential instances:
      instance Integral Integer -- Defined in ‘GHC.Real’
      instance Integral Int -- Defined in ‘GHC.Real’
      instance Integral Word -- Defined in ‘GHC.Real’
    In the first argument of ‘map’, namely ‘(x ^)’
    In the expression: map (x ^) oddl
    In an equation for ‘powerl’: powerl x = map (x ^) oddl

exert.hs:35:13:
    No instance for (Eq t0) arising from a use of ‘factorial’
    The type variable ‘t0’ is ambiguous
    Relevant bindings include factl :: [t0] (bound at exert.hs:35:1)
    Note: there are several potential instances:
      instance (Eq a, Eq b) => Eq (Either a b)
        -- Defined in ‘Data.Either’
      instance forall (k :: BOX) (s :: k). Eq (Data.Proxy.Proxy s)
        -- Defined in ‘Data.Proxy’
      instance (GHC.Arr.Ix i, Eq e) => Eq (GHC.Arr.Array i e)
        -- Defined in ‘GHC.Arr’
      ...plus 28 others
    In the first argument of ‘map’, namely ‘factorial’
    In the expression: map factorial oddl
    In an equation for ‘factl’: factl = map factorial oddl

exert.hs:38:23:
    Couldn't match expected type ‘[t0]’
                with actual type ‘Integer -> [Integer]’
    Relevant bindings include
      sinex :: t -> [t0] (bound at exert.hs:38:1)
    Probable cause: ‘sined’ is applied to too few arguments
    In the second argument of ‘zipWith’, namely ‘sined’
    In the expression: zipWith (/) sined factl

exert.hs:39:16:
    Could not deduce (Num [t0]) arising from a use of ‘+’
    from the context (Foldable t)
      bound by the inferred type of
               sinf :: Foldable t => t1 -> t [t0] -> [t0]
      at exert.hs:39:1-38
    The type variable ‘t0’ is ambiguous
    Relevant bindings include
      sinf :: t1 -> t [t0] -> [t0] (bound at exert.hs:39:1)
    In the first argument of ‘foldl’, namely ‘(+)’
    In the expression: foldl (+) (take 30 (sinex x))
    In an equation for ‘sinf’: sinf x = foldl (+) (take 30 (sinex x))
Failed, modules loaded: none.

这么多消息。哇。 我对我做错了什么都没有任何线索,甚至不知道从哪里开始阅读这条消息。 愿有人对此有所了解吗?实际上作为初学者,我很好奇Haskell有什么不对,以及它阻止它工作的原因。

编辑:添加类型签名和缺少参数后。

oddl :: [Integer]
oddl = [1,3..]
powerl :: Integer -> [Integer]
powerl x = map (x^) oddl
factorial 0 = 1
factorial x = factorial (x - 1) * x
factl :: [Integer]
factl = map factorial oddl
alterl = scanl (*) (-1) [-1,-1..]
sined x = zipWith (*) (powerl x) alterl
sinex x = zipWith (/) (sined x) factl
sinf x = foldl (+) (take 30 (sinex x))

现在我得到一个更短的错误:

exert.hs:41:19:
    No instance for (Fractional Integer) arising from a use of ‘/’
    In the first argument of ‘zipWith’, namely ‘(/)’
    In the expression: zipWith (/) (sined x) factl
    In an equation for ‘sinex’: sinex x = zipWith (/) (sined x) factl

exert.hs:42:16:
    Could not deduce (Num [Integer]) arising from a use of ‘+’
    from the context (Foldable t)
      bound by the inferred type of
               sinf :: Foldable t => Integer -> t [Integer] -> [Integer]
      at exert.hs:42:1-38
    In the first argument of ‘foldl’, namely ‘(+)’
    In the expression: foldl (+) (take 30 (sinex x))
    In an equation for ‘sinf’: sinf x = foldl (+) (take 30 (sinex x))

3 个答案:

答案 0 :(得分:3)

Haskell中的数字与类型和类型推断有很大关系,因此对于初学者来说它们可能很棘手。您不允许使用/运算符对整数进行除法(`div`用于截断整数除法)。使常量为整数的原因也可能相当棘手,因为它取决于它的使用方式(稍后!) 1 。但是你需要知道的主要事情是fromIntegral函数,它从一个整数转换为任何其他数字类型。

ghci> let x = 10 :: Integer
ghci> x / 5
<interactive>:6:1: error:
    • No instance for (Fractional Integer) arising from a use of ‘/’
    • In the expression: x / 5
      In an equation for ‘it’: it = x / 5
ghci> fromIntegral x / 5
2.0

@letrtroundabout 的评论是绝对正确的!类型推断是可爱的,但如果你让它狂奔,它可能会非常混乱;用类型签名控制它。

1 如果您有兴趣,可能会出现问题。也许它有助于理解类型和数字如何相互作用。

(^)采用整数(技术上任何&#34; Integral&#34;类型,默认为Integer,如果没有其他可做出决定的话)论证,所以我们从

知道
powerl x = map (x^) oddl

oddl必须是整数列表。 (*)返回与其参数相同的类型(必须是相同的类型),因此我们从

中知道
factorial x = factorial (x - 1) * x

factorial返回与其参数相同的类型,因此

factl = map factorial oddl

也必须是整数列表。然后我们有

sinex x = zipWith (/) sined factl

现在将整数作为(/)的参数,这是不合法的。

答案 1 :(得分:2)

编辑完成后,查看第一个错误:

No instance for (Fractional Integer) arising from a use of ‘/’

(/)的类型是:

(/) :: Fractional a => a -> a -> a

因此它需要两个a类型的值,它必须是Fractional,并返回相同类型a的结果。 Integer不是Fractional - 有整数除法函数divquot(以及它们的余数对应,modrem),但那是不是你想要的,因为你想要一个小数结果。解决方案是使用Double将整数参数转换为小数类型,例如fromIntegral

fromIntegral :: (Num b, Integral a) => a -> b

fromIntegral (x :: Integer) :: Double  -- or ‘… :: Float’

例如,您可以编写sum xs / fromIntegral (length xs)来获取Double列表的平均值。

第二个错误:

Could not deduce (Num [Integer]) arising from a use of ‘+’

来自这个表达:

foldl (+) (take 30 (sinex x))

foldl有三个参数:reduce函数,起始值和输入容器;您尝试将容器作为起始值传递,因此foldl尝试在未定义的整数列表上调用+。您可能需要foldl (+) 0sum

答案 2 :(得分:1)

这是一种更合理的方式来写这一切:

sinf :: Double -> Double
sinf x = sum $ take 30
           [ sign * power / fact
           | (sign, power, fact) <- zip3
               (iterate negate 1)
               (oddEntries $ iterate (*x) 1)
               (oddEntries factorials) ]
 where factorials :: [Double]
       factorials = scanl (*) 1 [1..]

oddEntries :: [a] -> [a]
oddEntries (_:x:xs) = x : oddEntries xs
oddEntries _ = []

请注意,我不需要任何fromIntegral,因为我将阶乘列表声明为Double。这当然不准确,但在这个应用程序中无关紧要(错误无论如何都被系列的截断所控制),对于大数字而言,它比从Integer转换更有效。

我还避免使用^运算符,这种情况非常浪费:乘法的中间结果无论如何都需要Maclaurin系列,所以迭代乘法也更好!

顺便提一下,您可以省略本地绑定,只需将其全部内联写入:

sinf :: Double -> Double
sinf x = sum $ take 30
           [ sign * power / fact
           | (sign, power, fact) <- zip3
               (iterate negate 1)
               (oddEntries $ iterate (*x) 1)
               (oddEntries $ scanl (*) 1 [1..]) ]

此处,typechecker会自动执行正确的操作,并为因子列表(以及符号列表)选择类型Double,因为上下文需要Double。 (但为了便于阅读,最好还是给它一个本地名称和签名。)

$ ghci wtmpf-file3770.hs 
GHCi, version 8.2.1: http://www.haskell.org/ghc/  :? for help
Loaded GHCi configuration from /home/sagemuej/.ghc/ghci.conf
Loaded GHCi configuration from /home/sagemuej/.ghci
[1 of 1] Compiling Main             ( wtmpf-file3770.hs, interpreted )
Ok, 1 module loaded.
*Main> :m +Graphics.Dynamic.Plot.R2 
*Main Graphics.Dynamic.Plot.R2> plotWindow [legendName "sin" $ continFnPlot sin, legendName "sinf" $ continFnPlot sinf]

plot of the truncate-maclaurin sine

实际上,你只在零范围内的小范围内使用Maclaurin系列,并利用周期性来定义实轴的其余部分的函数:

sinf :: Double -> Double
sinf x = sum $ take 30
           [ sign * power / fact
           | (sign, power, fact) <- zip3
               (iterate negate 1)
               (oddEntries $ iterate (*x') 1)
               (oddEntries factorials) ]
 where factorials :: [Double]
       factorials = scanl (*) 1 [1..]
       x' = x - 2*pi*fromIntegral (round $ x/(2*pi))

trunctation error of Maclauring fixed by exploiting periodicity