در نوشته قبل و در آموزش ساختار به بیان مفاهیم و اصول استراکچر پرداخته شد. در این نوشته در قالب دو مساله کاربردی از این مفاهیم استفاده شده است . در هر دو مساله مثالهای مرتبط از ساده به سخت مرتب شده است.

مساله اول – دانشجویان

در این مساله به بیان چند مثال کاربردی مرتبط با دانشجویان پرداخته شده است. اولین نکته ای در مثال بعد خواهید آموخت طریقه استفاده از آرایه ای از استراکچر است:

مثال 1- برنامه ای بنویسید که از ورودی اطلاعات 100 دانشجو شامل نام دانشجو، شماره دانشجو و معدل دانشجو دریافت کند و بالاترین معدل را چاپ کند.

در آموزش ساختار در مورد دریافت اطلاعات یک دانشجو صحبت کردیم و در آنجا استراکچر student را معرفی کردیم. اما در این مثال نیاز است که ما اطلاعات 100 دانشجو را از ورودی دریافت کنیم. بنابراین باید از آرایه ها استفاده کنیم. اگر به یاد داشته باشید برای تعریف یک آرایه 100 تایی از نوع int از کد زیر استفاده میکردیم:

;int A[100]

دقیقا به همین صورت می‌توان آرایه ای از استراکچر student تعریف کرد:

;student s[100]

در تصویر زیر استراکچر student را می‌بینید که شامل سه عضو داده ای number، name و avg است. ما در این مثال به آرایه ای از جنس student نیاز داریم. آرایه S آرایه ای از جنس student است و همانطور که در تصویر می‌بینید هر درایه این آرایه شامل سه عضو داده ای است.که برای دسترسی به هر کدام از اعضا مانند قبل باید از دات ( . ) استفاده کنیم. مثلا چنانچه بخواهید به نام Ali دسترسی داشته باشید باید اینگونه بنویسید: s[0].name و یا برای دسترسی به شماره دانشجویی Sina باید به این شکل دسترسی پیدا کنید: S[3].number. با توجه به اینکه شما میخواهید بالاترین معدل را پیدا کنید در واقع به یک پیمایش شبیه به نوار قرمز رنگ در تصویر زیر نیاز دارید:

با این توضیحات به سراغ کد می‌رویم:

#include <iostream>

using namespace std;

struct student {
	int number;
	string name;
	float avg;
};

int main(){
	
	int const n=100;
	student s[n];
	
	int i,max=0;
	
	for(i=0;i<n;i++)
	{
		cout<<"Enter name of student: ";
		cin>>s[i].name;
		
		cout<<"Enter number of student: ";
		cin>>s[i].number;
		
		cout<<"Enter average of student: ";
		cin>>s[i].avg;
		
		cout<<endl<<"*********"<<endl<<endl;
	}
	
	for(i=0;i<n;i++)
		if(s[i].avg > max)
			max = s[i].avg;
			
	cout<<"max: "<<max;		
}

توضیح کد:
در خط 18 تا 30 اطلاعات دانشجویان در آرایه قرار میگیرد. آرایه s از جنس student است بنابراین هر خانه آرایه از جنس student است و شامل سه متغیر name، number و avg است. بنابراین دستور cin>>s[i] به تنهایی یک کد اشتباه است و باید مشخص کنیم که کدام عضو داده ای این اندیس قرار است دریافت شود. در خط 21، 24 و 27 اعضای داده ای s[i] دریافت می‌شوند.
دز خط 32 تا 34 کل آرایه پیمایش می‌شود و فقط عضو داده ای avg اعضا ملاقات می‌شود. دوباره تاکید میکنیم که s[i] به تنهایی بی معنی است و باید دقیق مشخص شود که به چه عضو داده ای s[i] نیاز داریم. در خط 33 با استفاده از s[i].avg به تک تک معدل دانشجویان دسترسی پیدا میکنیم و با مقایسه آن با max بزرگترین عدد را پیدا میکنیم.

تذکر: در خط 13 مقدار n را برابر 100 قرار داده ایم، شما اگر میخواهید این برنامه را تست کنید n=3 قرار دهید تا در تست برنامه نیاز به وارد کردن اطلاعات 100 دانشجو نداشته باشید! اگر از نحوه استفاده const هم اطلاعی ندارید مثال 4 آموزش آرایه را بخوانید.

در مثال بالا فقط معدل دانشجو را پیدا کردیم. در مثال بعدی همه اطلاعات دانشجویی که بیشترین معدل را دارد چاپ می‌شود.

مثال 2 – برنامه ای بنویسید که اطلاعات 100 دانشجو را از ورودی دریافت کند و نام دانشجویی که بیشترین معدل دارد را چاپ کند.

اگر به یاد داشته باشید در بحث آرایه ها به دو روش جستجوی یک عدد را انجام دادیم. یکی پیدا کردن عدد (به این معنی که آیا عدد در آرایه وجود دارد یا خیر) و دیگری پیدا کردن مکان آن عدد (به این معنی که اگر عدد در آرایه وجود دارد در چه خانه ای ذخیره شده است. (مثال 5 آموزش آرایه را ببینید) در این مثال از ایده روش دوم استفاده می‌کنیم، یعنی اگر در پیمایش آرایه متوجه شدیم که معدل از max بزرگتر است اندیس خانه آرایه را در idx ذخیره میکنیم. مثلا در تصویر زیر ما به دنبال یافتن خانه 2 آرایه هستیم. زیرا اگر شما این خانه را پیدا کنید ( که در آن بزرگترین معدل آرایه وجود دارد) به راحتی میتوانید به نام و معدل دانشجو دسترسی داشته باشید.

با این توضیحات به سراغ کد می‌رویم:

#include <iostream>

using namespace std;

struct student {
	int number;
	string name;
	float avg;
};

int main(){
	
	int const n=3;
	student s[n];
	
	int i,max=0;
	
	for(i=0;i<n;i++)
	{
		cout<<"Enter name of student: ";
		cin>>s[i].name;
		
		cout<<"Enter number of student: ";
		cin>>s[i].number;
		
		cout<<"Enter average of student: ";
		cin>>s[i].avg;
		
		cout<<endl<<"*********"<<endl<<endl;
	}
	
	int idx = 0;
	for(i=1;i<n;i++)
		if(s[i].avg > max){
			max = s[i].avg;
			idx=i;	
		}
			
	cout<<"name: "<<s[idx].name<<endl;		
	cout<<"number: "<<s[idx].number<<endl;		
	cout<<"avg: "<<s[idx].avg<<endl;		
}

توضیح کد:
همانظور که بالاتر توضیح داده شد، در خط 33 تا 37 با استفاده از اندیس i، تک تک خانه های آرایه پیمایش می‌شود و چنانچه معدل دانشجو از max بیشتر باشد علاوه بر اینکه متغیر max آپدیت می‌شود اندیس آرایه یا همان i در idx ذخیره می‌شود.
اگر مثال 5 از بحث آرایه را خوانده باشید متوجه خواهید شد که این کد را بدون استفاده از متغیر max نیز می‌توان پیاده کرد:

int idx = 0;
	for(i=1;i<n;i++)
		if(s[i].avg > s[idx].avg)
			idx=i;		

در مثال آخر از مساله دانشجویان چند

مثال سوم: برنامه ای بنویسید که از ورودی اطلاعات 100 دانشجو شامل نام، شماره دانشجویی، معدل و تاریخ تولد آنها را دریافت کند. سپس:
الف) میانگین معدل کلاس را چاپ کند.
ب) نام دانشجویانی که معدل آنها از میانگین معدل دانشجویان بیشتر است را چاپ کند.
ج) تعداد دانشجویانی که معدل زیر 14 دارند را چاپ کند.
د) نام جوانترین دانشجوی کلاس را چاپ کند.

با توجه به توضیحات کاملی که در مثال 1 و 2 دادیم انتظار داریم که موارد الف، ب و ج را بتوانید به راحتی کد نویسی کنید. بهتر است ابتدا خودتان این 4 مورد را کدنویسی کنید و سپس کد زیر را نگاه کنید.

#include <iostream>

using namespace std;

struct date{
	int day,month,year;	
};

struct student {
	int number;
	string name;
	float avg;
	date d;
};

int timestamp(date d);

int main(){
	
	int const n=3;
	student s[n];
	
	int i,max=0;
	
	for(i=0;i<n;i++)
	{
		cout<<"Enter name of student: ";
		cin>>s[i].name;
		
		cout<<"Enter number of student: ";
		cin>>s[i].number;
		
		cout<<"Enter average of student: ";
		cin>>s[i].avg;
		
		cout<<"Enter birthday of student(day,month,year)"<<endl;
		cout<<"Day: ";
		cin>>s[i].d.day;
		cout<<"Month: ";
		cin>>s[i].d.month;
		cout<<"Year: ";
		cin>>s[i].d.year;
		
		cout<<endl<<"*********"<<endl<<endl;
	}
	
	//الف
	float sum = 0;
	float mid;
	for(i=0;i<n;i++)
		sum = sum + s[i].avg;
	
	mid=sum/n;
	cout<<"Average of avg: "<<mid<<endl	;

	cout<<endl<<"*********"<<endl<<endl;	
	//ب
	for(i=0;i<n;i++)
		if(s[i].avg>mid)
			cout<<s[i].name<<endl;
		
	
	cout<<endl<<"*********"<<endl<<endl;
	//ج
	int counter = 0;
	for(i=0;i<n;i++)
		if(s[i].avg <14)
			counter++;
	cout<<"Students with avg of less than 14: "<<counter<<endl;


	cout<<endl<<"*********"<<endl<<endl;
	//د
	int dates[n];
	for(i=0;i<n;i++)
		dates[i] = timestamp(s[i].d);
	
	int idx = 0;
	for(i=0;i<n;i++)
		if(dates[i] > dates[idx])
			idx = 0;
			
	cout<<"The youngest student: "<<s[idx].name<<endl;						
}

int timestamp(date d){
	int t= d.year *365 + d.month * 30 + d.day;
	return t;
}

توضیح کد:
موارد الف، ب و ج نیاز به توضیح اضافه ندارند و کدها گویا هستند. اما در مورد قسمت د باید این نکته را اضافه کنیم که برای کار با تاریخ بهتر است تابعی به نام timestamp تعریف کنیم. در مواقع این تابع تاریخ دریافتی را به تعداد روزها تبدیل میکند. بنابراین اگر ما به دنبال جوانترین دانشجو هستیم و بخواهیم از این تابع استفاده کنیم در واقع باید به دنبال بزرگترین روز باشیم. زیرا فردی که جوان تر است عدد برگشتی آن از این تابع بزرگتر می‌شود. بنابراین در خط 74 آرایه dates را تعریف کردیم که در این آرایه عدد برگشتیِ تاریخ تولد هر دانشجو از تابع timestamp در آن ذخیره می‌شود. یعنی به ازای هر دانشجو تاریخ تولدش را به تابع timestamp میدهیم و مقدار بازگشتی از این تابع را در اندیس متناظر آن در dates قرار میدهیم. (مثلا تایم استمپ دانشجوی اول در خانه اول dates ذخیره میشود، تایم استمپ دانشجوی دوم در خانه دوم dates و …) در خط 78 تا 81 با روش مثال دوم، اندیس بزرگترین خانه آرایه پیدا می‌شود و در انتها نام آن دانشجو چاپ می‌شود. توجه داشته باشید که آرایه های s و dates در راستای هم هستند. یعنی اطلاعات خانه صفر آرایه s و اطلاعات خانه صفر آرایه dates متعلق به یک دانشجو هستند و بنابراین می‌توانسیتیم این اطلاعات را در یک استراکچر جمع کنیم. به این معنی که مثلا عضو داده ای timestamp را به کلاس student اضافه کنیم:

struct student {
	int number;
	string name;
	float avg;
	date d;
	int timestamp;
};

به عنوان تمرین می‌توانید قسمت د را با استراکچر جدید پیاده کنید. (توجه کنید که دیگر نیازی به آرایه dates نداریم)

مساله دوم – مختصات دو بعدی

در این مساله می‌خواهیم در مورد مختصات دوبعدی مثال حل کنیم. قبل از این که به مثالها برسیم کمی در مورد کاربرد جذاب این مساله صحبت کنیم. مطالبی که در ادامه مطرح می‌شود صرفا جهت بالا بردن انگیزه شما در یادگیری این مثالهاست و چنانچه این مطالب را کامل متوجه نشدید جای نگرانی نیست و میتوانید مستقیما به سراغ مثالها بروید.

OCR

OCR که مخفف عبارت Optical Character Reader است، تکنولوژی است که صفحات حاوی متنی که اسکن کرده‌اید را از فرمت عکس به متن تبدیل می‌کند. از یک مثال ساده استفاده می‌کنیم. حتما نرم افزارهایی را دیده‌اید که کارت بانکی شما را اسکن می‌کنند و شماره کارت موجود در عکس را بیرون می‌کشند. این نرم‌افزارها نمونه ساده ای از OCR ها هستند.

تصویر 1

اما این نرم افزار ها چگونه عمل می‌کنند؟ در واقع این نرم افزارها داده هایی تحت عنوان داده های آموزشی دارند که از این داده ها برای تشخیص اعداد روی کارت استفاده می‌کنند. فرض کنید که این داده ها در نرم افزار برچسب گذاری شده اند و به ازای هر تصویر میدانیم که این تصویر چه عددی است:

تصویر 2

حال کافیست الگوریتمی نوشته شود که ابتدا ارقام موجود در کارت به 16 تصویر (که هر کدام شامل یک رقم است) تبدیل شود و هر عکس را با عکسهای موجود در داده های آموزشی (تصویر 2) مطابقت دهد و نزدیکترین (شبیه ترین) عکس را پیدا کند. از آنجایی که داده های آموزشی برچسب گذاری شده است و میدانیم هر عکس چه عددی است به این شکل می‌توان اعداد روی کارت را شناسایی کنیم. سوالی که ممکن است در ذهن شما نقش ببندد این است که چطور میتوان نزدیکترن عکس را پیدا کرد؟ شاید اگر به این مساله بخواهید فکر کنید هیچ روشی که بتوان الگوریتم آن را نوشت و پیاده سازی کرد به ذهنتان نرسد. زیرا که داده های ورودی شما عکس است و تا به حال با این جنس مساله روبرو نشده اید. برای این کار ما نیاز به مدل بندی مساله داریم. برای این کار باید عکس را به گونه ای به آرایه تبدیل کنیم که آرایه نماد آن عکس باشد. به این معنی که بتوانیم از آن آرایه هم عکس را تولید کنیم. اینکه چه طور می‌شود عکس را به آرایه تبدیل کرد در حوصله این بحث نیست ولی همین که متوجه شدید برای کار کردن با عکس، باید عکس را به آرایه یا ماتریس مدل بندی کرد قدم بزرگی در درک برنامه ها و نرم افزارهای اطراف شماست. الان شما میدانید که اگر میخواهید دو عکس شبیه به هم را پیدا کنید در واقع باید به دو آرایه شبیه به هم را پیدا کنید. البته اگر بخواهیم دقیق تر صحبت کنیم منظور ما از آرایه مثلا 5 تایی، یک نقطه در مختصات 5 بعدی است. ساده تر بخواهیم در نظر بگیریم آرایه دوتا میشود همان نقطه ای که در صفحات دو بعدی روی کاغذ با آن کار میکنیم. در مثالهای پیش رو ما با مختصات دو بعدی کار میکنیم ولی شما می‌توانید همچنان مساله OCR را در ذهن خودتان در این مثالها دنبال کنید.

مثال 1- برنامه ای بنویسید که از ورودی دو نقطه (در مختصات دو بعدی) از ورودی دریافت کند و فاصله این دو نقطه را محاسبه کند.

در این مثال داده ورودی ما نقطه است. هر نقطه یک x دارد و یک y. بنابراین بهتر است از یک استراکچر به نام point استفاده کنیم که شامل دو عضو داده ای x و y است. پس وقتی میخواهیم دو نقطه از ورودی دریافت کنیم در واقع میخواهیم دو متغیر از جنس point از ورودی دریافت کنیم. کد زیر را ببینید:

#include <iostream>
#include <cmath>

using namespace std;

struct point {
	double x,y;
};

int main(){
	point p1,p2;
	
	cout<<"Enter point1 (x,y): "<<endl;
	
	cout<<"X: ";
	cin>>p1.x;
	
	cout<<"Y: ";
	cin>>p1.y;

	cout<<"Enter point2 (x,y): "<<endl;
	
	cout<<"X: ";
	cin>>p2.x;
	
	cout<<"Y: ";
	cin>>p2.y;


	double d = sqrt(pow(p1.x - p2.x,2) + pow(p1.y - p2.y,2));
	cout<<"distance between 2 points: "<< d;
}

توضیح کد:
در خط 6 تا 8 استراکچر point تعریف شده است، در خط 13 تا 27 دو نقطه از ورودی دریافت شده است و در خط 30 فاصله دو نقطه چاپ شده است. همانطور که در آموزشهای قبل هم گفته شده است تابع sqr(x) جذر x و تابع pow(a,b) مقدار a به توان b را محاسبه میکند که پارامترهای ورودی این دو تابع حتما باید double باشند ( به همین دلیل است که در استراکچر point متغیرهای x و y را از double تعریف کردیم.
تذکر: همانطور که میدانید فاصله دو نقطه از فرمول زیر محاسبه می‌شود:

مثال 2- برنامه ای بنویسید که از ورودی 100 نقطه دریافت کند و مرکز این نقاط را پیدا کند.

در تصویر زیر تعدادی نقاط سبز رنگ میبینید که دایره مشکی رنگ میانگی همه نقاط است. میانگین نقاط، نقطه ای است که مقدار x آن میانگین همه xها و y آن میانگین همه yهاست.

نوشتن این کد چندان سخت نیست:

#include <iostream>
#include <cmath>

using namespace std;

struct point {
	double x,y;
};

int main(){
	
	int const n=100;
	point points[n];
	int i;
	double sum_x=0,sum_y=0;
	
	for(i=0;i<n;i++){
			
		cout<<"Enter point (x,y): "<<endl;
		
		cout<<"X: ";
		cin>>points[i].x;
		
		cout<<"Y: ";
		cin>>points[i].y;
	}
	
	for(i=0;i<n;i++){
		sum_x = sum_x + points[i].x;
		sum_y = sum_y + points[i].y;
	}
	
	cout<<"mid: ( "<<sum_x/n<<" , "<<sum_y/n<<" )"<<endl;
	
}

مثال 3- برنامه ای بنویسید که از ورودی 100 نقطه دریافت کند. سپس نقطه 101 ام را دریافت کند و نزدیکترین نقطه (از بین 100 نقطه) به نقطه جدید را پیدا کند

این مثال شبیه به صورت مساله ای است که در OCR مطرح کردیم. یعنی 100 داده آموزشی داریم و میخواهیم با استفاده از این 100 داده ، تکلیف داده 101ام را مشخص کنید!

در این مثال به یک آرایه 100 تایی از جنس point نیاز داریم که بتوانیم 100 نقطه را از ورودی بگیریم. با استفاده از فرمولی که در مثال 1 استفاده کردیم فاصله نقطه جدید از تک تک نقاط را پیدا میکنیم و کوتاهترین فاصله را ذخیره میکنیم.

#include <iostream>
#include <cmath>

using namespace std;

struct point {
	double x,y;
};

double distance_points(point p1, point p2);

int main(){
	
	int const n=100;
	point points[n],new_point;
	int i;
	
	
	for(i=0;i<n;i++){
			
		cout<<"Enter point (x,y): "<<endl;
		
		cout<<"X: ";
		cin>>points[i].x;
		
		cout<<"Y: ";
		cin>>points[i].y;
	}
	
	cout<<"Enter new point (x,y): "<<endl;
		
	cout<<"X: ";
	cin>>new_point.x;
		
	cout<<"Y: ";
	cin>>new_point.y;

	double min = distance_points(new_point,points[0]);
	
	
	int idx = 0;
	
	for(i=1;i<n;i++)
		if(distance_points(new_point , points[i]) < min){
			idx = i;
			min = distance_points(new_point,points[i]);
		}
	
	cout<<"("<<new_point.x<<" , "<<new_point.y<<") - "
		<<"("<<points[idx].x<<" , "<<points[idx].y<<") : "<<min;
	
}

double distance_points(point p1, point p2){
	return sqrt(pow(p1.x - p2.x,2) + pow(p1.y - p2.y,2));
}

توضیح کد:
در خط 19 تا 36 اعداد ورودی دریافت شده است. آرایه points شامل 100 نقطه و متغیر new_point شامل آخرین نقطه است. برای محاسبه فاصله بین دو نقطه از تابع distance_points استفاده شده است که دو نقطه از جنس point دریافت می‌کند و فاصله دو نقطه را برمیگرداند.
در خط 38 تا 47 نزدیکترین نقطه به new_point محاسبه شده است. به این صورت که ابتدا فاصله نقطه صفر و new_point در خط 38 در min ذخیره شده است. سپس در حلقه for فاصله تک تک نقاط با new_point محاسبه می‌شود، چنانجه این فاصله از min کم باشد هم min آپدیت می‌شود و هم اندیس نقطه در idx ذخیره می‌شود.

در این نوشته با چند مثال کاربردی که با ساختار قابل پیاده شدن است، آشنا شدید. در قسمت بعد وارد مبحث اشاره گرها و می‌شویم و به صورت کوتاه و مختصر به اشاره گرها می‌پردازیم.

اگر در توضیح مثالهای این آموزش مشکل دارید، حتما در قسمت نظرات سوال خود را مطرح کنید.