در قسمت یازدهم از آموزش آردوینو به بررسی تبدیل عدد به رشته (استرینگ) و همچنین، تبدیل رشته (استرینگ) به عدد، پرداختیم. در این قسمت قصد داریم درباره ساختاردهی کد با استفاده از بلوکهای تابع در آردوینو و همچنین، برگرداندن بیش از یک مقدار از یک تابع صحبت کنیم.
تتوابع برای مرتبسازی و سازماندهی کارهایی که اسکتچ شما انجام میدهد، به بلوکهای مجزا استفاده میشوند. توابع، عملکردهای مختلف را در ورودیها (اطلاعاتی که به تابع داده میشود) و خروجیها (اطلاعاتی که از تابع دریافت میشود) بستهبندی میکنند که این کار، ساختاردهی، نگهداری و استفاده مجدد از کد را راحتتر میکند.
شما قبلاً با دو تابع آشنا هستید که در هر اسکتچ آردوینو وجود دارند: setup
و loop
. برای ساخت یک تابع، ابتدا نوع دادهای که تابع برمیگرداند (اطلاعاتی که به شما میدهد)، نام تابع و همچنین پارامترهای اختیاری (مقادیر) که تابع هنگام فراخوانی دریافت میکند، باید مشخص کنید. توابع میتوانند بدون ورودی و خروجی (فقط تکه کدی را اجرا می کند) یا با هر کدام از آن ها یا با هر دو باشند. به ورودی و خروجی توابع پارامترهای تابع گفته می شود.
شما قبلاً با دو تابعی که در هر اسکچ آردوینو وجود دارد آشنا شدید؛ یعنی توابع setup و loop. برای ساخت یک تابع، ابتدا نوع دادهای که تابع برمیگرداند (اطلاعاتی که به شما میدهد)، نام تابع و همچنین پارامترهای اختیاری (مقادیر) که تابع هنگام فراخوانی دریافت میکند، باید مشخص کنید.
اصطلاحات توابع (functions) و متدها (methods) برای اشاره به بلوکهای well-defined از کد استفاده میکنند.
اصطلاحات “تابع” و “متد” برای اشاره به بلوکهای کدی که میتواند توسط بخشهای دیگر برنامه فراخوانی و اجرا شوند، استفاده میشوند. زبان C اینها را به عنوان تابع میشناسد. زبانهای شیءگرا مانند C++ که عملکردها را از طریق کلاسها نمایش میدهند، معمولاً از اصطلاح “متد” استفاده میکنند. آردوینو از ترکیبی از این دو سبک استفاده میکند (اسکتچهای نمونه معمولاً از سبک مشابه C استفاده میکنند؛ کتابخانهها معمولاً بهگونهای نوشته میشوند که متدهای کلاس C++ را نمایش دهند).
در اینجا یک فانکشن ساده وجود دارد که فقط یک LED چشمک میزند و هیچ پارامتری وجود ندارد و چیزی برنمیگرداند (قبل از تابع، void نشان میدهد که چیزی برگردانده نخواهد شد):
1 2 3 4 5 6 7 8 | // blink an LED once void blink1() { digitalWrite(LED_BUILTIN, HIGH); // turn the LED on delay(500); // wait 500 milliseconds digitalWrite(LED_BUILTIN, LOW); // turn the LED off delay(500); // wait 500 milliseconds } |
ورژن زیر دارای یک پارامتر (یک اینتیجر با نام count) است که تعیین میکند، چند بار LED چشمک میزند:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | // blink an LED the number of times given in the count parameter void blink2(int count) { while(count > 0 ) // repeat until count is no longer greater than zero { digitalWrite(LED_BUILTIN, HIGH); // turn the LED on delay(500); // wait 500 milliseconds digitalWrite(LED_BUILTIN, LOW); // turn the LED off delay(500); // wait 500 milliseconds count = count -1; // decrement count } } |
برنامهنویسان باتجربه متوجه خواهند شد که هر دو تابع میتوانند blink
نامگذاری شوند زیرا کامپایلر با توجه به نوع مقادیر استفاده شده در پارامترها آنها را تفکیک خواهد کرد. این رفتار اضافهبار تابع (function overloading) نامیده میشود.
تابع blink2() بررسی میکند که آیا مقدار count برابر 0 است یا خیر که اگر نباشد، LED چشمک می زند و سپس مقدار count یک واحد کاهش پیدا می کند. این کار تا زمانی تکرار می شود که متغیر count دیگر از 0 بیش تر نباشد.
گاهی اوقات، یک پارامتر (parameter) در برخی داکیومنتها، بهعنوان یک ارگومان (argument) ورودی شناخته میشود. در پروژههای عملی، شما میتوانید این دو اصطلاح را یکسان در نظر بگیرید.
در اینجا یک نمونه اسکتچ با تابعی است که یک پارامتر میگیرد و یک مقدار برمیگرداند. این پارامتر طول مدت زمان روشن و خاموش بودن LED را (به میلیثانیه) تعیین میکند. این تابع بهطور مداوم LED را چشمک میزند تا دکمهای فشرده شود و تعداد دفعاتی که LED چشمک زده است از تابع بازمیگردد.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | /* blink3 sketch Demonstrates calling a function with a parameter and returning a value. The LED flashes when the program starts and stops when a switch connected to digital pin 2 is pressed. The program prints the number of times that the LED flashes. */ const int inputPin = 2; // input pin for the switch void setup() { pinMode(LED_BUILTIN, OUTPUT); pinMode(inputPin, INPUT); digitalWrite(inputPin,HIGH); // use internal pull-up resistor (Recipe 5.2) Serial.begin(9600); } void loop(){ Serial.println("Press and hold the switch to stop blinking"); int count = blink3(250); // blink the LED 250 ms on and 250 ms off Serial.print("The number of times the switch blinked was "); Serial.println(count); while(digitalRead(inputPin) == LOW) { // do nothing until they let go of the button } } // blink an LED using the given delay period // return the number of times the LED flashed int blink3(int period) { int blinkCount = 0; while(digitalRead(inputPin) == HIGH) // repeat until switch is pressed // (it will go low when pressed) { digitalWrite(LED_BUILTIN, HIGH); delay(period); digitalWrite(LED_BUILTIN, LOW); delay(period); blinkCount = blinkCount + 1; // increment the count } // here when inputPin is no longer HIGH (means the switch is pressed) return blinkCount; // this value will be returned } |
در زبان C توابع باید قبل از تابع main یا setup در آردوینو معرفی شوند. این معرفی اعلان تابع که یک پروتوتایپ (نمونه اولیه) نامیده می شود که دارای مشخصاتی ازجمله: نام، انواع مقادیری که ممکن است به تابع ارسال شود و نوع بازگشتی تابع میباشد. فرایند ساخت آردوینو، اعلانها را برای شما در زیر کاور (covers) ایجاد میکند؛ بنابراین شما نیازی به پیروی از الزامات استاندارد C برای اعلان تابع جداگانه ندارید.
کد موجود در اینجا سه شکل فراخوانی تابعی را که با آن روبهرو خواهید شد را نشان میدهد. blink1 هیچ پارامتر و مقداری بازگشتی ندارد. شکل آن به این صورت است:
1 2 3 4 | void blink1() { // implementation code goes here... } |
blink2 یک پارامتر میگیرد ولی هیچ مقداری باز نمیگرداند.
1 2 3 4 | void blink2(int count) { // implementation code goes here... } |
blink3 یک پارامتر میگیرد و یک مقدار باز میگرداند.
1 2 3 4 5 6 | int blink3(int period) { int result = 0; // implementation code goes here... return result; // this value will be returned } |
نوع دادهای که قبل از نام تابع قرار میگیرد، نوع برگشتی تابع (خروجی تابع) را نشان میدهد. هنگام نوشتن تابع (نوشتن کدی که تابع و عملکرد آن را تعریف میکند)، نباید در انتهای پرانتز، نقطهویرگول قرار دهید. اما هنگامی که از تابع استفاده میکنید، به یک نقطهویرگول در انتهای خط نیاز دارید که تابع را فراخوانی کند.
اکثر توابعی که با آنها روبهرو میشوید، تغییراتی در این فرمها خواهند داشت.
نوع داده (data type)ای که قبل از نام تابع قرار میگیرد، نوع برگشتی تابع را نشان میدهد. در مورد blink1 و blink2 نوع داده void نشان میدهد که هیچ مقداری برگردانده نمیشود؛ اما در blink3 نوع داده int نشاندهنده آن است یک عدد صحیح برگردانده میشود. هنگام ایجاد توابع، نوع بازگشت را متناسب با عملکردی که تابع انجام میدهد، انتخاب کنید.
توصیه میشود که به توابع خود نامهای مناسب دهید؛ روش رایج انجام این کار این است که کلمات را با حرف بزرگ ابتدای هر کلمه (به جز کلمه اول) ترکیب کنید(به عنوان مثال تابع ()setBlinkerCount قاعده نامگذاری را رعایت میکند). اما بهطورکلی، شما میتوانید از هر روشی که میخواهید، استفاده کنید، اما اگر از یک روش نامگذاری ثابت و یکسان استفاده کنید، به کسانی که کد شما را میخوانند، کمک زیادی خواهید کرد.
تابع blink2 یک پارامتر به نام count دارد (زمانی که تابع فراخوانی میشود، مقدار count بهعنوان پارامتر به تابع منتقل میشود). اما تابع blink3 متفاوت است، زیرا یک پارامتر به نام period دارد.
توابع blink2 ،blink1 و blink3 به ترتیب موارد زیر را انجام میدهند:
تا حالا در این مطلب، مثالهایی از رایجترین شکل یک تابع (تابعی که فقط یک مقدار یا هیچ مقداری برمیگرداند) ارائه کردیم. اما گاهی لازم است که شما بیش از یک مقدار (دو یا چند مقدار) از یک تابع را برگردانید. بنابراین، در ادامه این قسمت روشهای مختلفی برای این کار توضیح داده میشود. سادهترین روش برای درک، این است که تابع برخی از متغیرهای سراسری (global) را تغییر دهد و در واقع چیزی از تابع را برنگرداند:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | /* swap sketch demonstrates changing two values using global variables */ int x; // x and y are global variables int y; void setup() { Serial.begin(9600); } void loop(){ x = random(10); // pick some random numbers y = random(10); Serial.print("The value of x and y before swapping are: "); Serial.print(x); Serial.print(","); Serial.println(y); swap(); Serial.print("The value of x and y after swapping are: "); Serial.print(x); Serial.print(","); Serial.println(y);Serial.println(); delay(1000); } // swap the two global values void swap() { int temp; temp = x; x = y; y = temp; } |
تابع swap با استفاده از متغیرهای سراسری (global) دو مقدار را تغییر میدهد. بهطورکلی، متغیرهای global بهراحتی قابلدرک هستند (متغیرهای global مقادیری هستند که در همهجا قابلدسترسی هستند و هر چیزی میتواند آنها را تغییر دهد)، اما برنامهنویسان حرفهای از این متغیر استفاده نمیکنند؛ زیرا بهراحتی میتوان مقدار این متغیر را تغییر داد یا اگر نام یا نوع یک متغیر سراسری در جایی از اسکچ تغییر کند، ممکن است تابع دیگر کار نکند.
روش بهتر و ایمنتر این است که رفرنسها را به مقادیری که میخواهید تغییر دهید منتقل کنید و به تابع اجازه دهید از رفرنسها برای تغییر مقادیر استفاده کند. این کار بهصورت زیر انجام میشود:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | /* functionReferences sketch demonstrates returning more than one value by passing references */ void setup() { 2.11 Returning More than One Value from a Function | 67 Serial.begin(9600); } void loop(){ int x = random(10); // pick some random numbers int y = random(10); Serial.print("The value of x and y before swapping are: "); Serial.print(x); Serial.print(","); Serial.println(y); swapRef(x,y); Serial.print("The value of x and y after swapping are: "); Serial.print(x); Serial.print(","); Serial.println(y);Serial.println(); delay(1000); } // swap the two given values void swapRef(int &value1, int &value2) { int temp; temp = value1; value1 = value2; value2 = temp; } |
تابع swapRef مشابه توابع قسمت قبل با پارامترهای یکسان است. اما علامت (&) نشان میدهد که پارامترها رفرنس (reference) هستند. این بدان معناست که تغییر در مقادیر درون تابع، مقدار متغیری را که هنگام فراخوانی تابع داده میشود را نیز تغییر میدهد. سپس کد را با حذف دو علامت در تعریف تابع تغییر دهید.
کد تغییریافته باید به شکل زیر باشد:
1 | void swapRef(int value1, int value2) |
اجرای کد نشان میدهد که مقادیر عوض نشدهاند؛ بلکه تغییرات ایجاد شده، به تابع منتقل شده است و هنگامی که تابع بازگردانده میشود، این تغییرات از بین میروند.
روش دیگر، استفاده از استراکچر (ساختار) C است که میتواند چندین فیلد داشته باشد و به شما این امکان را میدهد که انواع دیتا را ارسال و برگردانید.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | /* struct sketch demonstrates returning more than one value by using a struct */ struct Pair { int a, b; }; void setup() { Serial.begin(9600); } void loop() { int x = random(10); // pick some random numbers int y = random(10); struct Pair mypair = { random(10), random(10) }; Serial.print("The value of x and y before swapping are: "); Serial.print(mypair.a); Serial.print(","); Serial.println(mypair.b); mypair = swap(mypair); Serial.print("The value of x and y after swapping are: "); Serial.print(mypair.a); Serial.print(","); Serial.println(mypair.b); Serial.println(); delay(1000); } // swap the two given values Pair swap(Pair pair) { int temp; temp = pair.a; pair.a = pair.b; pair.b = temp; return pair; } |
تابع swapPair از یک ویژگی زبان C به نام struct (یا استراکچر (structure)) استفاده میکند. یک استراکچر میتواند تعدادی متغیر یا پوینتر (اشارهگر) داشته باشد.
حجم حافظهای که یک استراکچر اشغال میکند، برابر با حجم عناصر (المنتهای) آن است (در یک آردوینو 8 بیتی، یک Pair حدود 4 بایت حافظه را اشغال میکند، اما در یک برد 32 بیتی، این حجم به 8 بایت افزایش مییابد).
در زبانهای برنامهنویسی، استراکچرها (Structs) و کلاسها (Classes) دو مفهوم متفاوت هستند. اگرچه در برخی جوانب شباهت دارند، اما تفاوتهای مهمی نیز دارند. در زیر به مقایسه این دو پرداختهایم:
من کاپیتان آردوینو، اسمم میلاده و اینجا هستم تا تجربیاتم در رابطه با آردوینو رو با شما به اشتراک بزارم!
مقالات بیشتراخه اخوی کپی هم میکنید حداقل ترجمشو یکم زحمت کشیده فقط به ترانسلیت نسپارید اخه خودت یبار خوندی؟
سلام دوست عزیز بله یک سری مشکلات ترجمه داشت این مطلب ویرایش شد. ممنون از توجهتون
درود بر شما
خیلی هم عالی . البته برای برگرداندون چند عدد از توابع روش های دیگه ای هم هست . مثل روشی که عدد ثانیه و دقیقه و ساعت توی کتابخانه ds1307 توی کد ویژن بر میگردونه .
لطفا در مورد استراکچر و کلاس ها هم به صورت کامل آموزش و مثال قرار بدید
نویسنده شو !
سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.