将 Haskell 多态余弦函数转换为 F#
我正在尝试将一些 Haskell 代码转换为 F#,但我遇到了一些麻烦,因为 Haskell 默认情况下是惰性的,而 F# 不是。我还在学习 F# 的方法。下面是 Haskell 中的多态余弦函数,具有相当好的性能。我想尝试在 F# 中保持相同或更好的性能参数。我希望看到 F# List 版本和 F# Seq 版本,因为 Seq 版本更像是惰性 Haskell,但 List 版本可能会表现更好。感谢您的任何帮助。
效率:所使用的算术运算数量与级数中的项数成正比
空间:使用恒定空间,与项数无关
takeThemTwoByTwo xs =
takeWhile (not . null) [take 2 ys | ys <- iterate (drop 2) xs]
products xss = [product xs | xs <- xss]
pairDifferences xs =
[foldr (-) 0 adjacentPair | adjacentPair <- takeThemTwoByTwo xs]
harmonics x = [x/(fromIntegral k) | k <- [1 ..]]
cosineTerms = scanl (*) 1 . products . takeThemTwoByTwo . harmonics
cosine = foldl (+) 0 . pairDifferences .
take numberOfTerms . cosineTerms
I'm trying to convert some Haskell code to F# but I'm having some trouble since Haskell is lazy by default and F# is not. I'm also still learning my way around F#. Below is a polymorphic cosine function in Haskell with pretty good performance. I want to try and keep the same or better performance parameters in F#. I would like to see a F# List version and a F# Seq version since the Seq version would be more like the lazy Haskell but the List version would probably perform better. Thanks for any help.
Efficiency: number of arithmetic operations used proportional to number of terms in series
Space: uses constant space, independent of number of terms
takeThemTwoByTwo xs =
takeWhile (not . null) [take 2 ys | ys <- iterate (drop 2) xs]
products xss = [product xs | xs <- xss]
pairDifferences xs =
[foldr (-) 0 adjacentPair | adjacentPair <- takeThemTwoByTwo xs]
harmonics x = [x/(fromIntegral k) | k <- [1 ..]]
cosineTerms = scanl (*) 1 . products . takeThemTwoByTwo . harmonics
cosine = foldl (+) 0 . pairDifferences .
take numberOfTerms . cosineTerms
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
这是我的尝试,以防您懒惰阅读:
我很难发现您正在使用泰勒级数以弧度计算
余弦
:让我描述一下您正在做什么:
x/k
的无限序列,其中k
是从1
开始的整数。将上述序列分成两个块,并通过与
1
种子相乘进行扫描,得到 x2/((2k-1)*(2k )) (开头处的1
除外)。再次将新序列分成两个块,以 x4k-4/((4k-4)!) - x4k-2/((4k-2)!) 并将它们全部相加以获得最终结果。
由于在 F# 中分割序列可能效率低下,而且
takeThemTwoByTwo
函数也不是必需的,因此我选择了另一种方法:k
是从1
开始的整数。1
种子相乘来扫描序列;我们得到一个序列 (-1)k * x2k/((2k)!)。上面的程序是我的描述的直接翻译,简洁明了。在我的机器上,使用
numberOfTerms = 200000
迭代计算cosine
需要 0.15 秒;我认为它对于您的目的来说足够有效。此外,
List
版本应该很容易从这个版本翻译出来。更新:
好吧,我的错误是低估了问题的多态性部分。我更关注性能部分。这是一个多态版本(与
float
版本密切相关):Seq.initInfinite
不如 @kvb 中的Seq.unfold
强大回答。我保留它是为了让事情变得简单,因为n
无论如何都在int
范围内。Here is my attempt in case you're lazy to read:
I have a hard time finding out that you're calculating
cosine
in radian using Taylor series:Let me describe what you're doing:
x/k
wherek
is an integer starting from1
.Split above sequence into chunks of two and scan by multiplying with a seed of
1
to have a sequence of x2/((2k-1)*(2k)) (with an exception of1
at the beginning).Split the new sequence into blocks of two again to have differences in the form of x4k-4/((4k-4)!) - x4k-2/((4k-2)!) and sum all of them to get final result.
Because it's likely to be inefficient to split sequences in F# and
takeThemTwoByTwo
function is not essential, I chose another approach:k
is an integer starting from1
.1
; we get a sequence of (-1)k * x2k/((2k)!).Above program is a direct translation of my description, succinct and simple. Computing
cosine
withnumberOfTerms = 200000
iterations takes 0.15 seconds on my machine; I suppose it is efficient enough for your purpose.Furthermore, a
List
version should be easy to translate from this one.UPDATE:
Ok, my fault was to underestimate the polymorphism part of the question. I focused more on the performance part. Here is a polymorphic version (keeping closely to the
float
version):Seq.initInfinite
is less powerful thanSeq.unfold
in @kvb 's answer. I keep it to make things simple becausen
is inint
range anyway.Pad的回答很好,但不是多态的。一般来说,在 F# 中创建此类定义的情况比在 Haskell 中要少得多(而且有点痛苦)。这是一种方法:
Pad's answer is good, but not polymorphic. In general, it's significantly less common to create such definitions in F# than in Haskell (and a bit of a pain). Here's one approach:
正如 Pad 所写,这似乎是 cos(x) 关于 x=0 的泰勒级数展开:
所以你的问题是一个XY问题:你提出了一个解决方案而不是提出问题。相反,提出问题可以更容易地以不同的方式解决问题。
让我们首先在 F# 中编写一个
float
特定版本:例如,我们可以计算 x=0.1 展开式的 1M 项:
在 F# 中实现此多态性的最佳方法是将函数参数化为它使用的运算符并将其标记为内联,以消除此参数化的性能开销:
现在我们可以使用
float
计算 1M 项,如下所示,速度同样快作为before:但我们也可以做单精度
float
:和任意精度有理数:
甚至符号:
为了使它更快,将公共子表达式
-x*x
提升出来的循环
。As Pad wrote, this appears to be the Taylor series expansion of cos(x) about x=0:
So your question is an XY question: you presented a solution rather than posing the problem. Presenting the problem instead makes it much easier to solve it differently.
Let's start by writing a
float
-specific version in F#:For example, we can compute 1M terms of the expansion of x=0.1:
The best way to make this polymorphic in F# is to parameterize the function over the operators it uses and mark it as
inline
in order to remove the performance overhead of this parameterization:Now we can compute 1M terms using
float
like this, which is just as fast as before:But we can also do single-precision
float
:And arbitrary-precision rational:
And even symbolic:
To make it faster, hoist the common subexpression
-x*x
out ofloop
.