简化/简化这个双向循环?

发布于 2024-09-18 07:07:15 字数 2141 浏览 6 评论 0原文

我的电线在某个地方交叉了(或者我睡眠不足)。我需要一个双向循环,而我当前的代码实在是丑陋。

问题:我正在使用索引沿着线性数据结构运行。我有一个起始索引,假设为 120。我想交替向两个方向运行。

例子: 120,121,119,122,118,123,117,...

我有一个停止标准,需要分别满足每个方向。如果一个方向满足,我只想运行到另一个方向,如果两个方向都满足,我需要退出循环。此外,如果下一个索引无效(数据结构结束,例如小于 0 或大于 200),我需要停止。

示例:在向后 116 处和向前 130 处停止执行: 120,121,119,122,118,123,117,124,116,(中断),125,126,127,128,129,130​​。

首先跑向一个方向,然后不幸的是,另一个方向是不可能的。

我当前的代码很丑陋。这是很多行,但不包含任何“生产性”代码。只有迭代逻辑:

  int start_idx = 120;
  int forward_idx = start_idx;
  int backward_idx = start_idx;

  bool next_step_forward = true; //should next step be forward or backward?

  int cur_idx;
  while(backward_idx >= 0 || forward_idx >= 0)
  {
    if(next_step_forward   //if we should step forward
      && forward_idx >= 0) //and we still can step forward
    {
      cur_idx = ++forward_idx;

      if(forward_idx >= 200) //200 is fictive "max index"
      {
        next_step_forward = false;
        forward_idx = -1; //end of data reached, no more stepping forward
        continue;
      }

      if(backward_idx >= 0)
      {
        next_step_forward = false;
      }
    }
    else if(!next_step_forward 
            && backward_idx >= 0)
    {
      cur_idx = --backward_idx;
      if(backward_idx < 0) //beginning of data reached, no more stepping backward
      {
        next_step_forward = true;
        continue;
      }

      if(forward_idx >= 0)
      {
        next_step_forward = true;
      }
    }
    else
    {
      next_step_forward = !next_step_forward; //ever hit?, just security case
      continue; 
    }

    //loop body
    //do something with cur_idx here

    if(stoppingCriterionMet())
    {

      if(cur_idx > start_idx)
      { //this was a forward step, stop forward stepping
        forward_idx = -1;
      }
      else
      { //this was backward step, stop backward stepping
        backward_idx = -1;
      }
    }
  }

我错过了什么吗?任何提示表示赞赏。谢谢。

编辑1:有很多非常好的答案,它们将“用 cur_idx 做一些事情”放入一个单独的函数中。虽然这对于我提出问题的方式来说是一个完美的想法,但我更喜欢将迭代代码放在其他地方,并将高效的代码留在那里。我有一个很长的算法,希望在完成后将其拆分,以最大程度地减少重新排列工作。

I've got my wires crossed somewhere (or I had not enough sleep). I need a two-way loop, and my current code is just plain ugly.

Problem: I am running along a linear datastructre using an index. I have an starting index, lets say 120. I want to run alternating into both directions.

Example:
120,121,119,122,118,123,117,...

I have a stopping criterion which needs to be met for each direction separately. If it is met for one direction, I only want to run into the other direction, if both are met I need to exit the loop. In addition I need to stop if the next index is invalid (end of data structure, say smaller than 0 or bigger than 200).

Example: Stopping execution at 116 backwards and 130 forward:
120,121,119,122,118,123,117,124,116,(break),125,126,127,128,129,130.

Running into one direction first, then the other one is unfortunately not an option.

My current code is plain ugly. It is a lot of lines without containing any "productive" code. Only iteration logic:

  int start_idx = 120;
  int forward_idx = start_idx;
  int backward_idx = start_idx;

  bool next_step_forward = true; //should next step be forward or backward?

  int cur_idx;
  while(backward_idx >= 0 || forward_idx >= 0)
  {
    if(next_step_forward   //if we should step forward
      && forward_idx >= 0) //and we still can step forward
    {
      cur_idx = ++forward_idx;

      if(forward_idx >= 200) //200 is fictive "max index"
      {
        next_step_forward = false;
        forward_idx = -1; //end of data reached, no more stepping forward
        continue;
      }

      if(backward_idx >= 0)
      {
        next_step_forward = false;
      }
    }
    else if(!next_step_forward 
            && backward_idx >= 0)
    {
      cur_idx = --backward_idx;
      if(backward_idx < 0) //beginning of data reached, no more stepping backward
      {
        next_step_forward = true;
        continue;
      }

      if(forward_idx >= 0)
      {
        next_step_forward = true;
      }
    }
    else
    {
      next_step_forward = !next_step_forward; //ever hit?, just security case
      continue; 
    }

    //loop body
    //do something with cur_idx here

    if(stoppingCriterionMet())
    {

      if(cur_idx > start_idx)
      { //this was a forward step, stop forward stepping
        forward_idx = -1;
      }
      else
      { //this was backward step, stop backward stepping
        backward_idx = -1;
      }
    }
  }

Am I missing anything? Any hints appreciated. Thanks.

Edit 1: There are lots of very nice answers, which put "do something with cur_idx" into a separate function. While this is a perfect idea for the way my question was asked, I prefer putting the iterating code somewhere else and leave the productive code there. I have a long algorithm and want to split it up after it is finished to minimize rearangement work.

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

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

发布评论

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

评论(4

爺獨霸怡葒院 2024-09-25 07:07:15

这个怎么样?

void do_loop(SomeType *arr, int start, int low, int high, int arr_max)
{
    int downwardIndex, upwardIndex;
    downwardIndex = upwardIndex = start;
    while (downwardIndex > 0 && upwardIndex < arr_max) {
        if (downwardIndex < low && upwardIndex > high) {
            break;
        }
        if (downwardIndex > low) {
            processElement(arr[downwardIndex]);
            downwardIndex--;
        }
        if (upwardIndex < high) {
            processElement(arr[upwardIndex]);
            upwardIndex++;
        }
    }
}

How about this?

void do_loop(SomeType *arr, int start, int low, int high, int arr_max)
{
    int downwardIndex, upwardIndex;
    downwardIndex = upwardIndex = start;
    while (downwardIndex > 0 && upwardIndex < arr_max) {
        if (downwardIndex < low && upwardIndex > high) {
            break;
        }
        if (downwardIndex > low) {
            processElement(arr[downwardIndex]);
            downwardIndex--;
        }
        if (upwardIndex < high) {
            processElement(arr[upwardIndex]);
            upwardIndex++;
        }
    }
}
℉服软 2024-09-25 07:07:15

正好我今天编码也差不多这个问题了。我使用 C# 迭代器函数来完成此操作。但我认为你想要一个更通用的解决方案。

如果您使用可以构建自己的迭代器的语言(C++、Java、C#),那么这很容易。您只需创建一个自定义迭代器,最初从中心开始输出数字。然后你给迭代器一个额外的函数来告诉它停止在当前方向上运行。

如果你在 C 中做类似的事情(对我来说看起来像 C),你可以使用包含迭代器状态的结构体以及你调用的向前推进或停止的函数来模仿这一点。

It so happened that I coded almost this problem today. And I used a C# iterator function to do it. But I think you want a more generic solution.

If you use a language where you can build your own iterators (C++,Java,C#), it's easy. You just make a custom iterator that initially spits out numbers starting from the center. Then you give the iterator an extra function to tell it to stop running in the current direction.

If you're doing something like this in C (it looks C to me), you can mimic this with a struct containing the iterator state, and functions that you call to step it forward or stop it.

绝不服输 2024-09-25 07:07:15

第一次破解这个(假设 C - 其他语言需要适应,但这些概念基本上是语言中立的):

void pass1(int start_x, int lo_limit, int hi_limit)
{
    assert(start_x >= lo_limit && start_x <= hi_limit);
    int lo_x = start_x - 1;
    int hi_x = start_x + 1;

    Process(start_x);
    if (StopCriterion(start_x))
        return;  // Is that correct?

    while (lo_x >= lo_limit && hi_x <= hi_limit)
    {
        Process(lo_x);
        if (StopCriterion(lo_x))
            lo_x = lo_limit - 1;
        else
            lo_x--;
        Process(hi_x);
        if (StopCriterion(hi_x))
            hi_x = hi_limit + 1;
        else
            hi_x++;
    }
    while (lo_x >= lo_limit)
    {
        Process(lo_x);
        if (StopCriterion(lo_x))
            lo_x = lo_limit - 1;
        else
            lo_x--;
    }
    while (hi_x <= hi_limit)
    {
        Process(hi_x);
        if (StopCriterion(hi_x))
            hi_x = hi_limit + 1;
        else
            hi_x++;
    }
}

尚不清楚如果起始位置与停止标准匹配会发生什么。搜索应该完全停止,还是应该继续向上、向下或双向。我选择了“完全停止”,但可以为列出的任何选项提供案例。在“两者”的情况下,您甚至不需要运行停止标准检查。

我也选择先做下方再做上方;显然,这完全是相反的。最后两个循环的顺序并不重要,因为如果两个方向都在同一迭代中终止,则两个循环都不会执行;如果只有一个方向被终止,则相应的循环根本不会执行 - 只有另一个方向会执行。

由于其中仍然存在重复的代码:

void pass2(int start_x, int lo_limit, int hi_limit)
{
    assert(start_x >= lo_limit && start_x <= hi_limit);
    int lo_x = start_x - 1;
    int hi_x = start_x + 1;

    Process(start_x);
    if (StopCriterion(start_x))
        return;  // Is that correct?

    while (lo_x >= lo_limit && hi_x <= hi_limit)
    {
        Process_lo(&lo_x, lo_limit);
        Process_hi(&hi_x, hi_limit);
    }
    while (lo_x >= lo_limit)
        Process_lo(&lo_x, lo_limit);
    while (hi_x <= hi_limit)
        Process_hi(&hi_x, hi_limit);
}

void Process_lo(int *lo_x, int lo_limit)
{
    Process(*lo_x);
    if (StopCriterion(*lo_x))
        *lo_x = lo_limit - 1;
    else
        *lo_x--;
}

void Process_hi(int *hi_x, int hi_limit)
{
    Process(*hi_x);
    if (StopCriterion(*hi_x))
        *hi_x = hi_limit + 1;
    else
        *hi_x++;
}

可见性控制(静态函数)等作为实现语言的细节而被遗漏。

First pass at hacking this (assuming C - adaptations needed for other languages, but the concepts are basically language neutral):

void pass1(int start_x, int lo_limit, int hi_limit)
{
    assert(start_x >= lo_limit && start_x <= hi_limit);
    int lo_x = start_x - 1;
    int hi_x = start_x + 1;

    Process(start_x);
    if (StopCriterion(start_x))
        return;  // Is that correct?

    while (lo_x >= lo_limit && hi_x <= hi_limit)
    {
        Process(lo_x);
        if (StopCriterion(lo_x))
            lo_x = lo_limit - 1;
        else
            lo_x--;
        Process(hi_x);
        if (StopCriterion(hi_x))
            hi_x = hi_limit + 1;
        else
            hi_x++;
    }
    while (lo_x >= lo_limit)
    {
        Process(lo_x);
        if (StopCriterion(lo_x))
            lo_x = lo_limit - 1;
        else
            lo_x--;
    }
    while (hi_x <= hi_limit)
    {
        Process(hi_x);
        if (StopCriterion(hi_x))
            hi_x = hi_limit + 1;
        else
            hi_x++;
    }
}

It is not clear what should happen if the starting position matches the stop criterion. Should the search stop altogether, or should it continue upwards, or downwards, or both ways. I chose 'stop altogether', but a case could be made for any of the options listed. In the case of 'both', you would not even bother to run the stop criterion check.

I also chose to do the lower before the upper direction; it is clearly trivially reversed. The order of the final two loops doesn't matter because if both directions terminate in the same iteration, neither trailing loop is executed; if only one direction is terminated, the corresponding loop won't execute at all - only the other will.

Since there is still repeated code in there:

void pass2(int start_x, int lo_limit, int hi_limit)
{
    assert(start_x >= lo_limit && start_x <= hi_limit);
    int lo_x = start_x - 1;
    int hi_x = start_x + 1;

    Process(start_x);
    if (StopCriterion(start_x))
        return;  // Is that correct?

    while (lo_x >= lo_limit && hi_x <= hi_limit)
    {
        Process_lo(&lo_x, lo_limit);
        Process_hi(&hi_x, hi_limit);
    }
    while (lo_x >= lo_limit)
        Process_lo(&lo_x, lo_limit);
    while (hi_x <= hi_limit)
        Process_hi(&hi_x, hi_limit);
}

void Process_lo(int *lo_x, int lo_limit)
{
    Process(*lo_x);
    if (StopCriterion(*lo_x))
        *lo_x = lo_limit - 1;
    else
        *lo_x--;
}

void Process_hi(int *hi_x, int hi_limit)
{
    Process(*hi_x);
    if (StopCriterion(*hi_x))
        *hi_x = hi_limit + 1;
    else
        *hi_x++;
}

Visibility controls (static functions) etc left out as details of the implementation language.

陈独秀 2024-09-25 07:07:15

这就是我在 C# 中的处理方式:

const int UPPER_BOUND = 200;
const int LOWER_BOUND = 0;
const int START = 120;
bool foundlower = false, foundupper = false;
int upper, lower; 
upper = lower = START;

while (!foundlower || !foundupper) {
    if (!foundlower) {
        if (--lower <= LOWER_BOUND) foundlower = true;
        if (stoppingCriterionMet(lower)) foundlower = true;
    }

    if (!foundupper) {
        if (++upper >= UPPER_BOUND) foundupper = true;
        if (stoppingCriterionMet(upper)) foundupper = true;
    }
}

This is how I'd approach it in C#:

const int UPPER_BOUND = 200;
const int LOWER_BOUND = 0;
const int START = 120;
bool foundlower = false, foundupper = false;
int upper, lower; 
upper = lower = START;

while (!foundlower || !foundupper) {
    if (!foundlower) {
        if (--lower <= LOWER_BOUND) foundlower = true;
        if (stoppingCriterionMet(lower)) foundlower = true;
    }

    if (!foundupper) {
        if (++upper >= UPPER_BOUND) foundupper = true;
        if (stoppingCriterionMet(upper)) foundupper = true;
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文