【问题标题】:Looping without a loop没有循环的循环
【发布时间】:2020-03-12 16:31:53
【问题描述】:

我们被限制为不能在程序中使用循环作为编程挑战。

限制: 不能使用 while、for、goto 和递归。

这些限制非常令人生畏。我真的想不出任何合适的解决方案。 所以我选择了这个通过修改返回地址实现的。

这会更好吗?

#include <unistd.h>
#include <sys/mman.h>

#include <iostream>
#include <cstring>

void the__(){}
void magic__(){}
void loop__(){}
void function__(){}
void here__(){}

template <typename T>
struct for_
{
    bool started = false;
    void* fix = nullptr;
    void(*body)(T&) = nullptr;

    for_(void(*body)(T&))
        : body(body)
    {
        auto do_for__ = uintptr_t(do_for_);
        uint64_t magic[] = {5243466812662057800, 6135086863767628931ull, 10416984888688609608ull, 144};
        mprotect((void*)(do_for__-do_for__%4096), 4096, 7);
        std::memcpy((void*)(do_for__+135), magic, 25);
    }

    static void do_for_(T& ctx)
    {
        void** p = (void**)((char*)&p+16);
        if (!ctx.started)
        {
            if (!ctx) return;
            ctx.started = true;
            ctx.fix = *p;
            *p = (void*)do_for_;
        }

        ctx.body(ctx);
        ctx.next();

        if (ctx)
        {
            the__();
            magic__();
            loop__();
            function__();
            here__();
        }
        else
        {
            *p = ctx.fix;
        }
    }
};

struct For0ToN : for_<For0ToN>
{
    For0ToN(int N, void(*f)(For0ToN&))
        : for_<For0ToN>(f)
        , N(N)
    {
        do_for_(*this);
    }

    operator bool() {return i < N;}
    operator int() {return i;}
    void next() {i++;}
    int count() {return i;}
    int i = 0, N = 0;
};

int main()
{
    For0ToN(10, +[](For0ToN& i)
    {
        std::cout << int(i) << ": ";
        For0ToN(i.count(), +[](For0ToN& i)
        {
            std::cout << int(i) << ". ";
        });
        std::cout << "\n";
    });
    std::cout << "done\n";
    return 0;
}

代码在这里演示:https://coliru.stacked-crooked.com/a/3dd77ade501ac748

【问题讨论】:

  • 您提到了限制,但没有提到您的实际任务。你想解决什么问题?
  • 阅读setjmp/longjmp。然后立即忘记你读过的所有内容。
  • 这个限制是谁设置的?上下文是什么?如果这是针对特定类或其他东西,则可能有一种“预期”的方式来执行此操作。
  • 在编译时循环中使用模板会受到递归的限制吗?见medium.com/@savas/…
  • 这是一种古老的智慧训练,你会发现循环一直在的内部?

标签: c++


【解决方案1】:

您可以使用longjmp。以下是来自cppreference 的示例:

#include <csetjmp>
#include <iostream>

std::jmp_buf jump_buffer;

[[noreturn]] void a(int count) 
{
    std::cout << "a(" << count << ") called\n";
    std::longjmp(jump_buffer, count+1);  // setjmp() will return count+1
}

int main() { 
    // loop from 0-9

    volatile int count = 0; // local variables must be volatile for setjmp
    if (setjmp(jump_buffer) != 10) {
        a(count++);  // This will cause setjmp() to exit
    }
}

【讨论】:

  • 附言。我使用longjmp 制作了 one 程序,那是 25-30 年前的事了。再也不会!
  • 我认为这是比使用accumulatefor_each 等更好的选择。
  • @PrinzRainerBuyo 据我所知(但我绝对不确定),堆栈上一次只有一个调用。我刚开始再次阅读它 - 我突然想起了头痛......
  • @KhalBuyo -- longjmp 在调用setjmp 时执行恢复上下文所需的任何操作。细节取决于编译器。
  • @PeteBecker 是的,这意味着堆栈应该被恢复——因此不会因为一百万次长跳而导致堆栈溢出。从我现在阅读的内容和我所做的小测试来看,这似乎是正确的。
【解决方案2】:

问题不清楚,但循环的另一种选择是std::transform()

【讨论】:

    【解决方案3】:

    模板元编程是避免在代码中编写显式循环的常用方法。这项工作由编译器完成。检查this 以获得阶乘和冒泡排序实现的示例,而无需编写显式循环。您也可以检查此堆栈溢出post

    【讨论】:

      【解决方案4】:

      如果我将递归隐藏到函数对象中并创建一个有限状态机,这是否算数?

      struct state
      {
          size_t current_column;
          size_t current_row;
          size_t max_column;
          size_t max_row;
      };
      
      typedef function<void(state&)> action_t;
      
      struct do_item
      {
          do_item(ostream& ss, action_t* move_next)
              : ss_(ss), move_next_(move_next) {}
      
          void operator()(state& s)
          {
              if (s.current_row == s.max_row)
              {
                  ss_ << "done";
                  return;
              }
              if (0 == s.current_column)
              {
                  ss_ << s.current_row << ':';
              }
              if (s.max_column == s.current_column)
              {
                  ss_ << '\n';
                  s.current_column = 0;
                  ++s.current_row;
                  s.max_column = s.current_row;
              }
              else
              {
                  ss_ << ' ' << s.current_column << '.';
                  ++s.current_column;
              }
              (*move_next_)(s);
          }
      
          ostream& ss_;
          action_t* move_next_;
      };
      
      static string no_loops_challenge(size_t n)
      {
          stringstream ss;
          state s = {0, 0, 0, n};
          action_t move_next;
          do_item action(ss, &move_next);
          move_next = action;
          action(s);
          return ss.str();
      }
      

      【讨论】:

      • 还是一个递归
      【解决方案5】:

      这是一个使用 range-v3 的解决方案,它应该满足所有约束:

      namespace rv = ranges::views;
          
      ranges::for_each(rv::iota(0, 10), [](int i) {
          std::cout << i << ": ";
          ranges::copy(rv::iota(0, i), 
              ranges::ostream_iterator<int>(std::cout, ". "));
          std::cout << "\n";
      });
      

      这是demo

      【讨论】:

        猜你喜欢
        • 2015-06-12
        • 1970-01-01
        • 2013-04-09
        • 1970-01-01
        • 1970-01-01
        • 2011-09-30
        • 2019-10-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多