Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 模糊编译器';s lambda表达式翻译_C#_Performance_Compiler Construction_Lambda - Fatal编程技术网

C# 模糊编译器';s lambda表达式翻译

C# 模糊编译器';s lambda表达式翻译,c#,performance,compiler-construction,lambda,C#,Performance,Compiler Construction,Lambda,我学习了Y Combinator(使用c#5.0),并对这种方法感到非常惊讶: public static Func<T1, Func<T2, TOut>> Curry<T1, T2, TOut> ( this Func<T1, T2, TOut> f) { return a => b => f(a, b); } 公共静态函数(此函数) { 返回a=>b=>f(a,b); } 。。。已由编译器翻译为: public sta

我学习了Y Combinator(使用c#5.0),并对这种方法感到非常惊讶:

public static  Func<T1, Func<T2, TOut>> Curry<T1, T2, TOut> ( this Func<T1, T2, TOut> f)
{
    return a => b => f(a, b);
}
公共静态函数(此函数)
{
返回a=>b=>f(a,b);
}
。。。已由编译器翻译为:

public static Func<T1, Func<T2, TOut>> Curry<T1, T2, TOut>(this Func<T1, T2, TOut> f)
        {
            first<T1, T2, TOut> local = new first<T1, T2, TOut>();
            local.function = f;
            return new Func<T1, Func<T2, TOut>>(local.Curry);
        }
    private sealed class first<T1, T2, TOut>
    {
        private sealed class second
        {
            public first<T1, T2, TOut> ancestor;
            public T1 firstParameter;
            public TOut Curry(T2 secondParameter)
            {
                return ancestor.function(firstParameter, secondParameter);
            }
        }
        public Func<T1, T2, TOut> function;
        public Func<T2, TOut> Curry(T1 firstParameter)
        {
            second local = new second();
            local.ancestor = this;
            local.firstParameter = firstParameter;
            return new Func<T2, TOut>(local.Curry);
        }
    }
公共静态函数(此函数)
{
第一个本地=新的第一个();
局部函数=f;
返回新的Func(local.Curry);
}
私人密封头等舱
{
二等兵
{
公共始祖;
公共参数;
公开兜售咖喱(T2第二参数)
{
返回祖先函数(firstParameter,secondParameter);
}
}
公共职能;
公共本币(T1第一参数)
{
第二个本地=新的第二个();
local.祖先=这个;
local.firstParameter=firstParameter;
返回新的Func(local.Curry);
}
}
所以,当我们使用引用second.Curry的委托时,第二个类是嵌套的,第一个类不可用于垃圾收集。同时,我们在第一堂课上所需要的就是功能。也许我们可以将其复制(委托)到第二个类,然后收集第一个类?是的,我们也应该做第二类非嵌套的,但这似乎是可以的。据我所知,代理是“按值”复制的,因此我可以建议它相当慢,但同时我们复制firstParameter?!所以,也许任何人都可以解释,为什么编译器会做这些事情?) 我说的是这样的话:

private sealed class first<T1, T2, TOut>
{
        public Func<T1, T2, TOut> function;
        public Func<T2, TOut> Curry(T1 firstParameter)
        {
            second<T1, T2, TOut> local = new second<T1, T2, TOut>();
            local.function = function;
            local.firstParameter = firstParameter;
            return new Func<T2, TOut>(local.Curry);
        }
    }
    public sealed class second<T1, T2, TOut>
    {
        public T1 firstParameter;
        public Func<T1, T2, TOut> function;
        public TOut Curry(T2 secondParameter)
        {
            return function(firstParameter, secondParameter);
        }
    } 
私有密封类优先
{
公共职能;
公共本币(T1第一参数)
{
第二个本地=新的第二个();
local.function=函数;
local.firstParameter=firstParameter;
返回新的Func(local.Curry);
}
}
公共密封二级
{
公共参数;
公共职能;
公开兜售咖喱(T2第二参数)
{
返回函数(第一个参数,第二个参数);
}
} 

您已经使用了两次lambda操作符,因此您将获得两个匿名委托,其中捕获的变量被提升为状态类型

内部lambda的state类型通过引用保存外部lambda的state类型的原因是在C#中捕获是这样工作的:捕获变量,而不是它的值

其他一些语言(例如C++11 lambdas)有替代语法来表示按值捕获与按引用捕获。C#没有,只是通过引用捕获所有内容。支持按值语义并不是一个很好的理由,因为垃圾收集避免了在没有按值捕获模式的情况下C++11中可能存在的生存期问题



C#编译器是否注意到变量从未写入,因此按值捕获与按引用捕获无法区分?可能吧,但这是编译器中的额外逻辑,设计和代码的额外审查,额外测试。所有这些都是为了在内存占用和局部性方面实现一个小的、几乎微不足道的改进。这显然不符合成本效益标准,你可以在Eric Lippert的许多博客文章中找到讨论的内容。

这个问题很难理解。让我澄清一下。您的建议是编译器可以生成

public static Func<T1, Func<T2, TOut>> Curry<T1, T2, TOut>(this Func<T1, T2, TOut> f)
{
    first<T1, T2, TOut> local = new first<T1, T2, TOut>();
    local.function = f;
    return new Func<T1, Func<T2, TOut>>(local.Curry);
}
private sealed class first<T1, T2, TOut>
{
    private sealed class second
    {
        //public first<T1, T2, TOut> ancestor;
        public Func<T1, T2, TOut> function;
        public T1 firstParameter;
        public TOut Curry(T2 secondParameter)
        {
            return /*ancestor.*/function(firstParameter, secondParameter);
        }
    }
    // public Func<T1, T2, TOut> function;
    public Func<T2, TOut> Curry(T1 firstParameter)
    {
        second local = new second();
        // local.ancestor = this;
        local.function = function;
        local.firstParameter = firstParameter;
        return new Func<T2, TOut>(local.Curry);
    }
}
然后无法收集第一个的实例。该实例不再可以通过
makeAdder
访问,但可以通过
addFive
访问

在建议的codegen中,可以在此场景中收集
first
,因为无法通过
addFive
访问实例

在这个特定场景中,优化是合法的,这是正确的。然而,由于Ben Voigt在其回答中描述的原因,一般来说,这是不合法的如果
f
Curry
内部发生变异,则
local.function
必须发生变异。
但是
local
在执行外部委托之前无法访问
second
的实例

C#编译器团队可以选择进行您确定的优化,但到目前为止,这一微小的节省根本不值得费心


我们考虑让Roslyn按照您描述的路线进行优化;也就是说,如果已知外部变量没有变异,则更积极地获取其值。我不知道这一优化是否让Roslyn受益。

你需要的不仅仅是函数,你正在关闭参数
a
,所以你需要存储它。@SergeyBerezovskiy:Currying允许你将两个参数的函数转换为一个参数的函数,返回一个参数的函数。例如,假设您有一个函数
add
,即
(a,b)=>a+b
。通过Curry,您有一个函数
makeAdder
,该函数接受一个数字并返回一个将该数字与数字相加的函数。@SergeyBerezovskiy看起来像是代码的标准实现,可能看起来像
var add5=Add.Curry(5)
。Alexei,第二个类有祖先字段,所以我们的第一个类在第二个存在和第二个存在之前不能被收集,因为我们的委托在第二个存在上有引用。Curry@AlexeiLevenkov:我想OP是在指出,
second
只使用
祖先
获取
函数
,那么,为什么不让
第二个
保留
功能
,而不是保留
祖先
?他的想法是可以更早地收集
祖先
。而且,JIT编译器在执行时可能会进一步简化此过程。@Grzenio:我认为合并类型不是允许JIT进行的优化之一。无论多么困难
Func<int, int, int> adder = (x, y)=>x+y;
Func<int, Func<int, int>> makeAdder = adder.Curry();
Func<int, int> addFive = makeAdder(5);
makeAdder = null;