تمامی فعالیت ها
این جریان به طور خودکار بروزرسانی می شود
- هفته گذشته
-
معرفی ویژگی 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', '[email protected]', 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 <[email protected]>"]). 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 <[email protected]>"] 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 عکس نمایه خود را تغییر داد
-
سندروم ایمپاستر بین برنامهنویسها و مهندسهای نرمافزار: چرا همیشه فکر میکنیم به اندازه کافی خوب نیستیم؟
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 و مدیریت دستی حافظهش یه گزینه بیرقیبه.
-
یادگیری نظارتشده (Supervised Learning)
sina پاسخی ارسال کرد برای یک مطلب در هوش مصنوعیدر دنیای هوش مصنوعی و یادگیری ماشین، اصطلاحاتی مثل "یادگیری نظارتشده" یا "Supervised Learning" بارها به گوشمون میخوره. اما واقعاً این مفهوم چیه و چرا انقدر مهمه؟ توی این مقاله قراره به زبانی ساده و با مثالهای ملموس، یادگیری نظارتشده رو توضیح بدیم، کاربردهاش رو بررسی کنیم و ببینیم چطور توی زندگی روزمره ما اثر میذاره. پس اگه به دنیای تکنولوژی و دادهها علاقه دارید، تا آخر این مقاله همراه من باشید! یادگیری نظارتشده چیست؟یادگیری نظارتشده یکی از شاخههای اصلی یادگیری ماشین (Machine Learning) هست که توی اون، یه مدل کامپیوتری با استفاده از دادههای برچسبگذاریشده آموزش داده میشه. حالا "دادههای برچسبگذاریشده" یعنی چی؟ فرض کنید میخواهید به یه بچه یاد بدید که گربه چیه. یه عالمه عکس بهش نشون میدید و میگید: "این گربهست، این سگ، اینم پرنده." بعد از یه مدت، بچه یاد میگیره که خودش تشخیص بده چه حیوانی توی عکس هست. توی یادگیری نظارتشده هم همین اتفاق میافته: ما به مدل دادههایی میدیم که جواب درستشون (یا همون برچسب) مشخصه، و مدل با تمرین یاد میگیره که چطور الگوها رو تشخیص بده و پیشبینی کنه. به عبارت سادهتر، یادگیری نظارتشده مثل یه معلم سختگیره که به شاگردش میگه: "این ورودیها رو بگیر، این خروجیها رو به خاطر بیار، و سعی کن دفعه بعد خودت درست حدس بزنی." این فرآیند شامل دو مرحله اصلیه: آموزش (Training) و آزمایش (Testing). اجزای اصلی یادگیری نظارتشدهبرای اینکه یادگیری نظارتشده درست کار کنه، چند جزء کلیدی داریم: دادههای آموزشی (Training Data): این دادهها شامل ورودیها (مثل ویژگیها یا Features) و خروجیهای مرتبط (برچسبها یا Labels) هستن. مثلاً اگه بخوایم یه مدل برای تشخیص ایمیلهای اسپم بسازیم، دادههای آموزشی شامل متن ایمیلها (ورودی) و برچسب "اسپم" یا "غیراسپم" (خروجی) میشه. مدل (Model): مدل مثل یه مغز مصنوعیه که قراره یاد بگیره. این میتونه یه الگوریتم ساده مثل رگرسیون خطی باشه یا یه چیز پیچیدهتر مثل شبکههای عصبی. تابع هزینه (Loss Function): این تابع به مدل میگه چقدر پیشبینیش با جواب درست فاصله داره. هدف اینه که این خطا رو کم کنیم. الگوریتم بهینهسازی: این بخش مدل رو تنظیم میکنه تا خطاهاش کمتر بشه. مثلاً الگوریتم گرادیان نزولی (Gradient Descent) یکی از روشهای معروفه. انواع مسائل در یادگیری نظارتشدهیادگیری نظارتشده معمولاً به دو دسته اصلی تقسیم میشه: رگرسیون (Regression): وقتی خروجی یه مقدار پیوسته (عدد) باشه، مثل پیشبینی قیمت خونه یا دمای هوا. مثلاً فرض کنید میخواهید با توجه به متراژ خونه و محلهش، قیمتش رو حدس بزنید. اینجا از الگوریتمهای رگرسیون مثل رگرسیون خطی استفاده میشه. طبقهبندی (Classification): وقتی خروجی یه دسته یا کلاس مشخص باشه، مثل تشخیص اینکه یه ایمیل اسپمه یا نه، یا اینکه یه عکس گربهست یا سگ. الگوریتمهایی مثل رگرسیون لجستیک، ماشین بردار پشتیبان (SVM) یا درخت تصمیم توی این مسائل خیلی کاربرد دارن. چطور یادگیری نظارتشده کار میکنه؟بیاید با یه مثال ساده این فرآیند رو مرور کنیم. فرض کنید میخواهید یه مدل بسازید که تشخیص بده یه میوه سیبه یا پرتقاله. مراحل کار اینجوریه: جمعآوری داده: یه مجموعه داده درست میکنید که شامل ویژگیهای میوهها (مثل رنگ، اندازه، وزن) و برچسبشون (سیب یا پرتقال) باشه. آموزش مدل: این دادهها رو به مدل میدید و ازش میخواهید الگوها رو پیدا کنه. مثلاً یاد میگیره که میوههای نارنجیرنگ و سنگینتر معمولاً پرتقالن. ارزیابی مدل: یه سری داده جدید (که مدل قبلاً ندیده) بهش میدید و میبینید چقدر درست پیشبینی میکنه. بهبود مدل: اگه خطاهاش زیاد باشه، پارامترهاش رو تنظیم میکنید یا دادههای بیشتری بهش میدید. در نهایت، مدلتون آمادهست که میوههای جدید رو با دقت خوبی تشخیص بده! الگوریتمهای معروف در یادگیری نظارتشدهحالا که با کلیات آشنا شدیم، بیاید چندتا از الگوریتمهای معروف رو بشناسیم: رگرسیون خطی (Linear Regression): برای پیشبینی مقادیر عددی مثل قیمت یا دما. رگرسیون لجستیک (Logistic Regression): برای مسائل طبقهبندی مثل تشخیص اسپم. درخت تصمیم (Decision Tree): یه مدل درختی که تصمیمگیری رو مرحله به مرحله انجام میده. ماشین بردار پشتیبان (SVM): برای پیدا کردن بهترین خط یا صفحه جداکننده بین کلاسها. شبکههای عصبی (Neural Networks): برای مسائل پیچیدهتر مثل تشخیص تصویر یا صدا. هر کدوم از این الگوریتمها مزایا و معایب خودشون رو دارن و بسته به نوع مسئله و دادهها انتخاب میشن. کاربردهای یادگیری نظارتشدهیادگیری نظارتشده توی زندگی روزمره ما خیلی بیشتر از چیزی که فکر میکنید حضور داره. چندتا مثال جالب: تشخیص اسپم: ایمیلهاتون رو فیلتر میکنه تا از شر تبلیغات مزاحم خلاص بشید. تشخیص چهره: توی گوشیتون یا دوربینهای امنیتی استفاده میشه. پیشبینی آبوهوا: دما و بارندگی رو با دقت پیشبینی میکنه. تشخیص بیماری: با تحلیل دادههای پزشکی، میتونه بیماریها رو زود تشخیص بده. ماشینهای خودران: برای تشخیص علائم رانندگی و عابرین پیاده. مزایا و معایب یادگیری نظارتشدهمثل هر تکنولوژی دیگهای، یادگیری نظارتشده هم نقاط قوت و ضعف خودش رو داره: مزایا: دقت بالا وقتی دادههای برچسبگذاریشده باکیفیت داریم. قابل فهم و قابل اعتماد برای مسائل مشخص. کاربرد گسترده توی صنعت و علم. معایب: نیاز به دادههای برچسبگذاریشده داره که گاهی جمعآوریش گرون و زمانبره. اگه دادهها ناقص یا اشتباه باشن، مدل هم اشتباه یاد میگیره. برای مسائل خیلی پیچیده ممکنه به اندازه روشهای دیگه (مثل یادگیری عمیق) انعطافپذیر نباشه. تفاوت یادگیری نظارتشده با یادگیری بدون نظارتیه سوال رایج اینه که یادگیری نظارتشده چه فرقی با یادگیری بدون نظارت (Unsupervised Learning) داره؟ توی یادگیری بدون نظارت، ما برچسب نداریم و مدل خودش باید الگوها رو پیدا کنه. مثلاً فرض کنید یه عالمه عکس میوه دارید، ولی نمیدونید کدوم سیبه و کدوم پرتقاله. مدل بدون نظارت میتونه میوهها رو بر اساس شباهتهاشون گروهبندی کنه، ولی نمیتونه بگه "این سیبه." برای همین، یادگیری نظارتشده برای وقتی مناسبه که جواب درست رو از قبل داریم و میخواهیم پیشبینی دقیق انجام بدیم. آینده یادگیری نظارتشدهبا پیشرفت تکنولوژی و افزایش قدرت محاسباتی، یادگیری نظارتشده داره هر روز قویتر میشه. ترکیبش با روشهای دیگه مثل یادگیری عمیق (Deep Learning) باعث شده که توی حوزههایی مثل پزشکی، خودروسازی و حتی هنر، تحولات بزرگی ایجاد بشه. اما چالشهایی مثل نیاز به دادههای زیاد و مسائل اخلاقی (مثل سوگیری توی دادهها) هنوز باید حل بشن. نتیجهگیرییادگیری نظارتشده مثل یه ابزار جادویی توی جعبه ابزار هوش مصنوعیه. با دادن دادههای درست و یه مدل مناسب، میتونیم مسائل پیچیده رو حل کنیم و زندگیمون رو بهتر کنیم. از تشخیص اسپم توی ایمیل گرفته تا پیشبینی قیمت خونه، این روش توی هر گوشه از دنیای مدرن ما حضور داره. اگه به یادگیری ماشین علاقه دارید، یادگیری نظارتشده یه نقطه شروع عالیه.
-
آشنایی با GMP در PHP: ابزار قدرتمند برای محاسبات اعداد بزرگ
sina پاسخی ارسال کرد برای یک مطلب در برنامه نویسیدر دنیای برنامهنویسی، گاهی نیاز داریم با اعدادی کار کنیم که از حد معمول بزرگتر یا پیچیدهتر هستند. مثلاً اعداد بسیار بزرگ در رمزنگاری، محاسبات ریاضی پیشرفته یا حتی مسائل علمی. زبان برنامهنویسی PHP بهصورت پیشفرض برای کار با اعداد معمولی طراحی شده، اما وقتی پای اعداد بزرگ یا محاسبات دقیق به میان میاد، نیاز به ابزاری قویتر احساس میشه. اینجا GMP (مخفف GNU Multiple Precision) وارد صحنه میشه. تو این مقاله قراره بهطور کامل با GMP در PHP آشنا بشیم، کاربردهاش رو بررسی کنیم و ببینیم چطور میتونیم ازش استفاده کنیم. GMP چیه و چرا مهمه؟GMP یه کتابخونه متنباز (open-source) هست که برای انجام محاسبات دقیق روی اعداد بزرگ و اعداد صحیح با دقت بالا طراحی شده. این کتابخونه ابتدا به زبان C نوشته شده، اما PHP از طریق افزونهای به نام GMP (که باید نصب بشه) این امکان رو به برنامهنویسا میده که از قابلیتهاش استفاده کنن. فرق اصلی GMP با روشهای معمولی PHP اینه که محدودیتهای اعداد صحیح یا اعشاری رو دور میزنه و اجازه میده با اعدادی کار کنیم که شاید صدها یا هزاران رقم داشته باشن. تو PHP، اعداد صحیح (integer) یه محدوده مشخص دارن (مثلاً در سیستمهای ۶۴ بیتی از -2^63 تا 2^63-1). اگه بخواید یه عدد بزرگتر از این محدوده رو محاسبه کنید، یا به مشکل سرریز (overflow) برمیخورید یا نتیجهتون دقیق نیست. GMP این مشکل رو حل میکنه و به شما اجازه میده بدون نگرانی از محدودیت، محاسبات رو انجام بدید. نصب GMP در PHPبرای استفاده از GMP، اول باید مطمئن بشید که این افزونه روی سرورتون نصب شده باشه. اگه از PHP روی لینوکس استفاده میکنید، میتونید با دستور زیر GMP رو نصب کنید: sudo apt-get install php-gmpبعد از نصب، باید چک کنید که تو فایل php.ini فعال باشه. برای این کار، خط زیر رو تو فایل اضافه کنید یا مطمئن بشید که وجود داره: extension=gmpاگه سرور ویندوزی دارید، باید فایل DLL مربوط به GMP رو دانلود کنید و تو تنظیمات PHP فعالش کنید. بعد از ریاستارت سرور، با اجرای تابع phpinfo() میتونید ببینید که GMP فعال شده یا نه. کار با GMP در PHPحالا که GMP رو نصب کردیم، بیایم چندتا از تابعهای مهمش رو بررسی کنیم. GMP تو PHP بهصورت شیءگرا (object-oriented) و رویهای (procedural) قابل استفادست. تو این بخش چند مثال ساده میزنم که بتونید سریع دست به کد بشید. 1.جمع و تفریق اعداد بزرگ: فرض کنید میخواهیم دو عدد خیلی بزرگ رو جمع کنیم: $num1 = gmp_init("12345678901234567890"); $num2 = gmp_init("98765432109876543210"); $sum = gmp_add($num1, $num2); echo gmp_strval($sum); // خروجی: 111111111011111111100تابع gmp_init یه عدد بزرگ رو به فرمت GMP تبدیل میکنه. بعد با gmp_add جمع رو انجام میدیم و با gmp_strval نتیجه رو به رشته تبدیل میکنیم تا نمایش داده بشه. 2.ضرب و تقسیم: برای ضرب از gmp_mul و برای تقسیم از gmp_div استفاده میکنیم: $product = gmp_mul($num1, $num2); echo gmp_strval($product); // یه عدد خیلی بزرگ! 3.توان و ریشه: اگه بخواید یه عدد رو به توان برسونید، از gmp_pow استفاده کنید: $power = gmp_pow($num1, 3); // num1 به توان 3 echo gmp_strval($power); 4.مقایسه اعداد: برای مقایسه دو عدد بزرگ، تابع gmp_cmp به کار میره: $result = gmp_cmp($num1, $num2); if ($result > 0) { echo "عدد اول بزرگتره"; } elseif ($result < 0) { echo "عدد دوم بزرگتره"; } else { echo "برابرن"; }کاربردهای GMPGMP فقط برای جمع و تفریق اعداد بزرگ نیست. تو زمینههای مختلفی مثل رمزنگاری (مثلاً الگوریتم RSA)، محاسبات علمی، و حتی بازیسازی که نیاز به دقت بالا داره، استفاده میشه. مثلاً تو RSA، شما باید با اعداد اول خیلی بزرگ کار کنید که GMP این کار رو راحت میکنه. مزایا و معایبمزایای GMP واضحه: دقت بالا، پشتیبانی از اعداد بزرگ، و انعطافپذیری. اما یه سری معایب هم داره. مثلاً سرعتش نسبت به عملیات معمولی PHP کمتره، چون محاسبات پیچیدهتری انجام میده. همچنین نیاز به نصب جداگانه داره که ممکنه برای همه پروژهها عملی نباشه. جمعبندیGMP تو PHP یه ابزار قدرتمنده که به برنامهنویسا اجازه میده از محدودیتهای اعداد معمولی عبور کنن و با خیال راحت محاسبات پیچیده انجام بدن. اگه تو پروژهتون نیاز به کار با اعداد بزرگ یا دقت بالا دارید، GMP میتونه بهترین دوستتون باشه. با نصب ساده و توابع کاربردی، این افزونه به شما کمک میکنه پروژههاتون رو به سطح بالاتری ببرید.