تمامی فعالیت ها
این جریان به طور خودکار بروزرسانی می شود
- جدیدا
-
چاپ و حروف چینی کتاب و روزنامه در دهه 40 و 50
sina پاسخی ارسال کرد برای یک مطلب در علمی و عمومیدر دهههای ۱۳۴۰ و ۱۳۵۰ خورشیدی، روزنامهها نقش پررنگی در زندگی فرهنگی و سیاسی مردم ایران داشتن؛ اونم تو دورانی که نه خبری از کامپیوتر بود، نه چاپگر لیزری، نه حتی نرمافزارهای طراحی صفحه. با این حال، روزنامهها هر روز صبح میرسیدن؛ با ستونهای مرتب و عکسهایی که کیفیتشون با استانداردهای اون زمان، واقعاً چشمگیر بود. اما سؤال اصلی اینه: چطور این کار انجام میشد؟ تو این مقاله، قراره نگاهی بندازیم به روند تولید روزنامه تو اون سالها؛ از چاپ متن گرفته تا درج عکس، با همهی ابزارها و مهارتهایی که تو دنیای تماماً آنالوگ دهه ۴۰ و ۵۰ شمسی، باعث میشدن چیزی به اسم «روزنامه» هر روز متولد بشه. حروفچینی: تایپ بدون کیبورداولین مرحلهی تولید هر روزنامه، حروفچینی بود؛ همون کاری که امروز با یه لپتاپ و کیبورد تو چند دقیقه انجامش میدیم. اما اون موقع همهچی دستی و مکانیکی بود. حروفچینی دستی (چاپ سربی)تو سادهترین حالت، حروفچینی یعنی واقعاً «چیدن حروف». هر حرف، یه قطعهی کوچیک فلزی (معمولاً سربی) بود که اپراتورها با دست میچیدن کنار هم تا یه کلمه یا جمله ساخته شه. این حروف روی یه قالب فلزی (بهاسم فرم) چیده میشدن و آماده چاپ میشدن. کار زمانبر و طاقتفرسایی بود، ولی تو نبود گزینهی سریعتر، همین روش استاندارد صنعت چاپ محسوب میشد. یک دستگاه Linotype دستگاههای حروفچینی (Linotype)کمکم دستگاههایی مثل Linotype وارد بازی شدن. اپراتور متن رو تایپ میکرد، دستگاه خودش حروف رو میساخت و کنار هم میچید و خروجیاش یه نوار فلزی برجسته از متن بود. این نوار مستقیماً تو چاپ استفاده میشد. سرعت کار نسبت به حالت دستی خیلی بیشتر بود. عکس در روزنامه: ماجرای کلیشهسازیگذاشتن عکس تو روزنامههای اون دوره خودش ماجرایی مفصل داشت. چون برعکس متن که با قالب فلزی چاپ میشد، عکس با یه تکنیک کاملاً متفاوت وارد صفحه میشد. نمونه ای از تصویر هافتون شده هافتونسازی و کلیشهاول باید تصویر تبدیل میشد به نسخهای قابل چاپ؛ چون چاپ با جوهر سیاه انجام میشد، باید تصویر به صورت نقطهنقطه (هافتون) در میاومد تا سایهروشنها شبیهسازی بشه. این کار با دوربینهای خاص و نگاتیو انجام میشد. بعدش تصویر نقطهدار رو منتقل میکردن روی یه ورق فلزی (معمولاً آلومینیوم یا روی) که بهش میگفتن کلیشه یا زینک. اسیدکاری کلیشهحالا نوبت اسیدکاری بود. قسمتهایی از فلز که قرار نبود جوهر بگیرن، با اسید خورده میشدن و بخشهای برجسته باقی میموندن. این کلیشهها میتونستن جوهر بگیرن و عکس رو چاپ کنن. طراحی صفحه: چسب و قیچیبعد از آماده شدن کلیشههای متن و عکس، نوبت میرسید به طراحی صفحه. این کار یه ترکیب از مهارت فنی و خلاقیت بود. طراحها با برش و چسبوندن تکهها روی یه ماکت، چیدمان نهایی صفحه رو میساختن. گاهی هم برای دقت بیشتر، از کاغذ شفاف استفاده میشد تا جای دقیق هر عنصر مشخص شه. همهچی با دست انجام میشد، بدون حتی یک پیکسل دیجیتال. دستگاه لترپرس چاپ: لترپرس یا افست؟وقتی صفحه نهایی آماده میشد، چاپ آغاز میشد. بسته به تجهیزات چاپخانه، از یکی از دو روش اصلی استفاده میکردن: چاپ لترپرستو این روش سنتی، فرمهای فلزی آغشته به جوهر بودن و مستقیم روی کاغذ فشار داده میشدن. روش قدیمیای بود ولی کیفیت قابلقبولی داشت. چاپ افستتوی افست، اول تصویر روی یه لاستیک مخصوص منتقل میشد و بعدش از اون روی کاغذ. این روش سریعتر بود و کیفیت بالاتری داشت، مخصوصاً برای چاپ رنگی یا تیراژ بالا. حرف آخرتولید روزنامه تو دهههای ۴۰ و ۵۰ شمسی، یه ترکیب جالب از هنر، مهارت و تکنولوژی مکانیکی بود. از حروفچینی دستی گرفته تا کلیشهسازی و طراحی صفحه با دست، همهی این کارها در نهایت منجر به یه روزنامه میشد که صبح فردا تو دکهها آماده بود. شاید امروز با یه لپتاپ و اینترنت بتونیم یه نشریه آنلاین تو چند ساعت بالا بیاریم، ولی نباید یادمون بره که همهی این راحتیها، نتیجهی راهی هست که اون نسل با زحمت و دقت طی کرده. واقعاً باید کلاه از سر برداشت جلوی اونهایی که با سرب و جوهر، دنیای رسانه رو ساختن.
-
چرا اینترنت طبقاتی و محدودیتهای اینترنتی به ضرر همه ماست؟
sina پاسخی ارسال کرد برای یک مطلب در شخصی و روزانهموضوع اینترنت طبقاتی و محدودیتهای دسترسی به اینترنت در سالهای اخیر به یکی از مسائل مورد بحث در جامعه تبدیل شده است. اینترنت طبقاتی به معنای ارائه دسترسی متفاوت به اینترنت بر اساس جایگاه شغلی، اجتماعی یا معیارهای دیگر است که در آن گروهی خاص از دسترسی آزاد و بدون محدودیت برخوردار میشوند، در حالی که دیگران با موانعی مانند فیلترینگ یا سرعت پایین مواجهاند. این رویکرد، همراه با محدودیتهای اینترنتی، نه تنها حقوق کاربران عادی را تضعیف میکند، بلکه به اقتصاد، آموزش و پیشرفت کلی جامعه آسیب میرساند. در این نوشتار، دلایلی برای مخالفت با این سیاستها ارائه میشود و بر ضرورت دسترسی برابر به اینترنت تأکید میگردد. اینترنت: حقی همگانیاینترنت در دنیای امروز دیگر یک ابزار تشریفاتی نیست، بلکه ضرورتی اساسی برای زندگی مدرن است. از دانشجویان در جستوجوی منابع علمی گرفته تا کارآفرینانی که به دنبال توسعه کسبوکار خود در فضای دیجیتال هستند، همه به دسترسی سریع و بدون محدودیت به اینترنت وابستهاند. اینترنت طبقاتی این حق را از بخشهایی از جامعه سلب میکند. به عنوان مثال، فردی که در یک شهر کوچک قصد راهاندازی کسبوکاری آنلاین دارد، اگر به دلیل عدم عضویت در گروههای خاص از دسترسی آزاد محروم باشد، فرصت رشد و رقابت را از دست خواهد داد. این امر نابرابری در دسترسی به فرصتها را تشدید میکند. فیلترینگ نیز پیامدهای مشابهی دارد. محدود شدن دسترسی به پلتفرمهای بینالمللی مانند اینستاگرام یا واتساپ، نه تنها کاربران عادی را متأثر میکند، بلکه کسبوکارهای کوچک را که به این ابزارها برای بازاریابی و ارتباط با مشتریان وابستهاند، با چالشهای جدی مواجه میسازد. برای نمونه، فروشگاهی که از طریق شبکههای اجتماعی محصولات خود را عرضه میکند، با فیلتر شدن این پلتفرمها ممکن است بازار خود را یکشبه از دست بدهد. آسیب به اقتصاد دیجیتالاقتصاد دیجیتال یکی از ستونهای اصلی رشد اقتصادی در جهان معاصر است. از فریلنسرهایی که در پلتفرمهای بینالمللی فعالیت میکنند تا استارتاپهایی که با نوآوری بازار را متحول میسازند، همگی به اینترنت آزاد و پایدار نیاز دارند. اینترنت طبقاتی با محدود کردن دسترسی عادلانه، فرصتهای این گروهها را کاهش میدهد. فریلنسری که برای انجام پروژههای بینالمللی به پلتفرمهای فیلترشده وابسته است، با محدودیتهای اینترنتی از رقابت در بازار جهانی بازمیماند. این امر نه تنها به زیان افراد است، بلکه اقتصاد کشور را از پتانسیلهای موجود محروم میکند. به عنوان نمونه، در سالهای اخیر، فیلترینگ برخی پلتفرمها باعث شده برنامهنویسان و طراحان با دشواریهای فراوانی مواجه شوند. بسیاری از آنها مجبور به استفاده از ابزارهای دور زدن فیلتر شدهاند که هزینهبر و ناامن است. اینترنت طبقاتی این نابرابری را عمیقتر میکند، زیرا تنها گروههای خاصی به دسترسی آزاد مجهز میشوند و دیگران در محدودیت باقی میمانند. تأثیر بر آموزش و پژوهشمحدودیتهای اینترنتی تأثیر منفی قابل توجهی بر آموزش و پیشرفت علمی دارد. بسیاری از منابع علمی، از مقالات تخصصی تا ویدیوهای آموزشی، در پلتفرمهایی قرار دارند که ممکن است در معرض فیلترینگ باشند. دانشجویی که برای انجام پژوهش خود به یک ژورنال بینالمللی نیاز دارد، در صورت مواجهه با محدودیت، یا باید هزینههای گزافی برای ابزارهای دور زدن فیلتر متحمل شود یا از ادامه کار خود صرفنظر کند. اینترنت طبقاتی این مشکل را تشدید میکند، زیرا دسترسی آزاد ممکن است تنها به گروههای خاصی مانند اساتید دانشگاه محدود شود، در حالی که دانشجویان عادی از آن محروم میمانند. این امر شکاف آموزشی را افزایش داده و پیشرفت علمی کشور را کند میکند. تهدید امنیت و حریم خصوصییکی از پیامدهای اینترنت طبقاتی و فیلترینگ، افزایش خطرات امنیتی برای کاربران است. وقتی دسترسی آزاد محدود میشود، بسیاری از افراد به استفاده از ابزارهای غیررسمی مانند فیلترشکنها روی میآورند. این ابزارها اغلب ناامن بوده و خطر سرقت اطلاعات شخصی یا هک شدن را افزایش میدهند. در حالی که دسترسی آزاد و امن به اینترنت میتواند نیاز به این ابزارهای پرریسک را از بین ببرد و امنیت کاربران را تضمین کند. اینترنت طبقاتی و تقویت نابرابریشاید یکی از مهمترین اشکالات اینترنت طبقاتی، ترویج نابرابری در جامعه باشد. وقتی دسترسی به اینترنت بر اساس معیارهای خاص محدود میشود، حس تبعیض در میان شهروندان تقویت میگردد. این که گروهی به دلیل جایگاه خاص خود به منابعی دسترسی داشته باشند که برای دیگران در دسترس نیست، چه تفاوتی با دیگر اشکال تبعیض دارد؟ دسترسی برابر به اینترنت، به عنوان یک زیرساخت عمومی، باید برای همه تضمین شود. پیشنهادهایی برای بهبودبه جای اعمال محدودیتهای اینترنتی و اینترنت طبقاتی، میتوان رویکردهایی را در پیش گرفت که دسترسی همگانی به اینترنت را تقویت کند: تقویت زیرساختهای اینترنتی: سرمایهگذاری در بهبود سرعت و کیفیت اینترنت به جای هزینه برای فیلترینگ، میتواند دسترسی بهتری برای همه فراهم کند. حمایت از کسبوکارهای دیجیتال: ایجاد بستری که پلتفرمهای داخلی و خارجی بتوانند به طور منصفانه فعالیت کنند، به رشد اقتصاد دیجیتال کمک میکند. شفافیت در سیاستگذاری: هرگونه قانونگذاری در حوزه اینترنت باید با شفافیت کامل و با در نظر گرفتن نظرات همه اقشار جامعه انجام شود. سخن پایانیاینترنت طبقاتی و محدودیتهای اینترنتی نه تنها حقوق کاربران عادی را نقض میکند، بلکه به اقتصاد دیجیتال، آموزش و حس برابری در جامعه آسیب میرساند. در جهانی که اینترنت به یکی از مهمترین ابزارهای پیشرفت تبدیل شده، دسترسی آزاد و برابر به آن باید به عنوان یک حق همگانی به رسمیت شناخته شود. به جای ایجاد محدودیت، باید به سمت سیاستهایی حرکت کنیم که اینترنت را برای همه سریع، امن و در دسترس کند. این رویکرد نه تنها به نفع افراد است، بلکه به پیشرفت و توسعه پایدار کشور کمک خواهد کرد.
-
ابزارها و کتابخانههای ساخت رابط کاربری گرافیکی در زبان Rust
sina پاسخی ارسال کرد برای یک مطلب در برنامه نویسیزبان برنامهنویسی Rust به دلیل ایمنی حافظه، عملکرد بالا، و قابلیتهای مدرن خود به یکی از محبوبترین زبانها برای توسعه نرمافزارهای سیستمی تبدیل شده است. اگرچه Rust در ابتدا برای برنامهنویسی سیستمی طراحی شده بود، اما در سالهای اخیر، ابزارها و کتابخانههای متعددی برای ساخت رابطهای کاربری گرافیکی (GUI) در این زبان توسعه یافتهاند. این مقاله به معرفی و بررسی مهمترین ابزارها و کتابخانههای موجود برای ایجاد رابطهای کاربری گرافیکی در Rust میپردازد و ویژگیها، مزایا، و معایب هر یک را مورد بحث قرار میدهد. چرا رابط کاربری گرافیکی در Rust؟Rust با ویژگیهایی مانند ایمنی حافظه، عدم استفاده از زبالهروب (Garbage Collector)، و عملکرد نزدیک به زبانهای سطح پایین مانند C++، گزینهای جذاب برای توسعه برنامههایی است که نیاز به رابط کاربری گرافیکی دارند. در مقایسه با زبانهایی مثل پایتون که کتابخانههای GUI بالغی مانند Tkinter یا PyQt دارند، اکوسیستم Rust در حوزه GUI هنوز در حال توسعه است. با این حال، جامعه فعال Rust و حمایت شرکتهایی مانند موزیلا باعث شده تا ابزارهای قدرتمندی برای این منظور در دسترس قرار گیرند. ساخت رابط کاربری گرافیکی در Rust میتواند برای پروژههایی که نیاز به عملکرد بالا، امنیت حافظه، و کنترل دقیق منابع دارند، بسیار مناسب باشد. از برنامههای دسکتاپ گرفته تا ابزارهای چندپلتفرمی، Rust امکان توسعه برنامههایی با رابط کاربری جذاب و کارآمد را فراهم میکند. معیارهای انتخاب کتابخانه GUIقبل از معرفی کتابخانهها، مهم است که معیارهای انتخاب یک کتابخانه GUI مناسب را در نظر بگیریم: عملکرد: کتابخانه باید با فلسفه Rust برای ارائه عملکرد بالا همخوانی داشته باشد. چندپلتفرمی بودن: پشتیبانی از سیستمعاملهای مختلف (ویندوز، مک، لینوکس) ضروری است. سادگی استفاده: رابط برنامهنویسی (API) باید کاربرپسند و همراستا با سینتکس Rust باشد. جامعه و پشتیبانی: وجود مستندات جامع و جامعه فعال برای رفع مشکلات اهمیت دارد. قابلیتهای گرافیکی: پشتیبانی از ویجتهای متنوع، انیمیشنها، و رندرینگ پیشرفته. اندازه و وابستگیها: کتابخانههای سبکتر با وابستگیهای کمتر برای پروژههای کوچک مناسبتر هستند. حالا به بررسی برخی از مهمترین کتابخانههای GUI در Rust میپردازیم. 1. DruidDruid یک فریمورک GUI متنباز است که به طور خاص برای Rust طراحی شده و بر عملکرد و سادگی تمرکز دارد. این کتابخانه توسط تیم توسعهدهنده Xi Editor ایجاد شده و برای ساخت برنامههای دسکتاپ چندپلتفرمی مناسب است. ویژگیها:معماری دادهمحور: Druid از الگوی دادهمحور (data-driven) استفاده میکند که در آن رابط کاربری به صورت واکنشی (reactive) به تغییرات دادهها پاسخ میدهد. پشتیبانی چندپلتفرمی: روی ویندوز، مک، و لینوکس به خوبی کار میکند. رندرینگ پیشرفته: از موتور رندرینگ Piet استفاده میکند که امکان رندرینگ دوبعدی با کیفیت بالا را فراهم میآورد. سبک و سریع: Druid تلاش میکند تا با حداقل سربار، عملکردی نزدیک به زبانهای سطح پایین ارائه دهد. مزایا:ادغام عالی با اکوسیستم Rust. مستندات مناسب و جامعه رو به رشد. مناسب برای برنامههای دسکتاپ با نیاز به رندرینگ گرافیکی پیشرفته. معایب:هنوز در مراحل اولیه توسعه است و ممکن است برخی ویژگیهای پیشرفته GUI را نداشته باشد. ویجتهای آماده محدودتری نسبت به فریمورکهای بالغ مانند Qt دارد. مثال استفاده:use druid::widget::{Button, Flex, Label}; use druid::{AppLauncher, LocalizedString, Widget, WindowDesc}; fn build_ui() -> impl Widget<u32> { let text = Label::new(|data: &u32, _env: &_| format!("شمارنده: {}", data)); let button = Button::new("افزایش").on_click(|_ctx, data, _env| *data += 1); Flex::column().with_child(text).with_child(button) } fn main() { let main_window = WindowDesc::new(build_ui()).title("برنامه ساده"); AppLauncher::with_window(main_window) .launch(0) .expect("خطا در راهاندازی"); }2. eguiegui یک کتابخانه GUI سبک و فوری (immediate mode) است که برای ساخت رابطهای کاربری ساده و سریع مناسب است. این کتابخانه به دلیل استفاده در ابزارهای گرافیکی و بازیها شناخته شده است. ویژگیها:حالت فوری: برخلاف حالتهای مبتنی بر ویجت (retained mode)، egui در هر فریم رابط کاربری را بازسازی میکند که این روش برای برنامههای پویا مناسب است. سبک و بدون وابستگی: نیازی به کتابخانههای سنگین خارجی ندارد. پشتیبانی از WebAssembly: امکان اجرای برنامههای GUI در مرورگر را فراهم میکند. ادغام با موتورهای بازی: به راحتی با کتابخانههایی مثل wgpu یا ggez ادغام میشود. مزایا:بسیار سبک و مناسب برای پروژههای کوچک یا ابزارهای توسعه. یادگیری آسان برای توسعهدهندگانی که با Rust آشنا هستند. مناسب برای برنامههای تعاملی و گرافیکی مانند ابزارهای ویرایشگر یا داشبورد. معایب:ویجتهای محدودتر نسبت به فریمورکهای سنتی. ممکن است برای برنامههای پیچیده دسکتاپ مناسب نباشد. مثال استفاده:use eframe::egui; fn main() -> Result<(), eframe::Error> { let options = eframe::NativeOptions::default(); eframe::run_simple_native("برنامه egui", options, |ctx, _frame| { egui::CentralPanel::default().show(ctx, |ui| { ui.heading("سلام، egui!"); if ui.button("کلیک کنید").clicked() { println!("دکمه کلیک شد!"); } }); }) }3. SlintSlint (که قبلاً به نام SixtyFPS شناخته میشد) یک فریمورک GUI چندپلتفرمی است که برای ساخت رابطهای کاربری جذاب و مدرن طراحی شده است. این کتابخانه از یک زبان توصیفی (DSL) برای تعریف رابط کاربری استفاده میکند. ویژگیها:زبان توصیفی: رابط کاربری با استفاده از یک سینتکس شبیه به QML تعریف میشود. پشتیبانی از WebAssembly: امکان اجرای برنامهها در مرورگر. عملکرد بالا: بهینهسازی شده برای دستگاههای با منابع محدود. پشتیبانی از انیمیشنها: قابلیت افزودن انیمیشنهای پیچیده به رابط کاربری. مزایا:رابط کاربری زیبا و مدرن با حداقل کدنویسی. مناسب برای برنامههای چندپلتفرمی و تعاملی. مستندات خوب و جامعه رو به رشد. معایب:نیاز به یادگیری زبان توصیفی Slint. هنوز به اندازه Qt یا GTK بالغ نیست. مثال استفاده:import { Button, VerticalBox } from "slint"; MainWindow { title: "برنامه Slint"; VerticalBox { Button { text: "کلیک کنید"; clicked => { println!("دکمه کلیک شد!"); } } } } 4. IcedIced یک کتابخانه GUI متنباز است که از معماری Elm الهام گرفته شده و برای ساخت برنامههای دسکتاپ و وب مناسب است. این کتابخانه بر سادگی و عملکرد تمرکز دارد. ویژگیها:معماری Elm: از الگوی پیاممحور برای مدیریت حالت استفاده میکند. چندپلتفرمی: پشتیبانی از ویندوز، مک، لینوکس، و WebAssembly. رندرینگ انعطافپذیر: از wgpu برای رندرینگ استفاده میکند. جامعه فعال: به سرعت در حال توسعه و بهبود است. مزایا:سینتکس تمیز و قابل فهم. مناسب برای توسعهدهندگانی که با معماریهای واکنشی آشنا هستند. پشتیبانی از WebAssembly برای برنامههای وب. معایب:ویجتهای محدودتر نسبت به فریمورکهای قدیمیتر. مستندات هنوز در حال تکمیل است. مثال استفاده:use iced::widget::{button, column, text}; use iced::{Element, Sandbox, Settings}; #[derive(Default)] struct Counter { value: i32, } #[derive(Debug, Clone)] enum Message { Increment, } impl Sandbox for Counter { type Message = Message; fn new() -> Self { Counter::default() } fn title(&self) -> String { String::from("برنامه Iced") } fn update(&mut self, message: Message) { match message { Message::Increment => self.value += 1, } } fn view(&self) -> Element<Message> { column![ text(format!("شمارنده: {}", self.value)), button("افزایش").on_press(Message::Increment), ] .into() } } fn main() -> iced::Result { Counter::run(Settings::default()) }5. GTK-rsGTK-rs یک اتصال (binding) برای کتابخانه GTK است که به زبان C نوشته شده و یکی از محبوبترین ابزارهای ساخت GUI در لینوکس است. این کتابخانه امکان استفاده از قابلیتهای GTK را در Rust فراهم میکند. ویژگیها:ویجتهای غنی: مجموعه گستردهای از ویجتهای آماده برای ساخت برنامههای پیچیده. پشتیبانی قوی از لینوکس: بهویژه در محیطهای گنوم. ادغام با Glade: امکان طراحی رابط کاربری با ابزارهای گرافیکی. مزایا:بسیار بالغ و پایدار. مناسب برای برنامههای دسکتاپ پیچیده. مستندات و جامعه قوی به دلیل قدمت GTK. معایب:وابستگی به کتابخانههای سیستمی که ممکن است نصب را پیچیده کند. کمتر با فلسفه Rust همخوانی دارد (به دلیل استفاده از C). 6. Qt (با اتصال Rust)Qt یک فریمورک قدرتمند و بالغ برای ساخت GUI است که از طریق اتصالهایی مانند qt-rs در Rust قابل استفاده است. این کتابخانه برای برنامههای دسکتاپ و موبایل مناسب است. ویژگیها:ویجتهای حرفهای: مجموعهای کامل از ابزارهای گرافیکی. چندپلتفرمی: پشتیبانی عالی از ویندوز، مک، لینوکس، و حتی موبایل. ابزارهای طراحی: امکان استفاده از Qt Designer برای طراحی رابط کاربری. معایب:اتصالهای Rust برای Qt هنوز در حال توسعه هستند و ممکن است ناپایدار باشند. وابستگی به کتابخانههای سنگین Qt. نتیجهگیریاکوسیستم Rust در زمینه توسعه رابط کاربری گرافیکی در حال رشد سریع است. کتابخانههایی مانند Druid، egui، Slint، و Iced هر یک ویژگیهای منحصربهفردی ارائه میدهند که بسته به نیاز پروژه میتوانند مناسب باشند. برای پروژههای سبک و سریع، egui و Iced گزینههای عالی هستند، در حالی که Druid و Slint برای برنامههای پیچیدهتر مناسباند. اگر به دنبال فریمورکهای بالغ هستید، GTK-rs و Qt میتوانند انتخابهای خوبی باشند، هرچند ممکن است با پیچیدگیهای بیشتری همراه باشند. توصیه میشود قبل از انتخاب کتابخانه، نیازهای پروژه خود را به دقت بررسی کنید و مستندات و نمونهکدهای هر کتابخانه را آزمایش کنید. با توجه به رشد سریع جامعه Rust، انتظار میرود که در آینده ابزارهای بیشتری برای توسعه GUI در این زبان ارائه شوند.
-
4 تیر ماه 1404
sina پاسخی ارسال کرد برای یک مطلب در شخصی و روزانهخب دیروز جنگ به ظاهر تموم شد و آتش بس برقرار شد. میانجیگری ایالات متحده، قطر و فشارهای پشت پرده از سوی چین و اتحادیه اروپا نهایتاً دو طرف رو پای میز یک آتشبس غیررسمی نشاند. دونالد ترامپ، با بیانیهای رسمی اعلام کرد که "آتشبس برقرار شده و دو طرف متعهد به توقف حملات شدهاند." با این حال، هیچ توافقنامهی مکتوب یا چارچوب دیپلماتیکی تاکنون منتشر نشده! طبق آمار رسمی و سازمانهای مستقل، تلفات ایرانیها بهصورت مختصر به شرح زیر است: وزارت بهداشت ایران: تا ۲۴ ژوئن: ۶۰۶ کشته و ۵٬۳۳۲ زخمی گروه Human Rights Activists (HRANA): بر اساس گزارش تا ۱۹ ژوئن: حدود ۶۳۹ کشته شامل نظامیان، نیروهای امنیتی و غیرنظامی منابع آمریکایی و انگلیسی: برآورد کلی: بین ۶۱۰ تا ۶۱۰+ کشته خلاصه که گلولهها فعلاً ساکت شدهاند، اما صدای تهدید هنوز خاموش نشده! برای منطقهای که از دیرباز درگیر زخمهای عمیق و تنشهای ریشهدار است، آتشبس معنای چندانی نداره
-
1 تیر ماه 1404
sina پاسخی ارسال کرد برای یک مطلب در شخصی و روزانهامروز تولدمه. رسماً وارد سی سالگی شدم... سال پیش تولدم یکی از بهترین روزهای زندگیم بود—کنار کسی که دوستش داشتم، با کلی حس خوب، لبخند و یه عالمه امید به آینده. اصلاً فکر نمیکردم فقط یه سال بعد، تولدم بیفته وسط جنگ، قطعی برق و اینترنت، دلنگرونی، و بدون حضور کسی که بودنش همهچی رو قشنگتر میکرد. زندگی گاهی اونقدر بیرحم و غیرقابل پیشبینی میشه که فقط میتونی نگاهش کنی و بگی: «واقعاً چرا؟» نمیدونم چی باید بنویسم، یا اصلاً چرا دارم مینویسم. شاید فقط دلم میخواد این روزها رو یهجایی ثبت کنم، یهجایی که بعداً که برگشتم و خوندم، یادم بیاد که گذشتم، که تموم شد، که هنوز زندهم. امیدوارم این وضعیت لعنتیِ جنگ خیلی زود مشخص بشه، بدون اینکه اتفاق بدتری بیفته، و اگه قراره چیزی تغییر کنه، حداقل به نفع همه باشه. امیدوارم تولد سال بعد، همین موقع، با کلی خبر خوب و حال بهتر بیام اینجا و برگردم به این پست بخندم؛ نه از غم، از سبک شدن. امیدوارم تا اون موقع، همهمون از این حال بد رد شده باشیم...
-
دفاع از خاک ایران فراتر از اختلافات داخلی است
sina پاسخی ارسال کرد برای یک مطلب در شخصی و روزانهدر شرایط فعلی، هر حمله نظامی بیمحابا به خاک ایران – یا هر کشور دیگری – نهتنها مغایر با اصول بنیادین حقوق بینالمللی است، بلکه منجر به قربانی شدن بیگناهان و نابودی زیرساختهای حیاتی میشود. حمله گسترده و پیشدستانهی اسرائیل به زیرساختهای هستهای و نظامی ایران در تاریخ جمعه ۱۳ ژوئن ۲۰۲۵ (۲۳ خرداد ۱۴۰۴)، که بهادعای رسانههای مختلف شامل بیش از ۲۰۰ هواپیمای جنگی و کشتار ده ها نفر از جمله فرماندهان ارشد نظامی و دانشمندان هستهای بوده، مصداق بارز تجاوز بوده و باید قویاً محکوم شود . ممکن است برخی با این توجیه پاسخ دهند که این حمله برای جلوگیری از توسعه سلاح هستهای بوده، اما این نگرانی هرچقدر هم معتبر باشد، نمیتواند مجوز مبنایی برای نقض حاکمیت ملی، شدتی از آسیب به مردم عادی و استفاده گسترده از ظرفیت نظامی باشد. تجربه نشان داده ضربه زدن به زیرساختها نه منجر به امنیت پایدار میشود و نه اسباب راهحل دیپلماتیک را فراهم میآورد. همواره باید این سؤال کلیدی را پرسید که آیا چنین اقدامی ارزش جان انسان بیگناه را داشت؟ حملات مشابه در گذشته حتی بهصورت نقطهای نیز توانسته آتش تنشها را کمتر کند، دشمنیها را تشدید کرده و خطری قریب الوقوع برای تبدیل شدن منطقه به «دورهای از جنگهای تمامعیار» به همراه داشته است . ایران در واکنش،بیش از ۱۰۰ پهپاد به سوی اسرائیل شلیک کرد؛ اتفاقی که نشان میدهد این تجاوز نهتنها باعث کاهش تهدید نشده بلکه احتمال انتقامجویی را افزایش داده است . ما ممکن است با نظام حاکم بر کشورمان موافق نباشیم، ممکن است اعتقاد داشته باشیم که ساختارهای داخلی نیازمند اصلاح و تحول هستند، اما این به هیچ وجه توجیهی برای پذیرش تجاوز خارجی نیست. اصل استقلال، تمامیت ارضی، و حق دفاع از ملت، از برجستهترین اصول قابل قبول در شکلگیری هر جامعهای هستند. اگر اجازه دهیم یک کشور ثالث با ادعای امنیت خود به خاک ایران حمله کند، چگونه میتوانیم مدعی دفاع از حقوق شهروندی، آزادی و حاکمیت ملی باشیم؟ بهعنوان شهروندی که با مفهوم عدالت اجتماعی و احترام متقابل بزرگ شدهایم، باید بگوییم: نه، هیچ حق اخلاقی برای تجویز تجاوز وجود ندارد؛ و بدون توقف و بازخواست حکومتهای متجاوز، هیچ تضمینی برای صلح و ثبات نخواهد بود. مهمتر از همه؛ جوانان، خانوادهها و نسلهای آینده ما نباید هزینه تصمیمات احساسی و نظامی دیگران را بپردازند. پس بیایید با زبان منطق و اصول حقوقبشری، صدای مخالفتمان را بالا ببریم: تجاوز اسرائیل به ایران، در هر شکل و بهانهای، باید محکوم شود – نه برای حمایت از دولت یا نظام فعلی، بلکه برای حفاظت از جان انسانها، حرمت حاکمیت ملی و شرافت انسانی.
-
کلیدواژه volatile در زبان C: یک ابزار قدرتمند و کمتر شناختهشده برای برنامهنویسی سطح پایین
sina پاسخی ارسال کرد برای یک مطلب در برنامه نویسیزبان برنامهنویسی C به دلیل انعطافپذیری و کنترل سطح پایینی که به برنامهنویس ارائه میدهد، یکی از محبوبترین زبانها برای توسعه سیستمهای نهفته، درایورهای سختافزاری و نرمافزارهای بلادرنگ است. اما یکی از ویژگیهای این زبان که اغلب در آموزشهای ابتدایی کمتر به آن پرداخته میشود، کلیدواژه volatile است. این کلیدواژه، که ممکن است در نگاه اول ساده به نظر برسد، نقش مهمی در بهینهسازی و اطمینان از عملکرد صحیح برنامهها در سناریوهای خاص ایفا میکند. در این مقاله، به بررسی عمیق کلیدواژه volatile، کاربردهای آن، و دلایلی که هر برنامهنویس C باید با آن آشنا باشد، میپردازیم. volatile چیست و چرا اهمیت دارد؟کلیدواژه volatile در زبان C به کامپایلر اطلاع میدهد که مقدار یک متغیر ممکن است بهصورت غیرمنتظره تغییر کند، حتی اگر در کد برنامه به نظر نرسد که این متغیر تغییر میکند. این ویژگی بهویژه در برنامهنویسی سطح پایین، مانند کار با سختافزار، سیستمهای نهفته، یا برنامههای چندنخی، حیاتی است. بدون استفاده از volatile، کامپایلر ممکن است بهینهسازیهایی انجام دهد که منجر به رفتار نادرست برنامه شود. به عنوان مثال، فرض کنید متغیری دارید که مقدار آن توسط یک وقفه سختافزاری یا یک رشته (thread) دیگر تغییر میکند. اگر این متغیر با volatile مشخص نشده باشد، کامپایلر ممکن است فرض کند که مقدار آن ثابت است و بهینهسازیهایی مانند ذخیرهسازی مقدار در یک ثبات (register) یا حذف دسترسیهای مکرر به آن را انجام دهد. این کار میتواند باعث شود که تغییرات واقعی متغیر در حافظه نادیده گرفته شوند. کاربردهای اصلی volatile در زبان Cبرای درک بهتر اهمیت volatile، بیایید به چند سناریوی کاربردی کلیدی نگاه کنیم: 1. کار با سختافزار و رجیسترهای حافظهیکی از رایجترین کاربردهای volatile در برنامهنویسی سیستمهای نهفته است. در این سیستمها، متغیرها اغلب به رجیسترهای سختافزاری (مانند رجیسترهای ورودی/خروجی) نگاشت میشوند. این رجیسترها ممکن است توسط سختافزار بهصورت غیرمنتظره تغییر کنند. به مثال زیر توجه کنید: volatile int *hardware_register = (volatile int *)0x1000;در این کد، متغیر hardware_register به یک آدرس حافظه خاص اشاره دارد که ممکن است توسط سختافزار تغییر کند. استفاده از volatile به کامپایلر میگوید که هر بار که به این متغیر دسترسی پیدا میکند، باید مقدار آن را مستقیماً از حافظه بخواند و نه از ثباتهای داخلی CPU. 2. برنامهنویسی چندنخیدر برنامههای چندنخی، متغیرهایی که بین نخها به اشتراک گذاشته میشوند، ممکن است بهصورت غیرمنتظره تغییر کنند. اگر یک نخ مقداری را تغییر دهد، نخ دیگر باید به مقدار بهروز شده دسترسی داشته باشد. بدون volatile، کامپایلر ممکن است فرض کند که متغیر در یک نخ ثابت است و بهینهسازیهایی انجام دهد که باعث از دست رفتن تغییرات شود. برای مثال: volatile int shared_flag = 0; void thread1() { while (!shared_flag) { // منتظر تغییر پرچم } } void thread2() { shared_flag = 1; // تغییر پرچم }در این مثال، اگر shared_flag بهعنوان volatile تعریف نشود، کامپایلر ممکن است حلقه while را بهینه کند و تغییرات اعمالشده توسط نخ دوم را نادیده بگیرد. 3. مدیریت وقفهها (Interrupts)در سیستمهایی که از وقفههای سختافزاری استفاده میکنند، متغیرهایی که توسط روالهای سرویسدهی وقفه (ISR) تغییر میکنند، باید volatile باشند. این کار تضمین میکند که تغییرات این متغیرها به درستی توسط برنامه اصلی دیده شوند. به مثال زیر توجه کنید: volatile int interrupt_flag = 0; void ISR() { interrupt_flag = 1; } int main() { while (!interrupt_flag) { // منتظر وقفه } // ادامه پردازش }بدون volatile، کامپایلر ممکن است فرض کند که interrupt_flag هرگز تغییر نمیکند و حلقه را بهصورت بینهایت اجرا کند. نکات مهم در استفاده از volatileاستفاده از volatile نیازمند دقت است، زیرا استفاده نادرست از آن میتواند عملکرد برنامه را تحت تأثیر قرار دهد. در ادامه چند نکته کلیدی آورده شده است: استفاده بیش از حد ممنوع: استفاده غیرضروری از volatile میتواند مانع بهینهسازیهای مفید کامپایلر شود و عملکرد برنامه را کاهش دهد. تنها متغیرهایی که واقعاً ممکن است بهصورت غیرمنتظره تغییر کنند را volatile کنید. ترکیب با سایر کلیدواژهها: volatile میتواند با کلیدواژههایی مانند const ترکیب شود. به عنوان مثال، volatile const int نشاندهنده متغیری است که نمیتوان آن را در کد تغییر داد، اما ممکن است توسط عوامل خارجی (مثل سختافزار) تغییر کند. محدودیتها در چندنخی: volatile بهتنهایی برای همگامسازی نخها کافی نیست. برای مدیریت دسترسیهای همزمان، باید از ابزارهایی مانند قفلها (locks) یا موانع حافظه (memory barriers) استفاده کنید. تفاوت volatile با سایر مفاهیم مشابهبرخی ممکن است volatile را با مفاهیمی مانند atomic یا مکانیزمهای همگامسازی اشتباه بگیرند. volatile صرفاً به کامپایلر میگوید که بهینهسازیهای خاصی را انجام ندهد، اما تضمین نمیکند که عملیات روی متغیر بهصورت اتمیک (atomic) انجام شوند. برای عملیات اتمیک، باید از کتابخانههایی مانند stdatomic.h در C11 استفاده کنید. مثال عملی: پیادهسازی یک پرچم وقفهبرای درک بهتر، بیایید یک مثال عملی از استفاده volatile در یک سیستم نهفته ببینیم: #include <stdio.h> volatile int sensor_data = 0; void sensor_interrupt() { sensor_data++; // داده حسگر بهروزرسانی میشود } int main() { while (1) { if (sensor_data > 0) { printf("داده حسگر دریافت شد: %d\n", sensor_data); sensor_data = 0; } } return 0; }در این کد، متغیر sensor_data توسط یک وقفه بهروزرسانی میشود. اگر volatile استفاده نشود، ممکن است کامپایلر شرط if را بهینه کند و تغییرات sensor_data را نادیده بگیرد. چالشها و محدودیتهایکی از چالشهای استفاده از volatile این است که درک دقیق زمان استفاده از آن نیازمند تجربه است. برنامهنویسان تازهکار ممکن است بهاشتباه از آن استفاده کنند یا آن را نادیده بگیرند. همچنین، در برخی معماریهای خاص، ممکن است نیاز به تنظیمات اضافی (مانند موانع حافظه) باشد تا رفتار مورد انتظار تضمین شود. چرا volatile کمتر شناختهشده است؟با وجود اهمیت volatile، این کلیدواژه در آموزشهای ابتدایی C کمتر مورد توجه قرار میگیرد، زیرا کاربردهای آن بیشتر در زمینههای تخصصی مانند سیستمهای نهفته یا برنامهنویسی بلادرنگ دیده میشود. در برنامههای سادهتر، که نیازی به تعامل با سختافزار یا چندنخی نیست، این کلیدواژه کمتر به کار میرود. اما برای برنامهنویسان حرفهای که با سیستمهای پیچیده کار میکنند، volatile یکی از ابزارهای کلیدی است. نتیجهگیریکلیدواژه volatile در زبان C یکی از ابزارهای قدرتمند برای مدیریت متغیرهایی است که ممکن است بهصورت غیرمنتظره تغییر کنند. این ویژگی در سناریوهایی مانند برنامهنویسی سختافزار، سیستمهای نهفته، و برنامههای چندنخی نقش حیاتی دارد. با درک صحیح و استفاده مناسب از volatile، میتوانید از رفتار غیرمنتظره برنامهها جلوگیری کنید و عملکرد قابل اعتمادی را تضمین کنید. اگر به برنامهنویسی سطح پایین علاقهمند هستید، یادگیری و تسلط بر این کلیدواژه میتواند شما را یک قدم به حرفهای شدن نزدیکتر کند. برای مطالعه بیشتر، پیشنهاد میکنم به استانداردهای زبان C (مانند C11) و مستندات کامپایلر خود (مثل GCC یا Clang) مراجعه کنید تا با جزئیات بیشتری از نحوه عملکرد volatile در پروژههای خود آشنا شوید.
-
sina عکس نمایه خود را تغییر داد
-
معرفی ویژگی Readonly Classes در PHP 8.2
sina پاسخی ارسال کرد برای یک مطلب در برنامه نویسیدر PHP 8.1، ویژگی Readonly Properties معرفی شد که به توسعهدهندگان اجازه میداد متغیرهای یک کلاس را بهگونهای تعریف کنند که فقط یکبار مقداردهی شوند و بعد از آن غیرقابل تغییر باشند. این قابلیت برای جلوگیری از تغییرات ناخواسته در متغیرها و افزایش ایمنی کد بسیار مفید بود. اما PHP 8.2 یک قدم فراتر رفت و مفهوم Readonly Classes را معرفی کرد. با این ویژگی، میتوانید یک کلاس را بهصورت کامل فقط خواندنی تعریف کنید، به این معنا که تمام متغیرهای (Properties) آن کلاس بهصورت پیشفرض فقط خواندنی خواهند بود، بدون نیاز به تعریف جداگانه برای هر متغیر. برای درک بهتر، بیایید یک مثال ساده را بررسی کنیم: readonly class UserProfile { public function __construct( public string $username, public string $email, public int $age ) {} }در این کد، کل کلاس UserProfile بهعنوان یک کلاس فقط خواندنی تعریف شده است. این یعنی متغیرهای username، email و age همگی بهصورت خودکار فقط خواندنی هستند و نمیتوان بعد از مقداردهی اولیه، آنها را تغییر داد. اگر سعی کنید مقداری را تغییر دهید، مثلاً: $profile = new UserProfile('ali', 'ali@example.com', 30); $profile->username = 'reza'; // خطا!مفسر PHP خطایی با این مضمون تولید میکند: "Cannot modify readonly property UserProfile::$username". این ویژگی تضمین میکند که دادههای کلاس شما پس از ایجاد نمونه (Instantiation) تغییر نمیکنند و به این ترتیب، یک لایه امنیتی و پایداری به کد اضافه میشود. چرا Readonly Classes مهم است؟شاید با خودتان فکر کنید که این ویژگی خیلی هم جدید نیست و میتوانستید با استفاده از Readonly Properties در PHP 8.1 به نتیجه مشابهی برسید. اما تفاوت اصلی در سادگی و خوانایی کد است. وقتی یک کلاس را بهصورت readonly تعریف میکنید، دیگر نیازی نیست که برای هر متغیر کلمه کلیدی readonly را بهصورت جداگانه بنویسید. این کار نه تنها کد را تمیزتر میکند، بلکه از خطاهای احتمالی ناشی از فراموش کردن افزودن readonly به یک متغیر جلوگیری میکند. علاوه بر این، Readonly Classes به شما کمک میکند تا قصد طراحی (Design Intent) خود را بهوضوح به سایر توسعهدهندگان نشان دهید. وقتی کلاسی را فقط خواندنی تعریف میکنید، به تیم خود (یا حتی خود آیندهتان!) اعلام میکنید که این کلاس قرار است بهعنوان یک موجودیت تغییرناپذیر (Immutable) عمل کند. این موضوع در پروژههای بزرگ که چندین توسعهدهنده روی کد کار میکنند، بسیار ارزشمند است. کاربردهای عملی Readonly Classesحالا بیایید به چند سناریوی واقعی نگاه کنیم که در آنها Readonly Classes میتواند بدرخشد: 1. مدلهای دادهای (Data Models)یکی از رایجترین کاربردهای Readonly Classes در تعریف مدلهای دادهای است که قرار نیست پس از مقداردهی اولیه تغییر کنند. فرض کنید در حال توسعه یک سیستم مدیریت کاربران هستید. میتوانید از یک کلاس فقط خواندنی برای ذخیره اطلاعات پروفایل کاربر استفاده کنید: readonly class User { public function __construct( public int $id, public string $name, public string $email ) {} }این کلاس تضمین میکند که اطلاعات کاربر، مثل شناسه یا ایمیل، پس از ایجاد نمونه تغییر نمیکنند. این موضوع در برنامههایی که نیاز به حفظ یکپارچگی دادهها دارند، مثل سیستمهای مالی یا مدیریت هویت، بسیار مفید است. 2. DTOها (Data Transfer Objects)Readonly Classes برای پیادهسازی اشیاء انتقال داده (DTO) بسیار مناسب هستند. DTOها معمولاً برای انتقال داده بین لایههای مختلف یک برنامه (مثلاً بین لایههای دیتابیس و API) استفاده میشوند. با استفاده از یک کلاس فقط خواندنی، میتوانید مطمئن شوید که دادههای منتقلشده در طول مسیر تغییر نمیکنند: readonly class OrderDTO { public function __construct( public int $orderId, public float $totalAmount, public string $status ) {} }در اینجا، شیء OrderDTO بهعنوان یک بسته دادهای عمل میکند که اطلاعات سفارش را بدون امکان تغییر منتقل میکند. 3. افزایش امنیت در برنامههای وبیکی از چالشهای رایج در توسعه وب، جلوگیری از تغییرات ناخواسته در دادهها به دلیل خطاهای برنامهنویسی یا حملات است. با استفاده از Readonly Classes، میتوانید اطمینان حاصل کنید که دادههای حساس، مثل تنظیمات پیکربندی یا اطلاعات کاربر، بهصورت تصادفی یا مخرب تغییر نمیکنند. 4. بهبود عملکرد در پروژههای بزرگوقتی کلاسی بهصورت فقط خواندنی تعریف میشود، مفسر PHP میتواند بهینهسازیهای بیشتری روی آن اعمال کند، زیرا میداند که متغیرهای این کلاس تغییر نخواهند کرد. این موضوع در پروژههای بزرگ که عملکرد اهمیت زیادی دارد، میتواند تأثیر قابلتوجهی داشته باشد. محدودیتها و نکات قابل توجههرچند Readonly Classes ویژگی قدرتمندی است، اما محدودیتهایی هم دارد که باید به آنها توجه کنید: عدم امکان تغییر پس از مقداردهی: وقتی کلاسی را فقط خواندنی تعریف میکنید، هیچیک از متغیرهای آن قابل تغییر نیستند، حتی اگر در شرایط خاصی نیاز به تغییر داشته باشید. در چنین مواردی، باید از یک کلاس معمولی استفاده کنید یا منطق برنامه را بازطراحی کنید. محدودیت در ارثبری: اگر یک کلاس فقط خواندنی را به ارث ببرید، کلاس فرزند هم باید فقط خواندنی باشد. این موضوع میتواند در برخی طراحیهای پیچیده محدودیت ایجاد کند. سازگاری با نسخههای قدیمی: این ویژگی فقط در PHP 8.2 و بالاتر در دسترس است. اگر پروژه شما نیاز به پشتیبانی از نسخههای قدیمیتر PHP دارد، باید راهحلهای جایگزین (مثل Readonly Properties یا Getterها) را در نظر بگیرید. چگونه از Readonly Classes استفاده کنیم؟برای استفاده از این ویژگی، کافی است کلمه کلیدی readonly را قبل از تعریف کلاس قرار دهید. همچنین، بهتر است متغیرهای عمومی (Public Properties) را در Constructor تعریف کنید تا مقداردهی اولیه بهصورت شفاف انجام شود. مثال زیر یک نمونه کاملتر را نشان میدهد: readonly class Product { public function __construct( public string $name, public float $price, public int $stock ) {} public function getSummary(): string { return "Product: {$this->name}, Price: {$this->price}, Stock: {$this->stock}"; } } $product = new Product('Laptop', 999.99, 10); echo $product->getSummary(); // خروجی: Product: Laptop, Price: 999.99, Stock: 10در این مثال، کلاس Product نه تنها دادهها را بهصورت فقط خواندنی نگه میدارد، بلکه با استفاده از متد getSummary، میتوانید منطق نمایش دادهها را بهراحتی پیادهسازی کنید. مقایسه با سایر زبانهاویژگیهایی مثل Readonly Classes در زبانهای دیگر هم وجود دارند. برای مثال، در جاوا میتوانید از کلمه کلیدی final برای متغیرها یا کلاسها استفاده کنید تا تغییرناپذیر شوند. در پایتون هم با استفاده از کتابخانههایی مثل dataclasses و ویژگی frozen=True میتوانید رفتار مشابهی را پیادهسازی کنید. اما آنچه PHP را متمایز میکند، سادگی و یکپارچگی این ویژگی با سینتکس موجود است که آن را برای توسعهدهندگان وب بسیار کاربردی میکند. نتیجهگیریویژگی Readonly Classes در PHP 8.2 شاید در نگاه اول یک تغییر کوچک به نظر برسد، اما تأثیر آن در نوشتن کدهای تمیز، امن و قابل نگهداری قابلتوجه است. این ویژگی به شما کمک میکند تا دادههای تغییرناپذیر را بهسادگی مدیریت کنید، از خطاهای ناخواسته جلوگیری کنید و قصد طراحی خود را بهوضوح به سایر توسعهدهندگان منتقل کنید. اگر در حال کار روی یک پروژه جدید هستید یا قصد دارید پروژه موجودتان را به PHP 8.2 ارتقا دهید، حتماً این ویژگی را امتحان کنید. با کمی تمرین، متوجه خواهید شد که Readonly Classes میتواند به یکی از ابزارهای اصلی شما در توسعه وب تبدیل شود.
-
نگاهی به ماکرو ها در زبان برنامهنویسی Rust
sina پاسخی ارسال کرد برای یک مطلب در برنامه نویسیماکروها (Macros) یکی از ویژگیهای قدرتمند زبان Rust هستند که به شما امکان میدهند کدهایی بنویسید که کدهای دیگری تولید کنند. این ویژگی در مواردی مانند حذف کد تکراری، ایجاد DSLهای خاصمنظوره، یا پیادهسازی metaprogramming patterns بسیار کاربردی است. در این مقاله با انواع ماکروها در Rust، مزایا، معایب و نحوهی استفادهی صحیح از آنها آشنا میشویم. چرا ماکرو؟ماکروها زمانی بهکار میآیند که کد شما دارای الگوهای تکراری زیادی باشد که با فانکشنها بهراحتی قابل بازنویسی نیستند. برخلاف توابع، ماکروها قبل از مرحلهی کامپایل اجرا میشوند و میتوانند ساختارهای مختلفی از کد را تولید کنند. بهعبارت دیگر، ماکروها به شما اجازه میدهند تا در مرحلهی کامپایل کد تولید کنید. انواع ماکروها در RustRust از دو نوع اصلی ماکرو پشتیبانی میکند: 1. Declarative Macros (با استفاده از macro_rules!)این نوع ماکروها که با macro_rules! تعریف میشوند، از الگوهای تطبیقی برای تولید کد استفاده میکنند. این همان چیزی است که بیشتر توسعهدهندگان Rust در ابتدا با آن برخورد میکنند. macro_rules! say_hello { () => { println!("Hello!"); }; } fn main() { say_hello!(); }ماکرو بالا هر بار که فراخوانی شود، کدی تولید میکند که println! را اجرا میکند. کاربرد آن بسیار شبیه توابع است، اما با این تفاوت که در مرحلهی pre-processing اجرا میشود. از قابلیتهای پیشرفتهتر macro_rules! میتوان به الگوهای چندگانه، تکرار با * و +، و پترنمچینگ اشاره کرد. مثلاً: macro_rules! create_functions { ($($name:ident),*) => { $( fn $name() { println!("You called {:?}!", stringify!($name)); } )* } } create_functions!(foo, bar, baz);2. Procedural Macrosماکروهای پردازشی نوع پیشرفتهتری از ماکروها هستند که امکان اعمال تغییر روی AST (Abstract Syntax Tree) را فراهم میکنند. این ماکروها به سه دسته تقسیم میشوند: - #[derive]برای تولید کد بهصورت خودکار روی ساختارها. مثلاً: #[derive(Debug, Clone, PartialEq)] struct Point { x: i32, y: i32, }- Attribute-like Macrosماکروهایی که با علامت #[] روی آیتمهایی مثل تابع یا ساختار اعمال میشوند: #[route(GET, "/")] fn index() {}- Function-like Macrosشبیه به macro_rules! ولی با انعطاف بیشتر. مثلاً: my_macro!(...);این نوع ماکروها معمولاً در crate جداگانهای نوشته میشوند و از proc_macro استفاده میکنند. نحوهی نوشتن یک ماکرو Procedural سادهنوشتن ماکروهای procedural نیاز به استفاده از crate proc-macro دارد. بهطور معمول، این کدها در crate جداگانه با نوع proc-macro = true تعریف میشوند. extern crate proc_macro; use proc_macro::TokenStream; #[proc_macro] pub fn make_answer(_item: TokenStream) -> TokenStream { "fn answer() -> u32 { 42 }".parse().unwrap() }مزایای استفاده از ماکروهاکاهش کد تکراری: میتوانید با چند خط ماکرو، صدها خط کد مشابه تولید کنید. افزایش خوانایی در برخی موارد: ماکروهای خوب طراحیشده میتوانند کدی شفافتر ارائه دهند. ایجاد DSL: ماکروها امکان ساخت syntax سفارشی را فراهم میکنند. معایب ماکروهاعیبیابی دشوار: خطاهای کامپایل در ماکروها ممکن است گنگ و پیچیده باشند. کاهش وضوح کد: اگر بیشازحد از ماکرو استفاده شود، کد میتواند مبهم و غیرقابلخواندن شود. افزایش زمان کامپایل: چون ماکروها قبل از کامپایل اجرا میشوند، میتوانند زمان build را افزایش دهند. چه زمانی ماکرو ننویسیم؟اگر میتوانید با یک تابع ساده یا generic مشکل را حل کنید، ماکرو ننویسید. ماکروها باید آخرین گزینه باشند، نه اولین. آنها ابزاری قدرتمند ولی دو لبهاند: هم میتوانند کدتان را زیباتر کنند، هم آن را به یک هیولای غیرقابل نگهداری تبدیل کنند. مثال کاربردی: DSL برای تستهافرض کنید میخواهید یک DSL برای تست بنویسید: macro_rules! test_case { ($name:ident, $body:block) => { #[test] fn $name() $body }; } test_case!(simple_addition, { assert_eq!(2 + 2, 4); });نتیجهگیریماکروها در Rust ابزاری بسیار قدرتمند برای تولید کد هستند که با درک صحیح و استفادهی هوشمندانه میتوانند کدهای پیچیده را ساده کنند. اما باید با احتیاط از آنها استفاده کرد، چرا که سوءاستفاده از آنها منجر به ایجاد کدی میشود که نه تنها نگهداری آن سخت است، بلکه ممکن است شما را در کامپایلهای نیمساعته غرق کند.
-
همهچیز درباره فایل Cargo.toml در Rust: راهنمای جامع
sina پاسخی ارسال کرد برای یک مطلب در برنامه نویسیدر زبان برنامهنویسی Rust، ابزار Cargo نقش کلیدی در مدیریت پروژهها ایفا میکند. این ابزار قدرتمند به توسعهدهندگان کمک میکند تا فرآیند ساخت، مدیریت وابستگیها و انتشار پروژهها را بهسادگی انجام دهند. در مرکز هر پروژهای که با Cargo مدیریت میشود، فایلی به نام Cargo.toml قرار دارد. این فایل، که به نوعی تنظیمات اصلی پروژه را در خود جای داده، مشخص میکند که پروژه چگونه باید ساخته شود، چه وابستگیهایی دارد و چه ویژگیهایی باید فعال شوند. در این مقاله، قصد داریم بهصورت جامع و دقیق تمام جنبههای فایل Cargo.toml را بررسی کنیم تا بتوانید با تسلط بیشتری از آن در پروژههای خود استفاده کنید. فایل Cargo.toml چیست و چه اهمیتی دارد؟فایل Cargo.toml در واقع فایل پیکربندی اصلی پروژههای Rust است که با استفاده از ابزار Cargo مدیریت میشوند. این فایل در ریشه پروژه قرار میگیرد و اطلاعاتی مانند نام پروژه، نسخه، وابستگیها و تنظیمات مختلف را در خود نگه میدارد. فرمت این فایل بر پایه TOML (مخفف Tom's Obvious, Minimal Language) است که یک زبان ساده و خوانا برای تعریف تنظیمات محسوب میشود. هر زمان که دستوری مانند cargo build یا cargo run اجرا میکنید، Cargo ابتدا به سراغ این فایل میرود تا اطلاعات لازم را از آن بخواند. به همین دلیل، درک ساختار و قابلیتهای این فایل برای هر توسعهدهنده Rust ضروری است. ساختار کلی فایل Cargo.tomlیک فایل Cargo.toml ساده ممکن است به این شکل باشد: [package] name = "my_project" version = "0.1.0" edition = "2021" [dependencies] serde = "1.0"در این مثال، دو بخش اصلی دیده میشود: بخش [package] که اطلاعات پایه پروژه را مشخص میکند و بخش [dependencies] که وابستگیهای پروژه را تعریف میکند. اما این تنها بخش کوچکی از قابلیتهای این فایل است. در ادامه، تمام بخشها و فیلدهای ممکن را بهصورت دقیق بررسی خواهیم کرد. بخش [package]: اطلاعات پایه پروژهبخش [package] شامل اطلاعات اصلی پروژه است که برای شناسایی و مدیریت آن استفاده میشود. فیلدهای مهم این بخش عبارتاند از: name: نام پروژه را مشخص میکند. این نام باید منحصربهفرد باشد، بهخصوص اگر قصد دارید پروژه را در crates.io منتشر کنید (مثال: "my_project"). version: نسخه پروژه، که معمولاً از فرمت Semantic Versioning پیروی میکند (مثال: "0.1.0"). edition: نسخه ادیشن Rust که پروژه از آن استفاده میکند (مانند "2021", "2018" یا "2015"). این فیلد مشخص میکند که کد شما با کدام مجموعه از قوانین و ویژگیهای Rust سازگار است. authors: فهرست نویسندگان پروژه (مثال: ["Sina Jalalvandi <sina@example.com>"]). description: توضیحی کوتاه درباره پروژه، که در صورت انتشار در crates.io نمایش داده میشود. license: نوع مجوز پروژه (مانند "MIT" یا "Apache-2.0"). repository: آدرس مخزن پروژه (مثلاً لینک گیتهاب). homepage: آدرس وبسایت پروژه (در صورت وجود). keywords: کلمات کلیدی مرتبط با پروژه برای جستجو در crates.io (مثال: ["web", "rust", "api"]). categories: دستهبندیهای پروژه (مثال: ["web-development", "data-structures"]). readme: مسیر فایل README پروژه (مثال: "README.md") که در زمان انتشار استفاده میشود. rust-version: حداقل نسخه Rust موردنیاز برای پروژه (مثال: "1.60")؛ این فیلد اختیاری است. یک نمونه کاملتر از بخش [package]: [package] name = "cool_app" version = "0.2.0" edition = "2021" authors = ["Sara Ahmadi <sara@example.com>"] description = "یه برنامه ساده برای تست Rust" license = "MIT" repository = "https://github.com/sara/cool_app" homepage = "https://sara.example.com/cool_app" keywords = ["rust", "test", "app"] categories = ["development-tools"] rust-version = "1.65" بخش [dependencies]: تعریف وابستگیهابخش [dependencies] برای مشخص کردن پکیجهای خارجی (crates) موردنیاز پروژه استفاده میشود. میتوانید نسخه دقیق یا بازهای از نسخهها را برای هر وابستگی تعیین کنید. چند روش رایج برای تعریف وابستگیها: نسخه ساده: مانند serde = "1.0" که نسخه 1.0 از crate موردنظر را مشخص میکند. بازه نسخه: مانند serde = ">1.0, <2.0" که هر نسخهای بین 1.0 و 2.0 را شامل میشود. با ویژگیها (features): اگر crate موردنظر قابلیتهای اختیاری داشته باشد، میتوانید آنها را فعال کنید: [dependencies] serde = { version = "1.0", features = ["derive"] }مسیر محلی: اگر crate در سیستم شما باشد: [dependencies] my_local_crate = { path = "../my_local_crate" }مخزن گیت: برای استفاده مستقیم از یک مخزن گیت: [dependencies] some_crate = { git = "https://github.com/user/some_crate" }بخش [dev-dependencies]: وابستگیهای توسعهاین بخش برای وابستگیهایی است که فقط در مرحله توسعه و تست استفاده میشوند و در نسخه نهایی پروژه (هنگام اجرای cargo build --release) وارد نمیشوند. مثال: [dev-dependencies] assert_approx_eq = "1.1"بخش [build-dependencies]: وابستگیهای ساختاگر پروژه شما از اسکریپت ساخت (build script) استفاده میکند، وابستگیهای موردنیاز آن را میتوانید در این بخش تعریف کنید: [build-dependencies] cc = "1.0"بخش [features]: قابلیتهای اختیاریبخش [features] به شما امکان میدهد قابلیتهای اختیاری برای پروژه تعریف کنید که کاربران بتوانند آنها را فعال یا غیرفعال کنند. مثال: [features] default = ["logging"] logging = ["log"] extra = []در این مثال، default مشخص میکند که ویژگی logging بهصورت پیشفرض فعال باشد. ویژگی extra نیز تعریف شده اما فعلاً وابستگیای ندارد. بخش [lib]: تنظیمات کتابخانهاگر پروژه شما یک کتابخانه (library) است، میتوانید تنظیمات آن را در این بخش مشخص کنید: name: نام کتابخانه (در صورتی که با نام پکیج متفاوت باشد). path: مسیر فایل اصلی کتابخانه (پیشفرض: src/lib.rs). crate-type: نوع خروجی کتابخانه (مانند "cdylib" برای کتابخانههای سازگار با C). مثال: [lib] name = "my_lib" path = "src/my_lib.rs" crate-type = ["rlib"]بخش [[bin]]: تنظیمات فایلهای اجراییاگر پروژه شما شامل برنامههای اجرایی (binary) است، میتوانید چندین فایل اجرایی را در این بخش تعریف کنید: [[bin]] name = "my_app" path = "src/main.rs" [[bin]] name = "another_app" path = "src/another.rs"بخش [profile]: تنظیمات بهینهسازیاین بخش برای تنظیم نحوه بهینهسازی پروژه در مراحل مختلف ساخت استفاده میشود. سه پروفایل اصلی وجود دارد: [profile.dev]: برای مرحله توسعه (با دیباگ فعال). [profile.release]: برای نسخه نهایی (بهینهسازی بالا). [profile.test]: برای تستها. مثال: [profile.release] opt-level = 3 # حداکثر بهینهسازی debug = falseبخش [workspace]: مدیریت چند پروژهاگر چندین پروژه مرتبط دارید، میتوانید آنها را در یک Workspace مدیریت کنید: [workspace] members = ["crate1", "crate2"] بخش [badges]: نمایش وضعیت پروژهبخش [badges] در فایل Cargo.toml به شما امکان میدهد وضعیت پروژه را بهصورت بصری و با استفاده از برچسبهای کوچک (badges) نمایش دهید. این برچسبها معمولاً در صفحه پروژه در crates.io یا در مستندات (مثلاً README گیتهاب) نشان داده میشوند و اطلاعاتی درباره وضعیت توسعه، تستها یا کیفیت پروژه به کاربران ارائه میدهند. استفاده از بجها میتواند پروژه شما را حرفهایتر نشان دهد و اعتماد کاربران را جلب کند. بجهای رایجدر ادامه، چند نمونه از بجهای رایجی که میتوانید در این بخش تعریف کنید، آورده شده است: maintenance: این بج وضعیت نگهداری پروژه را نشان میدهد. مقادیر ممکن برای آن عبارتاند از: "actively-developed": پروژه در حال توسعه فعال است. "passively-maintained": پروژه بهصورت غیرفعال نگهداری میشود. "as-is": پروژه بدون تغییر ارائه شده است. "experimental": پروژه آزمایشی است. "deprecated": پروژه منسوخ شده است. مثال: [badges] maintenance = { status = "actively-developed" }codecov: برای نمایش پوشش تست (code coverage) پروژه، اگر از سرویسی مثل Codecov استفاده میکنید. مثال: [badges] codecov = { repository = "user/repo", branch = "main", service = "github" }بجهای دیگر: بجهای دیگری مانند travis-ci (برای Travis CI)، circle-ci (برای CircleCI)، یا is-it-maintained-issue-resolution (برای نمایش میانگین زمان حل مسائل) نیز وجود دارند که بسته به نیاز پروژه میتوانید از آنها استفاده کنید. نکات مهم درباره بجهامحدودیت نمایش: همه بجها در crates.io نمایش داده نمیشوند. برای مثال، بج maintenance در crates.io نشان داده میشود، اما بجهای مربوط به CI/CD (مانند github-actions) بیشتر در README گیتهاب استفاده میشوند. تنظیمات اضافی: برای بجهای مربوط به CI/CD یا پوشش تست، باید ابتدا سرویس مربوطه (مثلاً GitHub Actions یا Codecov) را در پروژه خود تنظیم کرده باشید. نمایش در README: برای نمایش بجها در README، میتوانید از لینکهای مستقیم بج (مثلاً از shields.io یا خود سرویس) استفاده کنید. بهعنوان مثال: استفاده از بجها نهتنها اطلاعات مفیدی به کاربران ارائه میدهد، بلکه نشاندهنده توجه شما به کیفیت و نگهداری پروژه است. نکات تکمیلی و کاربردیفایل Cargo.lock: این فایل همراه با Cargo.toml ایجاد میشود و نسخه دقیق وابستگیها را قفل میکند. برای پروژههای اجرایی توصیه میشود آن را در گیت نگه دارید، اما برای کتابخانهها معمولاً نیازی نیست. دستورات مفید: میتوانید از cargo check برای بررسی سریع پروژه یا از cargo update برای بهروزرسانی وابستگیها استفاده کنید. فیلدهای سفارشی: امکان افزودن فیلدهای سفارشی با پیشوند metadata وجود دارد (مثال: [package.metadata.docs]). حرف آخرفایل Cargo.toml یکی از مهمترین اجزای هر پروژه Rust است که با ابزار Cargo مدیریت میشود. این فایل با ساختار ساده اما قدرتمند خود، امکان مدیریت پروژه، وابستگیها و تنظیمات مختلف را فراهم میکند. در این مقاله، تمام بخشها و قابلیتهای این فایل را بررسی کردیم تا بتوانید با اطمینان بیشتری از آن در پروژههای خود استفاده کنید. اگر سؤال یا نکتهای در این زمینه دارید، خوشحال میشوم در بخش نظرات با شما در میان بگذارم.
-
سندروم ایمپاستر بین برنامهنویسها و مهندسهای نرمافزار: چرا همیشه فکر میکنیم به اندازه کافی خوب نیستیم؟
sina پاسخی ارسال کرد برای یک مطلب در علمی و عمومیسندروم ایمپاستر یا همون حس جعل هویت، یه موضوع خیلی رایج بین برنامهنویسها و مهندسهای نرمافزاره که شاید خودمون هم گاهی نفهمیم داریم باهاش دستوپنجه نرم میکنیم. فکرش رو بکن: ساعتها کد زدی، پروژه رو به موقع تحویل دادی و حتی تونستی باگهای پیچیده رو حل کنی، ولی هنوز یه صدای کوچیک توی سرت میگه: «تو فقط شانسی این کار رو کردی، بالاخره یه روز همه میفهمن که تو به اندازه کافی خوب نیستی.» اگه این حس برات آشناست، بدون که تنها نیستی. توی این مقاله قراره با هم نگاه عمیقتری به سندروم ایمپاستر بندازیم، ببینیم چرا توی دنیای برنامهنویسی اینقدر شایعه و چطور میتونیم باهاش کنار بیایم یا حتی ازش به نفع خودمون استفاده کنیم. سندروم ایمپاستر چیه و چرا برنامهنویسها رو هدف گرفته؟سندروم ایمپاستر یه حس روانشناختیه که باعث میشه آدما موفقیتهاشون رو به شانس، زمانبندی خوب یا حتی اشتباه بقیه نسبت بدن، نه به توانایی و تلاش خودشون. حالا چرا توی دنیای برنامهنویسی و مهندسی نرمافزار اینقدر این حس قویه؟ یه دلیلش اینه که این حوزه همیشه در حال تغییره. یه روز داری با یه فریمورک جدید کار میکنی، فردا یه تکنولوژی دیگه میاد که انگار همهچیز رو از نو باید یاد بگیری. این سرعت تغییرات باعث میشه خیلی از برنامهنویسها فکر کنن که همیشه یه پله عقبتر از بقیهان. یه چیز دیگه هم هست: فرهنگ مقایسه توی این صنعت. توی شبکههای اجتماعی مثل توییتر یا لینکدین، مدام میبینیم که فلان برنامهنویس یه پروژه خفن رو توی دو روز تموم کرده یا یه نفر دیگه توی ۲۵ سالگی مدیر فنی یه شرکت بزرگ شده. این مقایسهها باعث میشه خودمون رو زیر سؤال ببریم و فکر کنیم که «من چرا هنوز اونجا نیستم؟» نشانههای سندروم ایمپاستر توی زندگی یه برنامهنویسشاید برات پیش اومده که یه پروژه رو با موفقیت تموم کنی، ولی به جای اینکه به خودت افتخار کنی، بشینی فکر کنی که «اگه بیشتر وقت گذاشته بودم، بهتر نمیشد؟» یا مثلاً موقع کد زدن، مدام از خودت بپرسی که «اگه همکارم این کد رو ببینه، فکر نمیکنه خیلی مبتدیام؟» اینا همشون نشونههای سندروم ایمپاستره. بیایم چند تا از این نشانهها رو با هم مرور کنیم: ترس از لو رفتن: همیشه فکر میکنی که یه روز بقیه میفهمن تو اون آدم باهوشی که فکر میکنن نیستی. کم اهمیت دونستن موفقیتها: مثلاً میگی «این پروژه که چیزی نبود، هرکی دیگه هم بود میتونست انجامش بده.» فشار برای کامل بودن: اگه کدت ۱۰۰٪ بینقص نباشه، حس میکنی شکست خوردی. خودت رو با بقیه مقایسه میکنی: مدام خودت رو با برنامهنویسهای دیگه که به نظرت موفقترن مقایسه میکنی و حس میکنی به گرد پاشون هم نمیرسی. چرا برنامهنویسها بیشتر درگیر این حس میشن؟یه لحظه فکر کن به محیط کار یه برنامهنویس: ددلاینهای فشرده، پروژههای پیچیده و انتظاراتی که گاهی از واقعیت خیلی دورن. توی این موقعیتها، حتی اگه کارمون رو خوب انجام بدیم، ممکنه فکر کنیم که فقط داریم ادای یه برنامهنویس حرفهای رو درمیآریم. یه دلیل دیگه هم اینه که توی برنامهنویسی، همیشه یه راه حل «بهتر» وجود داره. مثلاً کدت کار میکنه، ولی یه نفر میگه «اگه از این الگوریتم استفاده کرده بودی، بهینهتر بود.» این جور بازخوردها، حتی اگه سازنده باشن، میتونن حس ناکافی بودن رو تقویت کنن. یه موضوع دیگه هم هست: خیلی از برنامهنویسها خودشون یاد گرفتن که چطور کد بزنن. این یعنی ممکنه مدرک رسمی نداشته باشن و همین باعث بشه فکر کنن که از بقیه «کمترن». در حالی که توی دنیای واقعی، مهارت و تجربه خیلی بیشتر از یه تکه کاغذ ارزش داره. چطور با سندروم ایمپاستر کنار بیایم؟حالا که فهمیدیم این حس از کجا میاد، بیایم ببینیم چطور میتونیم باهاش روبهرو بشیم. خبر خوب اینه که سندروم ایمپاستر یه چیز قابل مدیریته و حتی میتونه به یه نیروی مثبت تبدیل بشه. این چند تا راهکار رو امتحان کن: موفقیتهات رو ثبت کن: یه دفترچه داشته باش و هر بار که یه باگ رو حل کردی، یه پروژه رو تموم کردی یا حتی یه خط کد خوب نوشتی، یادداشتش کن. این کار باعث میشه به مرور ببینی که چقدر توانایی داری. با بقیه حرف بزن: یه بار با همکارات یا دوستات که برنامهنویس هستن راجع به این حس صحبت کن. میبینی که خیلیها همین حس رو دارن و تو تنها نیستی. کمالگرایی رو بذار کنار: قبول کن که هیچ کدی توی دنیا کامل نیست. مهم اینه که کار کنه و مشکل رو حل کنه. یادگیری رو بپذیر: به جای اینکه از تغییرات سریع تکنولوژی بترسی، بهش به چشم یه فرصت نگاه کن. هر چی بیشتر یاد بگیری، اعتماد به نفست بیشتر میشه. از مقایسه دست بکش: به جای اینکه خودت رو با بقیه مقایسه کنی، پیشرفت خودت رو نسبت به گذشتهات بسنج. مثلاً ببین پارسال کجا بودی و الان کجایی. وقتی سندروم ایمپاستر میتونه مفید باشهباور کن یا نه، این حس گاهی میتونه به نفعمون باشه. مثلاً همین که همیشه دنبال بهتر شدن هستی و نمیخوای توی کارات کم بذاری، خودش یه نقطه قوته. خیلی از برنامهنویسهای موفق میگن که این حس باعث شده بیشتر مطالعه کنن، کدهای بهتری بنویسن و توی کارشون پیشرفت کنن. فقط کافیه نذاری این حس کنترلت کنه و به جاش ازش به عنوان یه انگیزه استفاده کنی. حرف آخر: تو به اندازه کافی خوبی!سندروم ایمپاستر یه چیزیه که خیلی از برنامهنویسها و مهندسهای نرمافزار باهاش روبهرو میشن، ولی قرار نیست همیشه باهات بمونه. دفعه بعدی که حس کردی به اندازه کافی خوب نیستی، یه نفس عمیق بکش و به خودت یادآوری کن که همین که داری کد میزنی، پروژه تحویل میدی و هر روز یه چیز جدید یاد میگیری، یعنی داری راه رو درست میری. دنیای برنامهنویسی جای آدمای باهوش و باپشتکاره، و تو قطعاً یکی از اونایی.
-
Parsidate -کامل ترین کتابخانه کار با تاریخ و ساعت شمسی در اکوسیستم Rust
sina پاسخی ارسال کرد برای یک پروژه در پروژه های منتقویم شمسی بهعنوان یک استاندارد رسمی در ایران و برخی کشورهای خاور میانه، جایگاه ویژهای در کاربردهای روزمره و حرفهای دارد. با این حال، در اکوسیستم Rust، ابزارهای محدودی برای پشتیبانی از این تقویم وجود داشت. کتابخانههای موجود یا به زبانهای دیگری توسعه یافته بودند که با نیازهای بهینهسازی Rust همخوانی نداشتند، یا فاقد بهروزرسانی و پشتیبانی کافی بودند و تمام نیاز های برنامه نویسان را پوشش نمیدادند به همین دلیل تصمیم گرفتم برای رفع نیاز خودم و سایر برنامه نویسان این پکیج رو طراحی و منتشر کنم که تا این لحظه کامل ترین و جامع ترین پکیج کار با تاریخ و ساعت شمسی برای اکوسیستم Rust میباشد. قابلیتها پایهتبدیل دوطرفه تاریخ: توابع from_gregorian و to_gregorian تاریخ ساعت را از میلادی به شمسی و از شمسی به میلادی تبدیل میکنند. تاریخ روز: تابع today تاریخ و ساعت فعلی(امروز) را برمیگرداند تشخیص سال کبیسه: تابع is_persian_leap_year سالهای کبیسه شمسی را شناسایی میکند. تشخیص روز هفته: تابع weekday که نام روز هفته را بازمیگرداند و همچنین توابع دیگر که شماره روز هفته و روزهای عادی را به شما میدهند. فرمت و قالب بندی: تابع format تاریخ و ساعت شمسی را به تاریخ های کوتاه و بلند و ایزو نمایش میدهد و همچنین تابع format_strftime تاریخ را به فرمت سفارشی و دلخواه شما به نمایش در میاورد. تجزیه (parse): تابع parse تاریخ و ساعت ورودی را به فرمت مد نظر شما تجزیه میکند. محاسبه:توابع add_days , add_months , add_years امکان جمع و تفریق روز ها ماه ها و سالها را به شما میدهد. تفاوت تاریخ و ساعت: تابع days_betweenتعداد روزهای بین دو تاریخ شمسی را محاسبه میکند. اعتبار سنجی:تابع is_valid بررسی میکند که آیا تاریخ و ساعت فعلی یک تاریخ معتبر را مطابق قوانین تقویم فارسی و محدوده پشتیبانی شده این کتابخانه نشان می دهد یا خیر. مدیریت خطا: استفاده از DateError و ParseErrorKind برای مدیریت انواع خطاها. فصل ها: در نسخه 1.5.0 قابلیت پشتیبانی از فصل ها اضافه شده است. هفته در سال: در نسخه 1.6.0 قابلیت تعیین اینکه یک تاریخ در کدام هفته ی سال میباشد اضافه شده است. پشتیبان از Serde: افزودن پکیج serde به پروژه قابلیت های serialization/deserialization آن در پارسی دیت را به شما میدهد. توابع کمکی(Helpers): به کمک توابع with_year, with_month, with_day میتوانید اولین و آخرین روز ماه و سال را بدست آورید یا تاریخ های سفارشی مورد نظرتان را ایجاد کنید. همچنین پارسی دیت از محدوده تاریخ 1/1/1 شمسی تا 12/12/999 شمسی پشتیبانی میکند. نحوه عملکردParsidate از کتابخانه استاندارد chrono برای مدیریت تاریخهای میلادی استفاده میکند و با پیادهسازی الگوریتمهای دقیق، عملیات کار با تاریخ را بصورت کاملا صحیح انجام میدهد. نمونهای از استفاده از این کتابخانه به شرح زیر است: use chrono::{NaiveDate, NaiveDateTime, Duration}; use parsidate::{ParsiDate, ParsiDateTime, DateError}; // Import both // --- ParsiDate Usage (Date only) --- // Create a ParsiDate (validates on creation) let pd = ParsiDate::new(1403, 5, 2).unwrap(); // 2 Mordad 1403 assert_eq!(pd.year(), 1403); assert_eq!(pd.month(), 5); // 5 = Mordad assert_eq!(pd.day(), 2); // Check validity assert!(pd.is_valid()); let invalid_date_res = ParsiDate::new(1404, 12, 30); // 1404 is not leap assert_eq!(invalid_date_res, Err(DateError::InvalidDate)); // Gregorian to Persian Date let g_date = NaiveDate::from_ymd_opt(2024, 7, 23).unwrap(); let pd_from_g = ParsiDate::from_gregorian(g_date).unwrap(); assert_eq!(pd_from_g, pd); // Persian Date to Gregorian let g_date_conv = pd.to_gregorian().unwrap(); assert_eq!(g_date_conv, g_date); // Formatting Date assert_eq!(pd.format("%Y-%m-%d is a %A"), "1403-05-02 is a سهشنبه"); assert_eq!(pd.format("%d %B %Y"), "02 مرداد 1403"); assert_eq!(pd.to_string(), "1403/05/02"); // Default Display // Parsing Date let parsed_short = ParsiDate::parse("1403/05/02", "%Y/%m/%d").unwrap(); assert_eq!(parsed_short, pd); // Date Arithmetic let next_day_date = pd.add_days(1).unwrap(); assert_eq!(next_day_date, ParsiDate::new(1403, 5, 3).unwrap()); // Get Today's Date match ParsiDate::today() { Ok(today) => println!("Today's Persian date: {}", today.format("long")), Err(e) => eprintln!("Error getting today's date: {}", e), } // --- ParsiDateTime Usage (Date and Time) --- // Create a ParsiDateTime (validates date and time) let pdt = ParsiDateTime::new(1403, 5, 2, 15, 30, 45).unwrap(); assert_eq!(pdt.year(), 1403); assert_eq!(pdt.hour(), 15); assert_eq!(pdt.minute(), 30); assert_eq!(pdt.second(), 45); assert_eq!(pdt.date(), pd); // Access the ParsiDate part // Invalid time creation let invalid_time_res = ParsiDateTime::new(1403, 5, 2, 24, 0, 0); assert_eq!(invalid_time_res, Err(DateError::InvalidTime)); // Gregorian DateTime to Persian DateTime let g_dt = NaiveDate::from_ymd_opt(2024, 7, 23).unwrap().and_hms_opt(15, 30, 45).unwrap(); let pdt_from_g = ParsiDateTime::from_gregorian(g_dt).unwrap(); assert_eq!(pdt_from_g, pdt); // Persian DateTime to Gregorian DateTime let g_dt_conv = pdt.to_gregorian().unwrap(); assert_eq!(g_dt_conv, g_dt); // Formatting DateTime assert_eq!(pdt.format("%Y/%m/%d %H:%M:%S"), "1403/05/02 15:30:45"); assert_eq!(pdt.format("%A %d %B ساعت %T"), "سهشنبه 02 مرداد ساعت 15:30:45"); assert_eq!(pdt.to_string(), "1403/05/02 15:30:45"); // Default Display // Parsing DateTime let parsed_dt = ParsiDateTime::parse("1403/05/02 15:30:45", "%Y/%m/%d %H:%M:%S").unwrap(); assert_eq!(parsed_dt, pdt); let parsed_dt_t = ParsiDateTime::parse("1403-05-02T15:30:45", "%Y-%m-%dT%T").unwrap(); assert_eq!(parsed_dt_t, pdt); // DateTime Arithmetic with Duration let next_hour = pdt.add_duration(Duration::hours(1)).unwrap(); assert_eq!(next_hour, ParsiDateTime::new(1403, 5, 2, 16, 30, 45).unwrap()); let prev_minute_rollover = pdt.sub_duration(Duration::minutes(31)).unwrap(); assert_eq!(prev_minute_rollover, ParsiDateTime::new(1403, 5, 2, 14, 59, 45).unwrap()); // Using operators assert_eq!(pdt + Duration::seconds(15), Ok(ParsiDateTime::new(1403, 5, 2, 15, 31, 0).unwrap())); // DateTime Arithmetic with days/months/years (preserves time) let next_day_dt = pdt.add_days(1).unwrap(); assert_eq!(next_day_dt, ParsiDateTime::new(1403, 5, 3, 15, 30, 45).unwrap()); let next_month_dt = pdt.add_months(1).unwrap(); assert_eq!(next_month_dt, ParsiDateTime::new(1403, 6, 2, 15, 30, 45).unwrap()); // Modifying time components let pdt_morning = pdt.with_hour(9).unwrap(); assert_eq!(pdt_morning.hour(), 9); let pdt_start_of_minute = pdt.with_second(0).unwrap(); assert_eq!(pdt_start_of_minute.second(), 0); // Get Current DateTime match ParsiDateTime::now() { Ok(now) => println!("Current Persian DateTime: {}", now), Err(e) => eprintln!("Error getting current DateTime: {}", e), } در صورتی که به serialization/deserialization نیاز دارید پکیج serde را به پروژه خود اضافه کنید: // --- Serde (Requires 'serde' feature) --- #[cfg(feature = "serde")] { // Make sure serde_json is a dev-dependency or added normally // use serde_json; // --- ParsiDate --- let pd_serde = ParsiDate::new(1403, 5, 2).unwrap(); let json_pd = serde_json::to_string(&pd_serde).unwrap(); println!("Serialized ParsiDate: {}", json_pd); // Output: {"year":1403,"month":5,"day":2} let deserialized_pd: ParsiDate = serde_json::from_str(&json_pd).unwrap(); assert_eq!(deserialized_pd, pd_serde); assert!(deserialized_pd.is_valid()); // Note: Default deserialization doesn't validate logical correctness for ParsiDate. let json_invalid_pd = r#"{"year":1404,"month":12,"day":30}"#; // Logically invalid let deserialized_invalid_pd: ParsiDate = serde_json::from_str(json_invalid_pd).unwrap(); assert!(!deserialized_invalid_pd.is_valid()); // --- ParsiDateTime --- let pdt_serde = ParsiDateTime::new(1403, 5, 2, 10, 20, 30).unwrap(); let json_pdt = serde_json::to_string(&pdt_serde).unwrap(); // Note the nested structure println!("Serialized ParsiDateTime: {}", json_pdt); // Output: {"date":{"year":1403,"month":5,"day":2},"hour":10,"minute":20,"second":30} let deserialized_pdt: ParsiDateTime = serde_json::from_str(&json_pdt).unwrap(); assert_eq!(deserialized_pdt, pdt_serde); assert!(deserialized_pdt.is_valid()); // Deserialization doesn't validate logical correctness for ParsiDateTime either. let json_invalid_pdt = r#"{"date":{"year":1403,"month":5,"day":2},"hour":25,"minute":0,"second":0}"#; // Invalid hour let deserialized_invalid_pdt: ParsiDateTime = serde_json::from_str(json_invalid_pdt).unwrap(); assert!(!deserialized_invalid_pdt.is_valid()); // is_valid() check is needed assert_eq!(deserialized_invalid_pdt.hour(), 25); // Field gets populated }مشخصات فنیوابستگیها: تنها وابستگی اجباری این کتابخانه، chrono است که برای مدیریت تاریخهای میلادی به کار میرود و بصورت اختیاری از کتابخانه serde نیز پشتیبانی میکند. الگوریتم تبدیل: مبتنی بر فرمولهای استاندارد تبدیل تقویم میلادی به شمسی، با پشتیبانی از محاسبات سالهای کبیسه. رابط برنامهنویسی (API): توابع این پکیج با استفاده از نوع بازگشتی Result، مدیریت خطاها را بهصورت ایمن تضمین میکنند. نحوه نصب و استفادهبرای نصب این کتابخانه میتوانید از دستور زیر استفاده کنید(روش پیشنهادی): cargo add parsidateیا برای افزودن Parsidate به پروژه خود، کافی است خط زیر را به فایل Cargo.toml اضافه کنید: [dependencies] parsidate = "1.6.0" chrono = "0.4"اگر به serde نیاز دارید: [dependencies] parsidate = { version = "1.6.0", features = ["serde"] } chrono = "0.4" serde = { version = "1.0", features = ["derive"] } # Required for derive دسترسیصفحه ی parsidate در crates.io : https://crates.io/crates/parsidate صفحه ی parsidate در lib.rs: https://lib.rs/crates/parsidate صفحه ی مستندات parsidate در docs.rs : https://docs.rs/parsidate WiKi: https://github.com/jalalvandi/ParsiDate/wiki آخرین تغییرات: https://github.com/jalalvandi/ParsiDate/blob/master/CHANGELOG.md
-
SubAI Web: ابزاری برای ترجمه زیرنویسها با PHP و Gemini API
sina پاسخی ارسال کرد برای یک پروژه در پروژه های منSubAI، یک وباپلیکیشن برای ترجمه خودکار فایلهای زیرنویس (SRT) با استفاده از API Gemini است. هدف این پروژه، ارائه ابزاری ساده و کاربردی برای ترجمه زیرنویسها به زبانهای مختلف و مدیریت فایلهای ترجمهشده است ویژگیهای اصلیترجمه خودکار زیرنویسها با انتخاب زبان مقصد. امکان مشاهده، جستجو و دانلود فایلهای ترجمهشده. تنظیمات پیشرفته شامل کلید API، مدل ترجمه و کش. امنیت تضمینشده با CSRF و فیلتر ورودیها. تکنولوژیهای مورد استفادهPHP: برای بکاند و منطق اصلی. MySQL: برای ذخیرهسازی دادهها. Gemini API: برای پردازش ترجمه. Bootstrap: برای رابط کاربری. JavaScript: برای تعاملات کاربر. نحوه کاردریافت Gemini API و وارد کردن آن در تنظیمات. فایل SRT را آپلود و زبان مقصد را انتخاب کنید. ترجمه با نمایش پیشرفت انجام میشود. فایل ترجمهشده را دانلود یا در بخش "Translated Files" مدیریت کنید. کاربردمناسب علاقهمندان به فیلم و سریال برای ترجمه زیرنویس. ابزار کمکی برای مترجمان حرفهای. برنامههای آیندهپشتیبانی از فرمتهای دیگر (مثل VTT). افزودن سایر مدل ها برای استفاده در ترجمه دسترسی و اجرابرنامه از طریق (این لینک) قابل دسترسی میباشد. کلید API Gemini نیز مورد نیاز است.
-
اتصال Rust به C: یه راهنمای کاربردی برای برنامهنویسها
sina پاسخی ارسال کرد برای یک مطلب در برنامه نویسیاگه برنامهنویس باشی و با Rust کار کرده باشی، حتما شنیدی که این زبان چقدر رو سرعت و امنیت تمرکز داره. از اون طرف، C هم که یه زبان قدیمی و قدرتمنده و هنوز توی خیلی از پروژهها، مخصوصا پروژههای سیستمی، حرف اول رو میزنه. حالا فکر کن بخوای این دو تا رو با هم ترکیب کنی! هم قدرت و انعطاف C رو داشته باشی، هم ایمنی و مدرن بودن Rust رو. توی این مطلب میخوام بهت نشون بدم چطور میتونی Rust و C رو به هم وصل کنی و کدات رو بین این دو تا جابجا کنی. چرا اصلا Rust و C رو به هم وصل کنیم؟قبل از اینکه بریم سراغ کد و جزییات، بذار یه لحظه ببینیم چرا این کار منطقیه. Rust یه زبان نسبتا جدیده که خیلی از مشکلات C مثل مدیریت دستی حافظه و خطاهای اشارهگر (pointer errors) رو حل کرده. اما خب، C یه اکوسیستم عظیم داره؛ کتابخونههایی مثل POSIX یا حتی کدهای قدیمی پروژهات که نمیخوای از صفر با Rust بازنویسیشون کنی. اینجا اتصال این دو زبان به کار میاد. میتونی از کتابخونههای C توی Rust استفاده کنی یا برعکس، کدهای Rust رو توی پروژههای C وارد کنی. مفاهیم اولیه: FFI چیه؟برای اینکه Rust و C با هم حرف بزنن، از یه چیزی به اسم FFI استفاده میکنیم. FFI مخفف "Foreign Function Interface" هست و به زبون ساده، یه جور پل ارتباطی بین زبونهای مختلفه. توی این مورد، FFI به Rust اجازه میده توابع C رو فراخوانی کنه و برعکس. اما این کار یه سری اصول داره که باید رعایت کنی، چون Rust و C توی مدیریت حافظه و ساختار دادهها خیلی فرق دارن. قدم اول: یه تابع C بنویسبیا با یه مثال ساده شروع کنیم. فرض کن توی C یه تابع داری که دو تا عدد رو میگیره و جمعشون میکنه: // math.c int add(int a, int b) { return a + b; }برای اینکه Rust بتونه این تابع رو ببینه، باید یه فایل هدر (header) هم بسازی: // math.h #ifndef MATH_H #define MATH_H int add(int a, int b); #endifحالا این فایل C رو کامپایل میکنیم تا یه کتابخونه استاتیک بسازیم: gcc -c math.c -o math.o ar rcs libmath.a math.oتا اینجا یه کتابخونه به اسم libmath.a داریم که قراره توی Rust ازش استفاده کنیم. قدم دوم: اتصال به Rustتوی پروژه Rust، باید به کامپایلر بگی که این کتابخونه خارجی رو لینک کنه. برای این کار از build.rs استفاده میکنیم. اگه پروژهات هنوز فایل build.rs نداره، توی ریشه پروژه یه فایل با این اسم بساز و این کد رو توش بذار: // build.rs fn main() { println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rustc-link-lib=math"); println!("cargo:rustc-link-search=native=."); }این کد به Rust میگه که کتابخونه libmath.a رو پیدا کنه و به پروژه لینک کنه. یادت باشه فایل libmath.a رو توی همون دایرکتوری پروژهات کپی کنی. قدم سوم: تعریف تابع در Rustحالا باید به Rust بگیم که تابع add توی C چه شکلیه. این کار رو با استفاده از بلاک extern "C" انجام میدیم. توی فایل main.rs اینجوری مینویسی: // main.rs extern "C" { fn add(a: i32, b: i32) -> i32; } fn main() { unsafe { let result = add(5, 3); println!("Result: {}", result); } }ری میکنه که مسئولیت هر مشکلی گردن خودته! اجرا و تستحالا کافیه پروژه رو با cargo run اجرا کنی. اگه همه چیز درست پیش بره، خروجی باید این باشه: Result: 8تبریک! تونستی یه تابع C رو توی Rust فراخوانی کنی. برعکسش چطوره؟ Rust به Cحالا فرض کن بخوای یه تابع توی Rust بنویسی و توی C ازش استفاده کنی. بیایم یه مثال بزنیم. توی Rust یه تابع میسازیم که یه رشته رو میگیره و طولش رو برمیگردونه: // lib.rs #[no_mangle] pub extern "C" fn string_length(s: *const i8) -> i32 { unsafe { let c_str = std::ffi::CStr::from_ptr(s); c_str.to_bytes().len() as i32 } }#[no_mangle]به Rust میگه اسم تابع رو تغییر نده تا C بتونه پیداش کنه. حالا این پروژه رو به صورت کتابخونه کامپایل میکنیم: cargo build --releaseفایل librust.a توی پوشه target/release ساخته میشه. توی C اینجوری ازش استفاده میکنیم: // main.c #include <stdio.h> #include <stdint.h> extern int32_t string_length(const char* s); int main() { const char* text = "Hello from Rust!"; int32_t len = string_length(text); printf("Length: %d\n", len); return 0; }کامپایلش میکنیم: gcc -L ./target/release -lrust main.c -o testاگه اجرا کنی، خروجی میشه: Length: 16نکات مهم و چالشهااتصال Rust و C خیلی هم ساده نیست. باید حواست به مدیریت حافظه باشه؛ مثلا اگه توی C حافظه تخصیص دادی، Rust انتظار نداره خودش آزادش کنه. یا برعکس، اگه Rust یه چیزی رو آزاد کرد، C نباید دوباره بخواد باهاش کار کنه. یه ابزار خوب برای این کار، استفاده از کتابخونههایی مثل cbindgen و bindgen هست که فایلهای هدر و تعاریف رو خودکار تولید میکنن و کار رو راحتتر میکنن. جمعبندیوصل کردن Rust به C یه راه عالیه برای اینکه از بهترینهای هر دو دنیا استفاده کنی. با FFI و چند خط کد، میتونی پروژههات رو قویتر و انعطافپذیرتر کنی.
-
سال 1404 مبارک
sina پاسخی ارسال کرد برای یک مطلب در شخصی و روزانهسال نو همیشه یادآور امید، شروعهای تازه و فرصتهای جدید است. هر سال با خودش چالشها، لحظات شیرین و گاه تلخ را به همراه دارد؛ اما همین تغییرات و فراز و نشیبهاست که زندگی را پر از معنا و تجربه میکند. با آمدن سال نو، همه ما به دنبال فرصتی هستیم تا از اشتباهات گذشته درس بگیریم و با انرژی و انگیزهای نو، به سمت آینده گام برداریم. این روزها که طبیعت لباس سبز به تن میکند و شکوفهها دوباره جان میگیرند، دلهای ما هم از امید و آرزوهای تازه پر میشود. در این سال نو، برایتان روزهایی پر از شادی، سلامتی، عشق و آرامش آرزو دارم. امیدوارم که لحظههایتان سرشار از لبخندهای صادقانه و دلهای شاد باشد. سالی سرشار از موفقیت، رشد و پیشرفت در انتظارتان باشد و هر قدمی که برمیدارید به سوی اهداف بزرگتر و آرزوهای زیباتر باشد. بیایید در این سال جدید، مهربانتر، صبورتر و قدرشناستر باشیم؛ در کنار همدیگر بمانیم و برای ساختن آیندهای روشنتر تلاش کنیم. امیدوارم که این سال برای همهی ما سالی پر از آرامش، دوستیهای ناب و لحظات به یادماندنی باشد. سال نو مبارک! 🎉
-
SubAI Desktop ابزاری برای ترجمه زیرنویس ها با Python و Gemini API
sina پاسخی ارسال کرد برای یک پروژه در پروژه های منSubAI، یک اپلیکیشن نوشته شده با پایتون برای ترجمه خودکار فایلهای زیرنویس (SRT) با استفاده از API Gemini است. هدف این پروژه، ارائه ابزاری ساده و کاربردی برای ترجمه زیرنویسها به زبانهای مختلف است ویژگیهای کلیدیاین برنامه امکانات متعددی را در اختیار کاربران قرار میدهد که آن را به ابزاری متمایز تبدیل میکند: رابط کاربری زیبا و کاربرپسند: طراحی شده با PyQT5،شامل جدول نمایش زیرنویسها، دکمههای کنترلی و منوهای تنظیمات است. پشتیبانی از زبانهای متنوع: امکان انتخاب زبانهای هدف مانند انگلیسی، فرانسه، آلمانی، اسپانیایی، فارسی، چینی و ژاپنی. ترجمه مبتنی بر هوش مصنوعی: استفاده از API مدلهای زبانی پیشرفته (مانند Gemini) برای ترجمه دقیق و سریع. مدیریت کش ترجمه: قابلیت ذخیره ترجمهها در حافظه RAM یا فایل SQLite برای جلوگیری از ترجمههای تکراری. کنترل فرآیند ترجمه: امکان شروع، توقف و ادامه ترجمهها همراه با نوار پیشرفت. تنظیمات پیشرفته: گزینههایی برای تنظیم نرخ درخواستها (RPM)، مدل زبانی، اندازه دسته (Batch Size) و نوع کش. معماری و پیادهسازیاین پروژه با استفاده از پایتون و فریمورک Qt توسعه یافته است که به دلیل عملکرد بالا و قابلیتهای گسترده در ساخت رابطهای کاربری، انتخاب شده است. معماری پروژه به صورت زیر است: کلاس اصلی (SubtitleTranslatorApp): هسته برنامه که رابط کاربری را مدیریت میکند. شامل ویجتهایی مانند QTableWidget برای نمایش زیرنویسها، QPushButton برای اقدامات کاربر و QProgressBar برای نشان دادن پیشرفت ترجمه. از QSqlDatabase برای مدیریت دیتابیس تنظیمات و کش استفاده میکند. پنجرههای تنظیمات (SettingsDialog و AdvancedSettingsDialog): پنجره تنظیمات عمومی برای ورود کلید API و تنظیمات پروکسی. پنجره تنظیمات پیشرفته برای مدیریت پارامترهای ترجمه مانند RPM و مدل زبانی. همزمانی (TranslationWorker): یک کلاس مبتنی بر QThread که ترجمهها را به صورت ناهمزمان انجام میدهد. از QNetworkAccessManager برای ارسال درخواستهای HTTP به API ترجمه استفاده میکند. پشتیبانی از پردازش دستهای (Batch Processing) برای بهینهسازی درخواستها. منابع و دیتابیس: فایلهای منبع مانند لوگوی برنامه (logo.png) با استفاده از سیستم منابع Qt (main.qrc) مدیریت میشوند. دیتابیس SQLite برای ذخیره تنظیمات و کش ترجمهها. برنامههای آیندهاین پروژه هنوز جای توسعه دارد. برخی از ایدهها برای آینده شامل موارد زیر است: پشتیبانی کامل از فرمتهای زیرنویس: اضافه کردن Parser پیشرفتهتر برای SRT و فرمتهای دیگر مانند ASS. ترجمه آفلاین: ادغام مدلهای محلی هوش مصنوعی برای استفاده بدون اینترنت. مدل های متنوع:افزوده شدن مدل های متنوع هوش مصنوعی در کنار Gemini بهینهسازی بیشتر: کاهش حجم و بهبود زمان پاسخدهی API. نحوه استفادهبرای استفاده از این نرمافزار: با توجه به توضیحات فایل README پایتون و نیازمندی های برنامه را نصب نمایید و اجرا کنید. از منوی "Public Settings" کلید API و پروکسی (در صورت نیاز) را تنظیم کنید. از منوی "Advanced Settings" پارامترهای ترجمه را مشخص کنید. فایل SRT را با دکمه "Choose File" انتخاب کنید. زبان مقصد را انتخاب کرده و "Start Translate" را بزنید. پس از اتمام ترجمه، فایل نهایی را با "Save" ذخیره کنید. این نرمافزار نه تنها ابزاری کاربردی برای کاربران عادی است، بلکه برای توسعهدهندگان نیز یک مطالعه موردی جذاب از مهندسی نرمافزار بهینه ارائه میدهد. اگر علاقهمند به مشارکت در توسعه یا استفاده از این پروژه هستید، خوشحال میشوم بازخورد شما را دریافت کنم.
-
الگوریتم و توابع اعتبار سنجی کد ملی به زبانهای برنامه نویسی مختلف
sina پاسخی ارسال کرد برای یک پروژه در پروژه های منگاهی اوقات در برنامهها و وبسایتها نیاز داریم که کد ملی کاربران را دریافت کنیم. شاید تا به حال تنها کاری که برای اعتبارسنجی انجام میدادیم، بررسی این بود که کد ملی دقیقاً ده رقم داشته باشد و فقط شامل اعداد باشد. در این حالت، کاربر میتوانست با وارد کردن یک عدد دهرقمی غیرمعتبر به راحتی از این مرحله عبور کند. اخیراً به دلیل نیاز شخصیام، کدی برای بررسی صحت کد ملی نوشتم. از آنجا که مطمئنم افراد زیادی هم به این موضوع نیاز دارند، در این مقاله توابع و مراحل انجام کار را توضیح میدهم. کد ملی یک شماره دهرقمی است که سه رقم اول از سمت چپ نشاندهنده کد شهرستان محل صدور شناسنامه، شش رقم بعدی یک کد منحصربهفرد برای فرد در آن شهرستان و رقم آخر یک رقم کنترلی است که بر اساس نه رقم قبلی محاسبه میشود. برای تأیید صحت کد، کافی است رقم کنترلی را دوباره از روی نه رقم اول محاسبه کنیم. با توجه به اینکه در سیستم کد ملی معمولاً صفرهایی قبل از کد وجود دارد (رقم اول یا دوم از سمت چپ ممکن است صفر باشد)، گاهی کاربران این صفرها را وارد نمیکنند یا نرمافزار آنها را ذخیره نمیکند. بنابراین، بهتر است ابتدا بررسی کنیم که اگر طول کد بین ۸ تا ۹ رقم است، به تعداد لازم (یک یا دو صفر) به سمت چپ عدد اضافه کنیم. الگوریتم محاسبهبرای محاسبه رقم کنترلی، هر رقم را در موقعیتش (از رقم یک تا رقم نه از چپ به راست) ضرب کرده و همه نتایج را با هم جمع میکنیم. مجموع بهدستآمده را بر ۱۱ تقسیم میکنیم. اگر باقیمانده کمتر از ۲ باشد، رقم کنترلی باید برابر باقیمانده باشد؛ در غیر این صورت، رقم کنترلی برابر است با ۱۱ منهای باقیمانده. مثال: آیا کد 7731689951 معتبر است؟محاسبه: 7×10 + 7×9 + 3×8 + 1×7 + 6×6 + 8×5 + 9×4 + 9×3 + 5×2 = 313 313 ÷ 11 = 28 (باقیمانده = 5)چون باقیمانده (۵) بزرگتر یا برابر ۲ است، رقم کنترلی باید ۱۱ - ۵ = ۶ باشد. اما در کد واردشده، رقم آخر ۱ است. پس این کد ملی معتبر نیست. نمونه کد جاوااسکریپت: /** * Validates an Iranian National Code based on the official algorithm. * * @param {string} nationalCode - The national code to validate * @returns {boolean} - True if valid, false otherwise */ function validateIranianNationalCode(nationalCode = '') { // Remove non-numeric characters const cleanedCode = (nationalCode || '').replace(/[^0-9]/g, ''); // Check length (must be 8 to 10 digits) const codeLength = cleanedCode.length; if (codeLength < 8 || codeLength > 10) { return false; } // Standardize to 10 digits by padding with leading zeros const standardizedCode = cleanedCode.padStart(10, '0'); // Check if all digits are the same if (standardizedCode.split('').every(digit => digit === standardizedCode[0])) { return false; } // Extract check digit (last digit) const checkDigit = parseInt(standardizedCode.slice(-1)); const digits = standardizedCode.slice(0, -1).split('').map(Number); // Calculate weighted sum (weights: 10 down to 2) const sum = digits.reduce((acc, digit, i) => acc + digit * (10 - i), 0); // Compute remainder and check digit const remainder = sum % 11; const calculatedCheckDigit = remainder < 2 ? remainder : 11 - remainder; // Return true if check digits match return calculatedCheckDigit === checkDigit; } // Test console.log(validateIranianNationalCode('6587452158')); // falseبرای راحتی کار، این الگوریتم را به زبانهای برنامهنویسی مختلف پیادهسازی کردهام و در گیتهاب قرار دادهام. کد در Repository در دسترس است و توضیحات تکمیلی در فایل README نوشته شده است. مثال زندهبرای استفاده از این پروژه میتوانید از صفحه زیر استفاده کنید. jalalvandi.github.io/IR-NationalCode توجه فرمایید اعدادی که وارد میکنید در هیچ جایی ذخیره نخواهند شد.
-
برنامهنویسی متا در C با ماکروها و پیشپردازنده
sina پاسخی ارسال کرد برای یک مطلب در برنامه نویسیزبان C به دلیل سادگی، کارایی بالا و دسترسی مستقیم به سختافزار، یکی از پرکاربردترین زبانهای برنامهنویسی است. اما یکی از قابلیتهای قدرتمند و در عین حال کمتر شناختهشده آن، برنامهنویسی متا با استفاده از ماکروها و پیشپردازنده است. این قابلیت به شما امکان میدهد کدهایی انعطافپذیر، بهینه و خودکار تولید کنید که در زمان کامپایل پردازش شده و از نظر کارایی بسیار سریع هستند. پیشپردازنده C چیست؟پیشپردازنده C یک مرحله قبل از کامپایل است که در آن دستورات خاصی پردازش شده و کد نهایی قبل از کامپایل تولید میشود. این مرحله شامل: تعریف ماکروها (#define) شرطیسازی کامپایل (#ifdef، #ifndef) گنجاندن فایلها (#include) دستورات پیشپردازندهی دیگر مانند #pragma و #error ماکروها، که معمولاً با #define تعریف میشوند، پایه اصلی برنامهنویسی متا در C هستند. برنامهنویسی متا با ماکروهابرنامهنویسی متا در C عمدتاً به کمک ماکروها و توابع درونخطی انجام میشود. در ادامه چند نمونه از استفادههای پیشرفته ماکروها را بررسی میکنیم. 1. ماکروهای توابع (Function-like Macros)ماکروها میتوانند مانند توابع رفتار کنند اما بدون سربار فراخوانی تابع، زیرا مستقیماً در کد جایگذاری میشوند. #include <stdio.h> #define SQUARE(x) ((x) * (x)) int main() { int num = 5; printf("Square of %d is %d\n", num, SQUARE(num)); return 0; } در اینجا SQUARE(5) به ((5) * (5)) تبدیل شده و مقدار 25 چاپ میشود. این نوع ماکروها برای بهینهسازی محاسبات ساده بسیار مفید هستند. 2. استفاده از # و ## در ماکروهادو عملگر خاص در ماکروهای C وجود دارند: # برای تبدیل آرگومان به رشته ## برای چسباندن دو آرگومان به هم #include <stdio.h> #define TO_STRING(x) #x #define CONCAT(a, b) a##b int main() { printf("String: %s\n", TO_STRING(Hello, World!)); int xy = 100; printf("Concatenated Variable: %d\n", CONCAT(x, y)); return 0; } در این مثال: TO_STRING(Hello, World!) مقدار "Hello, World!" را تولید میکند. CONCAT(x, y) باعث میشود xy به عنوان متغیر خوانده شود و مقدار 100 را نمایش دهد. 3. ماکروهای متغیر (Variadic Macros)ماکروهای متغیر میتوانند تعداد نامحدودی آرگومان دریافت کنند. #include <stdio.h> #define DEBUG_PRINT(fmt, ...) printf("DEBUG: " fmt "\n", __VA_ARGS__) int main() { DEBUG_PRINT("Value of x: %d", 10); DEBUG_PRINT("Sum: %d, Product: %d", 5 + 3, 5 * 3); return 0; } در اینجا __VA_ARGS__ آرگومانهای متغیر را پردازش کرده و امکان لاگگیری انعطافپذیر را فراهم میکند. مزایا و معایب برنامهنویسی متا در Cمزایا بهینهسازی سرعت – ماکروها مستقیماً در کد جایگزین میشوند و باعث حذف سربار اجرای تابع میشوند. کاهش تکرار کد – بسیاری از کدهای تکراری را میتوان به ماکرو تبدیل کرد. امکان برنامهنویسی سطح پایین – امکان تعریف ساختارهای پیشرفته مانند مدیریت سختافزار و رجیسترهای پردازنده را فراهم میکند. معایبدیباگ دشوار – خطاهای ناشی از ماکروها معمولاً در خروجی کامپایل ظاهر نمیشوند و تشخیص مشکل سخت است. عدم بررسی نوع داده – ماکروها مانند توابع تایپسیف نیستند و ممکن است مشکلاتی در نوع داده ایجاد کنند. افزایش پیچیدگی کد – خوانایی کد در صورت استفاده نادرست از ماکروها کاهش مییابد. نمونههای کاربردی برنامهنویسی متا در C1. مدیریت حافظه هوشمندیکی از کاربردهای متا، تعریف ماکروهایی برای تخصیص و آزادسازی خودکار حافظه است. #include <stdio.h> #include <stdlib.h> #define SAFE_ALLOC(ptr, size) do { ptr = malloc(size); if (!ptr) { fprintf(stderr, "Memory allocation failed\n"); exit(1); } } while(0) int main() { int *arr; SAFE_ALLOC(arr, 10 * sizeof(int)); // استفاده از آرایه... free(arr); return 0; } این ماکرو اطمینان حاصل میکند که تخصیص حافظه موفق بوده و در غیر این صورت برنامه را متوقف میکند. 2. تبدیل توابع به درونخطی (Inline Functions)اگرچه inline در C مدرن موجود است، اما در نسخههای قدیمیتر C، ماکروها برای درونخطی کردن توابع استفاده میشدند: #define MAX(a, b) ((a) > (b) ? (a) : (b)) int main() { printf("Max: %d\n", MAX(10, 20)); return 0; } در نهایتبرنامهنویسی متا در C با استفاده از ماکروها و پیشپردازنده یک ابزار قدرتمند برای بهینهسازی، کاهش تکرار کد و خودکارسازی عملیات پیچیده است. این تکنیک بهویژه در سیستمهای نهفته، توسعه کرنل و برنامههای کاراییمحور بسیار پرکاربرد است. با این حال، نیاز به درک عمیق از ساختار کد و مدیریت صحیح خطاها دارد تا از مشکلات رایج آن جلوگیری شود. استفاده مناسب از ماکروها میتواند توسعه نرمافزار را سریعتر، بهینهتر و خواناتر کند، اما باید از استفاده بیشازحد و غیرضروری آنها اجتناب کرد.
-
مغز شاتلهای فضایی: نگاهی عمیق به سیستمهای نرمافزاری و کامپیوتری ناسا - بخش اول
sina پاسخی ارسال کرد برای یک مطلب در علمی و عمومیشاتلهای فضایی ناسا، از کلمبیا تا آتلانتیس، فقط ماشینهای عظیم آهنی نبودن؛ اونا یه شاهکار تکنولوژیک بودن که با قدرت ذهن بشر و چند خط کد به فضا پرواز کردن. تصور کن یه شاتل با سرعت ۲۸٬۰۰۰ کیلومتر بر ساعت دور زمین میگرده، توی جو مانور میده و بعد با دقت روی باند فرود میاد. حالا فکر کن همه اینا با کامپیوتری انجام شده که حافظهاش از یه فلش USB امروزی هم کمتر بود! اینجاست که سیستمهای نرمافزاری و کامپیوتری شاتلها وارد داستان میشن: یه دنیای پیچیده از کد، سختافزار و هوشمندی که هر خطاش میتونست یه مأموریت رو به فاجعه بکشونه. توی این مقاله قراره سفری داشته باشیم به قلب این سیستمها. از زبان برنامهنویسی خاص HAL/S که شاتلها رو هدایت میکرد، تا کامپیوترهای قدیمیای که مثل یه ارکستر هماهنگ کار میکردن. میخوام خطاهایی که نفسها رو حبس کردن و درسهایی که ازشون گرفته شد رو باهم ببینیم. این نه فقط یه داستان فنیه، بلکه روایتی از تلاش بشر برای فتح فضاست با ابزارهایی که الان شاید ساده به نظر بیان. پس اگه کنجکاوی که بدونی چطور این غولهای فضایی با چند خط کد زنده بودن، بیا با من همراه شو! کامپیوترهای GPC - ستون فقرات شاتلها هر شاتل فضایی به پنج تا کامپیوتر IBM AP-101 مجهز بود که بهشون General Purpose Computers یا GPC میگفتن. اینا هسته مرکزی عملیات بودن و همهچیز، از پرتاب و کنترل مداری تا فرود، رو مدیریت میکردن. حالا فکر کن هر کدوم از این کامپیوترها فقط ۱ مگابایت حافظه و قدرت پردازش ۰٫۴ میلیون دستور در ثانیه داشتن. برای مقایسه، یه لپتاپ معمولی امروزی هزاران برابر قویتره! چهار تا از این GPCها یه برنامه مشترک به اسم PASS (Primary Avionics Software System) رو اجرا میکردن و مدام با هم مقایسه میشدن. این طراحی "redundant" یعنی اگه یکی خراب میشد یا اشتباه میکرد، بقیه میتونستن تشخیص بدن و کار رو ادامه بدن. پنجمی اما یه نقش خاص داشت: فقط سیستم پشتیبان (Backup Flight System یا BFS) رو اجرا میکرد و برای مواقع اضطراری آماده بود. این کامپیوترها توی شرایط سخت فضا کار میکردن: تشعشع کیهانی، دمای شدید و لرزشهای وحشتناک. با این حال، ناسا طوری اونا رو ساخته بود که حتی با این محدودیتها، دقتشون از دست نره. چطور؟ جواب توی نرمافزارهاییه که این سختافزار ساده رو به یه ماشین فضایی تبدیل کرده بود. توی بخش بعدی، زبانی که این کدها رو شکل داد رو بررسی میکنیم. HAL/S - زبان برنامهنویسی شاتلهاناسا برای شاتلها نمیتونست سراغ زبانهای معمولی مثل فورترن یا اسمبلی بره. به جاش یه زبان اختصاصی به اسم HAL/S (High-order Assembly Language/Shuttle) طراحی کرد که دهه ۱۹۷۰ توسط شرکت Intermetrics ساخته شد. این زبان انگار برای شاتلها زاده شده بود: هم قدرتمند بود، هم بهینه برای سختافزار محدود GPCها. HAL/S برای سیستمهای "بلادرنگ" (real-time) طراحی شده بود، یعنی میتونست توی کسری از ثانیه به تغییرات واکنش نشون بده؛ چیزی که توی فضا حیاتیه. مثلاً وقتی شاتل توی جو باید زاویهاش رو تنظیم میکرد، این زبان به برنامهنویسها اجازه میداد زمانبندی دقیق دستورات رو کنترل کنن. یه نمونه کد ساده: DCL THRUST VECTOR(3) FIXED; UPDATE THRUST WITH NEW_VALUES; SCHEDULE THRUST_ADJUST AT 0.1 SECONDS; اینجا یه بردار رانش تعریف میشه، با دادههای جدید آپدیت میشه و بعد توی ۰٫۱ ثانیه تنظیم میشه. یه ویژگی دیگه HAL/S، مدیریت خطاهاش بود. اگه یه متغیر از محدوده خارج میشد، خودش هشدار میداد. این برای شاتلها حیاتی بود، چون یه خطای کوچیک میتونست فاجعه بیاره. حجم کدها هم کم نبود: PASS حدود ۴۰۰٬۰۰۰ خط کد داشت و BFS نزدیک ۱۰۰٬۰۰۰ خط. برنامهنویسها باید این کدها رو طوری مینوشتن که توی ۱ مگابایت حافظه جا بشه و کند نشه. برای همین، تستها و شبیهسازیها گاهی ماهها طول میکشید تا مطمئن بشن همهچیز بینقصه. اما آیا واقعاً بینقص بود؟ بیایم چندتا خطای واقعی رو ببینیم. خطاهای نرمافزاری - لحظات نفسگیرهرچند سیستم شاتلها خیلی دقیق بود، اما خطاها گاهی خودشون رو نشون میدادن. یکی از معروفترینش توی اولین پرتاب شاتل (STS-1، کلمبیا، ۱۹۸۱) بود. قرار بود ۱۰ آوریل پرتاب بشه، ولی ۲۰ دقیقه قبل از بلند شدن، سیستم قفل کرد. چهار تا GPC که PASS رو اجرا میکردن، باید همزمان باهم sync میشدن، ولی یه اختلاف یک میلیثانیهای توی زمانبندی همهچیز رو متوقف کرد. مشکل از یه "race condition" توی کد HAL/S بود. یه بخش از برنامه که زمانبندی رو چک میکرد، به این شکل بود: SYNC_CLOCKS: READ MASTER_CLOCK INTO LOCAL_TIME; IF LOCAL_TIME ≠ EXPECTED_TIME THEN HALT; یه تأخیر ریز توی انتقال داده باعث شد LOCAL_TIME مقدار قدیمی بخونه و سیستم فکر کنه یه جای کار خرابه. ناسا دو روز پرتاب رو عقب انداخت و با اضافه کردن یه بافر زمانی، مشکل رو حل کرد. این خطا نشون داد که حتی با تستهای گسترده، شرایط نادر میتونن غافلگیرکننده باشن. یه مورد دیگه توی STS-9 (۱۹۸۳) بود. وقتی شاتل کلمبیا برای فرود آماده میشد، دو تا GPC بهخاطر مشکل سختافزاری (شاید تشعشع کیهانی) از کار افتادن. BFS فعال شد، ولی یه باگ باعث شد یه جت کنترلی (RCS) بیش از حد روشن بمونه و شاتل یه چرخش ناخواسته بکنه. مشکل از یه متغیر بود که توی سوئیچ به BFS درست ریست نشده بود. خلبانها با مهارتشون فرود رو نجات دادن، ولی ناسا بعدش کد BFS رو آپدیت کرد. این خطاها درسهای بزرگی داشتن: تستها باید سختگیرانهتر بشن و حتی سیستمهای پشتیبان هم نیاز به دقت دارن. BFS - خط دفاعی شاتلهاBackup Flight System یا BFS مثل یه سپر اضطراری بود. اگه PASS به هر دلیلی از کار میافتاد، BFS با ۱۰۰٬۰۰۰ خط کدش کنترل رو میگرفت. این سیستم فقط برای کارهای ضروری طراحی شده بود: نویگیشن پایه، کنترل موتورها و فرود امن. BFS توسط تیمی جدا از PASS نوشته شده بود تا باگهای مشترک نداشته باشن. حتی از نسخه کمی متفاوت HAL/S استفاده میکرد. توی کابین خلبان، یه دکمه به اسم "BFS Engage" بود که با زدنش، GPC پنجم فعال میشد و شاتل رو نجات میداد. توی STS-9 دیدیم که BFS چطور وارد عمل شد، ولی همون باگ جت کنترلی نشون داد که سادگی BFS هم محدودیتهایی داره. ناسا بعدش مطمئن شد که هر وقت BFS فعال میشه، همه متغیرها از نو مقداردهی بشن. این سیستم یه فلسفه مهم رو یادآوری کرد: توی فضا، گاهی ساده نگه داشتن بهترین راهحله. تستها و شبیهسازیها - نبرد با ناشناختههاناسا نمیتونست ریسک کنه. برای همین قبل از هر مأموریت، هزاران ساعت شبیهسازی انجام میداد. توی مرکز Shuttle Avionics Integration Laboratory (SAIL) توی هیوستون، یه نسخه کامل از شاتل ساخته شده بود که همه سناریوها رو تست میکرد: از پرتاب توی طوفان خورشیدی تا فرود توی شرایط بحرانی. هر خط کد بارها اجرا میشد تا مطمئن بشن هیچ باگی نیست. ناسا از رویکرد "correct-by-construction" استفاده میکرد، یعنی کدها رو طوری مینوشتن که از اول درست باشن. اما همونطور که توی STS-1 دیدیم، بعضی خطاها فقط توی لحظه واقعی خودشون رو نشون میدادن. تستها انقدر دقیق بود که گاهی یه مأموریت ماهها عقب میافتاد. مثلاً قبل از STS-1، ناسا ده ها هزار بار پرتاب رو شبیهسازی کرد، ولی بازم اون باگ زمانبندی غافلگیرشون کرد. بعد از هر خطا، سناریوهای جدید به تستها اضافه میشد تا دیگه تکرار نشه. این دقت باعث شد توی ۱۳۵ مأموریت شاتل، هیچ فاجعهای مستقیماً بهخاطر نرمافزار رخ نده. مقایسه با امروز - شاتلها در برابر فضاپیما های امروزیحالا که شاتلها بازنشسته شدن، فضاپیماهای مدرن مثل Crew Dragon اسپیسایکس جای اونا رو گرفتن. تفاوتشون با شاتلها مثل مقایسه یه ماشینحساب با یه سوپرکامپیوتره. Crew Dragon از پردازندههای چند هستهای با گیگابایتها حافظه استفاده میکنه و نرمافزارش با زبانهای مدرن مثل C++ نوشته شده. شاتلها با ۱ مگابایت حافظه و HAL/S کار میکردن، ولی این محدودیت باعث شده بود ناسا روی دقت و بهینهسازی تمرکز کنه. توی سیستمهای مدرن، قدرت پردازش اجازه میده کدها پیچیدهتر باشن و حتی هوش مصنوعی برای تصمیمگیری استفاده بشه. اما یه تفاوت بزرگ اینه که شاتلها برای هر مأموریت دستی تنظیم میشدن، در حالی که فضاپیماهای جدید مثل Crew Dragon خودکارترن. با این حال، شاتلها یه میراث بزرگ گذاشتن: نشون دادن که با منابع کم هم میشه کارهای بزرگ کرد. این فلسفه هنوز توی طراحی سیستمهای فضایی دیده میشه.
-
زنجیره اختیاری (Optional Chaining) در جاوا اسکریپت: انقلابی در دسترسی به دادهها
sina پاسخی ارسال کرد برای یک مطلب در برنامه نویسیاگر توسعهدهنده وب باشید یا به دنیای برنامهنویسی جاوا اسکریپت علاقه داشته باشید، احتمالاً با چالشهایی مثل خطای undefined یا null هنگام دسترسی به خواص یک شیء مواجه شدهاید. این مشکل معمولاً زمانی رخ میدهد که بخواهید به یک ویژگی عمیق در یک شیء دسترسی پیدا کنید، اما مطمئن نباشید که تمام زنجیره مسیر دسترسی به آن ویژگی وجود دارد یا نه. خوشبختانه، جاوا اسکریپت با معرفی Optional Chaining یا زنجیره اختیاری در نسخه ES11 (سال 2020) این مشکل را به شکلی زیبا و کارآمد حل کرده است. در این مقاله قصد دارم شما را با این قابلیت جدید، نحوه کارکرد آن، کاربردهایش و تأثیرش بر کدنویسی روزمره آشنا کنم. زنجیره اختیاری چیست؟زنجیره اختیاری که با عملگر ?. نشان داده میشود، به شما اجازه میدهد به خواص یا متدهای یک شیء دسترسی پیدا کنید، بدون اینکه نگران باشید که آیا مقدار آن شیء یا بخشی از زنجیره مسیرش null یا undefined است یا نه. اگر در هر مرحله از زنجیره، مقدار null یا undefined پیدا شود، به جای پرتاب خطا، به سادگی مقدار undefined برگردانده میشود. این ویژگی نه تنها کدنویسی را تمیزتر میکند، بلکه از بسیاری از خطاهای رایج که توسعهدهندگان با آن دستوپنجه نرم میکنند، جلوگیری میکند. بیایید با یک مثال ساده شروع کنیم. فرض کنید یک شیء به شکل زیر دارید: const user = { name: "علی", address: { city: "تهران" } };حالا اگر بخواهید به user.address.city دسترسی پیدا کنید، مشکلی نیست چون مسیر کامل وجود دارد. اما اگر شیء address وجود نداشته باشد چه؟ مثلاً: const user = { name: "علی" }; console.log(user.address.city); // TypeError: Cannot read property 'city' of undefinedدر این حالت، چون address وجود ندارد، جاوا اسکریپت نمیتواند به city دسترسی پیدا کند و خطای TypeError پرتاب میشود. قبل از معرفی زنجیره اختیاری، توسعهدهندگان مجبور بودند با شرطهای متعدد این مشکل را مدیریت کنند: const city = user && user.address && user.address.city; console.log(city); // undefinedاین روش کار میکند، اما کد را شلوغ و خواندنش را سختتر میکند. حالا با زنجیره اختیاری، همین کار را میتوان به این شکل انجام داد: const city = user?.address?.city; console.log(city); // undefinedخیلی سادهتر و خواناتر، نه؟ نحو و نحوه کارکردعملگر ?. در سه موقعیت اصلی استفاده میشود: دسترسی به خواص شیء: مثل obj?.prop دسترسی به آرایهها: مثل arr?.[index] فراخوانی متدها: مثل obj?.method() هر وقت از این عملگر استفاده میکنید، جاوا اسکریپت ابتدا بررسی میکند که مقدار سمت چپ ?. آیا null یا undefined است یا نه. اگر باشد، فرآیند متوقف میشود و undefined برمیگرداند. اگر نه، به دسترسی یا فراخوانی ادامه میدهد. مثال دیگر برای آرایهها: const data = [1, 2, 3]; console.log(data?.[1]); // 2 const emptyData = null; console.log(emptyData?.[1]); // undefinedیا برای متدها: const user = { sayHello() { return "سلام!"; } }; console.log(user?.sayHello?.()); // "سلام!" console.log(null?.sayHello?.()); // undefinedچرا زنجیره اختیاری مهم است؟حالا که با نحوه کارش آشنا شدید، بیایید ببینیم چرا این قابلیت اینقدر مورد استقبال قرار گرفته است: کاهش خطاها: دیگر نیازی نیست نگران خطاهای غیرمنتظره باشید. اگر دادهای که از API یا منبع خارجی میآید ناقص باشد، زنجیره اختیاری به شما کمک میکند بدون دردسر از آن عبور کنید. کد تمیزتر: به جای نوشتن شرطهای طولانی و پیچیده، با یک عملگر ساده میتوانید همان نتیجه را بگیرید. این یعنی خوانایی بیشتر و نگهداری آسانتر. انعطافپذیری با دادههای پویا: در دنیای واقعی، دادهها همیشه طبق انتظار ما نیستند. مثلاً وقتی با یک API کار میکنید، ممکن است بعضی فیلدها گاهی وجود داشته باشند و گاهی نه. زنجیره اختیاری این عدم قطعیت را به خوبی مدیریت میکند. سازگاری با فریمورکها: در کتابخانههایی مثل React یا Vue که با دادههای پویا زیاد سر و کار دارید، این قابلیت به شدت زمان توسعه را کاهش میدهد. کاربردهای عملیبیایید چند سناریوی واقعی را بررسی کنیم که زنجیره اختیاری در آنها میدرخشد: 1. کار با APIهافرض کنید از یک API اطلاعاتی درباره کاربر دریافت میکنید: fetch("https://api.example.com/user") .then((response) => response.json()) .then((data) => { const street = data?.user?.address?.street || "خیابان نامشخص"; console.log(street); });اگر هر یک از این فیلدها وجود نداشته باشد، به جای خطا، مقدار پیشفرض نمایش داده میشود. 2. مدیریت تنظیمات اختیاریتصور کنید یک تابع دارید که تنظیماتی را به صورت اختیاری میپذیرد: function configure(options) { const timeout = options?.timeout ?? 5000; // اگر timeout نباشد، 5000 console.log(`تنظیم زمان: ${timeout} میلیثانیه`); } configure({ timeout: 3000 }); // "تنظیم زمان: 3000 میلیثانیه" configure({}); // "تنظیم زمان: 5000 میلیثانیه"اینجا از عملگر ?? (Nullish Coalescing) هم کنار ?. استفاده شده که فقط در صورت null یا undefined مقدار پیشفرض را برمیگرداند. 3. بررسی توابع اختیاریاگر بخواهید متدی را فراخوانی کنید که ممکن است وجود نداشته باشد: const plugin = { init: () => console.log("شروع شد!") }; plugin?.init?.(); // "شروع شد!" const noPlugin = null; noPlugin?.init?.(); // هیچ خطایی نمیدهد، فقط undefinedمحدودیتها و نکات مهمهرچند زنجیره اختیاری فوقالعاده است، اما محدودیتهایی هم دارد که باید به آنها توجه کنید: پشتیبانی مرورگر: این قابلیت در مرورگرهای قدیمی (مثل IE) پشتیبانی نمیشود. اگر پروژهتان نیاز به سازگاری با مرورگرهای قدیمی دارد، باید از ابزارهایی مثل Babel استفاده کنید. استفاده بیش از حد: اگر در هر خط کد از ?. استفاده کنید، ممکن است کدتان بیش از حد دفاعی شود و اشکالزدایی سختتر شود. تفاوت با عملگرهای دیگر: ?. با && یا || فرق دارد. مثلاً 0 && "something" مقدار 0 را برمیگرداند، اما 0?.something خطا نمیدهد و undefined میدهد. جمعبندیزنجیره اختیاری (?.) یکی از بهترین قابلیتهایی است که جاوا اسکریپت در سالهای اخیر به خودش دیده است. این ویژگی نه تنها زندگی توسعهدهندگان را آسانتر کرده، بلکه به ما اجازه میدهد کدهایی بنویسیم که هم ایمنترند و هم خواناتر. اگر هنوز از این قابلیت استفاده نکردهاید، پیشنهاد میکنم همین حالا آن را در پروژه بعدیتان امتحان کنید. مطمئنم بعد از چند بار استفاده، دیگر نمیتوانید بدون آن کدنویسی کنید! نظر شما چیست؟ آیا تجربهای با زنجیره اختیاری دارید؟ در بخش نظرات وبلاگم برام بنویسید و اگر سوالی دارید، خوشحال میشم کمکتون کنم!
-
مسئله کولاتز: معمایی ریاضی که ذهنها را به چالش میکشد
sina پاسخی ارسال کرد برای یک مطلب در علمی و عمومیمسئله کولاتز یکی از معروفترین و در عین حال حلنشدهترین مسائل در دنیای ریاضیات است. این مسئله که گاهی با نامهای «حدس کولاتز»، «دنباله ۳n+1» یا «مسئله سیراکیوز» نیز شناخته میشود، توسط لوتار کولاتز، ریاضیدان آلمانی، در سال ۱۹۳۷ مطرح شد. سادگی صورت مسئله در کنار پیچیدگی اثبات آن، این موضوع را به یکی از جذابترین معماهای ریاضی تبدیل کرده است. در این مقاله، به بررسی این مسئله، نحوه عملکرد آن، یک مثال برنامهنویسی و اهمیت آن در ریاضیات مدرن میپردازیم. مسئله کولاتز چیست؟صورت مسئله کولاتز بسیار ساده است: یک عدد طبیعی (مثبت) دلخواه انتخاب کنید و سپس از این قانون پیروی کنید: اگر عدد زوج باشد، آن را بر ۲ تقسیم کنید. اگر عدد فرد باشد، آن را در ۳ ضرب کرده و ۱ را به نتیجه اضافه کنید (۳n + 1). این روند را با عدد جدید تکرار کنید. حدس کولاتز میگوید که مهم نیست چه عددی را در ابتدا انتخاب کنید، با تکرار این مراحل، در نهایت به عدد ۱ خواهید رسید. پس از رسیدن به ۱، دنباله وارد یک چرخه میشود: ۱ → ۴ → ۲ → ۱، و این روند بیپایان تکرار میشود. به این ترتیب، سؤال اصلی این است: آیا این حدس برای همه اعداد طبیعی صادق است؟ مثال سادهبرای درک بهتر، بیایید با عدد ۱۳ شروع کنیم و مراحل را دنبال کنیم: ۱۳ فرد است: ۳ × ۱۳ + ۱ = ۴۰ ۴۰ زوج است: ۴۰ ÷ ۲ = ۲۰ ۲۰ زوج است: ۲۰ ÷ ۲ = ۱۰ ۱۰ زوج است: ۱۰ ÷ ۲ = ۵ ۵ فرد است: ۳ × ۵ + ۱ = ۱۶ ۱۶ زوج است: ۱۶ ÷ ۲ = ۸ ۸ زوج است: ۸ ÷ ۲ = ۴ ۴ زوج است: ۴ ÷ ۲ = ۲ ۲ زوج است: ۲ ÷ ۲ = ۱ حالا به ۱ رسیدیم و دنباله وارد چرخه ۱ → ۴ → ۲ → ۱ میشود. این مثال نشان میدهد که حدس کولاتز برای عدد ۱۳ برقرار است. اما آیا این برای همه اعداد صادق است؟ تاکنون هیچ کس نتوانسته این را به طور قطعی اثبات یا رد کند! برنامهنویسی مسئله کولاتزبرای درک بهتر رفتار این دنباله، میتوانیم از برنامهنویسی استفاده کنیم. در زیر یک کد ساده به زبان پایتون ارائه شده است که دنباله کولاتز را برای یک عدد دلخواه محاسبه میکند: def collatz(n): print(n, end=" -> ") while n != 1: if n % 2 == 0: # اگر زوج باشد n = n // 2 else: # اگر فرد باشد n = 3 * n + 1 print(n, end=" -> ") print("پایان") # تست با یک عدد collatz(13)خروجی این کد همان دنبالهای است که برای عدد ۱۳ در بالا محاسبه کردیم: 13 -> 40 -> 20 -> 10 -> 5 -> 16 -> 8 -> 4 -> 2 -> 1 -> پایاناین برنامه نشان میدهد که دنباله چگونه به ۱ میرسد. میتوانید عدد دیگری را امتحان کنید و رفتار دنباله را بررسی کنید. نکته جالب اینجاست که حتی برای اعداد بسیار بزرگ (مثلاً میلیونها یا میلیاردها)، آزمایشها نشان دادهاند که دنباله در نهایت به ۱ میرسد، اما این فقط یک مشاهده تجربی است و نه یک اثبات ریاضی. چرا کولاتز حلنشده باقی مانده است؟با وجود سادگی ظاهری، اثبات یا رد حدس کولاتز به یکی از دشوارترین چالشهای ریاضی تبدیل شده است. ریاضیدانان تا به امروز دنباله کولاتز را برای اعداد بسیار بزرگ آزمایش کردهاند (تا حدود ۲ به توان ۶۸) و همه آنها به ۱ رسیدهاند. با این حال، این آزمایشها نمیتوانند تضمین کنند که هیچ عددی وجود ندارد که به ۱ نرسد یا وارد یک چرخه دیگر شود. این مسئله به نظریه اعداد و رفتار غیرقابل پیشبینی دنبالهها مرتبط است و ابزارهای ریاضی فعلی هنوز برای حل کامل آن کافی به نظر نمیرسند. اهمیت و جذابیت کولاتزمسئله کولاتز نه تنها برای ریاضیدانان، بلکه برای برنامهنویسان و علاقهمندان به علوم کامپیوتر نیز جذاب است. سادگی پیادهسازی آن در کد و در عین حال عمق ریاضیاتیاش، آن را به یک موضوع ایدهآل برای آموزش و پژوهش تبدیل کرده است. علاوه بر این، کولاتز نمونهای از مسائلی است که نشان میدهد چگونه یک سؤال ساده میتواند به یک معمای عمیق و حلنشده منجر شود. نتیجهگیریحدس کولاتز همچنان یکی از اسرار بزرگ ریاضیات مدرن است. آیا روزی کسی خواهد توانست آن را اثبات یا رد کند؟ یا شاید این مسئله برای همیشه حلنشده باقی بماند؟ تا آن زمان، ما میتوانیم با آزمایش اعداد مختلف و نوشتن برنامههایی مثل مثال بالا، از زیبایی و رمز و راز این دنباله لذت ببریم. اگر به ریاضیات یا برنامهنویسی علاقه دارید، پیشنهاد میکنم خودتان چند عدد را امتحان کنید و ببینید که آیا میتوانید الگویی جدید کشف کنید!
-
Lifetime در Rust چیست و چرا باید به آن اهمیت بدهیم؟
sina پاسخی ارسال کرد برای یک مطلب در برنامه نویسیزبان برنامهنویسی Rust به خاطر ویژگیهای منحصربهفردش مثل ایمنی حافظه و عملکرد بالا، حسابی سر زبونها افتاده. یکی از مفاهیم کلیدی که Rust رو از بقیه زبانها متمایز میکنه، چیزیه به اسم Lifetime (طول عمر). اگه تازه با Rust آشنا شدید یا میخواهید عمیقتر این زبون رو بشناسید، این مقاله قراره شما رو با این مفهوم مهم آشنا کنه و نشون بده چرا توی نوشتن کدهای امن و کارآمد انقدر نقش داره. پس بیاید با هم وارد این موضوع بشیم و ببینیم Lifetime چیه، چرا مهمه و چطور توی عمل کار میکنه. Lifetime دقیقاً چیه؟توی Rust، وقتی از Lifetime صحبت میکنیم، داریم به یه قانون اساسی اشاره میکنیم که به مدیریت حافظه ربط داره. به زبان ساده، Lifetime مشخص میکنه که یه مرجع (Reference) توی کدتون تا چه زمانی معتبره و میتونید ازش استفاده کنید. مرجع همون چیزیه که با علامت & توی کد نشون داده میشه، مثل &x که به یه متغیر یا داده اشاره میکنه بدون اینکه مالکیتش رو بگیره. Rust با این سیستم مطمئن میشه که هیچوقت به دادهای که دیگه توی حافظه وجود نداره (مثلاً پاک شده) اشاره نکنید. این کار از خطاهایی مثل Dangling Pointer (اشارهگر معلق) جلوگیری میکنه که توی زبانهایی مثل C یا C++ خیلی دردسرسازن. به عبارت دیگه، Lifetime مثل یه نگهبان هوشمنده که نمیذاره کدتون به جاهای خطرناک حافظه سرک بکشه. چرا Lifetime انقدر توی Rust مهمه؟بیاید یه لحظه به این فکر کنیم که چرا Rust همچین سیستمی رو طراحی کرده. توی زبانهای برنامهنویسی دیگه مثل Python یا Java، مدیریت حافظه معمولاً با یه Garbage Collector انجام میشه. این ابزار خودش میگرده و حافظهای که دیگه لازم نیست رو آزاد میکنه. اما Rust راه دیگهای رو انتخاب کرده: اینجا خبری از Garbage Collector نیست! در عوض، با قوانین سختگیرانهای مثل Ownership (مالکیت) و Lifetime، مدیریت حافظه رو توی زمان کامپایل انجام میده. مزیتش چیه؟ اول اینکه برنامههاتون سریعتر اجرا میشن چون سربار Garbage Collector رو ندارید. دوم اینکه خیلی از خطاهایی که توی زبونای دیگه موقع اجرا (Runtime) پیداشون میشه، توی Rust همون اول، موقع کامپایل، گیر میافتن. Lifetime اینجا نقشش پررنگ میشه چون تضمین میکنه هر مرجعی که استفاده میکنید، به دادهای اشاره داره که هنوز زندهست. Lifetime توی کد چطور کار میکنه؟حالا بیاید یه نگاه عملیتر به موضوع بندازیم. توی Rust، Lifetime با یه علامت خاص به اسم Lifetime Annotation نشون داده میشه که معمولاً یه حرف مثل 'a یا 'b هست. این علامت به کامپایلر میگه که یه مرجع تا چه مدت معتبره و باید بهش توجه کنه. یه مثال ساده رو ببینیم: fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } }اینجا چی داریم؟ یه تابع به اسم longest که دو رشته (String) رو بهعنوان مرجع میگیره و بلندترینش رو برمیگردونه. علامت 'a به کامپایلر میگه که مرجع خروجی تابع (یعنی چیزی که برگردونده میشه) باید به دادهای اشاره کنه که حداقل به اندازه کوتاهترین Lifetime بین x و y زنده میمونه. اینجوری مطمئن میشیم خروجی همیشه به یه حافظه معتبر اشاره داره. اگه این 'a نبود، کامپایلر نمیتونست بفهمه که مرجع برگشتی تا کی قراره معتبر باشه و ممکن بود خطا بده یا بدتر، یه اشارهگر معلق بسازیم که به داده پاکشده اشاره کنه. یه مثال پیچیدهتر: Lifetime توی ساختارهافرض کنید یه ساختار (Struct) دارید که داخلش یه مرجع هست. توی Rust، وقتی یه مرجع توی یه ساختار استفاده میشه، باید Lifetime اون رو مشخص کنید. مثلاً: struct Book<'a> { title: &'a str, author: &'a str, } fn main() { let title = String::from("The Rust Journey"); let author = String::from("Jane Doe"); let book = Book { title: &title, author: &author, }; println!("{} by {}", book.title, book.author); }توی این کد، ساختار Book دو مرجع داره: title و author. علامت 'a به کامپایلر میگه که این مرجعها به دادههایی اشاره میکنن که حداقل تا یه مدت مشخص (یعنی Lifetime 'a) توی حافظه زنده هستن. اگه title یا author قبل از استفاده از book از حافظه پاک بشن، کامپایلر خطا میده و نمیذاره کد اجرا بشه. انواع Lifetime و قوانینشتوی Rust، Lifetimeها همیشه بهصورت دستی نوشته نمیشن. خیلی وقتها کامپایلر خودش میتونه بفهمه که Lifetime چیه و نیازی به مشخص کردنش نیست. به این میگن Lifetime Elision. مثلاً توی این کد: fn first_word(s: &str) -> &str { s.split(' ').next().unwrap() }نیازی به نوشتن a' نیست چون کامپایلر میفهمه که مرجع خروجی به ورودی s وابستهست و تا وقتی s زندهست، خروجی هم معتبره. اما وقتی موضوع پیچیدهتر میشه (مثل تابع longest یا ساختارها)، باید خودتون دست به کار بشید و Lifetime رو مشخص کنید. قانون اصلی اینه: هر مرجعی که برمیگردونید یا توی یه ساختار استفاده میکنید، باید به دادهای اشاره کنه که هنوز توی حافظه وجود داره. Lifetime و ایمنی حافظهیکی از بزرگترین مزیتهای Lifetime اینه که جلوی خطاهای حافظه رو میگیره. مثلاً توی C، ممکنه یه اشارهگر به حافظهای اشاره کنه که دیگه آزاد شده و برنامهتون کرش کنه. اما توی Rust، کامپایلر با چک کردن Lifetimeها مطمئن میشه که همچین چیزی اتفاق نیفته. این یعنی شما بهعنوان برنامهنویس، خیالتون راحته که کدتون از نظر حافظه امن و قابلاعتماده. چطور Lifetime رو یاد بگیریم؟اگه تازه با Rust شروع کردید، ممکنه اولش Lifetime یه کم گیجکننده به نظر بیاد. اما نگران نباشید! با تمرین و نوشتن کد، کمکم دستتون میاد که کجا باید ازش استفاده کنید. یه راه خوب برای یادگیری اینه که با مثالهای ساده شروع کنید و بعد سراغ پروژههای پیچیدهتر برید. مثلاً یه تابع بنویسید که چند مرجع رو مدیریت میکنه و ببینید کامپایلر چه خطاهایی میده. این خطاها بهترین معلم شما توی یادگیری Rust هستن! کاربرد Lifetime توی پروژههای واقعیLifetime فقط یه مفهوم تئوری نیست؛ توی دنیای واقعی هم حسابی به کار میاد. مثلاً فرض کنید دارید یه سرور وب با Rust مینویسید (مثل پروژههایی که با فریمورک Actix-Web ساخته میشن). توی همچین برنامهای، مدیریت دقیق مرجعها و حافظه خیلی مهمه تا سرور پایدار بمونه و کرش نکنه. Lifetime اینجا کمک میکنه که مطمئن بشید هیچ مرجعی به داده نامعتبر اشاره نمیکنه. یا مثلاً توی توسعه سیستمعاملها (مثل پروژه Redox که با Rust نوشته شده)، جایی که عملکرد و ایمنی حرف اول رو میزنه، Lifetime یه ابزار قدرتمند برای کنترل حافظهست. Lifetime توی Rust مثل یه سپر محافظه که نمیذاره کدتون به دردسر بیفته. این سیستم نهتنها باعث میشه برنامههاتون امنتر باشن، بلکه بهتون کنترل بیشتری روی منابع میده و کمک میکنه کدی بنویسید که هم سریع باشه و هم قابلاعتماد. اگه دنبال زبانی هستید که بهتون قدرت و امنیت رو با هم بده، Rust و مفهوم Lifetimeش چیزیه که نباید ازش غافل بشید.
-
وباسکرپینگ با پایتون: چگونه دادهها را از وب استخراج کنیم؟
sina پاسخی ارسال کرد برای یک مطلب در برنامه نویسیدر دنیای امروز که دادهها حرف اول رو میزنن، توانایی استخراج اطلاعات از وب یه مهارت فوقالعاده ارزشمنده. چه بخواهید قیمت محصولات یه فروشگاه آنلاین رو جمعآوری کنید، چه اخبار رو تحلیل کنید یا دادههای تحقیقاتی رو از سایتها بکشید بیرون، وباسکرپینگ (Web Scraping) راهحل شماست. توی این مقاله قراره با وباسکرپینگ، ابزارهای پایتون برای این کار و مراحل انجامش آشنا بشید. اگه به پایتون و داده علاقه دارید، تا آخر با من همراه باشید! وباسکرپینگ چیه؟وباسکرپینگ به فرآیند خودکار جمعآوری دادهها از صفحات وب گفته میشه. به جای اینکه دستی برید و اطلاعات رو کپی کنید، یه برنامه مینویسید که این کار رو براتون انجام بده. پایتون به خاطر سادگی، کتابخانههای قدرتمند و انعطافپذیریش، یکی از بهترین زبانها برای وباسکرپینگه. چرا پایتون؟کتابخانههای آماده: ابزارهایی مثل Beautiful Soup، Requests و Scrapy کار رو خیلی راحت میکنن. خوانایی کد: حتی اگه تازهکار باشید، میتونید با چند خط کد شروع کنید. جامعه بزرگ: هر سوالی داشته باشید، جوابش توی انجمنهای پایتون پیدا میشه. ابزارهای اصلی وباسکرپینگ با پایتونبیاید با چند ابزار کلیدی که توی این مسیر نیاز دارید آشنا بشیم: Requests: برای ارسال درخواست HTTP و دریافت محتوای صفحه وب. Beautiful Soup: برای تجزیه (parse) کردن HTML و پیدا کردن دادههای مورد نظر. Scrapy: یه فریمورک کامل برای پروژههای بزرگتر و پیچیدهتر (اختیاری). یه مثال ساده: استخراج عنوان صفحهبیاید با یه مثال عملی شروع کنیم. فرض کنید میخواهیم عنوان یه صفحه وب رو بگیریم: import requests from bs4 import BeautifulSoup # آدرس صفحهای که میخواهیم اسکرپ کنیم url = "https://example.com" # درخواست به صفحه response = requests.get(url) # چک کردن اینکه درخواست موفق بوده if response.status_code == 200: # تجزیه HTML soup = BeautifulSoup(response.text, "html.parser") # پیدا کردن تگ عنوان title = soup.find("title").text print(f"عنوان صفحه: {title}") else: print("خطا در اتصال به صفحه")خروجی چیزی شبیه اینه: عنوان صفحه: Example Domainاینجا با requests محتوای صفحه رو گرفتیم و با BeautifulSoup تگ <title> رو پیدا کردیم. ساده بود، نه؟ مراحل وباسکرپینگبرای یه پروژه وباسکرپینگ موفق، این مراحل رو دنبال کنید: شناسایی هدف: اول مشخص کنید چه دادهای میخواهید (مثلاً قیمتها، نظرات کاربران، یا لینکها). بررسی ساختار صفحه: ابزار توسعهدهنده مرورگر (Inspector) رو باز کنید و HTML صفحه رو نگاه کنید تا بفهمید دادهها کجا هستن. نوشتن کد: با پایتون درخواست بفرستید و دادهها رو استخراج کنید. ذخیره دادهها: اطلاعات رو توی فایل CSV، JSON یا دیتابیس ذخیره کنید. اتوماسیون (اختیاری): اگه نیازه، کدتون رو طوری تنظیم کنید کi (مثلاً روزانه) اجرا بشه. یه پروژه واقعی: استخراج قیمت محصولاتفرض کنید میخواهید قیمت یه محصول رو از یه سایت فروشگاهی بکشید بیرون: import requests from bs4 import BeautifulSoup url = "https://fake-shop.com/product/sample" # آدرس فرضی response = requests.get(url) if response.status_code == 200: soup = BeautifulSoup(response.text, "html.parser") price = soup.find("span", class_="price").text # فرض بر اینکه قیمت توی این تگ هست print(f"قیمت محصول: {price}") else: print("اتصال ناموفق")توی این کد، ما دنبال تگ <span> با کلاس price گشتیم. اگه سایت واقعی باشه، باید کلاس یا تگ دقیق رو از HTMLش پیدا کنید. نکات مهم و چالشهاوباسکرپینگ به همین سادگیا هم نیست! یه سری نکته و چالش هست که باید حواستون باشه: قوانین سایت: قبل از اسکرپ کردن، فایل robots.txt سایت رو چک کنید و مطمئن شید که اجازه این کار رو دارید. بعضی سایتها اسکرپینگ رو ممنوع کردن. بلاک شدن: اگه درخواستهای زیادی بفرستید، ممکنه IPتون بلاک بشه. برای این کار میتونید از تأخیر (delay) بین درخواستها یا پراکسی استفاده کنید. تغییر ساختار سایت: اگه سایت HTMLش رو عوض کنه، کدهاتون ممکنه خراب بشه. باید آماده بهروزرسانی باشید. دادههای پویا: بعضی سایتها با جاوا اسکریپت لود میشن. توی این موارد، ابزارهایی مثل Selenium یا Playwright که مرورگر رو شبیهسازی میکنن، به کارتون میاد. ذخیره دادهها توی فایلبعد از استخراج، میتونید دادهها رو توی یه فایل CSV ذخیره کنید: import csv data = {"title": "محصول نمونه", "price": "100,000 تومان"} with open("products.csv", "w", newline="", encoding="utf-8") as file: writer = csv.DictWriter(file, fieldnames=["title", "price"]) writer.writeheader() writer.writerow(data)حالا دادههاتون توی یه فایل مرتب ذخیره شدن! چرا وباسکرپینگ یاد بگیریم؟تحلیل بازار: قیمتها و محصولات رقبا رو مقایسه کنید. تحقیقات: دادههای علمی یا خبری رو جمعآوری کنید. اتوماسیون: کارای تکراری مثل چک کردن موجودی انبار رو خودکار کنید. جمعبندیوباسکرپینگ با پایتون یه مهارت قدرتمنده که با چند خط کد ساده شروع میشه، اما میتونه به پروژههای پیچیده و حرفهای تبدیل بشه. ابزارهایی مثل Requests و Beautiful Soup نقطه شروع عالیای هستن و اگه بخواهید حرفهایتر بشید، میتونید سراغ Scrapy یا Selenium برید. فقط یادتون باشه با مسئولیتپذیری و رعایت قوانین پیش برید.
-
بررسی Ownership و Borrowing در Rust: کلید امنیت حافظه و عملکرد بهینه
sina پاسخی ارسال کرد برای یک مطلب در برنامه نویسیزبان برنامهنویسی Rust به دلیل رویکرد نوآورانهاش در مدیریت حافظه و ایمنی کد، در سالهای اخیر توجه بسیاری از توسعهدهندگان را به خود جلب کرده است. یکی از مهمترین ویژگیهای این زبان که آن را از رقبا متمایز میکند، سیستم Ownership (مالکیت) و Borrowing (امانتدهی) است. در این مقاله، به صورت عمیق و پیشرفته به این مفاهیم میپردازم، نحوه کارکرد آنها را بررسی میکنیم و نشان میدهیم که چرا این قابلیتها، Rust را به انتخابی ایدهآل برای پروژههای پیچیده و حساس تبدیل کردهاند. اگر به دنبال یادگیری پیشرفته Rust یا بهبود مهارتهای برنامهنویسی خود هستید، تا انتها همراه من باشید. Ownership در Rust چیست و چرا اهمیت دارد؟در هسته زبان Rust، مفهومی به نام Ownership قرار دارد که به معنای مالکیت دادهها توسط یک متغیر خاص است. هر تکه داده در حافظه فقط یک صاحب دارد و وقتی این صاحب از دسترس خارج شود (مثلاً با پایان یافتن محدوده یا scope)، حافظه به صورت خودکار آزاد میشود. این مکانیزم بدون نیاز به Garbage Collector عمل میکند و به همین دلیل، Rust عملکردی نزدیک به زبانهایی مثل C++ ارائه میدهد، اما با ایمنی بیشتر. برای مثال، کد زیر را در نظر بگیرید: fn main() { let s = String::from("Hello, Rust"); println!("{}", s); } // حافظه متعلق به s اینجا آزاد میشوداین سیستم ساده به نظر میرسد، اما وقتی بحث انتقال مالکیت (Move) پیش میآید، قدرت واقعی آن مشخص میشود. انتقال مالکیت (Move) و جلوگیری از خطاهای حافظهدر Rust، وقتی یک متغیر به متغیر دیگری اختصاص داده میشود، مالکیت داده منتقل میشود و متغیر اولیه دیگر معتبر نیست. این رفتار که به Move معروف است، از دسترسی همزمان چند متغیر به یک حافظه جلوگیری میکند و خطاهایی مثل dangling pointer یا double free را حذف میکند. مثال زیر این موضوع را روشنتر میکند: fn main() { let s1 = String::from("Rust"); let s2 = s1; // مالکیت به s2 منتقل میشود // println!("{}", s1); // خطا: s1 دیگر معتبر نیست println!("{}", s2); // خروجی: Rust }این قانون سختگیرانه تضمین میکند که در هر لحظه فقط یک متغیر کنترل داده را در اختیار داشته باشد، که برای برنامهنویسی امن و بدون باگ حیاتی است. Borrowing: انعطافپذیری در کنار امنیتگاهی اوقات نیازی به انتقال مالکیت نیست و فقط میخواهید به داده دسترسی پیدا کنید یا آن را تغییر دهید. اینجا Borrowing وارد میدان میشود. Rust دو نوع امانتدهی ارائه میدهد: Reference غیرقابل تغییر (&): امکان خواندن داده بدون تغییر آن. Reference قابل تغییر (&mut): امکان تغییر داده با رعایت قوانین ایمنی. مثال: fn main() { let mut s = String::from("Hello"); let r1 = &s; // امانت غیرقابل تغییر println!("Reference: {}", r1); let r2 = &mut s; // امانت قابل تغییر r2.push_str(", Rust!"); println!("Updated: {}", s); // خروجی: Hello, Rust! }قانون کلیدی Borrowing در Rust این است: میتوانید چندین امانت غیرقابل تغییر داشته باشید، اما فقط یک امانت قابل تغییر در هر لحظه مجاز است و این دو نمیتوانند همزمان وجود داشته باشند. این محدودیت از Data Race (رقابت داده) جلوگیری میکند و ایمنی را تضمین میکند. کاربرد Ownership و Borrowing در برنامهنویسی چندنخییکی از حوزههایی که این سیستم واقعاً میدرخشد، برنامهنویسی چندنخی (multi-threaded) است. در زبانهای سنتی مثل C++، مدیریت دسترسی thread ها به دادههای مشترک نیازمند استفاده از قفلها (locks) و هماهنگی دستی است. اما Rust با قوانین Ownership و ابزارهایی مثل Arc و Mutex، این پیچیدگی را به حداقل میرساند. مثال پیشرفته: use std::sync::{Arc, Mutex}; use std::thread; fn main() { let data = Arc::new(Mutex::new(vec![1, 2, 3])); let mut threads = vec![]; for i in 0..3 { let data = Arc::clone(&data); threads.push(thread::spawn(move || { let mut num = data.lock().unwrap(); num.push(i); println!("Thread {}: {:?}", i, num); })); } for t in threads { t.join().unwrap(); } }در این کد، Arc (Atomic Reference Counting) امکان اشتراک داده بین thread ها را فراهم میکند و Mutex تضمین میکند که فقط یک thread در لحظه به داده دسترسی تغییرپذیر داشته باشد. نتیجه؟ کدی امن، بدون Data Race و با عملکرد بالا. مزایای استفاده از Ownership و Borrowing در Rustایمنی حافظه: حذف خطاهای رایج مثل استفاده از اشارهگرهای نامعتبر. عملکرد بهینه: عدم نیاز به Garbage Collector و مدیریت دستی حافظه. کنترل دقیق: توسعهدهنده با قوانین مشخص، کنترل بهتری بر رفتار برنامه دارد. البته، این سیستم در ابتدا ممکن است برای برنامهنویسان تازهکار چالشبرانگیز باشد، اما با تسلط بر آن، میتوانید پروژههایی پایدار و قابل اعتماد بسازید.
-
مدیریت دستی حافظه در زبان C
sina پاسخی ارسال کرد برای یک مطلب در برنامه نویسیزبان برنامهنویسی C به عنوان یکی از پایهایترین و قدرتمندترین زبانهای برنامهنویسی شناخته میشه که هنوز هم در توسعه سیستمعاملها، نرمافزارهای Embedded و برنامههای با کارایی بالا حرف اول رو میزنه. یکی از قابلیتهای خاص و برجسته این زبان، مدیریت دستی حافظه هست که به برنامهنویس اجازه میده به طور مستقیم کنترل کاملی روی تخصیص و آزادسازی حافظه داشته باشه. این ویژگی، هم یه مزیت بزرگه و هم یه چالش مهم که درک عمیقش برای هر برنامهنویس C ضروریه. مدیریت دستی حافظه چیه؟در C، برخلاف زبانهای مدرنتر مثل Java یا Python که از Garbage Collector برای مدیریت خودکار حافظه استفاده میکنن، برنامهنویس خودش مسئول تخصیص و آزادسازی حافظهست. این کار با استفاده از توابعی مثل malloc()، calloc()، realloc() و free() انجام میشه. به عنوان مثال، وقتی نیاز به یه آرایه پویا دارید، میتونید با malloc() حافظه مورد نیاز رو تخصیص بدید و بعد از استفاده، با free() اون رو آزاد کنید. این کنترل دستی به شما اجازه میده دقیقاً بفهمید چه مقدار حافظه در چه زمانی استفاده میشه و کی آزاد میشه. چرا این قابلیت خاصه؟مدیریت دستی حافظه به برنامهنویس قدرت بینظیری میده. اول اینکه، بهینهسازی عملکرد برنامهها در C خیلی بهتر از زبانهایی با مدیریت خودکار حافظهست. چون هیچ پروسه اضافی مثل Garbage Collection وجود نداره که به صورت غیرقابل پیشبینی اجرا بشه و منابع سیستم رو مصرف کنه. این ویژگی برای برنامههایی که نیاز به سرعت بالا دارن، مثل سیستمعاملها یا بازیهای ویدیویی، حیاتیه. دوم اینکه، شما میتونید حافظه رو دقیقاً به شکلی که نیاز دارید مدیریت کنید؛ مثلاً یه ساختار داده پیچیده بسازید و فقط همون مقدار حافظهای که لازمه رو تخصیص بدید. مثال عملیفرض کنید میخواید یه لیست پویا از اعداد بسازید. در C، میتونید این کار رو با یه اشارهگر و تخصیص حافظه انجام بدید: #include <stdio.h> #include <stdlib.h> int main() { int *numbers; int size = 5; numbers = (int *)malloc(size * sizeof(int)); // تخصیص حافظه برای 5 عدد if (numbers == NULL) { printf("خطا در تخصیص حافظه!\n"); return 1; } for (int i = 0; i < size; i++) { numbers[i] = i + 1; } for (int i = 0; i < size; i++) { printf("%d ", numbers[i]); } free(numbers); // آزادسازی حافظه return 0; }این کد نشون میده که چطور حافظه تخصیص داده میشه، استفاده میشه و بعد آزاد میشه. اگه free() رو فراموش کنید، با مشکل Memory Leak مواجه میشید که حافظه به طور بیهوده اشغال میمونه. چالشها و مسئولیتهابا این همه قدرت، یه مسئولیت بزرگ هم میاد. مدیریت نادرست حافظه میتونه به مشکلاتی مثل Memory Leak، Dangling Pointers (اشارهگرهایی که به حافظه آزادشده اشاره میکنن) یا Segmentation Fault منجر بشه. برای همین، برنامهنویس باید دقت زیادی داشته باشه و همیشه مطمئن بشه که هر حافظهای که تخصیص داده، در زمان مناسب آزاد میشه. نتیجهگیریمدیریت دستی حافظه در C مثل یه شمشیر دو لبهست: هم قدرت فوقالعادهای به برنامهنویس میده و هم نیاز به دقت و مهارت داره. این قابلیت باعث شده C همچنان انتخاب اول برای پروژههایی باشه که نیاز به کنترل سطح پایین و بهینهسازی دقیق دارن. اگه دنبال زبانی هستید که شما رو مجبور کنه عمیقتر به عملکرد سیستم فکر کنید و در عین حال انعطافپذیری بالایی بهتون بده، C و مدیریت دستی حافظهش یه گزینه بیرقیبه.