我一直在研究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))
答案 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
- 有整数除法函数div
和quot
(以及它们的余数对应,mod
和rem
),但那是不是你想要的,因为你想要一个小数结果。解决方案是使用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 (+) 0
或sum
。
答案 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]
实际上,你只在零范围内的小范围内使用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))