如何在F#递归算法中正确返回生成的序列

时间:2015-09-25 03:44:59

标签: recursion f# functional-programming path-finding chess

作为一项辅导练习,我在CS中实施了Knights Tour算法并且工作正常,在尝试将其移植到F#之后我无法超越我将聚集Knight路径的结果序列的部分返回到呼叫者。

代码是这样的:

let offsets = [|(-2,-1);(-2,1);(-1,-2);(-1,2);(1,-2);(1,2);(2,-1);(2,1)|];

let squareToPair sqr = 
    (sqr % 8, sqr / 8)

let pairToSquare (col, row) = 
    row * 8 + col

// Memoizing function taken from Don Syme (http://blogs.msdn.com/b/dsyme/archive/2007/05/31/a-sample-of-the-memoization-pattern-in-f.aspx)
let memoize f =
    let cache = ref Map.empty
    fun x ->
        match (!cache).TryFind(x) with
        | Some res -> res
        | None ->
             let res = f x
             cache := (!cache).Add(x,res)
             res

let getNextMoves square = 
    let (col, row) = squareToPair square
    offsets 
    |> Seq.map    (fun (colOff, rowOff) -> (col + colOff, row + rowOff))
    |> Seq.filter (fun (c, r) -> c >= 0 && c < 8 && r >= 0 && r < 8) // make sure we don't include squares out of the board
    |> Seq.map    (fun (c, r) -> pairToSquare (c, r))

let getNextMovesMemoized = memoize getNextMoves

let squareToBoard square = 
    1L <<< square

let squareToBoardMemoized = memoize squareToBoard

let getValidMoves square board =
    getNextMovesMemoized square 
    |> Seq.filter (fun sqr -> ((squareToBoardMemoized sqr) &&& board) = 0L)

// gets all valid moves from a particular square and board state sorted by moves which have less next possible moves
let getValidMovesSorted square board =
    getValidMoves square board
    |> Seq.sortBy (fun sqr -> (getValidMoves sqr board) |> Seq.length ) 

let nextMoves = getValidMovesSorted
let sqrToBoard = squareToBoardMemoized

let findPath square = 
    let board = sqrToBoard square
    let rec findPathRec brd sqr sequence = seq {
        match brd with 
            | -1L -> yield sequence
            |   _ -> for m in nextMoves sqr do yield! findPathRec (brd ||| (sqrToBoard m)) m m::sequence
    }

    findPathRec board square [square]

let solution = findPath ((4,4) |> pairToSquare) |> Seq.take 1

我收到以下错误:

The type '(int64 -> seq<int>)' is not a type whose values can be enumerated with this syntax, i.e. is not compatible with either seq<_>, IEnumerable<_> or IEnumerable and does not have a GetEnumerator method (using external F# compiler)

我可能会误解这是如何工作的,但我希望nextMoves的结果是seq&lt; _&gt;。有没有更好的方法呢?我错过了什么吗?任何推荐的模式?

提前致谢!

2 个答案:

答案 0 :(得分:1)

所以问题是nextMoves有类型

val nextMoves : (int -> int64 -> seq<int>)

因为它与getValidMovesSorted相同。您需要提供board参数

答案 1 :(得分:1)

nextMoves只是getValidMovesSorted,它带有两个参数(squareboard) - 现在findPath你只提供一个,我想你想要写这个

nextMoves sqr board

但是其他代码中存在更多问题,而且很难弄清楚你想要做什么

我想你想做这样的事情:

let findPath square = 
    let board = sqrToBoard square
    let rec findPathRec brd sqr (sequence : int list) = 
        match brd with 
            | -1L -> sequence
            |   _ -> 
                [
                    for m in nextMoves sqr board do 
                    yield! findPathRec (brd ||| (sqrToBoard m)) m (m::sequence)
                ]

这将编译(但会导致堆栈溢出异常)