Ruby語言的類(class)所定義的任何方法(method)都能接受一個過程對象(proc)作為附加參數,在方法中只要使用yield運算式便能調用此過程對象。調用時此過程對象的參數由yield運算式指定,其傳回值作為yield運算式的值返回。
下面給出一個Ruby文檔中的具體例子。
def threeTimes<br /> yield<br /> yield<br /> yield<br />end<br />threeTimes { puts "Hello" }<br />#輸出結果<br />Hello<br />Hello<br />Hello<br />
上例中所定義的全域方法threeTimes三次使用yield運算式語句調用了所接受的過程對象,而實際調用所給出的過程對象的功能是列印Hello,因此結果便是列印三行Hello。
分析上例不難得出結論,Ruby中方法的過程對象本質上相當於回呼函數(callback),其功能與Java的Listener以及.NET的delegate相當。由於Ruby具有動態語言的特點,其過程對象的參數和傳回值類型不必像Listener和delegate那樣事先定義,因此這種回調可以稱之為泛回調(generic callback)。Ruby中的過程體可以存放在變數中,平時不活躍,只有在被調用時才被執行,因此其兼具懶函數(lazy function)的特點。
注意Ruby中的yield關鍵字所實現的功能與Python和C#的同名關鍵字不同,後兩者在將自身的參數返回調用者之前,先儲存函數現場,以備下次調用。此類功能一般被稱作協程(coroutine)。
試比較以下Python代碼
def f():<br /> yield "Hello"<br /> yield "Hello"<br /> yield "Hello"<br />g=f()<br />g.next();g.next();g.next()<br />#輸出結果<br />'Hello'<br />'Hello'<br />'Hello'<br />
下面給出一個較為複雜的例子(取自Ruby文檔),並用C++類比其功能。
Ruby代碼
class Array<br /> def inject(n)<br /> each { |value| n = yield(n, value) }<br /> n<br /> end<br /> def sum<br /> inject(0) { |n, value| n + value }<br /> end<br /> def product<br /> inject(1) { |n, value| n * value }<br /> end<br /> def find<br /> for i in 0...size<br /> value = self[i]<br /> return value if yield(value)<br /> end<br /> return nil<br /> end<br />end<br />[ 1, 2, 3, 4, 5 ].sum # 15<br />[ 1, 2, 3, 4, 5 ].product # 120<br />[ 1, 2, 3, 4, 5 ].find {|v| v*v > 10 } # 4<br />
C++代碼
#include <iostream><br />#include <boost/foreach.hpp><br />#include <boost/assign.hpp><br />#include <boost/function.hpp><br />#include <boost/spirit/phoenix.hpp><br />using namespace std;<br />using namespace boost;<br />using namespace assign;<br />using namespace phoenix;</p><p>template<typename T><br />struct Array<br />{<br /> typedef vector<T> self_t;<br /> typedef boost::function<T(T,T)> func_t1;<br /> typedef boost::function<bool(T)> func_t2;</p><p> Array(const self_t& arr) : m_arr(arr) {}</p><p> T inject(T n, func_t1 f){<br /> BOOST_FOREACH(T v, m_arr)<br /> n = f(n, v);<br /> return n;<br /> }<br /> T sum(){<br /> return inject(0, arg1 + arg2);<br /> }<br /> T product(){<br /> return inject(1, arg1 * arg2);<br /> }<br /> const T* find(func_t2 f){<br /> BOOST_FOREACH(T v, m_arr)<br /> if(f(v))<br /> return &v;<br /> throw NULL;<br /> }<br />private:<br /> const self_t& m_arr;<br />};</p><p>int main()<br />{<br /> vector<int> v;<br /> v += 1,2,3,4,5;<br /> Array<int> a(v);<br /> cout << a.sum() << endl;<br /> cout << a.product() << endl;<br /> cout << *a.find(arg1 * arg1 > 10) << endl;</p><p> return 0;<br />}<br />//15<br />//120<br />//4
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,則返回Null 物件nil。
C++部分代碼說明
- Ruby數組用標準庫的vector組件來類比。
- Ruby數組的遍曆用boost庫的foreach組件來類比。
- Ruby回呼函數用boost庫的function組件來類比。
- Ruby數組的初始化用boost庫的assign組件來類比。
- Ruby回呼函數的懶函數特性用boost庫的phoenix子庫來類比。boost庫的lambda子庫也能完成此功能,但屬於spirit子庫的phoenix子庫功能更為強大,其函數式編程功能遠超lambda子庫,值得推薦。