我正在尝试编写一个返回List<的递归函数。 int * int>,但我在语法方面遇到了一些麻烦。
当recursione结束时,函数应返回一个空列表,否则元组(int * int)与递归调用返回的List合并:
let rec foobar () : List<int * int> =
if (recursionIsEnded) then
[]
else
foobar () :: (10, 10) // this is wrong
// (10,10) works, but I need to concatenate it with the elements returned by foobar recursive call
有人可以向我解释我做错了吗?
编辑:
我会尝试提供更多细节。 实际上我的功能有点复杂。我正在迭代2d数组并使用满足特定条件的数组索引元素构建元组列表。实际上这是我的代码:
let rec GetSameColorNeighs(grid : Option<Ball> [,], row : int , col : int, color : Microsoft.Xna.Framework.Color) : List<int * int> =
if (row < 0 || col < 0 || row > MaxLineNumber - 1 || col > BallsPerLine - 1) then
[]
else
let ball = grid.[row,col]
match ball with
|Some(ball) ->
if (!ball.visited = false || not <| ball.color.Equals(color)) then
[row , col]
else
ball.visited := true
(row,col) ::GetSameColorNeighs(grid, row + 1, col + 1, color) :: GetSameColorNeighs(grid, row - 1, col - 1, color)
|None -> []
所以这里还有两个问题:):
如何修改以下行以使其编译?
(row,col) ::GetSameColorNeighs(grid, row + 1, col + 1, color) :: GetSameColorNeighs(grid, row - 1, col - 1, color)
有没有更好的方法呢?
我不关心列表的元素顺序。
答案 0 :(得分:2)
使用带累加器的内部函数可能是最简单的方法(作为奖励它的尾递归)。
let foobar() =
let rec foobar acc =
if (recursionIsEnded) then
List.rev acc
else
foobar ((10, 10)::acc)
foobar []
根据您的更新,我更喜欢可变解决方案。像
这样的东西let GetSameColorNeighs(grid : Option<Ball> [,], row : int , col : int, color : Microsoft.Xna.Framework.Color) : List<int * int> =
let results = ResizeArray()
let rec loop (row, col) =
if (row < 0 || col < 0 || row > MaxLineNumber - 1 || col > BallsPerLine - 1) then ()
else
let ball = grid.[row,col]
match ball with
|Some(ball) ->
if (!ball.visited = false || not <| ball.color.Equals(color)) then
[row , col] //doesn't compile, not sure what to do here
else
ball.visited := true
results.Add(row, col)
loop(row + 1, col + 1)
loop(row - 1, col - 1)
|None -> ()
loop(row, col)
results |> Seq.toList
答案 1 :(得分:2)
Daniel的解决方案看起来不错,但您不需要使用可变状态(ResizeArray
)。相反,您可以将loop
函数编写为使用yield
生成结果的序列表达式,并使用yield!
进行递归调用:
let GetSameColorNeighs (grid:Option<Ball>[,], row, col, color:Color) =
let rec loop (row, col) = seq {
if not (row < 0 || col < 0 || row > MaxLineNumber - 1
|| col > BallsPerLine - 1) then
let ball = grid.[row,col]
match ball with
| Some(ball) ->
if (!ball.visited = false || not <| ball.color.Equals(color)) then
// Not sure what you want here - yield items using 'yield'?
// [row , col]
else
ball.visited := true
yield row, col // Add single item to results
yield! loop(row + 1, col + 1) // Add all generated to results
yield! loop(row - 1, col - 1) // -- || --
| None -> () }
loop(row, col) |> Seq.toList
答案 2 :(得分:1)
由于foobar返回一个列表,你必须附加列表或concat然后反转
(foobar())@(10,10)
但我通常会这样做
反过来说
(10,10)::(foobar()))
然后在你回来时反转。上面有一个尾递归的问题,所以要调整你最终得到的东西:
let foobar () : List<int * int> =
let rec inner acc =
if (recursionIsEnded) then
list.Rev acc
else
inner ((10, 10)::acc)
inner []
最后的反转通常会导致比将列表附加到列表更少的操作
答案 3 :(得分:0)
虽然它是一个更简单的功能,但我希望它有助于说明这一点,因为你通过两个列表来构建元组。此函数以形式([],[])的形式输入两个列表作为输入,并将每个元素组合在一起作为新列表中的元组。
let rec pairlists twolists =
match twolists with
| ([],[]) -> []
| (x::xs, y::ys) ->
(x,y)::pairlists(xs,ys)
;;
给定两个相同长度的列表(如果它们的长度不相同,则会得到错误),每次递归都会将前两个元素配对成元组(x,y)。语法:
(x,y)::pairlists(xs,ys)
真的只是在说#34;用我们当前的元素(x,y)构建一个元组,然后用剩下的列表(xs,ys)继续构建元组。
执行命令
pairlists ([1;2;3], 1;2;3])
您将获得输出:
[(1,1);(2,2);(3;3)]