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

لمحبي طريقة Java, طباعة المصفوفات و عناصر Stl بكل سهولة و يسر!

بدأه Khaled.Alshaya في 9 سبتمبر 2009 · 13 رد · 5,666 مشاهدة · في قسم المواضيع الهامة في قسم السي /سي++
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكم ...

عندما تعلمت Java, كنت أعاني مع الـ Debugger. فأنا كنت أستخدم JCreator :lol:

و طالما مللت من طباعة مكونات المصفوفات بهذا الشكل:

int[] arr = {1, 2, 3, 4, 5};

System.out.print("[ ");
for(int i = 0; i < arr.length; i++)
{
	System.out.print(arr + " ");
}
System.out.println("]");

بالطبع ظهر أني لا أعرف مكتبة Java جيداً! و هناك toString حتى للمصفوفات.

int[] arr = {1, 2, 3, 4, 5};

System.out.println(Arrays.toString(arr));

جميل جداً و مختصر, إضافة إلى ذلك, فإن جميع الـ Data Structure في مكتبة الـ Java توفر toString لعملية طباعة العناصر. تستخدم في الـ Debugging في الـ Logging أو حتى لأي استخدام آخر.

مادخلنا بـ Java إذاً!

حسناً ++C ليس فيها toString لطباعة العناصر في أي Container فيها, لذلك أردت أن أحاكي طريقة Java و لكن على طريقة ++C! أكيد لم تفهمني حتى الآن, هذا ما أردته بكل بساطة!

std::vector<int> iVec;
.....
std::cout  <<  "vector		 = "  << iVec << std::endl;

يمكنك مع الأداة التي قمت بكتابتها, بطباعة أي Container في STL! و هذا عينة الاختبار التي كتبتها :)

post-89451-1252522469_thumb.png

ستجد الكود الذي في الأعلى, مع كود أداة الطباعة نفسها في الملف التالي, مع مشروع VS2008.

Streamer.rar

و هذا هو ناتج الكود:

post-89451-1252522637_thumb.png

ملاحظة : الكود الذي في الأعلى كتبته لاختبار الأداة, و هو يعتمد على boost::assign, بينما الأداة نفسها لا تعتمد على أي كود خارج STL. لذلك يمكنك استخدام الأداة مباشرة دون وجود Boost, و لكن لكي يعمل ملف الاختبار لابد من وجود Boost :)

تحياتي ...

تم تعديل هذه المشاركة بواسطة Khaled.Alshaya في 9 سبتمبر 2009 في 23:41

1
#2

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

أكثر من رائع أخي خالد ، بارك الله فيك ..

هذه نقطه تتفوق فيها الجافا على سي++ :P

تقريبا كل شيء واضح في ملف الهيدر ما عدا الStack,Queue :

template < class Type, class Container >
	const Container& container(const std::stack<Type, Container>& stack)
	{
		struct HackedStack : private std::stack<Type, Container>
		{
			static const Container& container(const std::stack<Type, Container>& stack)
			{
				return stack.*&HackedStack::c;
			}
		};

		return HackedStack::container(stack);
	}

:wacko:

يبدوا أننا بحاجة الى الإطلاع على ملفات الهيدر في STL لكي نفهم ما يحصل :P

موفق أخي العزيز :) ..

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

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

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

#3

أهلاً وجدي,

اقتباس
هذه نقطه تتفوق فيها الجافا على سي++

و بكم سطر نحل المشكلة بـ ++C :P

بالنسبة للسؤال, فمعك حق, أنا عندما انتهيت منذ فترة, كان قد بقي stack و queue و priority_queue لم أستطع طباعة العناصر فيهم. السبب لأنهم Adapters, فهم لا يعطون Iterators لعناصرهم, و هذا منطقي لأنك إذا كنت تريد stack فأنت لا تريد مصفوفة تتجول فيها!

المشكلة أنه كيف نستطيع الحصول على الـ Container الداخلي ؟

هذا السؤال لحسن الحظ وجدت اجابته و لم أكتبها بنفسي للمعلومية :)

بداية, الـ Container الموجود في stack أو queue أو priority_queue, معرف على أنه protected, و بالتالي لاتوجد طريقة للوصول إليه إلى من خلال الوراثة. قمنا بوراثة std::stack على سبيل المثال للوصول إلى المتغيرات الـ protected

struct HackedStack : private std::stack<Type, Container>
{
.....
};

و الزبدة موجودة في السطر التالي:

return stack.*&HackedStack::c;

يمكنك تخيل HackedStack::c& على أنه الـ offset الذي يوجد عنده الـ Container المسمى c في كل Adapter. بمعنى أننا أخذنا الـ offset بالنسبة لبداية الـ Class و لكننا لم نحصل بعد على القيمة! كل ما سنقوم به, هو أننا سنقوم بدمج stack و الـ offset للحصول على المتغير المطلوب. بالتالي, فإن (this) الخاصة بالكائن stack سيتم تمريرها, و سيتم تمرير الـ offset و لكن إلى ماذا ؟!

هل تصدق أن هناك operator في ++C على الشكل التالي:

a.*b

أو عندما a يكون مؤشراً إلى class

a->*b

الأول Bind pointer to member by reference, و الثاني Bind pointer to member by pointer. بالتالي, نحن عن طريق هذين المعاملين بكل بساطة, نقوم بمزج this أو this* بحسب أي operator ستستخدمه, ثم نقوم بأخذ المطلوب, عن طريق الـ offset الذي نحصل عليه من تعريف الـ Class نفسه.

hack حلو :lol:

تحياتي...

تم تعديل هذه المشاركة بواسطة Khaled.Alshaya في 10 سبتمبر 2009 في 19:41

1
#4

السلام عليكم

جميل جداً أخ خالد..

بارك الله فيك

SAFETY TIP : Always delete what you new, and free what you malloc, never mix new with free or malloc with delete.

#5

السلام عليكم.

نسخة محسنة, تدعم الـ wide characters في ++C. حيث بإمكانك استعمالها مع التطبيقات التي تدعم UNICODE في Windows بهذا التطوير البسيط, و غيره من الأنظمة بالطبع :)

أرفقت ملفاً إضافياً, يحتوي على عملية اختبار كما في النسخة الأولى, و لكن هذه المرة للـ wide characters.

Streamer.rar

#6

السلام عليكم

بارك الله فيك اخ خالد.. لدي ملاحظة صغيرة على الكود في الملف الرأسي..

#ifndef DATASTRUCTRE_STREAMER
#define DATASTRUCTRE_STREAMER_INCLUDED

يبدو أنك لم تقصد وضع اسمين مختلفين... لأنهما بلا معنى هكذا

تحياتي

SAFETY TIP : Always delete what you new, and free what you malloc, never mix new with free or malloc with delete.

#7

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

هو أنت بتزاكر من ورانا ولا إيه!!!! post-46090-1226414104.gif

#8

تم إضافة دعم للمصفوفات الأصلية في ++C!

يمكنك باستخدام هذه الإضافة على سبيل المثال طباعة أي مصفوفة من أي نوع:

int x[] = {0, 1, 2, 3, 4};
std::cout << x;

ملاحظة, المؤشرات ليست مصفوفات, و ليس هناك طريقة لمعرفة حجم المصفوفات الديناميكة, لذلك الإضافة مختصة فقط بالمصفوفات الثابتة :)

تحياتي...

Streamer.rar

تم تعديل هذه المشاركة بواسطة Khaled.Alshaya في 24 سبتمبر 2009 في 17:45

#9

syntax غريب قليلا :

	template <class Type, std::size_t size, class Stream>
	Stream& operator<<(Stream& outputstream, Type (&array)[size])
	{
		outputstream << "[";

		for(std::size_t i = 0; i < size; ++i)
		{
			outputstream << " " << array;
		}

		outputstream << " ]";

		return outputstream;
	}

Type (&array)[size]

هذه تقوم بحساب حجم المصفوفة , أذكر أننا تحدثنا عنها في هذا الموضوع :

لمحة : كيف تعرف حجم أي مصفوفة ستاتيكية بطريقتين .

هل تعرف شيء عن هذه الصيغة .. كيف اكتشف المترجم حجم المصفوفة !

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#10

السلام عليكم ...

أهلاً بالشمري,

لازلت تذكر الموضوع حتى الآن :wink:

أعتقد أني تعلمت القليل منذ ذاك الموضوع, بكل بساطة نحن نمرر المصفوفة بالـ reference.

المصفوفات في ++C, عندما تمررها يقوم المترجم بإرسال pointer لأول عنصر, و بكل بساطة هذان متساويان:

1) void fun(int* const array);
2) void fun(int array[]);

في الحقيقة أنه لا يمكن أن تتواجد هاتان الدالتان في نفس الوقت, سيخبرك المترجم بأن fun تم إعادة محاولة تعريفها!

لاحظ أن الدالة الأولى تستقبل مؤشر ثابت لبيانات متغيرة! و هو بالضبط تعريف المصوفات الثابتة.

هذا الأمر يظهر أنه من C, التي تعامل المصفوفات بهذا الشكل. في ++C, يمكنك تمرير أي شيء بالمرجع, فمثلاً الدوال و المصفوفات يمكن تمريرها بالمرجع و بالمؤشر. رأينا كيف نمرر المصفوفات بالمؤشر, و هذه هي الحالة الافتراضية للمصفوفات.

مارأيك أن نلقي نظرة على الدوال, و ستجد أن الطريقة متشابهة نوعاً ما :)

لتمرير مؤشر دالة إلى دالة, نقوم بكتابة التالي:

void print()
{
	std::cout << "void print();";
}

void caller( void (*fun)() ) // ==========
{
	fun();
}

int main()
{
	caller(print);
}

و لتمريرها بالمرجع, كل ما علينا هو استبدال التالي:

void caller( void (&fun)() ) // ==========
{
	fun();
}

بنفس الطريقة, لتمرير مرجع إلى مصفوفة:

template <std::size_t size>
void printarray(int (&array)[size]) // ===========
{
	for(std::size_t i = 0; i < size; i++)
	{
		std::cout << array << std::endl;
	}
}

أعلم أن الـ syntax غريب قليلاً, و لكن هذه ++C :)

النقطة الباقية الوحيدة, و هي لماذا استخدمنا reference أصلاً ؟

لأن المصفوفة عندما يتم تحويلها لمؤشر في الـ parameter فإنها تفقد معلومات الحجم ببساطة, فيمكنك تمرير أي مؤشر ثابت مكانها! بالتالي من الخطأ اعتبار كل argument يحمل معلومات الحجم كما تحمله المصفوفات الثابتة.

عموماً هذا ما أعرفه, و أعتقد أني مسكت طرف الخيط لا أكثر, من الصدف أن تكون حركة جميلة كهذه تمكنني من طباعة المصفوفات في الأداة التي قمت بكتابتها :P

تحياتي..

#11

كود غريب فعلا، يبدو أن اللغة مليئة بالمفاجئات..

بالمناسبة، أنا حاسس بمعاناة مصنعي المترجمات (( مترجمات السي++ ))

هل باستطاعتهم أن يعيشوا حياة طبيعية!!؟

تحياتي

SAFETY TIP : Always delete what you new, and free what you malloc, never mix new with free or malloc with delete.

#12

شكرا خالد :-) .

أيضا أنا بدأت أمسك بطرف خيط .. لا أعلم هل هو الخيط المطلوب أم لا :D .

المشكلة هي في هذه الصيغة .. لماذا عنوان عنوان المصفوفة .. الا يكفي عنوان أول عنصر من المصفوفة ؟ :

void printIntArray(int (&array)[5]) 
{
	for(std::size_t i = 0; i < 5; i++)
	{
		std::cout << array << std::endl;
	}
}

لماذا لانكتبها هكذا :

void printIntArray(int array[5]) 
{
	for(std::size_t i = 0; i < 5; i++)
	{
		std::cout << array << std::endl;
	}
}

فالنتيجة واحدة , لكن يظهر خطأ عندما استخدم Template .. مثال :

template <typename T , std::size_t size>
void printArray(T array[size]) 
{
	for(std::size_t i = 0; i < size; i++)
	{
		std::cout << array << std::endl;
	}
}



void printIntArray(int array[5]) 
{
	for(std::size_t i = 0; i < 5; i++)
	{
		std::cout << array << std::endl;
	}
}



using namespace std;
int main()
{
	int intArray[]={1,2,3,4,5};

	printArray<int,5>(intArray);
	printIntArray(intArray);
	//printArray(intArray);// error ?!

return 0;
}

لكن لا عليك .. ستتضح الامور مع الوقت :-) .

اقتباس
هل باستطاعتهم أن يعيشوا حياة طبيعية!!؟

يقولون أن هذا syntax قد تراه في مكان ما :

 const int (&getArray() const)[10]
  {
	return array_;
  }

أخ خالد .. لماذا لاتضع نسخة من هذا الكلاس في source forge أو google code حتى لو كانت صغيرة .. الا انها تكبر مع الوقت .. ولها فائدة وتريح المبرمجين .

تم تعديل هذه المشاركة بواسطة الشمري في 27 سبتمبر 2009 في 07:10

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#13

أهلاً بالشمري :)

عندما تمرير مصفوفة في Java, مالذي يمرر فعلاً ؟

أولاً عنوان المصفوفة, أو الـ reference. و حجم الصفوفة, صحيح ؟

في C لا يمرر إلا عنوان المصفوفة. بالتالي, الحجم هو مهمة الدالة, و لا يسلم لها, بالطبع C هذه عمرها 50 عاماً, هكذا كان يفكر مصممها :)

++C توفر لك طريقتين لتمرير الـ parameters أليس كذلك؟ إما أن تمرره بمؤشر كما في C أصلاً, و إما أن تمرره بالمرجع. بالطبع التمرير بالمرجع لا يستخدم فقط مع الأنواع البسيطة و الـ classes و إنما أي شيء يمكن تمريره بالمرجع! الدوال و المصفوفات و جميع ما يمكن أن تمرره بمؤشر.

رأينا أن الفرق في تمرير الدوال بالمرجع, و تمرير الدوال بالمؤشر هو حرف واحد. بينما في حالة المصفوفات, فإننا عندما نكتب:

void fun(int array[10]);

أو

void fun(int array[]);

array يتم تحويلها في المترجم إلى مؤشر لأول عنصر و 10 الموجودة هذه ليس لها أي علاقة بالمصفوفة الأصلية. لماذا لا يصدر المترجم خطأ عندما نكتب 10 داخل [ ] فهذا أمر آخر أيضاً.

في ++C, التمرير بالمرجع تمرير حقيقي. و لا تحدث عمليات تحويل كما رأيت في حالة المؤشرات في C و المترجم يستطيع الوصول إلى معلومات أكثر مما لو كان الممرر مؤشراً. و هذا يحتاج إلى مقال لوحده عن الفرق بين المؤشرات و المراجع. فمثلاً لو أردنا قبول أي مصفوفة حجمها 10 و نريد رفض ماعدا ذلك:

void fun(int (&array)[10])
{

}

فلو أنك قمت بكتابة التالي:

int array[12] = {0};
fun(array);

سيخبرك المترجم بوجود خطأ, و هو عدم إمكانية تحويل [int [12 إلى [int[10, و كما ترى اللغة في حالة المؤشرات لا تحمل المعلومات للتوافقية مع C. أنا متأكد بأنك ستسأل لماذا يتم الإبقاء على هذه التوافقية المربكة :lol:

ببساطة لأن فوائدها أكبر من أن تقارن بالارتباك الحاصل :)

اقتباس
أخ خالد .. لماذا لاتضع نسخة من هذا الكلاس في source forge أو google code حتى لو كانت صغيرة .. الا انها تكبر مع الوقت .. ولها فائدة وتريح المبرمجين .

ربما عندما يصبح Logger بسيط :P

تحياتي...

تم تعديل هذه المشاركة بواسطة Khaled.Alshaya في 27 سبتمبر 2009 في 07:54

#14

أهلاً أخي محمد :)

اقتباس
بالمناسبة، أنا حاسس بمعاناة مصنعي المترجمات (( مترجمات السي++ ))

هذه حقيقة :)

درسني شخص اسمه Mike زمان في ثاني مادة برمجة آخذها و أخبرنا معلومة لطيفة, و هو أحد المبرمجين السابقين لمترجم Microsoft Cpp :) و عاد للدراسة ليحصل على الـ Ph. D, بأن عملية الترجمة فقط لـ source المترجم و الأدوات المرفقة معه من الصفر تحتاج إلى ليلة كاملة من انصراف الموظفين إلى عودتهم باستخدام Server جبار :lol:

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