وبلاگ شخصی
محمّدرضا
علی حسینی

جایی که تجربیات, علایق
و چیزهایی که یادگرفته‌ام را
با هم مرور می‌کنیم.

آموزش زبان برنامه‌نویسی Rust - قسمت5: حلقه ها + تمرین

آموزش زبان برنامه‌نویسی Rust-قسمت5:حلقه‌ها

حالا که در جلسه‌ی قبلی با دستور شرطی if آشنا شدیم, وقت آن است که سراغ حلقه‌ها برویم تا از این به بعد بتوانیم با هم برنامه‌های واقعی بنویسیم.
راستی یک خبر خوب. از این به بعد اکثر جلسات شامل یک یا چند تمرین می‌شوند تا سریع‌تر و بهتر بتوانیم زبان Rust را با هم یاد بگیریم.
برای این دوره یک repository در گیت‌هاب ساخته‌ام که با کلیک روی این نوشته می‌توانید به آن دسترسی داشته باشید. در این repository پاسخ تمرین‌ها و نمونه کدهای این دوره‌ی آموزشی قرار دارند.
خب برویم سراغ حلقه‌ها.
ما خیلی وقت‌ها نیاز داریم که یک قطعه کدرا چند بار اجرا کنیم. برای همین هم در زبان‌های برنامه‌نویسی مختلف, دستورات تکرار مختلفی ساخته شده است.
خیلی از زبان‌ها سه حلقه‌ی for, while و do while را پشتیبانی می‌کنند. امّا در Rust حلقه‌ای مثل do while وجود ندارد.
به جای آن ما 3 نوع حلقه در Rust داریم: loop, for و while.
حالا هر کدام از این حلقه‌هارا با هم بررسی می‌کنیم.

حلقه loop

ما می‌توانیم بقیه‌ی حلقه‌ها را با همین دستور پیاده‌سازی کنیم(یکی از کارهایی که در تمرین‌های این جلسه انجام می‌دهیم).
حلقه‌ی loop شرطی نمی‌گیرد. یعنی این حلقه تا ابد اجرا می‌شود. اگر با زبان‌های توصیف سخت‌افزار مثل VHDL آشنایی داشته باشید، این نوع حلقه را پیش از این‌ها دیده‌اید.
مثلاً اگر کد زیر را اجرا کنید, تا زمانی که ctrl+c را فشار ندهید برنامه به چاپ کردن مقدارش ادامه می‌دهد(اخطار: این برنامه ممکن است موجب هنگ کردن دستگاهتان شود. لطفاً با رعایت نکات ایمنی و با کمک بزرگترها آن‌را اجرا کنید)

fn main() {
    loop {
        println!("همینطوری این‌رو پرینت می‌کنه...");
    }
}

دستور break

حالا شاید بپرسید که چطوری می‌شود این حلقه‌را متوقّف کرد؟ پاسخ ساده است: با گذاشتن دستور break.
هروقت که جریان اجرای برنامه به دستور break برسد, اجرای حلقه متوقّف می‌شود و کنترل برنامه به دستورات بعد از حلقه منتقل می‌شود.
دستور break یک قابلیّت دیگر هم دارد. شما می‌توانید با این دستور مقداری را خروجی بدهید.
مثلاً کد زیر را ببینید:

fn main() {
    let mut counter = 0;
    let a = loop {
        if counter == 5 {
            break counter;
        }
        counter += 1;
    };
    println!("a = {}", a);
}

در این برنامه اوّل از همه یک متغیّر mutable به نام counter تعریف کردیم تا تعداد تکرارهای حلقه‌را با آن کنترل کنیم.
بعد از آن یک متغیّر دیگر به نام a تعریف کردیم. امّا به جای مقداردهی مستقیم به آن, مقدارش را برابر خروجی حلقه‌ی مقابلش قرار دادیم.
ابتدای حلقه با یک دستور if می‌بینیم که آیا مقدار counter به 5 رسیده است یا نه. اگر رسیده بود, با استفاده از دستور break مقدار متغیّر conter را بر می‌گردانیم.
یعنی هروقت counter برابر با 5 شود ما مقدار a را برابر با آن قرار می‌دهیم.
اگر counter برابر با 5 نبود, مقدارش‌را یک واحد افزایش می‌دهیم.
در این کد ۲ نکته وجود دارد که باید خوب به آن توجّه کنید:
۱-بعد از دستور if از else استفاده نکردیم. چون هروقت که شرط if برقرار شود دیگر از حلقه خارج می‌شویم و مطمئن هستیم که کدهای حالت else اجرا نمی‌شوند.
۲-بعد از بستن آکولاد loop باید ; بگذاریم. چون در غیر این صورت کامپایلر نمی‌فهمد که این statement تمام شده است.
نکته‌ی کنکوری: از این دستور می‌توان در حلقه‌های for و while هم برای متوقّف کردن و خروج از حلقه استفاده کرد. امّا نمی‌توان با آن مقدار برگرداند.

حلقه loop چه کاربردی دارد؟

حلقه‌ی loop کاربردهای فراوانی دارد. مثلاً می‌خواهیم منتظر بمانیم تا یک thread کارش تمام شود یا پاسخی از سرور دریافت کنیم.
اگر شرایط سرراست نباشد, استفاده از حلقه‌ی loop و بررسی وضعیت درون آن بهترین راه است.

حلقه while

اینکه یک حلقه‌را تا زمانی که یک شرط برقرار است تکرار کنیم اتّفاق خیلی خیلی رایجی است. برای همین برای این الگو در زبان Rust یک حلقه‌ی جداگانه داریم.
الگوی کلّی دستور while به شکل زیر است:

while condition {
    // do something
}

تا زمانی که شرط حلقه (condition) درست باشد, حلقه تکرار می‌شود. مثلاً حلقه‌ی زیر تا زمانی که مقدار متغیّر a مضرب 10 نباشد به چاپ کردن آن ادامه می‌‌دهد:

fn main() {
    let mut a = 1;
    while a % 10 != 0 {
        println!("a= {}", a);
        a += 1;
    }
}

حلقه for

خب حالا فرض کنید که ما حلقه‌ای می‌خواهیم که از عدد ۲ تا ۱۰ را بشمارد و یک تکّه کد را با توجّه به آن اجرا کند.
اگر بخواهیم این کار را با while انجام بدهیم, احتمال رخ دادن ارور خیلی زیاد است.
مثلاً فرض کنید که یادمان برود condtion حلقه while را داخل آن آپدیت کنیم. در این صورت یک حلقه‌ی بی‌نهایت به‌وجود می‌آید که دستگاه شما را به خاطر مصرف بیش از اندازه‌ی منابع به فنا می‌دهد.
اینجا پای حلقه‌ی for وسط می‌آید. همین مثالی که زدم را درنظر بگیرید. می‌شود کد زیر:

fn main() {
    for counter in 2..11 {
        println!("counter = {}", counter);
    }
}

خروجی این کد می‌شود این:

counter = 2
counter = 3
counter = 4
counter = 5
counter = 6
counter = 7
counter = 8
counter = 9
counter = 10

حالا کدی که نوشتیم یعنی چی؟ بگذارید حالت کلّی حلقه‌ی for را بررسی کنیم:

for counter in range {
    // your code
}

حلقه‌ی for با کلمه‌ی کلیدی for شروع می‌شود. پس از این کلمه‌ی کلیدی نام شمارنده‌ی حلقه می‌آید. (حواستان باشد که شما نمی‌توانید مقدار شمارنده‌را داخل بدنه‌ی حلقه تغییر بدهید, چون یک متغیّر immutable است).
بعد از شمارنده کلمه‌ی کلیدی in می‌آید. کلمه‌ای که بعد از آن range حلقه قرار می‌گیرد.
درمورد range بعداً به صورت مفصّل صحبت می‌کنیم.
الان فقط کافی است که بدانید وقتی می‌خواهیم بگوییم که حلقه از عدد m شروع شود و به عدد n ختم شود, از مقدار زیر به عنوان range استفاده می‌کنیم:

m..n+1

توجّه کنید که وقتی که می‌خواهیم مثلاً تا عدد 10 را بشماریم, باید حد بالایی range را برابر با 11 قرار بدهیم.

پیمایش مجموعه‌ها با حلقه‌ی for

یکم اسم فارسی‌اش سخت شد. یعنی می‌خواهیم با یک حلقه‌ی for روی تک تک عناصر یک مجموعه(مثلاً یک آرایه) حلقه بزنیم و روی هرکدام یک کد را اجرا کنیم.
مثلاً فرض کنید می‌خواهیم عناصر یک آرایه‌را پرینت کنیم. در حالت عادی کد زیر را می‌زنیم:

fn main() {
    let my_array: [i32;5] = [1,2,3,4,5];
    for counter in (0..5){
        println!("current element= {}", my_array[counter]);
    }
}

این برنامه خروجی زیر را می‌دهد:

current element= 1
current element= 2
current element= 3
current element= 4
current element= 5

خب این روش کار می‌کند, امّا راه حل خیلی خوبی نیست. ممکن است range را اشتباه وارد کنیم و به خاطر درست نبودن index آرایه به ارور بخوریم.
همچنین هربار که می‌خواهیم به عنصر فعلی اشاره کنیم باید تمامی عبارت: my_array[counter] را به کار ببریم. اتّفاقی که در برنامه‌های واقعی اذیت کننده است.
Rust برای این کار راه حل خیلی ساده‌تری دارد. یک نگاهی به کد زیر بیاندازید تا توضیح بدهم:

fn main() {
    let my_array: [i32;5] = [1,2,3,4,5];
    for element in my_array.iter(){
        println!("current element = {}", element);
    }
}

تفاوتش را فهمیدید؟ به جای اینکه روی یک range حلقه بزنیم, روی خود آرایه این کار را کردیم.
همه چیز مثل قبل است, به جز اینکه به جای نوشتن range نام آرایه را می‌نویسیم و آخرش ()iter. اضافه می‌کنیم.
حالا element به جای اینکه مقدار عددی فعلی در range باشد, خود عنصر فعلی در آرایه است.
اینطوری هم مطمئن هستیم که با index به مشکل نمی‌خوریم و هم راحت‌تر به عنصر آرایه دسترسی پیدا می‌کنیم.

 

خب حالا برویم سراغ تمرین‌ها:

تمرین

این تمرین‌ها همان تمرین‌های رایج یادگیری برنامه‌نویسی هستند که به نظرم ارزش اینکه یک بار با Rust هم آن‌ها را امتحان کنیم دارند.
۱-حلقه‌ی while را با حلقه‌ی loop پیاده‌سازی کنید

۲-شکل زیر را با استفاده از حلقه‌ی for چاپ کنید:

*
**
***
****
*****
****
***
**
*

بعد از اینکه خوب با این دو تا تمرین ور رفتید با کلیک روی این نوشته پاسخ‌هایشان‌را ببینید. هدف فقط این است که با syntax زبان Rust بیشتر آشنا بشویم.

 

خواندن قسمت قبلی

رفتن به اوّلین قسمت مجموعه رایگان آموزش زبان برنامه نویسی Rust

«نوشته‌های مرتبط»

پاسخی بگذارید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

«نوشته‌های ویژه»

«نوشته‌های محبوب»

«دیدگاه کاربران»