生成无限个哈明数(即所有满足条件 n = 2^i * 3^j * 5^k 的正整数 n)有一个众所周知的解决方案。我已经用 F# 实现了两种不同的方法。第一种方法使用 seq,这种解决方案优雅而简洁,但性能很差。第二种方法使用自定义类型,其中尾部包裹在 Lazy> 中,这种解决方案笨重,但性能很惊人。
可以有人解释一下为什么使用 seq 的性能如此之差,如果有办法修复它吗?谢谢。
第一种方法使用 seq。
可以有人解释一下为什么使用 seq 的性能如此之差,如果有办法修复它吗?谢谢。
第一种方法使用 seq。
// 2-way merge with deduplication
let rec (-|-) (xs: seq<int>) (ys: seq<int>) =
let x = Seq.head xs
let y = Seq.head ys
let xstl = Seq.skip 1 xs
let ystl = Seq.skip 1 ys
if x < y then seq { yield x; yield! xstl -|- ys }
elif x > y then seq { yield y; yield! xs -|- ystl }
else seq { yield x; yield! xstl -|- ystl }
let rec hamming: seq<int> = seq {
yield 1
let xs = Seq.map ((*) 2) hamming
let ys = Seq.map ((*) 3) hamming
let zs = Seq.map ((*) 5) hamming
yield! xs -|- ys -|- zs
}
[<EntryPoint>]
let main argv =
Seq.iter (printf "%d, ") <| Seq.take 100 hamming
0
使用Lazy<LazyList<int>>
的方法2。
type LazyList<'a> = Cons of 'a * Lazy<LazyList<'a>>
// Map `f` over an infinite lazy list
let rec inf_map f (Cons(x, g)) = Cons(f x, lazy(inf_map f (g.Force())))
// 2-way merge with deduplication
let rec (-|-) (Cons(x, f) as xs) (Cons(y, g) as ys) =
if x < y then Cons(x, lazy(f.Force() -|- ys))
elif x > y then Cons(y, lazy(xs -|- g.Force()))
else Cons(x, lazy(f.Force() -|- g.Force()))
let rec hamming =
Cons(1, lazy(let xs = inf_map ((*) 2) hamming
let ys = inf_map ((*) 3) hamming
let zs = inf_map ((*) 5) hamming
xs -|- ys -|- zs))
[<EntryPoint>]
let main args =
let a = ref hamming
let i = ref 0
while !i < 100 do
match !a with
| Cons (x, f) ->
printf "%d, " x
a := f.Force()
i := !i + 1
0
LazyList
代码版本。它还有一个lazyList
构建器,极大地简化了编写此类代码的过程。 - Jack P.seq
推导通常更快,因为编译器可以更好地进行优化。Seq.map
本质上并不慢,但在这种情况下,由于代码重复评估序列,编译后的代码每次调用Seq.map
都必须分配一个闭包,因此存在很多开销。 - Jack P.main
,我编译上面的代码也没有问题。你介意在ExtCore的github页面上开一个问题吗?我想帮助你解决这个问题,并且在那里讨论会更容易。 - Jack P.