Ruby语言的类(class)所定义的任何方法(method)都能接受一个过程对象(proc)作为附加参数,在方法中只要使用yield表达式便能调用此过程对象。调用时此过程对象的参数由yield表达式指定,其返回值作为yield表达式的值返回。
下面给出一个Ruby文档中的具体例子。
下面给出一个Ruby文档中的具体例子。
上例中所定义的全局方法threeTimes三次使用yield表达式语句调用了所接受的过程对象,而实际调用所给出的过程对象的功能是打印Hello,因此结果便是打印三行Hello。
分析上例不难得出结论,Ruby中方法的过程对象本质上相当于回调函数(callback),其功能与Java的Listener以及.NET的delegate相当。由于Ruby具有动态语言的特点,其过程对象的参数和返回值类型不必像Listener和delegate那样事先定义,因此这种回调可以称之为泛回调(generic callback)。Ruby中的过程体可以存放在变量中,平时不活跃,只有在被调用时才被执行,因此其兼具懒函数(lazy function)的特点。
注意Ruby中的yield关键字所实现的功能与Python和C#的同名关键字不同,后两者在将自身的参数返回调用者之前,先保存函数现场,以备下次调用。此类功能一般被称作协程(coroutine)。
试比较以下Python代码
分析上例不难得出结论,Ruby中方法的过程对象本质上相当于回调函数(callback),其功能与Java的Listener以及.NET的delegate相当。由于Ruby具有动态语言的特点,其过程对象的参数和返回值类型不必像Listener和delegate那样事先定义,因此这种回调可以称之为泛回调(generic callback)。Ruby中的过程体可以存放在变量中,平时不活跃,只有在被调用时才被执行,因此其兼具懒函数(lazy function)的特点。
注意Ruby中的yield关键字所实现的功能与Python和C#的同名关键字不同,后两者在将自身的参数返回调用者之前,先保存函数现场,以备下次调用。此类功能一般被称作协程(coroutine)。
试比较以下Python代码
下面给出一个较为复杂的例子(取自Ruby文档),并用C++模拟其功能。
Ruby代码
Ruby代码
C++代码
Ruby部分代码说明
- Array类为Ruby内置数组类型,与Python的list类型类似,其成员类型不受限制
- Array类的each方法遍历所有数组成员,遍历时将数组成员作为参数依次调用其回调函数。
- Array类的inject方法为sum方法和product方法的辅助方法,接受参数n作为初始值。该方法首先调用each方法遍历所有数组成员,遍历时将数组成员value与自身的参数n作为参数调用回调函数,并将每次调用的返回值保存在变量n中。最后返回变量n的值。
- Array类的sum方法调用inject方法实现求和功能,其给予inject方法的初始值为0,回调函数的功能为返回数组成员value与参数n之和。
- Array类的product方法调用inject方法实现求积功能,其给予inject方法的初始值为1,回调函数的功能为返回数组成员value与参数n之积。
- Array类的find方法遍历所有数组成员,遍历时将数组成员作为参数依次调用其回调函数,检查回调函数的值,如返回值为true,则中止遍历,返回该成员。如果没有一次调用返回true,则返回空对象nil。
C++部分代码说明
- Ruby数组用标准库的vector组件来模拟。
- Ruby数组的遍历用boost库的foreach组件来模拟。
- Ruby回调函数用boost库的function组件来模拟。
- Ruby数组的初始化用boost库的assign组件来模拟。
- Ruby回调函数的懒函数特性用boost库的phoenix子库来模拟。boost库的lambda子库也能完成此功能,但属于spirit子库的phoenix子库功能更为强大,其函数式编程功能远超lambda子库,值得推荐。