zoukankan      html  css  js  c++  java
  • C++标准 bind函数用法与C#简单实现

      在看C++标准程序库书中,看到bind1st,bind2nd及bind的用法,当时就有一种熟悉感,仔细想了下,是F#里提到的柯里化。下面是维基百科的解释:在计算机科学中,柯里化英语Currying),又译为卡瑞化加里化,是把接受多个参数函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

      下面来看一个简单的例子。

    void mult(int& a, int b)
    {
        cout << "a:" << a << " b:" << b << endl;
        a += b;
    }
    void test24()
    {
        using namespace std::placeholders;
        vector<int> list;
        int i = 0;
        generate_n(back_inserter(list), 10, [&i](){
            return i++;
        });
        for_each(list.begin(), list.end(), bind(mult, _1, 10));
        for_each(list.begin(), list.end(), bind(mult, 100, _1));
        copy(list.begin(), list.end(), ostream_iterator<int>(cout, " "));
    }
    bind用法

      

      在这,for_each最后接受一个void fun(int p)的函数做参数,p就是我们的每次遍历的数据,而在这我们用到mult,带有二个参数。在这我们就要用到柯里化,把mult转成前面的void fun(int p)的形式,下面我们看下相应函数如何工作。

      我们先来看下bind1st,这个相当于做了如下事。 f(a,b) -> f(a)(b).简单来说,就是把带二个参数的函数变成只带一个参数的函数的过程。bind2nd如上,类似f(a,b)->f(b)(a).而bind的用法更广,不限制个数,参数顺序和函数类型等。上面第一个for_each中bind的用法就相当于bind2nd,第二个就相当于bind1st.

      下面再来看个小例子:

    void test23()
    {
        using namespace std::placeholders;
        auto func = [](int x, string y){
            return to_string(x) + y;
        };
        auto f = bind(func, 1, _1);
        auto fs = bind(func, _1, "xx");
        auto fss = bind(func, 3, "xxx");
        auto fsss = bind(func, _2, _1);
    
        cout << f("x") << endl;
        cout << fs(2) << endl;
        cout << fss() << endl;
        cout << fsss("xxxx", 4) << endl;
    }
    C++ bind

      输出结果分别是1x,2xx,3xxx,4xxxx.在二个参数的情况下,bind的几种简单重组函数的方法。为了好理解与说明,我直接把对应F#里相应写法写出。

    let func x y = x.ToString() + y
    let f x = func 1 x
    let fs x = func x "xx"
    let fss = func 3 "xxx"
    let fsss x y = func y x
    
    [<EntryPoint>]
    let main argv = 
        printfn "%s" (f "x")
        printfn "%s" (fs 2)
        printfn "%s" (fss)
        printfn "%s" (fsss  "xxxx" 4)
          
        ignore(System.Console.Read())    
        0 // 返回整数退出代码
    F# bind

      F#因为本身就是FP语言,故相应在C++还需要调用外部函数相比,本身内部支持。

      如下是对应各变量类型:

      val func : x:'a -> y:string -> string
      val f : x:string -> string
      val fs : x:'a -> string
      val fss : string = "3xxx"
      val fsss : x:string -> y:'a -> string

      在这,我们把泛形a具体化成int类型,好做说明,func (int,string)->string.而f是func第一参数具体化后生成的新的函数,fs是第二个参数具体化后生成新的函数,其中fss略过,而fsss则是把原(int,string)->string类型函数变成(string,int)->string的类型函数。

      如果F#难理解,下面是C#版的bind方法,只是简单针对二个参数的函数情况下,希望这个有助大家理解。

        public class BindHelper
        {
            public static Func<T1, T> bind<T1, T2, T>(Func<T1, T2, T> fun, T2 t2)
            {
                return (t11) =>
                {
                    return fun(t11, t2);
                };
            }
    
            public static Func<T2, T> bind<T1, T2, T>(Func<T1, T2, T> fun, T1 t1)
            {
                return (t22) =>
                {
                    return fun(t1, t22);
                };
            }
    
            public static Func<T> bind<T1, T2, T>(Func<T1, T2, T> fun, T1 t1, T2 t2)
            {
                return () =>
                {
                    return fun(t1, t2);
                };
            }
    
            public static Func<T2, T1, T> bind<T1, T2, T>(Func<T1, T2, T> fun)
            {
                return (t22, t11) =>
                {
                    return fun(t11, t22);
                };
            }
    
            static void Main()
            {
                Func<int, string, string> func = (int x, string y) => { return x.ToString() + y; };
                var f = bind(func, 1);
                var fs = bind(func, "xx");
                var fss = bind(func, 3, "xxx");
                var fsss = bind(func);
    
                Console.WriteLine(f("x"));
                Console.WriteLine(fs(2));
                Console.WriteLine(fss());
                Console.WriteLine(fsss("xxxx", 4));
                Console.Read();
            }
        }
    C# bind

      这个应该是最好理解了,相应bind的重载方法在C#中列出如何实现。

      最后上文中float(*(*f)(float, float))(float)如何初始化还是没搞定,不过相应类似的可以正确初始化。也可以看下bind中带bind代表的方法与意义。

        //如何具体化.
        float(*(*f)(float, float))(float);
    
        auto fvv = function<function<float(float)>(float, float)>(f);
    
        auto fv = [](float f, float d){        
            return[](float c)
            {
                return c;
            };
        };
        
        using namespace std::placeholders;
        fvv = fv;
        //f = fv;
        auto x = bind(bind(fv, _1, _1)(4), _1)(6);
        auto xxx = fv(3, 4)(2.0f);
        auto yyy = fvv(3, 4)(2.0f);
        cout << x << endl;
    View Code

      PS:STL刚开始看,只能说C++的模板与泛形太强大了,相应模板方法可以用动态语言的方式写(声明有这元素,这元素能做啥,就和javascript一样),而编译时,根据调用相应模板方法得到正确的具体化方法就相当于运行结果。所以很多模板调用错误是在编译阶段指出来的。

      

  • 相关阅读:
    简单的进度条程序
    python装饰器
    冒泡,递归
    实现随机验证码
    三木运算,三元运算
    set()集合
    list()列表
    python3.5 Str类型与bytes类型转换
    说说编码与转义的区别
    Java 中字符编码相关的问题解析
  • 原文地址:https://www.cnblogs.com/zhouxin/p/4287257.html
Copyright © 2011-2022 走看看