Syntax | سینتکس – Telegram
Syntax | سینتکس
سوال: چجوری سی پی یو، نوع دیتایی که توی memory ذخیره کردیم رو، به درستی شناسایی می کنه؟ #Question @Syntax_fa
بیایید این موضوع رو با جزئیات بیشتری بررسی کنیم.

### مفهوم نوع داده

در زبان‌های برنامه‌نویسی، نوع داده‌ها (Data Types) به ما کمک می‌کنند تا مشخص کنیم که یک تکه از حافظه باید چگونه تفسیر و استفاده شود. به عنوان مثال، یک نوع داده می‌تواند عدد صحیح، عدد اعشاری، کاراکتر، رشته یا هر نوع داده دیگری باشد.

### نقش برنامه‌نویس

برنامه‌نویس هنگام نوشتن کد، نوع داده‌ها را تعریف می‌کند. برای مثال در زبان C، شما می‌توانید یک عدد صحیح را اینگونه تعریف کنید:

int a = 10;


در اینجا int بیانگر این است که متغیر a یک عدد صحیح است.

### نقش کامپایلر

کامپایلر نقش مهمی در ترجمه کد برنامه‌نویس به کد ماشین (که توسط سی پی یو قابل فهم است) دارد. کامپایلر نوع داده‌ها را از کد منبع (Source Code) می‌خواند و بر اساس آن دستورات مناسب را تولید می‌کند. این دستورات شامل عملیات‌هایی مانند جمع، تفریق، ضرب و تقسیم برای اعداد صحیح، یا عملیات‌های دیگری برای انواع داده‌های مختلف است.

### نقش حافظه

حافظه (RAM) فقط مجموعه‌ای از بیت‌ها است. حافظه نوع داده‌ها را نمی‌داند؛ فقط داده‌ها را ذخیره و بازیابی می‌کند. این برنامه‌نویس و کامپایلر هستند که مشخص می‌کنند چگونه باید به این بیت‌ها نگاه کرد و آنها را تفسیر کرد.

### نقش سی پی یو

سی پی یو دستورات کامپایل شده را اجرا می‌کند. سی پی یو فقط بیت‌ها را می‌بیند و نوع داده‌ها برای آن معنایی ندارد. به عنوان مثال، اگر سی پی یو دستور ADD را دریافت کند، دو تکه از داده‌ها (که می‌تواند اعداد صحیح، اعشاری یا هر نوع دیگری باشند) را جمع می‌کند. سی پی یو اهمیتی نمی‌دهد که این داده‌ها چه نوعی دارند، فقط دستور را اجرا می‌کند.

### مثال عملی

فرض کنید شما یک برنامه ساده دارید که دو عدد صحیح را در حافظه ذخیره می‌کند و آنها را جمع می‌کند.

1. تعریف متغیرها:
    int a = 5;
int b = 10;


2. کامپایل کد:
کامپایلر این کد را به دستوراتی تبدیل می‌کند که سی پی یو می‌تواند اجرا کند. این دستورات می‌توانند به شکل زیر باشند (به زبان ماشین):
    MOV eax, [a]   ; بارگذاری عدد اول در ثبات eax
ADD eax, [b] ; افزودن عدد دوم به eax


3. اجرای دستورات توسط سی پی یو:
سی پی یو این دستورات را اجرا می‌کند. در اینجا، MOV و ADD دستورات ساده‌ای هستند که سی پی یو را راهنمایی می‌کنند تا داده‌ها را از حافظه بارگذاری کرده و آنها را جمع کند.

### نتیجه

سی پی یو به خودی خود نوع داده‌ها را شناسایی نمی‌کند. این برنامه‌نویس است که نوع داده‌ها را تعریف می‌کند و کامپایلر است که این اطلاعات را به دستورات قابل فهم برای سی پی یو تبدیل می‌کند. حافظه فقط داده‌ها را به صورت بیت ذخیره می‌کند و سی پی یو این بیت‌ها را بدون توجه به نوع آنها پردازش می‌کند. بنابراین، مدیریت صحیح نوع داده‌ها بر عهده برنامه‌نویس و کامپایلر است.

### خلاصه

- برنامه‌نویس: نوع داده‌ها را در کد منبع تعریف می‌کند.
- کامپایلر: کد منبع را تجزیه و تحلیل کرده و دستورات مناسب برای سی پی یو تولید می‌کند.
- حافظه: داده‌ها را به صورت بیت ذخیره می‌کند و نوع داده‌ها را نمی‌داند.
- سی پی یو: دستورات را اجرا می‌کند و بیت‌ها را بدون توجه به نوع آنها پردازش می‌کند.

#data_types

@Syntax_fa
👍12
وقتی یه برنامه نویس میره تفریح ولی باید تو سرور یچیزیو درست کنه😔

#fun

@Syntax_fa
💔17
main.pdf
153.9 KB
دوستان اگه برای پروژه هاتون فرانت کار نیاز داشتید این دوستمون رو حتما در نظر بگیرید.
(برید نمونه کارای رزومشو ببینید تا پی ببرید چقدر کارش خوبه)

ایدی تلگرام:
@Alivolley
🔥8👍1
یه سایتی هست به نام #ناکامولوژی که میاد استارتاپ هایی که شکست خوردن رو کامل معرفی میکنه و علت شکست رو هم عنوان میکنه👌 میتونه برای مقابله با خوش بینی بیش از حد و انتخاب مسیر درست خیلی کمک کننده باشه. اینکه صرفا چشممون به استارت آپ های قوی نره و فکر نکنیم فقط شروع کردن مهمه!🤔 خیلی چیزا هست که میتونه یه پروژه خیلی قوی رو زمین بزنه💥 خوندن داستان های شکست به اندازه ی خوندن داستان های موفقیت ضروریه!

https://nakamology.ir/

link

#introduction

@Syntax_fa
👍14
مدحج:
همستر کوچیک شده ی نات کوینه

#fun

@Syntax_fa
😁38👍2👎1
solid-book-v1.0.5.pdf
3.4 MB
کتاب فارسی پنج اصل SOLID

سولید ( SOLID ) یک کلمه مخفف برای پنچ اصل اولیه طراحی شئ گرا است که رابرت سیسیل مارتین معروف به عمو باب ( uncle bob ) اون رو مطرح کرد.

این اصول زمانی که دست به دست هم میدن، کار گسترش یا اضافه کردن قابلیت های جدید به برنامه و نگهداری و دیباگ یک برنامه رو برای برنامه نویس ها آسان می کنند.

#book

@Syntax_fa
🔥14👍5
🔥یه مقاله تازه و داغ در مورد scale کردن بکند با حداقل منابع روی سرور

خیلی نکات جالب و مهمی رو بهش اشاره میکنه، از جمله اینکه لازمه observability pipeline رو قبل از هر چیزی راه اندازی کنید که بتونید بر اساس داده و اطلاعات تصمیم گیری کنید. این نکته خیلی مهمی هست که ما خیلی وقت ها فراموش می کنیم.
وقتی مشکل performanceی داریم اول باید در مورد مشکل مون داده و اطلاعات جمع آوری کنیم. بعدش بریم سراغ اینکه حالا چطور مشکل رو حل کنیم.

مقاله جذابیه که در مورد موارد مختلفی برای بهینه سازی صحبت میکنه از connection pooling گرفته تا ایندکس دیتابیس و goroutine throttling

Scaling Backend to 1M requests with just 2GB ram ⚡️

https://dev.to/rikenshah/scaling-backend-to-1m-requests-with-just-2gb-ram-4m0c

@gocasts

@Syntax_fa

#backend #golang
7🔥2
اگه همکار شما بودم. چیکار میکردید؟ 😂

فقط اونجا که گفتم امروز(دوشنبه) تسکارو مشخص میکنم. شنبه هفته بعد شروع کنیم

#fun

@Syntax_fa
😁18👍2
Fluent API
یک سبک برنامه‌نویسی است که در آن متدها به شکلی زنجیره‌ای (chaining) فراخوانی می‌شوند تا کد خواناتر و روان‌تر شود.

ویژگی‌های Fluent API
- خوانایی بالا: کدها به گونه‌ای نوشته می‌شوند که شبیه به جملات طبیعی هستند.
- پیکربندی زنجیره‌ای: متدها به صورت زنجیره‌ای فراخوانی می‌شوند که به کاهش پیچیدگی و افزایش خوانایی کد کمک می‌کند.
- استفاده آسان: با استفاده از این روش، توسعه‌دهندگان می‌توانند به راحتی و با کمترین پیچیدگی ممکن اشیا را پیکربندی کنند.

مثال کاربردی از Fluent API در Go

در این مثال، یک استراکت Car را با استفاده از Fluent API پیکربندی می‌کنیم:

package main

import (
"fmt"
)

type Car struct {
Model string
Color string
Year int
}

func main() {
car := Car{}.SetModel("pride").SetColor("white").SetYear(2015)
fmt.Printf("%+v", car)
}

func (car Car) SetModel(model string) Car {
car.Model = model
return car
}

func (car Car) SetColor(color string) Car {
car.Color = color
return car
}

func (car Car) SetYear(year int) Car {
car.Year = year
return car
}


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

#fluent_api

@Syntax_fa
👍8❤‍🔥1👎1
هزینه‌ی Raise کردن Exception و رویکرد متفاوت گولنگ

در اکثر زبان ها، استثناها (Exceptions) ابزار اصلی برای مدیریت خطاها هستند. برای مثال وقتی یک استثنا در Python رخ می‌دهد، سیستم اجرا (Runtime) باید کارهای زیر را انجام دهد:
1. ایجاد یک شیء استثنا: این شامل تخصیص حافظه و مقداردهی اولیه برای شیء استثنا است.
2. جمع‌آوری اطلاعات پشته: Python باید مسیر اجرای فعلی را بررسی کند و یک traceback ایجاد کند.
3. مدیریت جریان کنترل: سیستم اجرا باید به دنبال بلوک‌های try و except بگردد و جریان اجرای برنامه را به بلوک مناسب منتقل کند.

این عملیات‌ها، به‌ویژه جمع‌آوری اطلاعات پشته و مدیریت جریان کنترل، هزینه‌بر هستند. در نتیجه، raise کردن یک استثنا در Python می‌تواند تاثیر منفی قابل توجهی بر عملکرد برنامه داشته باشد، به خصوص اگر استثناها به صورت مکرر رخ دهند.

رویکرد GoLang برای مدیریت خطاها

در زبان برنامه‌نویسی Go، مدیریت خطاها به گونه‌ای متفاوت انجام می‌شود. به جای استفاده از استثناها، خطاها به عنوان مقادیر معمولی برگردانده می‌شوند. این رویکرد به دلایل زیر باعث افزایش عملکرد می‌شود:

1. عدم نیاز به مدیریت جریان کنترل پیچیده:
- در Go، خطاها به صورت مقادیر بازگشتی از توابع برگردانده می‌شوند. این رویکرد از مدیریت پیچیده جریان کنترل که در استثناها نیاز است، جلوگیری می‌کند. بنابراین، زمان و منابعی که برای جستجو و انتقال جریان کنترل در بلوک‌های try و catch صرف می‌شود، در Go وجود ندراد.

2. عدم نیاز به جمع‌آوری اطلاعات پشته:
- وقتی یک خطا به عنوان مقدار بازگشتی مدیریت می‌شود، نیازی به جمع‌آوری اطلاعات پشته و ایجاد traceback نیست. این باعث کاهش هزینه‌های محاسباتی و بهبود عملکرد می‌شود.

3. کاهش سربار حافظه:
- ایجاد استثناها معمولاً شامل تخصیص حافظه برای شیء استثنا و اطلاعات پشته است. در مقابل، بازگرداندن یک خطا به عنوان مقدار بازگشتی نیاز به تخصیص حافظه اضافی ندارد و سربار حافظه را کاهش می‌دهد.

مثال از مدیریت خطا در Go

package main

import (
"errors"
"fmt"
)

func divide(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}

func main() {
result, err := divide(10, 0)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Result:", result)
}


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

#exception

@Syntax_fa
👍6👎1👏1
جلوگیری از کد تکراری (Duplicate Code)

کد تکراری یا Duplicate Code به بخشی از کد گفته می‌شود که به‌طور مشابه یا یکسان در چندین نقطه از برنامه تکرار شده است. وجود کد تکراری نه تنها خوانایی کد را کاهش می‌دهد، بلکه باعث می‌شود مدیریت و نگهداری کد نیز پیچیده‌تر شود.

آقا / خانم این کارارو نکن:

1. کپی و پیست کردن کد: یکی از رایج‌ترین دلایل وجود کد تکراری، کپی و پیست کردن کد به جای استفاده از رویکردهای ماژولار و تابعی است.

2. عدم وجود طراحی مناسب: طراحی نادرست و عدم استفاده از اصول برنامه‌نویسی شیءگرا و الگوهای طراحی می‌تواند منجر به تکرار کد شود.

3. توسعه توسط تیم‌های مختلف: توسعه موازی توسط تیم‌های مختلف بدون هماهنگی مناسب نیز می‌تواند باعث ایجاد کد تکراری شود.

کد تکراری چه مشکلاتی بوجود میاره؟

1. افزایش پیچیدگی و کاهش خوانایی: کد تکراری باعث افزایش حجم کد و کاهش خوانایی آن می‌شود، که می‌تواند درک و نگهداری کد را دشوارتر کند.

2. افزایش احتمال خطا: در صورت نیاز به تغییر در بخشی از کد، باید همه نمونه‌های تکراری آن تغییر یابند که احتمال بروز خطا را افزایش می‌دهد.

3. کاهش کارایی توسعه: مدیریت کد تکراری زمان‌بر است و باعث کاهش کارایی فرآیند توسعه و نگهداری نرم‌افزار می‌شود.

روش‌های جلوگیری از کد تکراری:

1. استفاده از توابع و روش‌های ماژولار:
   - با تبدیل کد تکراری به توابع یا متدهای مستقل، می‌توان از تکرار کد جلوگیری کرد و از مزایای کد قابل استفاده مجدد بهره‌مند شد.

2. استفاده از اصول برنامه‌نویسی شیءگرا:
   - با استفاده از وراثت، ترکیب (composition) و سایر اصول برنامه‌نویسی شیءگرا می‌توان کد تکراری را کاهش داد.

3. استفاده از الگوهای طراحی (Design Patterns):
   - الگوهای طراحی مانند Adapter، Factory، Strategy و دیگر الگوها می‌توانند به کاهش کد تکراری کمک کنند.

4. استفاده از ابزارهای تشخیص کد تکراری:
   - ابزارهایی مانند SonarQube می‌توانند به شناسایی و حذف کد تکراری کمک کنند.

مثال:
حرفای ما ثابت شدست مثال نمیخواد که 😁

پ.ن:
تو آخرین شرکتی که کار می کردم، بزرگوار یکی از طرفداران duplicate code بود.
حتی اون تابعی که قبلا نوشته بودو دوباره استفاده نمیکرد. کپی پیست میکرد تا بغل دست کدی که ازش استفاده کرده باشه😐

#duplicate_code

@Syntax_fa
👍8😁1
ساختمان داده Deque (Double-Ended Queue)

Deque،
مخفف "Double-Ended Queue"، یک نوع ساختمان داده است که به‌صورت همزمان امکان اضافه و حذف عناصر را از هر دو انتها (ابتدا و انتها) فراهم می‌کند. این ویژگی Deque را به یک ابزار قدرتمند در بسیاری از الگوریتم‌ها و برنامه‌های کاربردی تبدیل کرده است.

ویژگی‌های Deque

1. دسترسی دو طرفه: امکان اضافه و حذف عناصر از (ابتدا و انتها) را فراهم می‌کند.

2. انعطاف‌پذیری: ترکیبی از ویژگی‌های پشته (stack) و صف (queue) را داراست.

3. پیچیدگی زمانی بهینه: عملیات افزودن و حذف در هر دو انتها دارای زمان اجرای O(1) است(اگر به لیست های پیوندی پیاده شود)

عملیات‌های اصلی در Deque:

1. افزودن به ابتدا (Add to Front):
   - عملیات: addFirst(element)
   - توضیح: این عملیات یک عنصر را به ابتدای Deque اضافه می‌کند.
  
2. افزودن به انتها (Add to Rear):
   - عملیات: addLast(element)
   - توضیح: این عملیات یک عنصر را به انتهای Deque اضافه می‌کند.
  
3. حذف از ابتدا (Remove from Front):
   - عملیات: removeFirst()
   - توضیح: این عملیات اولین عنصر را از Deque حذف می‌کند.
  
4. حذف از انتها (Remove from Rear):
   - عملیات: removeLast()
   - توضیح: این عملیات آخرین عنصر را از Deque حذف می‌کند.
  
5. دسترسی به اولین عنصر (Peek at Front):
   - عملیات: peekFirst()
   - توضیح: این عملیات اولین عنصر را بدون حذف از Deque برمی‌گرداند.
  
6. دسترسی به آخرین عنصر (Peek at Rear):
   - عملیات: peekLast()
   - توضیح: این عملیات آخرین عنصر را بدون حذف از Deque برمی‌گرداند.
  
7. بررسی خالی بودن (Check if Empty):
   - عملیات: isEmpty()
   - توضیح: این عملیات بررسی می‌کند که آیا Deque خالی است یا خیر.
  
8. بررسی اندازه (Check Size):
   - عملیات: size()
   - توضیح: این عملیات تعداد عناصر موجود در Deque را برمی‌گرداند.

پیاده‌سازی Deque

برای پیاده‌سازی Deque، چندین ساختار داده وجود دارند که می‌توانند به کار گرفته شوند، اما دو ساختار داده‌ای که معمولاً برای پیاده‌سازی Deque مناسب هستند عبارتند از:

1. لیست پیوندی دوطرفه (Doubly Linked List):
   - توضیح: لیست پیوندی دوطرفه دارای گره‌هایی است که هر گره شامل دو اشاره‌گر است: یکی به گره قبلی و دیگری به گره بعدی.
این ساختار داده امکان افزودن و حذف عناصر از هر دو انتها را با پیچیدگی زمانی O(1) فراهم می‌کند.
   - مزایا:
     - زمان اجرای بهینه برای عملیات افزودن و حذف.
     - انعطاف‌پذیری بالا.
   - معایب:
     - سربار حافظه به دلیل استفاده از اشاره‌گرها.
    
2. آرایه دایره‌ای (Circular Array):
   - توضیح: آرایه دایره‌ای یک آرایه ثابت است که انتهای آن به ابتدای آرایه پیوند داده شده است. این ساختار داده نیز امکان افزودن و حذف عناصر از هر دو انتها را با پیچیدگی زمانی O(1) فراهم می‌کند.
   - مزایا:
     - استفاده کارآمد از حافظه.
     - دسترسی سریع به عناصر.
   - معایب:
     - اندازه ثابت آرایه می‌تواند منجر به مشکلاتی در صورت نیاز به فضای بیشتر یا کمتر شود.
     - نیاز به مدیریت دقیق اندیس‌ها برای جلوگیری از سرریز (overflow) یا سربار (underflow).

تمرین:
مثال Deque رو تو زبانی که کار میکنید پیاده سازیش کنید و توی کامنت ارسال کنید.

#deque #data_structures

@Syntax_fa
👍7
This media is not supported in your browser
VIEW IN TELEGRAM
کاری که کروم با رم می کنه

#fun

@Syntax_fa
🤣20👍1
چند نکته در خصوص الگوریتم های بازگشتی

الگوریتم‌های Tail Recursion:

مفهوم Tail Recursion
الگوریتم‌های بازگشتی، تکنیکی برای حل مسائل پیچیده از طریق تجزیه آنها به زیرمسائل کوچکتر و حل آنها با استفاده از خود تابع بازگشتی هستند. یکی از انواع خاص این الگوریتم‌ها، Tail Recursion است. در tail recursion، فراخوانی بازگشتی به عنوان آخرین عملیات در تابع انجام می‌شود. به عبارت دیگر، هیچ عملیات دیگری بعد از فراخوانی بازگشتی انجام نمی‌شود. این ویژگی اجازه می‌دهد که حالت فعلی تابع دیگر نیازی به نگهداری در حافظه نداشته باشد.

به عنوان مثال، تابع بازگشتی برای پیدا کردن یک عدد در آرایه مرتب شده:
func SearchRecursive(array []int, x int) bool {
if len(array) == 1 {
return array[0] == x
}

middle := len(array) / 2
if array[middle] == x {
return true
}

if array[middle] < x && len(array) >= middle+1 {
return SearchRecursive(array[middle:], x)
} else {
return SearchRecursive(array[:middle], x)
}
}

نکته:
الگوریتم های بازگشت دمی رو ، میشه بصورت خطی نوشت.

بهینه‌سازی Tail Recursion توسط کامپایلرها
یکی از ویژگی‌های مهم بازگشت دم این است که بسیاری از کامپایلرها و مفسرهای زبان‌های برنامه‌نویسی می‌توانند این نوع بازگشت را بهینه‌سازی کنند. این بهینه‌سازی که به نام Tail Call Optimization (TCO) یا بهینه‌سازی فراخوانی دم شناخته می‌شود، به کامپایلر اجازه می‌دهد که فراخوانی‌های بازگشتی دم را به یک حلقه ساده تبدیل کند، در نتیجه نیاز به استفاده از پشته بازگشتی را حذف می‌کند.

در زبان‌های برنامه‌نویسی که از این بهینه‌سازی پشتیبانی می‌کنند، مانند Haskell، Scheme و برخی پیاده‌سازی‌های Python (مثل PyPy)، کامپایلر می‌تواند بازگشت دم را به یک حلقه for یا while تبدیل کند:

def factorial(n):
acc = 1
while n > 0:
acc *= n
n -= 1
return acc


این تبدیل باعث می‌شود که عملکرد و بهره‌وری برنامه بهبود یابد، زیرا دیگر نیازی به افزایش عمق پشته بازگشتی برای هر فراخوانی وجود ندارد.

محدودیت‌های Recursion Stack
در اکثر زبان‌های برنامه‌نویسی، هر بار که یک تابع بازگشتی فراخوانی می‌شود، یک فریم جدید به پشته بازگشتی اضافه می‌شود. پشته بازگشتی یا recursion stack مکانی است که اطلاعات مربوط به هر فراخوانی، شامل پارامترها، متغیرهای محلی و آدرس بازگشت، ذخیره می‌شود.

این پشته محدودیت‌های خاص خود را دارد:
1. محدودیت حافظه: هر پشته بازگشتی مقدار مشخصی از حافظه را مصرف می‌کند. در صورتی که عمق بازگشتی زیاد باشد، ممکن است برنامه با خطای Stack Overflow مواجه شود.
2. کاهش کارایی: هر فراخوانی بازگشتی نیاز به زمان اضافی برای مدیریت فریم‌های پشته دارد که می‌تواند منجر به کاهش کارایی شود.
3. پیچیدگی کد: مدیریت دستی بازگشت‌ها و پارامترها می‌تواند کد را پیچیده‌تر و مشکل‌تر برای فهم کند.

نکته:
اکثرا الگوریتمی که بصورت خطی بتونیم بنویسیم، نسبت به الگوریتم بازگشتی پرفورمنس بهتری داره.

#recursion

@Syntax_fa
🔥3👍2
فامیل و آشنا:
شنیدم برنامه نویسی و طراحی وب سایت بلدی

_ آره بلدم

یه ایده دارم بیا بزنش سودش نصف نصف میلیاردر میشیم

_ فرار

#fun

@Syntax_fa
🤣18
وضعیت هسته ها تو زبان های single thread

#fun

@Syntax_fa | @GoInsights
🤣24👍3😁1