【发布时间】:2012-06-21 16:43:11
【问题描述】:
这是我的第一个 F# 程序。我想我会实施康威的生命游戏作为第一个练习。
请帮助我理解为什么下面的代码性能如此糟糕。
let GetNeighbours (p : int, w : int, h : int) : seq<int> =
let (f1, f2, f3, f4) = (p > w, p % w <> 1, p % w <> 0, p < w * (h - 1))
[
(p - w - 1, f1 && f2);
(p - w, f1);
(p - w + 1, f1 && f3);
(p - 1, f2);
(p + 1, f3);
(p + w - 1, f4 && f2);
(p + w, f4);
(p + w + 1, f4 && f3)
]
|> List.filter (fun (s, t) -> t)
|> List.map (fun (s, t) -> s)
|> Seq.cast
let rec Evolve (B : seq<int>, S : seq<int>, CC : seq<int>, g : int) : unit =
let w = 10
let h = 10
let OutputStr = (sprintf "Generation %d: %A" g CC) // LINE_MARKER_1
printfn "%s" OutputStr
let CCN = CC |> Seq.map (fun s -> (s, GetNeighbours (s, w, h)))
let Survivors =
CCN
|> Seq.map (fun (s, t) -> (s, t |> Seq.map (fun u -> (CC |> Seq.exists (fun v -> u = v)))))
|> Seq.map (fun (s, t) -> (s, t |> Seq.filter (fun u -> u)))
|> Seq.map (fun (s, t) -> (s, Seq.length t))
|> Seq.filter (fun (s, t) -> (S |> Seq.exists (fun u -> t = u)))
|> Seq.map (fun (s, t) -> s)
let NewBorns =
CCN
|> Seq.map (fun (s, t) -> t)
|> Seq.concat
|> Seq.filter (fun s -> not (CC |> Seq.exists (fun t -> t = s)))
|> Seq.groupBy (fun s -> s)
|> Seq.map (fun (s, t) -> (s, Seq.length t))
|> Seq.filter (fun (s, t) -> B |> Seq.exists (fun u -> u = t))
|> Seq.map (fun (s, t) -> s)
let NC = Seq.append Survivors NewBorns
let SWt = new System.Threading.SpinWait ()
SWt.SpinOnce ()
if System.Console.KeyAvailable then
match (System.Console.ReadKey ()).Key with
| System.ConsoleKey.Q -> ()
| _ -> Evolve (B, S, NC, (g + 1))
else
Evolve (B, S, NC, (g + 1))
let B = [3]
let S = [2; 3]
let IC = [4; 13; 14]
let g = 0
Evolve (B, S, IC, g)
前五次迭代,即第 0、1、2、3、4 代,毫无问题地发生。然后,在大约 100 毫秒的短暂暂停后,第 5 代完成。但在那之后,程序挂在标记为“LINE_MARKER_1”的行处,如断点 Visual Studio 所示。它永远不会到达printfn 行。
奇怪的是,到了第 2 代,函数Evolve 中的CC 序列已经稳定到序列[4; 13; 14; 3],所以我认为第6 代没有理由不能进化。
我知道粘贴大段代码并在调试时寻求帮助通常被认为是令人反感的,但我不知道如何将其减少到最低限度的工作示例。任何可以帮助我调试的指针都将不胜感激。
提前感谢您的帮助。
编辑
我真的相信任何希望帮助我的人都可能会忽略GetNeighbours 函数。为了完整起见,我将其包括在内。
【问题讨论】:
-
@Shredderroy :
Seq用于惰性评估,您几乎肯定不希望这样做。 -
F# 序列没有提供不错的性能。使用
Seq.length和Seq.append会使性能更差。 -
另外 - 请注意您不需要
Seq.cast(它不会像您认为的那样做)。将|> Seq.cast替换为:> _可以满足您的要求,但仍会检查类型。你也不能投到seq。 -
@RamonSnir 是的,
List是解决O(exp(N))的一种方法。但是我认为更大的问题是不理解 Seq。就建议而言,“从不使用 Seq”并不是很有教育意义 :) -
@Shredderroy:由于局部性,数组的性能将优于
Seqs,甚至列出此类处理。
标签: performance recursion f# sequences