考虑一下
Pentagonal numbers are generated by the formula, Pn=n(3n−1)/2.
我选择在F#中创建一系列五角形数字:
let pentagonalSeq = { 1..Int32.MaxValue } |> Seq.map (fun n -> n*(3*n-1)/2)
到目前为止一切顺利。在大多数情况下,我只想计算几个小整数五边形数。但有时我希望获得所有Int32
五角形数字。我以为可以继续计算它们,直到我最终得到OverflowException
(我使用的是检查算术)。问题是F#对我的想法不是特别高兴,大喊那个
'try/with' cannot be used inside sequence expressions
让这位年轻女士满意的最佳方法是什么?
假设我要创建一个int32_pentagonalSeq
:
pentagonalSeq
由于
答案 0 :(得分:4)
我认为Gene的答案可能就是要走的路!但是如果你想使用序列表达式迭代你已经拥有的序列的元素,你可以写下这样的东西:
let takeWhileNonException (input:seq<_>) = seq {
use ps = input.GetEnumerator()
while (try ps.MoveNext() with _ -> false) do
yield ps.Current }
可悲的是,你不能只用for
.. yield!
包装try
循环或with
语句,因为(如你所说),序列表达式不支持异常处理程序。但是,您可以使用底层迭代器并将MoveNext
调用包装在异常处理程序中(因为这是一个普通表达式)并在第一次操作失败时返回false
。
因此,要获得序列的最后一个数字,您现在可以写:
pentagonalSeq
|> takeWhileNonException
|> Seq.last
答案 1 :(得分:2)
你可以这样做吗?
let pentagonal n =
try
Some(n*(3*n-1)/2)
with
| ex -> None
let x = { 1.. Int32.MaxValue } |> Seq.map pentagonal
答案 2 :(得分:2)
您可以使用Seq.unfold
算法使用生成器函数代替序列理解而不是checked,就像下面的代码段一样:
open Checked
let generator n =
try
Some ((n*(3*n-1)/2), n+1)
with
| :? System.OverflowException -> None
let pentagonalSeq = Seq.unfold generator 1
现在你可以在FSI中看到什么是最后的五角形整数可以用这个公式计算而没有溢出:
pentagonalSeq |> Seq.last;;
val it : int = 1073731660
显然这不是最大五边形Int32,顺便说一下,2147438935.但是,要计算1073731660和2147438935之间的五边形数字,您需要在int
数字字段之外操作。从某种意义上说,这更容易实现您的初始目标,因为它根本不需要检查算术和try-with
机制:
let pentagonalSeq = Seq.unfold
(fun n -> let pentagonal = n*(3L*n-1L)/2L in
if pentagonal > (int64 System.Int32.MaxValue) then
None // Sequence is over
else
Some((int pentagonal), n+1L)) // Member and next state
1L // Initial state
再次检查FSI,我们可以看到现在它是一个真正的int
五角形数字序列,确实:
pentagonalSeq |> Seq.last;;
val it : int = 2147438935