الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

Function Object Adapter

بدأه Wajdy Essam في 17 نوفمبر 2009 · 3 رد · 1,479 مشاهدة · في لغة C و ++C
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكــم ورحمـة الله وبركاتــه ،،

في عالم البرمجة بشكل عام عندما يتم ذكر المصطلح Adapter فهذا يعني أن لدينا implementation ما ونريد التعديل قليلا على هذا الimplementation سواء بمنع مجموعه من الدوال أو باضافه دوال جديدة ، وهنا سنقدم للمستخدم كلاس جديد Wrapper Class بمجموعه من الدوال والمستخدم لن يعلم شيئا عن الUnderlaying implementation ، وهنا تكمن الفائده في أمكانية تغيير هذه الlower data structure بدون تغيير اي سطر في كود المستخدم .

أشهر استخدامات نمط الAdapter في سي++ هو في الSTL ، حيث نجد أن هناك الAdapter Container مثل الstack و الqueue والتي تستخدم كل منها (list or deque ) في الوضع الإفتراضي ، ولكن طبيعة مكتبة الSTL المرنة تمكنك من تغيير الunderlaying Container بكل سهوله كما تريد ، وذلك بفضل الtemplate ، يمكنك مراجعه مقال الأخ خالد الشايع ( نظرة حول مكتبات الـ Data Structre في ++C ) وهو مقال رائع بلا أدنى شك.

وكما ان هناك Adapter للكلاسات في اللغه ، فهناك Adapter للدوال ، ولكنها تختلف قليلا في الفكرة حيث تكمن مهمه الFunctor Adapter في ربط عدة دوال مع بعضهم ليتم تحقيق هدف معين . وقبل أن نتناول هذه الAdapter سوف نتناول بعضا من الدوال في الSTL ثم نشرح المشكلة التي تحلها هذه الAdapter حتى ندرك تماما متى نستخدمها وهل يمكن أن نتجاهلها أم لأ.

أحد أهم الخوارزميات في الSTL هي خوارزميات البحث find و find_if واللتان تبحثان عن المفتاح في الContainer وترجع مؤشر iterator لأول عنصر كان مشابه للمفتاح ،، المثال التالي يوضح ذلك :

// find and find_if exmaple

#include <iostream>
#include <vector>		
#include <algorithm>	
#include <numeric>		
using namespace std;

bool isPass (int degree) { return (degree >= 50 ); }

int main(int arcg , char* argv[]) {
	vector<int> v1;
	v1.push_back(10); v1.push_back(56); v1.push_back(23); v1.push_back(12);

	// print vector
	cout << "content: "; copy(v1.begin(),v1.end(),ostream_iterator<int>(cout," ") ); cout << endl;

	// search for key using find
	int key = 23;
	vector<int>::iterator itr = find(v1.begin(), v1.end(), key);
	if ( itr != v1.end() ) 
		cout << "find key at position: " << (itr - v1.begin()) << endl;
	else
		cout << "cannot find key ." << endl;

	// search for first passing degree ( >= 50 )
	itr = find_if(v1.begin(),v1.end(), isPass);
	if ( itr != v1.end() ) 
		cout << "find passing degree at position: " << (itr - v1.begin()) << endl;
	else
		cout << "cannot find passing degree ." << endl;

	return (0);
}

بعد النظر للمثال فقد يبدوا هذا السطر محيرا قليلا :

cout << "content: "; copy(v1.begin(),v1.end(),ostream_iterator<int>(cout," ") ); cout << endl;

الجمله التي في الوسط هي عباره عن استدعاء للدالة copy في STL تقوم هذه الدالة بأخذ عنصر عنصر من الContainer وهو في هذه الحالة v1 الى الDestination وهو عباره عن iterator يأخذ العنصر ويقوم بطباعته ثم طباعه مسافه بعد ذلك ،، سوف نستخدم هذا السطر لطباعه اي Sequentail Container ، ويمكن اذا لم يعجبك أن تطبع بالطريقة التي تريد ، كما يلي :

for ( vector<int>::iterator itr=v1.begin(); itr != v1.end(); ++itr)
		cout << *itr << " ";

أو :

// function defnition
void myPrint (int x) { cout << x << " "; }

// in main function
for_each ( v1.begin() , v1.end() , &myPrint);

الدالة for_each أيضا هي من دوال الSTL تأخذ عنصر عنصر الى الCallback function والتي تقوم بطباعه العنصر ،،

الى هذه اللحظه سوف نجد أن هناك الكثير من الدوال في الSTL تتعامل مع أي دوال Callback Function ويمكنك أن تصنع دوال هكذه عن طريق وضع مؤشر للدالة كما في for_each أو أن تعيد تعريف المعامل ( ) في الكائن الذي تريده أن يعمل كCallback .

نرجع للمثال السابق (الأول) ، بعد سطر الطباعه سنجد أننا بحثنا عن المفتاح أولا باستخدام find ، وهي ترجع مؤشر يساوي الخانه الأخيره من الcontainer (تذكر أن الخانه الأخيره هي one-past أي أنها لا تؤشر لشيئ ) .. بعد ذلك استخدمنا الدالة الثانية find_if وسنجد أن هذه الدالة (وكل دوال STL التي تنتهي بif) سوف تتطلب Callback funcation يرجع قيمه منطقية boolean فاذا كان أحد العناصر مساويا لذلك ، أرجعت الدالة true وهنا سوف تتوقف الدالة find_if وترجع العنصر الصحيح . (الدالتين find و find_if ترجع قيمه واحده فقط ، اذا أردت يمكن أن تكتب دالة مشابه ترجع جميع العناصر وجعلها STL-Compitable بحيث تستخدمها مع أي container ) .

الى هنا سنجد أن الCallback يعمل جيدا ، ولكن ،، هناك Functor جاهز في سي++ للقيام بالعديد من المهام ، فلماذا لا نستخدمها بدلا من اعاده العجلة من جديد ؟

الFunctor الموجود في سي++ هو للعمليات الحسابية جمع وطرح وقسمه ... ، ولعمليات المقارنه (أكبر أصغر يساوي ..) وأيضا للعمليات المنطقية and or not .

المثال التالي يبين استخدام هذه الكائنات:

// Pre-defined Functor

//Arithmetics Functor:
//---------------------
//Binary operator : plus , minus , multiplies , divides , modules
//Unary operator : negate

//comparsion Functor:
//Binary operator : less, equal_to , not_equal_to, greater, less_equal, greater_equal

//Logical Functor :
//logical_not, logical_and , logical_or

#include <iostream>
#include <functional>
using namespace std;

#define print(x) cout<<#x << " : " << x << endl;

int main (int argc , char* argv[]) {
	plus<int> p;

	int sum = p(2,45);
	print(sum);

	multiplies<int> m;
	int x = m(sum,3);
	print(x);

	less<int> le;
	bool rslt = le(900,3);
	print(rslt);

	greater<int> gr;
	rslt = gr(900,3);
	print(rslt);

	return (0);
}

ال#define في المثال اعلاه هي تقوم بطباعه اسم المتغير وقيمته وهي مفيده في البرامج الصغيره ،، تسمى Stringizing وليس لها دخل طبعا بSTL فهي موجودة من أيام لغه السي .. المثال تقريبا واضح ، وبالرغم من ذلك فإن استخدام هذه الكائنات بهذا الشكل أمر لا فائده مرجوه منه ، ولكن في بعض الأحيان قد تضطر الى استخدامها فمثلا في الpriority_queue معامل المقارنة الإفتراضي هو less فاذا أردت تغيير ذلك فيمكنك أن تستخدم مثلا greater ، وهنا سوف تكون طريقة الإنشاء بهذا الشكل :

priority_queue<int, vector<int>, greater<int> > myQueue

أعلاه اضطررنا أن نحدد الContainer المستخدم بسبب أنه في حال أردنا تغيير الdefualt argument فيجب أن نغير كل ما قبله اذا كان هناك .

بعد كل هذه المقدمة الطويلة نعود لمثالنا find_if ، وهنا هذه المرة نريد استخدام الCallback الموجوده مثل greater أو less ، فاذا كتبت العباره :

vector<int>:: iterator itr = find_if(v1.begin(),v1.end(), greater<int>());

فسوف تحصل على رسالة خطأ مشفره :) خلاصتها أن الكائن greater يحتاج لمعاملين ، وأن الدالة find_if ترسل عنصر واحد في كل مرة ،، اذا ما الحل ؟ هل هناك طريقة تمكننا من جعل الكائن graeter يستقبل عنصرين بدون التأثير على find_if ؟؟

نعم وهذه هي وظيفة الAdapter أو الFunctional Compsition ، ومرحبا بك في عالم التعقيد :) ..

لنبدأ بأول Adapter وهو bind2nd ، وسنستخدمه لحل المشكلة السابقة ، وبالتالي برنامجنا سوف يصبح :

// find_if exmaple

#include <iostream>
#include <vector>		
#include <algorithm>	
#include <numeric>		
using namespace std;

int main(int arcg , char* argv[]) {
	vector<int> v1;
	v1.push_back(10); v1.push_back(56); v1.push_back(23); v1.push_back(12);

	// print vector
	cout << "content: "; copy(v1.begin(),v1.end(),ostream_iterator<int>(cout," ") ); cout << endl;

	// search for first passing degree ( >= 50 )
	vector<int>:: iterator itr = find_if(v1.begin(),v1.end(), bind2nd(greater_equal<int>(),50));
	if ( itr != v1.end() ) 
		cout << "find passing degree at position: " << (itr - v1.begin()) << endl;
	else
		cout << "cannot find passing degree ." << endl;

	return (0);
}

وظيفة الbind2nd هي اضافة الرقم 50 للمعامل الثاني لgreater_equal وهكذا سيعمل المثال ويتم ارجاع القيمة الصحيحه ، هناك bind1nd وهي اضافة الرقم الفلاني للمعامل الأول (لاحظ 1 في اسم الدالة للإشاره الى المعامل الأول ) .

الAdapter الأخر وهو not1 وهو لنفي النتيجه بالكامل ، فاذا أردنا في المثال السابق أن نرجع أول عنصر هو ليس بأكبر من أو يساوي 50 ، فيكون ذلك كما يلي (لن نضيف المثال بالكامل ، فقط سطر الfind_if ) :

vector<int>:: iterator itr = find_if(v1.begin(),v1.end(), not1(bind2nd(greater_equal<int>(),50)) );

هكذا سيعود بأول نتيجه أقل من 50 ، بالطبع يمكن استخدام الكائن less بدلا من ذلك :

vector<int>:: iterator itr = find_if(v1.begin(),v1.end(), bind2nd(less<int>(),50));

مثلا ، اذا أردت جمع 100 لكل عنصر :

transform(v1.begin(), v1.end(), v1.begin(),bind2nd(plus<int>(), 100));

مثلا أردت حصر قيمه أي عنصر بين 0 الى 100 ، يمكنك أن تستخدم :

replace_if(myVector.begin(), myVector.end(), bind2nd(less<int>(), 0), 0);
replace_if(myVector.begin(), myVector.end(), bind2nd(greater<int>(), 100),100);

الى هنا كان الAdapter يتعامل مع الكائنات الجاهزة Functor مثل less أو greater ، لكن ماذا اذا أردنا أن نستخدم هذه الAdapter مع الدوال العاديه أو حتى الكائنات التي تعرف () ؟ هذا ما سنتناوله الأن ..

استخدام الAdapter مع الدوال العادية :

-----------------------------------------

أولا هذه الAdapter تتطلب بعض من الtypedef المعرفة في الكائن الذي تعمل عليه ، لذلك اذا أردنا أن نستخدم هذه الAdapter مع الدوال العاديه يجب أن نستخدم ptr_func والذي مهمته تحويل مؤشر الدالة الى طريقة يمكن أن تستخدمه الدوال bind2nd و not1 ، لنشاهد المثال :

مثلا لدينا string ونريد معرفه هل هذا النص هو عدد أم لأ :

// isNumber.cpp

#include <iostream>
#include <cctype>	// for isdigit
#include <algorithm>
#include <functional>
using namespace std;

bool isNumber (const string& s) {
	string::const_iterator itr= find_if(s.begin(),s.end(),::isdigit);
	if ( itr == s.end() )
		return false;
	else
		return true;
}

int main (int argc, char* argv[]){
	string s = "42566";
	string s1= "ahemd";

	cout << isNumber(s) << endl;
	cout << isNumber(s1) << endl;

	return (0);
}

المثال جيد ، ولكن لنفرض أننا نريد أول حرف ليس رقم ، بالتالي سنحتاج نفي العباره بnot1 ، وكما ذكرنا أن الدوال العادية لا يمكن استخدامها مع هذه الAdapter الا عند اضافتها ل ptr_fun ، وهنا سوف يكون التغيير في الدالة isNumber كما يلي :

bool isNumber (const string& s) {
	string::const_iterator itr= find_if(s.begin(),s.end(), not1( ptr_fun(::isdigit) ) );
	if ( itr == s.end() )
		return true;
	else
		return false;
}

استخدام الAdapter مع الكائنات التي تعرف () :

---------------------------------------------------

أيضا اذا أردنا أن نستخدم الكائنات التي تعيد تعريف () مع هذه الAdapter ، فإنه يجب تعريف بضعه typedef أو الطريق الأسهل وهو الوراثه من أحدى الكائنين unary_function أو binary_function على حسب الدالة هل تأخذ وسيط أو اثنين ، كما يبين ذلك المثال :

// isNumber.cpp

#include <iostream>
#include <cctype>	// for isdigit
#include <algorithm>
#include <functional>
using namespace std;

class MyIsDigit : public unary_function<char,bool>  {
	public:
		bool operator() (char c) const { return (::isdigit(c) ); }
};

bool isNumber (const string& s) {
	string::const_iterator itr= find_if(s.begin(),s.end(), not1( MyIsDigit() ) );
	if ( itr == s.end() )
		return true;
	else
		return false;
}

int main (int argc, char* argv[]){
	string s = "42566";
	string s1= "ahemd";

	cout << isNumber(s) << endl;
	cout << isNumber(s1) << endl;

	return (0);
}

قبل أن تنتهي لنتعرف على Adapter أخر وظيفته جعل دالة داخل الكلاس تعمل كCallback ، وهنا في هذه الحالة سوف نستخدم mem_fun_ref اذا كان الcontainer من كائنات عادية ، أما اذا كان الContainer من مؤشرات فسوف نستخدم mem_fun .

مثلا لدينا vector of string ونريد حذف أي string فارغ من هذا الvector ، نشاهد المثال :

// remove empty string

#include <iostream>
#include <vector>		// vector
#include <algorithm>	// for find
#include <numeric>		// ostream_iterator
using namespace std;

void removeStrings (vector<string>& strings);

int main (int argc , char* argv[]){
	vector<string> strings;
	strings.push_back("ahmed");strings.push_back("");strings.push_back("");strings.push_back("ali");

	cout << "Before Remove : " << endl;
	copy(strings.begin(),strings.end(),ostream_iterator<string>(cout,"\n"));

	removeStrings(strings);

	cout << endl << "After Remove : " << endl;
	copy(strings.begin(),strings.end(),ostream_iterator<string>(cout,"\n"));

	return (0);
}

void removeStrings (vector<string>& strings) {
	vector<string>::iterator itr=remove_if(strings.begin(),strings.end(),mem_fun_ref( &string::empty ) );
	strings.erase(itr,strings.end());	// erase removing elements
}

لاحظ تم استخدام mem_fun_ref وهي بمثابة جعل الدالة empty تعمل كcallback للدالة remove_if .

أخيرا وليس أخر ، فإن boost قدمت المكتبة bind للقيام بنفس مهمه الadapter في STL ، ولكنها أسهل وأيسر استخداما ، فكما لاحظنا أنه لجعل الدوال داخل الكلاس تعمل ك callback قد نستخدم mem_fun_ref أو mem_fun (وذلك حسب محتوي الcontainer هل يتعامل مع الvalue أو الpointer) ولكن في boost.bind فهي تتعامل مع الحالتين من خلال داله واحده boost::bind .

أرجوا أن يفيد الموضوع من قرر استخدام الadapter في برامجه :) .

بالتوفيق ،

تم تعديل هذه المشاركة بواسطة Wajdy Essam في 17 نوفمبر 2009 في 21:15

2

http://informatic-ar.com منصة تعليمية عربية في علوم الحاسب والبرمجة

https://moalfat.com  للكتب الالكترونية والكورسات التعليمية

Everything we see now is just an engineering solution based on old science

#2

السلام عليكم و رحمة الله و بركاته,

جميل جداً أخ وجدي, أضفت لجعبتي مقالاً استفدت منه حتى آخر قطرة :)

لم أستخدم إلا الـ Operator Functions من قبل, و استخدمها في شيء بسيط لا أكثر,

و لكن في الحقيقة, لدي طلب, و هو أن تكمل الموضوع بالكلام عن std::bind الجديدة في cpp0x التي تعتبر نسخة محسنة من الموجودة في boost,

للأسف لا أعرف حتى الآن, ماهي إمكانيتها, بمعنى آخر, إذا استخدمناها, هل نستطيع الاستغناء عن جميع هذه الطرق المذكورة؟

مافهمته أن bind ستغني عن جميع الطرق المذكورة, و لكن هذا كلام سابق لأوانه, و هذا ما ننتظره أخ وجدي :P B)

و بهذا يصبح الموضوع مرجعاً لنا جميعاً في هذا الموضوع, و إن شاء الله سأكون أول من يبدأ بترجمته,

تحياتي,

#3

أهلا أخي خالد ،، بالنسبة لstd::bind فكما ذكرت هي نسخه محسنة من boost::bind وتم حل أحدى المشاكل ( the forwarding problem ) على حسب ما قرأت بها ،، وبما أنهم متشابهات فالحديث عن الboost.Bind سوف ينطبق على الstd::boost .

عند استخدامك لboost::bind فيمكنك أن تنسى تماما كلا من الدوال التالية :

bind2nd , bind1st , mem_fun ,mem_fun_ref , ptr_func, not1, not2

وسوف تستخدم بدلا من كل هذا فقط boost::bind ! لذلك يمكن لمن هو جديد في موضوع الbinder أن يبدأ بboost::bind مباشره بدون النظر للSTL Adapter .

وأحد أهم نقطه تفوق الboost::bind على الAdapter في الSTL (مثل bind2nd,bind1st) هي أنها تعمل على عدد كبير من المعاملات (9 معاملات) وليس كما في الSTL Adapter والتي فقط تدعم اما معامل واحد ، أو معاملين ..وهنا قدمت الboost::bind مفهوم الplaceholders وهي خاصية جميلة تسمح لك باستدعاء معاملات الدالة بالطريقة التي نريد ، نشاهد مثال على ذلك :

// Placeholder in boost::bind

#include <iostream>
#include <boost/bind.hpp>
using namespace std;

void f (int x, int y , int z) {
	cout << x << " " << y << " " << z << endl;
}

int main (int argc, char* argv[]) {
	boost::bind(f,_3,_1,_2)(6,7,8);

	return (0);
}

في المثال اعلاه نشاهد أن الدالة bind قامت بتوليد Un-Named Function Object وقامت بتمرير المعاملات كما تم تحديد ذلك في الPlaceholder وهي 8 ثم 6 ثم 7 .

التعامل مع الplaceholder سهل ومريح ، وأفضل من استدعاء الدوال bind2nd , bind1st المحدودة بكثير ،، فقط لدينا داله واحده ونقوم بتحديد الPlaceholder وهي تتولى الباقي .. المثال التالي يبين لنا الفرق في عملية استدعاء الدوال بين ال STL Adapter والboost::bind .

// Using bind with functions and function pointers

#include <iostream>
#include <boost\bind.hpp>
#include <boost\ref.hpp>

using namespace std;

int f1 (int x, int y ) 	{ return x + y; }

int main(int argc , char* argv[]){
	// calling function 
	cout << f1(4,5)	 				 << endl;			// 4+5
	cout << boost::bind( f1, 4, 5)() << endl;			// 4+5

	// binding x to f1 and x be first argument 
	int x = 3;
	cout << bind1st(ptr_fun(f1), 5) (x) << endl;		// 5+3
	cout << boost::bind(f1, 5 , _1)	(x) << endl;		// 5+3

	// binding x to f1 and x be second argument
	cout << bind2nd( ptr_fun(f1), 5) (x) << endl;		// 3+5
	cout << boost::bind(f1, _1 , 5)  (x) << endl;		// 3+5

	// boost bind
	cout << boost::bind(f1,_2,_2)(x,x) << endl;			// 3+3
	cout << boost::bind(f1,_1,_2)(6,x) << endl;			// 6+x
	cout << boost::bind(f1,_3,_3)(1,3,6) << endl;		// 6+6
	cout << boost::bind(f1,boost::ref(x),boost::cref(5))() << endl;	// x copy by reference and 5 by const ref

	return (0);
}

لاحظ مدي سهوله استخدام الboost مقارنة مع الSTL Adapter ، حيث أنها لا تتطلب منك استخدام مثلا ptr_fun لكي تولد Functor بل تقوم هي بذلك العمل بنفسها ، أيضا لاحظ أنك في الSTL تحدد الدالة اما bind1 أو bind2 وهو أمر مرهق ويزيد من صعوبة الكود ومقروئيته.

ايضا تستطيع في boost::bind تحديد الدالة التي سيتم استدعائها عن طريق القيمه الراجعه ، كما يلي :

// Using bind with function objects

#include <iostream>
#include <boost\bind.hpp>
using namespace std;

struct F {
	int operator() 	(int x, int y) 		{ return x + y; }
	bool operator() (long a, long b) 	{ return a == b; }
};

int main (int argc , char* argv[]){

	// specify function , call the () function that return int 
	F f;
	int x = 4;
	cout << boost::bind<int>(f,_1,_1)(x) << endl;				// 4+4
	cout << boost::bind(boost::type<int>(),f,_1,_1)(x)<< endl; // same result 4+4

	// calling predefined functor
	cout << bind2nd(std::less<int>(),6)(1) << endl;				// (1<6) return true (1) 
	cout << boost::bind(std::less<int>(),_2,_1)(6,1) << endl;	// (1<6) return true (1) 


	return (0);
}

لنرى الأن الفرق بينهم في استدعاء الدوال داخل الكلاس ، المثال التالي يعرض استدعاء الدالة في كلاس داخل container :

// using STL Adapter

#include <iostream>
#include <vector>
#include <boost\bind.hpp>
using namespace std;

class Student {
	public:
		Student(const string& s) : name_(s) { }
		void print () { cout << name_ << endl; }

	private:
		string name_;
};

void STL_Adapter (vector<Student>& nNames );
void Boost_Bind  (vector<Student>& nNames );

int main(int argc, char* argv[]){
	vector<Student> nNames;
	nNames.push_back(Student("Ahmed"));
	nNames.push_back(Student("Ali"));
	nNames.push_back(Student("Bassem"));
	nNames.push_back(Student("Khalid"));

	// using STL Adapter
	STL_Adapter(nNames);

	cout << endl << endl;

	// using Boost.Bind
	Boost_Bind(nNames);

	return (0);
}

void STL_Adapter (vector<Student>& nNames ) {
	for_each(nNames.begin(),nNames.end(),mem_fun_ref(&Student::print));
}

void Boost_Bind  (vector<Student>& nNames ) {
	for_each(nNames.begin(),nNames.end(),boost::bind(&Student::print,_1));	 
}

الدالتين STL_Adapter و Boost_Bind تقوم بطباعه محتوى الvector ، وحاليا لا يوجد فرق بين استخدام اي من الطريقتين ، لكن اذا قمنا بتغيير محتوى الcontainer وجعلناه يحتوي على مؤشرات ؟ سوف يتغير كود الstl بينما الboost سوف يبقى كما هو ..

// in main 
vector<Student*> nNames;
nNames.push_back(new Student("Ahmed"));
nNames.push_back(new Student("Ali"));
nNames.push_back(new Student("Bassem"));
nNames.push_back(new Student("Khalid"));

// the functions
void STL_Adapter (vector<Student*>& nNames ) {
	for_each(nNames.begin(),nNames.end(),mem_fun(&Student::print));
}

void Boost_Bind  (vector<Student*>& nNames ) {
	for_each(nNames.begin(),nNames.end(),boost::bind(&Student::print,_1));	 
}

الى هنا الSTL Adapter ما زالت تقاوم ولديها الحلول :) ، ولكن بمجرد استخدامك لأي من الsmart_ptr فلن تستطيع الSTL Adapter التعامل معها على الإطلاق ، بينما ما يزال كود الboost كما هو بدون تغيير ، جرب أن تغير الcontainer واجعلها بدلا من مؤشرات الى shared_ptr وستحصل على رسالة خطأ في كود الstl ...

أحب أن أعرض أحد الأمثله "المثيرة" :) في boost وهي في الnested bind ، والمثال هنا نريد أن نعرف كم عدد الطلاب الناجحين الذين درجاتهم من 50 الى 100 .. سوف نحل المثال باستخدام الطريقتين STL Adapter والBoost.Bind :

// number of successful

#include <iostream>
#include <vector>
#include <boost\bind.hpp>
using namespace std; 

int get_STL (vector<int> nDeg );
int get_BOOST(vector<int> nDeg);
int get_First (vector<int> nDeg );

int main (int argc, char* argv[]) {
	vector<int> nDeg;
	nDeg.push_back(10);nDeg.push_back(65);
	nDeg.push_back(55);nDeg.push_back(98);
	nDeg.push_back(20);nDeg.push_back(232);

	int num = get_STL( nDeg );
	cout << "number of successful: " << num << endl;

	num = get_BOOST( nDeg );
	cout << "number of successful: " << num << endl;

	num = get_First( nDeg );
	cout << "first passing in position: " << num << endl;
	return (0);
}

bool isPass (int n ) { return (n>=50 && n<=100); }

int get_STL (vector<int> nDeg ) {
	return count_if(nDeg.begin(),nDeg.end(),isPass);
}

int get_BOOST (vector<int> nDeg ) {
	return count_if(nDeg.begin(),nDeg.end(),
		boost::bind(std::logical_and<bool>(),
			boost::bind(std::greater_equal<int>(),_1,50),
			boost::bind(std::less_equal<int>(),_1,100) ) );
}

int get_First (vector<int> nDeg ) {
	vector<int>::iterator pos = 
		std::find_if(nDeg.begin(),nDeg.end(),
			boost::bind(std::logical_and<bool>(),
				boost::bind(std::greater_equal<int>(),_1,50),
				boost::bind(std::less_equal<int>(),_1,100) ) );

	return ( pos - nDeg.begin() );		
}

في الدالة get_STL قمنا باستخدام الFunctor لإستخراج النتيجه ،، أما في get_BOOST والget_First قمنا بعمل nested bind ، وهي مكونه من عملية and للشرط الأول وهو أن الدرجه أكبر أو يساوي 50 والشرط الثاني أن النتيجه أصغر أو تساوي 100. أعتقد عملية شرح مثل هذا التعبير صعبه قليلا ، ولكنه تعبير مفيد حقا :) .

أخيرا نشاهد مثال لطباعه الmap :

// print map content

#include <iostream>
#include <map>
#include <boost\bind.hpp>
using namespace std; 

typedef map<int,string> MyMap;

void printMap (const string& s) { cout << s << endl; }

int main(int argc, char* argv[]) {
	MyMap myMap;
	myMap[0] = "Ahmed";
	myMap[1] = "Ali";
	myMap[2] = "Salem";

	std::for_each(
		myMap.begin(),
		myMap.end(),
		boost::bind(&printMap,
			boost::bind(&MyMap::value_type::second,_1) /* value_type is type for memeber variable second*/
		)
	);

	return (0);
}

أعتقد أنه باستخدام ميزة الnested binder يمكن كتابة كود صغير (معقد قليلا :) ) ولكنه يريحك من كتابة العديد من الدوال والحلقات والمرور بالiterator ،،

كان هذا شرح مبسط لكيفة استخدام boost::bind ، أما عن كيفية عملها من الداخل فهذا موضوع أخر وهو موضوع صعب لذلك أتركه لك أخي خالد :wink: ،

بالتوفيق ،

2

http://informatic-ar.com منصة تعليمية عربية في علوم الحاسب والبرمجة

https://moalfat.com  للكتب الالكترونية والكورسات التعليمية

Everything we see now is just an engineering solution based on old science

#4

مجهود 10/10 أخ وجدي, تشكر عليه صراحة :)

بصراحة أنا تعلمت هذه "الحركات" في Scheme, و لكن هناك العملية زي العسل, اللغة أصلاً functional :P

وجدتها في ++C معقدة جداً, و عقلي لم يستوعب الموضوع, لذلك عندما تعلمت boost::random, كنت أكتب الكود بهذه الطريقة:

uniform_int<> dist(100, 1000);
mt19937 gen;
vector<int> numbers;

for(int i = 0; i < 100; ++i)
{
	numbers.push_back(dist(gen));
}

بالطبع هذا السطر كان دائماً يزعجني في هذه المكتبة, لأنني أحتاج إلى تمرير الـ generator, بدلاً من دالة واضحة كـ rand!

حسناً, تطور الموضوع قليلاً, و تعلمت boost::function, و في نفس الوقت ألقيت نظرة على boost::bind. بصراحة function أمرها بسيط, و لكن خلطة function مع bind هي الخلطة السحرية :)

function<int ()> simple = bind(dist, gen);

for(int i = 0; i < 100; ++i)
{
	numbers.push_back(simple());
}

هذا أكبر ما كتبته باستخدام bind, عبارة عن عمليات بسيطة لا أكثر,

عموماً, ماشاء الله قمت بكسر أحد الحواجز في ++C بالنسبة لي :)

تحياتي...

1

مواضيع مشابهة