这种疯狂是什么?

mot*_*oku 3 c gcc

我从未见过这样的事; 我似乎无法绕过它.这段代码甚至做了什么?它看起来非常华丽,我很确定这些东西在我的C书中没有任何描述.:(

union u;
typedef union u (*funcptr)();

union u {
  funcptr f;
  int i;
};

typedef union u $;

int main() {
  int printf(const char *, ...);

  $ fact =
      ($){.f = ({
            $ lambda($ n) {
              return ($){.i = n.i == 0 ? 1 : n.i * fact.f(($){.i = n.i - 1}).i};
            }
            lambda;
          })};

  $ make_adder = ($){.f = ({
                       $ lambda($ n) {
                         return ($){.f = ({
                                      $ lambda($ x) {
                                        return ($){.i = n.i + x.i};
                                      }
                                      lambda;
                                    })};
                       }
                       lambda;
                     })};

  $ add1 = make_adder.f(($){.i = 1});

  $ mul3 = ($){.f = ({
                 $ lambda($ n) { return ($){.i = n.i * 3}; }
                 lambda;
               })};

  $ compose = ($){
      .f = ({
        $ lambda($ f, $ g) {
          return ($){.f = ({
                       $ lambda($ n) {
                         return ($){.i = f.f(($){.i = g.f(($){.i = n.i}).i}).i};
                       }
                       lambda;
                     })};
        }
        lambda;
      })};

  $ mul3add1 = compose.f(mul3, add1);

  printf("%d\n", fact.f(($){.i = 5}).i);
  printf("%d\n", mul3.f(($){.i = add1.f(($){.i = 10}).i}).i);
  printf("%d\n", mul3add1.f(($){.i = 10}).i);
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

Leu*_*nko 5

此示例主要基于两个GCC扩展:嵌套函数语句表达式.

嵌套函数扩展允许你到另一个函数体中定义的功能.应用常规块作用域规则,因此嵌套函数在调用时可以访问外部函数的局部变量:

void outer(int x) {
    int inner(int y) {
        return x + y;
    }
    return inner(6);
}

...
int z = outer(4)' // z == 10
Run Code Online (Sandbox Code Playgroud)

语句表达扩展允许包裹一个C块语句(代码任何你通常将能够括号内放置:变量声明,for循环等),用于以值产生上下文.它看起来像括号中的块语句:

int foo(x) {
    return 5 + ({
        int y = 0;
        while (y < 10) ++y;
        x + y;
    });
}

...
int z = foo(6); // z == 20
Run Code Online (Sandbox Code Playgroud)

包装块中的最后一个语句提供值.所以它的工作方式与您想象的内联函数体非常相似.

通过组合使用这两个扩展,您可以定义一个可以访问周围范围变量的函数体,并在表达式中立即使用它,从而创建一种基本的lambda表达式.由于语句表达式可以包含任何语句,嵌套函数定义是语句,函数名称是值,因此语句表达式可以定义函数并立即将指向该函数的指针返回到周围的表达式:

int foo(int x) {
    int (*f)(int) = ({      // statement expression
        int nested(int y) { // statement 1: function definition
            return x + y;
        }
        nested;             // statement 2 (value-producing): function name
    });                     // f == nested

    return f(6); // return nested(6) == return x + 6
}
Run Code Online (Sandbox Code Playgroud)

该示例中的代码被向上进一步通过使用美元符号作为用于返回类型的缩短的标识符(修整此另一GCC扩展,更不用说重要的实施例的功能性).lambda在示例中不是关键字或宏(但美元应该使它看起来像一个),它只是在语句表达式范围内定义的函数名称(重复使用多次).C的范围嵌套规则意味着在更深的范围内重用相同的名称(嵌套的"lambdas")是完全可以的,特别是当没有期望使用该名称的身体代码用于任何其他目的时(lambdas通常是匿名的,所以函数预计不会"知道"他们实际上被称为 lambda.

如果您阅读嵌套函数的GCC文档,您会发现这种技术非常有限.嵌套函数在其包含框架的生命周期结束时到期.这意味着它们无法返回,并且它们无法真正有效地存储.它们可以通过指针传递到从包含期望普通函数指针的包含框架调用的其他函数中,因此它们仍然非常有用.但它们没有任何接近真正lambda的灵活性,它们接受它们所关闭的变量的所有权(共享或总取决于语言),并且可以作为真值传递到所有方向或存储供以后使用完全不相关的程序部分.即使你将它包装在很多辅助宏中,语法也相当笨拙.

C很可能会在下一版本的语言中获得真正的lambdas,目前称为C2x.您可以在此处阅读有关所提议表单的更多信息- 它看起来并不像这样(它复制了Objective-C中的匿名函数语法和语义).以这种方式创建的函数的生命周期可以超出其创建范围; 函数体是真正的表达式,不需要包含语句的hack; 并且函数本身是真正的匿名,没有像lambda需要的中间名称.


以上示例的C2x版本很可能看起来像这样:

#include <stdio.h>

int main(void) {
  typedef int (^ F)(int);

  __block F fact;  // needs to be mutable - block can't copy-capture
                   // its own variable before initializing it
  fact = ^(int n) {
    return n == 0 ? 1 : n * fact(n - 1);
  };

  F (^ make_adder)(int) = ^(int n) {
    return _Closure_copy(^(int x) { return n + x; });
  };

  F add1 = make_adder(1);

  F mul3 = ^(int n) { return n * 3; };

  F (^ compose)(F, F) = ^(F f, F g) {
    return _Closure_copy(^(int n) { return f(g(n)); });
  };

  F mul3add1 = compose(mul3, add1);

  printf("%d\n", fact(5));
  printf("%d\n", mul3(add1(10)));
  printf("%d\n", mul3add1(10));

  _Closure_free(add1);
  _Closure_free(mul3add1);

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

更简单,没有所有联盟的东西.

(您可以编译和现在运行铛该修改示例-使用-fblocks标志来启用拉姆达扩展,添加#include <Block.h>到文件的顶部,并更换_Closure_copy_Closure_free使用Block_copy,并Block_release分别.)