【问题标题】:Can you capture a reference in a recursive lambda?您可以在递归 lambda 中捕获引用吗?
【发布时间】:2019-04-25 13:58:53
【问题描述】:

我已经使用带有记忆的递归解决方案完全解决了 HackerRank (https://www.hackerrank.com/challenges/ctci-recursive-staircase/problem) 上的一个特定问题:

std::map<int, int> memoize;

int davis_staircase(int n) {
    if (n == 0) {
        return 1;
    } else if (n < 0) {
        return 0;
    }

    auto find = memoize.find(n);
    if (find != memoize.end()) {
        return find->second;
    }

    int num_steps = davis_staircase(n - 1) + davis_staircase(n - 2) + davis_staircase(n - 3);
    memoize[n] = num_steps;

    return num_steps;
}

我想隐藏我用作查找的全局std::map(不使用类),并认为我会尝试创建一个可以递归调用的 lambda,并通过引用捕获缓存/映射.我尝试了以下方法:

int davis_staircase_2(int n) {

    std::map<int, int> memo;

    //auto recurse = [&memo](int n) -> int {                    // attempt (1)
    //std::function<int(int)> recurse = [&memo](int n) -> int { // attempt (2)
    std::function<int(std::map<int, int>&, int)> recurse = [](std::map<int, int>& memo, int n) -> int { // attempt (3)
        if (n == 0) {
            return 1;
        } else if (n < 0) {
            return 0;
        }

        auto find = memo.find(n);
        if (find != memo.end()) {
            return find->second;
        }

        //int num_steps = recurse(n - 1) + recurse(n - 2) + recurse(n - 3); // attempt (1) or (2)
        int num_steps = recurse(memo, n - 1) + recurse(memo, n - 2) + recurse(memo, n - 3); // attempt (3)

        memo[n] = num_steps;

        return num_steps;
    };

    //return recurse(n); // attempt (1) or (2)
    return recurse(memo, n); // attempt (3)
}

我在上面交错了 3 次略有不同的尝试,但我无法编译。我想做的事,可能吗?

我在 MacOS 上使用 clang:

Apple LLVM version 10.0.0 (clang-1000.10.44.4)
Target: x86_64-apple-darwin18.2.0
Thread model: posix

【问题讨论】:

  • std::function&lt;int(std::map&lt;int, int&gt;&amp;, int)&gt; recurse = [](std::map&lt;int, int&gt;&amp; memo, int n) -&gt; int { -> auto recurse = [&amp;memo, &amp;recurse](std::map&lt;int, int&gt;&amp; memo, int n) -&gt; int {.
  • This post 会有所帮助。
  • 离题:这可以通过将问题表示为矩阵并通过平方使用取幂来解决,无需递归。这样您就可以将问题从o(n) 复杂度更改为o(log(n)) 复杂度。另一方面,由于n 的约束是如此之小&lt;36 这将是一个矫枉过正。

标签: c++ c++11 recursion lambda


【解决方案1】:

您忘记捕获recurse,因此您的代码可能是

std::function<int(int)> recurse = [&recurse, &memo](int n) -> int { // attempt (2)

std::function<int(int)> recurse = [&](int n) -> int { // attempt (2)

同理,对于// attempt (3)

std::function<int(std::map<int, int>&, int)> recurse = [&recurse](std::map<int, int>& memo, int n) -> int { // attempt (3)

// attempt (1) 无法按原样固定,因为recurse 的类型在定义之前就已使用。

要在没有std::function 的情况下执行此操作,您可以使用Y-combinator(需要 C++14,用于通用 lambda):

int davis_staircase_2(int n) {
    std::map<int, int> memo;
    auto recurse = [&memo](auto self, int n) -> int { // attempt (4)
        if (n == 0) {
            return 1;
        } else if (n < 0) {
            return 0;
        }

        auto find = memo.find(n);
        if (find != memo.end()) {
            return find->second;
        }

        int num_steps = self(self, n - 1) + self(self, n - 2) + self(self, n - 3); // attempt (4)

        memo[n] = num_steps;

        return num_steps;
    };
    return recurse(recurse, n); // attempt (4)
}

【讨论】:

    【解决方案2】:

    你不需要递归函数...

    int stepPerms(int n) {
    
      std::map<int, int> memoize;
    
      memoize[-2] = 0;
      memoize[-1] = 0;
      memoize[0] = 1;
    
     for(int i=1;i<=n;++i)
     {
       memoize[i] = memoize[i - 1] + memoize[i - 2] + memoize[i-3];
     }
    
     return memoize[n];
    }
    

    【讨论】:

    • 不需要map 也不需要,一个向量,甚至一个循环缓冲区就足够了。
    • 是的。但这会使代码有点难以阅读,无论如何......我只是想指出想法,而不是编写完美的代码:)
    • cprogrammer:感谢那个解决方案伙伴。我很想知道用于达成类似目标的思考过程。而且,还使用循环缓冲区@Jarod42
    【解决方案3】:

    您可以在没有类型擦除的情况下执行递归 lambda (std::function)。这就是使用通用 lambda 的方式:

    auto recurse = [](auto lambda) {
        return [lambda](auto&&... args) {
            return lambda(lambda, std::forward<decltype(args)>(args)...);
        };
    };
    
    auto my_recursive_lambda = recurse([](auto self, std::map<int, int>& memo, int n) {
        if (n == 0) {
            return 1;
        } else if (n < 0) {
            return 0;
        }
    
        auto find = memo.find(n);
        if (find != memo.end()) {
            return find->second;
        }
    
        int num_steps = self(self, memo, n - 1) + self(self, memo, n - 2) + self(self, memo, n - 3);
    
        memo[n] = num_steps;
    
        return num_steps;
    });
    
    my_recursive_lambda(memo, n); // magic!
    

    如果你真的需要这个c++11,你需要std::function:

    auto recurse = std::function<int(std::map<int, int>&, int)>{};
    
    recurse = [&recurse](std::map<int, int>& memo, int n) {
        // same as you tried.
    }
    

    或者如果你放弃方便,你可以手动滚动你的 lambda 类型:

    struct {
        auto operator()(std::map<int, int>& memo, int n) -> int {
            auto&& recurse = *this;
            if (n == 0) {
                return 1;
            } else if (n < 0) {
                return 0;
            }
    
            auto find = memo.find(n);
            if (find != memo.end()) {
                return find->second;
            }
    
            //int num_steps = recurse(n - 1) + recurse(n - 2) + recurse(n - 3); // attempt (1) or (2)
            int num_steps = recurse(memo, n - 1) + recurse(memo, n - 2) + recurse(memo, n - 3); // attempt (3)
    
            memo[n] = num_steps;
    
            return num_steps;
        }
    } recurse{};
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-01-10
      • 2022-01-08
      • 2010-10-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-25
      相关资源
      最近更新 更多