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一样),而编译时,根据调用相应模板方法得到正确的具体化方法就相当于运行结果。所以很多模板调用错误是在编译阶段指出来的。

      

  • 相关阅读:
    HDU 1525
    kmp模板
    hdu 4616 Game(树形DP)
    hdu 4619 Warm up 2(并查集活用)
    hdu 4614 Vases and Flowers(线段树加二分查找)
    Codeforces 400D Dima and Bacteria(并查集最短路)
    poj 2823 Sliding Window (单调队列)
    hdu 2196 Computer(树形dp)
    hdu 4604 Deque
    最短路径
  • 原文地址:https://www.cnblogs.com/zhouxin/p/4287257.html
Copyright © 2011-2022 走看看