文档章节

C++标准 bind函数用法与C#简单实现

 天天不在
发布于 2015/02/13 14:19
字数 1182
阅读 19
收藏 0

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

  

© 著作权归作者所有

共有 人打赏支持
粉丝 0
博文 1
码字总数 1182
作品 0
万州
[开源世界]从自动导出动态链接库接口看C++的缺点

自动导出动态链接库接口在C++编程中绝对是一件烦人的事情,因为你不得不大量的重复以下几个步骤: 1.加载动态链接库 2.定义导出函数指针定义 3.定义导出函数指针变量 4.从动态链接库中导出函...

梁欢
2013/10/21
0
2
[C/C++]属性的秘密——C++仿C#的属性实作

一直以来,我都想为C++引入C#里面定义的属性(Property),我尝试了几次: [C/C++]一个实现反射和事件绑定的例子 [C/C++]一个实现反射和事件绑定的例子 (增强版) [C/C++]模仿C#实作C++版属性...

梁欢
2013/11/10
0
0
C++ 跨平台多功能扩展库 - cpgf

cpgf 可为C++代码增加反射(元数据)、序列化、以及Luau和Python脚本绑定等功能,且不依赖任何外部工具。 cpgf 的目标是扩展C++语言,其中包含许多易于使用的功能,这些功能仅在其他更高级别...

wqking
09/17
0
0
C++中的 .h 和 .cpp 区别详解

在C++编程过程中,随着项目的越来越大,代码也会越来越多,并且难以管理和分析。于是,在C++中就要分出了头(.h)文件和实现(.cpp)文件,并且也有了Package的概念。 对于以C起步,C#作为“母语...

刘小米
2014/09/18
0
0
C++基础教程之C/C++区别

C/C++基础教程之C/C++区别 这是C++教程得第一步,后续会持续更新哦!欢迎新手(具有C基础),老鸟可绕道,可指导。 C++标准输入和输出 C++标准输入和输出分别是cin和cout,用法非常简单 cin>>n...

这个人很懒什么都没留下
09/01
0
0

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周四乱弹 —— 毒蛇当辣条

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @ 达尔文:分享花澤香菜/前野智昭/小野大輔/井上喜久子的单曲《ミッション! 健?康?第?イチ》 《ミッション! 健?康?第?イチ》- 花澤香菜/前野智...

小小编辑
46分钟前
4
0
java -jar运行内存设置

java -Xms64m #JVM启动时的初始堆大小 -Xmx128m #最大堆大小 -Xmn64m #年轻代的大小,其余的空间是老年代 -XX:MaxMetaspaceSize=128m # -XX:CompressedClassSpaceSize=6...

李玉长
56分钟前
1
0
Spring | 手把手教你SSM最优雅的整合方式

HEY 本节主要内容为:基于Spring从0到1搭建一个web工程,适合初学者,Java初级开发者。欢迎与我交流。 MODULE 新建一个Maven工程。 不论你是什么工具,选这个就可以了,然后next,直至finis...

冯文议
今天
1
0
RxJS的另外四种实现方式(四)——性能最高的库(续)

接上一篇RxJS的另外四种实现方式(三)——性能最高的库 上一篇文章我展示了这个最高性能库的实现方法。下面我介绍一下这个性能提升的秘密。 首先,为了弄清楚Most库究竟为何如此快,我必须借...

一个灰
今天
1
0
麒麟AI首席科学家现世

8月31日,华为发布了新一代顶级人工智能手机芯片麒麟980,成为全球首款7nm工艺手机芯片,AI方面也实现飞跃,支持人脸识别、物体识别、物体检测、图像分割、智能翻译等。 虽然如今人人都在热议...

问题终结者
昨天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部