栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

Lambda 自递归调用

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Lambda 自递归调用

当接触到 C++ 之后,如果说它与 JS 相近的一个地方,那么便是看到了匿名函数,或者说 Lambda 表达式(后文简称 Lambda),只是 JS 当中不会这么去称呼。

为什么说痴迷这个东西,主要是 Lambda 可以捕获所在作用域当中的变量,而无需手动传递参数。虽然从某种角度上来说,显式传递更加符合一种编程风格。

然而不同的地方来了,先谈 JS,对于函数声明和 Lambda 来说,其实差别就在于声明的关键字和声明提升,其他是没有的。然而到了 C++ 这边,就不能这么玩了,最主要的区别就是:Lambda 自递归调用。

function main() {
    const dfs = (n) => {
        if (n === 0) {
            return;
        }
        console.log(n);
        dfs(n - 1);
    };
}
int main() {
    auto dfs = [](int n) {
        if (n == 0) {
            return;
        }
        cout << n << endl;
        // Error
        dfs(n - 1);
    };
    return 0;
}

若解释的话,在 C++ 当中这似乎是这样一个动作:先创建值,后又声明。于是无法达成完成这种操作。

那是否存在替代方案,当然,就是略显繁琐,但是可以达到目的:

int main() {  
    auto dfs = [](int n, auto dfs) {  
        if (n == 0) {  
            return;  
        }        
        cout << n << endl;  
        // OK  
        dfs(n - 1, dfs);  
    };  
    dfs(5, dfs);  
    return 0;  
}

由此延申两个问题:

  • 为什么函数可以递归调用
  • JS 为什么可以这么写
为什么函数可以递归调用

请注意,此处说的函数是普通函数,而不再是 Lambda。

首先了解一点,为什么 Lambda 不可以自递归,那就是先初始化值,再有的名称,于是无法在函数体内找到自己。

或许这时候就联想到了一个叫 this 的东西。

而普通函数可以找到自己,那是来自函数声明,通常存在头文件当中:

void dfs(int);  
  
void dfs(int) {  
    // ....  
}

可能想说,自己直接写的实现,就没有写声明。当然,编译器发现没有声明时,它会自己做好声明工作的。

这样的话,让人有了一个想法,那就是先完成 Lambda 声明,然后再调用如何?

看一段想法落实:

void dfs(int);  
  
void dfs(int) {  
    // ...
}  
  
int main() {  
    void (* dfsFn)(int);  
    // OK
    dfsFn = dfs;  
    return 0;  
}

一个指针函数没有毛病,再进一步,尝试创建 Lambda:

int main() {  
    void (* dfsFn)(int);  
    // OK
    dfsFn = [](int){  
        // ...
    };  
    return 0;  
}

好,开启捕获…失败了

int main() {  
    void (* dfsFn)(int);  
    // Error
    dfsFn = [&](int){  
        // ...
    };  
    return 0;  
}

先别谈调用,变量类型就完全对不上了,导致计划破产。

一个关键问题是,无法显式,将一个 Lambda,可捕获变量的类型写出来,而在 STL 算法当中,多数是直接使用泛型盖过。

JS 为什么可以这么写

先看 C++ 的一段表现,还是从 Lambda 开始:

int main() {  
    // Error
    auto print = [&]() {  
        cout << num << endl;  
    };  
    int num = 10;  
    return 0;  
}

别提使用了 Lambda,在声明时,使用到了一个,当时还未完成初始化的变量,于是直接就在编译阶段挂掉了,这很合理。

那么回头看 JS,就不用这么复杂了,直接在函数体内犯错即可:

function createErr() {
    console.log(num);
    const num = 10;
}
console.log(createErr)

由于 JS 不存在编译动作,于是,只要不调用函数,那么这个错误就不会产生,哪怕是输出了这个函数的内容。

回头看一眼 TS,增添了编译环节,这样的错误也就被扼杀掉了。

于是就好说了,那就是 JS 是执行到什么,就解释什么。于是,Lambda 创建过关了,虽然在创建的时候完全不对劲,但是,在使用时,只要 Lambda 能捕获到对应的变量,那么它就不会出现错误。

-END-

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/983506.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号