F#:编写递归构建元组列表的函数,语法错误

发布于 2024-12-20 06:03:51 字数 1512 浏览 2 评论 0原文

我正在尝试编写一个返回 List < 的递归函数。 int * int>,但我在语法上遇到了一些问题。

当递归结束时,该函数应该返回一个空列表,否则一个元组 (int * int) 与递归调用自身返回的列表合并:

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

有人可以向我解释我做错了什么吗?

编辑:

我会尝试提供更多细节。 实际上我的功能有点复杂。我正在迭代一个二维数组,并使用满足特定条件的数组索引元素构建一个元组列表。实际上这是我的代码:

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  -> []

所以这里还有两个问题:):

  1. 如何修改以下行以使其编译?

    (row,col) ::GetSameColorNeighs(grid, row + 1, col + 1, color) :: GetSameColorNeighs(grid, row - 1, col - 1, color) 
    
  2. 有没有更好的方法可以做到这一点?

我不关心列表的元素顺序。

I'm trying to write a recursive function that return a List < int * int>, but I'm having some trouble with the syntax.

The function should return an empty list when the recursione ends, otherwise a tuple (int * int) merged with a List returned by a recursive call to itself:

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

Could someone explain to me what I'm doing wrong?

EDIT:

I'll try to give more details .
Actually my function is a bit more complicated. I'm iterating through a 2d array and build a list of tuples with the array index elements that satisfy a certain condition. Actually that's my code:

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  -> []

So here's 2 more question :):

  1. How modify the following row in order to make it compile?

    (row,col) ::GetSameColorNeighs(grid, row + 1, col + 1, color) ::  GetSameColorNeighs(grid, row - 1, col - 1, color) 
    
  2. Is there any better way to do that?

I don't care about the list's elements order.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

烛影斜 2024-12-27 06:03:51

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

Daniel's solution looks good to me, but you don't need to use mutable state (ResizeArray). Instead, you can write the loop function as a sequence expression that generates results using yield and makes recursive calls using 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
软的没边 2024-12-27 06:03:51

使用带有累加器的内部函数可能是最简单的方法(作为奖励,它是尾递归的)。

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

Using an inner function with an accumulator is probably the easiest way to do it (as a bonus it's tail recursive).

let foobar() =
  let rec foobar acc =
    if (recursionIsEnded) then
        List.rev acc
    else 
        foobar ((10, 10)::acc)
  foobar []

Based on your update, I'd prefer a mutable solution. Something like

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
如何视而不见 2024-12-27 06:03:51

由于 foobar 返回一个列表,您必须附加列表或连接,然后反转

(foobar())@(10,10) 但是我通常会

以相反的方式执行此操作

   (10,10)::(foobar()))

,然后在以下情况下反转你回来了。上面的尾递归有一个问题,所以到了 tweek,你最终会得到类似的结果:

let foobar () : List<int * int> =
  let rec inner acc =
    if (recursionIsEnded) then
        list.Rev acc
    else 
        inner ((10, 10)::acc)
  inner []

与将列表附加到列表相比,最终反转通常会导致更少的操作

Since foobar returns a list you either have to append the lists or concat and then reverse

(foobar())@(10,10) however I'd usually do

it the other way around

   (10,10)::(foobar()))

and then reverse when you return. the above have a problem with tail recursion so to tweek that you end up with something like:

let foobar () : List<int * int> =
  let rec inner acc =
    if (recursionIsEnded) then
        list.Rev acc
    else 
        inner ((10, 10)::acc)
  inner []

reversing in the end should usually result in fewer operations than if you append a list to a list

东风软 2024-12-27 06:03:51

虽然它是一个更简单的函数,但我希望它有助于说明这一点,因为您通过两个列表递归来构建元组。此函数采用 ([],[]) 形式的两个列表作为输入,并将每个元素作为新列表中的元组配对在一起。

let rec pairlists twolists = 
  match twolists  with
    | ([],[]) -> []
    | (x::xs, y::ys) -> 
            (x,y)::pairlists(xs,ys)
;;

给定两个长度相同的列表(如果它们长度不同,您将收到错误),每次递归都会将前两个元素配对为一个元组(x,y)。语法:

(x,y)::pairlists(xs,ys)

实际上只是说“用我们当前的元素(x,y)构建一个元组,然后继续用我们的列表的其余部分(xs,ys)构建元组。

运行:

pairlists ([1;2;3], 1;2;3])

您将得到输出:

[(1,1);(2,2);(3;3)]

Although it's a simpler function, I hope it helps illustrate the point as your recurse through two lists to build tuples. This function takes two lists as input in the form ([],[]), and pairs each element together as a tuple in a new list.

let rec pairlists twolists = 
  match twolists  with
    | ([],[]) -> []
    | (x::xs, y::ys) -> 
            (x,y)::pairlists(xs,ys)
;;

Given two lists of the same length (If they aren't the same length you'll get an error), each recursion will pair the first two elements into a tuple as (x,y). The syntax:

(x,y)::pairlists(xs,ys)

Is really just saying "Build a tuple with our current elements (x,y), then proceed building tuples with the rest of our lists (xs,ys).

Run:

pairlists ([1;2;3], 1;2;3])

And you'll get the output:

[(1,1);(2,2);(3;3)]
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文