zoukankan      html  css  js  c++  java
  • 条件表达式的短路求值与函数的延迟求值

    延迟求值是 .NET的一个很重要的特性,在LISP语言,这个特性是依靠宏来完成的,在C,C++,可以通过函数指针来完成,而在.NET,它是靠委托来完成的。如果不明白什么是延迟求值的同学,我们先看看下面的一段代码:

          static void TestDelayFunction()
            {
                TestDelayFunton1(true,trueFun3);
            }
    
            static void TestDelayFunton1(bool flag , Func<bool> fun  )
            {
                if(flag)
                   fun();
            }

    在方法  TestDelayFunton1 中,函数型参数 fun 是否求值,取决于第一个参数  flag,如果它的值为false,那么函数 fun 是永远都不会被求值的,所以,这里函数 fun的求值被推迟到了方法TestDelayFunton1 的内部,而不是在参数计算的时候。

    延迟求值很有用,它可以避免我们无谓的计算,比如上面的例子,这样可以节省计算成本,假如 fun的求值很耗时的话。

    我们注意这一段代码:

    if(flag)
    
       fun();

    其实它等价于一个逻辑表达式:

    bool result= flag && fun();

    在这个表达式中,fun() 函数是否求值,取决于变量 flag,这个功能叫做“短路”判断,“条件短路”功能正好实现了我们的“延迟求值”的功能,因此,我们可以得到如下推论:

    任何时候一个函数fun如果需要延迟求值,那么都可以表示成 一个条件表达式:

    (Test() && fun())

    所以,前面的2个函数,本质上可以改写成下面的一个函数:

          static void TestDelayFunton2(bool flag)
            {
                bool result = flag && trueFun3();
            }

    它将  TestDelayFunton1(true,trueFun3); 的形式调用,转换成了上面的一个函数调用。

    当然,要让这种调用变得可用,我们还需要解决一个问题,就是函数 fun()的类型并不是 bool类型,这个问题处理很简单,将函数再包装下即可:

    bool WarpFunction()
    {
      fun();
      return true;
    }

    之后的调用将是这个样子的:

    (Test() && WarpFunction())

    对于本例,它其实等价于:

    (flag && trueFun3())

    如果是“聪明”的编译器,它是可以完成上面的转换的,下面给出一个完整的代码图片,这样你能够看得更清楚:

    上面被标记的部分的2个函数,等价于下面这一个函数,也就是说,TestDelayFunton1 的调用变换成了 TestDelayFunton2的调用。

    如果你对上面的这个过程还是不太明白,那么我们看看下面这个例子:

     static bool trueFun1()
            {
                Console.WriteLine("call fun 1");
                return true;
            }
    
            static bool falseFun2()
            {
                Console.WriteLine("call fun 2");
                return false;
            }
    
            static bool trueFun3()
            {
                Console.WriteLine("call fun 3");
                return true;
            }

    执行下面的代码,trueFun3都会被执行么?

    if (trueFun1() && falseFun2() && (trueFun3()))
    { 
                
    }
     Console.WriteLine();
    if (trueFun1() || falseFun2() || trueFun3())
    {
    
    }

    假如你非常理解C#的“条件短路”特性,相信答案很快就出来了。

    阅读完本文,你可能会问如此奇淫巧技,有何作用?

    如果你深入研究.NET的委托,就会明白委托调用其实是将一个函数用对象进行包装,.NET自动为你生成了很多代码,性能上必然有所损耗,假如你在某些地方需要性能极致的代码,那么本文这个技巧一定可以帮助你,假如你还能够写出一个这种转换的编译器来,恭喜你,未来的大神就是你了!

  • 相关阅读:
    LINQ To SQL: Eager Loading
    返回JSon格式数据
    Tips
    Easyui的DateBox日期格式化
    jquery treeview 展开指定节点,选中指定节点
    jquery treeview 功能参数
    Javascript 中 ShowModalDialog 的使用方法
    GetDlgItem用法
    20个开源项目托管站点推荐
    DLINQ(十): 分层构架的例子
  • 原文地址:https://www.cnblogs.com/bluedoctor/p/5031998.html
Copyright © 2011-2022 走看看