我能找到的所有续课教程都是固定长度的延续(即数据结构在遍历时具有已知数量的项目
我正在实施DepthFirstSearch Negamax(http://en.wikipedia.org/wiki/Negamax),虽然代码有效,但我想使用continuation重写代码
我的代码如下
let naiveDFS driver depth game side =
List.map (fun x ->
//- negamax depth-1 childnode opposite side
(x, -(snd (driver (depth-1) (update game x) -side))))
(game.AvailableMoves.Force())
|> List.maxBy snd
let onPlay game = match game.Turn with
| Black -> -1
| White -> 1
///naive depth first search using depth limiter
let DepthFirstSearch (depth:int) (eval:Evaluator<_>) (game:GameState) : (Move * Score) =
let myTurn = onPlay game
let rec searcher depth game side =
match depth with
//terminal Node
| x when x = 0 || (isTerminal game) -> let movescore = (eval ((),game)) |> fst
(((-1,-1),(-1,-1)),(movescore * side))
//the max of the child moves, each child move gets mapped to
//it's associated score
| _ -> naiveDFS searcher depth game side
其中更新使用给定移动更新游戏状态,eval评估游戏状态并返回增量(当前未使用)以进行增量评估,并且终端评估该位置是否为结束位置。
问题是我必须将未知数量的操作(每个剩余的list.map迭代)注册到延续,我实际上无法想到这样做的有效方法。
由于这是一个指数算法,我显然希望尽可能保持这种效率(尽管我的大脑很痛苦,试图把它想象成我们的,所以我确实希望答案不仅仅是一个有效的答案)
由于
答案 0 :(得分:5)
我认为你需要实现基于续例的List.map
版本才能执行此操作。
map
的标准实现(使用accumulator参数)如下所示:
let map' f l =
let rec loop acc l =
match l with
| [] -> acc |> List.rev
| x::xs -> loop ((f x)::acc) xs
loop [] l
如果你添加 continuation 作为参数并将代码转换为通过延续返回,你将得到(有趣的案例是x::xs
loop
分支函数,我们首先使用tail-call调用f
,并将一些continuation作为参数):
let contMap f l cont =
let rec loop acc l cont =
match l with
| [] -> cont acc |> List.rev
| x::xs -> f x (fun x' -> loop (x'::acc) xs cont)
loop [] l cont
然后您可以将普通List.map
转换为基于延续的版本,如下所示:
// Original version
let r = List.map (fun x -> x*2) [ 1 .. 3 ]
// Continuation-based version
contMap (fun x c -> c(x*2)) [ 1 .. 3 ] (fun r -> ... )
我不确定这是否能给你带来显着的性能提升。我认为如果你有一个非常深的递归(不适合堆栈),主要需要继续。如果它适合堆栈,那么它可能会使用堆栈快速运行。
此外,重写为显式延续风格会使程序有点难看。您可以通过使用计算表达式来处理continuation来改进它。 Brian有一个blog post on this very topic。