{"algorithms-searches":{"Title":"عملیات جستجو","Description":"این بخش نمونه‌هایی ارائه می‌دهد که عملیات‌های جستجو را انجام می‌دهند.","Pages":[{"Title":"جستجوی دودویی","Content":"\n \u003ch2\u003eجستجوی دودویی\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/training/individual-on-demand/ultimate-go-bundle/\" target=\"_blank\"\u003eتماشای ویدئو\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003eنیاز به کمک مالی دارید؟ از \u003ca href=\"https://www.ardanlabs.com/scholarship/\" target=\"_blank\"\u003eفرم Scholarship\u003c/a\u003e ما استفاده کنید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n این برنامه نمونه یک تابع را پیاده‌سازی می‌کند که جستجوی دودویی تکراری را علیه مجموعه‌ای از اعداد انجام می‌دهد.\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003eاطلاعات بیشتر در \u003ca href=\"https://en.wikipedia.org/wiki/Binary_search_algorithm\" target=\"_blank\"\u003ehttps://en.wikipedia.org/wiki/Binary_search_algorithm\u003c/a\u003e\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n \u003cb\u003eدیاگرام\u003c/b\u003e\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eجستجوی دودویی مقدار هدف را با عنصر میانی آرایه مقایسه می‌کند.\nاگر مقدارها برابر نباشند، نیمی از آرایه که ممکن است مقدار هدف در آن نباشد\nحذف می‌شود و جستجو در نیمی باقیمانده ادامه پیدا می‌کند. دوباره عنصر میانی\nرا برای مقایسه با مقدار هدف انتخاب می‌کند و این عملیات تا زمانی که مقدار\nهدف پیدا شود ادامه می‌یابد. اگر جستجو با نیمی باقیمانده از آرایه به پایان\nبرسد و آن نیم خالی باشد، به این معناست که مقدار هدف در آرایه وجود ندارد.\n\n┌────┐\n│ 83 │ ◁── عدد هدف\n└────┘\n┌────┐┌────┐┌────┐┌────┐┌────┐\n│ 04 ││ 42 ││ 80 ││ 83 ││ 95 │ ◁── آرایه شروعی\n└────┘└────┘└────┘└────┘└────┘\n ┌────┐ ◁── مقدار میانی\n │ 80 │ ◁── عدد هدف بزرگتر است\n └────┘\n ┌────┐┌────┐\n │ 83 ││ 95 │ ◁── این نیمه را جستجو کنید\n └────┘└────┘\n ┌────┐\n │ 83 │ ◁──مقدار میانی\n └────┘\n ┌────┐\n │ 83 │ ◁── هدف پیدا شد / اندیس 3\n └────┘\u003c/pre\u003e\n \n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n","Files":[{"Name":"binary_iterative.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This sample program shows you how to write a binary search using an\n// iterative approach.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n)\n\nfunc main() {\n\tnumbers := []int{4, 42, 80, 83, 121, 137, 169, 182, 185, 180}\n\tfind := rand.Intn(10)\n\n\tfmt.Println(\"Numbers:\", numbers)\n\tfmt.Println(\"Find. :\", numbers[find])\n\n\tidx, err := binarySearchIterative(numbers, numbers[find])\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\treturn\n\t}\n\n\tfmt.Println(\"Found : Idx\", idx)\n}\n\nfunc binarySearchIterative(sortedList []int, target int) (int, error) {\n\tvar leftIdx int\n\trightIdx := len(sortedList) - 1\n\n\t// Loop until we find the target or searched the list.\n\tfor leftIdx \u003c= rightIdx {\n\n\t\t// Calculate the middle index of the list.\n\t\tmid := (leftIdx + rightIdx) / 2\n\n\t\t// Capture the value to check.\n\t\tvalue := sortedList[mid]\n\n\t\tswitch {\n\n\t\t// Check if we found the target.\n\t\tcase value == target:\n\t\t\treturn mid, nil\n\n\t\t// If the value is greater than the target, cut the list\n\t\t// by moving the rightIdx into the list.\n\t\tcase value \u003e target:\n\t\t\trightIdx = mid - 1\n\n\t\t// If the value is less than the target, cut the list\n\t\t// by moving the leftIdx into the list.\n\t\tcase value \u003c target:\n\t\t\tleftIdx = mid + 1\n\t\t}\n\t}\n\n\treturn -1, fmt.Errorf(\"target not found\")\n}\n","Hash":"PyHQWydyvZRnQx07p5DWsx7qj7s="},{"Name":"binary_recursive.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This sample program shows you how to write a binary search using a\n// recursive approach.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n)\n\nfunc main() {\n\tnumbers := []int{4, 42, 80, 83, 121, 137, 169, 182, 185, 180}\n\tfind := rand.Intn(10)\n\n\tfmt.Println(\"Numbers:\", numbers)\n\tfmt.Println(\"Find. :\", numbers[find])\n\n\tidx, err := binarySearchRecursive(numbers, numbers[find], 0, len(numbers))\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\treturn\n\t}\n\n\tfmt.Println(\"Found : Idx\", idx)\n}\n\nfunc binarySearchRecursive(sortedList []int, target int, leftIdx int, rightIdx int) (int, error) {\n\n\t// Calculate the middle index of the list.\n\tmidIdx := (leftIdx + rightIdx) / 2\n\n\t// Check until leftIdx is smaller or equal with rightIdx.\n\tif leftIdx \u003c= rightIdx {\n\n\t\tswitch {\n\n\t\t// Check if we found the target.\n\t\tcase sortedList[midIdx] == target:\n\t\t\treturn midIdx, nil\n\n\t\t// If the value is greater than the target, cut the list\n\t\t// by moving the rightIdx into the list.\n\t\tcase sortedList[midIdx] \u003e target:\n\t\t\treturn binarySearchRecursive(sortedList, target, leftIdx, midIdx-1)\n\n\t\t// If the value is less than the target, cut the list\n\t\t// by moving the leftIdx into the list.\n\t\tcase sortedList[midIdx] \u003c target:\n\t\t\treturn binarySearchRecursive(sortedList, target, midIdx+1, rightIdx)\n\t\t}\n\t}\n\n\treturn -1, fmt.Errorf(\"target not found\")\n}\n\nfunc generateList(totalNumbers int) []int {\n\tnumbers := make([]int, totalNumbers)\n\n\tfor i := 0; i \u003c totalNumbers; i++ {\n\t\tnumbers[i] = rand.Intn(totalNumbers * 20)\n\t}\n\n\treturn numbers\n}\n","Hash":"qLTFcJZ6ASJglaM6yUbqVLXl10I="}]}]} ,"composition-assertions":{"Title":"نوع تبدیل و تأییدها","Description":"یاد بگیرید که تبدیل‌ها و تأییدها نوع چگونه کار می‌کنند.","Pages":[{"Title":"نوع تبدیل و تأییدها","Content":"\n \u003ch2\u003eنوع تبدیل و تأییدها\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/training/individual-on-demand/ultimate-go-bundle/\" target=\"_blank\"\u003eتماشای ویدئو\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003eنیاز به کمک مالی دارید؟ از \u003ca href=\"https://www.ardanlabs.com/scholarship/\" target=\"_blank\"\u003eفرم Scholarship\u003c/a\u003e ما استفاده کنید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n تبدیل نوع به دادهٔ یک نوع، امکان می‌دهد تا به نوع دیگری تبدیل شود. تأیید نوع به شما امکان می‌دهد تا بپرسید که آیا یک مقدار از نوع دادهٔ داده شده درون یک رابطه ذخیره شده است یا خیر.\n \u003c/p\u003e\n \n\n \u003ch2\u003eبررسی کد\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e1:\u003c/b\u003e تبدیل‌های اینترفیس\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e2:\u003c/b\u003e تأییدهای نوع زمان اجرا\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e3:\u003c/b\u003e تغییرات رفتار\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n \n \u003cp\u003e\n **تبدیل‌های ضمنی رابط\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n همانطور که در مثال گذشته دیدید، یک مقدار رابط از یک نوع می‌تواند برای نوع دیگری از رابطه ارسال شود، اگر مقدار محتوای ذخیره شده درون رابط هر دو رفتار را پیاده‌سازی کند. این می‌تواند به عنوان یک تبدیل ضمنی رابط در نظر گرفته شود، اما بهتر است دربارهٔ اینکه چگونه دادهٔ محتوا در حالت مجزا از طریق رابطها جابجا می‌شود، فکر کنیم.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype Mover interface {\n Move()\n}\n\ntype Locker interface {\n Lock()\n Unlock()\n}\n\ntype MoveLocker interface {\n Mover\n Locker\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n با توجه به این سه رابطه، جایی که MoveLocker ترکیبی از Mover و Locker است.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype bike struct{}\n\nfunc (bike) Move() {\n fmt.Println(\u0026#34;Moving the bike\u0026#34;)\n}\n\nfunc (bike) Lock() {\n fmt.Println(\u0026#34;Locking the bike\u0026#34;)\n}\n\nfunc (bike) Unlock() {\n fmt.Println(\u0026#34;Unlocking the bike\u0026#34;)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n و با توجه به این نوع محسوب‌شده bike که همه سه رابطه را پیاده‌سازی می‌کند، شما چه کارهایی می‌توانید انجام دهید؟\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003evar ml MoveLocker\nvar m Mover\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n شما می‌توانید یک مقدار از نوع MoveLocker و Mover را به حالت مقدار صفر خود بسازید. این مقادیر رابطه ای هستند که واقعاً بی‌ارزش هستند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eml = bike{}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n سپس شما می‌توانید یک مقدار از نوع bike را به حالت مقدار صفر خود بسازید و یک کپی از آن را به متغیر MoveLocker با نام ml اختصاص دهید. این امکان وجود دارد زیرا یک bike همه سه رفتار را پیاده‌سازی می‌کند و کامپایلر می‌تواند ببیند که پیاده‌سازی موجود است.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003em = ml\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n سپس شما می‌توانید متغیر MoveLocker با نام ml را به متغیر Mover با نام m اختصاص دهید. این امکان وجود دارد زیرا من مقدار رابطه ml را نمی‌دهم، بلکه مقدار محتوای واقعی که در داخل ml ذخیره شده است و یک مقدار bike است را اختصاص می‌دهم. کامپایلر می‌داند که هر مقدار واقعی که در داخل ml ذخیره شده است باید همچنین رابطه Mover را پیاده‌سازی کند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n با این حال، این اختصاص معتبر نیست.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eml = m\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eنمی‌توانید از m (نوع Mover) به عنوان نوع MoveLocker در اختصاص استفاده کنید:\n Mover پیاده‌سازی MoveLocker را انجام نمی‌دهد (متد Lock وجود ندارد)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n نمی‌توانید متغیر Mover با نام m را به متغیر MoveLocker با نام ml اختصاص دهید زیرا کامپایلر تنها می‌تواند تضمین کند که مقدار واقعی که در داخل m ذخیره شده است، می‌داند چگونه حرکت کند. در زمان کامپایل، این مشخص نیست که آیا مقدار واقعی همچنین می‌داند چگونه قفل کند و باز کند.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eمکانیک تأیید نوع\u003c/h2\u003e\n \n \n \u003cp\u003e\n یک تأیید نوع به شما اجازه می‌دهد تا در زمان اجرا سوالی را مطرح کنید، آیا یک مقدار از نوع داده شده درون یک رابطه ذخیره شده است. شما این را با استفاده از نحو m.(bike) مشاهده می‌کنید. \n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eb := m.(bike)\nml = b\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در این حالت، شما در زمان اجرای کد سؤال می‌کنید که آیا یک مقدار bike درون مقدار m ذخیره شده است یا خیر. اگر چنین باشد، متغیر b یک کپی از مقدار bike ذخیره شده را دریافت می‌کند. سپس این کپی می‌تواند درون متغیر رابطه ml کپی شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n اگر چیزی درون مقدار رابطه ذخیره نشده باشد، آنگاه برنامه به خطا می‌افتد. اگر شما مطمئن هستید که باید یک مقدار bike درون ذخیره شده باشد، این رفتار مطلوب است. اما اگر احتمال وجود نداشتن یک مقدار bike وجود داشته باشد و این معتبر باشد، آنگاه شما نیاز به فرم دوم تأیید نوع دارید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eb, ok := m.(bike)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در این فرم، اگر مقدار ok برابر با true باشد، یک مقدار bike درون رابطه ذخیره شده است. اگر مقدار ok برابر با false باشد، بدین معنی است که مقدار bike ذخیره نشده است و برنامه به خطا نمی‌افتد. با این حال، متغیر b همچنان از نوع bike است، اما به حالت مقدار صفر خود تنظیم می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc main() {\n mvs := []fmt.Stringer{\n Car{},\n Cloud{},\n }\n\n for i := 0; i \u0026lt; 10; i\u0026#43;\u0026#43; {\n rn := rand.Intn(2)\n\n if v, is := mvs[rn].(Cloud); is {\n fmt.Println(\u0026#34;Got Lucky:\u0026#34;, v)\n continue\n }\n\n fmt.Println(\u0026#34;Got Unlucky\u0026#34;)\n }\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n با فرض اینکه برنامه دو نوع به نام Car و Cloud را تعریف کند که هر دو آنها رابطه fmt.Stringer را پیاده‌سازی می‌کنند، شما می‌توانید یک مجموعه بسازید که به شما امکان ذخیره کردن مقداری از نوع Car و Cloud را می‌دهد. سپس به طور تصادفی و 10 بار، یک عدد از 0 تا 1 را انتخاب کرده و با استفاده از تأیید نوع، می‌توانید ببینید که آیا مقدار در آن شاخه تصادفی شامل یک مقدار Cloud است یا خیر. از آنجایی که احتمال دارد که این مقدار از نوع Cloud نباشد، فرم دوم تأیید نوع در اینجا بسیار حیاتی است.\n \u003c/p\u003e\n \n\n\n","Files":[{"Name":"example1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program demonstrating when implicit interface conversions\n// are provided by the compiler.\npackage main\n\nimport \"fmt\"\n\n// Mover provides support for moving things.\ntype Mover interface {\n\tMove()\n}\n\n// Locker provides support for locking and unlocking things.\ntype Locker interface {\n\tLock()\n\tUnlock()\n}\n\n// MoveLocker provides support for moving and locking things.\ntype MoveLocker interface {\n\tMover\n\tLocker\n}\n\n// bike represents a concrete type for the example.\ntype bike struct{}\n\n// Move can change the position of a bike.\nfunc (bike) Move() {\n\tfmt.Println(\"Moving the bike\")\n}\n\n// Lock prevents a bike from moving.\nfunc (bike) Lock() {\n\tfmt.Println(\"Locking the bike\")\n}\n\n// Unlock allows a bike to be moved.\nfunc (bike) Unlock() {\n\tfmt.Println(\"Unlocking the bike\")\n}\n\nfunc main() {\n\n\t// Declare variables of the MoveLocker and Mover interfaces set to their\n\t// zero value.\n\tvar ml MoveLocker\n\tvar m Mover\n\n\t// Create a value of type bike and assign the value to the MoveLocker\n\t// interface value.\n\tml = bike{}\n\n\t// An interface value of type MoveLocker can be implicitly converted into\n\t// a value of type Mover. They both declare a method named move.\n\tm = ml\n\n\t// prog.go:68: cannot use m (type Mover) as type MoveLocker in assignment:\n\t//\t Mover does not implement MoveLocker (missing Lock method)\n\tml = m\n\n\t// Interface type Mover does not declare methods named lock and unlock.\n\t// Therefore, the compiler can't perform an implicit conversion to assign\n\t// a value of interface type Mover to an interface value of type MoveLocker.\n\t// It is irrelevant that the concrete type value of type bike that is stored\n\t// inside of the Mover interface value implements the MoveLocker interface.\n\n\t// We can perform a type assertion at runtime to support the assignment.\n\n\t// Perform a type assertion against the Mover interface value to access\n\t// a COPY of the concrete type value of type bike that was stored inside\n\t// of it. Then assign the COPY of the concrete type to the MoveLocker\n\t// interface.\n\tb := m.(bike)\n\tml = b\n\n\t// It's important to note that the type assertion syntax provides a way\n\t// to state what type of value is stored inside the interface. This is\n\t// more powerful from a language and readability standpoint, than using\n\t// a casting syntax, like in other languages.\n}\n","Hash":"2WQGiMEGXUUlVP4K9qheLDu0nQc="},{"Name":"example2.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program demonstrating that type assertions are a runtime and\n// not compile time construct.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n)\n\n// car represents something you drive.\ntype car struct{}\n\n// String implements the fmt.Stringer interface.\nfunc (car) String() string {\n\treturn \"Vroom!\"\n}\n\n// cloud represents somewhere you store information.\ntype cloud struct{}\n\n// String implements the fmt.Stringer interface.\nfunc (cloud) String() string {\n\treturn \"Big Data!\"\n}\n\nfunc main() {\n\n\t// Create a slice of the Stringer interface values.\n\tmvs := []fmt.Stringer{\n\t\tcar{},\n\t\tcloud{},\n\t}\n\n\t// Let's run this experiment ten times.\n\tfor i := 0; i \u003c 10; i++ {\n\n\t\t// Choose a random number from 0 to 1.\n\t\trn := rand.Intn(2)\n\n\t\t// Perform a type assertion that we have a concrete type\n\t\t// of cloud in the interface value we randomly chose.\n\t\tif v, is := mvs[rn].(cloud); is {\n\t\t\tfmt.Println(\"Got Lucky:\", v)\n\t\t\tcontinue\n\t\t}\n\n\t\tfmt.Println(\"Got Unlucky\")\n\t}\n}\n","Hash":"ue/tsEsz3OfGbss7yERoYMKFcqY="},{"Name":"example3.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how method sets can affect behavior.\npackage main\n\nimport \"fmt\"\n\n// user defines a user in the system.\ntype user struct {\n\tname string\n\temail string\n}\n\n// String implements the fmt.Stringer interface.\nfunc (u *user) String() string {\n\treturn fmt.Sprintf(\"My name is %q and my email is %q\", u.name, u.email)\n}\n\nfunc main() {\n\n\t// Create a value of type user.\n\tu := user{\n\t\tname: \"Bill\",\n\t\temail: \"bill@ardanlabs.com\",\n\t}\n\n\t// Display the values.\n\tfmt.Println(u)\n\tfmt.Println(\u0026u)\n}\n","Hash":"6KF4CjCIcjL2H4fc39Bav/ow2l8="}]}]} ,"data_race":{"Title":"تداخل داده‌ها (Data Races)","Description":"تداخل داده، زمانی رخ می‌دهد که دو یا بیشتر گوروتین سعی در خواندن و نوشتن به منبع یکسان در همان زمان داشته باشند.","Pages":[{"Title":"Data Races","Content":"\n \u003ch2\u003eData Races\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/training/individual-on-demand/ultimate-go-bundle/\" target=\"_blank\"\u003eتماشای ویدئو\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003eنیاز به کمک مالی دارید؟ از \u003ca href=\"https://www.ardanlabs.com/scholarship/\" target=\"_blank\"\u003eفرم Scholarship\u003c/a\u003e ما استفاده کنید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n یک تداخل داده وقتی رخ می‌دهد زمانی که دو یا بیشتر گوروتین تلاش می‌کنند که به همان مکان حافظه دسترسی پیدا کنند، در حالی که حداقل یک گوروتین عملیات نوشتن انجام می‌دهد. زمانی که این اتفاق می‌افتد، امکان پیش‌بینی نتیجه وجود ندارد. این نوع اشکالات سخت به دلیل این هستند که مشکلاتی ایجاد می‌کنند که همیشه به صورت تصادفی ظاهر می‌شوند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n این حدود 8 دقیقه از سخنرانی اسکات مایرز بسیار عالی است که می‌توانید در اینجا گوش کنید: [لینک سخنرانی](insert_link_here)\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u003ca href=\"https://youtu.be/WDIkqP4JbkE?t=1809\" target=\"_blank\"\u003eCPU Caches and Why You Care 30:09-38:30\u003c/a\u003e\n \u003c/p\u003e\n \n\n \u003ch2\u003eبررسی کد\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e1:\u003c/b\u003e Data Race\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e2:\u003c/b\u003e Atomic افزایش‌های\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e3:\u003c/b\u003e Mutex\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e4:\u003c/b\u003e خواندن/نوشتن Mutex\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e5:\u003c/b\u003e Map Data Race\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e6:\u003c/b\u003e Interface براساس Race Condition\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n \u003ch2\u003eمثال تداخل داده\u003c/h2\u003e\n \n \n \u003cp\u003e\n این یک مثال عالی از تداخل داده است و نشان می‌دهد که چگونه می‌توانند سال‌ها پنهان بمانند و در نهایت در زمان‌های عجیب ظاهر شوند و به فساد داده منجر شوند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003evar counter int\n\nfunc main() {\n const grs = 2\n\n var wg sync.WaitGroup\n wg.Add(grs)\n\n for g := 0; g \u0026lt; grs; g\u0026#43;\u0026#43; {\n go func() {\n for i := 0; i \u0026lt; 2; i\u0026#43;\u0026#43; {\n value := counter\n value\u0026#43;\u0026#43;\n counter = value\n }\n wg.Done()\n }()\n }\n\n wg.Wait()\n fmt.Println(\u0026#34;Counter:\u0026#34;, counter)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این برنامه دو گوروتین ایجاد می‌کند که هرکدام به یک متغیر صحیح مشترک دسترسی پیدا می‌کنند و متغیر را دوبار افزایش می‌دهند. گوروتین عملیات خواندن، اصلاح و نوشتن را در مقابل وضعیت مشترک به صورت دستی انجام می‌دهد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003evar counter int\n\nfunc main() {\n . . .\n\n go func() {\n for i := 0; i \u0026lt; 2; i\u0026#43;\u0026#43; {\n value := counter\n value\u0026#43;\u0026#43;\n counter = value\n }\n wg.Done()\n }()\n\n . . .\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n می‌توانید دسترسی به وضعیت مشترک را در داخل حلقه for ببینید. هنگامی که این برنامه را بسازید و اجرا کنید، هر بار جواب درستی به مقدار ۴ دریافت می‌کنید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e$ ./example1\nFinal Counter: 4\n\n$ ./example1\nFinal Counter: 4\n\n$ ./example1\nFinal Counter: 4\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n چگونه این کار می‌کند؟\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eG1 Shared State: 0 G2\n----------------------------------------------------------------------------\nRead: 0\nModify: 1\nWrite: 1 Shared State: 1\nContext Switch \n Read: 1\n Modify: 2\n Shared State: 2 Write: 2\n Context Switch \nRead: 2\nModify: 3\nWrite: 3 Shared State: 3\nTerminate\n Read: 3\n Modify: 4\n Shared State: 4 Write: 4\n Terminate\n----------------------------------------------------------------------------\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n عملیات‌های خواندن، اصلاح و نوشتن بدون وقفه انجام می‌شوند. فقط به این دلیل که جواب صحیحی دریافت می‌کنم، به این معنا نیست که مشکلی وجود ندارد. اگر یک دستور log به وسط عملیات خواندن، اصلاح و نوشتن اضافه کنید، چه اتفاقی می‌افتد؟\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003evar counter int\n\nfunc main() {\n . . .\n\n go func() {\n for i := 0; i \u0026lt; 2; i\u0026#43;\u0026#43; {\n value := counter\n value\u0026#43;\u0026#43;\n log.Println(\u0026#34;logging\u0026#34;) \u0026lt;-- Add Logging Here\n counter = value\n }\n wg.Done()\n }()\n\n . . .\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n اگر این برنامه را اجرا کنید، دیگر نتیجه یکسانی که 4 بود، نخواهید گرفت. حالا جوابی به مقدار 2 دریافت می‌کنید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e$ ./example1\nFinal Counter: 2\n\n$ ./example1\nFinal Counter: 2\n\n$ ./example1\nFinal Counter: 2\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n چه اتفاقی می‌افتد؟ شما به یک باگ تداخل داده برخورد کرده‌اید که قبلاً وجود داشت، اما رخ نمی‌داد. فراخوانی log در حال حاضر باعث می‌شود که برنامه‌ای بین دو گوروتین در زمان نامناسب تغییر کانتکست دهد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eG1 Shared State: 0 G2\n----------------------------------------------------------------------------\nRead: 0\nModify: 1\nContext Switch\n Read: 0\n Modify: 1\n Context Switch \nWrite: 1 Shared State: 1\nRead: 1\nModify: 2\nContext Switch\n Shared State: 1 Write: 1\n Read: 1\n Modify: 2\n Context Switch \nWrite: 2 Shared State: 2\nTerminate\n Shared State: 2 Write: 2\n Terminate\n----------------------------------------------------------------------------\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n بعد از عملیات اصلاح، یک تغییر کانتکست انجام می‌شود. سه عملیات دیگر دیگر به صورت پیوسته انجام نمی‌شوند و گوروتین 2 در پایان عملیات نوشتن، مقدار محلی اش را به اشتباه دارای مقدار نادرست می‌کند. شما خوش شانس هستید که این موضوع هر بار رخ می‌دهد و می‌توانید آن را مشاهده کنید. اما به طور معمول یک تداخل داده مانند این به صورت \u0026#34;تصادفی\u0026#34; رخ می‌دهد و تا زمانی که خیلی دیر نشده باشد، امکان شناختن آن وجود ندارد. خوشبختانه، Go یک تشخیص‌دهنده تداخل داده دارد که به شما کمک می‌کند تداخل‌های داده را پیدا کنید.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eتشخیص تداخل (Race Detection)\u003c/h2\u003e\n \n \n \u003cp\u003e\n چندین روش برای فعال‌سازی تشخیص تداخل وجود دارد. شما می‌توانید از آن با دستور run، build و test استفاده کنید. اگر از آن با دستور build استفاده می‌کنید، باید به یاد داشته باشید که برنامه را اجرا کنید. آنها می‌گویند که یک باینری ابزاری می‌تواند عملکرد برنامه‌ام را تا حدود 20٪ کاهش دهد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e$ go build -race\n$ ./example1\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n پرچم -race به شما این امکان را می‌دهد که برنامه را با تشخیص‌دهنده تداخل داده ابزاری کنید. احتمالاً بیشتر از آن با \u0026#34;go test\u0026#34; استفاده خواهید کرد، اما در این مثال شما باید باینری را ابزاری کنید و سپس آن را اجرا کنید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e2021/02/01 17:30:52 logging\n2021/02/01 17:30:52 logging\n2021/02/01 17:30:52 logging\n==================\nWARNING: DATA RACE\nWrite at 0x000001278d88 by goroutine 8:\nmain.main.func1()\n /data_race/example1/example1.go:41 \u0026#43;0xa6\n\nPrevious read at 0x000001278d88 by goroutine 7:\nmain.main.func1()\n /data_race/example1/example1.go:38 \u0026#43;0x4a\n\nGoroutine 8 (running) created at:\nmain.main()\n /data_race/example1/example1.go:36 \u0026#43;0xaf\n\nGoroutine 7 (finished) created at:\nmain.main()\n /data_race/example1/example1.go:36 \u0026#43;0xaf\n==================\n2021/02/01 17:30:52 logging\nFinal Counter: 2\nFound 1 data race(s)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n می‌توانید ببینید که هنگام اجرای برنامه یک تداخل تشخیص داده شد. این اتفاق بدون در نظر گرفتن دستور log هم رخ می‌دهد. وقتی یک تداخل تشخیص داده می‌شود، برنامه خطا می‌دهد و این ردیابی را ارائه می‌دهد. ردیابی نشان می‌دهد کجا دسترسی بدون هماهنگی به همان وضعیت مشترک انجام شده است و حداقل یک دسترسی به نوشتن بوده است.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n در این ردیابی، یک گوروتین در خط 41 یک عملیات نوشتن را در آدرس 0x000001278d88 انجام داد و در همان آدرس، یک دسترسی بدون هماهنگی توسط یک گوروتین دیگر در خط 38 صورت گرفت. هر دو گوروتین در خط 36 ایجاد شدند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e36 go func() {\n37 for i := 0; i \u0026lt; 2; i\u0026#43;\u0026#43; {\n38 value := counter\n39 value\u0026#43;\u0026#43;\n40 log.Println(\u0026#34;logging\u0026#34;)\n41 counter = value\n42 }\n43 wg.Done()\n44 }()\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n می‌توانید به وضوح دسترسی بدون هماهنگی به خواندن و نوشتن را ببینید. به عنوان یک نکته جانبی، عملیات پلاس‌پلاس در خط 39 نیز تداخل داده خواهد بود اگر کد به متغیر شمارنده دسترسی داشته باشد. عملیات پلاس‌پلاس در واقعیت یک عملیات خواندن، اصلاح و نوشتن در زیرین است و سیستم عامل می‌تواند به راحتی در وسط آن تغییر کانتکست دهد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n پس چگونه می‌توانید کد را اصلاح کنید تا مطمئن شوید که تداخل داده را حذف کرده‌اید؟ دو ابزار می‌توانید استفاده کنید، دستورات اتمیک و میوتکس (Mutex).\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eاتمیک (Atomics)\u003c/h2\u003e\n \n \n \u003cp\u003e\n اتمیک‌ها همگام‌سازی را در سطح سخت‌افزار ارائه می‌دهند. به همین دلیل، تا کلمات و نیمه‌کلمات داده محدود می‌شود. بنابراین، برای شمارنده‌ها یا مکانیزم‌های سریع تعویض عالی هستند. API‌های WaitGroup از اتمیک‌ها استفاده می‌کنند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n برای اعمال اتمیک‌ها به کد، چه تغییراتی نیاز دارید؟\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003evar counter int32 \u0026lt;-- CHANGED\n\nfunc main() {\n const grs = 2\n\n var wg sync.WaitGroup\n wg.Add(grs)\n\n for g := 0; g \u0026lt; grs; g\u0026#43;\u0026#43; {\n go func() {\n for i := 0; i \u0026lt; 2; i\u0026#43;\u0026#43; {\n atomic.AddInt32(\u0026amp;counter, 1) \u0026lt;-- CHANGED\n }\n wg.Done()\n }()\n }\n\n wg.Wait()\n fmt.Println(\u0026#34;Counter:\u0026#34;, counter)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n شما فقط باید چند کار انجام دهید. اولاً، متغیر شمارنده را به یک عدد صحیح مبتنی بر دقت تغییر دهید. می‌توانید این تغییر را در بالای لیست کد مشاهده کنید. توابع اتمیک فقط با اعداد صحیح مبتنی بر دقت کار می‌کنند. دوماً، کد خواندن، اصلاح و نوشتن دستی را برای یک فراخوانی از atomic.AddInt32 حذف کنید. این یک فراخوانی همه‌چیز را پوشش می‌دهد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n تمام توابع مرتبط با بسته اتمیک آدرس وضعیت مشترک را برای همگام‌سازی می‌گیرند. همگام‌سازی فقط در سطح آدرس اتفاق می‌افتد. بنابراین، گوروتین‌های مختلفی که توابع یکسانی را فراخوانی می‌کنند، اما در آدرس‌های مختلف، همگام‌سازی نخواهند شد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n API اتمیک‌ها به شکل زیر است:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc AddInt32(addr *int32, delta int32) (new int32)\nfunc AddInt64(addr *int64, delta int64) (new int64)\nfunc AddUint32(addr *uint32, delta uint32) (new uint32)\nfunc AddUint64(addr *uint64, delta uint64) (new uint64)\nfunc AddUintptr(addr *uintptr, delta uintptr) (new uintptr)\n\nfunc CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)\nfunc CompareAndSwapInt64(addr *int64, old, new int64) (swapped bool)\nfunc CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool)\nfunc CompareAndSwapUint32(addr *uint32, old, new uint32) (swapped bool)\nfunc CompareAndSwapUint64(addr *uint64, old, new uint64) (swapped bool)\nfunc CompareAndSwapUintptr(addr *uintptr, old, new uintptr) (swapped bool)\n\nfunc LoadInt32(addr *int32) (val int32)\nfunc LoadInt64(addr *int64) (val int64)\nfunc LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer)\nfunc LoadUint32(addr *uint32) (val uint32)\nfunc LoadUint64(addr *uint64) (val uint64)\nfunc LoadUintptr(addr *uintptr) (val uintptr)\n\nfunc StoreInt32(addr *int32, val int32)\nfunc StoreInt64(addr *int64, val int64)\nfunc StorePointer(addr *unsafe.Pointer, val unsafe.Pointer)\nfunc StoreUint32(addr *uint32, val uint32)\nfunc StoreUint64(addr *uint64, val uint64)\nfunc StoreUintptr(addr *uintptr, val uintptr)\n\nfunc SwapInt32(addr *int32, new int32) (old int32)\nfunc SwapInt64(addr *int64, new int64) (old int64)\nfunc SwapPointer(addr *unsafe.Pointer, new unsafe.Pointer) (old unsafe.Pointer)\nfunc SwapUint32(addr *uint32, new uint32) (old uint32)\nfunc SwapUint64(addr *uint64, new uint64) (old uint64)\nfunc SwapUintptr(addr *uintptr, new uintptr) (old uintptr)\n\ntype Value\n func (v *Value) Load() (x interface{})\n func (v *Value) Store(x interface{})\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n می‌بینید که پارامتر اول همیشه آدرس یک عدد صحیح مبتنی بر دقت یا اشاره‌گر است. همچنین یک نوع به نام \u0026#34;Value\u0026#34; وجود دارد که یک مقدار همگام با یک API کوچک فراهم می‌کند.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eمیوتکس‌ها (Mutexes)\u003c/h2\u003e\n \n \n \u003cp\u003e\n اگر می‌خواستید سه خط کدی که داشتید را حفظ کنید، اتمیک‌ها کار نخواهند کرد. در این صورت، نیاز دارید به یک میوتکس. میوتکس به من اجازه می‌دهد یک گروه از کد را به‌طوری بسته‌بندی کنم که تنها یک گوروتین در هر زمان می‌تواند آن کد را اجرا کند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003evar counter int\n\nfunc main() {\n const grs = 2\n\n var wg sync.WaitGroup\n wg.Add(grs)\n\n var mu sync.Mutex \u0026lt;-- CHANGED\n\n for g := 0; g \u0026lt; grs; g\u0026#43;\u0026#43; {\n go func() {\n for i := 0; i \u0026lt; 2; i\u0026#43;\u0026#43; {\n mu.Lock() \u0026lt;-- CHANGED\n {\n value := counter\n value\u0026#43;\u0026#43;\n counter = value\n }\n mu.Unlock() \u0026lt;-- CHANGED\n }\n wg.Done()\n }()\n }\n\n wg.Wait()\n fmt.Println(\u0026#34;Counter:\u0026#34;, counter)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n تغییراتی متعدد در این کد نسبت به نسخه اصلی وجود دارد. شما ساختار متغیر mu را به عنوان یک میوتکس با مقدار صفر آن اضافه کردید. سپس درون حلقه for، فراخوانی‌های Lock و Unlock را با یک بلوک کد مصنوعی اضافه کردید. درون بلوک کد، کدی که نیاز به همگام‌سازی دارد، قرار دارد. بلوک کد برای خوانایی استفاده می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n با این کد در مکان، برنامه‌ریز فقط به یک گوروتین اجازه می‌دهد که وارد بلوک کد شود. مهم است که بفهمید که یک میوتکس یک صف نیست. گوروتین اولی که Lock را فراخوانی می‌کند، لزوماً گوروتین اولی نیست که قفل را می‌گیرد. البته یک الگوریتم مبتنی بر عدالت وجود دارد، اما این کار به عمد انجام می‌شود تا افراد میوتکس را به عنوان صف استفاده نکنند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n مهم است که به یاد داشته باشید که Lock فشار پشتیبانی ایجاد می‌کند، بنابراین هر چه زمان بیشتری از Lock به Unlock طول می‌کشد، فرصت بیشتری برای گوروتین‌ها به انتظار نوبت‌شان رفتن وجود دارد. اگر فراموش کنید Unlock را فراخوانی کنید، آن‌همه گوروتین‌های در انتظار به وضعیت بن‌بست می‌روند. به همین دلیل حائز اهمیت است که فراخوانی Lock و Unlock در همان تابع انجام شود. مطمئن شوید که حداقل همگام‌سازی مورد نیاز را در بلوک کد انجام می‌دهید، اما حداقل.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n این کد بسیار بد است که در آن کسی سعی دارد به سرعت وارد و خارج از Lock شود تا واقعاً همگام‌سازی را از دست دهد و تشخیص‌دهنده تداخل حتی نتواند مشکل را کشف کند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003evar counter int\n\nfunc main() {\n const grs = 2\n\n var wg sync.WaitGroup\n wg.Add(grs)\n\n var mu sync.Mutex\n\n for g := 0; g \u0026lt; grs; g\u0026#43;\u0026#43; {\n go func() {\n for i := 0; i \u0026lt; 2; i\u0026#43;\u0026#43; {\n var value int\n mu.Lock() \u0026lt;-- Bad Use Of Mutex\n {\n value = counter\n }\n mu.Unlock()\n\n value\u0026#43;\u0026#43;\n\n mu.Lock() \u0026lt;-- Bad Use Of Mutex\n {\n counter = value\n }\n mu.Unlock()\n }\n wg.Done()\n }()\n }\n\n wg.Wait()\n fmt.Println(\u0026#34;Counter:\u0026#34;, counter)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n به عنوان یک رهنمود عمومی، اگر در یک تابع دو بار فراخوانی Lock از همان میوتکس را ببینید، باید مرور کد را متوقف کنید. احتمالاً یک اشتباه یا پیچیدگی اضافی وجود دارد. در این مورد، فراخوانی‌های خواندن و نوشتن همگام‌سازی می‌شوند، اما دو گوروتین ممکن است در خط value++ با همان مقدار قرار بگیرند. تداخل داده هنوز وجود دارد و تشخیص‌دهنده تداخل در پیدا کردن آن ناتوان است.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eمیوتکس‌های خواندن/نوشتن (Read/Write Mutexes)\u003c/h2\u003e\n \n \n \u003cp\u003e\n یک نوع دوم از میوتکس به نام میوتکس خواندن/نوشتن وجود دارد. این به من اجازه می‌دهد که قفل‌ها را در اطراف عملیات‌های خواندن و نوشتن جدا کنم. این مهم است زیرا خواندن داده تهدیدی ایجاد نمی‌کند مگر اینکه یک گوروتین در همان زمان سعی در نوشتن کند. بنابراین، این نوع میوتکس اجازه می‌دهد تا چندین گوروتین به طور همزمان از حافظه همان اطلاعات خوانده شود. به محض درخواست قفل نوشتن، خواندها دیگر صادر نمی‌شوند، نوشتن انجام می‌شود، و بعداً خواندها می‌توانند دوباره شروع به کار کنند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003epackage main\n\nimport (\n \u0026#34;fmt\u0026#34;\n \u0026#34;math/rand\u0026#34;\n \u0026#34;sync\u0026#34;\n \u0026#34;time\u0026#34;\n)\n\nvar data []string\nvar rwMutex sync.RWMutex\n\nfunc main() {\n var wg sync.WaitGroup\n wg.Add(1)\n\n go func() {\n for i := 0; i \u0026lt; 10; i\u0026#43;\u0026#43; {\n writer(i)\n }\n wg.Done()\n }()\n\n for i := 0; i \u0026lt; 8; i\u0026#43;\u0026#43; {\n go func(id int) {\n for {\n reader(id)\n }\n }(i)\n }\n\n wg.Wait()\n fmt.Println(\u0026#34;Program Complete\u0026#34;)\n}\n\nfunc writer(i int) {\n rwMutex.Lock()\n {\n time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)\n fmt.Println(\u0026#34;****\u0026gt; : Performing Write\u0026#34;)\n data = append(data, fmt.Sprintf(\u0026#34;String: %d\u0026#34;, i))\n }\n rwMutex.Unlock()\n}\n\nfunc reader(id int) {\n rwMutex.RLock()\n {\n time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond)\n fmt.Printf(\u0026#34;%d : Performing Read : Length[%d]\\n\u0026#34;, id, len(data))\n }\n rwMutex.RUnlock()\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n می‌بینید که از یک میوتکس خواندن/نوشتن استفاده شده است که در آن 8 گوروتین دقیقاً در فاصله 10 میلی‌ثانیه از یکدیگر طول یک اسلایس را می‌خوانند و 1 گوروتین دیگر در مدت زمان 100 میلی‌ثانیه برای افزودن یک مقدار (نوشتن) به اسلایس بیدار می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n کلید اینجا به اجرای توابع نویسنده و خواننده است. توجه داشته باشید که برای نویسنده از Lock و برای خواننده از RLock استفاده می‌شود. یکی از بزرگ‌ترین اشتباهاتی که می‌توانید در اینجا انجام دهید، ترکیب نادرست فراخوانی‌های Unlock با نسخه اشتباه باشد. داشتن یک Lock با RUnlock هیچ‌وقت به خوبی پایان نمی‌یابد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e7 : Performing Read : Length[0]\n5 : Performing Read : Length[0]\n0 : Performing Read : Length[0]\n3 : Performing Read : Length[0]\n7 : Performing Read : Length[0]\n2 : Performing Read : Length[0]\n1 : Performing Read : Length[0]\n****\u0026gt; : Performing Write\n0 : Performing Read : Length[1]\n5 : Performing Read : Length[1]\n3 : Performing Read : Length[1]\n6 : Performing Read : Length[1]\n7 : Performing Read : Length[1]\n4 : Performing Read : Length[1]\n1 : Performing Read : Length[1]\n2 : Performing Read : Length[1]\n****\u0026gt; : Performing Write\n7 : Performing Read : Length[2]\n1 : Performing Read : Length[2]\n3 : Performing Read : Length[2]\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی نشان می‌دهد که چگونه چندین گوروتین در همان زمان خواندن می‌کنند، اما تمامی خواندن‌ها وقتی که عملیات نوشتن انجام می‌شود متوقف می‌شوند.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eیادداشت‌ها\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003eگوروتین‌ها نیاز به هماهنگی و همگام‌سازی دارند.\u003c/li\u003e\n \n \u003cli\u003eزمانی که دو یا چند گوروتین سعی در دسترسی به منبع مشترک دارند، تداخل داده داریم.\u003c/li\u003e\n \n \u003cli\u003eتوابع اتمیک و میوتکس‌ها می‌توانند پشتیبانی مورد نیاز را فراهم کنند.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eهمگرایی حافظه نهان و به‌اشتباه به اشتراک‌گذاری (Cache Coherency and False Sharing)\u003c/h2\u003e\n \n \n \u003cp\u003e\n این محتوا توسط Scott Meyers از سخنرانی او در سال ۲۰۱۴ در مراسم Dive ارائه شده است.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u003ca href=\"https://youtu.be/WDIkqP4JbkE?t=1809\" target=\"_blank\"\u003eCPU Caches and Why You Care (30:09-38:30)\u003c/a\u003e \n\n\n \u003ca href=\"https://github.com/ardanlabs/gotraining/blob/master/topics/go/testing/benchmarks/falseshare/README.md\" target=\"_blank\"\u003eCode Example\u003c/a\u003e\n \u003c/p\u003e\n \n\n \u003ca href=\"/tour/eng/static/img/figure1_data_race.png\" target=\"_blank\"\u003e\n \u003cimg class=\"codeimg\" src=\"/tour/eng/static/img/figure1_data_race.png\"\u003e\n \u003c/a\u003e\n\n\n \u003ch2\u003eیادداشت‌های همگرایی حافظه نهان و به اشتراک‌گذاری اشتباه\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003eدسترسی به حافظه توسط رشته‌ها مهم است.\u003c/li\u003e\n \n \u003cli\u003eاگر الگوریتم شما مقیاس‌پذیر نیست، به دنبال مشکلات به اشتراک‌گذاری اشتباهی بگردید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eخواندن بیشتر\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"http://www.drdobbs.com/parallel/eliminate-false-sharing/217500206\" target=\"_blank\"\u003eEliminate False Sharing\u003c/a\u003e - Herb Sutter \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://golang.org/ref/mem\" target=\"_blank\"\u003eThe Go Memory Model\u003c/a\u003e \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://blog.golang.org/race-detector\" target=\"_blank\"\u003eIntroducing the Go Race Detector\u003c/a\u003e - Dmitry Vyukov and Andrew Gerrand \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2013/09/detecting-race-conditions-with-go.html\" target=\"_blank\"\u003eDetecting Race Conditions With Go\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://golang.org/doc/articles/race_detector.html\" target=\"_blank\"\u003eData Race Detector\u003c/a\u003e \u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n","Files":[{"Name":"example1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// YOU NEED TO RUN THIS EXAMPLE OUTSIDE OF THE TOUR\n// go build -race or go run main.go -race\n\n// Sample program to show how to create race conditions in\n// our programs. We don't want to do this.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n)\n\n// counter is a variable incremented by all goroutines.\nvar counter int\n\nfunc main() {\n\n\t// Number of goroutines to use.\n\tconst grs = 2\n\n\t// wg is used to manage concurrency.\n\tvar wg sync.WaitGroup\n\twg.Add(grs)\n\n\t// Create two goroutines.\n\tfor g := 0; g \u003c grs; g++ {\n\t\tgo func() {\n\t\t\tfor i := 0; i \u003c 2; i++ {\n\n\t\t\t\t// Capture the value of Counter.\n\t\t\t\tvalue := counter\n\n\t\t\t\t// Increment our local value of Counter.\n\t\t\t\tvalue++\n\n\t\t\t\t// Store the value back into Counter.\n\t\t\t\tcounter = value\n\t\t\t}\n\n\t\t\twg.Done()\n\t\t}()\n\t}\n\n\t// Wait for the goroutines to finish.\n\twg.Wait()\n\tfmt.Println(\"Final Counter:\", counter)\n}\n\n/*\n==================\nWARNING: DATA RACE\nRead at 0x0000011a5118 by goroutine 7:\n main.main.func1()\n example1.go:34 +0x4e\n\nPrevious write at 0x0000011a5118 by goroutine 6:\n main.main.func1()\n example1.go:40 +0x6d\n\nGoroutine 7 (running) created at:\n main.main()\n example1.go:44 +0xc3\n\nGoroutine 6 (finished) created at:\n main.main()\n example1.go:44 +0xc3\n==================\nFinal Counter: 4\nFound 1 data race(s)\n*/\n","Hash":"uoHh7aa9CUuwaQNG10SqmntY5i8="},{"Name":"example2.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// YOU NEED TO RUN THIS EXAMPLE OUTSIDE OF THE TOUR\n// go build -race or go run main.go -race\n\n// Sample program to show how to use the atomic package to\n// provide safe access to numeric types.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n\t\"sync/atomic\"\n)\n\n// counter is a variable incremented by all goroutines.\nvar counter int64\n\nfunc main() {\n\n\t// Number of goroutines to use.\n\tconst grs = 2\n\n\t// wg is used to manage concurrency.\n\tvar wg sync.WaitGroup\n\twg.Add(grs)\n\n\t// Create two goroutines.\n\tfor g := 0; g \u003c grs; g++ {\n\t\tgo func() {\n\t\t\tfor i := 0; i \u003c 2; i++ {\n\t\t\t\tatomic.AddInt64(\u0026counter, 1)\n\t\t\t}\n\n\t\t\twg.Done()\n\t\t}()\n\t}\n\n\t// Wait for the goroutines to finish.\n\twg.Wait()\n\n\t// Display the final value.\n\tfmt.Println(\"Final Counter:\", counter)\n}\n","Hash":"Jq+0MDW2w9f8gLbu3z8A2Bnu3Q0="},{"Name":"example3.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to use a mutex to define critical\n// sections of code that need synchronous access.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n)\n\n// counter is a variable incremented by all goroutines.\nvar counter int\n\n// mutex is used to define a critical section of code.\nvar mutex sync.Mutex\n\nfunc main() {\n\n\t// Number of goroutines to use.\n\tconst grs = 2\n\n\t// wg is used to manage concurrency.\n\tvar wg sync.WaitGroup\n\twg.Add(grs)\n\n\t// Create two goroutines.\n\tfor g := 0; g \u003c grs; g++ {\n\t\tgo func() {\n\t\t\tfor i := 0; i \u003c 2; i++ {\n\n\t\t\t\t// Only allow one goroutine through this critical section at a time.\n\t\t\t\tmutex.Lock()\n\t\t\t\t{\n\t\t\t\t\t// Capture the value of counter.\n\t\t\t\t\tvalue := counter\n\n\t\t\t\t\t// Increment our local value of counter.\n\t\t\t\t\tvalue++\n\n\t\t\t\t\t// Store the value back into counter.\n\t\t\t\t\tcounter = value\n\t\t\t\t}\n\t\t\t\tmutex.Unlock()\n\t\t\t\t// Release the lock and allow any waiting goroutine through.\n\t\t\t}\n\n\t\t\twg.Done()\n\t\t}()\n\t}\n\n\t// Wait for the goroutines to finish.\n\twg.Wait()\n\tfmt.Printf(\"Final Counter: %d\\n\", counter)\n}\n","Hash":"DqmHWtidPhKct3YXM7sXPNIInHA="},{"Name":"example4.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to use a read/write mutex to define critical\n// sections of code that needs synchronous access.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n)\n\n// data is a slice that will be shared.\nvar data []string\n\n// rwMutex is used to define a critical section of code.\nvar rwMutex sync.RWMutex\n\n// Number of reads occurring at ay given time.\nvar readCount int64\n\nfunc main() {\n\n\t// wg is used to manage concurrency.\n\tvar wg sync.WaitGroup\n\twg.Add(1)\n\n\t// Create a writer goroutine.\n\tgo func() {\n\t\tfor i := 0; i \u003c 10; i++ {\n\t\t\twriter(i)\n\t\t}\n\t\twg.Done()\n\t}()\n\n\t// Create eight reader goroutines.\n\tfor i := 0; i \u003c 8; i++ {\n\t\tgo func(id int) {\n\t\t\tfor {\n\t\t\t\treader(id)\n\t\t\t}\n\t\t}(i)\n\t}\n\n\t// Wait for the write goroutine to finish.\n\twg.Wait()\n\tfmt.Println(\"Program Complete\")\n}\n\n// writer adds a new string to the slice in random intervals.\nfunc writer(i int) {\n\n\t// Only allow one goroutine to read/write to the slice at a time.\n\trwMutex.Lock()\n\t{\n\t\t// Capture the current read count.\n\t\t// Keep this safe though we can due without this call.\n\t\trc := atomic.LoadInt64(\u0026readCount)\n\n\t\t// Perform some work since we have a full lock.\n\t\ttime.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)\n\t\tfmt.Printf(\"****\u003e : Performing Write : RCount[%d]\\n\", rc)\n\t\tdata = append(data, fmt.Sprintf(\"String: %d\", i))\n\t}\n\trwMutex.Unlock()\n\t// Release the lock.\n}\n\n// reader wakes up and iterates over the data slice.\nfunc reader(id int) {\n\n\t// Any goroutine can read when no write operation is taking place.\n\trwMutex.RLock()\n\t{\n\t\t// Increment the read count value by 1.\n\t\trc := atomic.AddInt64(\u0026readCount, 1)\n\n\t\t// Perform some read work and display values.\n\t\ttime.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond)\n\t\tfmt.Printf(\"%d : Performing Read : Length[%d] RCount[%d]\\n\", id, len(data), rc)\n\n\t\t// Decrement the read count value by 1.\n\t\tatomic.AddInt64(\u0026readCount, -1)\n\t}\n\trwMutex.RUnlock()\n\t// Release the read lock.\n}\n","Hash":"giFOhX7bsf3OftNIhBQsqHCWxwE="},{"Name":"example5.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how maps are not safe for concurrent use by default.\n// The runtime will detect concurrent writes and panic.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n)\n\n// scores holds values incremented by multiple goroutines.\nvar scores = make(map[string]int)\n\nfunc main() {\n\tvar wg sync.WaitGroup\n\twg.Add(2)\n\n\tgo func() {\n\t\tfor i := 0; i \u003c 1000; i++ {\n\t\t\tscores[\"A\"]++\n\t\t}\n\t\twg.Done()\n\t}()\n\n\tgo func() {\n\t\tfor i := 0; i \u003c 1000; i++ {\n\t\t\tscores[\"B\"]++\n\t\t}\n\t\twg.Done()\n\t}()\n\n\twg.Wait()\n\tfmt.Println(\"Final scores:\", scores)\n}\n","Hash":"R4pc+mOl/3CtmUGdjOyerVHmNsg="},{"Name":"example6.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show a more complicated race condition using\n// an interface value. This produces a read to an interface value after\n// a partial write.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"sync\"\n)\n\n// Speaker allows for speaking behavior.\ntype Speaker interface {\n\tSpeak() bool\n}\n\n// Ben is a person who can speak.\ntype Ben struct {\n\tname string\n}\n\n// Speak allows Ben to say hello. It returns false if the method is\n// called through the interface value after a partial write.\nfunc (b *Ben) Speak() bool {\n\tif b.name != \"Ben\" {\n\t\tfmt.Printf(\"Ben says, \\\"Hello my name is %s\\\"\\n\", b.name)\n\t\treturn false\n\t}\n\n\treturn true\n}\n\n// Jerry is a person who can speak.\ntype Jerry struct {\n\tname string\n}\n\n// Speak allows Jerry to say hello. It returns false if the method is\n// called through the interface value after a partial write.\nfunc (j *Jerry) Speak() bool {\n\tif j.name != \"Jerry\" {\n\t\tfmt.Printf(\"Jerry says, \\\"Hello my name is %s\\\"\\n\", j.name)\n\t\treturn false\n\t}\n\n\treturn true\n}\n\nfunc main() {\n\n\t// Create values of type Ben and Jerry.\n\tben := Ben{\"Ben\"}\n\tjerry := Jerry{\"Jerry\"}\n\n\t// Assign the pointer to the Ben value to the interface value.\n\tperson := Speaker(\u0026ben)\n\n\t// Have a goroutine constantly assign the pointer of\n\t// the Ben value to the interface and then Speak.\n\tgo func() {\n\t\tfor {\n\t\t\tperson = \u0026ben\n\t\t\tif !person.Speak() {\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\t\t}\n\t}()\n\n\t// Have a goroutine constantly assign the pointer of\n\t// the Jerry value to the interface and then Speak.\n\tgo func() {\n\t\tfor {\n\t\t\tperson = \u0026jerry\n\t\t\tif !person.Speak() {\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\t\t}\n\t}()\n\n\t// Just hold main from returning. The data race will\n\t// cause the program to exit.\n\tvar wg sync.WaitGroup\n\twg.Add(1)\n\twg.Wait()\n}\n","Hash":"xT1O2rUKBmi1J317ugjfo91Y+ew="}]},{"Title":"تمرین‌ها","Content":"\n \u003ch2\u003eتمرین‌ها\u003c/h2\u003e\n \n \n \u003cp\u003e\n از الگو به عنوان نقطه شروع برای انجام تمرین‌ها استفاده کنید. یک راه‌حل ممکن ارائه شده است.\n \u003c/p\u003e\n \n\n \u003ch2\u003eتمرین 1\u003c/h2\u003e\n \n \n \u003cp\u003e\n با توجه به برنامه زیر، از تشخیص‌دهنده تداخل برای پیدا کردن و تصحیح تداخل داده استفاده کنید.\n \u003c/p\u003e\n \n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n","Files":[{"Name":"exercise1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Fix the race condition in this program.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"sync\"\n)\n\n// numbers maintains a set of random numbers.\nvar numbers []int\n\nfunc main() {\n\n\t// Number of goroutines to use.\n\tconst grs = 3\n\n\t// wg is used to manage concurrency.\n\tvar wg sync.WaitGroup\n\twg.Add(grs)\n\n\t// Create three goroutines to generate random numbers.\n\tfor i := 0; i \u003c grs; i++ {\n\t\tgo func() {\n\t\t\trandom(10)\n\t\t\twg.Done()\n\t\t}()\n\t}\n\n\t// Wait for all the goroutines to finish.\n\twg.Wait()\n\n\t// Display the set of random numbers.\n\tfor i, number := range numbers {\n\t\tfmt.Println(i, number)\n\t}\n}\n\n// random generates random numbers and stores them into a slice.\nfunc random(amount int) {\n\n\t// Generate as many random numbers as specified.\n\tfor i := 0; i \u003c amount; i++ {\n\t\tn := rand.Intn(100)\n\t\tnumbers = append(numbers, n)\n\t}\n}\n","Hash":"a0Ebkx0381HuUS6U/zEkOUpldoI="},{"Name":"answer1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Answer for exercise 1 of Race Conditions.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"sync\"\n)\n\n// numbers maintains a set of random numbers.\nvar numbers []int\n\n// mutex will help protect the slice.\nvar mutex sync.Mutex\n\n// main is the entry point for the application.\nfunc main() {\n\t// Number of goroutines to use.\n\tconst grs = 3\n\n\t// wg is used to manage concurrency.\n\tvar wg sync.WaitGroup\n\twg.Add(grs)\n\n\t// Create three goroutines to generate random numbers.\n\tfor i := 0; i \u003c grs; i++ {\n\t\tgo func() {\n\t\t\trandom(10)\n\t\t\twg.Done()\n\t\t}()\n\t}\n\n\t// Wait for all the goroutines to finish.\n\twg.Wait()\n\n\t// Display the set of random numbers.\n\tfor i, number := range numbers {\n\t\tfmt.Println(i, number)\n\t}\n}\n\n// random generates random numbers and stores them into a slice.\nfunc random(amount int) {\n\t// Generate as many random numbers as specified.\n\tfor i := 0; i \u003c amount; i++ {\n\t\tn := rand.Intn(100)\n\n\t\t// Protect this append to keep access safe.\n\t\tmutex.Lock()\n\t\t{\n\t\t\tnumbers = append(numbers, n)\n\t\t}\n\t\tmutex.Unlock()\n\t}\n}\n","Hash":"jwVW1FIbUTkDCvNH9AwdZmRK40g="}]}]} ,"functions":{"Title":"توابع","Description":"توابع در مرکز زبان قرار دارند و این امکان را فراهم می‌کنند که کد خود را به قسمت‌های مجزا و متمایز از عملکرد تقسیم و سازماندهی کنیم.","Pages":[{"Title":"توابع","Content":"\n \u003ch2\u003eتوابع\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/training/individual-on-demand/ultimate-go-bundle/\" target=\"_blank\"\u003eتماشای ویدئو\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003eنیاز به کمک مالی دارید؟ از \u003ca href=\"https://www.ardanlabs.com/scholarship/\" target=\"_blank\"\u003eفرم Scholarship\u003c/a\u003e ما استفاده کنید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n توابع در مرکز زبان قرار دارند و این امکان را فراهم می‌کنند که کد خود را به قسمت‌های مجزا و متمایز از عملکرد تقسیم و سازماندهی کنیم. آنها می‌توانند برای ارائه یک رابط به پکیج‌هایی که می‌نویسیم و یک جزء اصلی از همزمانی باشند، استفاده شوند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n توابع در Go نقش مرکزی در ساختار دهی کد و ترویج نرم‌افزاری تمیز، قابل نگهداری و کارآمد ایفا می‌کنند. درک این که چگونه توابع را اعلام، تعریف و استفاده کنیم برای نوشتن برنامه‌های موثر Go بسیار حیاتی است.\n \u003c/p\u003e\n \n\n \u003ch2\u003eمرور کد\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e1:\u003c/b\u003e برگرداندن مقادیر چندگانه\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e2:\u003c/b\u003e شناسایی خالی\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e3:\u003c/b\u003e تعریف مجدد\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e4:\u003c/b\u003e Anonymous Functions/Closures\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e5:\u003c/b\u003e بازیابی خطاها\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n \n \u003cp\u003e\n توابع بلوک‌های اساسی برنامه‌ها هستند و برای مدولاریت و قابلیت استفاده مجدد بهتر، کد را کپسوله و سازماندهی می‌کنند. توابع بلوک‌های کد هستند که یک کار خاص یا مجموعه‌ای از کارهای مرتبط را انجام می‌دهند. در ادامه یک بررسی کلی از توابع در Go داریم:\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eتعریف تابع\u003c/h2\u003e\n \n \n \u003cp\u003e\n برای تعریف یک تابع در Go، از کلمه کلیدی \u003ccode\u003efunc\u003c/code\u003e استفاده می‌شود که بعد از آن نام تابع، یک لیست از پارامترها که در پرانتز قرار دارند، و یک نوع بازگشتی اختیاری دنبال می‌شود. ساختار عمومی به این صورت است:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc functionName(parameter1 type, parameter2 type, ...) return_type {\n // Function body\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n For example:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc add(x int, y int) int {\n return x \u0026#43; y\n}\u003c/pre\u003e\n \n\n\n \u003ch2\u003eپارامترها و مقادیر بازگشتی\u003c/h2\u003e\n \n \n \u003cp\u003e\n توابع می‌توانند صفر یا بیشتر پارامتر را بپذیرند که این مقادیر هنگام فراخوانی تابع به آن منتقل می‌شوند. هر پارامتر شامل یک نام و یک نوع است. در مثال بالا، \u003ccode\u003eadd\u003c/code\u003e دو پارامتر صحیح به نام‌های \u003ccode\u003ex\u003c/code\u003e و \u003ccode\u003ey\u003c/code\u003e دریافت می‌کند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n توابع می‌توانند صفر یا یک مقدار (یا هیچ مقدار، در صورتی که می‌توانید نوع بازگشتی را حذف کنید) بازگشت دهند. دستور \u003ccode\u003ereturn\u003c/code\u003e برای مشخص کردن مقداری که باید بازگشت داده شود، استفاده می‌شود. در مثال بالا، \u003ccode\u003eadd\u003c/code\u003e یک عدد صحیح بازمی‌گرداند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n Go اجازه می‌دهد که توابع مقادیر چندگانه بازگشت دهند. این در مواردی مفید است که می‌خواهید بیش از یک نتیجه از یک تابع بازگردانید. به عنوان مثال:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc divide(x, y float64) (float64, error) {\n if y == 0 {\n return 0, errors.New(\u0026#34;division by zero\u0026#34;)\n }\n return x / y, nil\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در این مثال، تابع \u003ccode\u003edivide\u003c/code\u003e همچنین یک نتیجه اعشاری و یک خطا (در صورت وقوع تقسیم بر صفر) باز می‌گرداند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n Go به شما امکان می‌دهد که نام‌ها را برای مقادیر بازگشتی در امضای یک تابع مشخص کنید. مقادیر بازگشتی با نام به طور خودکار مقداردهی اولیه می‌شوند و می‌توانند در داخل تابع به عنوان متغیرهای عادی استفاده شوند. این مخصوصاً در مواجهه با توابع پیچیده یا کنترل خطا بسیار مفید است. به عنوان مثال:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc divide(x, y float64) (result float64, err error) {\n if y == 0 {\n err = errors.New(\u0026#34;division by zero\u0026#34;)\n return\n }\n result = x / y\n return\n}\u003c/pre\u003e\n \n\n\n \u003ch2\u003eفراخوانی توابع\u003c/h2\u003e\n \n \n \u003cp\u003e\n برای فراخوانی یک تابع در Go، شما از نام تابع پس از آن یک لیست از آرگومان‌ها که در پرانتز قرار دارند استفاده می‌کنید. اگر تابع دارای مقادیر بازگشتی چندگانه باشد، می‌توانید آنها را در متغیرها ذخیره کنید. به عنوان مثال:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003esum := add(3, 5) // Call the add function and assign its result to sum\nresult, err := divide(8, 2) // Call the divide function and capture both result and error\u003c/pre\u003e\n \n\n\n \u003ch2\u003eپارامترهای متغیر توابع\u003c/h2\u003e\n \n \n \u003cp\u003e\n Go از توابع متغیر پشتیبانی می‌کند که به شما امکان می‌دهد تا تعداد متغیری از آرگومان‌ها را به یک تابع ارسال کنید. برای تعریف یک پارامتر متغیر، از سه نقطه (\u003ccode\u003e...\u003c/code\u003e) پس از نوع پارامتر استفاده می‌کنید. به عنوان مثال:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc calculateSum(nums ...int) int {\n sum := 0\n for _, num := range nums {\n sum \u0026#43;= num\n }\n return sum\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n شما می‌توانید این تابع را با هر تعداد اعداد صحیح فراخوانی کنید.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eتوابع ناشناس\u003c/h2\u003e\n \n \n \u003cp\u003e\n Go از توابع ناشناس نیز به عنوان closures پشتیبانی می‌کند. این توابع بدون نام هستند و می‌توانند به متغیرها اختصاص داده و به عنوان آرگومان‌ها به توابع دیگر ارسال شوند. Closures معمولاً در Go برای وظایفی مانند تعریف توابع درونی یا برنامه‌نویسی همروند با استفاده از goroutine‌ها به کار می‌روند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eadd := func(x, y int) int {\n return x \u0026#43; y\n}\n\nresult := add(3, 5)\u003c/pre\u003e\n \n\n\n \u003ch2\u003eتابع به عنوان یک نوع\u003c/h2\u003e\n \n \n \u003cp\u003e\n در Go، توابع همچنین می‌توانند به عنوان انواع مورد استفاده قرار گیرند. این به شما امکان می‌دهد تا توابعی را تعریف کنید که توابع دیگر را به عنوان آرگومان‌ها بپذیرند یا آنها را به عنوان نتایج بازگردانند. این یک ویژگی قدرتمند برای پیاده‌سازی توابع بالارده و تابع‌های فرعی (callbacks) است.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype MathFunc func(int, int) int\n\nfunc operate(x, y int, op MathFunc) int {\n return op(x, y)\n}\u003c/pre\u003e\n \n\n\n \u003ch2\u003eتأخیر و پانیک\u003c/h2\u003e\n \n \n \u003cp\u003e\n Go دو تابع ویژه به نام‌های \u003ccode\u003edefer\u003c/code\u003e و \u003ccode\u003epanic\u003c/code\u003e را برای مدیریت شرایط استثنایی و مدیریت منابع ارائه می‌دهد. \u003ccode\u003edefer\u003c/code\u003e برای زمانبندی تماس تابع به منظور اجرا شدن قبل از بازگشت تابع استفاده می‌شود، در حالی که \u003ccode\u003epanic\u003c/code\u003e برای ایجاد یک خطای اجرایی و عملیات رفتار از پشته استفاده می‌شود.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eیادداشت‌ها\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003eتوابع می‌توانند مقادیر متعددی را بازگردانند و بیشتر از یک مقدار خطا بازگردانند.\u003c/li\u003e\n \n \u003cli\u003eمقدار خطا همیشه باید به عنوان بخشی از منطق برنامه‌نویسی بررسی شود.\u003c/li\u003e\n \n \u003cli\u003eشناسه خالی برای نادیده گرفتن مقادیر بازگشتی مورد استفاده قرار می‌گیرد.\u003c/li\u003e\n \n \u003cli\u003eدستوراتی که برای تعریف توابع استفاده می‌شوند: \u003ccode\u003efunc\u003c/code\u003e، \u003ccode\u003ereceiver\u003c/code\u003e، \u003ccode\u003eidentifier\u003c/code\u003e، \u003ccode\u003e[parameter(s)]\u003c/code\u003e، \u003ccode\u003e[return(s)]\u003c/code\u003e، `بلوک کد`.\u003c/li\u003e\n \n \u003cli\u003eپارامترهای متغیر، آرگومان‌های متغیر و بازگردانی یک آرایه.\u003c/li\u003e\n \n \u003cli\u003eتوابع نوعی از داده هستند: می‌توانید توابع را به عنوان پارامترها، آرگومان‌ها و بازگشتی‌ها استفاده کنید.\u003c/li\u003e\n \n \u003cli\u003e\u003ccode\u003edefer\u003c/code\u003e برای زمانبندی تماس تابع در تابع دیگری استفاده می‌شود.\u003c/li\u003e\n \n \u003cli\u003e\u003ccode\u003epanic\u003c/code\u003e خطای اجرایی را ایجاد کرده و پشته را بازگشت می‌دهد.\u003c/li\u003e\n \n \u003cli\u003eبازگشت‌های نامگذاری‌شده کمتر قابل خواندن هستند و بازگشت‌های معمولی نمی‌باشند.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eمطالب اضافی\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://golang.org/doc/effective_go.html#functions\" target=\"_blank\"\u003eEffective Go\u003c/a\u003e \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2013/10/functions-and-naked-returns-in-go.html\" target=\"_blank\"\u003eFunctions and Naked returns in Go\u003c/a\u003e \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2013/06/understanding-defer-panic-and-recover.html\" target=\"_blank\"\u003eUnderstanding defer panics and recover\u003c/a\u003e\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eتمرین ۱\u003c/h2\u003e\n \n \n \u003cp\u003e\n \u003cb\u003eقسمت\u003c/b\u003e \u003cb\u003eالف:\u003c/b\u003e یک نوع ساختار برای نگه‌داری اطلاعات یک کاربر اعلام کنید. یک تابع اعلام کنید که مقداری از این نوع ایجاد می‌کند و اشاره‌گرهای آن را به عنوان مقدار بازگردانی می‌دهد و مقدار خطا را. این تابع را از تابع \u003ccode\u003emain\u003c/code\u003e فراخوانی کرده و مقدار را نمایش دهید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u003cb\u003eقسمت\u003c/b\u003e \u003cb\u003eب:\u003c/b\u003e یک تماس دوم به تابع خود بزنید، اما این بار مقدار را نادیده بگیرید و فقط مقدار خطا را بررسی کنید.\n \u003c/p\u003e\n \n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n","Files":[{"Name":"example1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how functions can return multiple values while using\n// named and struct types.\npackage main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// user is a struct type that declares user information.\ntype user struct {\n\tID int\n\tName string\n}\n\nfunc main() {\n\n\t// Retrieve the user profile.\n\tu, err := retrieveUser(\"sally\")\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\treturn\n\t}\n\n\t// Display the user profile.\n\tfmt.Printf(\"%+v\\n\", *u)\n}\n\n// retrieveUser retrieves the user document for the specified\n// user and returns a pointer to a user type value.\nfunc retrieveUser(name string) (*user, error) {\n\n\t// Make a call to get the user in a json response.\n\tr, err := getUser(name)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Unmarshal the json document into a value of\n\t// the user struct type.\n\tvar u user\n\terr = json.Unmarshal([]byte(r), \u0026u)\n\treturn \u0026u, err\n}\n\n// GetUser simulates a web call that returns a json\n// document for the specified user.\nfunc getUser(name string) (string, error) {\n\tresponse := `{\"id\":1432, \"name\":\"sally\"}`\n\treturn response, nil\n}\n","Hash":"JbgLG0slSV4XETLD96vydyV9p+w="},{"Name":"example2.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how we can use the blank identifier to\n// ignore return values.\npackage main\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n)\n\n// user is a struct type that declares user information.\ntype user struct {\n\tID int\n\tName string\n}\n\n// updateStats provides update stats.\ntype updateStats struct {\n\tModified int\n\tDuration float64\n\tSuccess bool\n\tMessage string\n}\n\nfunc main() {\n\n\t// Declare and initialize a value of type user.\n\tu := user{\n\t\tID: 1432,\n\t\tName: \"Betty\",\n\t}\n\n\t// Update the user Name. Don't care about the update stats.\n\tif _, err := updateUser(\u0026u); err != nil {\n\t\tfmt.Println(err)\n\t\treturn\n\t}\n\n\t// Display the update was Successful.\n\tfmt.Println(\"Updated user record for ID\", u.ID)\n}\n\n// updateUser updates the specified user document.\nfunc updateUser(u *user) (*updateStats, error) {\n\n\t// response simulates a JSON response.\n\tresponse := `{\"Modified\":1, \"Duration\":0.005, \"Success\" : true, \"Message\": \"updated\"}`\n\n\t// Unmarshal the json document into a value of\n\t// the userStats struct type.\n\tvar us updateStats\n\tif err := json.Unmarshal([]byte(response), \u0026us); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Check the update status to verify the update\n\t// was Successful.\n\tif us.Success != true {\n\t\treturn nil, errors.New(us.Message)\n\t}\n\n\treturn \u0026us, nil\n}\n","Hash":"hBSa06N0gn7y46K/PzculqcpFeo="},{"Name":"example3.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// From Spec:\n// a short variable declaration may redeclare variables provided they\n// were originally declared earlier in the same block with the same\n// type, and at least one of the non-blank variables is new.\n\n// Sample program to show some of the mechanics behind the\n// short variable declaration operator redeclares.\npackage main\n\nimport \"fmt\"\n\n// user is a struct type that declares user information.\ntype user struct {\n\tid int\n\tname string\n}\n\nfunc main() {\n\n\t// Declare the error variable.\n\tvar err1 error\n\n\t// The short variable declaration operator will\n\t// declare u and redeclare err1.\n\tu, err1 := getUser()\n\tif err1 != nil {\n\t\treturn\n\t}\n\n\tfmt.Println(u)\n\n\t// The short variable declaration operator will\n\t// redeclare u and declare err2.\n\tu, err2 := getUser()\n\tif err2 != nil {\n\t\treturn\n\t}\n\n\tfmt.Println(u)\n}\n\n// getUser returns a pointer of type user.\nfunc getUser() (*user, error) {\n\treturn \u0026user{1432, \"Betty\"}, nil\n}\n","Hash":"O7zksLUFIxYTNJ/qs+a6QmLGbrA="},{"Name":"example4.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how anonymous functions and closures work.\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tvar n int\n\n\t// Declare an anonymous function and call it.\n\tfunc() {\n\t\tfmt.Println(\"Direct:\", n)\n\t}()\n\n\t// Declare an anonymous function and assign it to a variable.\n\tf := func() {\n\t\tfmt.Println(\"Variable:\", n)\n\t}\n\n\t// Call the anonymous function through the variable.\n\tf()\n\n\t// Defer the call to the anonymous function till after main returns.\n\tdefer func() {\n\t\tfmt.Println(\"Defer 1:\", n)\n\t}()\n\n\t// Set the value of n to 3 before the return.\n\tn = 3\n\n\t// Call the anonymous function through the variable.\n\tf()\n\n\t// Defer the call to the anonymous function till after main returns.\n\tdefer func() {\n\t\tfmt.Println(\"Defer 2:\", n)\n\t}()\n}\n","Hash":"wsliFjOgGCdhSuHhm1bQ1vWtOf4="},{"Name":"example5.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to recover from panics.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n)\n\nfunc main() {\n\n\t// Call the testPanic function to run the test.\n\tif err := testPanic(); err != nil {\n\t\tfmt.Println(\"Error:\", err)\n\t}\n}\n\n// testPanic simulates a function that encounters a panic to\n// test our catchPanic function.\nfunc testPanic() (err error) {\n\n\t// Schedule the catchPanic function to be called when\n\t// the testPanic function returns.\n\tdefer catchPanic(\u0026err)\n\n\tfmt.Println(\"Start Test\")\n\n\t// Mimic a traditional error from a function.\n\terr = mimicError(\"1\")\n\n\t// Trying to dereference a nil pointer will cause the\n\t// runtime to panic.\n\tvar p *int\n\t*p = 10\n\n\tfmt.Println(\"End Test\")\n\treturn err\n}\n\n// catchPanic catches panics and processes the error.\nfunc catchPanic(err *error) {\n\n\t// Check if a panic occurred.\n\tif r := recover(); r != nil {\n\t\tfmt.Println(\"PANIC Deferred\")\n\n\t\t// Capture the stack trace.\n\t\tbuf := make([]byte, 10000)\n\t\truntime.Stack(buf, false)\n\t\tfmt.Println(\"Stack Trace:\", string(buf))\n\n\t\t// If the caller wants the error back provide it.\n\t\tif err != nil {\n\t\t\t*err = fmt.Errorf(\"%v\", r)\n\t\t}\n\t}\n}\n\n// mimicError is a function that simulates an error for\n// testing the code.\nfunc mimicError(key string) error {\n\treturn fmt.Errorf(\"Mimic Error : %s\", key)\n}\n","Hash":"rHmk/7Zj+8L5IeBJj4OId/XaPKk="},{"Name":"exercise1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Declare a struct type to maintain information about a user. Declare a function\n// that creates value of and returns pointers of this type and an error value. Call\n// this function from main and display the value.\n//\n// Make a second call to your function but this time ignore the value and just test\n// the error value.\npackage main\n\n// Add imports.\n\n// Declare a type named user.\n\n// Declare a function that creates user type values and returns a pointer\n// to that value and an error value of nil.\nfunc funcName() /* (pointer return arg, error return arg) */ {\n\n\t// Create a value of type user and return the proper values.\n}\n\nfunc main() {\n\n\t// Use the function to create a value of type user. Check\n\t// the error being returned.\n\n\t// Display the value that the pointer points to.\n\n\t// Call the function again and just check the error.\n}\n","Hash":"5beNH5BwsJ+WMx2E2NTKV2ensQs="},{"Name":"answer1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Declare a struct type to maintain information about a user. Declare a function\n// that creates value of and returns pointers of this type and an error value. Call\n// this function from main and display the value.\n//\n// Make a second call to your function but this time ignore the value and just test\n// the error value.\npackage main\n\nimport \"fmt\"\n\n// user represents a user in the system.\ntype user struct {\n\tname string\n\temail string\n}\n\n// newUser creates and returns pointers of user type values.\nfunc newUser() (*user, error) {\n\treturn \u0026user{\"Bill\", \"bill@ardanlabs.com\"}, nil\n}\n\nfunc main() {\n\n\t// Create a value of type user.\n\tu, err := newUser()\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\treturn\n\t}\n\n\t// Display the value.\n\tfmt.Println(*u)\n\n\t// Call the function and just check the error on the return.\n\t_, err = newUser()\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\treturn\n\t}\n}\n","Hash":"XUzF6Juc7+11tOVF8EKkl+DPjfU="}]}]} ,"goroutines":{"Title":"گوروتین‌ها","Description":"گوروتین‌ها توابعی هستند که توسط برنامه‌نویس ایجاد می‌شوند و توسط برنامه اجرا می‌شوند. این اجراها مستقل از یکدیگر توسط برنامه‌ی Go مدیریت می‌شوند.","Pages":[{"Title":"گوروتین‌ها","Content":"\n \u003ch2\u003eگوروتین‌ها\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/training/individual-on-demand/ultimate-go-bundle/\" target=\"_blank\"\u003eتماشای ویدئو\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003eنیاز به کمک مالی دارید؟ از \u003ca href=\"https://www.ardanlabs.com/scholarship/\" target=\"_blank\"\u003eفرم Scholarship\u003c/a\u003e ما استفاده کنید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n گوروتین‌ها توابعی هستند که به صورت مستقل توسط برنامه‌ی Go ایجاد و برنامه‌ی Go برای اجرای آن‌ها زمان‌بندی می‌کند. زمان‌بندی و اجرای گوروتین‌ها توسط برنامه‌ی Go انجام می‌شود.\n \u003c/p\u003e\n \n\n \u003ch2\u003eمرور کد\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e1:\u003c/b\u003e گوروتین‌ها و همروندی\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e2:\u003c/b\u003e تعویض متناوب کانتکست گوروتین\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e3:\u003c/b\u003e گوروتین‌ها و همروندی\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n \u003ch2\u003eمعنایی از زمان‌بند\u003c/h2\u003e\n \n \n \u003cp\u003e\n زمانی که یک برنامه Go شروع به اجرا می‌کند، محیط اجرایی Go از سیستم (مجازی یا فیزیکی) می‌پرسد که چند نخ سیستم عامل می‌توانند به صورت موازی اجرا شوند. این بر اساس تعداد هسته‌هایی است که برنامه در دسترس دارد. برای هر نخ که می‌تواند به صورت موازی اجرا شود، محیط اجرایی یک نخ سیستم عامل (M) ایجاد می‌کند و آن را به یک ساختار داده متصل می‌کند که یک پردازنده منطقی (P) در داخل برنامه را نمایان می‌کند. این P و M قدرت محاسباتی یا زمینه اجرایی را برای اجرای برنامه Go نمایان می‌کنند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n همچنین، یک گوروتین اولیه (G) ایجاد می‌شود تا مدیریت اجرای دستورات را در یک M/P انتخاب شده انجام دهد. همانطور که یک M مدیریت اجرای دستورات را روی سخت‌افزار انجام می‌دهد، یک G مدیریت اجرای دستورات را روی M انجام می‌دهد. این یک لایه جدید از انتزاع بالاتر از زمان‌بند سیستم عامل ایجاد می‌کند، اما کنترل اجرا را به سطح برنامه انتقال می‌دهد.\n \u003c/p\u003e\n \n\n \u003ca href=\"/tour/eng/static/img/gor1.png\" target=\"_blank\"\u003e\n \u003cimg class=\"codeimg\" src=\"/tour/eng/static/img/gor1.png\"\u003e\n \u003c/a\u003e\n\n \n \u003cp\u003e\n زیرا زمان‌بند Go روی زمان‌بند سیستم عامل قرار دارد، اهمیت دارد که مفهومی از زمان‌بند سیستم عامل و محدودیت‌های آن نسبت به زمان‌بند Go و برنامه‌ها وجود داشته باشد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n زمان‌بند سیستم عامل وظیفه ایجاد حس واقعی از اجرای همزمان چند کار را دارد، حتی زمانی که این امر فیزیکیاً ممکن نباشد. این نیاز به مدیریت تعادل‌هایی در طراحی زمان‌بند دارد. قبل از ادامه، مهم است که تعریف‌هایی ارائه دهیم:\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u003cb\u003e کار:*\u003c/b\u003e یک مجموعه دستوراتی که باید برای اجرای یک برنامه اجرا شود. این با استفاده از نخ‌ها انجام می‌شود و یک برنامه می‌تواند 1 تا متعدد نخ داشته باشد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u003cb\u003e نخ:*\u003c/b\u003e یک مسیر اجرایی که برنامه ریزی شده و اجرا می‌شود. نخ‌ها مسئول اجرای دستورات بر روی سخت‌افزار هستند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n **وضعیت‌های نخ:\u003cb\u003e\u003c/b\u003e یک نخ ممکن است در یکی از سه وضعیت زیر باشد: در حال اجرا، در حالت آماده به اجرا (قابل اجرا) یا در حالت انتظار. در حال اجرا به معنای اجرای دستورات اختصاص داده شده بر روی سخت‌افزار با قرار دادن یک G بر روی M است. قابل اجرا به معنای نخ می‌باشد که می‌خواهد زمانی را در سخت‌افزار داشته باشد تا دستورات اختصاص داده شده را اجرا کند و در یک صف اجرایی قرار دارد. انتظار به معنای انتظار نخ برای چیزی قبل از ادامه کار خود است. نخ‌های در حالت انتظار مورد نظر زمان‌بند نیستند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u003cb\u003eهمزمانی:\u003c/b\u003e این به معنای اجرای غیر معین و بدون ترتیب است. به عبارت دیگر، یک مجموعه دستورات که باید به ترتیب اجرا شود، به ترتیب معینی اجرا نمی‌شوند، بلکه به ترتیب نامعینی دیگری اجرا می‌شوند، اما تمام اجرا می‌شوند. کلید این است که نتیجه اجرای تمام دستورات در هر ترتیب نامعینی، نتیجه مشابهی تولید می‌کند. می‌توانید بگویید که کار می‌تواند همزمان انجام شود زمانی که ترتیب اجرای کار در واقعیت مهم نیست، تا زمانی که همه کار انجام شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n *پردازش همزمان:* این به معنای انجام چندین کار به یکدیگر است. برای این که این امکان وجود داشته باشد، باید قابلیت اجرای حداقل دو یا بیشتر نخ سیستم عامل را بر روی سخت‌افزار داشته باشید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n *کار محدود به پردازنده (CPU Bound):* این نوع کار به نوعی است که باعث نمی‌شود نخ به صورت طبیعی در وضعیت انتظار بیفتد. مثلاً محاسبه اعداد فیبوناچی به عنوان کار محدود به پردازنده محسوب می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n *کار محدود به ورودی/خروجی (I/O Bound):* این نوع کار باعث می‌شود نخ به صورت طبیعی به وضعیت انتظار بیفتد. مثلاً دریافت داده از آدرس‌های مختلف در شبکه به عنوان کار محدود به ورودی/خروجی محسوب می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u003cb\u003eهمگام‌سازی:\u003c/b\u003e زمانی که دو یا بیشتر از گوروتین‌ها نیاز به دسترسی به همان مکان حافظه دارند، ممکن است در همان زمان این عملیات رخ دهد. در این صورت، نیاز به همگام‌سازی و تنظیم ترتیب اجرا دارند. اگر این همگام‌سازی انجام نشود و حداقل یک گوروتین دارای عملیات نوشتن باشد، ممکن است با یک اشکال در داده (مسابقه داده) روبه‌رو شوید. مسابقه داده، علتی از اشکالات خرابی داده است که ممکن است به دشواری پیدا شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n *ارتباط گیری:* زمانی که دو یا بیشتر از گوروتین‌ها نیاز به ارسال پیام به یکدیگر دارند، با یا بدون داده، ارتباط گیری مورد نیاز است. اگر ارتباط گیری انجام نشود، تضمینات مربوط به کار همزمان انجام شده و تکمیل شده از دست می‌روند. این می‌تواند علت ایجاد انواع مختلفی از اشکالات خرابی داده باشد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n تعداد زیادی جزئیات کوچک مرتبط با نحوه زمان‌بندی وجود دارد، بنابراین برای یادگیری بیشتر می‌توانید سه مطلب در فصل 14 با عنوان \u0026#34;زمان‌بندی در Go\u0026#34; را مطالعه کنید.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eمبانی همزمانی\u003c/h2\u003e\n \n \n \u003cp\u003e\n شروع با یک مشکل همزمانی پایه که نیاز به ارتباط گیری دارد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc init() {\n runtime.GOMAXPROCS(1)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n تماس به \u003ccode\u003eGOMAXPROCS\u003c/code\u003e برای اجرای برنامه Go به عنوان یک برنامه تک‌نخی Go استفاده می‌شود. این برنامه تک‌نخی خواهد بود و یک P/M تنها برای اجرای تمام گوروتین‌ها دارد. تابع با حروف بزرگ نوشته شده است چرا که همچنین یک متغیر محیطی است. با این حال، این فراخوانی تابع متغیر محیطی را بازنویس می‌کند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eg := runtime.GOMAXPROCS(0)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این تابع یک تابع مهم است زمانی که نرخ‌های پردازنده را به پیکربندی کانتینر تنظیم می‌کنید. با ارسال 0، تعداد نخ‌هایی که برنامه Go از آن استفاده خواهد کرد، گزارش می‌شود. باید اطمینان حاصل کنید که این تعداد با تعداد نخ‌های سیستم عاملی که در محیط کانتینر دارید مطابقت دارد. اگر اعداد مطابقت نداشته باشند، برنامه Go به عنوان خواهد بود که بدون بهینه‌سازی اجرا می‌شود. ممکن است بخواهید از متغیر محیطی یا این تماس برای تطابق استفاده کنید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc main() {\n var wg sync.WaitGroup\n wg.Add(2)\n\n go func() {\n lowercase()\n wg.Done()\n }()\n\n go func() {\n uppercase()\n wg.Done()\n }()\n\n fmt.Println(\u0026#34;Waiting To Finish\u0026#34;)\n wg.Wait()\n\n fmt.Println(\u0026#34;\\nTerminating Program\u0026#34;)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این برنامه باید یک مشکل هماهنگی را حل کند. گوروتین اصلی نمی‌تواند به تابع اصلی اجازه دهد تا تا زمانی که دو گوروتین ایجاد شده خودشان کار خود را به اتمام نرسانند، بازگردد. WaitGroup ابزاری مناسب برای مسائل هماهنگی است که نیازی به انتقال داده بین گوروتین‌ها ندارند. ارسال سیگنال در اینجا از طریق یک API انجام می‌شود که به یک گوروتین اجازه می‌دهد منتظر شدن از سایر گوروتین‌ها برای اعلام پایان کار خود باشد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n در این کد، یک WaitGroup به حالت مقدار صفر ایجاد شده و سپس به طور فوری متد Add فراخوانی می‌شود تا مقدار WaitGroup به 2 تنظیم شود که با تعداد گوروتین‌هایی که باید ایجاد شوند مطابقت خواهد داشت. وقتی می‌دانید که چند گوروتین به صورت پیش‌فرض ایجاد می‌شوند، باید یک بار با آن تعداد Add را فراخوانی کنید. وقتی نمی‌دانید (مثل در یک سرویس استریمینگ)، آنگاه فراخوانی Add(1) قابل قبول است.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n در پایان تابع main، فراخوانی به Wait وجود دارد. Wait گوروتین اصلی را متوقف می‌کند تا از بازگشت تابع اصلی جلوگیری شود. وقتی تابع اصلی بازگردد، برنامه Go با حذف سریع بسته می‌شود. به همین دلیل مدیریت هماهنگی با تضمینات مناسب مهم است. تماس Wait تا زمانی که مقدار WaitGroup به 0 تنظیم شود بلاک می‌کند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n در وسط برنامه، شما دو گوروتین ایجاد کرده‌اید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc main() {\n . . .\n\n go func() {\n lowercase()\n wg.Done()\n }()\n\n go func() {\n uppercase()\n wg.Done()\n }()\n\n . . .\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n توابع متنی با استفاده از کلمه کلیدی go اعلام و اجرا می‌شوند. در این نقطه، به زمانبند Go می‌گویید که این توابع را به صورت موازی اجرا کند. برای اجرای آنها به ترتیبی نامشخص. در داخل اجرای هر گوروتین، فراخوانی Done وجود دارد. این فراخوانی باعث کاهش مقدار WaitGroup به مقدار 1 می‌شود. یک بار که هر دو فراخوانی به Done انجام شود، مقدار WaitGroup از 2 به 0 تغییر خواهد کرد و سپس گوروتین اصلی از فراخوانی Wait باز می‌شود و برنامه خاتمه می‌یابد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc main() {\n var wg sync.WaitGroup\n wg.Add(2)\n\n go func() {\n lowercase()\n wg.Done()\n }()\n\n . . .\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n یک بخش مهم از این الگوی ارکستراسیون نگه داشتن تماس‌های Add و Done در دید یکسان است. سعی کنید وقتی تماس‌ها انجام می‌شوند، WaitGroup را به عنوان پارامتر تابع منتقل نکنید که تماس‌ها گم شوند. این کمک می‌کند تا خطاها کاهش پیدا کنند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eخروجی:\n\nشروع گوروتین‌ها\nدر انتظار پایان\nA B C D E F G H I J K L M N O P Q R S T U V W X Y Z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z\nخاتمه برنامه\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n وقتی این برنامه را بسازید و اجرا کنید، مشاهده می‌کنید که این برنامه به صورت موازی اجرا می‌شود. گوروتین دومی که ایجاد شده، ابتدا زمان‌بندی شد. این اجازه را داد تا کار خود را به اتمام برساند و سپس گوروتین دیگر اجرا شد. هر دو به پایان رسیدند پیش از اینکه برنامه خاتمه یابد. بار دیگر که این برنامه را اجرا می‌کنید، هیچ تضمینی برای مشاهده خروجی یکسان وجود ندارد. تنها تضمین در این برنامه این است که برنامه تا زمانی که دو گوروتین به اتمام نرسیده‌اند، خاتمه نخواهد یافت.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n حتی اگر این برنامه را 100 بار اجرا کنید و همان خروجی را مشاهده کنید، تضمینی برای تکرار این موضوع وجود ندارد. این ممکن است بسیار احتمالی باشد، اما تضمین نشده است، به ویژه در بین نسخه‌ها، سیستم‌عامل‌ها و معماری‌های مختلف.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc main() {\n . . .\n\n fmt.Println(\u0026#34;Waiting To Finish\u0026#34;)\n // wg.Wait() \u0026lt;-- CHANGED\n\n fmt.Println(\u0026#34;\\nTerminating Program\u0026#34;)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n اگر تماس به \u003ccode\u003eWait\u003c/code\u003e را کامنت کنید، چه اتفاقی می‌افتد وقتی این برنامه را اجرا می‌کنید؟ دوباره، دیگر هیچ تضمینی در مورد اتفاقات وجود ندارد، اما احتمالات مختلفی وجود دارد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n این برنامه ممکن است مانند قبل عمل کند، زیرا تماس‌ها به \u003ccode\u003ePrintln\u003c/code\u003e تماس‌های سیستمی هستند که به اجرای تبادل سیاق اجازه می‌دهند. این برنامه ممکن است تنها یکی از دو گوروتین را اجرا کند یا شاید فوراً پایان یابد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc main() {\n var wg sync.WaitGroup\n wg.Add(2)\n\n go func() {\n lowercase()\n // wg.Done() \u0026lt;-- CHANGED\n }()\n\n . . .\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n اگر در یکی از گوروتین‌ها فراموش کنید که تابع \u003ccode\u003eDone\u003c/code\u003e را فراخوانی کنید، چه اتفاقی می‌افتد؟ در این صورت، برنامه به دلیل عدم توانایی تعداد \u003ccode\u003eWaitGroup\u003c/code\u003e برگشت به 0، به وقوع می‌پیوندد. تماس \u003ccode\u003eWait\u003c/code\u003e به طور بی‌پایان مسدود خواهد شد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eخروجی:\n\nStart Goroutines\nWaiting To Finish\nA B C D E F G H I J K L M N O P Q R S T U V W X Y Z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z\nfatal error: all goroutines are asleep - deadlock!\n\ngoroutine 1 [semacquire]:\nsync.runtime_Semacquire(0xc00001a0a8)\n /usr/local/go/src/runtime/sema.go:56 \u0026#43;0x45\nsync.(*WaitGroup).Wait(0xc00001a0a0)\n /usr/local/go/src/sync/waitgroup.go:130 \u0026#43;0x65\nmain.main()\n concurrency/goroutines/example1/example1.go:42 \u0026#43;0x145\nexit status 2\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n می‌توانید ببینید که زمانی که تماس به \u003ccode\u003eWait\u003c/code\u003e در خط 42 رخ می‌دهد، Go Runtime تشخیص می‌دهد که برنامه در وضعیت مسدود شده (deadlock) قرار دارد. با این حال، باید به خاطر داشته باشید که تشخیص مسدودیت انجام شده تنها زمانی صورت می‌پذیرد که همه‌ی گوروتین‌ها مسدود شده و هیچ راهی برای خروج از آنها وجود نداشته باشد. این نشان می‌دهد چرا اهمیت دارد که تماس‌های \u003ccode\u003eAdd\u003c/code\u003e و \u003ccode\u003eDone\u003c/code\u003e را در کنار هم نگه دارید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc main() {\n var wg sync.WaitGroup\n wg.Add(1) \u0026lt;-- CHANGED, Number Too Small\n\n go func() {\n lowercase()\n wg.Done()\n }()\n\n go func() {\n uppercase()\n wg.Done()\n }()\n\n . . .\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n اگر تعداد نادرستی از گوروتین‌ها به \u003ccode\u003eWaitGroup\u003c/code\u003e دهید چه اتفاقی می‌افتد؟ اگر تعداد زیادی باشد، ممکن است یک مسدودیت دیگر داشته باشید. اگر تعداد کم باشد، تضمینی وجود ندارد که کار قبل از ادامه برنامه انجام شود. خروجی برنامه تعریف نشده است.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eبرنامه‌ریزی ممنوع\u003c/h2\u003e\n \n \n \u003cp\u003e\n اگرچه برنامه‌ریز در داخل محدوده برنامه اجرا می‌شود، مهم است که ببینیم برنامه‌ریزی چگونه ممنوع (preemptive) است. این به معنای نمی‌توانید پیش‌بینی کنید کی یک تعویض متن رخ خواهد داد و این در هر بار اجرای برنامه تغییر می‌کند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc main() {\n var wg sync.WaitGroup\n wg.Add(2)\n\n go func() {\n printHashes(\u0026#34;A\u0026#34;)\n wg.Done()\n }()\n\n go func() {\n printHashes(\u0026#34;B\u0026#34;)\n wg.Done()\n }()\n\n fmt.Println(\u0026#34;Waiting To Finish\u0026#34;)\n wg.Wait()\n\n fmt.Println(\u0026#34;\\nTerminating Program\u0026#34;)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n با استفاده از الگوی هماهنگی مشابه به قبل، این برنامه باعث می‌شود که هر گوروتین کار بیشتری انجام دهد. کاری که برنامه‌ریز به یک گوروتین اجازه تکمیل آن در یک تایم‌اسلایس نخواهد داد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc printHashes(prefix string) {\n for i := 1; i \u0026lt;= 50000; i\u0026#43;\u0026#43; {\n num := strconv.Itoa(i)\n sum := sha1.Sum([]byte(num))\n fmt.Printf(\u0026#34;%s: %05d: %x\\n\u0026#34;, prefix, i, sum)\n }\n fmt.Println(\u0026#34;Completed\u0026#34;, prefix)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این تابع کارهای زیادی که به ورودی-خروجی مربوط می‌شود را انجام می‌دهد و این انجام این کارها احتمالاً باعث تغییر کانتکست می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e$ ./example2 | cut -c1 | grep \u0026#39;[AB]\u0026#39; | uniq\nB\nA\nB\nA\nB\nA\nB\nA\nB\nA 9 Context Switches\n\n$ ./example2 | cut -c1 | grep \u0026#39;[AB]\u0026#39; | uniq\nB\nA\nB\nA 3 Context Switches\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n همانطور که می‌بینید، هر بار که برنامه را اجرا می‌کنید، تعداد متفاوتی از تغییرات کانتکست وقوع می‌کند. این چیزی عالی است چرا که یک برنامه زمان‌بندی نباید قابل پیش‌بینی باشد. همزمانی باید به عنوان یک چیز نامعلوم باقی بماند و شما باید به خاطر داشته باشید که وقتی از همزمانی برای حل مشکلات عملکرد استفاده می‌کنید، این ویژگی برای شما حفظ شود.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc init() {\n runtime.GOMAXPROCS(2)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n اگر به برنامه اصلی برگشته و از GOMAXPROCS استفاده کنید تا برنامه به عنوان یک برنامه Go دو نخی اجرا شود، چه اتفاقی می‌افتد؟\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eخروجی:\n\nStart Goroutines\nWaiting To Finish\nA B C D E F G H I J K L M N O P Q R S T U V W X Y Z A B C D E F G H I J K L M N a b c d e f g h i j k l m n o O P Q R S T U V W X Y Z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z\nTerminating Program\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n آنچه می‌بینید این است که هم اکنون همگرایی برنامه با دقت بیشتری انجام می‌شود. خروجی حروف به ترتیب تعریف نشده و به ترتیب اجرا نمی‌شود.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eیادداشت‌ها\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003eگوروتین‌ها توابعی هستند که برنامه‌هایی هستند که برای اجرا به صورت مستقل برنامه‌ریزی می‌شوند.\u003c/li\u003e\n \n \u003cli\u003eما همیشه باید حساب کرد که گوروتین‌های در حال اجرا را به دقت نگه داریم و به‌طور منظم خاتمه دهیم.\u003c/li\u003e\n \n \u003cli\u003eهمگرایی و هم‌پردازی مفاهیم متفاوتی هستند.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \u003cul\u003e\n \n \u003cli\u003eهمگرایی در مورد مقابله با چندین چیز در یک زمان است.\u003c/li\u003e\n \n \u003cli\u003eهم‌پردازی در مورد انجام چندین کار به یک زمان است.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n \u0026#34;هم‌پردازی به معنای انجام فیزیکی دو یا چند کار به یک زمان است. همگرایی به معنای اجرای تعریف‌نشده و به ترتیب اجرا نمی‌شود.\u0026#34; - ویلیام کندی\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u0026#34;به صورت پیش‌فرض، گوروتین‌ها نباید بعد از تابعی که از آن ایجاد شده‌اند به پایان برسند. این باعث می‌شود شما در یک وضعیت طراحی بسیار خوب قرار گیرید.\u0026#34; - پیتر بورگون\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eرهنمودهای طراحی\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003eدرباره \u003ca href=\"https://github.com/ardanlabs/gotraining/blob/master/topics/go/#concurrent-software-design\" target=\"_blank\"\u003eرهنمودهای طراحی\u003c/a\u003e هم‌پردازی بیشتری بیاموزید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eمطالب اضافی\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2018/08/scheduling-in-go-part1.html\" target=\"_blank\"\u003eScheduling In Go - Part I\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2018/08/scheduling-in-go-part2.html\" target=\"_blank\"\u003eScheduling In Go - Part II\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2015/02/scheduler-tracing-in-go.html\" target=\"_blank\"\u003eScheduler Tracing In Go\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://blog.golang.org/advanced-go-concurrency-patterns\" target=\"_blank\"\u003eAdvanced Go Concurrency Patterns\u003c/a\u003e - Sameer Ajmani \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://blog.golang.org/context\" target=\"_blank\"\u003eGo Concurrency Patterns: Context\u003c/a\u003e - Sameer Ajmani \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://blog.golang.org/concurrency-is-not-parallelism\" target=\"_blank\"\u003eConcurrency is not parallelism\u003c/a\u003e - Rob Pike \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://talks.golang.org/2013/distsys.slide\" target=\"_blank\"\u003eGo, for Distributed Systems\u003c/a\u003e - Russ Cox \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://docs.google.com/document/d/1At2Ls5_fhJQ59kDK2DFVhFu3g5mATSXqqV5QrxinasI/edit\" target=\"_blank\"\u003eGo 1.5 GOMAXPROCS Default\u003c/a\u003e \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2014/01/concurrency-goroutines-and-gomaxprocs.html\" target=\"_blank\"\u003eConcurrency, Goroutines and GOMAXPROCS\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"http://www.ece.ubc.ca/~sasha/papers/eurosys16-final29.pdf\" target=\"_blank\"\u003eThe Linux Scheduler: a Decade of Wasted Cores\u003c/a\u003e \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://news.ycombinator.com/item?id=12460807\" target=\"_blank\"\u003eExplanation of the Scheduler\u003c/a\u003e \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"http://joeduffyblog.com/2016/11/30/15-years-of-concurrency/\" target=\"_blank\"\u003e15 Years of Concurrency\u003c/a\u003e - Joe Duffy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.quora.com/How-does-the-golang-scheduler-work/answer/Ian-Lance-Taylor\" target=\"_blank\"\u003eHow does the golang scheduler work?\u003c/a\u003e - Ian Lance Taylor \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.youtube.com/watch?v=YHRO5WQGh0k\" target=\"_blank\"\u003eThe Scheduler Saga\u003c/a\u003e - Kavya Joshi \u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eتمرین 1\u003c/h2\u003e\n \n \n \u003cp\u003e\n \u003cb\u003eقسمت\u003c/b\u003e \u003cb\u003eالف\u003c/b\u003e یک برنامه ایجاد کنید که دو تابع ناشناس اعلام کند. یکی که از ۱۰۰ به ۰ شماره برمی‌شود و دیگری که از ۰ به ۱۰۰ شماره می‌برد. هر عدد را با یک شناسه منحصر به فرد برای هر گوروتین نمایش دهید. سپس گوروتین‌ها را از این توابع ایجاد کنید و به main اجازه بازگشت ندهید تا گوروتین‌ها کامل شوند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u003cb\u003eقسمت\u003c/b\u003e \u003cb\u003eب\u003c/b\u003e برنامه را به صورت موازی اجرا کنید.\n \u003c/p\u003e\n \n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n","Files":[{"Name":"example1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to create goroutines and\n// how the scheduler behaves.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n\t\"sync\"\n)\n\nfunc init() {\n\n\t// Allocate one logical processor for the scheduler to use.\n\truntime.GOMAXPROCS(1)\n}\n\nfunc main() {\n\n\t// wg is used to manage concurrency.\n\tvar wg sync.WaitGroup\n\twg.Add(2)\n\n\tfmt.Println(\"Start Goroutines\")\n\n\t// Create a goroutine from the lowercase function.\n\tgo func() {\n\t\tlowercase()\n\t\twg.Done()\n\t}()\n\n\t// Create a goroutine from the uppercase function.\n\tgo func() {\n\t\tuppercase()\n\t\twg.Done()\n\t}()\n\n\t// Wait for the goroutines to finish.\n\tfmt.Println(\"Waiting To Finish\")\n\twg.Wait()\n\n\tfmt.Println(\"\\nTerminating Program\")\n}\n\n// lowercase displays the set of lowercase letters three times.\nfunc lowercase() {\n\n\t// Display the alphabet three times\n\tfor count := 0; count \u003c 3; count++ {\n\t\tfor r := 'a'; r \u003c= 'z'; r++ {\n\t\t\tfmt.Printf(\"%c \", r)\n\t\t}\n\t}\n}\n\n// uppercase displays the set of uppercase letters three times.\nfunc uppercase() {\n\n\t// Display the alphabet three times\n\tfor count := 0; count \u003c 3; count++ {\n\t\tfor r := 'A'; r \u003c= 'Z'; r++ {\n\t\t\tfmt.Printf(\"%c \", r)\n\t\t}\n\t}\n}\n","Hash":"RBkebacNV6jryrcGxd2wp1bPmdI="},{"Name":"example2.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// $ ./example2 | cut -c1 | grep '[AB]' | uniq\n\n// Sample program to show how the goroutine scheduler\n// will time slice goroutines on a single thread.\npackage main\n\nimport (\n\t\"crypto/sha1\"\n\t\"fmt\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"sync\"\n)\n\nfunc init() {\n\n\t// Allocate one logical processor for the scheduler to use.\n\truntime.GOMAXPROCS(1)\n}\n\nfunc main() {\n\n\t// wg is used to manage concurrency.\n\tvar wg sync.WaitGroup\n\twg.Add(2)\n\n\tfmt.Println(\"Create Goroutines\")\n\n\t// Create the first goroutine and manage its lifecycle here.\n\tgo func() {\n\t\tprintHashes(\"A\")\n\t\twg.Done()\n\t}()\n\n\t// Create the second goroutine and manage its lifecycle here.\n\tgo func() {\n\t\tprintHashes(\"B\")\n\t\twg.Done()\n\t}()\n\n\t// Wait for the goroutines to finish.\n\tfmt.Println(\"Waiting To Finish\")\n\twg.Wait()\n\n\tfmt.Println(\"Terminating Program\")\n}\n\n// printHashes calculates the sha1 hash for a range of\n// numbers and prints each in hex encoding.\nfunc printHashes(prefix string) {\n\n\t// print each has from 1 to 10. Change this to 50000 and\n\t// see how the scheduler behaves.\n\tfor i := 1; i \u003c= 50000; i++ {\n\n\t\t// Convert i to a string.\n\t\tnum := strconv.Itoa(i)\n\n\t\t// Calculate hash for string num.\n\t\tsum := sha1.Sum([]byte(num))\n\n\t\t// Print prefix: 5-digit-number: hex encoded hash\n\t\tfmt.Printf(\"%s: %05d: %x\\n\", prefix, i, sum)\n\t}\n\n\tfmt.Println(\"Completed\", prefix)\n}\n","Hash":"Pnx/XFZipGeRP6kyw0DTx0LIlqA="},{"Name":"example3.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to create goroutines and\n// how the goroutine scheduler behaves with two contexts.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n\t\"sync\"\n)\n\nfunc init() {\n\n\t// Allocate two logical processors for the scheduler to use.\n\truntime.GOMAXPROCS(2)\n}\n\nfunc main() {\n\n\t// wg is used to wait for the program to finish.\n\t// Add a count of two, one for each goroutine.\n\tvar wg sync.WaitGroup\n\twg.Add(2)\n\n\tfmt.Println(\"Start Goroutines\")\n\n\t// Declare an anonymous function and create a goroutine.\n\tgo func() {\n\n\t\t// Display the alphabet three times.\n\t\tfor count := 0; count \u003c 3; count++ {\n\t\t\tfor r := 'a'; r \u003c= 'z'; r++ {\n\t\t\t\tfmt.Printf(\"%c \", r)\n\t\t\t}\n\t\t}\n\n\t\t// Tell main we are done.\n\t\twg.Done()\n\t}()\n\n\t// Declare an anonymous function and create a goroutine.\n\tgo func() {\n\n\t\t// Display the alphabet three times.\n\t\tfor count := 0; count \u003c 3; count++ {\n\t\t\tfor r := 'A'; r \u003c= 'Z'; r++ {\n\t\t\t\tfmt.Printf(\"%c \", r)\n\t\t\t}\n\t\t}\n\n\t\t// Tell main we are done.\n\t\twg.Done()\n\t}()\n\n\t// Wait for the goroutines to finish.\n\tfmt.Println(\"Waiting To Finish\")\n\twg.Wait()\n\n\tfmt.Println(\"\\nTerminating Program\")\n}\n","Hash":"PpFAV7bqjIDOGzz7fbXXGTMRjvo="},{"Name":"exercise1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Create a program that declares two anonymous functions. One that counts down from\n// 100 to 0 and one that counts up from 0 to 100. Display each number with an\n// unique identifier for each goroutine. Then create goroutines from these functions\n// and don't let main return until the goroutines complete.\n//\n// Run the program in parallel.\npackage main\n\n// Add imports.\nimport \"runtime\"\n\nfunc init() {\n\n\t// Allocate one logical processor for the scheduler to use.\n\truntime.GOMAXPROCS(1)\n}\n\nfunc main() {\n\n\t// Declare a wait group and set the count to two.\n\n\t// Declare an anonymous function and create a goroutine.\n\t{\n\t\t// Declare a loop that counts down from 100 to 0 and\n\t\t// display each value.\n\n\t\t// Tell main we are done.\n\t}\n\n\t// Declare an anonymous function and create a goroutine.\n\t{\n\t\t// Declare a loop that counts up from 0 to 100 and\n\t\t// display each value.\n\n\t\t// Tell main we are done.\n\t}\n\n\t// Wait for the goroutines to finish.\n\n\t// Display \"Terminating Program\".\n}\n","Hash":"Aax+V6i/e9lPdY/epDpuTQq0S8I="},{"Name":"answer1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Create a program that declares two anonymous functions. One that counts down from\n// 100 to 0 and one that counts up from 0 to 100. Display each number with an\n// unique identifier for each goroutine. Then create goroutines from these functions\n// and don't let main return until the goroutines complete.\n//\n// Run the program in parallel.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n\t\"sync\"\n)\n\nfunc init() {\n\n\t// Allocate one logical processor for the scheduler to use.\n\truntime.GOMAXPROCS(1)\n}\n\nfunc main() {\n\n\t// Declare a wait group and set the count to two.\n\tvar wg sync.WaitGroup\n\twg.Add(2)\n\n\tfmt.Println(\"Start Goroutines\")\n\n\t// Declare an anonymous function and create a goroutine.\n\tgo func() {\n\t\t// Count down from 100 to 0.\n\t\tfor count := 100; count \u003e= 0; count-- {\n\t\t\tfmt.Printf(\"[A:%d]\\n\", count)\n\t\t}\n\n\t\t// Tell main we are done.\n\t\twg.Done()\n\t}()\n\n\t// Declare an anonymous function and create a goroutine.\n\tgo func() {\n\t\t// Count up from 0 to 100.\n\t\tfor count := 0; count \u003c= 100; count++ {\n\t\t\tfmt.Printf(\"[B:%d]\\n\", count)\n\t\t}\n\n\t\t// Tell main we are done.\n\t\twg.Done()\n\t}()\n\n\t// Wait for the goroutines to finish.\n\tfmt.Println(\"Waiting To Finish\")\n\twg.Wait()\n\n\t// Display \"Terminating Program\".\n\tfmt.Println(\"\\nTerminating Program\")\n}\n","Hash":"ZHF97i+9MwB5y7rZc/nXSyhw/40="}]}]} ,"algorithms-fun":{"Title":"مسائل سرگرم‌کننده","Description":"","Pages":[{"Title":"مسئله آرایشگر خواب‌آلود","Content":"\n \u003ch2\u003eمسئله آرایشگر خواب‌آلود\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/training/individual-on-demand/ultimate-go-bundle/\" target=\"_blank\"\u003eتماشای ویدئو\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003eنیاز به کمک مالی دارید؟ از \u003ca href=\"https://www.ardanlabs.com/scholarship/\" target=\"_blank\"\u003eفرم Scholarship\u003c/a\u003e ما استفاده کنید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n این برنامه نمونه‌ای از مسئله آرایشگر خواب‌آلود را پیاده‌سازی می‌کند.\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003eبرای اطلاعات بیشتر به این لینک مراجعه کنید: \u003ca href=\"https://en.wikipedia.org/wiki/Sleeping_barber_problem\" target=\"_blank\"\u003ehttps://en.wikipedia.org/wiki/Sleeping_barber_problem\u003c/a\u003e\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n در این مسئله آرایشگاه، یک آرایشگر، یک صندلی آرایشگر و \u003ccode\u003en\u003c/code\u003e صندلی برای مشتریان منتظر وجود دارد. اگر مشتریانی حاضر نباشند، آرایشگر در صندلی آرایشگر نشسته و خواب می‌کند. مشتری وارد می‌شود و باید آرایشگر را بیدار کند. مشتریانی که بعداً وارد می‌شوند، اگر صندلی ای منتظر باشد، یکی از صندلی‌های منتظر را اشغال می‌کنند و در غیر این صورت آرایشگاه را ترک می‌کنند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u003cb\u003eخروجی:\u003c/b\u003e\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eباز کردن آرایشگاه\nآرایشگر آماده به کار است\nمشتری \u0026#34;مشتری-1\u0026#34; به آرایشگاه وارد شد\nمشتری \u0026#34;مشتری-1\u0026#34; صندلی‌ای را انتخاب کرد و منتظر می‌ماند\nآرایشگر در حال خدمت‌دهی به مشتری \u0026#34;مشتری-1\u0026#34; است\nآرایشگر خدمت به مشتری \u0026#34;مشتری-1\u0026#34; را به اتمام رساند\nآرایشگر در حال خوابیدن است\nمشتری \u0026#34;مشتری-2\u0026#34; به آرایشگاه وارد شد\nمشتری \u0026#34;مشتری-2\u0026#34; صندلی‌ای را انتخاب کرد و منتظر می‌ماند\nآرایشگر در حال خدمت‌دهی به مشتری \u0026#34;مشتری-2\u0026#34; است\nمشتری \u0026#34;مشتری-3\u0026#34; به آرایشگاه وارد شد\nمشتری \u0026#34;مشتری-3\u0026#34; صندلی‌ای را انتخاب کرد و منتظر می‌ماند\nآرایشگر خدمت به مشتری \u0026#34;مشتری-2\u0026#34; را به اتمام رساند\nآرایشگر در حال خدمت‌دهی به مشتری \u0026#34;مشتری-3\u0026#34; است\nمشتری \u0026#34;مشتری-4\u0026#34; به آرایشگاه وارد شد\nمشتری \u0026#34;مشتری-4\u0026#34; صندلی‌ای را انتخاب کرد و منتظر می‌ماند\nبستن آرایشگاه\nآرایشگر خدمت به مشتری \u0026#34;مشتری-3\u0026#34; را به اتمام رساند\nآرایشگر در حال خدمت‌دهی به مشتری \u0026#34;مشتری-4\u0026#34; است\nآرایشگر خدمت به مشتری \u0026#34;مشتری-4\u0026#34; را به اتمام رساند\nآرایشگاه بسته شد\u003c/pre\u003e\n \n\n\t\n\t\t\n\t\n\n","Files":[{"Name":"barber.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This sample program shows you how to implement the sleeping barber\n// problem.\npackage main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"math/rand\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n)\n\nfunc main() {\n\tconst maxChairs = 3\n\n\tshop := OpenShop(maxChairs)\n\tdefer shop.Close()\n\n\t// Close the shop in 50 milliseconds.\n\tt := time.NewTimer(50 * time.Millisecond)\n\t\u003c-t.C\n}\n\n// =============================================================================\n\nvar (\n\t// ErrShopClosed is returned when the shop is closed.\n\tErrShopClosed = errors.New(\"shop closed\")\n\n\t// ErrNoChair is returned when all the chairs are occupied.\n\tErrNoChair = errors.New(\"no chair available\")\n)\n\n// customer represents a customer to be serviced.\ntype customer struct {\n\tname string\n}\n\n// Shop represents the barber's shop which contains chairs for customers\n// that customers can occupy and the barber can service. The shop can\n// be closed for business.\ntype Shop struct {\n\topen int32 // Determines if the shop is open for business.\n\tchairs chan customer // The set of chairs customers wait in.\n\twgClose sync.WaitGroup // Provides support for closing the shop.\n}\n\n// OpenShop creates a new shop for business and gets the barber working.\nfunc OpenShop(maxChairs int) *Shop {\n\tfmt.Println(\"Opening the shop\")\n\n\ts := Shop{\n\t\tchairs: make(chan customer, maxChairs),\n\t}\n\tatomic.StoreInt32(\u0026s.open, 1)\n\n\t// This is the barber and they will service customers.\n\n\ts.wgClose.Add(1)\n\tgo func() {\n\t\tdefer s.wgClose.Done()\n\n\t\tfmt.Println(\"Barber ready to work\")\n\n\t\tfor cust := range s.chairs {\n\t\t\ts.serviceCustomer(cust)\n\t\t}\n\t}()\n\n\t// Start creating customers who enter the shop.\n\n\tgo func() {\n\t\tvar id int64\n\n\t\tfor {\n\t\t\t// Wait some random time for the next customer to enter.\n\t\t\ttime.Sleep(time.Duration(rand.Intn(75)) * time.Millisecond)\n\n\t\t\tname := fmt.Sprintf(\"cust-%d\", atomic.AddInt64(\u0026id, 1))\n\t\t\tif err := s.newCustomer(name); err != nil {\n\t\t\t\tif err == ErrShopClosed {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}()\n\n\treturn \u0026s\n}\n\n// Close prevents any new customers from entering the shop and waits for\n// the barber to finish all existing customers.\nfunc (s *Shop) Close() {\n\tfmt.Println(\"Closing the shop\")\n\tdefer fmt.Println(\"Shop closed\")\n\n\t// Mark the shop closed.\n\tatomic.StoreInt32(\u0026s.open, 0)\n\n\t// Wait for the barber to finish with the existing customers.\n\tclose(s.chairs)\n\ts.wgClose.Wait()\n}\n\n// =============================================================================\n\nfunc (s *Shop) serviceCustomer(cust customer) {\n\tfmt.Printf(\"Barber servicing customer %q\\n\", cust.name)\n\n\ttime.Sleep(time.Duration(rand.Intn(50)) * time.Millisecond)\n\n\tfmt.Printf(\"Barber finished customer %q\\n\", cust.name)\n\n\tif len(s.chairs) == 0 \u0026\u0026 atomic.LoadInt32(\u0026s.open) == 1 {\n\t\tfmt.Println(\"Barber taking a nap\")\n\t}\n}\n\nfunc (s *Shop) newCustomer(name string) error {\n\tif atomic.LoadInt32(\u0026s.open) == 0 {\n\t\tfmt.Printf(\"Customer %q leaves, shop closed\\n\", name)\n\t\treturn ErrShopClosed\n\t}\n\n\tfmt.Printf(\"Customer %q entered shop\\n\", name)\n\n\tselect {\n\tcase s.chairs \u003c- customer{name: name}:\n\t\tfmt.Printf(\"Customer %q takes a seat and waits\\n\", name)\n\n\tdefault:\n\t\tfmt.Printf(\"Customer %q leaves, no seat\\n\", name)\n\t}\n\n\treturn nil\n}\n","Hash":"sK2TtWvYjjkEtc7R98KOX1+/1eY="}]},{"Title":"فرکانس","Content":"\n \u003ch2\u003eفرکانس\u003c/h2\u003e\n \n \n \u003cp\u003e\n این برنامه نمونه‌هایی ارائه می‌دهد که نشان می‌دهد چگونه یک تابع را پیاده‌سازی کنید که توانایی پیدا کردن فرکانس یک رون (کاراکتر) داده شده که در جمله مشخصی استفاده شده است، را داشته باشد.\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003eترتیبی: الگوریتم خطی برای شمارش رون‌ها.\u003c/li\u003e\n \n \u003cli\u003eهمزمان: الگوریتم همزمان برای انجام شمارش رون‌ها.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n","Files":[{"Name":"freq_sequential.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This sample program shows you how to implement a function that can\n// find the frequency a given rune is used in a specified sentence.\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\tsentence := `The quick brown fox jumps over the lazy dog Stay hungry.\n\tStay foolish Keep going. Be all in Boldness be my friend Screw it,\n\tlet's do it My life is my message Leave no stone unturned Dream big.\n\tPray bigger`\n\n\tprint(sequential(sentence))\n}\n\nfunc sequential(text string) map[rune]int {\n\tm := make(map[rune]int)\n\n\tfor _, r := range text {\n\t\tm[r]++\n\t}\n\n\treturn m\n}\n\nfunc print(m map[rune]int) {\n\tvar cols int\n\n\tfor r := 65; r \u003c 65+26; r++ {\n\t\tv := m[rune(r)]\n\t\tfmt.Printf(\"%q:%d, \", rune(r), v)\n\n\t\tv = m[rune(r+32)]\n\t\tfmt.Printf(\"%q:%d, \", rune(r+32), v)\n\n\t\tcols++\n\t\tif cols == 5 {\n\t\t\tfmt.Print(\"\\n\")\n\t\t\tcols = 0\n\t\t}\n\t}\n}\n","Hash":"NMpux3E8w78VCZ0C8rWqbN9Jgnw="},{"Name":"freq_concurrent.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This sample concurrent program shows you how to implement a function\n// that can find the frequency a given rune is used in a specified sentence.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n)\n\nfunc main() {\n\tsentence := `The quick brown fox jumps over the lazy dog Stay hungry.\n\tStay foolish Keep going. Be all in Boldness be my friend Screw it,\n\tlet's do it My life is my message Leave no stone unturned Dream big.\n\tPray bigger`\n\n\tprint(concurrent(sentence))\n}\n\nfunc concurrent(text string) map[rune]int {\n\tm := make(map[rune]int) // Map with final result\n\tg := runtime.GOMAXPROCS(0) // Number of goroutines\n\tl := len(text) // Number of bytes to process\n\tb := l / g // Number of buckets, one per goroutine\n\n\t// Receives the result of each bucket processed\n\t// by a goroutine.\n\tch := make(chan map[rune]int, g)\n\n\t// Create g number of goroutines.\n\n\tfor i := 0; i \u003c g; i++ {\n\t\tstr := i * b // Starting idx position of bucket\n\t\tend := str + b // Ending idx position of bucket\n\t\tif i == g-1 { // The last bucket gets ant remaining bytes\n\t\t\tend = end + (l - end)\n\t\t}\n\n\t\tgo func() {\n\t\t\tm := make(map[rune]int)\n\n\t\t\tdefer func() {\n\t\t\t\tch \u003c- m\n\t\t\t}()\n\n\t\t\t// This G processes its bucket sequentially.\n\t\t\tfor _, r := range text[str:end] {\n\t\t\t\tm[r]++\n\t\t\t}\n\t\t}()\n\t}\n\n\t// Wait for the results of each bucket to come\n\t// in and process them into the final map.\n\n\tfor i := 0; i \u003c g; i++ {\n\t\tresult := \u003c-ch\n\t\tfor rk, rv := range result {\n\t\t\tm[rk] = m[rk] + rv\n\t\t}\n\t}\n\n\treturn m\n}\n\nfunc print(m map[rune]int) {\n\tvar cols int\n\n\tfor r := 65; r \u003c 65+26; r++ {\n\t\tv := m[rune(r)]\n\t\tfmt.Printf(\"%q:%d, \", rune(r), v)\n\n\t\tv = m[rune(r+32)]\n\t\tfmt.Printf(\"%q:%d, \", rune(r+32), v)\n\n\t\tcols++\n\t\tif cols == 5 {\n\t\t\tfmt.Print(\"\\n\")\n\t\t\tcols = 0\n\t\t}\n\t}\n}\n","Hash":"UpS5seVg/m0LGLojYuxI8wtnuIU="}]},{"Title":"رمزگذاری/رمزگشایی متغیر طول","Content":"\n \u003ch2\u003eرمزگذاری/رمزگشایی متغیر طول\u003c/h2\u003e\n \n \n \u003cp\u003e\n این برنامه نمونه‌ای ارائه می‌دهد که نشان می‌دهد چگونه Go می‌تواند برای پیاده‌سازی رمزگذاری/رمزگشایی متغیر طول مورد استفاده قرار گیرد.\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003eبرای اطلاعات بیشتر به این لینک مراجعه کنید: \u003ca href=\"https://en.wikipedia.org/wiki/Variable-length_code\" target=\"_blank\"\u003ehttps://en.wikipedia.org/wiki/Variable-length_code\u003c/a\u003e\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n به طور خلاصه، هدف از این رمزگذاری این است که اعداد صحیح را به گونه‌ای کدگذاری کند که باعث صرفه‌جویی در بایت‌ها شود. تنها 7 بیت اول هر بایت معنی دارند (از راست چین؛ مشابه یک بایت ASCII). بنابراین، اگر یک مقدار 32 بیتی داشته باشید، باید آن را به سری بایت‌های 7 بیتی تجزیه کنید. البته، تعداد متغیری از بایت‌ها خواهید داشت که به مقدار صحیح شما بستگی دارد. برای نشان دادن کدام بایت آخرین بایت در سری است، باید بیت #7 را خاموش (0) بگذارید. در تمام بایت‌های پیشین، بایت #7 را روشن (1) می‌کنید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n بنابراین، اگر یک عدد صحیح بین 0 تا 127 باشد، می‌تواند به عنوان یک بایت نمایش داده شود. بزرگترین عدد مجاز برابر با 0FFFFFFF است که معادل 4 بایت متغیر طول می‌شود. در زیر نمونه‌هایی از مقادیر 32 بیتی به عنوان زمان‌های دلتا و مقادیر متغیر طولی که به آنها ترجمه می‌شوند آورده شده است:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eمتغیر تعداد عدد\u003c/pre\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e00000000 00\n00000040 40\n0000007F 7F\n00000080 81 00\n00002000 C0 00\n00003FFF FF 7F\n00004000 81 80 00\n00100000 C0 80 00\n001FFFFF FF FF 7F\n00200000 81 80 80 00\n08000000 C0 80 80 00\n0FFFFFFF FF FF FF 7F\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n یک متغیر تعداد (VLQ) کد یکانیورسال است که از تعداد خودسرانه‌ای از اکتت‌های دودویی (بایت‌های 8 بیتی) برای نمایش یک عدد صحیح به طور خودسرانه‌ای استفاده می‌کند. این کد برای استفاده در قالب فایل معمولی میدی استفاده شد تا برای یک سیستم با محدودیت منابع فضای اضافی ذخیره کند و همچنین در فرمت موسیقی قابل توسعه (XMF) نیز استفاده می‌شود. یک VLQ در اصل نمایش یک عدد صحیح بدون علامت با اساس 128 است با اضافه کردن بیت هشتم برای نشان دادن ادامه بایت‌ها. نمونه زیر را ببینید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eInt: 16384\nIntHex: 0x00004000\nIntBin: 00000000 00000000 01000000 00000000\nVLQHex: 0x81 0x80 0x00\nVLQBin: 00000000 10000001 10000000 00000000\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n بیایید فرض کنیم می‌خواهیم عدد 3435 را با استفاده از VLQ نمایش دهیم. عدد 3435 به صورت دودویی برابر با 110101101011 است. این را نمی‌توانیم به صورت کامل در یک بایت نمایش دهیم. بنابراین، می‌خواهیم آن را از انتها به صورت بلوک‌های 7 بیتی تقسیم کنیم.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eSeptet 7 6 5 4 3 2 1\n#1 1 1 0 1 0 1 1\n#2 0 0 1 1 0 1 0\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n حالا به همه‌ی بلوک‌ها علاوه بر آخرین بلوک، یک بیت 1 را در ابتدای آنها اضافه می‌کنیم تا نشان دهیم که یک اکتت پیگیری می‌شود و یک بیت 0 به آخرین بلوک اضافه می‌کنیم تا نشان دهیم که این آخرین اکتت است.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eOctet 8 7 6 5 4 3 2 1\n#1 0 1 1 0 1 0 1 1\n#2 1 0 0 1 1 0 1 0\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n سرانجام، این دو بلوک را به هم چسبانده، با اولین اکتت مهم‌ترین را ابتدا، به شکل زیر نمایش می‌دهیم:\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n Encoded: 10011010 01101011 ToHex: 0x9A 0x6B\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u003cb\u003eمنابع\u003c/b\u003e \u003cb\u003eاضافی:\u003c/b\u003e\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://en.wikipedia.org/wiki/Variable-length_quantity\" target=\"_blank\"\u003ehttps://en.wikipedia.org/wiki/Variable-length_quantity\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://blogs.infosupport.com/a-primer-on-vlq/\" target=\"_blank\"\u003ehttps://blogs.infosupport.com/a-primer-on-vlq/\u003c/a\u003e\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n \u003cb\u003eکنید\u003c/b\u003e \u003cb\u003eبرای\u003c/b\u003e \u003cb\u003eپیاده\u003c/b\u003e \u003cb\u003eسازی\u003c/b\u003e \u003cb\u003eعالی\u003c/b\u003e \u003cb\u003eاین\u003c/b\u003e \u003cb\u003eالگوریتم\u003c/b\u003e \u003cb\u003eبه\u003c/b\u003e \u003cb\u003eاینجا\u003c/b\u003e \u003cb\u003eنگاه:\u003c/b\u003e\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://github.com/go-audio/midi/blob/master/varint.go\" target=\"_blank\"\u003ehttps://github.com/go-audio/midi/blob/master/varint.go\u003c/a\u003e\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\t\n\t\t\n\t\n\n","Files":[{"Name":"vlq.go","Content":"package main\n\nimport (\n\t\"fmt\"\n\t\"math\"\n\t\"math/bits\"\n)\n\nfunc main() {\n\tinputs := [][]byte{\n\t\t[]byte{0x7F},\n\t\t[]byte{0x81, 0x00},\n\t\t[]byte{0xC0, 0x00},\n\t\t[]byte{0xFF, 0x7F},\n\t\t[]byte{0x81, 0x80, 0x00},\n\t\t[]byte{0xFF, 0xFF, 0x7F},\n\t\t[]byte{0x81, 0x80, 0x80, 0x00},\n\t\t[]byte{0xC0, 0x80, 0x80, 0x00},\n\t\t[]byte{0xFF, 0xFF, 0xFF, 0x7F},\n\t\t[]byte{0x82, 0x00},\n\t\t[]byte{0x81, 0x10},\n\t}\n\n\tfor _, input := range inputs {\n\t\tdecoded, err := DecodeVarint(input)\n\t\tif err != nil {\n\t\t\tfmt.Println(err)\n\t\t\treturn\n\t\t}\n\n\t\tencoded := EncodeVarint(decoded)\n\t\tfmt.Printf(\"input 0x%x, decoded: %d, encoded: 0x%x\\n\", input, decoded, encoded)\n\t}\n}\n\n// DecodeVarint takes a variable length VLQ based integer and\n// decodes it into a 32 bit integer.\nfunc DecodeVarint(input []byte) (uint32, error) {\n\tconst lastBitSet = 0x80 // 1000 0000\n\n\tvar d uint32\n\tvar bitPos int\n\n\tfor i := len(input) - 1; i \u003e= 0; i-- {\n\t\tn := uint8(input[i])\n\n\t\t// Process the first 7 bits and ignore the 8th.\n\t\tfor checkBit := 0; checkBit \u003c 7; checkBit++ {\n\n\t\t\t// Rotate the last bit off and move it to the back.\n\t\t\t// Before: 0000 0001\n\t\t\t// After: 1000 0000\n\t\t\tn = bits.RotateLeft8(n, -1)\n\n\t\t\t// Calculate based on only those 1 bits that were rotated.\n\t\t\t// Convert the bitPos to base 10.\n\t\t\tif n \u003e= lastBitSet {\n\t\t\t\tswitch {\n\t\t\t\tcase bitPos == 0:\n\t\t\t\t\td++\n\t\t\t\tdefault:\n\t\t\t\t\tbase10 := math.Pow(2, float64(bitPos))\n\t\t\t\t\td += uint32(base10)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Move the bit position.\n\t\t\tbitPos++\n\t\t}\n\t}\n\n\treturn d, nil\n}\n\n// EncodeVarint takes a 32 bit integer and encodes it into\n// a variable length VLQ based integer.\nfunc EncodeVarint(n uint32) []byte {\n\tconst maxBytes = 4\n\tconst eightBitSet = 0x80 // 1000 0000\n\tconst lastBitSet = 0x80000000 // 1000 0000 0000 0000\n\n\tencoded := make([]byte, maxBytes)\n\n\tfor bytePos := maxBytes - 1; bytePos \u003e= 0; bytePos-- {\n\t\tvar d uint8\n\n\t\t// Process the next 7 bits.\n\t\tfor checkBit := 0; checkBit \u003c 7; checkBit++ {\n\n\t\t\t// Rotate the last bit off and move it to the back.\n\t\t\t// Before: 0000 0000 0000 0001\n\t\t\t// After: 1000 0000 0000 0000\n\t\t\tn = bits.RotateLeft32(n, -1)\n\n\t\t\t// Calculate based on only those 1 bits that were\n\t\t\t// rotated. Convert the bit position to base 10.\n\t\t\tif n \u003e= lastBitSet {\n\t\t\t\tswitch {\n\t\t\t\tcase checkBit == 0:\n\t\t\t\t\td++\n\t\t\t\tdefault:\n\t\t\t\t\tbase10 := math.Pow(2, float64(checkBit))\n\t\t\t\t\td += uint8(base10)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// These values need the 8th bit to be set as 1.\n\t\tif bytePos \u003c 3 {\n\t\t\td += eightBitSet\n\t\t}\n\n\t\t// Store the value in reserve order.\n\t\tencoded[bytePos] = d\n\t}\n\n\t// Remove leading zero values by finding values that only\n\t// have their eight bit set.\n\tfor bytePos, b := range encoded {\n\t\tif b == eightBitSet {\n\t\t\tcontinue\n\t\t}\n\t\tencoded = encoded[bytePos:]\n\t\tbreak\n\t}\n\n\treturn encoded\n}\n","Hash":"pS5A986ELRif8kf7WHEzL/Wpo4M="}]}]} ,"constants":{"Title":"ثابت‌ها (constant)","Description":"یکی از ویژگی‌های منحصربه‌فرد Go، نحوه‌ی اجرای زبان برای ثوابت است.","Pages":[{"Title":"ثابت ها","Content":"\n \u003ch2\u003eثابت ها\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/training/individual-on-demand/ultimate-go-bundle/\" target=\"_blank\"\u003eتماشای ویدئو\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003eنیاز به کمک مالی دارید؟ از \u003ca href=\"https://www.ardanlabs.com/scholarship/\" target=\"_blank\"\u003eفرم Scholarship\u003c/a\u003e ما استفاده کنید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n یکی از ویژگی‌های منحصر به فرد Go، نحوه پیاده‌سازی ثوابت در زبان است. قوانین برای ثوابت در مشخصات زبان، به Go ویژگی‌های خاصی ارائه می‌دهند. این قوانین امکان ایجاد انعطاف‌پذیری برای Go را فراهم می‌کنند تا کدی که می‌نویسیم قابل خواندن و شفاف باشد در حالی که از نظر ایمنی نوع هم همچنان حفظ شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n ثوابت می‌توانند دارای نوع (typed) یا بی‌نوع (untyped) باشند. وقتی یک ثابت بی‌نوع باشد، به عنوان نوعی خاص محسوب می‌شود. ثوابتی که دارای نوع هستند، ممکن است توسط کامپایلر به صورت ضمنی تبدیل شوند. این همه در زمان کامپایل و نه در زمان اجرا رخ می‌دهد.\n \u003c/p\u003e\n \n\n \u003ch2\u003eبررسی کد\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e1:\u003c/b\u003e تعریف و مقداردهی ثابت ها\t\t\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e2:\u003c/b\u003e سیستم نوع موازی (Kind) (گم شده است)\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e3:\u003c/b\u003e iota\t\t\t\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e4:\u003c/b\u003e تبدیل ضمنی\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n \n \u003cpre class=\"codeblock\"\u003econst ui = 12345 // kind: integer\nconst uf = 3.141592 // kind: floating-point\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n ثوابت عددی بی‌نوع دقت 256 بیتی دارند که توسط مشخصات مشخص شده است. آنها بر اساس نوعی (kind) مبتنی هستند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003econst ti int = 12345 // type: int\nconst tf float64 = 3.141592 // type: float64\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n ثوابت دارای نوع هنوز از سیستم نوع ثابت استفاده می‌کنند، اما دقت آنها محدود شده است.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003econst myUint8 uint8 = 1000 // Compiler Error: constant 1000 overflows uint8\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این کار کار نمی‌کند زیرا عدد 1000 بزرگ‌تر از حداکثر مقدار قابل ذخیره‌سازی در یک uint8 است.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003evar answer = 3 * 0.333 // float64 = KindFloat(3) * KindFloat(0.333)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n حساب‌های ثابت پشتیبانی می‌کند از استفاده از انواع مختلف ثوابت. ترفیع نوع (Kind Promotion) برای مدیریت این حالت‌های مختلف استفاده می‌شود. همه این‌ها به صورت ضمنی رخ می‌دهد. متغیر پاسخ در این مثال از نوع float64 خواهد بود و مقدار 0.999 را با دقت 64 بیتی نمایان می‌کند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003econst third = 1 / 3.0 // KindFloat = KindFloat(1) / KindFloat(3.0)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n ثابت سوم از نوع float خواهد بود و مقدار 1/3 را با دقت 256 بیتی نمایان می‌کند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003econst zero = 1 / 3 // KindInt = KindInt(1) / KindInt(3)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n ثابت صفر از نوع integer خواهد بود و به مقدار 0 تنظیم می‌شود زیرا تقسیم integer هیچ باقی‌مانده‌ای ندارد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003econst one int8 = 1\nconst two = 2 * one // int8(2) * int8(1)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این یک مثال از حساب‌های ثابت بین ثوابت دارای نوع و ثوابت بی‌نوع است. در این مورد، یک ثابت از نوع به نوع دیگری ترفیع می‌یابد. دو ثابت از نوع int8 خواهند بود و به مقدار 2 تنظیم می‌شوند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003econst maxInt = 9223372036854775807\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این بیشترین مقدار ممکن برای یک عدد صحیح 64 بیتی است.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003econst bigger = 9223372036854775808543522345\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n ثابت بزرگتر، مقداری بسیار بزرگتر از یک عدد صحیح 64 بیتی است، اما می‌تواند در یک ثابت نوع int ذخیره شود زیرا ثوابت نوع int به 64 بیت دقت محدود نیستند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003econst bigger int64 = 9223372036854775808543522345\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خطای کامپایلر:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003econstant 9223372036854775808543522345 overflows int64\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n اما اگر ثابت bigger از نوع int64 باشد، این کد کامپایل نخواهد شد.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eIOTA\u003c/h2\u003e\n \n \n \u003cp\u003e\n IOTA امکان تنظیم ثوابت متوالی صحیح را فراهم می‌کند. ممکن است نام آن از تابع صحیح ⍳ از زبان برنامه‌نویسی APL آمده باشد. در APL، تابع ⍳ (که با حرف نهم الفبای یونانی، iota، نمایان می‌شود) برای ایجاد آرایه‌ای با مبنای صفر از اعداد صحیح متوالی و صعودی با طول مشخص استفاده می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003econst (\n A1 = iota // 0 : Start at 0\n B1 = iota // 1 : Increment by 1\n C1 = iota // 2 : Increment by 1\n)\nfmt.Println(A1, B1, C1)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e0 1 2\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n کلمه کلیدی iota در محدوده‌ی ثابت (constant block) کار می‌کند و با مقدار 0 شروع می‌شود. سپس برای هر ثابت متوالی که در محدوده اعلام شده در بلاک، iota یک واحد افزایش می‌یابد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003econst (\n A2 = iota // 0 : Start at 0\n B2 // 1 : Increment by 1\n C2 // 2 : Increment by 1\n)\nfmt.Println(A2, B2, C2)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e0 1 2\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n شما نیازی به تکرار استفاده از کلمه کلیدی iota ندارید. طبیعت متوالی ثوابت صحیح پس از استفاده یک بار فرض می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003econst (\n A3 = iota \u0026#43; 1 // 1 : 0 \u0026#43; 1\n B3 // 2 : 1 \u0026#43; 1\n C3 // 3 : 2 \u0026#43; 1\n)\nfmt.Println(A3, B3, C3)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e1 2 3\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n اگر نمی‌خواستید یک الگوی ریاضی اعمال کنید، می‌توانید محاسباتی انجام دهید و محاسبات با افزایش مقدار iota مجدداً اعمال می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003econst (\n Ldate= 1 \u0026lt;\u0026lt; iota // 1 : Shift 1 to the left 0. 0000 0001\n Ltime // 2 : Shift 1 to the left 1. 0000 0010\n Lmicroseconds // 4 : Shift 1 to the left 2. 0000 0100\n Llongfile // 8 : Shift 1 to the left 3. 0000 1000\n Lshortfile // 16 : Shift 1 to the left 4. 0001 0000\n LUTC // 32 : Shift 1 to the left 5. 0010 0000\n)\n\nfmt.Println(Ldate, Ltime, Lmicroseconds, Llongfile, Lshortfile, LUTC)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e1 2 4 8 16 32\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n شما می‌توانید از این ویژگی به مانند بسته‌ی Log برای تنظیم پرچم‌ها (flags) استفاده کنید. در این مورد، عملیات بیتی با افزایش مقادیر iota برای محاسبه مقادیر پرچم انجام می‌شود.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eیادداشت‌ها\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003eثوابت (constants) متغیرها نیستند.\u003c/li\u003e\n \n \u003cli\u003eآنها تنها در زمان کامپایل وجود دارند.\u003c/li\u003e\n \n \u003cli\u003eثوابت بی‌نوع می‌توانند به صورت ضمنی تبدیل شوند، در حالی که ثوابت و متغیرهای دارای نوع نمی‌توانند.\u003c/li\u003e\n \n \u003cli\u003eثوابت بی‌نوع را به عنوان داشتن نوع، نه داشتن نوع (Kind) در نظر بگیرید.\u003c/li\u003e\n \n \u003cli\u003eدر مورد تبدیل‌های صریح و ضمنی آشنا شوید.\u003c/li\u003e\n \n \u003cli\u003eقدرت ثوابت و کاربرد آنها در کتابخانه استاندارد را بیاموزید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cpre class=\"codeblock\"\u003e** خواندن اضافی\u003c/pre\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://golang.org/ref/spec#Constants\" target=\"_blank\"\u003eConstants specification\u003c/a\u003e \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://blog.golang.org/constants\" target=\"_blank\"\u003eConstants\u003c/a\u003e - Rob Pike \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2014/04/introduction-to-numeric-constants-in-go.html\" target=\"_blank\"\u003eIntroduction To Numeric Constants In Go\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n","Files":[{"Name":"example1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to declare constants and their\n// implementation in Go.\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\n\t// Constants live within the compiler.\n\t// They have a parallel type system.\n\t// Compiler can perform implicit conversions of untyped constants.\n\n\t// Untyped Constants.\n\tconst ui = 12345 // kind: integer\n\tconst uf = 3.141592 // kind: floating-point\n\n\t// Typed Constants still use the constant type system but their precision\n\t// is restricted.\n\tconst ti int = 12345 // type: int\n\tconst tf float64 = 3.141592 // type: float64\n\n\t// ./constants.go:XX: constant 1000 overflows uint8\n\t// const myUint8 uint8 = 1000\n\n\t// Constant arithmetic supports different kinds.\n\t// Kind Promotion is used to determine kind in these scenarios.\n\n\t// Variable answer will of type float64.\n\tvar answer = 3 * 0.333 // KindFloat(3) * KindFloat(0.333)\n\tfmt.Println(answer)\n\n\t// Constant third will be of kind floating point.\n\tconst third = 1 / 3.0 // KindFloat(1) / KindFloat(3.0)\n\tfmt.Println(third)\n\n\t// Constant zero will be of kind integer.\n\tconst zero = 1 / 3 // KindInt(1) / KindInt(3)\n\tfmt.Println(zero)\n\n\t// This is an example of constant arithmetic between typed and\n\t// untyped constants. Must have like types to perform math.\n\tconst one int8 = 1\n\tconst two = 2 * one // int8(2) * int8(1)\n\tfmt.Println(two)\n}\n","Hash":"N6BHVINKTeEG8qSHYJENmy/QyVc="},{"Name":"example2.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how constants do have a parallel type system.\npackage main\n\nimport \"fmt\"\n\nconst (\n\t// Max integer value on 64 bit architecture.\n\tmaxInt = 9223372036854775807\n\n\t// Much larger value than int64.\n\tbigger = 9223372036854775808543522345\n\n\t// Will NOT compile\n\t// biggerInt int64 = 9223372036854775808543522345\n)\n\nfunc main() {\n\tfmt.Println(\"Will Compile\")\n}\n","Hash":"xwdJNm1b7a9ykruakvMsW5PkERQ="},{"Name":"example3.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how iota works.\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\n\tconst (\n\t\tA1 = iota // 0 : Start at 0\n\t\tB1 = iota // 1 : Increment by 1\n\t\tC1 = iota // 2 : Increment by 1\n\t)\n\n\tfmt.Println(\"1:\", A1, B1, C1)\n\n\tconst (\n\t\tA2 = iota // 0 : Start at 0\n\t\tB2 // 1 : Increment by 1\n\t\tC2 // 2 : Increment by 1\n\t)\n\n\tfmt.Println(\"2:\", A2, B2, C2)\n\n\tconst (\n\t\tA3 = iota + 1 // 1 : Start at 0 + 1\n\t\tB3 // 2 : Increment by 1\n\t\tC3 // 3 : Increment by 1\n\t)\n\n\tfmt.Println(\"3:\", A3, B3, C3)\n\n\tconst (\n\t\tLdate = 1 \u003c\u003c iota // 1 : Shift 1 to the left 0. 0000 0001\n\t\tLtime // 2 : Shift 1 to the left 1. 0000 0010\n\t\tLmicroseconds // 4 : Shift 1 to the left 2. 0000 0100\n\t\tLlongfile // 8 : Shift 1 to the left 3. 0000 1000\n\t\tLshortfile // 16 : Shift 1 to the left 4. 0001 0000\n\t\tLUTC // 32 : Shift 1 to the left 5. 0010 0000\n\t)\n\n\tfmt.Println(\"Log:\", Ldate, Ltime, Lmicroseconds, Llongfile, Lshortfile, LUTC)\n}\n","Hash":"9BjwMctAu4zOmYCkZIYCjh7LiBg="},{"Name":"example4.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n/*\n// A Duration represents the elapsed time between two instants as\n// an int64 nanosecond count. The representation limits the largest\n// representable duration to approximately 290 years.\n\ntype Duration int64\n\n// Common durations. There is no definition for units of Day or larger\n// to avoid confusion across daylight savings time zone transitions.\n\nconst (\n Nanosecond Duration = 1\n Microsecond = 1000 * Nanosecond\n Millisecond = 1000 * Microsecond\n Second = 1000 * Millisecond\n Minute = 60 * Second\n Hour = 60 * Minute\n)\n\n// Add returns the time t+d.\nfunc (t Time) Add(d Duration) Time\n*/\n\n// Sample program to show how literal, constant and variables work\n// within the scope of implicit conversion.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc main() {\n\n\t// Use the time package to get the current date/time.\n\tnow := time.Now()\n\n\t// Subtract 5 nanoseconds from now using a literal constant.\n\tliteral := now.Add(-5)\n\n\t// Subtract 5 seconds from now using a declared constant.\n\tconst timeout = 5 * time.Second // time.Duration(5) * time.Duration(1000000000)\n\tconstant := now.Add(-timeout)\n\n\t// Subtract 5 nanoseconds from now using a variable of type int64.\n\tminusFive := int64(-5)\n\tvariable := now.Add(minusFive)\n\n\t// example4.go:50: cannot use minusFive (type int64) as type time.Duration in argument to now.Add\n\n\t// Display the values.\n\tfmt.Printf(\"Now : %v\\n\", now)\n\tfmt.Printf(\"Literal : %v\\n\", literal)\n\tfmt.Printf(\"Constant: %v\\n\", constant)\n\tfmt.Printf(\"Variable: %v\\n\", variable)\n}\n","Hash":"ZoSxgPKX92UME2fMkcUOCuRmyIQ="}]},{"Title":"تمرین‌ها","Content":"\n \u003ch2\u003eتمرین‌ها\u003c/h2\u003e\n \n \n \u003cp\u003e\n از الگو به عنوان نقطه شروع برای انجام تمرین‌ها استفاده کنید. یک راه حل ممکن ارائه شده است.\n \u003c/p\u003e\n \n\n \u003ch2\u003eتمرین 1\u003c/h2\u003e\n \n \n \u003cp\u003e\n \u003cb\u003eبخش\u003c/b\u003e \u003cb\u003eالف:\u003c/b\u003e یک ثابت بی‌نوع و یک ثابت دارای نوع تعریف کرده و مقادیر آنها را نمایش دهید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u003cb\u003eبخش\u003c/b\u003e \u003cb\u003eب:\u003c/b\u003e دو ثابت حرفی (literal constants) را به یک متغیر دارای نوع تقسیم کنید و مقدار آن را نمایش دهید.\n \u003c/p\u003e\n \n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n","Files":[{"Name":"exercise1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Declare an untyped and typed constant and display their values.\n//\n// Multiply two literal constants into a typed variable and display the value.\npackage main\n\n// Add imports.\n\nconst (\n// Declare a constant named server of kind string and assign a value.\n\n// Declare a constant named port of type integer and assign a value.\n)\n\nfunc main() {\n\n\t// Display the value of both server and port.\n\n\t// Divide a constant of kind integer and kind floating point and\n\t// assign the result to a variable.\n\n\t// Display the value of the variable.\n}\n","Hash":"jVS9BSi87w993JvmHUeIavJGIgU="},{"Name":"answer1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Declare an untyped and typed constant and display their values.\n//\n// Multiply two literal constants into a typed variable and display the value.\npackage main\n\nimport \"fmt\"\n\nconst (\n\t// server is the IP address for connecting.\n\tserver = \"124.53.24.123\"\n\n\t// port is the port to make that connection.\n\tport int16 = 9000\n)\n\nfunc main() {\n\n\t// Display the server information.\n\tfmt.Println(server)\n\tfmt.Println(port)\n\n\t// Calculate the number of minutes in 5320 seconds.\n\tminutes := 5320 / 60.0\n\tfmt.Println(minutes)\n}\n","Hash":"jMTAOpRhUCTuuH/d7d5mxVc5wh4="}]}]} ,"error-handling":{"Title":"مدیریت خطا","Description":"مدیریت خطا برای ساخت برنامه‌های شما قابل اعتماد، قابل اعتماد و محترمانه نسبت به کسانی که به آن‌ها وابسته هستند، حیاتی است.","Pages":[{"Title":"طراحی مدیریت خطا","Content":"\n \u003ch2\u003eطراحی مدیریت خطا\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/training/individual-on-demand/ultimate-go-bundle/\" target=\"_blank\"\u003eتماشای ویدئو\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003eنیاز به کمک مالی دارید؟ از \u003ca href=\"https://www.ardanlabs.com/scholarship/\" target=\"_blank\"\u003eفرم Scholarship\u003c/a\u003e ما استفاده کنید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n یکی از جنبه‌های مهمی در فرآیند مهندسی، صداقت است. در مرکز صداقت، مدیریت خطا قرار دارد. زمانی که به Go می‌آید، مدیریت خطا نباید یک استثناء باشد که بعداً یا در جای دیگری در کد مدیریت شود. این بخشی از مسیر اصلی است و باید یک تمرکز اصلی باشد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n توسعه‌دهندگان مسئولیت دارند که متن کافی در مورد هر خطایی را برگردانند تا کاربر بتواند تصمیم مطلعانه‌ای در مورد نحوه ادامه کار بگیرد. مدیریت یک خطا درباره سه چیز است: ثبت خطا، انتقال خطا به دیگرجاهای کد نشان ندهد، و تعیین کند که آیا Goroutine/برنامه باید متوقف شود یا خیر.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n در Go، خطاها فقط مقادیر هستند، بنابراین می‌توانند هر چیزی باشند که نیاز دارید. آنها می‌توانند هر وضعیت یا رفتاری را حفظ کنند.\n \u003c/p\u003e\n \n\n \u003ch2\u003eمرور کد\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e1:\u003c/b\u003e مقادیر خطای پیش‌فرض\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e2:\u003c/b\u003e متغیرهای خطا\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e3:\u003c/b\u003e Type As Context\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e4:\u003c/b\u003e رفتار به عنوان Context \u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e5:\u003c/b\u003e پیدا کردن باگ\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e6:\u003c/b\u003e پوشاندن خطاها با stdlib\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n \u003ch2\u003eمبانی مدیریت خطا\u003c/h2\u003e\n \n \n \u003cp\u003e\n رابط خطا به زبان داخلی تعبیه شده است.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e// https://golang.org/pkg/builtin/#error\ntype error interface {\n Error() string\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n دلیل این موضوع این است که به عنوان یک شناسه غیر قابل صدور به نظر می‌آید. هر مقدار محسوس که این رابط را پیاده‌سازی می‌کند، می‌تواند به عنوان مقدار خطا مورد استفاده قرار گیرد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n یک جنبه مهم در Go این است که مدیریت خطا از طریق این رابط در یک حالت جداگانه انجام می‌شود. دلیل اصلی این امر این است که مدیریت خطا یک جنبه از برنامه من است که بیشتر به تغییر و بهبود حساس است. این رابط نوعی است که برنامه‌های Go باید از آن به عنوان نوع بازگشتی برای مدیریت خطا استفاده کنند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e// https://golang.org/src/pkg/errors/errors.go\ntype errorString struct {\n s string\n}\n\n// https://golang.org/src/pkg/errors/errors.go\nfunc (e *errorString) Error() string {\n return e.s\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این ارزش خطای بیشترین استفاده در برنامه‌های Go است. در بسته errors از کتابخانه استاندارد اعلام شده است. توجه کنید که نوع آن غیر قابل صدور است و یک فیلد غیر قابل صدور دارد که یک رشته است. همچنین می‌توانید ببینید که نحوه استفاده از معنای اشاره‌گر برای پیاده‌سازی رابط خطا استفاده شده است. این به معنای این است که فقط آدرس‌ها به مقادیر این نوع می‌توانند درون رابط به اشتراک گذاشته و ذخیره شوند. این متد فقط رشته خطا را برمی‌گرداند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n مهم است به خاطر بیاورید که پیاده‌سازی متد Error به منظور پیاده‌سازی رابط و برای ثبت وقوع خطا استفاده می‌شود. اگر کاربری نیاز به تجزیه رشته ای که از این متد برگشت داده شده است داشته باشد، شما نتوانسته‌اید به کاربر مقدار مناسبی از متن را برای تصمیم اطلاع‌داری ارائه دهید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e// https://golang.org/src/pkg/errors/errors.go\nfunc New(text string) error {\n return \u0026amp;errorString{text}\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n تابع New نحوه ساخت یک خطا با استفاده از نوع محسوس errorString است. توجه داشته باشید که تابع خطا را با استفاده از رابط خطا برمی‌گرداند. همچنین توجه کنید که از معنای اشاره‌گر هم استفاده شده است.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc main() {\n if err := webCall(); err != nil {\n fmt.Println(err)\n return\n }\n fmt.Println(\u0026#34;Life is good\u0026#34;)\n}\n\nfunc webCall() error {\n return New(\u0026#34;bad request\u0026#34;)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n زمینه (Context) در مورد خطاها بسیار مهم است. هر خطا باید متن کافی را فراهم کند تا فراخواننده قادر به انجام تصمیم اطلاع‌داری در مورد وضعیت گوروتین/برنامه باشد. در این مثال، تابع webCall یک خطا با پیام \u0026#34;Bad Request\u0026#34; برمی‌گرداند. در تابع اصلی، یک فراخوانی به webCall انجام می‌شود و سپس یک بررسی انجام می‌شود تا ببینیم آیا در فراخوانی خطایی رخ داده است یا خیر.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eif err := webCall(); err != nil {\n fmt.Println(err)\n return\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n کلید برای بررسی شرط err != nil است. این شرط درخواست می‌کند که آیا یک مقدار محسوس درون مقدار رابط err ذخیره شده است یا نه. زمانی که مقدار رابط یک مقدار محسوس را ذخیره می‌کند، یک خطا وجود دارد. در این مورد، متن کافی به معنای واقعی کمی است که یک مقدار محسوس وجود دارد، اهمیتی ندارد که مقدار محسوس چیست.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n اگر اهمیت دارد که بدانیم چه مقدار خطایی درون متغیر رابط err وجود دارد، آنگاه متغیرهای خطا یک گزینه خوب هستند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003evar (\n ErrBadRequest = errors.New(\u0026#34;Bad Request\u0026#34;)\n ErrPageMoved = errors.New(\u0026#34;Page Moved\u0026#34;)\n)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n متغیرهای خطا یک مکانیزم را فراهم می‌کنند تا شناسایی کنند که کدام خطای خاص بازگردانده شده است. آنها دارای اصطلاحی به نام \u0026#34;Err\u0026#34; به عنوان پیشوند هستند و بر اساس نوع محسوس errorString از بسته errors ایجاد می‌شوند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc webCall(b bool) error {\n if b {\n return ErrBadRequest\n }\n return ErrPageMoved\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در این نسخه جدید تابع webCall، تابع یکی از متغیرهای خطا را بازگردانده یا دیگری را. این به فراخواننده اجازه می‌دهد تا تشخیص دهد کدام خطا رخ داده است.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc main() {\n if err := webCall(true); err != nil {\n switch err {\n case ErrBadRequest:\n fmt.Println(\u0026#34;Bad Request Occurred\u0026#34;)\n return\n\n case ErrPageMoved:\n fmt.Println(\u0026#34;The Page moved\u0026#34;)\n return\n\n default:\n fmt.Println(err)\n return\n }\n }\n\n fmt.Println(\u0026#34;Life is good\u0026#34;)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در این برنامه پس از انجام فراخوانی به webCall، می‌توان بررسی کرد تا ببینیم آیا یک مقدار محسوس درون متغیر رابط err ذخیره شده است یا نه. اگر وجود داشته باشد، سپس از یک دستور switch برای تشخیص کدام خطا با مقایسه err با متغیرهای خطای مختلف استفاده می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n در این مورد، متن خطا بر اساس اینکه کدام متغیر خطا بازگردانده شده است، تعیین می‌شود. اگر یک متغیر خطا کافی از متن نباشد، چه اگر نیاز به بررسی وضعیت ویژه‌ای مثل خطاهای شبکه داشته باشیم؟ در این موارد، یک نوع خطای محسوس سفارشی پاسخ مناسبی است.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype UnmarshalTypeError struct {\n Value string\n Type reflect.Type\n}\n\nfunc (e *UnmarshalTypeError) Error() string {\n return \u0026#34;json: cannot unmarshal \u0026#34; \u0026#43; e.Value \u0026#43;\n \u0026#34; into Go value of type \u0026#34; \u0026#43; e.Type.String()\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این یک نوع خطای محسوس سفارشی است که در بسته json پیاده‌سازی شده است. توجه کنید که نام دارای پسوند \u0026#34;Error\u0026#34; در نام‌گذاری نوع است. همچنین توجه کنید که از معنای اشاره‌گر برای پیاده‌سازی رابط خطا استفاده شده است. همچنین مانند دیگر پیاده‌سازی‌ها، برای ثبت وقوع خطا و نمایش اطلاعات در مورد تمامی فیلدهایی که ذخیره می‌شوند، استفاده می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype InvalidUnmarshalError struct {\n Type reflect.Type\n}\n\nfunc (e *InvalidUnmarshalError) Error() string {\n if e.Type == nil {\n return \u0026#34;json: Unmarshal(nil)\u0026#34;\n }\n if e.Type.Kind() != reflect.Ptr {\n return \u0026#34;json: Unmarshal(non-pointer \u0026#34; \u0026#43; e.Type.String() \u0026#43; \u0026#34;)\u0026#34;\n }\n return \u0026#34;json: Unmarshal(nil \u0026#34; \u0026#43; e.Type.String() \u0026#43; \u0026#34;)\u0026#34;\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این یک دومین نوع خطای محسوس سفارشی است که در بسته json یافت می‌شود. پیاده‌سازی متد Error یکم پیچیده‌تر است، اما همچنان فقط برای ثبت وقوع خطا و استفاده از معنای اشاره‌گر است.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc Unmarshal(data []byte, v interface{}) error {\n rv := reflect.ValueOf(v)\n if rv.Kind() != reflect.Ptr || rv.IsNil() {\n return \u0026amp;InvalidUnmarshalError{reflect.TypeOf(v)}\n }\n return \u0026amp;UnmarshalTypeError{\u0026#34;string\u0026#34;, reflect.TypeOf(v)}\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در اینجا یک قسمت از تابع Unmarshal نشان داده شده است. توجه کنید که چگونه مقادیر خطای محسوس محسوس در مقدار بازگشتی ساخته می‌شود و از طریق رابط خطا به فراخواننده منتقل می‌شود. ساختار معنای اشاره‌گر در اینجا استفاده می‌شود زیرا در تعریف متد Error از معنای اشاره‌گر استفاده شده است.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n زمینه خطا در اینجا بیشتر در مورد نوع خطا ذخیره شده درون رابط خطا است. باید یک راهی وجود داشته باشد تا بتوان تشخیص داد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc main() {\n var u user\n err := Unmarshal([]byte(`{\u0026#34;name\u0026#34;:\u0026#34;bill\u0026#34;}`), u)\n if err != nil {\n switch e := err.(type) {\n case *UnmarshalTypeError:\n fmt.Printf(\u0026#34;UnmarshalTypeError: Value[%s] Type[%v]\\n\u0026#34;,\n e.Value, e.Type)\n case *InvalidUnmarshalError:\n fmt.Printf(\u0026#34;InvalidUnmarshalError: Type[%v]\\n\u0026#34;, e.Type)\n default:\n fmt.Println(err)\n }\n return\n }\n fmt.Println(\u0026#34;Name:\u0026#34;, u.Name)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n یک تصدیق نوع عمومی در داخل دامنه دستور switch کد نویسی است که می‌توانید با آن کد برای آزمایش نوع مقداری که درون مقدار رابط err ذخیره شده است، بنویسید. در اینجا مفهوم زمینه (Context) است و اکنون می‌توانید با دسترسی به تمام وضعیت‌های خطا، آزمایش کنید و اقداماتی انجام دهید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n با این حال، این یک مشکل ایجاد می‌کند. دیگر من جدا نیستم از مقدار خطای محسوس. این بدان معناست که اگر مقدار خطای محسوس تغییر کند، کد من ممکن است خراب شود. قسمت زیبای استفاده از رابط برای مدیریت خطا، جدا شدن از تغییرات مخرب است.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n اگر مقدار خطای محسوس دارای یک مجموعه متد باشد، می‌توانید از یک رابط برای بررسی نوع استفاده کنید. به عنوان مثال، بسته net دارای بسیاری از انواع خطای محسوس مختلف است که متدهای مختلفی پیاده‌سازی می‌کنند. یکی از متد‌های معمولی به نام Temporary است. این متد به کاربر این امکان را می‌دهد که بررسی کند که آیا خطای شبکه بحرانی است یا فقط یک مشکل است که به تنهایی قابل بازیابی است.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype temporary interface {\n Temporary() bool\n}\n\nfunc (c *client) BehaviorAsContext() {\n for {\n line, err := c.reader.ReadString(\u0026#39;\\n\u0026#39;)\n if err != nil {\n switch e := err.(type) {\n case temporary:\n if !e.Temporary() {\n log.Println(\u0026#34;Temporary: Client leaving chat\u0026#34;)\n return\n }\n default:\n if err == io.EOF {\n log.Println(\u0026#34;EOF: Client leaving chat\u0026#34;)\n return\n }\n log.Println(\u0026#34;read-routine\u0026#34;, err)\n }\n }\n fmt.Println(line)\n }\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در این کد، فراخوانی ReadString ممکن است با یک خطا از بسته net شکست بخورد. در این مورد، یک رابط تعریف شده که نمایانگر رفتار مشترکی است که یک مقدار خطای محسوس خاص می‌تواند پیاده‌سازی کند. سپس با یک تصدیق نوع عمومی، شما آزمایش می‌کنید که آیا این رفتار وجود دارد و می‌توانید به آن دسترسی داشته باشید. بهترین بخش این است که در حالت جدا شده با مدیریت خطا باقی می‌مانید.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eهمیشه از رابط خطا استفاده کنید\u003c/h2\u003e\n \n \n \u003cp\u003e\n یک اشتباهی که توسعه‌دهندگان Go می‌توانند انجام دهند این است که از نوع خطای محسوس و نه رابط خطا برای نوع بازگشتی برای مدیریت خطا استفاده کنند. اگر چنین کنند، اتفاقات بدی ممکن است رخ دهد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype customError struct{}\n\nfunc (c *customError) Error() string {\n return \u0026#34;Find the bug.\u0026#34;\n}\n\nfunc fail() ([]byte, *customError) {\n return nil, nil\n}\n\nfunc main() {\n var err error\n if _, err = fail(); err != nil {\n log.Fatal(\u0026#34;Why did this fail?\u0026#34;)\n }\n log.Println(\u0026#34;No Error\u0026#34;)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n Output:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eچرا این کد فکر می‌کند که خطا وجود دارد؟\n\nچرا این کد فکر می‌کند که خطا وجود دارد وقتی تابع fail مقدار nil را برای خطا برمی‌گرداند؟ دلیل این امر این است که تابع fail از نوع خطای محسوس و نه رابط خطا استفاده می‌کند. در این مورد، یک اشاره‌گر nil از نوع customError درون متغیر err ذخیره شده است. این با یک مقدار رابطی nil از نوع error یکسان نیست.\u003c/pre\u003e\n \n\n\n \u003ch2\u003eمدیریت خطاها\u003c/h2\u003e\n \n \n \u003cp\u003e\n مدیریت خطاها بیشتر مورد مکالمه مهندسی سطح ماکرو است. در دنیای من، مدیریت خطا به معنای این است که خطا در تابعی که خطا را مدیریت می‌کند متوقف می‌شود، خطا با متن کامل ثبت می‌شود و خطا برای شدت آن بررسی می‌شود. براساس شدت و قابلیت بازیابی، تصمیمی برای بازیابی، ادامه دادن یا خاموش کردن گرفته می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n یک مشکل این است که همه توابع قادر به مدیریت خطا نیستند. یک دلیل ممکن این است که همه توابع مجاز به ثبت وقوع خطا نیستند. چه اتفاقی می‌افتد وقتی یک خطا به بالا در توده فراخوانی منتقل می‌شود و توسط تابعی که آن را دریافت می‌کند نمی‌تواند مدیریت شود؟ یک خطا باید با متنی که تابعی که در نهایت آن را مدیریت می‌کند، بتواند به درستی مدیریت کند، محاصره شود.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003epackage main\n\nimport (\n \u0026#34;errors\u0026#34;\n \u0026#34;fmt\u0026#34;\n)\n\ntype AppError struct {\n State int\n}\n\nfunc (ae *AppError) Error() string {\n return fmt.Sprintf(\u0026#34;App Error, State: %d\u0026#34;, ae.State)\n}\n\nfunc IsAppError(err error) bool {\n var ae *AppError\n return errors.As(err, \u0026amp;ae)\n}\n\nfunc GetAppError(err error) *AppError {\n var ae *AppError\n if !errors.As(err, \u0026amp;ae) {\n return nil\n }\n return ae\n}\n\nfunc main() {\n if err := firstCall(10); err != nil {\n\n // Check if the error is an AppError.\n if IsAppError(err) {\n ae := GetAppError(err)\n fmt.Printf(\u0026#34;Is AppError, State: %d\\n\u0026#34;, ae.State)\n }\n\n fmt.Print(\u0026#34;\\n********************************\\n\\n\u0026#34;)\n\n // Display the error using the implementation of\n // the error interface.\n fmt.Printf(\u0026#34;%v\\n\u0026#34;, err)\n }\n}\n\nfunc firstCall(i int) error {\n if err := secondCall(i); err != nil {\n return fmt.Errorf(\u0026#34;secondCall(%d) : %w\u0026#34;, i, err)\n }\n return nil\n}\n\nfunc secondCall(i int) error {\n return \u0026amp;AppError{99}\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eIs AppError, State: 99\n\n********************************\n\nsecondCall(10) : App Error, State: 99\u003c/pre\u003e\n \n\n\n \u003ch2\u003eیادداشت‌ها\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003eاز مقدار خطای پیش‌فرض برای پیام‌های استاتیک و ساده فرمت‌شده استفاده کنید.\u003c/li\u003e\n \n \u003cli\u003eایجاد و بازگرداندن متغیرهای خطا برای کمک به تمیزشناسی خطاهای خاص تماس‌گیرنده.\u003c/li\u003e\n \n \u003cli\u003eایجاد انواع خطای سفارشی زمانی مناسب است که زمینه خطا پیچیده‌تر باشد.\u003c/li\u003e\n \n \u003cli\u003eمقادیر خطا در Go ویژگی خاصی ندارند، بلکه همانند سایر مقادیر هستند و بنابراین شما تمام زبان را در دسترس دارید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eنقل قول‌ها\u003c/h2\u003e\n \n \n \u003cp\u003e\n \u0026#34;سیستم‌ها نمی‌توانند با فرض اینکه انسان‌ها قادر به نوشتن میلیون‌ها خط کد بدون اشتباه باشند، توسعه داده شوند، و تنها اشکال‌زدایی یک راه کار کارآمد برای توسعه سیستم‌های قابل اطمینان نیست.\u0026#34; - آل آهو (سازنده AWK)\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eمطالعات اضافی\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://go.dev/blog/error-handling-and-go\" target=\"_blank\"\u003eError handling and Go\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://go.dev/blog/go1.13-errors\" target=\"_blank\"\u003eWorking with Errors in Go 1.13\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2014/10/error-handling-in-go-part-i.html\" target=\"_blank\"\u003eError Handling In Go, Part I\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2014/11/error-handling-in-go-part-ii.html\" target=\"_blank\"\u003eError Handling In Go, Part II\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2017/05/design-philosophy-on-logging.html\" target=\"_blank\"\u003eDesign Philosophy On Logging\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://clipperhouse.com/bugs-are-a-failure-of-prediction/\" target=\"_blank\"\u003eBugs are a failure of prediction\u003c/a\u003e - Matt Sherman \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://dave.cheney.net/2014/12/24/inspecting-errors\" target=\"_blank\"\u003eInspecting errors\u003c/a\u003e - Dave Cheney \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://dave.cheney.net/2016/04/27/dont-just-check-errors-handle-them-gracefully\" target=\"_blank\"\u003eDon’t just check errors, handle them gracefully\u003c/a\u003e - Dave Cheney \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://dave.cheney.net/2016/06/12/stack-traces-and-the-errors-package\" target=\"_blank\"\u003eStack traces and the errors package\u003c/a\u003e - Dave Cheney \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://commandcenter.blogspot.com/2017/12/error-handling-in-upspin.html\" target=\"_blank\"\u003eError handling in Upspin\u003c/a\u003e - Rob Pike \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://rauljordan.com/why-go-error-handling-is-awesome/\" target=\"_blank\"\u003eWhy Go\u0026#39;s Error Handling is Awesome\u003c/a\u003e - Raul Jordan\n\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eتمرین‌ها\u003c/h2\u003e\n \n \n \u003cp\u003e\n از الگو به عنوان نقطه شروع برای تکمیل تمرین‌ها استفاده کنید. یک راه حل ممکن نیز ارائه شده است.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eتمرین 1\u003c/h2\u003e\n \n \n \u003cp\u003e\n دو متغیر خطایی بنام ErrInvalidValue و دیگری به نام ErrAmountTooLarge ایجاد کنید. پیام استاتیک مناسب را برای هر متغیر ارائه دهید. سپس یک تابع به نام checkAmount بنویسید که یک مقدار از نوع float64 را بپذیرد و یک مقدار خطا برگرداند. مقدار را برای صفر بودن بررسی کنید و اگر صفر باشد، ErrInvalidValue را برگردانید. مقدار را برای بیشتر از 1,000 دلار بودن بررسی کنید و اگر بله باشد، ErrAmountTooLarge را برگردانید. یک تابع اصلی بنویسید تا تابع checkAmount را فراخوانی کند و مقدار خطای برگشتی را بررسی کند. یک پیام مناسب را در صفحه نمایش نمایش دهید.\n \u003c/p\u003e\n \n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n \u003ch2\u003eتمرین 2\u003c/h2\u003e\n \n \n \u003cp\u003e\n یک نوع خطای سفارشی به نام appError ایجاد کنید که شامل سه فیلد باشد: err error، message string و code int. رابط خطا را پیاده‌سازی کرده و با استفاده از این سه فیلد پیام خود را ارائه دهید. یک متد دوم به نام temporary پیاده‌سازی کنید که وقتی مقدار فیلد code برابر با 9 باشد، مقدار false را برگرداند. تابعی به نام checkFlag بنویسید که یک مقدار bool بپذیرد. اگر مقدار false باشد، یک اشاره‌گر به نوع خطای سفارشی خود را با مقداری دلخواه ایجاد کرده و برگرداند. اگر مقدار true باشد، یک خطای پیش‌فرض برگردانید. یک تابع اصلی بنویسید تا تابع checkFlag را فراخوانی کرده و خطا را با استفاده از رابط temporary بررسی کند.\n \u003c/p\u003e\n \n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n","Files":[{"Name":"example1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how the default error type is implemented.\npackage main\n\nimport \"fmt\"\n\n// https://golang.org/pkg/builtin/#error\ntype error interface {\n\tError() string\n}\n\n// https://golang.org/src/pkg/errors/errors.go\ntype errorString struct {\n\ts string\n}\n\n// https://golang.org/src/pkg/errors/errors.go\nfunc (e *errorString) Error() string {\n\treturn e.s\n}\n\n// https://golang.org/src/pkg/errors/errors.go\n// New returns an error that formats as the given text.\nfunc New(text string) error {\n\treturn \u0026errorString{text}\n}\n\nfunc main() {\n\tif err := webCall(); err != nil {\n\t\tfmt.Println(err)\n\t\treturn\n\t}\n\n\tfmt.Println(\"Life is good\")\n}\n\n// webCall performs a web operation.\nfunc webCall() error {\n\treturn New(\"Bad Request\")\n}\n","Hash":"YWVVJ3bcTxEqV86k2JIRM6Kt12g="},{"Name":"example2.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to use error variables to help the\n// caller determine the exact error being returned.\npackage main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\nvar (\n\t// ErrBadRequest is returned when there are problems with the request.\n\tErrBadRequest = errors.New(\"Bad Request\")\n\n\t// ErrPageMoved is returned when a 301/302 is returned.\n\tErrPageMoved = errors.New(\"Page Moved\")\n)\n\nfunc main() {\n\tif err := webCall(true); err != nil {\n\t\tswitch err {\n\t\tcase ErrBadRequest:\n\t\t\tfmt.Println(\"Bad Request Occurred\")\n\t\t\treturn\n\n\t\tcase ErrPageMoved:\n\t\t\tfmt.Println(\"The Page moved\")\n\t\t\treturn\n\n\t\tdefault:\n\t\t\tfmt.Println(err)\n\t\t\treturn\n\t\t}\n\t}\n\n\tfmt.Println(\"Life is good\")\n}\n\n// webCall performs a web operation.\nfunc webCall(b bool) error {\n\tif b {\n\t\treturn ErrBadRequest\n\t}\n\n\treturn ErrPageMoved\n}\n","Hash":"vMgc1oFtiUHnWAVz3kRycm+h3W4="},{"Name":"example3.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// https://golang.org/src/pkg/encoding/json/decode.go\n// Sample program to show how to implement a custom error type\n// based on the json package in the standard library.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n)\n\n// An UnmarshalTypeError describes a JSON value that was\n// not appropriate for a value of a specific Go type.\ntype UnmarshalTypeError struct {\n\tValue string // description of JSON value\n\tType reflect.Type // type of Go value it could not be assigned to\n}\n\n// Error implements the error interface.\nfunc (e *UnmarshalTypeError) Error() string {\n\treturn \"json: cannot unmarshal \" + e.Value + \" into Go value of type \" + e.Type.String()\n}\n\n// An InvalidUnmarshalError describes an invalid argument passed to Unmarshal.\n// (The argument to Unmarshal must be a non-nil pointer.)\ntype InvalidUnmarshalError struct {\n\tType reflect.Type\n}\n\n// Error implements the error interface.\nfunc (e *InvalidUnmarshalError) Error() string {\n\tif e.Type == nil {\n\t\treturn \"json: Unmarshal(nil)\"\n\t}\n\n\tif e.Type.Kind() != reflect.Ptr {\n\t\treturn \"json: Unmarshal(non-pointer \" + e.Type.String() + \")\"\n\t}\n\treturn \"json: Unmarshal(nil \" + e.Type.String() + \")\"\n}\n\n// user is a type for use in the Unmarshal call.\ntype user struct {\n\tName int\n}\n\nfunc main() {\n\tvar u user\n\terr := Unmarshal([]byte(`{\"name\":\"bill\"}`), u) // Run with a value and pointer.\n\tif err != nil {\n\t\tswitch e := err.(type) {\n\t\tcase *UnmarshalTypeError:\n\t\t\tfmt.Printf(\"UnmarshalTypeError: Value[%s] Type[%v]\\n\", e.Value, e.Type)\n\t\tcase *InvalidUnmarshalError:\n\t\t\tfmt.Printf(\"InvalidUnmarshalError: Type[%v]\\n\", e.Type)\n\t\tdefault:\n\t\t\tfmt.Println(err)\n\t\t}\n\t\treturn\n\t}\n\n\tfmt.Println(\"Name:\", u.Name)\n}\n\n// Unmarshal simulates an unmarshal call that always fails.\nfunc Unmarshal(data []byte, v interface{}) error {\n\trv := reflect.ValueOf(v)\n\tif rv.Kind() != reflect.Ptr || rv.IsNil() {\n\t\treturn \u0026InvalidUnmarshalError{reflect.TypeOf(v)}\n\t}\n\n\treturn \u0026UnmarshalTypeError{\"string\", reflect.TypeOf(v)}\n}\n","Hash":"gTCeM7VgJjcVhdvKgYZJ0cXj0nA="},{"Name":"example4.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Package example4 provides code to show how to implement behavior as context.\npackage example4\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"net\"\n)\n\n// client represents a single connection in the room.\ntype client struct {\n\tname string\n\treader *bufio.Reader\n}\n\n// TypeAsContext shows how to check multiple types of possible custom error\n// types that can be returned from the net package.\nfunc (c *client) TypeAsContext() {\n\tfor {\n\t\tline, err := c.reader.ReadString('\\n')\n\t\tif err != nil {\n\t\t\tswitch e := err.(type) {\n\t\t\tcase *net.OpError:\n\t\t\t\tif !e.Temporary() {\n\t\t\t\t\tlog.Println(\"Temporary: Client leaving chat\")\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\tcase *net.AddrError:\n\t\t\t\tif !e.Temporary() {\n\t\t\t\t\tlog.Println(\"Temporary: Client leaving chat\")\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\tcase *net.DNSConfigError:\n\t\t\t\tif !e.Temporary() {\n\t\t\t\t\tlog.Println(\"Temporary: Client leaving chat\")\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\tdefault:\n\t\t\t\tif err == io.EOF {\n\t\t\t\t\tlog.Println(\"EOF: Client leaving chat\")\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tlog.Println(\"read-routine\", err)\n\t\t\t}\n\t\t}\n\n\t\tfmt.Println(line)\n\t}\n}\n\n// temporary is declared to test for the existence of the method coming\n// from the net package.\ntype temporary interface {\n\tTemporary() bool\n}\n\n// BehaviorAsContext shows how to check for the behavior of an interface\n// that can be returned from the net package.\nfunc (c *client) BehaviorAsContext() {\n\tfor {\n\t\tline, err := c.reader.ReadString('\\n')\n\t\tif err != nil {\n\t\t\tswitch e := err.(type) {\n\t\t\tcase temporary:\n\t\t\t\tif !e.Temporary() {\n\t\t\t\t\tlog.Println(\"Temporary: Client leaving chat\")\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\tdefault:\n\t\t\t\tif err == io.EOF {\n\t\t\t\t\tlog.Println(\"EOF: Client leaving chat\")\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tlog.Println(\"read-routine\", err)\n\t\t\t}\n\t\t}\n\n\t\tfmt.Println(line)\n\t}\n}\n","Hash":"E6KKDilyBzpvahisdxM4fgQL1nM="},{"Name":"example5.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show see if the class can find the bug.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n)\n\n// customError is just an empty struct.\ntype customError struct{}\n\n// Error implements the error interface.\nfunc (c *customError) Error() string {\n\treturn \"Find the bug.\"\n}\n\n// fail returns nil values for both return types.\nfunc fail() ([]byte, *customError) {\n\treturn nil, nil\n}\n\nfunc main() {\n\tvar err error\n\tif _, err = fail(); err != nil {\n\t\tlog.Fatal(\"Why did this fail?\")\n\t}\n\n\tlog.Println(\"No Error\")\n}\n\n// =============================================================================\n\nfunc reason() {\n\tvar err error\n\tfmt.Printf(\"Type of value stored inside the interface: %T\\n\", err)\n\n\tif _, err = fail(); err != nil {\n\t\tfmt.Printf(\"Type of value stored inside the interface: %T\\n\", err)\n\t}\n\n\tlog.Println(\"No Error\")\n}\n","Hash":"RPJ/ZXi2qObi8t3eWTdbym0XkMs="},{"Name":"example6.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how wrapping errors work with the stdlib.\npackage main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\n// AppError represents a custom error type.\ntype AppError struct {\n\tState int\n}\n\n// Error implements the error interface.\nfunc (ae *AppError) Error() string {\n\treturn fmt.Sprintf(\"App Error, State: %d\", ae.State)\n}\n\n// IsAppError checks if an error of type AppError exists.\nfunc IsAppError(err error) bool {\n\tvar ae *AppError\n\treturn errors.As(err, \u0026ae)\n}\n\n// GetAppError returns a copy of the AppError pointer.\nfunc GetAppError(err error) *AppError {\n\tvar ae *AppError\n\tif !errors.As(err, \u0026ae) {\n\t\treturn nil\n\t}\n\treturn ae\n}\n\nfunc main() {\n\n\t// Make the function call and validate the error.\n\tif err := firstCall(10); err != nil {\n\n\t\t// Check if the error is an AppError.\n\t\tif IsAppError(err) {\n\t\t\tae := GetAppError(err)\n\t\t\tfmt.Printf(\"Is AppError, State: %d\\n\", ae.State)\n\t\t}\n\n\t\tfmt.Print(\"\\n********************************\\n\\n\")\n\n\t\t// Display the error using the implementation of\n\t\t// the error interface.\n\t\tfmt.Printf(\"%v\\n\", err)\n\t}\n}\n\n// firstCall makes a call to a second function and wraps any error.\nfunc firstCall(i int) error {\n\tif err := secondCall(i); err != nil {\n\t\treturn fmt.Errorf(\"firstCall-\u003esecondCall(%d) : %w\", i, err)\n\t}\n\treturn nil\n}\n\n// secondCall makes a call to a third function and wraps any error.\nfunc secondCall(i int) error {\n\tif err := thirdCall(); err != nil {\n\t\treturn fmt.Errorf(\"secondCall-\u003ethirdCall() : %w\", err)\n\t}\n\treturn nil\n}\n\n// thirdCall create an error value we will validate.\nfunc thirdCall() error {\n\treturn \u0026AppError{99}\n}\n","Hash":"NmYPKjFSZ+VyuZ4etLuILj6y7f0="},{"Name":"exercise1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Create two error variables, one called ErrInvalidValue and the other\n// called ErrAmountTooLarge. Provide the static message for each variable.\n// Then write a function called checkAmount that accepts a float64 type value\n// and returns an error value. Check the value for zero and if it is, return\n// the ErrInvalidValue. Check the value for greater than $1,000 and if it is,\n// return the ErrAmountTooLarge. Write a main function to call the checkAmount\n// function and check the return error value. Display a proper message to the screen.\npackage main\n\n// Add imports.\n\nvar (\n// Declare an error variable named ErrInvalidValue using the New\n// function from the errors package.\n\n// Declare an error variable named ErrAmountTooLarge using the New\n// function from the errors package.\n)\n\n// Declare a function named checkAmount that accepts a value of\n// type float64 and returns an error interface value.\nfunc checkAmount( /* parameter */ ) /* return arg */ {\n\n\t// Is the parameter equal to zero. If so then return\n\t// the error variable.\n\n\t// Is the parameter greater than 1000. If so then return\n\t// the other error variable.\n\n\t// Return nil for the error value.\n}\n\nfunc main() {\n\n\t// Call the checkAmount function and check the error. Then\n\t// use a switch/case to compare the error with each variable.\n\t// Add a default case. Return if there is an error.\n\n\t// Display everything is good.\n}\n","Hash":"lnCpc4ldvg5/RdGV69+ixjFgEEo="},{"Name":"answer1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Create two error variables, one called ErrInvalidValue and the other\n// called ErrAmountTooLarge. Provide the static message for each variable.\n// Then write a function called checkAmount that accepts a float64 type value\n// and returns an error value. Check the value for zero and if it is, return\n// the ErrInvalidValue. Check the value for greater than $1,000 and if it is,\n// return the ErrAmountTooLarge. Write a main function to call the checkAmount\n// function and check the return error value. Display a proper message to the screen.\npackage main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\nvar (\n\t// ErrInvalidValue indicates the value is invalid.\n\tErrInvalidValue = errors.New(\"Invalid Value\")\n\n\t// ErrAmountTooLarge indicates the value beyond the upper bound.\n\tErrAmountTooLarge = errors.New(\"Amount To Large\")\n)\n\nfunc main() {\n\n\t// Call the function and get the error.\n\tif err := checkAmount(0); err != nil {\n\t\tswitch err {\n\n\t\t// Check if the error is an ErrInvalidValue.\n\t\tcase ErrInvalidValue:\n\t\t\tfmt.Println(\"Value provided is not valid.\")\n\t\t\treturn\n\n\t\t// Check if the error is an ErrAmountTooLarge.\n\t\tcase ErrAmountTooLarge:\n\t\t\tfmt.Println(\"Value provided is too large.\")\n\t\t\treturn\n\n\t\t// Handle the default error.\n\t\tdefault:\n\t\t\tfmt.Println(err)\n\t\t\treturn\n\t\t}\n\t}\n\n\t// Display everything is good.\n\tfmt.Println(\"Everything checks out.\")\n}\n\n// checkAmount validates the value passed in.\nfunc checkAmount(f float64) error {\n\tswitch {\n\n\t// Is the parameter equal to zero.\n\tcase f == 0:\n\t\treturn ErrInvalidValue\n\n\t// Is the parameter greater than 1000.\n\tcase f \u003e 1000:\n\t\treturn ErrAmountTooLarge\n\t}\n\n\treturn nil\n}\n","Hash":"oycX2nlCesN9faFTEHHQKTRskXo="},{"Name":"exercise2.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Create a custom error type called appError that contains three fields, err error,\n// message string and code int. Implement the error interface providing your own message\n// using these three fields. Implement a second method named temporary that returns false\n// when the value of the code field is 9. Write a function called checkFlag that accepts\n// a bool value. If the value is false, return a pointer of your custom error type\n// initialized as you like. If the value is true, return a default error. Write a main\n// function to call the checkFlag function and check the error using the temporary\n// interface.\npackage main\n\n// Add imports.\n\n// Declare a struct type named appError with three fields, err of type error,\n// message of type string and code of type int.\n\n// Declare a method for the appError struct type that implements the\n// error interface.\n\n// Declare a method for the appError struct type named Temporary that returns\n// true when the value of the code field is not 9.\n\n// Declare the temporary interface type with a method named Temporary that\n// takes no parameters and returns a bool.\n\n// Declare a function named checkFlag that accepts a boolean value and\n// returns an error interface value.\nfunc checkFlag( /* parameter */ ) /* return arg */ {\n\n\t// If the parameter is false return an appError.\n\n\t// Return a default error.\n}\n\nfunc main() {\n\n\t// Call the checkFlag function to simulate an error of the\n\t// concrete type.\n\n\t// Check the concrete type and handle appropriately.\n\tswitch e := err.(type) {\n\n\t// Apply the case for the existence of the Temporary behavior.\n\t// Log the error and write a second message only if the\n\t// error is not temporary.\n\n\t// Apply the default case and just log the error.\n\t}\n}\n","Hash":"dTgHxE/SAJevPrfzegzAoxFgcHA="},{"Name":"answer2.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Create a custom error type called appError that contains three fields, err error,\n// message string and code int. Implement the error interface providing your own message\n// using these three fields. Implement a second method named temporary that returns false\n// when the value of the code field is 9. Write a function called checkFlag that accepts\n// a bool value. If the value is false, return a pointer of your custom error type\n// initialized as you like. If the value is true, return a default error. Write a main\n// function to call the checkFlag function and check the error using the temporary\n// interface.\npackage main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\n// appError is a custom error type for the program.\ntype appError struct {\n\terr error\n\tmessage string\n\tcode int\n}\n\n// Error implements the error interface for appError.\nfunc (a *appError) Error() string {\n\treturn fmt.Sprintf(\"App Error[%s] Message[%s] Code[%d]\", a.err, a.message, a.code)\n}\n\n// Temporary implements behavior about the error.\nfunc (a *appError) Temporary() bool {\n\treturn (a.code != 9)\n}\n\n// temporary is used to test the error we receive.\ntype temporary interface {\n\tTemporary() bool\n}\n\nfunc main() {\n\tif err := checkFlag(false); err != nil {\n\t\tswitch e := err.(type) {\n\t\tcase temporary:\n\t\t\tfmt.Println(err)\n\t\t\tif !e.Temporary() {\n\t\t\t\tfmt.Println(\"Critical Error!\")\n\t\t\t}\n\t\tdefault:\n\t\t\tfmt.Println(err)\n\t\t}\n\t}\n}\n\n// checkFlag returns one of two errors based on the value of the parameter.\nfunc checkFlag(t bool) error {\n\n\t// If the parameter is false return an appError.\n\tif !t {\n\t\treturn \u0026appError{errors.New(\"Flag False\"), \"The Flag was false\", 9}\n\t}\n\n\t// Return a default error.\n\treturn errors.New(\"Flag True\")\n}\n","Hash":"EC+6G7XEZZxtAU6BCrvFTtYSRuw="}]}]} ,"generics-channels":{"Title":"کانال‌ها","Description":"بررسی کنید که چگونه تیم Go می‌تواند بسته‌ای از الگوهای همروندی را به کتابخانه استاندارد اضافه کند، با تشکر از generic.","Pages":[{"Title":"ژنریک‌ها - کانال‌ها","Content":"\n \u003ch2\u003eژنریک‌ها - کانال‌ها\u003c/h2\u003e\n \n \n \u003cp\u003e\n بررسی کنید که چگونه تیم Go می‌تواند با تشکر از ژنریک، یک بسته از الگوهای همروندی را به کتابخانه استاندارد اضافه کند.\n \u003c/p\u003e\n \n\n","Files":[]},{"Title":"ویدیو","Content":"\n \u003ch2\u003eویدیو\u003c/h2\u003e\n \n \n \u003cp\u003e\n تماشای سخنرانی من در مورد ژنریک‌ها که شما را از طریق تمام مثال‌ها در این بخش از تور هدایت می‌کند.\n \u003c/p\u003e\n \n\u003ciframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/gIEPspmbMHM\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" allowfullscreen\u003e\u003c/iframe\u003e\n \u003ch2\u003eبررسی کد\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e1\u003c/b\u003e: Work Function\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e2\u003c/b\u003e: Pooling\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n \u003ch2\u003eتوضیح داده شده\u003c/h2\u003e\n \n \n \u003cp\u003e\n برای انجام این کار، نیاز به تعریف کانال‌ها و توابع با استفاده از انواع ژنریک داریم.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype workFn[Result any] func(context.Context) Result\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در این مثال، یک نوع تعریف شده است که یک تابع را نمایان می‌کند که یک متغیر context را می‌پذیرد\n\n\n و یک مقدار از نوع ژنریک Result بر می‌گرداند. این تعریف تابع یک تابع را توصیف می‌کند که اجرای کار همروند را انجام می‌دهد و نتیجه آن کار را برمی‌گرداند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc doWork[Result any](ctx context.Context, work workFn[Result]) chan Result {\n ch := make(chan Result, 1)\n \n go func() {\n ch \u0026lt;- work(ctx)\n fmt.Println(\u0026#34;doWork : work complete\u0026#34;)\n }()\n \n return ch\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n اکنون یک تابع به نام doWork بنویسید که تابع کار مشخص شده را به صورت همروند اجرا کرده و یک کانال برگشتی کند تا فراخواننده بتواند نتیجه کار انجام شده توسط تابع کار را دریافت کند. یک نوع ژنریک به نام Result تعریف می‌شود تا نوع بازگشتی برای تابع کار و نوع کانال را نمایان کند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n در پیاده‌سازی تابع doWork، یک کانال بافر شده با ظرفیت یک به نوع ژنریک Result ساخته می‌شود. این کانال به فراخواننده برگشت داده می‌شود تا نتیجه کار همروند را دریافت کند. در وسط تابع، یک گوروتین برای اجرای تابع کار همروند ساخته می‌شود. هنگامی که تابع کار بازگشت دهد، آرگومان بازگشتی از طریق کانال به فراخواننده ارسال می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n برای تست استفاده از تابع doWork، یک برنامه کوچک بسازید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc main() {\n duration := 100 * time.Millisecond\n \n ctx, cancel := context.WithTimeout(context.Background(), duration)\n defer cancel()\n \n dwf := func(ctx context.Context) string {\n time.Sleep(time.Duration(rand.Intn(200)) * time.Millisecond)\n return \u0026#34;work complete\u0026#34;\n }\n\n result := doWork(ctx, dwf)\n \n select {\n case v := \u0026lt;-result:\n fmt.Println(\u0026#34;main:\u0026#34;, v)\n case \u0026lt;-ctx.Done():\n fmt.Println(\u0026#34;main: timeout\u0026#34;)\n }\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n Output:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003edoWork : work complete\nmain: work complete\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003edoWork: کار تکمیل شد\nmain: کار تکمیل شد\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n برنامه با تعریف یک متغیر context آغاز می‌شود که در ۱۰۰ میلی‌ثانیه منقضی می‌شود. سپس یک تابع کار تعریف می‌شود که تا ۲۰۰ میلی‌ثانیه قبل از بازگشت رشته \u0026#34;کار تکمیل شد\u0026#34; منتظر می‌ماند. با داشتن context و تابع کار، یک فراخوانی به تابع doWork انجام می‌شود و یک کانال از نوع string برگشت داده می‌شود و به متغیر نتیجه اختصاص داده می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n کامپایلر قادر است نوع محتوایی را که باید برای نوع ژنریک Result استفاده کند را با بررسی نوع بازگشتی تابع کار لغوشده که به تابع doWork منتقل می‌شود، تشخیص دهد. این بسیار شگفت‌انگیز است زیرا به این معناست که نیازی به انتقال نوع به تماس با doWork نداشته‌اید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n با کانال نوع string که به متغیر نتیجه اختصاص داده شده است، از یک select case برای انتظار بازگشت نتیجه به موقع یا منقضی شدن timeout استفاده می‌شود. تابع doWork می‌تواند برای انجام این کار همروند برای هر نوع محتوایی مورد نیاز استفاده شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n این ایده می‌تواند به یک مجموعه از گوروتین‌ها که می‌توانند کار را روی ورودی ژنریک انجام دهند و نتیجه‌ای ژنریک را برگردانند، اعمال شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n نوع تابع کار را در این مثال به گونه‌ای تغییر دهید که ورودی ژنریک را پذیرفته و نتیجه‌ای ژنریک را برگرداند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n type workFn[Input any, Result any] func(input Input) Result\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n در این مثال، نوع تابع را به گونه‌ای تغییر دهید که ورودی ژنریک را پذیرفته و نتیجه‌ای ژنریک را برگرداند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc poolWork[Input any, Result any](size int, work workFn[Input, Result]) (chan Input, func()) {\n var wg sync.WaitGroup\n wg.Add(size)\n \n ch := make(chan Input)\n \n for i := 0; i \u0026lt; size; i\u0026#43;\u0026#43; {\n go func() {\n defer wg.Done()\n for input := range ch {\n result := work(input)\n fmt.Println(\u0026#34;pollWork :\u0026#34;, result)\n }\n }()\n }\n \n cancel := func() {\n close(ch)\n wg.Wait()\n }\n\n return ch, cancel\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در تابع poolWork، دو نوع ژنریک یکسان برای نمایانگر ورودی و نوع بازگشتی تابع کار تعریف می‌شوند. یک WaitGroup ساخته می‌شود تا مدیریت ژیروتین‌ها در استخر را انجام دهد. سپس یک کانال از نوع ژنریک Input ساخته می‌شود. این کانال توسط ژیروتین‌ها در استخر برای دریافت داده ورودی برای تابع کار استفاده می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n سپس استخر ژیروتین‌ها با ایجاد هر ژیروتین در عملیات دریافتی با استفاده از حلقه for-range در برابر کانال ایجاد می‌شود. در نهایت، یک تابع کنسل ساخته می‌شود تا به فراخواننده اجازه دهد استخر را خاموش کند و منتظر شود که تمام ژیروتین‌ها اعلام کنند که ترمینال شده‌اند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n برای تست استفاده از تابع poolWork، یک برنامه کوچک دیگر بسازید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc main() {\n size := runtime.GOMAXPROCS(0)\n \n pwf := func(input int) string {\n time.Sleep(time.Duration(rand.Intn(200)) * time.Millisecond)\n return fmt.Sprintf(\u0026#34;%d : received\u0026#34;, input)\n }\n\n ch, cancel := poolWork(size, pwf)\n defer cancel()\n \n for i := 0; i \u0026lt; 4; i\u0026#43;\u0026#43; {\n ch \u0026lt;- i\n }\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003epoolWork : 3 : دریافت شد\npoolWork : 2 : دریافت شد\npoolWork : 1 : دریافت شد\npoolWork : 0 : دریافت شد\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n اندازه استخر بر اساس تعداد ژیروتین‌هایی محاسبه می‌شود که می‌توانند به صورت موازی اجرا شوند. سپس یک تابع کار ساخته می‌شود تا به مدت زمان تصادفی بخوابد و سپس یک رشته را که نمایانگر ورودی است برگرداند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n با داشتن این تغییرات، تابع poolWork اجرا می‌شود و کانال و تابع کنسل برگردانده می‌شود. تابع کنسل به صورت بازگشتی فراخوانی می‌شود و یک حلقه ساخته می‌شود تا ۴ مقدار به استخر ارسال شود. خروجی هر بار که برنامه اجرا می‌شود متفاوت خواهد بود زیرا این کار به صورت همروند انجام می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n این مثال‌های کوچک نمونه‌ای از اینکه چگونه یک بسته همروند می‌تواند پیاده‌سازی شود، ارائه می‌دهند.\n \u003c/p\u003e\n \n\n\n","Files":[{"Name":"example1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to execute a work function in a goroutine and\n// return a channel of type Result (to be determined later) back to the caller.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"math/rand\"\n\t\"time\"\n)\n\ntype doworkFn[Result any] func(context.Context) Result\n\nfunc doWork[Result any](ctx context.Context, work doworkFn[Result]) chan Result {\n\tch := make(chan Result, 1)\n\n\tgo func() {\n\t\tch \u003c- work(ctx)\n\t\tfmt.Println(\"doWork : work complete\")\n\t}()\n\n\treturn ch\n}\n\nfunc main() {\n\tctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)\n\tdefer cancel()\n\n\tdwf := func(ctx context.Context) string {\n\t\ttime.Sleep(time.Duration(rand.Intn(200)) * time.Millisecond)\n\t\treturn \"work complete\"\n\t}\n\n\tselect {\n\tcase v := \u003c-doWork(ctx, dwf):\n\t\tfmt.Println(\"main:\", v)\n\tcase \u003c-ctx.Done():\n\t\tfmt.Println(\"main: timeout\")\n\t}\n}\n","Hash":"6dS3Wzip0LMSBbnh6i1QikV73y4="},{"Name":"example2.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to execute a work function via a pool of goroutines\n// and return a channel of type Input (to be determined later) back to the caller.\n// Once input is received by any given goroutine, the work function is executed\n// and the Result value is displayed.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"runtime\"\n\t\"sync\"\n\t\"time\"\n)\n\ntype poolWorkFn[Input any, Result any] func(input Input) Result\n\nfunc poolWork[Input any, Result any](size int, work poolWorkFn[Input, Result]) (chan Input, func()) {\n\tvar wg sync.WaitGroup\n\twg.Add(size)\n\n\tch := make(chan Input)\n\n\tfor i := 0; i \u003c size; i++ {\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\tfor input := range ch {\n\t\t\t\tresult := work(input)\n\t\t\t\tfmt.Println(\"pollWork :\", result)\n\t\t\t}\n\t\t}()\n\t}\n\n\tcancel := func() {\n\t\tclose(ch)\n\t\twg.Wait()\n\t}\n\n\treturn ch, cancel\n}\n\nfunc main() {\n\tsize := runtime.GOMAXPROCS(0)\n\tpwf := func(input int) string {\n\t\ttime.Sleep(time.Duration(rand.Intn(200)) * time.Millisecond)\n\t\treturn fmt.Sprintf(\"%d : received\", input)\n\t}\n\n\tch, cancel := poolWork(size, pwf)\n\tdefer cancel()\n\n\tfor i := 0; i \u003c 5; i++ {\n\t\tch \u003c- i\n\t}\n}\n","Hash":"MUNeHL1CBLOX1amACBmEclJ2sKI="}]}]} ,"struct-types":{"Title":"ساختار (Struct)","Description":"ساختار (Struct) مجموعه ای از فیلد ها با تایپ های مختلف را درخود نگه می دارد.","Pages":[{"Title":"ساختار (Struct)","Content":"\n \u003ch2\u003eساختار (Struct)\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/training/individual-on-demand/ultimate-go-bundle/\" target=\"_blank\"\u003eتماشای ویدئو\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003eنیاز به کمک مالی دارید؟ از \u003ca href=\"https://www.ardanlabs.com/scholarship/\" target=\"_blank\"\u003eفرم Scholarship\u003c/a\u003e ما استفاده کنید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n انواع ساختاری (Struct types) یک روش برای ایجاد انواع پیچیده هستند که فیلدهای داده را با هم گروه‌بندی می‌کنند. آنها راه عالی‌ای برای سازماندهی و به اشتراک‌گذاری جنبه‌های مختلف داده‌هایی که برنامه شما مصرف می‌کند هستند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n کارایی پتانسیلی معماری کامپیوتر به طور عمده توسط طول کلمه (تعداد بیت‌های قابل پردازش در هر دسترسی) و، به ویژه، اندازه حافظه یا تعداد کلماتی که می‌توان به آن دسترسی پیدا کرد، تعیین می‌شود.\n \u003c/p\u003e\n \n\n \u003ch2\u003eبررسی کد\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e1:\u003c/b\u003e اعلام، ایجاد و مقداردهی انواع ساختاری\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e2:\u003c/b\u003e انواع ساختاری ناشناس\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e3:\u003c/b\u003e انواع نام‌دار در مقابل انواع بی‌نام\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e4:\u003c/b\u003e ترازبندی انواع ساختاری\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n \u003ch2\u003eStruct و Construction مکانزیم\u003c/h2\u003e\n \n \n \u003cp\u003e\n این اعلام یک نوع تعریف‌شده توسط کاربر به عنوان یک ترکیب از فیلدها و انواع مختلف را نمایان می‌کند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype example struct {\n flag bool\n counter int16\n pi float32\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n یک متغیر از نوع مثال (example) اعلام کنید و آن را به حالت مقدار صفری مقداردهی اولیه کنید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003evar e1 example\n\nfmt.Printf(\u0026#34;%\u0026#43;v\\n\u0026#34;, e1)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n Output:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e{flag:false counter:0 pi:0}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n یک متغیر از نوع مثال (example) را با استفاده از نحوه ساخت از مقدار ابتدایی آن (صفر) مستثنی کرده و مقداردهی اولیه کنید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003ee2 := example{\n flag: true,\n counter: 10,\n pi: 3.141592,\n}\n\nfmt.Println(\u0026#34;Flag\u0026#34;, e2.flag)\nfmt.Println(\u0026#34;Counter\u0026#34;, e2.counter)\nfmt.Println(\u0026#34;Pi\u0026#34;, e2.pi)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eFlag true\nCounter 10\nPi 3.141592\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n یک متغیر از یک نوع معنون ناشناس اعلام کنید و آن را به حالت مقدار غیر صفری مقداردهی اولیه کنید با استفاده از نحوه ساخت از مقدار ابتدایی.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003ee3 := struct {\n flag bool\n counter int16\n pi float32\n}{\n flag: true,\n counter: 10,\n pi: 3.141592,\n}\n\nfmt.Println(\u0026#34;Flag\u0026#34;, e3.flag)\nfmt.Println(\u0026#34;Counter\u0026#34;, e3.counter)\nfmt.Println(\u0026#34;Pi\u0026#34;, e3.pi)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eFlag true\nCounter 10\nPi 3.141592\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n ایده‌ی ساخت از مقدار ابتدایی دقیقا همین است، به معنای ساخت چیزی به صورت واقعی بدون نام.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n برای مقدار صفر از var استفاده کنید و برای ساخت مقدار غیر صفر از اپراتور تعریف متغیر کوتاه به همراه نحو { } استفاده کنید.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eپدینگ و ترازبندی (Padding and Aligment)\u003c/h2\u003e\n \n \n \u003cp\u003e\n مقدار حافظه مختصر یک نوع مثال چقدر است؟\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype example struct {\n flag bool\n counter int16\n pi float32\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n یک متغیر bool 1 بایت، int16 2 بایت و float32 4 بایت حافظه اشغال می‌کند. اگر این مقادیر را با هم جمع کنید، مجموعاً 7 بایت خواهید داشت. با این حال، پاسخ واقعی 8 بایت است. چرا؟ چون بین فیلدهای پرچم و شمارنده یک بایت پدینگ وجود دارد به عنوان یک روش ترازبندی.\n \u003c/p\u003e\n \n\n \u003ca href=\"/tour/eng/static/img/f1.png\" target=\"_blank\"\u003e\n \u003cimg class=\"codeimg\" src=\"/tour/eng/static/img/f1.png\"\u003e\n \u003c/a\u003e\n\n \n \u003cp\u003e\n ایده ترازبندی این است که به سخت‌افزار امکان خواندن حافظه به صورت کارآمد‌تر با قرار دادن حافظه در مرزهای خاص ترازبندی اجازه دهد. کامپایلر از مکانیزم‌های مرز ترازبندی مراقبت می‌کند، بنابراین شما نیازی به انجام کارهای مربوط به ترازبندی ندارید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n بسته به اندازه یک فیلد خاص و مکان آن در ساختار، Go پدینگ مورد نیاز شما را تعیین می‌کند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype example2 struct {\n flag bool\n counter int16\n flag2 bool\n pi float32\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در این مثال، یک فیلد جدید به نام \u003ccode\u003eflag2\u003c/code\u003e بین فیلدهای شمارنده و pi اضافه کرده‌ام. این باعث می‌شود که در داخل ساختار (struct) بیشترین پدینگ (padding) ایجاد شود.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype example2 struct {\n flag bool // 0xc000100020 \u0026lt;- Starting Address\n byte // 0xc000100021 \u0026lt;- 1 byte padding\n counter int16 // 0xc000100022 \u0026lt;- 2 byte alignment\n flag2 bool // 0xc000100024 \u0026lt;- 1 byte alignment\n byte // 0xc000100025 \u0026lt;- 1 byte padding\n byte // 0xc000100026 \u0026lt;- 1 byte padding\n byte // 0xc000100027 \u0026lt;- 1 byte padding\n pi float32 // 0xc000100028 \u0026lt;- 4 byte alignment\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این نشان‌دهنده نحوه عملکرد ترازبندی و پدینگ است اگر یک مقدار از نوع example2 از آدرس 0xc000100020 شروع شود. فیلد پرچم (flag) نشان‌دهنده آدرس شروع است و فقط 1 بایت اندازه دارد. از آنجایی که فیلد شمارنده (counter) نیاز به اختصاص 2 بایت حافظه دارد، باید در حافظه در تراز 2 بایتی قرار گیرد، به این معنی که باید در یک آدرس که ضریب 2 باشد قرار گیرد. این باعث می‌شود که فیلد شمارنده از آدرس 0xc000100022 شروع شود. این ایجاد یک فاصله 1 بایتی بین فیلدهای پرچم و شمارنده دارد.\n \u003c/p\u003e\n \n\n \u003ca href=\"/tour/eng/static/img/f2.png\" target=\"_blank\"\u003e\n \u003cimg class=\"codeimg\" src=\"/tour/eng/static/img/f2.png\"\u003e\n \u003c/a\u003e\n\n \n \u003cp\u003e\n فیلد \u003ccode\u003eflag2\u003c/code\u003e یک متغیر بول است و می‌تواند در آدرس بعدی 0xc000100024 قرار گیرد. فیلد نهایی، pi، نیاز به اختصاص 4 بایت حافظه دارد، بنابراین باید در یک تراز 4 بایتی قرار گیرد. آدرس بعدی برای یک مقدار 4 بایتی در 0xc000100028 قرار دارد. این به معنی نیاز به 3 بایت پدینگ دیگر برای حفظ تراز صحیح است. این منجر به این می‌شود که یک مقدار از نوع example2 نیاز به 12 بایت از حافظه اختصاصی کلی داشته باشد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n بزرگترین فیلد در یک struct تراز مرز تراز برای کل struct را نمایش می‌دهد. در این مورد، بزرگترین فیلد 4 بایت است، بنابراین آدرس شروع برای این مقدار struct باید ضریبی از 4 باشد. می‌توانید ببینید که آدرس 0xc000100020 ضریبی از 4 است.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n اگر نیاز دارید که حداقل تعداد بایت‌های پدینگ را کاهش دهید، باید فیلدها را از حجم بالاتر به حجم کمتر بچینید. این کار باعث می‌شود که هر بایت پدینگ مورد نیاز به پایین‌ترین قسمت struct فشرده شود و تعداد کلی بایت‌های پدینگ مورد نیاز کاهش یابد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype example struct {\n pi float32 // 0xc000100020 \u0026lt;- Starting Address\n counter int16 // 0xc000100024 \u0026lt;- 2 byte alignment\n flag bool // 0xc000100026 \u0026lt;- 1 byte alignment\n flag2 bool // 0xc000100027 \u0026lt;- 1 byte alignment\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n بعد از بازترتیب دهی مجدد فیلدها، مقدار struct فقط به 8 بایت از حافظه اختصاصی نیاز دارد و نه 12 بایت. از آنجا که تمامی فیلدها اجازه می‌دهند مقدار struct بر روی یک تراز 4 بایتی قرار گیرد، هیچ بایت پدینگ اضافی مورد نیاز نیست.\n \u003c/p\u003e\n \n\n \u003ca href=\"/tour/eng/static/img/f3.png\" target=\"_blank\"\u003e\n \u003cimg class=\"codeimg\" src=\"/tour/eng/static/img/f3.png\"\u003e\n \u003c/a\u003e\n\n\n \u003ch2\u003eتخصیص مقادیر\u003c/h2\u003e\n \n \n \u003cp\u003e\n اگر دو نوع نامگذاری شده متفاوت که در ساختار یکسانی هستند را داشته باشید، نمی‌توانید مقدار یکی را به دیگری اختصاص دهید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n به عنوان مثال، اگر انواع example1 و example2 با استفاده از همان تعریف دقیق اعلام شوند و متغیرهایی را مقداردهی اولیه کنیم.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003evar ex1 example1\nvar ex2 example2\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n شما نمی‌توانید این دو متغیر را به یکدیگر اختصاص دهید چرا که آن‌ها از نوع‌های نامگذاری شده متفاوتی هستند. حقیقت این است که شباهت در ساختار آن‌ها اهمیتی ندارد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eex1 = ex2 // اجازه نمیده, خطای کامپایلر\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n برای انجام این اختصاص، شما باید از نحو تبدیل (conversion) استفاده کنید و از آنجا که در ساختار یکسان هستند، کامپایلر اجازه این عمل را می‌دهد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eex1 = example1(ex2) // اجازه داده میشه, بدون خطای کامپایلر\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n با این حال، اگر ex2 به عنوان یک نوع بدون نام با استفاده از همان تعریف دقیق ex1 اعلام شود، نیازی به نحو تبدیل (conversion) نخواهد بود.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003evar ex2 struct {\n flag bool\n counter int16\n pi float32\n}\n\nex1 = ex2 // Allowed, NO need for conversion syntax\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n کامپایلر به انجام این اختصاص بدون نیاز به تبدیل اجازه می‌دهد.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eنکات\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003eما می‌توانیم از فرم لیترال struct برای مقداردهی از یک نوع struct استفاده کنیم.\u003c/li\u003e\n \n \u003cli\u003eاپراتور نقطه (.) به ما امکان دسترسی به مقادیر فیلد‌های فردی را می‌دهد.\u003c/li\u003e\n \n \u003cli\u003eما می‌توانیم struct‌های ناشناس ایجاد کنیم.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eنقل قول ها\u003c/h2\u003e\n \n \n \u003cp\u003e\n \u0026#34;تبدیل ضمنی انواع معادل عید هالووین برنامه‌نویسی است. هر کسی که به این فکر افتاده است، به جهنم ویژه خودش لایق است.\u0026#34; - مارتین تامسون\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eخواندن اضافی\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2013/07/understanding-type-in-go.html\" target=\"_blank\"\u003eUnderstanding Type in Go\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2013/07/object-oriented-programming-in-go.html\" target=\"_blank\"\u003eObject Oriented Programming in Go\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://dave.cheney.net/2015/10/09/padding-is-hard\" target=\"_blank\"\u003ePadding is hard\u003c/a\u003e - Dave Cheney \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.geeksforgeeks.org/structure-member-alignment-padding-and-data-packing/\" target=\"_blank\"\u003eStructure Member Alignment, Padding and Data Packing\u003c/a\u003e \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"http://www.catb.org/esr/structure-packing\" target=\"_blank\"\u003eThe Lost Art of Structure Packing\u003c/a\u003e - Eric S. Raymond \u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n","Files":[{"Name":"example1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to declare and initialize struct types.\npackage main\n\nimport \"fmt\"\n\n// example represents a type with different fields.\ntype example struct {\n\tflag bool\n\tcounter int16\n\tpi float32\n}\n\nfunc main() {\n\n\t// Declare a variable of type example set to its\n\t// zero value.\n\tvar e1 example\n\n\t// Display the value.\n\tfmt.Printf(\"%+v\\n\", e1)\n\n\t// Declare a variable of type example and init using\n\t// a struct literal.\n\te2 := example{\n\t\tflag: true,\n\t\tcounter: 10,\n\t\tpi: 3.141592,\n\t}\n\n\t// Display the field values.\n\tfmt.Println(\"Flag\", e2.flag)\n\tfmt.Println(\"Counter\", e2.counter)\n\tfmt.Println(\"Pi\", e2.pi)\n}\n","Hash":"HTIuQGVocDnkwJ/yzY/hNEAYqVA="},{"Name":"example2.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to declare and initialize anonymous\n// struct types.\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\n\t// Declare a variable of an anonymous type set\n\t// to its zero value.\n\tvar e1 struct {\n\t\tflag bool\n\t\tcounter int16\n\t\tpi float32\n\t}\n\n\t// Display the value.\n\tfmt.Printf(\"%+v\\n\", e1)\n\n\t// Declare a variable of an anonymous type and init\n\t// using a struct literal.\n\te2 := struct {\n\t\tflag bool\n\t\tcounter int16\n\t\tpi float32\n\t}{\n\t\tflag: true,\n\t\tcounter: 10,\n\t\tpi: 3.141592,\n\t}\n\n\t// Display the values.\n\tfmt.Printf(\"%+v\\n\", e2)\n\tfmt.Println(\"Flag\", e2.flag)\n\tfmt.Println(\"Counter\", e2.counter)\n\tfmt.Println(\"Pi\", e2.pi)\n}\n","Hash":"HM74LbBvAkW0dCluD6OHAimtNT0="},{"Name":"example3.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how variables of an unnamed type can\n// be assigned to variables of a named type, when they are\n// identical.\npackage main\n\nimport \"fmt\"\n\n// example represents a type with different fields.\ntype example struct {\n\tflag bool\n\tcounter int16\n\tpi float32\n}\n\nfunc main() {\n\n\t// Declare a variable of an anonymous type and init\n\t// using a struct literal.\n\te := struct {\n\t\tflag bool\n\t\tcounter int16\n\t\tpi float32\n\t}{\n\t\tflag: true,\n\t\tcounter: 10,\n\t\tpi: 3.141592,\n\t}\n\n\t// Create a value of type example.\n\tvar ex example\n\n\t// Assign the value of the unnamed struct type\n\t// to the named struct type value.\n\tex = e\n\n\t// Display the values.\n\tfmt.Printf(\"%+v\\n\", ex)\n\tfmt.Printf(\"%+v\\n\", e)\n\tfmt.Println(\"Flag\", e.flag)\n\tfmt.Println(\"Counter\", e.counter)\n\tfmt.Println(\"Pi\", e.pi)\n}\n","Hash":"oganQrg8i79gcdE+fFn3MifWEX4="},{"Name":"example4.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// https://github.com/dominikh/go-tools#installation\n// go install honnef.co/go/tools/cmd/...@2023.1.3\n\n// Alignment is about placing fields on address alignment boundaries\n// for more efficient reads and writes to memory.\n\n// Sample program to show how struct types align on boundaries.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"unsafe\"\n)\n\n// No byte padding.\ntype nbp struct {\n\ta bool // \t1 byte\t\t\t\tsizeof 1\n\tb bool // \t1 byte\t\t\t\tsizeof 2\n\tc bool // \t1 byte\t\t\t\tsizeof 3 - Aligned on 1 byte\n}\n\n// Single byte padding.\ntype sbp struct {\n\ta bool //\t1 byte\t\t\t\tsizeof 1\n\t//\t\t\t1 byte padding\t\tsizeof 2\n\tb int16 // \t2 bytes\t\t\t\tsizeof 4 - Aligned on 2 bytes\n}\n\n// Three byte padding.\ntype tbp struct {\n\ta bool //\t1 byte\t\t\t\tsize 1\n\t//\t\t\t3 bytes padding\t\tsize 4\n\tb int32 //\t4 bytes\t\t\t\tsize 8 - Aligned on 4 bytes\n}\n\n// Seven byte padding.\ntype svnbp struct {\n\ta bool //\t1 byte\t\t\t\tsize 1\n\t//\t\t\t7 bytes padding\t\tsize 8\n\tb int64 //\t8 bytes\t\t\t\tsize 16 - Aligned on 8 bytes\n}\n\n// No padding.\ntype np struct {\n\ta string // 16 bytes\t\t\tsize 16\n\tb string // 16 bytes\t\t\tsize 32\n\tc int32 // 4 bytes\t\t\tsize 36\n\td int32 // 4 bytes\t\t\tsize 40 - Aligned on 8 bytes\n}\n\n// Eight byte padding on 64bit Arch. Word size is 8 bytes.\ntype ebp64 struct {\n\ta string //\t16 bytes\t\t\tsize 16\n\tb int32 //\t 4 bytes\t\t\tsize 20\n\t// \t\t 4 bytes padding\tsize 24\n\tc string //\t16 bytes\t\t\tsize 40\n\td int32 //\t 4 bytes\t\t\tsize 44\n\t// \t\t 4 bytes padding\tsize 48 - Aligned on 8 bytes\n}\n\nfunc main() {\n\tvar nbp nbp\n\tsize := unsafe.Sizeof(nbp)\n\tfmt.Printf(\"nbp : SizeOf[%d][%p %p %p]\\n\", size, \u0026nbp.a, \u0026nbp.b, \u0026nbp.c)\n\n\t// -------------------------------------------------------------------------\n\n\tvar sbp sbp\n\tsize = unsafe.Sizeof(sbp)\n\tfmt.Printf(\"sbp : SizeOf[%d][%p %p]\\n\", size, \u0026sbp.a, \u0026sbp.b)\n\n\t// -------------------------------------------------------------------------\n\n\tvar tbp tbp\n\tsize = unsafe.Sizeof(tbp)\n\tfmt.Printf(\"tbp : SizeOf[%d][%p %p]\\n\", size, \u0026tbp.a, \u0026tbp.b)\n\n\t// -------------------------------------------------------------------------\n\n\tvar svnbp svnbp\n\tsize = unsafe.Sizeof(svnbp)\n\tfmt.Printf(\"svnbp: SizeOf[%d][%p %p]\\n\", size, \u0026svnbp.a, \u0026svnbp.b)\n\n\t// -------------------------------------------------------------------------\n\n\tvar np np\n\tsize = unsafe.Sizeof(np)\n\tfmt.Printf(\"np : SizeOf[%d][%p %p %p %p]\\n\", size, \u0026np.a, \u0026np.b, \u0026np.c, \u0026np.d)\n\n\t// -------------------------------------------------------------------------\n\n\tvar ebp64 ebp64\n\tsize = unsafe.Sizeof(ebp64)\n\tfmt.Printf(\"ebp64: SizeOf[%d][%p %p %p %p]\\n\", size, \u0026ebp64.a, \u0026ebp64.b, \u0026ebp64.c, \u0026ebp64.d)\n}\n","Hash":"3Nqm5dE1kJPWKJtyC5ajK0KOToA="}]},{"Title":"تمرینات","Content":"\n \u003ch2\u003eتمرینات\u003c/h2\u003e\n \n \n \u003cp\u003e\n از الگو به عنوان نقطه شروع برای انجام تمرینات استفاده کنید. یک راه حل ممکن ارائه شده است.\n \u003c/p\u003e\n \n\n \u003ch2\u003eتمرین 1\u003c/h2\u003e\n \n \n \u003cp\u003e\n \u003cb\u003eبخش\u003c/b\u003e \u003cb\u003eA:\u003c/b\u003e یک نوع struct برای نگه‌داری اطلاعات یک کاربر (نام، ایمیل و سن) اعلام کنید. یک مقدار از این نوع ایجاد کرده، با مقادیر مقداردهی اولیه کنید و هر فیلد را نمایش دهید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u003cb\u003eبخش\u003c/b\u003e \u003cb\u003eB:\u003c/b\u003e یک نوع struct بدون نام اعلام کنید و با سه فیلد مشابه مقداردهی اولیه کنید. مقدار را نمایش دهید.\n \u003c/p\u003e\n \n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n","Files":[{"Name":"exercise1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Declare a struct type to maintain information about a user (name, email and age).\n// Create a value of this type, initialize with values and display each field.\n//\n// Declare and initialize an anonymous struct type with the same three fields. Display the value.\npackage main\n\n// Add imports.\n\n// Add user type and provide comment.\n\nfunc main() {\n\n\t// Declare variable of type user and init using a struct literal.\n\n\t// Display the field values.\n\n\t// Declare a variable using an anonymous struct.\n\n\t// Display the field values.\n}\n","Hash":"LQI0gqk8F7QKbKum99HpvZMHr8Y="},{"Name":"answer1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Declare a struct type to maintain information about a user (name, email and age).\n// Create a value of this type, initialize with values and display each field.\n//\n// Declare and initialize an anonymous struct type with the same three fields. Display the value.\npackage main\n\nimport \"fmt\"\n\n// user represents a user in the system.\ntype user struct {\n\tname string\n\temail string\n\tage int\n}\n\nfunc main() {\n\n\t// Declare variable of type user and init using a struct literal.\n\tbill := user{\n\t\tname: \"Bill\",\n\t\temail: \"bill@ardanlabs.com\",\n\t\tage: 45,\n\t}\n\n\t// Display the field values.\n\tfmt.Println(\"Name\", bill.name)\n\tfmt.Println(\"Email\", bill.email)\n\tfmt.Println(\"Age\", bill.age)\n\n\t// Declare a variable using an anonymous struct.\n\ted := struct {\n\t\tname string\n\t\temail string\n\t\tage int\n\t}{\n\t\tname: \"Ed\",\n\t\temail: \"ed@ardanlabs.com\",\n\t\tage: 46,\n\t}\n\n\t// Display the field values.\n\tfmt.Println(\"Name\", ed.name)\n\tfmt.Println(\"Email\", ed.email)\n\tfmt.Println(\"Age\", ed.age)\n}\n","Hash":"2fxD14/fOhk6AB1gcC3/73V3JrQ="}]}]} ,"algorithms-sorting":{"Title":"عملیات مرتب‌سازی","Description":"این بخش نمونه‌هایی ارائه می‌دهد که عملیات مرتب‌سازی را انجام می‌دهند.","Pages":[{"Title":"مرتب‌سازی حبابی","Content":"\n \u003ch2\u003eمرتب‌سازی حبابی\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/training/individual-on-demand/ultimate-go-bundle/\" target=\"_blank\"\u003eتماشای ویدئو\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003eنیاز به کمک مالی دارید؟ از \u003ca href=\"https://www.ardanlabs.com/scholarship/\" target=\"_blank\"\u003eفرم Scholarship\u003c/a\u003e ما استفاده کنید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n این برنامه نمونه یک تابع را پیاده‌سازی می‌کند که مرتب‌سازی حبابی علیه مجموعه‌ای از اعداد صحیح را انجام می‌دهد.\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003eاطلاعات ببیشتر در \u003ca href=\"https://en.wikipedia.org/wiki/Bubble_sort\" target=\"_blank\"\u003ehttps://en.wikipedia.org/wiki/Bubble_sort\u003c/a\u003e\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n \u003cb\u003eدیاگرام\u003c/b\u003e\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eمرتب‌سازی حبابی یک الگوریتم ساده مرتب‌سازی است\nکه به صورت مکرر از ابتدا تا انتهای لیست ورودی، عنصر به عنصر، عنصر فعلی\nرا با عنصر بعدی مقایسه می‌کند و اگر لازم باشد مقادیر آنها را جابجا می‌کند.\n\n┌────┐┌────┐┌────┐┌────┐┌────┐\n│ 10 ││ 30 ││ 05 ││ 25 ││ 15 │ ◁── آرایه شروعی\n└────┘└────┘└────┘└────┘└────┘\n┌────┐┌────┐┌────┐┌────┐┌────┐\n│ 10 ││ 05 ││ 25 ││ 15 ││ 30 │ ◁── بعد از اولین تکرار\n└────┘└────┘└────┘└────┘└────┘\n┌────┐┌────┐┌────┐┌────┐┌────┐\n│ 05 ││ 10 ││ 15 ││ 25 ││ 30 │ ◁── بعد از دومین تکرار / مرتب شده\n└────┘└────┘└────┘└────┘└────┘\u003c/pre\u003e\n \n\n\t\n\t\t\n\t\n\n","Files":[{"Name":"bubble.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This sample program shows you how to write a bubble sort.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n)\n\nfunc main() {\n\tnumbers := generateList(10)\n\tfmt.Println(\"Before:\", numbers)\n\n\tbubbleSort(numbers)\n\tfmt.Println(\"Sequential:\", numbers)\n}\n\nfunc bubbleSort(numbers []int) {\n\tn := len(numbers)\n\n\tfor i := 0; i \u003c n; i++ {\n\t\tif !sweep(numbers, i) {\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc sweep(numbers []int, currentPass int) bool {\n\tvar idx int\n\tvar swap bool\n\n\tidxNext := idx + 1\n\tn := len(numbers)\n\n\tfor idxNext \u003c (n - currentPass) {\n\t\ta := numbers[idx]\n\t\tb := numbers[idxNext]\n\n\t\tif a \u003e b {\n\t\t\tnumbers[idx] = b\n\t\t\tnumbers[idxNext] = a\n\t\t\tswap = true\n\t\t}\n\n\t\tidx++\n\t\tidxNext = idx + 1\n\t}\n\n\treturn swap\n}\n\nfunc generateList(totalNumbers int) []int {\n\tnumbers := make([]int, totalNumbers)\n\n\tfor i := 0; i \u003c totalNumbers; i++ {\n\t\tnumbers[i] = rand.Intn(totalNumbers * 20)\n\t}\n\n\treturn numbers\n}\n","Hash":"lhOJTqgOzYrVNILHnySSxlnecbk="}]},{"Title":"مرتب‌سازی درجی","Content":"\n \u003ch2\u003eمرتب‌سازی درجی\u003c/h2\u003e\n \n \n \u003cp\u003e\n این برنامه نمونه یک تابع را پیاده‌سازی می‌کند که مرتب‌سازی درجی را بر روی یک مجموعه از اعداد صحیح انجام می‌دهد.\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003eاطلاعات ببیشتر در \u003ca href=\"https://en.wikipedia.org/wiki/Insertion_sort\" target=\"_blank\"\u003ehttps://en.wikipedia.org/wiki/Insertion_sort\u003c/a\u003e\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n \u003cb\u003eدیاگرام\u003c/b\u003e\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eمرتب‌سازی درجی تعداد تکرارهایی مشابه طول آرایه منهای یک دارد. برای یک \nآرایه با 5 عدد، مرتب‌سازی 4 بار انجام می‌شود. از اندیس 1 شروع کرده، مرتب‌سازی\nآن عدد را به سمت چپ می‌اندازد و آن را در یک موقعیت مرتب‌سازی شده قرار می‌دهد.\n\n┌────┐┌────┐┌────┐┌────┐┌────┐ ◁── آرایه شروعی\n│ 10 ││ 30 ││ 05 ││ 25 ││ 15 │ ◁── جابجایی [1]=30 به چپ\n└────┘└────┘└────┘└────┘└────┘\n┌────┐┌────┐┌────┐┌────┐┌────┐ ◁── بعد از اولین تکرار\n│ 10 ││ 30 ││ 05 ││ 25 ││ 15 │ ◁── جابجایی [2]=05 به چپ\n└────┘└────┘└────┘└────┘└────┘\n┌────┐┌────┐┌────┐┌────┐┌────┐ ◁── بعد از دومین تکرار\n│ 05 ││ 10 ││ 30 ││ 25 ││ 15 │ ◁── جابجایی [3]=25 به چپ\n└────┘└────┘└────┘└────┘└────┘\n┌────┐┌────┐┌────┐┌────┐┌────┐ ◁── بعد از سومین تکرار\n│ 05 ││ 10 ││ 25 ││ 30 ││ 15 │ ◁── جابجایی [4]=15 به چپ\n└────┘└────┘└────┘└────┘└────┘\n┌────┐┌────┐┌────┐┌────┐┌────┐ ◁── بعد از چهارمین تکرار\n│ 05 ││ 10 ││ 15 ││ 25 ││ 30 │ ◁── مرتب شده\n└────┘└────┘└────┘└────┘└────┘\u003c/pre\u003e\n \n\n\t\n\t\t\n\t\n\n","Files":[{"Name":"insertion.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This sample program shows you how to write a insertion sort.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n)\n\nfunc main() {\n\tnumbers := generateList(10)\n\tfmt.Println(\"Before:\", numbers)\n\n\tinsertionSort(numbers)\n\tfmt.Println(\"Sequential:\", numbers)\n}\n\nfunc insertionSort(numbers []int) {\n\tvar n = len(numbers)\n\n\t// Walk through the numbers from left to right. Through\n\t// each outer loop iteration we move values from right\n\t// to left inside the array when they are larger than\n\t// the value that precedes it.\n\n\tfor i := 1; i \u003c n; i++ {\n\t\tj := i\n\n\t\t// For the given starting i index position, look\n\t\t// for smaller values to move left down the numbers list.\n\n\t\tfor j \u003e 0 {\n\n\t\t\t// Is the value on the left larger than the\n\t\t\t// right. If true, swap the two values.\n\n\t\t\tif numbers[j-1] \u003e numbers[j] {\n\t\t\t\tnumbers[j-1], numbers[j] = numbers[j], numbers[j-1]\n\t\t\t}\n\n\t\t\t// Walk through the item from right to left.\n\n\t\t\tj--\n\t\t}\n\t}\n}\n\nfunc generateList(totalNumbers int) []int {\n\tnumbers := make([]int, totalNumbers)\n\n\tfor i := 0; i \u003c totalNumbers; i++ {\n\t\tnumbers[i] = rand.Intn(totalNumbers * 20)\n\t}\n\n\treturn numbers\n}\n","Hash":"kPNaon7S933mJ3Wef0pENPnsRY4="}]},{"Title":"Heap مرتب‌سازی","Content":"\n \u003ch2\u003eHeap مرتب‌سازی\u003c/h2\u003e\n \n \n \u003cp\u003e\n این برنامه نمونه یک تابع را پیاده‌سازی می‌کند که مرتب‌سازی Heap را بر روی یک مجموعه از اعداد صحیح انجام می‌دهد.\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003eاطلاعات ببیشتر در \u003ca href=\"https://en.wikipedia.org/wiki/Heapsort\" target=\"_blank\"\u003ehttps://en.wikipedia.org/wiki/Heapsort\u003c/a\u003e\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n \u003cb\u003eدیاگرام\u003c/b\u003e\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n مرتب‌سازی Heap ورودی خود را به دو آرایه مرتب‌شده و مرتب‌نشده تقسیم می‌کند. الگوریتم به طور تکراری منطقه مرتب‌نشده را با استخراج بزرگترین عنصر از آن کوچک می‌کند و آن را به آرایه مرتب‌شده اضافه می‌کند. این الگوریتم از دو فاز تشکیل شده است.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eفاز اول \nلیست را به دو نیمه تقسیم کرده و با کار کردن در نیمه‌ی اول لیست،\nبزرگترین مقدار را در ابتدای لیست جابجا می‌کنیم و سپس دومین بزرگترین مقدار را.\n\n┌────┐┌────┐ | ┌────┐┌────┐┌────┐ ◁── آرایه شروعی\n│ 63 ││ 16 │ | │ 40 ││ 71 ││ 73 │ ◁── بررسی [1]=16 \u0026lt; [4]=73 : جابجایی\n└────┘└────┘ | └────┘└────┘└────┘\n┌────┐┌────┐ | ┌────┐┌────┐┌────┐ ◁── بعد از اولین تکرار\n│ 63 ││ 73 │ | │ 40 ││ 71 ││ 16 │ ◁── بررسی [0]=63 \u0026lt; [3]=71 : جابجایی\n└────┘└────┘ | └────┘└────┘└────┘\n┌────┐┌────┐ | ┌────┐┌────┐┌────┐ ◁── بعد از دومین تکرار\n│ 73 ││ 71 │ | │ 40 ││ 63 ││ 16 │ ◁── فاز اول تکمیل شد\n└────┘└────┘ | └────┘└────┘└────┘\n\nفاز دوم\nلیست را بگیرید و شروع به جابجایی اعداد\nبه بیرون و به یک لیست مرتب جدید کنید. عدد در موقعیت اول را بگیرید و از لیست \nصلی حذف کنید و آن را به لیست جدیدی که حاوی مرتب‌سازی نهایی خواهد بود، انتقال\nدهید. سپس دوباره بزرگترین عددی که پیدا می‌کنیم را در ابتدای لیست جابجا کنید.\n\n┌────┐┌────┐┌────┐┌────┐┌────┐ | ◁── آرایه شروعی\n│ 73 ││ 71 ││ 40 ││ 63 ││ 16 │ |\n└────┘└────┘└────┘└────┘└────┘ |\n┌────┐┌────┐┌────┐┌────┐ | ┌────┐ ◁── بعد از اولین تکرار\n│ 71 ││ 63 ││ 40 ││ 16 │ | │ 73 │ ◁── 73 را به بیرون منتقل کرده و 71 را در جلو جایگذاری کنید\n└────┘└────┘└────┘└────┘ | └────┘\n┌────┐┌────┐┌────┐ | ┌────┐┌────┐ ◁── بعد از دومین تکرار\n│ 63 ││ 16 ││ 40 │ | │ 71 ││ 73 │ ◁── 71 را به بیرون منتقل کرده و 63 را در جلو جایگذاری کنید\n└────┘└────┘└────┘ | └────┘└────┘\n┌────┐┌────┐ | ┌────┐┌────┐┌────┐ ◁── بعد از سومین تکرار\n│ 40 ││ 16 │ | │ 63 ││ 71 ││ 73 │ ◁── 63 را به بیرون منتقل کرده و 40 را در جلو جایگذاری کنید\n└────┘└────┘ | └────┘└────┘└────┘\n┌────┐ | ┌────┐┌────┐┌────┐┌────┐ ◁── بعد از چهارمین تکرار\n│ 16 │ | │ 40 ││ 63 ││ 71 ││ 73 │ ◁── 40 را به بیرون منتقل کرده و 16 را در جلو جایگذاری کنید\n└────┘ | └────┘└────┘└────┘└────┘\n | ┌────┐┌────┐┌────┐┌────┐┌────┐ ◁── بعد از پنجمین تکرار\n | │ 16 ││ 40 ││ 63 ││ 71 ││ 73 │ ◁── 16 را به بیرون منتقل کرده / مرتب شده\n | └────┘└────┘└────┘└────┘└────┘\u003c/pre\u003e\n \n\n\t\n\t\t\n\t\n\n","Files":[{"Name":"heap.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This sample program shows you how to write a heap sort.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n)\n\nfunc main() {\n\tnumbers := generateList(10)\n\tfmt.Println(\"Before:\", numbers)\n\n\theapSort(numbers)\n\tfmt.Println(\"Sequential:\", numbers)\n}\n\nfunc heapSort(numbers []int) []int {\n\n\t// Split the list in half and work the front half of the list, moving\n\t// the largest value we find to the front of the list and then the\n\t// second largest.\n\n\tfor index := (len(numbers) / 2) - 1; index \u003e= 0; index-- {\n\t\tnumbers = moveLargest(numbers, len(numbers), index)\n\t}\n\n\t// Take the list and start moving numbers out and into a new sorted\n\t// list. Take the number in the first position and remove it to the\n\t// new list which will contain the final sort. Then move the largest\n\t// number we find once again to the front of the list.\n\n\tsize := len(numbers)\n\tfor index := size - 1; index \u003e= 1; index-- {\n\t\tnumbers[0], numbers[index] = numbers[index], numbers[0]\n\t\tsize--\n\t\tnumbers = moveLargest(numbers, size, 0)\n\t}\n\n\treturn numbers\n}\n\n// moveLargest starts at the index positions specified in the list and attempts\n// to move the largest number it can find to that position in the list.\nfunc moveLargest(numbers []int, size int, index int) []int {\n\n\t// Calculate the index deviation so numbers in the list can be\n\t// compared and swapped if needed.\n\t// index 0: cmpIdx1: 1 cmpIdx2: 2 index 5: cmpIdx1: 11 cmpIdx2: 12\n\t// index 1: cmpIdx1: 3 cmpIdx2: 4 index 6: cmpIdx1: 13 cmpIdx2: 14\n\t// index 2: cmpIdx1: 5 cmpIdx2: 6 index 7: cmpIdx1: 15 cmpIdx2: 16\n\t// index 3: cmpIdx1: 7 cmpIdx2: 8 index 8: cmpIdx1: 17 cmpIdx2: 19\n\t// index 4: cmpIdx1: 9 cmpIdx2: 10 index 9: cmpIdx1: 19 cmpIdx2: 20\n\tcmpIdx1, cmpIdx2 := 2*index+1, 2*index+2\n\n\t// Save the specified index as the index with the current largest value.\n\tlargestValueIdx := index\n\n\t// Check if the value at the first deviation index is greater than\n\t// the value at the current largest index. If so, save that\n\t// index position.\n\tif cmpIdx1 \u003c size \u0026\u0026 numbers[cmpIdx1] \u003e numbers[largestValueIdx] {\n\t\tlargestValueIdx = cmpIdx1\n\t}\n\n\t// Check the second deviation index is within bounds and is greater\n\t// than the value at the current largest index. If so, save that\n\t// index position.\n\tif cmpIdx2 \u003c size \u0026\u0026 numbers[cmpIdx2] \u003e numbers[largestValueIdx] {\n\t\tlargestValueIdx = cmpIdx2\n\t}\n\n\t// If we found a larger value than the value at the specified index, swap\n\t// those numbers and then recurse to find more numbers to swap from that\n\t// point in the list.\n\tif largestValueIdx != index {\n\t\tnumbers[index], numbers[largestValueIdx] = numbers[largestValueIdx], numbers[index]\n\t\tnumbers = moveLargest(numbers, size, largestValueIdx)\n\t}\n\n\treturn numbers\n}\n\nfunc generateList(totalNumbers int) []int {\n\tnumbers := make([]int, totalNumbers)\n\n\tfor i := 0; i \u003c totalNumbers; i++ {\n\t\tnumbers[i] = rand.Intn(totalNumbers * 20)\n\t}\n\n\treturn numbers\n}\n","Hash":"L2NS4PKCOFsUJHagROixCaBBtOA="}]},{"Title":"مرتب‌سازی سریع","Content":"\n \u003ch2\u003eمرتب‌سازی سریع\u003c/h2\u003e\n \n \n \u003cp\u003e\n این برنامه نمونه یک تابع را پیاده‌سازی می‌کند که مرتب‌سازی سریع را بر روی یک مجموعه از اعداد صحیح انجام می‌دهد.\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003eاطلاعات ببیشتر در \u003ca href=\"https://en.wikipedia.org/wiki/Quicksort\" target=\"_blank\"\u003ehttps://en.wikipedia.org/wiki/Quicksort\u003c/a\u003e\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n \u003cb\u003eدیاگرام\u003c/b\u003e\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eمرتب‌سازی سریع یک الگوریتم تقسیم و\nحکمرانی است. این با انتخاب یک عنصر \u0026#39;محوری\u0026#39; از آرایه شروع می‌کند و سایر عناصر\nرا بر اساس اینکه آیا کمتر یا بزرگتر از محور هستند، به دو زیرآرایه تقسیم می‌کند.\n\n-------------------------------------------------------------------\nمثال 1\n┌────┐┌────┐┌────┐┌────┐┌────┐ ◁── آرایه شروعی\n│ 45 ││ 39 ││ 37 ││ 15 ││ 41 │ ◁── مقدار محور 41\n└────┘└────┘└────┘└────┘└────┘ ◁── مرتب‌سازی عناصر 0 - 4\n┌────┐┌────┐┌────┐┌────┐┌────┐\n│ 39 ││ 37 ││ 15 ││ 41 ││ 45 │ ◁── مرتب‌سازی اول کامل شد\n└────┘└────┘└────┘└────┘└────┘\n\nدو عنصر آخر مرتب شده‌اند، تمرکز روی سه عنصر اول است.\n\n┌────┐┌────┐┌────┐\n│ 39 ││ 37 ││ 15 │ ◁── مقدار محور 15\n└────┘└────┘└────┘\n┌────┐┌────┐┌────┐┌────┐┌────┐\n│ 15 ││ 37 ││ 39 ││ 41 ││ 45 │ ◁── مرتب‌سازی دوم کامل شد\n└────┘└────┘└────┘└────┘└────┘\n\n-------------------------------------------------------------------\nمثال 2\n┌────┐┌────┐┌────┐┌────┐┌────┐ ◁── آرایه شروعی\n│ 34 ││ 55 ││ 59 ││ 73 ││ 09 │ ◁── مقدار محور 09\n└────┘└────┘└────┘└────┘└────┘ ◁── مرتب‌سازی عناصر 0 - 4\n┌────┐┌────┐┌────┐┌────┐┌────┐\n│ 09 ││ 55 ││ 59 ││ 73 ││ 34 │ ◁── مرتب‌سازی اول کامل شد\n└────┘└────┘└────┘└────┘└────┘\n\nعنصر اول مرتب شده است، تمرکز روی چهار عنصر آخر است.\n\n┌────┐┌────┐┌────┐┌────┐\n│ 55 ││ 59 ││ 73 ││ 34 │ ◁── مقدار محور 34\n└────┘└────┘└────┘└────┘\n┌────┐┌────┐┌────┐┌────┐┌────┐\n│ 09 ││ 34 ││ 59 ││ 73 ││ 55 │ ◁── مرتب‌سازی دوم کامل شد\n└────┘└────┘└────┘└────┘└────┘\n\nدو عنصر اول مرتب شده‌اند، تمرکز روی سه عنصر آخر است.\n\n┌────┐┌────┐┌────┐\n│ 59 ││ 73 ││ 55 │ ◁── مقدار محور 55\n└────┘└────┘└────┘\n┌────┐┌────┐┌────┐┌────┐┌────┐\n│ 09 ││ 34 ││ 55 ││ 73 ││ 59 │ ◁── مرتب‌سازی سوم کامل شد\n\nسه عنصر اول مرتب شده‌اند، تمرکز روی دو عنصر آخر است.\n\n┌────┐┌────┐ ◁── مقدار محور 59\n│ 73 ││ 59 │ ◁── مرتب‌سازی سوم کامل شد\n└────┘└────┘\n┌────┐┌────┐┌────┐┌────┐┌────┐\n│ 09 ││ 34 ││ 55 ││ 59 ││ 73 │ ◁── مرتب‌سازی نهایی\n└────┘└────┘└────┘└────┘└────┘\u003c/pre\u003e\n \n\n\t\n\t\t\n\t\n\n","Files":[{"Name":"quick.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This sample program shows you how to write a quick sort.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n)\n\nfunc main() {\n\tnumbers := generateList(5)\n\tfmt.Println(\"Before:\", numbers)\n\n\tQuickSort(numbers)\n\tfmt.Println(\"Sequential:\", numbers)\n}\n\nfunc QuickSort(numbers []int) []int {\n\treturn quickSort(numbers, 0, len(numbers)-1)\n}\n\nfunc quickSort(numbers []int, leftIdx, rightIdx int) []int {\n\tswitch {\n\tcase leftIdx \u003e rightIdx:\n\t\treturn numbers\n\n\t// Divides array into two partitions.\n\tcase leftIdx \u003c rightIdx:\n\t\tnumbers, pivotIdx := partition(numbers, leftIdx, rightIdx)\n\n\t\tquickSort(numbers, leftIdx, pivotIdx-1)\n\t\tquickSort(numbers, pivotIdx+1, rightIdx)\n\t}\n\n\treturn numbers\n}\n\n// partition it takes a portion of an array then sort it.\nfunc partition(numbers []int, leftIdx, rightIdx int) ([]int, int) {\n\tpivot := numbers[rightIdx]\n\n\tfor smallest := leftIdx; smallest \u003c rightIdx; smallest++ {\n\t\tif numbers[smallest] \u003c pivot {\n\t\t\tnumbers[smallest], numbers[leftIdx] = numbers[leftIdx], numbers[smallest]\n\t\t\tleftIdx++\n\t\t}\n\t}\n\n\tnumbers[leftIdx], numbers[rightIdx] = numbers[rightIdx], numbers[leftIdx]\n\n\treturn numbers, leftIdx\n}\n\nfunc generateList(totalNumbers int) []int {\n\tnumbers := make([]int, totalNumbers)\n\n\tfor i := 0; i \u003c totalNumbers; i++ {\n\t\tnumbers[i] = rand.Intn(totalNumbers * 20)\n\t}\n\n\treturn numbers\n}\n","Hash":"pboWDwXjEtQQX6EdYLalNk2/RUY="}]}]} ,"composition-grouping":{"Title":"گروه‌بندی با نوع‌ها","Description":"مهم است به یاد داشته باشیم که در Go، مفاهیم زیرنوع‌بندی یا زیرکلاس‌بندی در واقع وجود ندارند و این الگوهای طراحی باید اجتناب شود.","Pages":[{"Title":"گروه‌بندی با نوع‌ها","Content":"\n \u003ch2\u003eگروه‌بندی با نوع‌ها\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/training/individual-on-demand/ultimate-go-bundle/\" target=\"_blank\"\u003eتماشای ویدئو\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003eنیاز به کمک مالی دارید؟ از \u003ca href=\"https://www.ardanlabs.com/scholarship/\" target=\"_blank\"\u003eفرم Scholarship\u003c/a\u003e ما استفاده کنید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n مهم است به یاد داشته باشیم که در Go، مفاهیم زیرنوع‌بندی یا زیرکلاس‌بندی واقعاً وجود ندارند و این الگوهای طراحی باید اجتناب شود.\n \u003c/p\u003e\n \n\n \u003ch2\u003eبررسی کد\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e1:\u003c/b\u003e گروه‌بندی بر اساس وضعیت\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e2:\u003c/b\u003e گروه‌بندی بر اساس رفتار\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n \u003ch2\u003eگروه‌بندی انواع مختلف داده‌ها\u003c/h2\u003e\n \n \n \u003cp\u003e\n مهم است به یاد داشته باشیم که در Go، مفاهیم زیرنوع‌بندی یا زیرکلاس‌بندی واقعاً وجود ندارند و این الگوهای طراحی باید اجتناب شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n مورد زیر الگوی ضد-الگویی است که نباید دنبال کرده یا پیاده‌سازی کنید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype Animal struct {\n Name string\n IsMammal bool\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n نوع Animal به عنوان یک نوع پایه اعلام شده است که سعی در تعریف داده‌هایی دارد که مشترک برای تمام حیوانات هستند. همچنین سعی می‌کنید برخی از رفتارهای مشترک را نیز برای یک حیوان فراهم کنید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc (a *Animal) Speak() {\n fmt.Println(\u0026#34;UGH!\u0026#34;,\n \u0026#34;My name is\u0026#34;, a.Name, \u0026#34;, it is\u0026#34;, a.IsMammal, \u0026#34;I am a mammal\u0026#34;)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n بیشتر حیوانات قابلیت صحبت کردن به یک شکل یا شکل دیگر را دارند. با این حال، سعی در اعمال این رفتار مشترک فقط بر روی یک حیوان هیچ معنایی ندارد. در این نقطه، شما هیچ اطلاعاتی در مورد صدایی که این حیوان تولید می‌کند ندارید، بنابراین UGH را می‌نویسید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype Dog struct {\n Animal\n PackFactor int\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n حالا مشکلات واقعی آغاز می‌شوند. من سعی می‌کنم با استفاده از تعبیه (embedding)، یک سگ همه چیزی که یک حیوان است و همچنین بیشتر از آن باشد. به نظر می‌رسد در ابتدا این روش کار می‌کند، اما مشکلاتی وجود خواهد داشت. با این حال، یک سگ یک روش خاص برای صحبت کردن دارد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc (d *Dog) Speak() {\n fmt.Println(\u0026#34;Woof!\u0026#34;,\n \u0026#34;My name is\u0026#34;, d.Name,\n \u0026#34;, it is\u0026#34;, d.IsMammal,\n \u0026#34;I am a mammal with a pack factor of\u0026#34;, d.PackFactor)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در پیاده‌سازی روش Speak، شما می‌توانید UGH را با Woof جایگزین کنید. این مربوط به نحوه صحبت کردن سگ است.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype Cat struct {\n Animal\n ClimbFactor int\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n اگر قرار است یک سگ داشته باشم که یک حیوان را نمایندگی کند، آنگاه باید یک گربه هم داشته باشم. با استفاده از تعبیه، یک گربه همه چیزی است که یک حیوان هست و همچنین بیشتر از آن.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc (c *Cat) Speak() {\n fmt.Println(\u0026#34;Meow!\u0026#34;,\n \u0026#34;My name is\u0026#34;, c.Name,\n \u0026#34;, it is\u0026#34;, c.IsMammal,\n \u0026#34;I am a mammal with a climb factor of\u0026#34;, c.ClimbFactor)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در پیاده‌سازی روش Speak، شما می‌توانید UGH را با Meow جایگزین کنید. این مربوط به نحوه صحبت کردن یک گربه است.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n همه چیز به نظر می‌رسد خوب است و به نظر می‌رسد تعبیه (embedding) همان قابلیت‌های وراثت را که در زبان‌های دیگر دارد، ارائه می‌دهد. سپس شما سعی می‌کنید به جلو بروید و سگ‌ها و گربه‌ها را بر اساس واقعیتی که یک DNA مشترک به عنوان یک حیوان دارند، گروه‌بندی کنید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eanimals := []Animal{\n Dog{\n Animal: Animal{\n Name: \u0026#34;Fido\u0026#34;,\n IsMammal: true,\n },\n PackFactor: 5,\n },\n\n Cat{\n Animal: Animal{\n Name: \u0026#34;Milo\u0026#34;,\n IsMammal: true,\n },\n ClimbFactor: 4,\n },\n}\n\nfor _, animal := range animals {\n animal.Speak()\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n وقتی سعی می‌کنید این کار را انجام دهید، کامپایلر اعتراض می‌کند که یک سگ و یک گربه حیوان نیستند و این درست است. تعبیه (embedding) همان وراثت نیست و این الگویی است که باید از آن دوری کنم. یک سگ یک سگ است، یک گربه یک گربه است و یک حیوان یک حیوان است. نمی‌توانم سگ‌ها و گربه‌ها را به عنوان حیوانات منتقل کنم زیرا آن‌ها حیوان نیستند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n این نوع مکانیک همچنین بسیار انعطاف‌پذیر نیست. آن نیاز به پیکربندی توسط توسعه‌دهنده دارد و مگر اینکه به کد دسترسی داشته باشید، نمی‌توانید تغییرات پیکربندی را در طول زمان اعمال کنید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n اگر این روش نباشد که می‌توانیم مجموعه‌ای از سگ‌ها و گربه‌ها را بسازیم، در Go چگونه این کار را انجام دهیم؟ این مورد مربوط به گروه‌بندی از طریق رفتار مشترک است نه از طریق DNA مشترک. رفتار کلید است.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype Speaker interface {\n Speak()\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n اگر از یک رابطه (interface) استفاده کنید، می‌توانید مجموعه‌ی متدهای مشترک رفتاری که می‌خواهید انواع مختلف داده را بر اساس آن گروه‌بندی کنید، تعریف کنید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003espeakers := []Speaker{\n \u0026amp;Dog{\n Animal: Animal{\n Name: \u0026#34;Fido\u0026#34;,\n IsMammal: true,\n },\n PackFactor: 5,\n },\n \u0026amp;Cat{\n Animal: Animal{\n Name: \u0026#34;Milo\u0026#34;,\n IsMammal: true,\n },\n ClimbFactor: 4,\n },\n}\n\nfor _, speaker := range speakers {\n speaker.Speak()\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در کد جدید، اکنون می‌توانید سگ‌ها و گربه‌ها را بر اساس مجموعه‌ی مشترک رفتاری‌شان، که صحبت کردن است، گروه‌بندی کنید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n در واقع، نوع Animal واقعاً آلودگی نوع (type pollution) است زیرا اعلام یک نوع فقط برای به اشتراک گذاشتن مجموعه‌ای از وضعیت‌های مشترک، نقصی است و باید اجتناب شود.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype Dog struct {\n Name string\n IsMammal bool\n PackFactor int\n}\n\ntype Cat struct {\n Name string\n IsMammal bool\n ClimbFactor int\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در این مورد خاص، ترجیح می‌دهید نوع Animal حذف شود و فیلدها در نوع‌های Dog و Cat کپی و الصق شوند. بعدها نکاتی درباره الگوهای بهتری که این سناریوها را از بین می‌برند، بررسی خواهید کرد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n این‌ها هستند نقاط ضعف کد اصلی:\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003eنوع Animal یک لایه انتزاعی از وضعیت قابل استفاده مجدد فراهم می‌کند.\u003c/li\u003e\n \n \u003cli\u003eبرنامه هرگز نیاز به ایجاد یا استفاده انحصاری از مقدار نوع Animal ندارد.\u003c/li\u003e\n \n \u003cli\u003eپیاده‌سازی روش Speak برای نوع Animal به صورت عمومی است.\u003c/li\u003e\n \n \u003cli\u003eروش Speak برای نوع Animal هرگز فراخوانی نخواهد شد.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n دستورالعمل‌های مربوط به اعلام نوع:\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003eنوع‌ها را برای نمایانگر چیزی جدید یا منحصربه‌فرد تعریف کنید.\u003c/li\u003e\n \n \u003cli\u003eفقط برای خوانایی، نوع‌های مستعار ایجاد نکنید.\u003c/li\u003e\n \n \u003cli\u003eتأیید کنید که مقدار هر نوع به تنهایی ایجاد یا استفاده می‌شود.\u003c/li\u003e\n \n \u003cli\u003eنوع‌ها را تعبیه نکنید فقط به این دلیل که به وضعیت نیاز داریم، بلکه به این دلیل که به رفتار نیاز داریم.\u003c/li\u003e\n \n \u003cli\u003eاگر درباره رفتار فکر نمی‌کنید، خود را در طراحی قفل کرده‌اید که نمی‌توانید در آینده بدون تغییرات کد پیوسته رشد کنید.\u003c/li\u003e\n \n \u003cli\u003eنوع‌هایی که مستعارها یا انتزاعاتی برای یک نوع موجود هستند، مورد سؤال قرار دهید.\u003c/li\u003e\n \n \u003cli\u003eنوع‌هایی که هدف اصلی آن‌ها به اشتراک گذاشتن مجموعه‌ای از وضعیت‌های مشترک است، مورد سؤال قرار دهید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eبا رابطه‌ها طراحی نکنید\u003c/h2\u003e\n \n \n \u003cp\u003e\n متأسفانه، بسیاری از توسعه‌دهندگان تلاش می‌کنند ابتدا مسائل را به صورت انتزاعی حل کنند. آن‌ها بلافاصله بر روی رابطه‌ها تمرکز می‌کنند و این باعث آلودگی رابطه می‌شود. به عنوان یک توسعه‌دهنده، شما در یکی از دو حالت وجود دارید: برنامه‌نویس و سپس مهندس.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n وقتی برنامه‌نویسی می‌کنید، تمرکزتان بر روی کار کردن یک قطعه کد است. تلاش برای حل مسئله و شکستن موانع. اثبات اینکه ادر این مورد خاص، بهتر است نوع Animal حذف شود و فیلدها در نوع‌های Dog و Cat کپی و الصق شوند. در ادامه، یادداشت‌هایی درباره الگوهای بهتری که این سناریوها را از بین می‌برند، خواهید داشت.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n وقتی شما در حال برنامه‌نویسی هستید، تمرکزتان بر روی کار کردن یک قطعه کد است. تلاش برای حل مسئله و شکستن موانع. اثبات اینکه ایده‌های اولیه من جواب می‌دهد. همین چیزی است که مهم است. این برنامه‌نویسی باید در قالبی محکم انجام شود و هرگز آماده استفاده تولیدی نیست.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n وقتی یک پروتوتایپ از کدی که مسئله را حل می‌کند دارید، باید به حالت مهندسی تغییر کنید. باید بر روی نحوه نوشتن کد در سطح میکرو برای معناشناسی داده و خوانایی، و سپس در سطح ماکرو برای مدلهای ذهنی و قابلیت ادامه کار تمرکز کنید. همچنین باید بر روی خطاها و وضعیت‌های شکست تمرکز کنید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n این کار در یک چرخه بازطراحی انجام می‌شود. بازطراحی برای خوانایی، کارآیی، انتزاع و قابلیت آزمون. انتزاع فقط یکی از چندین بازطراحی است که باید انجام شود. این کار بهترین نتیجه را زمانی می‌دهد که با یک قطعه کد محکم شروع کنید و سپس رابطه‌هایی که نیاز دارید را کشف کنید. انتزاع را اعمال نکنید مگر اینکه به طور مطلق ضروری باشد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n هر مسئله‌ای که با کد حل می‌کنید، یک مسئله داده است که نیازمند نوشتن تبدیلات داده است. اگر داده را نفهمیدید، مسئله را نفهمیدید. اگر مسئله را نفهمیدید، نمی‌توانید هیچ کدی بنویسید. شروع با یک راه‌حل محکم که بر اساس ساختارهای داده محکم است، بسیار حائز اهمیت است. همانطور که راب پایک گفت:\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u0026#34;داده حاکم است. اگر ساختارهای داده صحیح را انتخاب و سازماندهی کنید، الگوریتم‌ها تقریباً همیشه روشن خواهند بود.\u0026#34; - راب پایک\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n کی انتزاع ضروری است؟ زمانی که در کد یک نقطه را مشاهده می‌کنید که داده ممکن است تغییر کند و می‌خواهید اثرات کد پیوسته که نتیجه آن خواهد بود را کمینه کنید. ممکن است از انتزاع برای کمک به قابلیت آزمون کردن کد استفاده کنم، اما اگر امکان دارد باید سعی کنید از آن پرهیز کنید. بهترین توابع قابل آزمون، توابعی هستند که داده‌های خام را دریافت کرده و داده‌های خام را ارسال می‌کنند. اهمیتی ندارد که داده از کجا می‌آید یا به کجا می‌رود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n در پایان، با یک راه‌حل محکم برای هر مسئله شروع کنید. حتی اگر بخش عمده‌ای از آن فقط برنامه‌نویسی باشد. سپس رابط‌هایی را کشف کنید که برای کد امروز به طور مطلق لازم هستند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u0026#34;با رابط‌ها طراحی نکنید، آن‌ها را کشف کنید.\u0026#34; - راب پایک\n \u003c/p\u003e\n \n\n\n","Files":[{"Name":"example1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This is an example of using type hierarchies with a OOP pattern.\n// This is not something we want to do in Go. Go does not have the\n// concept of sub-typing. All types are their own and the concepts of\n// base and derived types do not exist in Go. This pattern does not\n// provide a good design principle in a Go program.\npackage main\n\nimport \"fmt\"\n\n// Animal contains all the base fields for animals.\ntype Animal struct {\n\tName string\n\tIsMammal bool\n}\n\n// Speak provides generic behavior for all animals and\n// how they speak.\nfunc (a *Animal) Speak() {\n\tfmt.Printf(\n\t\t\"UGH! My name is %s, it is %t I am a mammal\\n\",\n\t\ta.Name,\n\t\ta.IsMammal,\n\t)\n}\n\n// Dog contains everything an Animal is but specific\n// attributes that only a Dog has.\ntype Dog struct {\n\tAnimal\n\tPackFactor int\n}\n\n// Speak knows how to speak like a dog.\nfunc (d *Dog) Speak() {\n\tfmt.Printf(\n\t\t\"Woof! My name is %s, it is %t I am a mammal with a pack factor of %d.\\n\",\n\t\td.Name,\n\t\td.IsMammal,\n\t\td.PackFactor,\n\t)\n}\n\n// Cat contains everything an Animal is but specific\n// attributes that only a Cat has.\ntype Cat struct {\n\tAnimal\n\tClimbFactor int\n}\n\n// Speak knows how to speak like a cat.\nfunc (c *Cat) Speak() {\n\tfmt.Printf(\n\t\t\"Meow! My name is %s, it is %t I am a mammal with a climb factor of %d.\\n\",\n\t\tc.Name,\n\t\tc.IsMammal,\n\t\tc.ClimbFactor,\n\t)\n}\n\nfunc main() {\n\n\t// Create a list of Animals that know how to speak.\n\tanimals := []Animal{\n\n\t\t// Create a Dog by initializing its Animal parts\n\t\t// and then its specific Dog attributes.\n\t\tDog{\n\t\t\tAnimal: Animal{\n\t\t\t\tName: \"Fido\",\n\t\t\t\tIsMammal: true,\n\t\t\t},\n\t\t\tPackFactor: 5,\n\t\t},\n\n\t\t// Create a Cat by initializing its Animal parts\n\t\t// and then its specific Cat attributes.\n\t\tCat{\n\t\t\tAnimal: Animal{\n\t\t\t\tName: \"Milo\",\n\t\t\t\tIsMammal: true,\n\t\t\t},\n\t\t\tClimbFactor: 4,\n\t\t},\n\t}\n\n\t// Have the Animals speak.\n\tfor _, animal := range animals {\n\t\tanimal.Speak()\n\t}\n}\n","Hash":"gbo5/wkQcJsDQQKbqYjpvkFTzCo="},{"Name":"example2.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This is an example of using composition and interfaces. This is\n// something we want to do in Go. We will group common types by\n// their behavior and not by their state. This pattern does\n// provide a good design principle in a Go program.\npackage main\n\nimport \"fmt\"\n\n// Speaker provide a common behavior for all concrete types\n// to follow if they want to be a part of this group. This\n// is a contract for these concrete types to follow.\ntype Speaker interface {\n\tSpeak()\n}\n\n// Dog contains everything a Dog needs.\ntype Dog struct {\n\tName string\n\tIsMammal bool\n\tPackFactor int\n}\n\n// Speak knows how to speak like a dog.\n// This makes a Dog now part of a group of concrete\n// types that know how to speak.\nfunc (d *Dog) Speak() {\n\tfmt.Printf(\n\t\t\"Woof! My name is %s, it is %t I am a mammal with a pack factor of %d.\\n\",\n\t\td.Name,\n\t\td.IsMammal,\n\t\td.PackFactor,\n\t)\n}\n\n// Cat contains everything a Cat needs.\ntype Cat struct {\n\tName string\n\tIsMammal bool\n\tClimbFactor int\n}\n\n// Speak knows how to speak like a cat.\n// This makes a Cat now part of a group of concrete\n// types that know how to speak.\nfunc (c *Cat) Speak() {\n\tfmt.Printf(\n\t\t\"Meow! My name is %s, it is %t I am a mammal with a climb factor of %d.\\n\",\n\t\tc.Name,\n\t\tc.IsMammal,\n\t\tc.ClimbFactor,\n\t)\n}\n\nfunc main() {\n\n\t// Create a list of Animals that know how to speak.\n\tspeakers := []Speaker{\n\n\t\t// Create a Dog by initializing its Animal parts\n\t\t// and then its specific Dog attributes.\n\t\t\u0026Dog{\n\t\t\tName: \"Fido\",\n\t\t\tIsMammal: true,\n\t\t\tPackFactor: 5,\n\t\t},\n\n\t\t// Create a Cat by initializing its Animal parts\n\t\t// and then its specific Cat attributes.\n\t\t\u0026Cat{\n\t\t\tName: \"Milo\",\n\t\t\tIsMammal: true,\n\t\t\tClimbFactor: 4,\n\t\t},\n\t}\n\n\t// Have the Animals speak.\n\tfor _, spkr := range speakers {\n\t\tspkr.Speak()\n\t}\n}\n","Hash":"8SagnKvE4NUre5wWlrTg+UO9vlM="}]}]} ,"maps":{"Title":"نگاشت‌ها (Maps)","Description":"نگاشت (Map) یک ساختار داده است که امکان ذخیره و دسترسی به داده‌ها بر اساس یک کلید را فراهم می‌کند.","Pages":[{"Title":"Maps","Content":"\n \u003ch2\u003eMaps\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/training/individual-on-demand/ultimate-go-bundle/\" target=\"_blank\"\u003eتماشای ویدئو\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003eنیاز به کمک مالی دارید؟ از \u003ca href=\"https://www.ardanlabs.com/scholarship/\" target=\"_blank\"\u003eفرم Scholarship\u003c/a\u003e ما استفاده کنید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n یک نگاشت (Map) یک ساختار داده است که امکان ذخیره و دسترسی به داده‌ها بر اساس یک کلید را فراهم می‌کند. از یک نمایه نگاشت (hash map) و سیستم تعدادی (bucket) استفاده می‌کند که یک بلوک پیوسته از حافظه را در زیر نگه می‌دارد.\n \u003c/p\u003e\n \n\n \u003ch2\u003eبررسی کد\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e1:\u003c/b\u003e اعلام کردن، نوشتن، خواندن و حذف\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e2:\u003c/b\u003e کلیدهای موجود نیستند\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e3:\u003c/b\u003e محدودیت‌های کلید map\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e4:\u003c/b\u003e map متناوب و محدوده\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e5:\u003c/b\u003e مرتب‌سازی maps بر اساس کلید\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e6:\u003c/b\u003e گرفتن آدرس یک عنصر\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e7:\u003c/b\u003e maps از نوع مرجع هستند\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n \u003ch2\u003eاعلام و ساخت maps\u003c/h2\u003e\n \n \n \u003cp\u003e\n اعلام و ساخت نگاشت‌ها می‌تواند به چندین روش انجام شود.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype user struct {\n name string\n username string\n}\n\n// Construct a map set to its zero value,\n// that can store user values based on a key of type string.\n// Trying to use this map will result in a runtime error (panic).\nvar users map[string]user\n\n// Construct a map initialized using make,\n// that can store user values based on a key of type string.\nusers := make(map[string]user)\n\n// Construct a map initialized using empty literal construction,\n// that can store user values based on a key of type string.\nusers := map[string]user{}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n یک نگاشت که به مقدار صفر تنظیم شده است، قابل استفاده نیست و ممکن است منجر به اشکال در برنامه شما شود. استفاده از تابع داخلی make و ساخت مستقیم (literal construction) یک map آماده برای استفاده ایجاد می‌کند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc main() {\n users := make(map[string]user)\n\n users[\u0026#34;Roy\u0026#34;] = user{\u0026#34;Rob\u0026#34;, \u0026#34;Roy\u0026#34;}\n users[\u0026#34;Ford\u0026#34;] = user{\u0026#34;Henry\u0026#34;, \u0026#34;Ford\u0026#34;}\n users[\u0026#34;Mouse\u0026#34;] = user{\u0026#34;Mickey\u0026#34;, \u0026#34;Mouse\u0026#34;}\n users[\u0026#34;Jackson\u0026#34;] = user{\u0026#34;Michael\u0026#34;, \u0026#34;Jackson\u0026#34;}\n\n for key, value := range users {\n fmt.Println(key, value)\n }\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eRoy {Rob Roy}\nFord {Henry Ford}\nMouse {Mickey Mouse}\nJackson {Michael Jackson}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n اگر تابع داخلی make برای ساخت یک نقشه (map) استفاده شود، آپراتور تخصیص (assignment operator) می‌تواند برای افزودن و به‌روزرسانی مقادیر در نقشه (map) استفاده شود. ترتیبی که کلیدها/مقادیر در حین تکرار (ranging) بر روی نقشه ارائه می‌شوند، توسط مشخصات (spec) تعریف نشده است و وظیفه اجرای آن به عهده کامپایلر است.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc main() {\n users := map[string]user{\n \u0026#34;Roy\u0026#34;: {\u0026#34;Rob\u0026#34;, \u0026#34;Roy\u0026#34;},\n \u0026#34;Ford\u0026#34;: {\u0026#34;Henry\u0026#34;, \u0026#34;Ford\u0026#34;},\n \u0026#34;Mouse\u0026#34;: {\u0026#34;Mickey\u0026#34;, \u0026#34;Mouse\u0026#34;},\n \u0026#34;Jackson\u0026#34;: {\u0026#34;Michael\u0026#34;, \u0026#34;Jackson\u0026#34;},\n }\n\n for key, value := range users {\n fmt.Println(key, value)\n }\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eFord {Henry Ford}\nJackson {Michael Jackson}\nRoy {Rob Roy}\nMouse {Mickey Mouse}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در این مورد، خروجی به ترتیب متفاوتی نسبت به نحوه‌ی آن‌ها در ساختار لیست شده‌اند. الگوریتم فعلی برای نسخه 1.16 نتایج را به ترتیب تصادفی با توجه به تعداد مقادیر به یک حد معین می‌آورد. مجدداً، این یک پیاده‌سازی کامپایلر است که ممکن است تغییر کند. نباید به آن اعتماد کرد.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eجستجوی کلیدها و حذف آنها از نقشه\u003c/h2\u003e\n \n \n \u003cp\u003e\n هنگامی که داده‌ها در داخل یک نقشه (map) ذخیره شده‌اند، برای استخراج هر داده، نیاز به جستجوی کلید (key lookup) وجود دارد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003euser1, exists1 := users[\u0026#34;Bill\u0026#34;]\nuser2, exists2 := users[\u0026#34;Ford\u0026#34;]\n\nfmt.Println(\u0026#34;Bill:\u0026#34;, exists1, user1)\nfmt.Println(\u0026#34;Ford:\u0026#34;, exists2, user2)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eBill: false { }\nFord: true {Henry Ford}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n برای انجام جستجوی کلید، از براکت‌های مربعی با متغیر نقشه (map variable) استفاده می‌شود. دو مقدار از یک جستجوی نقشه (map lookup) برگردانده می‌شوند، مقدار و یک مقدار بولین که نشان می‌دهد آیا مقدار پیدا شده یا نه. اگر نیاز به اطلاع از این موضوع نداشته باشید، می‌توانید متغیر \u0026#34;exists\u0026#34; را حذف کنید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n هنگامی که یک کلید در نقشه پیدا نشود، عملیات یک مقدار از نوع نقشه را به حالت مقدار صفرش بازمی‌گرداند. شما این موضوع را با جستجوی کلید \u0026#34;Bill\u0026#34; می‌بینید. از مقدار صفر برای تعیین اینکه یک کلید در نقشه وجود دارد یا نه استفاده نکنید، زیرا مقدار صفر ممکن است معتبر باشد و واقعاً برای کلید ذخیره شده باشد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003edelete(users, \u0026#34;Roy\u0026#34;)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n یک تابع داخلی به نام \u0026#34;delete\u0026#34; وجود دارد که امکان حذف داده‌ها از نقشه بر اساس یک کلید را فراهم می‌کند.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eمحدودیت‌های کلید نقشه\u003c/h2\u003e\n \n \n \u003cp\u003e\n همه‌ی انواع نمی‌توانند به عنوان کلید استفاده شوند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype slice []user\nUsers := make(map[slice]user)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خطای کامپایلر:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003einvalid map key type users\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n یک تیکه (slice) مثال خوبی از یک نوع است که نمی‌تواند به عنوان یک کلید استفاده شود. تنها مقادیری که می‌توانند از طریق تابع هش (hash function) اجرا شوند، واجد شرایط هستند. یک راه خوب برای شناسایی انواعی که می‌توانند یک کلید باشند، این است که اگر نوع می‌تواند در یک عملیات مقایسه استفاده شود، معتبر است. شما نمی‌توانید دو مقدار تیکه را مقایسه کنید.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eیادداشت‌ها\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003eنقشه‌ها یک روش برای ذخیره و بازیابی جفت‌های کلید/مقدار فراهم می‌کنند.\u003c/li\u003e\n \n \u003cli\u003eخواندن یک کلید غیرحاضر، مقدار صفر برای نوع مقدار نقشه باز می‌گرداند.\u003c/li\u003e\n \n \u003cli\u003eتکرار بر روی یک نقشه همیشه به صورت تصادفی است.\u003c/li\u003e\n \n \u003cli\u003eکلید نقشه باید یک مقدار قابل مقایسه باشد.\u003c/li\u003e\n \n \u003cli\u003eعناصر در یک نقشه قابل دسترس نیستند.\u003c/li\u003e\n \n \u003cli\u003eنقشه‌ها نوع مرجعی (reference type) هستند.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eلینک ها\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://blog.golang.org/go-maps-in-action\" target=\"_blank\"\u003eGo maps in action\u003c/a\u003e - Andrew Gerrand \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2013/12/macro-view-of-map-internals-in-go.html\" target=\"_blank\"\u003eMacro View of Map Internals In Go\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.youtube.com/watch?v=Tl7mi9QmLns\" target=\"_blank\"\u003eInside the Map Implementation\u003c/a\u003e - Keith Randall \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://dave.cheney.net/2018/05/29/how-the-go-runtime-implements-maps-efficiently-without-generics\" target=\"_blank\"\u003eHow the Go runtime implements maps efficiently (without generics)\u003c/a\u003e - Dave Cheney \u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n","Files":[{"Name":"example1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to initialize a map, write to\n// it, then read and delete from it.\npackage main\n\nimport \"fmt\"\n\n// user represents someone using the program.\ntype user struct {\n\tname string\n\tsurname string\n}\n\nfunc main() {\n\n\t// Declare and make a map that stores values\n\t// of type user with a key of type string.\n\tusers := make(map[string]user)\n\n\t// Add key/value pairs to the map.\n\tusers[\"Roy\"] = user{\"Rob\", \"Roy\"}\n\tusers[\"Ford\"] = user{\"Henry\", \"Ford\"}\n\tusers[\"Mouse\"] = user{\"Mickey\", \"Mouse\"}\n\tusers[\"Jackson\"] = user{\"Michael\", \"Jackson\"}\n\n\t// Read the value at a specific key.\n\tmouse := users[\"Mouse\"]\n\n\tfmt.Printf(\"%+v\\n\", mouse)\n\n\t// Replace the value at the Mouse key.\n\tusers[\"Mouse\"] = user{\"Jerry\", \"Mouse\"}\n\n\t// Read the Mouse key again.\n\tfmt.Printf(\"%+v\\n\", users[\"Mouse\"])\n\n\t// Delete the value at a specific key.\n\tdelete(users, \"Roy\")\n\n\t// Check the length of the map. There are only 3 elements.\n\tfmt.Println(len(users))\n\n\t// It is safe to delete an absent key.\n\tdelete(users, \"Roy\")\n\n\tfmt.Println(\"Goodbye.\")\n}\n","Hash":"ozw9uwbvkWN4T0bKh0fcA07WSes="},{"Name":"example2.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how maps behave when you read an\n// absent key.\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\n\t// Create a map to track scores for players in a game.\n\tscores := make(map[string]int)\n\n\t// Read the element at key \"anna\". It is absent so we get\n\t// the zero-value for this map's value type.\n\tscore := scores[\"anna\"]\n\n\tfmt.Println(\"Score:\", score)\n\n\t// If we need to check for the presence of a key we use\n\t// a 2 variable assignment. The 2nd variable is a bool.\n\tscore, ok := scores[\"anna\"]\n\n\tfmt.Println(\"Score:\", score, \"Present:\", ok)\n\n\t// We can leverage the zero-value behavior to write\n\t// convenient code like this:\n\tscores[\"anna\"]++\n\n\t// Without this behavior we would have to code in a\n\t// defensive way like this:\n\tif n, ok := scores[\"anna\"]; ok {\n\t\tscores[\"anna\"] = n + 1\n\t} else {\n\t\tscores[\"anna\"] = 1\n\t}\n\n\tscore, ok = scores[\"anna\"]\n\tfmt.Println(\"Score:\", score, \"Present:\", ok)\n}\n","Hash":"X3kSd6syB9eVKlv061A/TDcARRs="},{"Name":"example3.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how only types that can have\n// equality defined on them can be a map key.\npackage main\n\nimport \"fmt\"\n\n// user represents someone using the program.\ntype user struct {\n\tname string\n\tsurname string\n}\n\n// users defines a set of users.\ntype users []user\n\nfunc main() {\n\n\t// Declare and make a map that uses a slice as the key.\n\tu := make(map[users]int)\n\n\t// ./example3.go:22: invalid map key type users\n\n\t// Iterate over the map.\n\tfor key, value := range u {\n\t\tfmt.Println(key, value)\n\t}\n}\n","Hash":"4TPX8/Sp5BsqapuS4iidObArgRA="},{"Name":"example4.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to declare, initialize and iterate\n// over a map. Shows how iterating over a map is random.\npackage main\n\nimport \"fmt\"\n\n// user represents someone using the program.\ntype user struct {\n\tname string\n\tsurname string\n}\n\nfunc main() {\n\n\t// Declare and initialize the map with values.\n\tusers := map[string]user{\n\t\t\"Roy\": {\"Rob\", \"Roy\"},\n\t\t\"Ford\": {\"Henry\", \"Ford\"},\n\t\t\"Mouse\": {\"Mickey\", \"Mouse\"},\n\t\t\"Jackson\": {\"Michael\", \"Jackson\"},\n\t}\n\n\t// Iterate over the map printing each key and value.\n\tfor key, value := range users {\n\t\tfmt.Println(key, value)\n\t}\n\n\tfmt.Println()\n\n\t// Iterate over the map printing just the keys.\n\t// Notice the results are different.\n\tfor key := range users {\n\t\tfmt.Println(key)\n\t}\n}\n","Hash":"1Cg+pyTzXDd9eSrR3BCixW1fD24="},{"Name":"example5.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to walk through a map by\n// alphabetical key order.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"sort\"\n)\n\n// user represents someone using the program.\ntype user struct {\n\tname string\n\tsurname string\n}\n\nfunc main() {\n\n\t// Declare and initialize the map with values.\n\tusers := map[string]user{\n\t\t\"Roy\": {\"Rob\", \"Roy\"},\n\t\t\"Ford\": {\"Henry\", \"Ford\"},\n\t\t\"Mouse\": {\"Mickey\", \"Mouse\"},\n\t\t\"Jackson\": {\"Michael\", \"Jackson\"},\n\t}\n\n\t// Pull the keys from the map.\n\tvar keys []string\n\tfor key := range users {\n\t\tkeys = append(keys, key)\n\t}\n\n\t// Sort the keys alphabetically.\n\tsort.Strings(keys)\n\n\t// Walk through the keys and pull each value from the map.\n\tfor _, key := range keys {\n\t\tfmt.Println(key, users[key])\n\t}\n}\n","Hash":"IPSHDOx+xm6Qp29YHqcItDpjpsY="},{"Name":"example6.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show that you cannot take the address\n// of an element in a map.\npackage main\n\n// player represents someone playing our game.\ntype player struct {\n\tname string\n\tscore int\n}\n\nfunc main() {\n\n\t// Declare a map with initial values using a map literal.\n\tplayers := map[string]player{\n\t\t\"anna\": {\"Anna\", 42},\n\t\t\"jacob\": {\"Jacob\", 21},\n\t}\n\n\t// Trying to take the address of a map element fails.\n\tanna := \u0026players[\"anna\"]\n\tanna.score++\n\n\t// ./example4.go:23:10: cannot take the address of players[\"anna\"]\n\n\t// Instead take the element, modify it, and put it back.\n\tplayer := players[\"anna\"]\n\tplayer.score++\n\tplayers[\"anna\"] = player\n}\n","Hash":"YGUt0if566M5YypaDFfhAZoxEcg="},{"Name":"example7.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how maps are reference types.\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\n\t// Initialize a map with values.\n\tscores := map[string]int{\n\t\t\"anna\": 21,\n\t\t\"jacob\": 12,\n\t}\n\n\t// Pass the map to a function to perform some mutation.\n\tdouble(scores, \"anna\")\n\n\t// See the change is visible in our map.\n\tfmt.Println(\"Score:\", scores[\"anna\"])\n}\n\n// double finds the score for a specific player and\n// multiplies it by 2.\nfunc double(scores map[string]int, player string) {\n\tscores[player] = scores[player] * 2\n}\n","Hash":"SddU6k+JgGZJfjANJKx04Cu5RY4="}]},{"Title":"تمرین‌ها","Content":"\n \u003ch2\u003eتمرین‌ها\u003c/h2\u003e\n \n \n \u003cp\u003e\n از الگو به عنوان نقطه شروع برای انجام تمرین‌ها استفاده کنید. یک راه حل ممکن ارائه شده است.\n \u003c/p\u003e\n \n\n \u003ch2\u003eتمرین 1\u003c/h2\u003e\n \n \n \u003cp\u003e\n یک نقشه از مقادیر صحیح با یک رشته به عنوان کلید تعریف کرده و سپس آن را پر کنید. پنج مقدار را در نقشه قرار دهید و سپس بر روی نقشه تکرار کنید تا جفت‌های کلید/مقدار را نمایش دهید.\n \u003c/p\u003e\n \n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n","Files":[{"Name":"exercise1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Declare and make a map of integer values with a string as the key. Populate the\n// map with five values and iterate over the map to display the key/value pairs.\npackage main\n\n// Add imports.\n\nfunc main() {\n\n\t// Declare and make a map of integer type values.\n\n\t// Initialize some data into the map.\n\n\t// Display each key/value pair.\n}\n","Hash":"PoSWCw/BHJ4kPAxNUSzsM13ZNf4="},{"Name":"answer1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Declare and make a map of integer values with a string as the key. Populate the\n// map with five values and iterate over the map to display the key/value pairs.\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\n\t// Declare and make a map of integer type values.\n\tdepartments := make(map[string]int)\n\n\t// Initialize some data into the map.\n\tdepartments[\"IT\"] = 20\n\tdepartments[\"Marketing\"] = 15\n\tdepartments[\"Executives\"] = 5\n\tdepartments[\"Sales\"] = 50\n\tdepartments[\"Security\"] = 8\n\n\t// Display each key/value pair.\n\tfor key, value := range departments {\n\t\tfmt.Printf(\"Dept: %s People: %d\\n\", key, value)\n\t}\n}\n","Hash":"zoAGUHlKI0Yk/YWIgmH4uaeDYNY="}]}]} ,"methods":{"Title":"متدها","Description":"یک تابع به نام یک متد فراخوانده می‌شود زمانی که آن تابع یک مشتری (receiver) داشته باشد.","Pages":[{"Title":"متدها","Content":"\n \u003ch2\u003eمتدها\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/training/individual-on-demand/ultimate-go-bundle/\" target=\"_blank\"\u003eتماشای ویدئو\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003eنیاز به کمک مالی دارید؟ از \u003ca href=\"https://www.ardanlabs.com/scholarship/\" target=\"_blank\"\u003eفرم Scholarship\u003c/a\u003e ما استفاده کنید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n یک تابع به نام یک متد فراخوانده می‌شود زمانی که آن تابع یک مشتری (receiver) داشته باشد. مشتری، پارامتری است که بین کلمه کلیدی func و نام تابع تعریف می‌شود.\n \u003c/p\u003e\n \n\n \u003ch2\u003eبررسی کد\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e1:\u003c/b\u003e تعریف و عملکرد مشتری\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e2:\u003c/b\u003e متدهای نوع داده‌های نامی\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e3:\u003c/b\u003e متغیرهای تابع/متد\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e4:\u003c/b\u003e انواع تابع\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e5:\u003c/b\u003e دستورات مقدار و اشاره‌گر\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n \u003ch2\u003eتعریف متد\u003c/h2\u003e\n \n \n \u003cp\u003e\n دو نوع مشتری وجود دارد، مشتری مقدار برای اجرای مفهوم مقدار و مشتری اشاره‌گر برای اجرای مفهوم اشاره‌گر.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype user struct {\n name string\n email string\n}\n\nfunc (u user) notify() {\n fmt.Printf(\u0026#34;Sending User Email To %s\u0026lt;%s\u0026gt;\\n\u0026#34;, u.name, u.email)\n}\n\nfunc (u *user) changeEmail(email string) {\n u.email = email\n fmt.Printf(\u0026#34;Changed User Email To %s\\n\u0026#34;, email)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n تابع notify با مشتری مقدار اجرا می‌شود. این بدان معناست که متد بر مبنای مفهوم مقدار اجرا می‌شود و بر روی نسخه خود از مقداری که برای انجام تماس استفاده شده، عمل می‌کند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n تابع changeEmail با مشتری اشاره‌گر اجرا می‌شود. این بدان معناست که متد بر مبنای مفهوم اشاره‌گر اجرا می‌شود و بر روی دسترسی به اشتراک گذاشته شده به مقداری که برای انجام تماس استفاده شده، عمل می‌کند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n به جز چند استثنا، مجموعه متد برای یک نوع نباید شامل ترکیبی از مشتری مقدار و مشتری اشاره‌گر باشد. انطباق دقیق معنایی داده بسیار مهم است و این شامل تعریف متدها نیز می‌شود.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eفراخوانی متدها\u003c/h2\u003e\n \n \n \u003cp\u003e\n در زمان فراخوانی یک متد، کامپایلر مهم نمی‌داند که مقدار مورد استفاده برای انجام تماس با مفهوم داده مشتری تطابق دقیق داشته باشد یا نه. کامپایلر فقط می‌خواهد یک مقدار یا اشاره‌گری از همان نوع داشته باشد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003ebill := user{\u0026#34;Bill\u0026#34;, \u0026#34;bill@email.com\u0026#34;}\nbill.notify()\nbill.changeEmail(\u0026#34;bill@hotmail.com\u0026#34;)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n می‌بینید که یک مقدار از نوع کاربر ایجاد و به متغیر بیل اختصاص داده شده است. در مورد تماس با notify، متغیر بیل با نوع مشتری همخوانی دارد که از یک مشتری مقدار استفاده می‌کند. در مورد تماس با changeEmail، متغیر بیل با نوع مشتری همخوانی ندارد که از یک مشتری اشاره‌گر استفاده می‌کند. با این حال، کامپایلر تماس متد را قبول می‌کند و متغیر بیل را با متد به اشتراک می‌گذارد. Go به منظور انجام تماس تنظیماتی انجام می‌دهد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n این کار وقتی انجام می‌شود که متغیری که برای انجام تماس استفاده می‌شود یک متغیر اشاره‌گر باشد، همانطور که توضیح داده شد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003ebill := \u0026amp;user{\u0026#34;Bill\u0026#34;, \u0026#34;bill@email.com\u0026#34;}\nbill.notify()\nbill.changeEmail(\u0026#34;bill@hotmail.com\u0026#34;)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در این مورد، متغیر بیل یک متغیر اشاره‌گر به یک مقدار از نوع کاربر است. دوباره، Go تنظیماتی را انجام می‌دهد تا تماس متد را برقرار کند هنگام فراخوانی متد notify.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n اگر Go تنظیماتی انجام نمی‌داد، آنگاه این است که باید برای انجام همان تماس‌های متد اقدام می‌کردید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003ebill := user{\u0026#34;Bill\u0026#34;, \u0026#34;bill@email.com\u0026#34;}\n(\u0026amp;bill).changeEmail(\u0026#34;bill@hotmail.com\u0026#34;)\n\nbill := \u0026amp;user{\u0026#34;Bill\u0026#34;, \u0026#34;bill@email.com\u0026#34;}\n(*bill).notify()\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خوشحالم که برای انجام تماس‌های متد در Go نیازی به انجام این کارها نیست.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eرهنمود داده‌های داده‌ای برای انواع داخلی\u003c/h2\u003e\n \n \n \u003cp\u003e\n به عنوان یک رهنمود، اگر داده‌ای که با آن کار می‌کنم نوع داخلی باشد (اسلایس، مپ، کانال، تابع، رابط)، در آن صورت از داده‌دانی ارزشی برای انتقال داده در برنامه استفاده کنید. این شامل تعریف فیلدها در یک نوع هم می‌شود. اما هنگامی که می‌خواهم خواندن و نوشتن را انجام دهم، باید به خاطر داشته باشم که از داده‌دانی اشاره‌گری استفاده می‌کنم.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype IP []byte\ntype IPMask []byte\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این انواع در پکیج net که جزو کتابخانه استاندارد است تعریف شده‌اند. آنها با یک نوع اساسی که یک اسلایس از بایت‌هاست تعریف شده‌اند. به علت این موضوع، این انواع به رهنمودهای انواع داخلی پیروی می‌کنند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc (ip IP) Mask(mask IPMask) IP {\n if len(mask) == IPv6len \u0026amp;\u0026amp; len(ip) == IPv4len \u0026amp;\u0026amp; allFF(mask[:12]) {\n mask = mask[12:]\n }\n if len(mask) == IPv4len \u0026amp;\u0026amp; len(ip) == IPv6len \u0026amp;\u0026amp;\n bytesEqual(ip[:12], v4InV6Prefix) {\n ip = ip[12:]\n }\n n := len(ip)\n if n != len(mask) {\n return nil\n }\n out := make(IP, n)\n for i := 0; i \u0026lt; n; i\u0026#43;\u0026#43; {\n out[i] = ip[i] \u0026amp; mask[i]\n }\n return out\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n با متد Mask، سمانتیک مقادیر برای هر دو گیرنده، پارامتر و آرگومان برگشتی در حال اجرا است. این متد یک کپی از مقدار Mask را می‌پذیرد، این مقدار را تغییر می‌دهد، و سپس یک کپی از تغییر را برمی‌گرداند. این متد از سمانتیک مقداری برای تغییرات استفاده می‌کند. این به طور اتفاقی یا تصادفی نیست.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n یک تابع می‌تواند تصمیم بگیرد که چه ورودی و خروجی داده‌ای نیاز دارد. اما نمی‌تواند در مورد سمانتیک داده‌ها برای نحوه جریان داده‌ها تصمیم بگیرد. داده‌ها این تصمیم را می‌گیرند و تابع باید با آن تطابق کند. به همین دلیل Mask یک رابط تغییری با سمانتیک مقداری اجرایی می‌کند. این باید با نحوه حرکت یک آرایه در برنامه مطابقت داشته باشد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc ipEmptyString(ip IP) string {\n if len(ip) == 0 {\n return \u0026#34;\u0026#34;\n }\n return ip.String()\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n تابع \u003ccode\u003eipEmptyString\u003c/code\u003e همچنین برای ورودی و خروجی از سمانتیک مقداری استفاده می‌کند. این تابع یک کپی از یک مقدار IP را می‌پذیرد و یک مقدار رشته را برمی‌گرداند. هیچ استفاده‌ای از سمانتیک نشانگر وجود ندارد زیرا داده سمانتیک داده را تعیین می‌کند و نه تابع.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n یک استثناء از استفاده از سمانتیک مقداری وقتی به وجود می‌آید که نیاز به اشتراک‌گذاری یک آرایه یا نقشه با یک تابعی است که عملیات بازگشت دادن یا رمزگشایی انجام می‌دهد.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eراهنمای سمانتیک داده برای انواع ساختاری\u003c/h2\u003e\n \n \n \u003cp\u003e\n به عنوان یک راهنمای کلی، اگر داده‌هایی که با آن کار می‌کنید از نوع ساختاری باشند، باید به این فکر بیفتید که داده چه چیزی را نمایان می‌کند تا تصمیم بگیرید. یک قانون کلی خوب این است که بپرسید که آیا ساختار داده‌ای را نمایان می‌کند یا یک رابط (API). اگر ساختار داده را نمایان می‌کند، از سمانتیک مقداری استفاده کنید. اگر ساختار یک رابط را نمایان می‌کند، از سمانتیک نشانگری استفاده کنید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype Time struct {\n sec int64\n nsec int32\n loc *Location\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n اینجا ساختار زمان (Time) از بسته زمان (time) آمده است. اگر زمان را به عنوان داده در نظر بگیرید، باید برای این ساختار از سمانتیک مقداری استفاده شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n وقتی کد موجودی را می‌بینید و می‌خواهید بدانید کدام سمانتیک داده انتخاب شده است، به دنبال یک تابع کارخانه بگردید. نوع بازگشتی یک تابع کارخانه باید سمانتیک داده‌ای که توسط توسعه‌دهنده انتخاب شده است را مشخص کند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc Now() Time {\n sec, nsec := now()\n return Time{sec \u0026#43; unixToInternal, nsec, Local}\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n Now تابع کارخانه برای ساخت مقادیر زمان (Time) است. به بازگشت نگاه کنید، از سمانتیک مقداری استفاده می‌کند. این به شما می‌گوید که برای مقادیر زمان باید از سمانتیک مقداری استفاده کنید که به این معناست که هر تابع کپی جداگانه‌ای از یک مقدار زمان (Time) دریافت می‌کند و فیلدها در یک ساختار به عنوان مقادیر نوع Time اعلام می‌شوند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc (t Time) Add(d Duration) Time {\n t.sec \u0026#43;= int64(d / 1e9)\n nsec := int32(t.nsec) \u0026#43; int32(d%1e9)\n if nsec \u0026gt;= 1e9 {\n t.sec\u0026#43;\u0026#43;\n nsec -= 1e9\n } else if nsec \u0026lt; 0 {\n t.sec--\n nsec \u0026#43;= 1e9\n }\n t.nsec = nsec\n return t\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n Add یک متد است که نیاز دارد تا یک عملیات تغییری را انجام دهد. اگر به دقت نگاه کنید، خواهید دید که تابع از سمانتیک مقداری برای انجام عملیات تغییری استفاده می‌کند. متد Add نسخه‌ای از مقدار Time که برای فراخوانی تابع استفاده شده است را می‌گیرد، نسخهٔ خودش از آن را تغییر می‌دهد، سپس یک نسخه جدید از آن را به فراخواننده برمی‌گرداند. دوباره، این روش ایمن‌ترین راه برای انجام یک عملیات تغییری است.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc div(t Time, d Duration) (qmod2 int, r Duration) {}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در اینجا مثال دیگری از تابع div آمده است که یک مقدار از نوع Time و Duration (int64) را می‌پذیرد، سپس مقادیری از نوع int و Duration برمی‌گرداند. برای نوع Time و تمام نوع‌های داخلی (built-in) از سمانتیک مقداری استفاده می‌شود. Duration دارای یک نوع داخلی به نام int64 است.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc (t *Time) UnmarshalBinary(data []byte) error {}\nfunc (t *Time) GobDecode(data []byte) error {}\nfunc (t *Time) UnmarshalJSON(data []byte) error {}\nfunc (t *Time) UnmarshalText(data []byte) error {}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این چهار متد از بستهٔ Time به نظر می‌آید که قوانین سمانتیک داده را نقض می‌کنند. آن‌ها از سمانتیک اشاره‌گری استفاده می‌کنند، چرا؟ چرا که آن‌ها یک رابط را پیاده‌سازی می‌کنند که امضای متد در آن قفل شده است. از آنجا که پیاده‌سازی نیاز به تغییر دارد، سمانتیک اشاره‌گری تنها انتخاب ممکن است.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n در اینجا یک راهنما وجود دارد: اگر سمانتیک مقداری در حال اجرا باشد، می‌توانید برای برخی از توابع به سمانتیک اشاره‌گری تغییر دهید، به شرطی که دیگرین در زنجیرهٔ فراخوانی بازیابی به سمانتیک مقداری ننشانده شوند. یک بار که به سمانتیک اشاره‌گری تغییر دادید، همهٔ فراخوانی‌های آینده از آن نقطه باید به سمانتیک اشاره‌گری پایبند بمانند. شما هرگز نمی‌توانید از اشاره‌گر به مقدار برگردید. هیچ‌وقت، هیچ‌وقت، هیچ‌وقت نمی‌توانید از اشاره‌گر به مقدار برگردید. ایجاد یک کپی از یک مقداری که یک اشاره‌گر به آن اشاره می‌کند هیچ‌گاه ایمن نیست.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc Open(name string) (file *File, err error) {\n return OpenFile(name, O_RDONLY, 0)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n تابع Open از بستهٔ os نشان می‌دهد که هنگام استفاده از مقدار نوع File، سمانتیک اشاره‌گری در حال اجرا است. مقادیر File باید به اشتراک گذاشته شوند و هرگز نباید کپی شوند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc (f *File) Chdir() error {\n if f == nil {\n return ErrInvalid\n }\n if e := syscall.Fchdir(f.fd); e != nil {\n return \u0026amp;PathError{\u0026#34;chdir\u0026#34;, f.name, e}\n }\n return nil\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n متد Chdir از یک رسیور اشاره‌گری استفاده می‌کند، حتی اگر این متد مقدار File را تغییر ندهد. این به این دلیل است که مقادیر File باید به اشتراک گذاشته شوند و نمی‌توانند کپی شوند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc epipecheck(file *File, e error) {\n if e == syscall.EPIPE {\n if atomic.AddInt32(\u0026amp;file.nepipe, 1) \u0026gt;= 10 {\n sigpipe()\n }\n } else {\n atomic.StoreInt32(\u0026amp;file.nepipe, 0)\n }\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n تابع epipecheck همچنین مقادیر File را با استفاده از اشاره‌گرها به صورت نحوه استفاده از اشاره‌گرها می‌پذیرد.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eمتدها فقط توابع هستند\u003c/h2\u003e\n \n \n \u003cp\u003e\n واقعاً متدها فقط توابعی هستند که قندشکری نحوی فراهم می‌کنند تا این امکان فراهم شود که داده‌ها رفتار نشان دهند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype data struct {\n name string\n age int\n}\n\nfunc (d data) displayName() {\n fmt.Println(\u0026#34;My Name Is\u0026#34;, d.name)\n}\n\nfunc (d *data) setAge(age int) {\n d.age = age\n fmt.Println(d.name, \u0026#34;Is Age\u0026#34;, d.age)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n یک نوع (type) و دو متد (method) اعلام شده‌اند. متد displayName از سمانتیک مقدار (value semantics) استفاده می‌کند و متد setAge از سمانتیک اشاره‌گر (pointer semantics) استفاده می‌کند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n توجه: در Go از ستر و گترها (setters و getters) استفاده نکنید. این‌ها APIs با هدف نیستند و در این موارد بهتر است که این فیلدها را برای دسترسی عمومی (export) قرار دهید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003ed := data{\n name: \u0026#34;Bill\u0026#34;,\n}\n\nd.displayName()\nd.setAge(21)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n یک مقدار از نوع data ساخته شده و تماس‌های متد انجام شده است.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003edata.displayName(d)\n(*data).setAge(\u0026amp;d, 21)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n از آنجایی که متد‌ها در واقع توابعی با شکر و سینتکتیک آرایه هستند، می‌توان متدها را مانند توابع اجرا کرد. می‌بینید که گیرنده در واقع یک پارامتر است، این اولین پارامتر است. وقتی شما یک متد را فرا می‌خوانید، کامپایلر آن را به یک تماس تابع تبدیل می‌کند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n توجه: اینجا متدها را به این صورت اجرا نکنید، اما ممکن است این نحوه نوشتار در پیام‌های ابزاردها را ببینید.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eنحوه عملکرد کد را بشناسید\u003c/h2\u003e\n \n \n \u003cp\u003e\n اگر شما سمانتیک داده در بازی را بشناسید، آنگاه شما عملکرد کد را می‌شناسید. اگر شما عملکرد کد را می‌شناسید، آنگاه هزینه کد را می‌شناسید. بعد از آن، من درحال مهندسی هستم.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n در اینجا نوع و مجموعه متدهای داده داده شده است.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype data struct {\n name string\n age int\n}\n\nfunc (d data) displayName() {\n fmt.Println(\u0026#34;My Name Is\u0026#34;, d.name)\n}\n\nfunc (d *data) setAge(age int) {\n d.age = age\n fmt.Println(d.name, \u0026#34;Is Age\u0026#34;, d.age)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n You can write the following code.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc main() {\n d := data{\n name: \u0026#34;Bill\u0026#34;,\n }\n\n f1 := d.displayName\n f1()\n d.name = \u0026#34;Joan\u0026#34;\n f1()\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eMy Name Is Bill\nMy Name Is Bill\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n شما با ساختن یک مقدار از نوع Data و اختصاص دادن آن به متغیر d شروع می‌کنید. سپس متد displayName را به d بسته و آن را به متغیری به نام f1 اختصاص می‌دهید. این یک فراخوانی متد نیست بلکه یک اختصاص است که یک سطح نهان ایجاد می‌کند. توابع مقادیر در Go هستند و به مجموعه‌ای از انواع داده داخلی تعلق دارند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n بعد از اختصاص، شما می‌توانید متد را به صورت غیرمستقیم از طریق استفاده از متغیر f1 فراخوانی کنید. این کار نام Bill را نمایش می‌دهد. سپس داده را تغییر می‌دهید تا نام Joan شود و دوباره متد را از طریق متغیر f1 فراخوانی می‌کنید. تغییر را نمی‌بینید. دوباره خروجی Bill است. پس چرا؟\n \u003c/p\u003e\n \n\n \u003ca href=\"/tour/eng/static/img/m1.png\" target=\"_blank\"\u003e\n \u003cimg class=\"codeimg\" src=\"/tour/eng/static/img/m1.png\"\u003e\n \u003c/a\u003e\n\n \n \u003cp\u003e\n این به سمانتیک داده در بازی مربوط دارد. متد displayName از یک گیرنده مقدار استفاده می‌کند، بنابراین سمانتیک مقدار در حال بازی است.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc (d data) displayName() {\n fmt.Println(\u0026#34;My Name Is\u0026#34;, d.name)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این بدان معناست که متغیر f1 کپی خود d را نگه می‌دارد و علیه کپی خودش عمل می‌کند. بنابراین فراخوانی متد از طریق متغیر f1 همیشه از کپی استفاده می‌کند و این کپی در برابر تغییر محافظت می‌شود. این چیزی است که با سمانتیک مقدار می‌خواهید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n حالا همان کار را با متد setAge انجام می‌دهید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc main() {\n d := data{\n name: \u0026#34;Bill\u0026#34;,\n }\n\n f2 := d.setAge\n f2(45)\n d.name = \u0026#34;Sammy\u0026#34;\n f2(45)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eBill Is Age 45\nSammy Is Age 45\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در این باره، متد setAge به متغیر f2 اختصاص داده می‌شود. دوباره، متد به طور غیرمستقیم از طریق متغیر f2 فراخوانی می‌شود و عدد 45 برای سن Bill ارسال می‌شود. سپس نام Bill به Sammy تغییر می‌کند و دوباره از متغیر f2 برای انجام تماس استفاده می‌شود. این بار می‌بینید که نام تغییر کرده است.\n \u003c/p\u003e\n \n\n \u003ca href=\"/tour/eng/static/img/m2.png\" target=\"_blank\"\u003e\n \u003cimg class=\"codeimg\" src=\"/tour/eng/static/img/m2.png\"\u003e\n \u003c/a\u003e\n\n \n \u003cp\u003e\n متد setAge از گیرنده نقطه‌ای استفاده می‌کند، بنابراین setAge بر روی کپی خود متغیر d عمل نمی‌کند، بلکه به طور مستقیم روی متغیر d عمل می‌کند. بنابراین، f2 بر روی دسترسی مشترک عمل می‌کند و تغییر را مشاهده می‌کنید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc (d *data) setAge(age int) {\n d.age = age\n fmt.Println(d.name, \u0026#34;Is Age\u0026#34;, d.age)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n بدون دانستن سمانتیک داده در حال بازی، شما نمی‌توانید رفتار کد را بفهمید. این سمانتیک داده‌ها واقعی هستند و بر رفتار تأثیر می‌گذارند.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eیادداشت‌ها\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003eمتدها توابعی هستند که متغیر گیرنده را اعلام می‌کنند.\u003c/li\u003e\n \n \u003cli\u003eگیرنده‌ها یک متد را به یک نوع متصل می‌کنند و می‌توانند از سمانتیک مقدار یا اشاره‌گر استفاده کنند.\u003c/li\u003e\n \n \u003cli\u003eسمانتیک مقدار به معنای ارسال یک کپی از مقدار از مرزهای برنامه است.\u003c/li\u003e\n \n \u003cli\u003eسمانتیک اشاره‌گر به معنای ارسال یک کپی از آدرس مقادیر از مرزهای برنامه است.\u003c/li\u003e\n \n \u003cli\u003eبه یک سمانتیک تنها برای یک نوع داده داده و پایبند باشید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eنقل‌قول‌ها\u003c/h2\u003e\n \n \n \u003cp\u003e\n \u0026#34;متدها وقتی معقول یا منطقی باشد که یک قطعه داده بتواند یک قابلیت را بیرون بیاورد.\u0026#34; - ویلیام کندی\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eمطالعه اضافی\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://golang.org/doc/effective_go.html#methods\" target=\"_blank\"\u003eMethods\u003c/a\u003e \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2014/05/methods-interfaces-and-embedded-types.html\" target=\"_blank\"\u003eMethods, Interfaces and Embedded Types in Go\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2018/01/escape-analysis-flaws.html\" target=\"_blank\"\u003eEscape-Analysis Flaws\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eتمرین‌ها\u003c/h2\u003e\n \n \n \u003cp\u003e\n از قالب به عنوان نقطه شروع برای انجام تمرین‌ها استفاده کنید. یک راه‌حل ممکن نیز ارائه شده است.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eتمرین 1\u003c/h2\u003e\n \n \n \u003cp\u003e\n یک ساختار داده اعلام کنید که یک بازیکن بیسبال را نمایندگی می‌کند. شامل نام، تعداد حضور در بازی (atBats) و تعداد ضربه‌ها (hits) باشد.\n\n\n یک متد اعلام کنید که میانگین بتینگ بازیکن را محاسبه می‌کند. فرمول این میانگین به شکل ضربه‌ها تقسیم بر حضور در بازی است (Hits / AtBats).\n\n\n یک برش از این نوع تعریف کرده و آن را با چندین بازیکن مقداردهی اولیه کنید. روی برش حلقه بزنید و نام بازیکنان و میانگین بتینگ آن‌ها را نمایش دهید.\n \u003c/p\u003e\n \n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n","Files":[{"Name":"example1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to declare methods and how the Go\n// compiler supports them.\npackage main\n\nimport (\n\t\"fmt\"\n)\n\n// user defines a user in the program.\ntype user struct {\n\tname string\n\temail string\n}\n\n// notify implements a method with a value receiver.\nfunc (u user) notify() {\n\tfmt.Printf(\"Sending User Email To %s\u003c%s\u003e\\n\",\n\t\tu.name,\n\t\tu.email)\n}\n\n// changeEmail implements a method with a pointer receiver.\nfunc (u *user) changeEmail(email string) {\n\tu.email = email\n}\n\nfunc main() {\n\n\t// Values of type user can be used to call methods\n\t// declared with both value and pointer receivers.\n\tbill := user{\"Bill\", \"bill@email.com\"}\n\tbill.changeEmail(\"bill@hotmail.com\")\n\tbill.notify()\n\n\t// Pointers of type user can also be used to call methods\n\t// declared with both value and pointer receiver.\n\tjoan := \u0026user{\"Joan\", \"joan@email.com\"}\n\tjoan.changeEmail(\"joan@hotmail.com\")\n\tjoan.notify()\n\n\t// Create a slice of user values with two users.\n\tusers := []user{\n\t\t{\"ed\", \"ed@email.com\"},\n\t\t{\"erick\", \"erick@email.com\"},\n\t}\n\n\t// Iterate over the slice of users switching\n\t// semantics. Not Good!\n\tfor _, u := range users {\n\t\tu.changeEmail(\"it@wontmatter.com\")\n\t}\n\n\t// Exception example: Using pointer semantics\n\t// for a collection of strings.\n\tkeys := make([]string, 10)\n\tfor i := range keys {\n\t\tkeys[i] = func() string { return \"key-gen\" }()\n\t}\n}\n","Hash":"au4D4R/VnVKngght4Rc2XGdemGk="},{"Name":"example2.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to declare methods against\n// a named type.\npackage main\n\nimport \"fmt\"\n\n// duration is a named type that represents a duration\n// of time in Nanosecond.\ntype duration int64\n\nconst (\n\tnanosecond duration = 1\n\tmicrosecond = 1000 * nanosecond\n\tmillisecond = 1000 * microsecond\n\tsecond = 1000 * millisecond\n\tminute = 60 * second\n\thour = 60 * minute\n)\n\n// setHours sets the specified number of hours.\nfunc (d *duration) setHours(h float64) {\n\t*d = duration(h * float64(hour))\n}\n\n// hours returns the duration as a floating point number of hours.\nfunc (d duration) hours() float64 {\n\thours := d / hour\n\tnsec := d % hour\n\treturn float64(hours) + (float64(nsec) * (1e-9 / 60 / 60))\n}\nfunc main() {\n\n\t// Declare a variable of type duration set to\n\t// its zero value.\n\tvar dur duration\n\n\t// Change the value of dur to equal\n\t// five hours.\n\tdur.setHours(5)\n\n\t// Display the new value of dur.\n\tfmt.Println(\"Hours:\", dur.hours())\n}\n","Hash":"Qo/VDjAqrqB7Al2Dk1Y17zm3b3k="},{"Name":"example3.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to declare function variables.\npackage main\n\nimport \"fmt\"\n\n// data is a struct to bind methods to.\ntype data struct {\n\tname string\n\tage int\n}\n\n// displayName provides a pretty print view of the name.\nfunc (d data) displayName() {\n\tfmt.Println(\"My Name Is\", d.name)\n}\n\n// setAge sets the age and displays the value.\nfunc (d *data) setAge(age int) {\n\td.age = age\n\tfmt.Println(d.name, \"Is Age\", d.age)\n}\n\nfunc main() {\n\n\t// Declare a variable of type data.\n\td := data{\n\t\tname: \"Bill\",\n\t}\n\n\tfmt.Println(\"Proper Calls to Methods:\")\n\n\t// How we actually call methods in Go.\n\td.displayName()\n\td.setAge(45)\n\n\tfmt.Println(\"\\nWhat the Compiler is Doing:\")\n\n\t// This is what Go is doing underneath.\n\tdata.displayName(d)\n\t(*data).setAge(\u0026d, 45)\n\n\t// =========================================================================\n\n\tfmt.Println(\"\\nCall Value Receiver Methods with Variable:\")\n\n\t// Declare a function variable for the method bound to the d variable.\n\t// The function variable will get its own copy of d because the method\n\t// is using a value receiver.\n\tf1 := d.displayName\n\n\t// Call the method via the variable.\n\tf1()\n\n\t// Change the value of d.\n\td.name = \"Joan\"\n\n\t// Call the method via the variable. We don't see the change.\n\tf1()\n\n\t// =========================================================================\n\n\tfmt.Println(\"\\nCall Pointer Receiver Method with Variable:\")\n\n\t// Declare a function variable for the method bound to the d variable.\n\t// The function variable will get the address of d because the method\n\t// is using a pointer receiver.\n\tf2 := d.setAge\n\n\t// Call the method via the variable.\n\tf2(45)\n\n\t// Change the value of d.\n\td.name = \"Sammy\"\n\n\t// Call the method via the variable. We see the change.\n\tf2(45)\n}\n","Hash":"PrZm/jf6ZqVoeKrtnhqP4nt8HyI="},{"Name":"example4.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to declare and use function types.\npackage main\n\nimport \"fmt\"\n\n// event displays global events.\nfunc event(message string) {\n\tfmt.Println(message)\n}\n\n// data is a struct to bind methods to.\ntype data struct {\n\tname string\n\tage int\n}\n\n// event displays events for this data.\nfunc (d *data) event(message string) {\n\tfmt.Println(d.name, message)\n}\n\n// =============================================================================\n\n// fireEvent1 uses an anonymous function type.\nfunc fireEvent1(f func(string)) {\n\tf(\"anonymous\")\n}\n\n// handler represents a function for handling events.\ntype handler func(string)\n\n// fireEvent2 uses a function type.\nfunc fireEvent2(h handler) {\n\th(\"handler\")\n}\n\n// =============================================================================\n\nfunc main() {\n\n\t// Declare a variable of type data.\n\td := data{\n\t\tname: \"Bill\",\n\t}\n\n\t// Use the fireEvent1 handler that accepts any\n\t// function or method with the right signature.\n\tfireEvent1(event)\n\tfireEvent1(d.event)\n\n\t// Use the fireEvent2 handler that accepts any\n\t// function or method of type `handler` or any\n\t// literal function or method with the right signature.\n\tfireEvent2(event)\n\tfireEvent2(d.event)\n\n\t// Declare a variable of type handler for the\n\t// global and method based event functions.\n\th1 := handler(event)\n\th2 := handler(d.event)\n\n\t// User the fireEvent2 handler that accepts\n\t// values of type handler.\n\tfireEvent2(h1)\n\tfireEvent2(h2)\n\n\t// User the fireEvent1 handler that accepts\n\t// any function or method with the right signature.\n\tfireEvent1(h1)\n\tfireEvent1(h2)\n}\n","Hash":"YNXA230N71kkz6RIa1V1aJf5pVM="},{"Name":"example5.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\nimport (\n\t\"sync/atomic\"\n\t\"syscall\"\n)\n\n// Sample code to show how it is important to use value or pointer semantics\n// in a consistent way. Choose the semantic that is reasonable and practical\n// for the given type and be consistent. One exception is an unmarshal\n// operation since that always requires the address of a value.\n\n// *****************************************************************************\n\n// These is a named type from the net package called IP and IPMask with a base\n// type that is a slice of bytes. Since we use value semantics for reference\n// types, the implementation is using value semantics for both.\n\ntype IP []byte\ntype IPMask []byte\n\n// Mask is using a value receiver and returning a value of type IP. This\n// method is using value semantics for type IP.\n\nfunc (ip IP) Mask(mask IPMask) IP {\n\tif len(mask) == IPv6len \u0026\u0026 len(ip) == IPv4len \u0026\u0026 allFF(mask[:12]) {\n\t\tmask = mask[12:]\n\t}\n\tif len(mask) == IPv4len \u0026\u0026 len(ip) == IPv6len \u0026\u0026 bytesEqual(ip[:12], v4InV6Prefix) {\n\t\tip = ip[12:]\n\t}\n\tn := len(ip)\n\tif n != len(mask) {\n\t\treturn nil\n\t}\n\tout := make(IP, n)\n\tfor i := 0; i \u003c n; i++ {\n\t\tout[i] = ip[i] \u0026 mask[i]\n\t}\n\treturn out\n}\n\n// ipEmptyString accepts a value of type IP and returns a value of type string.\n// The function is using value semantics for type IP.\n\nfunc ipEmptyString(ip IP) string {\n\tif len(ip) == 0 {\n\t\treturn \"\"\n\t}\n\treturn ip.String()\n}\n\n// *****************************************************************************\n\n// Should time use value or pointer semantics? If you need to modify a time\n// value should you mutate the value or create a new one?\n\ntype Time struct {\n\tsec int64\n\tnsec int32\n\tloc *Location\n}\n\n// Factory functions dictate the semantics that will be used. The Now function\n// returns a value of type Time. This means we should be using value\n// semantics and copy Time values.\n\nfunc Now() Time {\n\tsec, nsec := now()\n\treturn Time{sec + unixToInternal, nsec, Local}\n}\n\n// Add is using a value receiver and returning a value of type Time. This\n// method is using value semantics for Time.\n\nfunc (t Time) Add(d Duration) Time {\n\tt.sec += int64(d / 1e9)\n\tnsec := int32(t.nsec) + int32(d%1e9)\n\tif nsec \u003e= 1e9 {\n\t\tt.sec++\n\t\tnsec -= 1e9\n\t} else if nsec \u003c 0 {\n\t\tt.sec--\n\t\tnsec += 1e9\n\t}\n\tt.nsec = nsec\n\treturn t\n}\n\n// div accepts a value of type Time and returns values of built-in types.\n// The function is using value semantics for type Time.\n\nfunc div(t Time, d Duration) (qmod2 int, r Duration) {\n\t// Code here\n}\n\n// The only use pointer semantics for the `Time` api are these\n// unmarshal related functions.\n\nfunc (t *Time) UnmarshalBinary(data []byte) error {\nfunc (t *Time) GobDecode(data []byte) error {\nfunc (t *Time) UnmarshalJSON(data []byte) error {\nfunc (t *Time) UnmarshalText(data []byte) error {\n\n// *****************************************************************************\n\n// Factory functions dictate the semantics that will be used. The Open function\n// returns a pointer of type File. This means we should be using pointer\n// semantics and share File values.\n\nfunc Open(name string) (file *File, err error) {\n\treturn OpenFile(name, O_RDONLY, 0)\n}\n\n// Chdir is using a pointer receiver. This method is using pointer\n// semantics for File.\n\nfunc (f *File) Chdir() error {\n\tif f == nil {\n\t\treturn ErrInvalid\n\t}\n\tif e := syscall.Fchdir(f.fd); e != nil {\n\t\treturn \u0026PathError{\"chdir\", f.name, e}\n\t}\n\treturn nil\n}\n\n// epipecheck accepts a pointer of type File.\n// The function is using pointer semantics for type File.\n\nfunc epipecheck(file *File, e error) {\n\tif e == syscall.EPIPE {\n\t\tif atomic.AddInt32(\u0026file.nepipe, 1) \u003e= 10 {\n\t\t\tsigpipe()\n\t\t}\n\t} else {\n\t\tatomic.StoreInt32(\u0026file.nepipe, 0)\n\t}\n}\n","Hash":"FK/bO7t9FZJg21E4iopcF1AuQx0="},{"Name":"exercise1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Declare a struct that represents a baseball player. Include name, atBats and hits.\n// Declare a method that calculates a player's batting average. The formula is hits / atBats.\n// Declare a slice of this type and initialize the slice with several players. Iterate over\n// the slice displaying the players name and batting average.\npackage main\n\n// Add imports.\n\n// Declare a struct that represents a ball player.\n// Include fields called name, atBats and hits.\n\n// Declare a method that calculates the batting average for a player.\nfunc ( /* receiver */ ) average() /* return type */ {\n}\n\nfunc main() {\n\n\t// Create a slice of players and populate each player\n\t// with field values.\n\n\t// Display the batting average for each player in the slice.\n}\n","Hash":"34Nejt1R9Zc535gAjJpmtw/nR2U="},{"Name":"answer1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Declare a struct that represents a baseball player. Include name, atBats and hits.\n// Declare a method that calculates a players batting average. The formula is hits / atBats.\n// Declare a slice of this type and initialize the slice with several players. Iterate over\n// the slice displaying the players name and batting average.\npackage main\n\nimport \"fmt\"\n\n// player represents a person in the game.\ntype player struct {\n\tname string\n\tatBats int\n\thits int\n}\n\n// average calculates the batting average for a player.\nfunc (p *player) average() float64 {\n\tif p.atBats == 0 {\n\t\treturn 0.0\n\t}\n\n\treturn float64(p.hits) / float64(p.atBats)\n}\n\nfunc main() {\n\n\t// Create a few players.\n\tps := []player{\n\t\t{\"bill\", 10, 7},\n\t\t{\"jim\", 12, 6},\n\t\t{\"ed\", 6, 4},\n\t}\n\n\t// Display the batting average for each player.\n\tfor i := range ps {\n\t\tfmt.Printf(\"%s: AVG[.%.f]\\n\", ps[i].name, ps[i].average()*1000)\n\t}\n\n\t// Why did I not choose this form?\n\tfor _, p := range ps {\n\t\tfmt.Printf(\"%s: AVG[.%.f]\\n\", p.name, p.average()*1000)\n\t}\n}\n","Hash":"N9Xz00m121bwbSJT+35/3f86GAM="}]}]} ,"pointers":{"Title":"اشاره‌گرها (Pointers)","Description":"اشاره‌گرها (Pointers) هدف به اشتراک‌گذاری مقادیر در مرزهای برنامه را دارند.","Pages":[{"Title":"اشاره‌گرها (Pointers)","Content":"\n \u003ch2\u003eاشاره‌گرها (Pointers)\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/training/individual-on-demand/ultimate-go-bundle/\" target=\"_blank\"\u003eتماشای ویدئو\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003eنیاز به کمک مالی دارید؟ از \u003ca href=\"https://www.ardanlabs.com/scholarship/\" target=\"_blank\"\u003eفرم Scholarship\u003c/a\u003e ما استفاده کنید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n اشاره‌گرها (Pointers) هدف به اشتراک‌گذاری مقادیر در مرزهای برنامه را دارند. انواع مختلفی از مرزهای برنامه وجود دارد. معمول‌ترین مرز بین فراخوانی‌های تابعی است. همچنین یک مرز بین Goroutine ها وجود دارد که برای آن دفترچه‌های یادداشت دارید.\n \u003c/p\u003e\n \n\n \u003ch2\u003eبررسی کد\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e1:\u003c/b\u003e انتقال با مقدار\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e2:\u003c/b\u003e اشتراک‌گذاری داده I\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e3:\u003c/b\u003e اشتراک‌گذاری داده II\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e4:\u003c/b\u003e تجزیه و تحلیل خروج\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e5:\u003c/b\u003e رشد استک\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n \n \u003cp\u003e\n هنگامی که یک برنامه Go شروع می‌شود، Go runtime یک Goroutine ایجاد می‌کند. Goroutine‌ها نخ‌های سطح برنامه سبک با بسیاری از همان مفاهیم نخ‌های سیستم عاملی هستند. وظیفه آن‌ها مدیریت اجرای فیزیکی یک مجموعه متمایز از دستورات است. هر برنامه Go حداقل یک Goroutine دارد که به آن Goroutine اصلی می‌گویید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n هر Goroutine دارای یک بلوک حافظه به نام استک است. هر استک ابتدا به عنوان یک تخصیص 2048 بایت (2k) شروع می‌شود. این بسیار کوچک است، اما استک‌ها می‌توانند با گذر زمان به اندازه بزرگ شدن.\n \u003c/p\u003e\n \n\n \u003ca href=\"/tour/eng/static/img/p1.png\" target=\"_blank\"\u003e\n \u003cimg class=\"codeimg\" src=\"/tour/eng/static/img/p1.png\"\u003e\n \u003c/a\u003e\n\n \n \u003cp\u003e\n هر بار که یک تابع صدا زده میشود، یک بلوکی از فضای استک برای کمک به اجرای دستورات مربوط به آن تابع گوروتین برداشت میشود. هر بلوک حافظه فردی، یک قابلیت سازگاری میباشد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n سایز یک قاب برای یک تابع خاص در زمان کامپایل محاسبه میشود. هیچ مقداری نمیتواند بر روی استک ساخته شود مگر اینکه کامپایلر سایز آن مقدار را در زمان کامپایل بداند. اگر کامپایلر سایز یک مقدار را در زمان کامپایل نداند، مقدار باید در هیپ ساخته شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n استک ها خودتمیزکننده هستند و مقدار صفر در کمک به مقداردهی اولیه استک کمک میکند.\n\n\n هر بار که یک تابع صدا زده میشود و یک بلاک از حافظه برای آن بلاک خارج میشود، حافظه برای آن بلاک مقداردهی اولیه میشود، که همین است که استک خودتمیزکننده است. هنگام یک بازگشت تابع، حافظه فریم تنها به حال خود برده میشود زیرا نامعلوم است که آیا حافظه مورد نیاز دوباره استفاده خواهد شد یا خیر. مقداردهی اولیه حافظه در بازگشت نیازمندانه خواهد بود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u003cb\u003ePass\u003c/b\u003e \u003cb\u003eBy\u003c/b\u003e \u003cb\u003eValue\u003c/b\u003e\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n همه داده‌ها به صورت مقادیری در برنامه جابجا می‌شوند. این به این معناست که در حین عبور داده از مرزهای برنامه، هر تابع یا گوروتین نسخه اختصاصی از داده را در اختیار می‌گیرد. دو نوع داده وجود دارد که با آن‌ها کار خواهید کرد، خود مقدار (عدد صحیح، رشته، کاربر) یا آدرس مقدار. آدرس‌ها داده‌هایی هستند که باید در مرزهای برنامه کپی شده و ذخیره شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n این قطعه کد سعی می‌کند توضیح این موضوع را بدهد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc main() {\n\n // Declare variable of type int with a value of 10.\n count := 10\n\n // To get the address of a value, use the \u0026amp; operator.\n println(\u0026#34;count:\\tValue Of[\u0026#34;, count, \u0026#34;]\\tAddr Of[\u0026#34;, \u0026amp;count, \u0026#34;]\u0026#34;)\n\n // Pass a copy of the \u0026#34;value of\u0026#34; count (what’s in the box)\n // to the increment1 function.\n increment1(count)\n\n // Print out the \u0026#34;value of\u0026#34; and \u0026#34;address of\u0026#34; count.\n // The value of count will not change after the function call.\n println(\u0026#34;count:\\tValue Of[\u0026#34;, count, \u0026#34;]\\tAddr Of[\u0026#34;, \u0026amp;count, \u0026#34;]\u0026#34;)\n\n // Pass a copy of the \u0026#34;address of\u0026#34; count (where is the box)\n // to the increment2 function. This is still considered a pass by\n // value and not a pass by reference because addresses are values.\n increment2(\u0026amp;count)\n\n // Print out the \u0026#34;value of\u0026#34; and \u0026#34;address of\u0026#34; count.\n // The value of count has changed after the function call.\n println(\n \u0026#34;count:\\tValue Of[\u0026#34;, \n count, \u0026#34;]\\tAddr Of[\u0026#34;, \u0026amp;count, \u0026#34;]\u0026#34;)\n}\n\n// increment1 declares the function to accept its own copy of\n// and integer value.\nfunc increment1(inc int) {\n\n // Increment the local copy of the caller’s int value.\n inc\u0026#43;\u0026#43;\n println(\u0026#34;inc1:\\tValue Of[\u0026#34;, inc, \u0026#34;]\\tAddr Of[\u0026#34;, \u0026amp;inc, \u0026#34;]\u0026#34;)\n}\n\n// increment2 declares the function to accept its own copy of\n// an address that points to an integer value.\n// Pointer variables are literal types and are declared using *.\nfunc increment2(inc *int) {\n\n // Increment the caller’s int value through the pointer.\n *inc\u0026#43;\u0026#43;\n println(\n \u0026#34;inc2:\\tValue Of[\u0026#34;, \n inc, \u0026#34;]\\tAddr Of[\u0026#34;, \u0026amp;inc, \n \u0026#34;]\\tPoints To[\u0026#34;, *inc, \u0026#34;]\u0026#34;)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003ecount: Value Of[ 10 ] Addr Of[ 0xc000050738 ]\ninc1: Value Of[ 11 ] Addr Of[ 0xc000050730 ]\ncount: Value Of[ 10 ] Addr Of[ 0xc000050738 ]\ninc2: Value Of[ 0xc000050738 ] Addr Of[ 0xc000050748 ] Points To[ 11 ]\ncount: Value Of[ 11 ] Addr Of[ 0xc000050738 ]\u003c/pre\u003e\n \n\n\n \u003ch2\u003eنکات\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003eاستفاده از اشاره‌گرها برای به اشتراک گذاری داده استفاده میشود.\u003c/li\u003e\n \n \u003cli\u003eمقادیر در گو همواره به صورت pass by value هستند.\u003c/li\u003e\n \n \u003cli\u003e\u0026#34;مقدار\u0026#34;، چه درون جعبه است. \u0026#34;آدرس\u0026#34; ( \u0026amp; )، جعبه کجاست.\u003c/li\u003e\n \n \u003cli\u003eعملگر ( * ) یک متغیر اشاره‌گر را تعریف میکند و \u0026#34;مقداری که اشاره‌گر به آن اشاره میکند\u0026#34;.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eتجزیه و تحلیل فرار\u003c/h2\u003e\n \n \n \u003cp\u003e\n الگوریتمی که کامپایلر برای تشخیص این کاربرد مقدارها روی استک یا هیپ استفاده میکند، \u0026#34;تجزیه و تحلیل فرار\u0026#34; نامیده میشود. نام این الگوریتم باعث میشود به نظر برسد که مقدارها در ابتدا روی استک ساخته شده و سپس فرار میکنند (یا منتقل میشوند) به هیپ در صورت نیاز. اما این مطلب صحیح نیست. یک مقدار تنها یک بار ساخته میشود و الگوریتم تجزیه و تحلیل فرار تصمیم میگیرد که آن مقدار کجا ساخته شود (استک یا هیپ). تنها ساختن مقدار در هیپ در گو، یک اختصاص می‌باشد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n متوجه شدن از تجزیه و تحلیل فرار به معنای درک مالکیت مقدار است. ایده این است که وقتی یک مقدار در محدوده یک تابع ساخته میشود، آن تابع مالک مقدار است. از آنجا، سوال را درباره این پرسیده میکنیم که آیا مقداری که در حال ساخت است، باید هنوز هم وجود داشته باشد وقتی که تابع مالک برمیگردد؟ اگر پاسخ منفی باشد، مقدار میتواند از روی استک ساخته شود. اگر پاسخ مثبت باشد، مقدار باید در هیپ ساخته شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n توجه: قاعده مالکیت یک قاعده پایه برای شناسایی کدهایی است که باعث ایجاد اختصاص میشوند. با این حال، باید توجه داشت که تجزیه و تحلیل فرار خرابی‌هایی در خود دارد که ممکن است منجر به اختصاص‌های غیر قابل تشخیص شود. همچنین، این الگوریتم فرصت‌هایی را از طریق بهینه‌سازی‌های کامپایلر می‌گیرد تا تعداد اختصاص‌ها را کاهش دهد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e// user represents a user in the system.\ntype user struct {\n name string\n email string\n}\n\nfunc stayOnStack() user {\n u := user{\n name: \u0026#34;Bill\u0026#34;,\n email: \u0026#34;bill@email.com\u0026#34;,\n }\n\n return u\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n تابع stayOnStack با استفاده از سمانتیک مقدار، مقدار کاربر را به تماس گیرنده برگرداند. به عبارت دیگر، تماس گیرنده یک نسخه جدید از مقدار کاربر را دریافت می‌کند که در حال ساخت است.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n زمانی که تابع stayOnStack فراخوانی شده و برگشت داده می‌شود، مقدار کاربر که در حال ساخت است برای وجود ندارد، زیرا تماس گیرنده نسخه خود را دریافت کرده است. بنابراین، ساخت مقدار کاربر در داخل تابع stayOnStack می‌تواند در استک انجام شود. هیچ تخصیصی انجام نمی‌شود.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype user struct {\n name string\n email string\n}\n\nfunc escapeToHeap() *user {\n u := user{\n name: \u0026#34;Bill\u0026#34;,\n email: \u0026#34;bill@email.com\u0026#34;,\n }\n\n return \u0026amp;u\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n تابع escapeToHeap از معنای اشاره‌گرها (pointer semantics) برای بازگرداندن یک مقدار کاربر به تماس‌گیرنده استفاده می‌کند. به عبارت دیگر، تماس‌گیرنده دسترسی به اشتراک (یک آدرس) به مقدار کاربر در حال ساخت را دریافت می‌کند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n زمانی که تابع escapeToHeap فراخوانی می‌شود و بازگشت می‌دهد، مقدار کاربری که ایجاد می‌کند همچنان باید وجود داشته باشد، زیرا تماس‌گیرنده دسترسی به اشتراک به مقدار در حال ساخت دارد. بنابراین، ساخت مقدار کاربر در داخل تابع escapeToHeap نمی‌تواند در استک انجام شود، بلکه باید در هیپ انجام شود. بله، تخصیص حافظه.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n فکر کنید چه اتفاقی می‌افتد اگر مقدار کاربر در مثال اخیر در استک ساخته شود هنگام استفاده از معنای اشاره‌گرها در بازگردانی.\n \u003c/p\u003e\n \n\n \u003ca href=\"/tour/eng/static/img/p2.png\" target=\"_blank\"\u003e\n \u003cimg class=\"codeimg\" src=\"/tour/eng/static/img/p2.png\"\u003e\n \u003c/a\u003e\n\n \n \u003cp\u003e\n تماس‌گیرنده یک کپی از آدرس استک از قسمت پایین‌تری به اشتراک می‌گیرد و سلامتی از بین می‌رود. یک‌بار که کنترل به تابع تماس‌گیرنده برگشت داده می‌شود، حافظه استک که مقدار کاربر در آن وجود دارد دوباره قابل استفاده است. لحظه‌ای که تابع تماس‌گیرنده یک تماس دیگر به تابع انجام می‌دهد، یک فریم جدید برش داده می‌شود و حافظه بازنویسی می‌شود و مقدار به اشتراک‌گذاری شده را از بین می‌برد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n در نتیجه، بهتر است درباره استک به عنوان یک محیط خودپاک‌سازی فکر کنید. مقدار ابتدایی صفر به تمیز کردن هر فریم استک کمک می‌کند که نیاز دارید تا بدون استفاده از GC تمیز شود. استک به عنوان یک محیط خودپاک‌سازی در نظر گرفته می‌شود زیرا برای اجرای هر تماس تابع یک فریم گرفته و مقدار ابتدایی تنظیم می‌شود. استک در طول تماس‌های تابع تمیز می‌شود و نه در زمان بازگشت، زیرا کامپایلر نمی‌داند آیا حافظه در استک در آینده نیاز خواهد شد یا نه.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n تحلیل گریز تصمیم می‌گیرد که آیا یک مقدار باید در استک (مقدار پیش‌فرض) یا در هیپ (گریز) ایجاد شود. با تابع stayOnStack، من یک کپی از مقدار را به تماس‌گیرنده باز می‌گردانم، بنابراین ایمن است که مقدار را در استک نگه داریم. با تابع escapeToHeap، من یک کپی از آدرس مقدار را به تماس‌گیرنده (با به اشتراک‌گذاری در استک) باز می‌گردانم، بنابراین ایمن نیست که مقدار را در استک نگه داریم.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n تعداد زیادی جزئیات کوچک مرتبط با تحلیل گریز وجود دارد، بنابراین برای یادگیری بیشتر مقاله‌ای را در فصل 14 با عنوان \u0026#34;مکانیک‌های تحلیل گریز\u0026#34; بخوانید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n توجه: از نسخه 1.17 به بعد، Go ABI (رابط باینری برنامه) را تغییر داده و یک روش جدید برای انتقال آرگومان‌های ورودی و خروجی توابع با استفاده از رجیسترها به جای حافظه در استک پیاده‌سازی کرده است. این ویژگی برای سیستم‌های Linux، MacOS و Windows در معماری 64 بیتی x86 فعال شده است. این به این معناست که برخی از آرگومان‌های توابع در استک کپی نخواهند شد، اما برخی از آنها ممکن است به توجیه استفاده از رجیسترها بستگی داشته باشد. این تغییری در معنا و رفتار توصیف شده در این فصل ایجاد نمی‌کند.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eنکات\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003eزمانی که مقدار ممکن است بعد از بازگشت تابعی که مقدار را ایجاد می‌کند، مورد ارجاع قرار گیرد.\u003c/li\u003e\n \n \u003cli\u003eزمانی که کامپایلر تشخیص می‌دهد که یک مقدار بیش از حد بزرگ است تا در استک جا بیافتد.\u003c/li\u003e\n \n \u003cli\u003eزمانی که کامپایلر اندازه‌ی یک مقدار را در زمان کامپایل نمی‌داند.\u003c/li\u003e\n \n \u003cli\u003eزمانی که یک مقدار از طریق استفاده از مقادیر تابعی یا رابطی از یکدیگر جدا می‌شود.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eمعنای مدیریت زباله\u003c/h2\u003e\n \n \n \u003cp\u003e\n هنگامی که یک مقدار در هیپ ایجاد می‌شود، مجمع زباله (GC) باید درگیر شود. بخش مهم‌ترین GC الگوریتم تنظیم سرعت است. این الگوریتم تعیین می‌کند که میزان و سرعتی که GC باید اجرا شود را تا حفظ کوچکترین هیپ ممکن به همراه بهترین توانایی برنامه تعیین می‌کند.\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2018/12/garbage-collection-in-go-part1-semantics.html\" target=\"_blank\"\u003eGarbage Collection Semantics Part I\u003c/a\u003e - William Kennedy\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eتفاوت استک و هیپ\u003c/h2\u003e\n \n \n \u003cp\u003e\n \u0026#34;استک برای داده‌هایی استفاده می‌شود که فقط برای مدت زمان اجرای تابعی که آن را ایجاد می‌کند نیاز دارند و بدون هیچ هزینه‌ای با خروج از تابع بازیابی می‌شوند. هیپ برای داده‌هایی استفاده می‌شود که باید پس از خروج از تابعی که آن را ایجاد کرده استفاده شوند و به وسیله مجمع زباله‌ای گاهاً هزینه‌بر برگشت داده می‌شوند.\u0026#34; - آیان جورج\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eرشد استک\u003c/h2\u003e\n \n \n \u003cp\u003e\n اندازه هر قاب برای هر تابع در زمان کامپایل محاسبه می‌شود. این بدان معناست که اگر کامپایلر اندازه‌ی یک مقدار را در زمان کامپایل نداند، مقدار باید در هیپ ایجاد شود. یک مثال از این موضوع استفاده از تابع داخلی make برای ایجاد یک تاییده است که اندازه آن بر اساس یک متغیر است.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eb := make([]byte, size) // Backing array allocates on the heap.\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n Go از یک پیاده‌سازی ادامه‌دار برای تعیین نحوه رشد و کوچک‌شدن استک استفاده می‌کند. یکی از جایگزین‌هایی که Go می‌توانست استفاده کند، یک پیاده‌سازی استک تقسیم‌شده است که توسط برخی سیستم‌عامل‌ها استفاده می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n هر فراخوانی تابع با یک آغازنامه کوچک همراه است که می‌پرسد: \u0026#34;آیا فضای کافی برای این قاب جدید وجود دارد؟\u0026#34;. اگر بله باشد، هیچ مشکلی وجود ندارد و قاب گرفته شده و مقداردهی اولیه می‌شود. اگر نه باشد، باید یک استک بزرگتر جدید ایجاد شود و حافظه در استک موجود باید به استک جدید کپی شود. این نیاز به تغییر اشاره‌گرهایی دارد که به حافظه در استک اشاره دارند. مزایای حافظه ادامه‌دار و عبورهای خطی با سخت‌افزارهای مدرن، مقابله با هزینه کپی است.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n به دلیل استفاده از استک‌های ادامه‌دار، هیچ Goroutine‌ای نمی‌تواند اشاره‌گری به استک Goroutine دیگری داشته باشد. این به اندازه‌ی کافی هزینه دار است که اجرایی باید همه اشاره‌گرها به هر استک و تنظیم مجدد این اشاره‌گرها به مکان جدید را پیگیری کند.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eخواندن موارد بیشتر\u003c/h2\u003e\n \n \n \u003cp\u003e\n \u003cb\u003e Pointer\u003c/b\u003e \u003cb\u003eMechanics\u003c/b\u003e\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://golang.org/doc/effective_go.html#pointers_vs_values\" target=\"_blank\"\u003ePointers vs. Values\u003c/a\u003e \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2017/05/language-mechanics-on-stacks-and-pointers.html\" target=\"_blank\"\u003eLanguage Mechanics On Stacks And Pointers\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2014/12/using-pointers-in-go.html\" target=\"_blank\"\u003eUsing Pointers In Go\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2013/07/understanding-pointers-and-memory.html\" target=\"_blank\"\u003eUnderstanding Pointers and Memory Allocation\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n \u003cb\u003eStacks\u003c/b\u003e\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://docs.google.com/document/d/1wAaf1rYoM4S4gtnPh0zOlGzWtrZFQ5suE8qr2sD8uWQ/pub\" target=\"_blank\"\u003eContiguous Stack Proposal\u003c/a\u003e \u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n \u003cb\u003eEscape\u003c/b\u003e \u003cb\u003eAnalysis\u003c/b\u003e \u003cb\u003eand\u003c/b\u003e \u003cb\u003eInlining\u003c/b\u003e\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://docs.google.com/document/d/1CxgUBPlx9iJzkz9JWkb6tIpTe5q32QDmz8l0BouG0Cw\" target=\"_blank\"\u003eGo Escape Analysis Flaws\u003c/a\u003e \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://github.com/golang/go/wiki/CompilerOptimizations\" target=\"_blank\"\u003eCompiler Optimizations\u003c/a\u003e\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n \u003cb\u003eGarbage\u003c/b\u003e \u003cb\u003eCollection\u003c/b\u003e\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"http://gchandbook.org/\" target=\"_blank\"\u003eThe Garbage Collection Handbook\u003c/a\u003e \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://github.com/golang/proposal/blob/master/design/44167-gc-pacer-redesign.md\" target=\"_blank\"\u003eGC Pacer Redesign - 2021\u003c/a\u003e - Michael Knyszek \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://en.wikipedia.org/wiki/Tracing_garbage_collection\" target=\"_blank\"\u003eTracing Garbage Collection\u003c/a\u003e \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://blog.golang.org/go15gc\" target=\"_blank\"\u003eGo Blog - 1.5 GC\u003c/a\u003e \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.youtube.com/watch?v=aiv1JOfMjm0\u0026amp;index=16\u0026amp;list=PL2ntRZ1ySWBf-_z-gHCOR2N156Nw930Hm\" target=\"_blank\"\u003eGo GC: Solving the Latency Problem\u003c/a\u003e \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"http://rubinius.com/2013/06/22/concurrent-garbage-collection\" target=\"_blank\"\u003eConcurrent garbage collection\u003c/a\u003e \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://docs.google.com/document/d/1wmjrocXIWTr1JxU-3EQBI6BK6KgtiFArkG47XK73xIQ/edit\" target=\"_blank\"\u003eGo 1.5 concurrent garbage collector pacing\u003c/a\u003e \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://github.com/golang/proposal/blob/master/design/17503-eliminate-rescan.md\" target=\"_blank\"\u003eEliminating Stack Re-Scanning\u003c/a\u003e \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://groups.google.com/forum/m/#!topic/golang-nuts/KJiyv2mV2pU\" target=\"_blank\"\u003eWhy golang garbage-collector not implement Generational and Compact gc?\u003c/a\u003e - Ian Lance Taylor \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://blog.golang.org/ismmkeynote\" target=\"_blank\"\u003eGetting to Go: The Journey of Go\u0026#39;s Garbage Collector\u003c/a\u003e - Rick Hudson \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2018/12/garbage-collection-in-go-part1-semantics.html\" target=\"_blank\"\u003eGarbage Collection In Go : Part I - Semantics\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2019/05/garbage-collection-in-go-part2-gctraces.html\" target=\"_blank\"\u003eGarbage Collection In Go : Part II - GC Traces\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2019/07/garbage-collection-in-go-part3-gcpacing.html\" target=\"_blank\"\u003eGarbage Collection In Go : Part III - GC Pacing\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://blog.twitch.tv/en/2019/04/10/go-memory-ballast-how-i-learnt-to-stop-worrying-and-love-the-heap-26c2462549a2/\" target=\"_blank\"\u003eGo memory ballast: How I learnt to stop worrying and love the heap\u003c/a\u003e - Ross Engers \u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n \u003cb\u003eStatic\u003c/b\u003e \u003cb\u003eSingle\u003c/b\u003e \u003cb\u003eAssignment\u003c/b\u003e \u003cb\u003eOptimizations\u003c/b\u003e\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.youtube.com/watch?v=D2-gaMvWfQY\" target=\"_blank\"\u003eGopherCon 2015: Ben Johnson - Static Code Analysis Using SSA\u003c/a\u003e \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://godoc.org/golang.org/x/tools/go/ssa\" target=\"_blank\"\u003ePackage SSA\u003c/a\u003e \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.youtube.com/watch?v=FnGCDLhaxKU\" target=\"_blank\"\u003eUnderstanding Compiler Optimization\u003c/a\u003e\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n","Files":[{"Name":"example1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show the basic concept of pass by value.\npackage main\n\nfunc main() {\n\n\t// Declare variable of type int with a value of 10.\n\tcount := 10\n\n\t// Display the \"value of\" and \"address of\" count.\n\tprintln(\"count:\\tValue Of[\", count, \"]\\tAddr Of[\", \u0026count, \"]\")\n\n\t// Pass the \"value of\" the count.\n\tincrement(count)\n\n\tprintln(\"count:\\tValue Of[\", count, \"]\\tAddr Of[\", \u0026count, \"]\")\n}\n\n// increment declares count as a pointer variable whose value is\n// always an address and points to values of type int.\n//\n//go:noinline\nfunc increment(inc int) {\n\n\t// Increment the \"value of\" inc.\n\tinc++\n\tprintln(\"inc:\\tValue Of[\", inc, \"]\\tAddr Of[\", \u0026inc, \"]\")\n}\n","Hash":"pOmUh0KFKfsBDVKYeDM0vX9f3r0="},{"Name":"example2.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show the basic concept of using a pointer\n// to share data.\npackage main\n\nfunc main() {\n\n\t// Declare variable of type int with a value of 10.\n\tcount := 10\n\n\t// Display the \"value of\" and \"address of\" count.\n\tprintln(\"count:\\tValue Of[\", count, \"]\\t\\tAddr Of[\", \u0026count, \"]\")\n\n\t// Pass the \"address of\" count.\n\tincrement(\u0026count)\n\n\tprintln(\"count:\\tValue Of[\", count, \"]\\t\\tAddr Of[\", \u0026count, \"]\")\n}\n\n// increment declares count as a pointer variable whose value is\n// always an address and points to values of type int.\n//\n//go:noinline\nfunc increment(inc *int) {\n\n\t// Increment the \"value of\" count that the \"pointer points to\".\n\t*inc++\n\n\tprintln(\"inc:\\tValue Of[\", inc, \"]\\tAddr Of[\", \u0026inc, \"]\\tValue Points To[\", *inc, \"]\")\n}\n","Hash":"SkbfO370ljcCFrP1cKDb+FuFBws="},{"Name":"example3.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show the basic concept of using a pointer\n// to share data.\npackage main\n\nimport \"fmt\"\n\n// user represents a user in the system.\ntype user struct {\n\tname string\n\temail string\n\tlogins int\n}\n\nfunc main() {\n\n\t// Declare and initialize a variable named bill of type user.\n\tbill := user{\n\t\tname: \"Bill\",\n\t\temail: \"bill@ardanlabs.com\",\n\t}\n\n\t//** We don't need to include all the fields when specifying field\n\t// names with a struct literal.\n\n\t// Pass the \"address of\" the bill value.\n\tdisplay(\u0026bill)\n\n\t// Pass the \"address of\" the logins field from within the bill value.\n\tincrement(\u0026bill.logins)\n\n\t// Pass the \"address of\" the bill value.\n\tdisplay(\u0026bill)\n}\n\n// increment declares logins as a pointer variable whose value is\n// always an address and points to values of type int.\nfunc increment(logins *int) {\n\t*logins++\n\tfmt.Printf(\"\u0026logins[%p] logins[%p] *logins[%d]\\n\\n\", \u0026logins, logins, *logins)\n}\n\n// display declares u as user pointer variable whose value is always an address\n// and points to values of type user.\nfunc display(u *user) {\n\tfmt.Printf(\"%p\\t%+v\\n\", u, *u)\n\tfmt.Printf(\"Name: %q Email: %q Logins: %d\\n\\n\", u.name, u.email, u.logins)\n}\n","Hash":"Jg2hSWEYmnWWIw8PvC2eTX9Fc4A="},{"Name":"example4.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to teach the mechanics of escape analysis.\npackage main\n\n// user represents a user in the system.\ntype user struct {\n\tname string\n\temail string\n}\n\n// main is the entry point for the application.\nfunc main() {\n\tu1 := createUserV1()\n\tu2 := createUserV2()\n\n\tprintln(\"u1\", \u0026u1, \"u2\", u2)\n}\n\n// createUserV1 creates a user value and passed\n// a copy back to the caller.\n//\n//go:noinline\nfunc createUserV1() user {\n\tu := user{\n\t\tname: \"Bill\",\n\t\temail: \"bill@ardanlabs.com\",\n\t}\n\n\tprintln(\"V1\", \u0026u)\n\n\treturn u\n}\n\n// createUserV2 creates a user value and shares\n// the value with the caller.\n//\n//go:noinline\nfunc createUserV2() *user {\n\tu := user{\n\t\tname: \"Bill\",\n\t\temail: \"bill@ardanlabs.com\",\n\t}\n\n\tprintln(\"V2\", \u0026u)\n\n\treturn \u0026u\n}\n\n/*\n// See escape analysis and inlining decisions.\n\n$ go build -gcflags -m=2\n# github.com/ardanlabs/gotraining/topics/go/language/pointers/example4\n./example4.go:24:6: cannot inline createUserV1: marked go:noinline\n./example4.go:38:6: cannot inline createUserV2: marked go:noinline\n./example4.go:14:6: cannot inline main: function too complex: cost 132 exceeds budget 80\n./example4.go:39:2: u escapes to heap:\n./example4.go:39:2: flow: ~r0 = \u0026u:\n./example4.go:39:2: from \u0026u (address-of) at ./example4.go:46:9\n./example4.go:39:2: from return \u0026u (return) at ./example4.go:46:2\n./example4.go:39:2: moved to heap: u\n\n// See the intermediate representation phase before\n// generating the actual arch-specific assembly.\n\n$ go build -gcflags -S\nCALL\t\"\".createUserV1(SB)\n\t0x0026 00038 MOVQ\t(SP), AX\n\t0x002a 00042 MOVQ\t8(SP), CX\n\t0x002f 00047 MOVQ\t16(SP), DX\n\t0x0034 00052 MOVQ\t24(SP), BX\n\t0x0039 00057 MOVQ\tAX, \"\".u1+40(SP)\n\t0x003e 00062 MOVQ\tCX, \"\".u1+48(SP)\n\t0x0043 00067 MOVQ\tDX, \"\".u1+56(SP)\n\t0x0048 00072 MOVQ\tBX, \"\".u1+64(SP)\n\t0x004d 00077 PCDATA\t$1,\n\n// See bounds checking decisions.\n\ngo build -gcflags=\"-d=ssa/check_bce/debug=1\"\n\n// See the actual machine representation by using\n// the disassembler.\n\n$ go tool objdump -s main.main example4\nTEXT main.main(SB) github.com/ardanlabs/gotraining/topics/go/language/pointers/example4/example4.go\n example4.go:15\t0x105e281\t\te8ba000000\t\tCALL main.createUserV1(SB)\n example4.go:15\t0x105e286\t\t488b0424\t\tMOVQ 0(SP), AX\n example4.go:15\t0x105e28a\t\t488b4c2408\t\tMOVQ 0x8(SP), CX\n example4.go:15\t0x105e28f\t\t488b542410\t\tMOVQ 0x10(SP), DX\n example4.go:15\t0x105e294\t\t488b5c2418\t\tMOVQ 0x18(SP), BX\n example4.go:15\t0x105e299\t\t4889442428\t\tMOVQ AX, 0x28(SP)\n example4.go:15\t0x105e29e\t\t48894c2430\t\tMOVQ CX, 0x30(SP)\n example4.go:15\t0x105e2a3\t\t4889542438\t\tMOVQ DX, 0x38(SP)\n example4.go:15\t0x105e2a8\t\t48895c2440\t\tMOVQ BX, 0x40(SP)\n\n// See a list of the symbols in an artifact with\n// annotations and size.\n\n$ go tool nm example4\n 105e340 T main.createUserV1\n 105e420 T main.createUserV2\n 105e260 T main.main\n 10cb230 B os.executablePath\n*/\n","Hash":"UomTM68Rpo2lG0Gq6ecuOpbMvmI="},{"Name":"example5.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how stacks grow/change.\npackage main\n\n// Number of elements to grow each stack frame.\n// Run with 1 and then with 1024\nconst size = 1\n\n// main is the entry point for the application.\nfunc main() {\n\ts := \"HELLO\"\n\tstackCopy(\u0026s, 0, [size]int{})\n}\n\n// stackCopy recursively runs increasing the size\n// of the stack.\n//\n//go:noinline\nfunc stackCopy(s *string, c int, a [size]int) {\n\tprintln(c, s, *s)\n\n\tc++\n\tif c == 10 {\n\t\treturn\n\t}\n\n\tstackCopy(s, c, a)\n}\n","Hash":"5ZB6rugKkwoMKkGLMDTUZr8DskY="}]},{"Title":"تمرینات","Content":"\n \u003ch2\u003eتمرینات\u003c/h2\u003e\n \n \n \u003cp\u003e\n از الگو به عنوان نقطه شروعی برای انجام تمرین‌ها استفاده کنید. یک پاسخ ممکن نیز ارائه شده است.\n \u003c/p\u003e\n \n\n \u003ch2\u003eتمرین 1\u003c/h2\u003e\n \n \n \u003cp\u003e\n \u003cb\u003eبخش\u003c/b\u003e \u003cb\u003eA:\u003c/b\u003e تعریف و مقداردهی اولیه یک متغیر از نوع int با مقدار 20 را انجام دهید.\n\n\n آدرس و مقدار متغیر را نمایش دهید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u003cb\u003eبخش\u003c/b\u003e \u003cb\u003eB:\u003c/b\u003e و مقداردهی اولیه یک متغیر اشاره‌گر از نوع int که به آخرین متغیری که تازه ایجاد کرده‌اید اشاره می‌کند. آدرس، مقدار و مقداری که اشاره‌گر به آن اشاره می‌کند را نمایش دهید.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eتمرین 2\u003c/h2\u003e\n \n \n \u003cp\u003e\n تعریف یک نوع ساختاری (struct) و ایجاد یک مقدار از این نوع. تعریف یک تابع که می‌تواند مقدار یک فیلد در این نوع ساختاری را تغییر دهد. نمایش مقدار قبل و بعد از فراخوانی تابع.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n با این توضیحات، شما می‌توانید کد مورد نظر خود را بنویسید و از تابع برای تغییر مقدار فیلد استفاده کنید.\n \u003c/p\u003e\n \n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n","Files":[{"Name":"exercise1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Declare and initialize a variable of type int with the value of 20. Display\n// the _address of_ and _value of_ the variable.\n//\n// Declare and initialize a pointer variable of type int that points to the last\n// variable you just created. Display the _address of_ , _value of_ and the\n// _value that the pointer points to_.\npackage main\n\n// Add imports.\n\nfunc main() {\n\n\t// Declare an integer variable with the value of 20.\n\n\t// Display the address of and value of the variable.\n\n\t// Declare a pointer variable of type int. Assign the\n\t// address of the integer variable above.\n\n\t// Display the address of, value of and the value the pointer\n\t// points to.\n}\n","Hash":"UT45taFQNRGna7ZDM3ZqplW03ag="},{"Name":"answer1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Declare and initialize a variable of type int with the value of 20. Display\n// the _address of_ and _value of_ the variable.\n//\n// Declare and initialize a pointer variable of type int that points to the last\n// variable you just created. Display the _address of_ , _value of_ and the\n// _value that the pointer points to_.\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\n\t// Declare an integer variable with the value of 20.\n\tvalue := 20\n\n\t// Display the address of and value of the variable.\n\tfmt.Println(\"Address Of:\", \u0026value, \"Value Of:\", value)\n\n\t// Declare a pointer variable of type int. Assign the\n\t// address of the integer variable above.\n\tp := \u0026value\n\n\t// Display the address of, value of and the value the pointer\n\t// points to.\n\tfmt.Println(\"Address Of:\", \u0026p, \"Value Of:\", p, \"Points To:\", *p)\n}\n","Hash":"C/e/8szXy5mAJI97cMHz8SiyNHY="},{"Name":"exercise2.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Declare a struct type and create a value of this type. Declare a function\n// that can change the value of some field in this struct type. Display the\n// value before and after the call to your function.\npackage main\n\n// Add imports.\n\n// Declare a type named user.\n\n// Create a function that changes the value of one of the user fields.\nfunc funcName( /* add pointer parameter, add value parameter */ ) {\n\n\t// Use the pointer to change the value that the\n\t// pointer points to.\n}\n\nfunc main() {\n\n\t// Create a variable of type user and initialize each field.\n\n\t// Display the value of the variable.\n\n\t// Share the variable with the function you declared above.\n\n\t// Display the value of the variable.\n}\n","Hash":"wURH8iUHD31yzXxDygZf6QOTCPM="},{"Name":"answer2.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Declare a struct type and create a value of this type. Declare a function\n// that can change the value of some field in this struct type. Display the\n// value before and after the call to your function.\npackage main\n\nimport \"fmt\"\n\n// user represents a user in the system.\ntype user struct {\n\tname string\n\temail string\n\taccessLevel int\n}\n\nfunc main() {\n\n\t// Create a variable of type user and initialize each field.\n\tbill := user{\n\t\tname: \"Bill\",\n\t\temail: \"bill@ardanlabs.com\",\n\t\taccessLevel: 1,\n\t}\n\n\t// Display the value of the accessLevel field.\n\tfmt.Println(\"access:\", bill.accessLevel)\n\n\t// Share the bill variable with the accessLevel function\n\t// along with a value to update the accessLevel field with.\n\taccessLevel(\u0026bill, 10)\n\n\t// Display the value of the accessLevel field again.\n\tfmt.Println(\"access:\", bill.accessLevel)\n}\n\n// accessLevel changes the value of the users access level.\nfunc accessLevel(u *user, accessLevel int) {\n\n\t// Set of value of the accessLevel field to the value\n\t// that is passed in.\n\tu.accessLevel = accessLevel\n}\n","Hash":"idovziRTg7za0YuFVNUvJWvArVI="}]}]} ,"algorithms-slices":{"Title":"عملیات slice","Description":"این بخش نمونه‌هایی ارائه می‌دهد که عملیات بر slice ش را انجام می‌دهند.","Pages":[{"Title":"کمترین عدد","Content":"\n \u003ch2\u003eکمترین عدد\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/training/individual-on-demand/ultimate-go-bundle/\" target=\"_blank\"\u003eتماشای ویدئو\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003eنیاز به کمک مالی دارید؟ از \u003ca href=\"https://www.ardanlabs.com/scholarship/\" target=\"_blank\"\u003eفرم Scholarship\u003c/a\u003e ما استفاده کنید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n این برنامه نمونه یک تابع را پیاده‌سازی می‌کند تا کمترین عدد صحیح را از یک slice از اعداد صحیح بازیابی کند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u003cb\u003eدیاگرام\u003c/b\u003e\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e┌────┐┌────┐┌────┐\n│ 10 ││ 30 ││ 15 │ ────▷ 10\n└────┘└────┘└────┘\n\n┌────┐\n│ 25 │ ────▷ 25\n└────┘\u003c/pre\u003e\n \n\n\t\n\t\t\n\t\n\n","Files":[{"Name":"min_number.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This sample program shows you how to retrieve the minimum integer\n// from a slice of integers.\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\ttt := []struct {\n\t\tinput []int\n\t\texpected int\n\t}{\n\t\t{[]int{}, 0},\n\t\t{nil, 0},\n\t\t{[]int{10}, 10},\n\t\t{[]int{20, 30, 10, 50}, 10},\n\t\t{[]int{30, 50, 10}, 10},\n\t}\n\n\tfor _, test := range tt {\n\t\tvalue, err := Min(test.input)\n\t\tif err != nil {\n\t\t\tfmt.Println(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tfmt.Printf(\"Input: %d, Value: %d, Expected: %d, Match: %v\\n\",\n\t\t\ttest.input,\n\t\t\tvalue,\n\t\t\ttest.expected,\n\t\t\tvalue == test.expected,\n\t\t)\n\t}\n}\n\n// Min returns the minimum integer in the slice.\nfunc Min(n []int) (int, error) {\n\n\t// First check there are numbers in the collection.\n\tif len(n) == 0 {\n\t\treturn 0, fmt.Errorf(\"slice %#v has no elements\", n)\n\t}\n\n\t// If the length of the slice is 1 then return the\n\t// integer at index 0.\n\tif len(n) == 1 {\n\t\treturn n[0], nil\n\t}\n\n\t// Save the first value as current min and then loop over\n\t// the slice of integers looking for a smaller number.\n\tmin := n[0]\n\tfor _, num := range n[1:] {\n\n\t\t// If num is less than min. Assign min to num.\n\t\tif num \u003c min {\n\t\t\tmin = num\n\t\t}\n\t}\n\n\treturn min, nil\n}\n","Hash":"OBzcPivErh/fh3wPr1IwdFhgSCQ="}]},{"Title":"بیشترین عدد","Content":"\n \u003ch2\u003eبیشترین عدد\u003c/h2\u003e\n \n \n \u003cp\u003e\n این برنامه نمونه یک تابع را پیاده‌سازی می‌کند تا بیشترین عدد صحیح را از یک slice از اعداد صحیح بازیابی کند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u003cb\u003eDiagram\u003c/b\u003e\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e┌────┐┌────┐┌────┐\n│ 45 ││ 23 ││ 68 │ ────▷ 68\n└────┘└────┘└────┘\n\n┌────┐\n│ 78 │ ────▷ 78\n└────┘\u003c/pre\u003e\n \n\n\t\n\t\t\n\t\n\n","Files":[{"Name":"max_number.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This sample program shows you how to retrieve the maximum integer\n// from a slice of integers.\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\ttt := []struct {\n\t\tinput []int\n\t\texpected int\n\t}{\n\t\t{[]int{}, 0},\n\t\t{nil, 0},\n\t\t{[]int{10}, 10},\n\t\t{[]int{20, 30, 10, 50}, 50},\n\t\t{[]int{30, 50, 10}, 50},\n\t}\n\n\tfor _, test := range tt {\n\t\tvalue, err := Max(test.input)\n\t\tif err != nil {\n\t\t\tfmt.Println(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tfmt.Printf(\"Input: %d, Value: %d, Expected: %d, Match: %v\\n\",\n\t\t\ttest.input,\n\t\t\tvalue,\n\t\t\ttest.expected,\n\t\t\tvalue == test.expected,\n\t\t)\n\t}\n}\n\n// Max returns the maximum integer in the slice.\nfunc Max(n []int) (int, error) {\n\n\t// First check there are numbers in the collection.\n\tif len(n) == 0 {\n\t\treturn 0, fmt.Errorf(\"slice %#v has no elements\", n)\n\t}\n\n\t// If the length of the slice is 1 then return the\n\t// integer at index 0.\n\tif len(n) == 1 {\n\t\treturn n[0], nil\n\t}\n\n\t// Save the first value as current max and then loop over\n\t// the slice of integers looking for a larger number.\n\tmax := n[0]\n\tfor _, num := range n[1:] {\n\n\t\t// If num is greater than max, assign max to num.\n\t\tif num \u003e max {\n\t\t\tmax = num\n\t\t}\n\t}\n\n\treturn max, nil\n}\n","Hash":"A05aPTEmS8hEsGYiYmQ3zVkupXs="}]}]} ,"arrays":{"Title":"آرایه ها","Description":"آرایه‌ها (Arrays) در Go یک ساختار داده خاص هستند که به ما اجازه می‌دهند بلوک‌های پیوسته‌ای از حافظه با اندازه ثابت اختصاص دهیم.","Pages":[{"Title":"آرایه ها","Content":"\n \u003ch2\u003eآرایه ها\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/training/individual-on-demand/ultimate-go-bundle/\" target=\"_blank\"\u003eتماشای ویدئو\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003eنیاز به کمک مالی دارید؟ از \u003ca href=\"https://www.ardanlabs.com/scholarship/\" target=\"_blank\"\u003eفرم Scholarship\u003c/a\u003e ما استفاده کنید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n آرایه‌ها ساختار داده‌ای ویژه در زبان Go هستند که به ما اجازه می‌دهند حافظه‌های متوالی با اندازه ثابت را تخصیص دهیم. آرایه‌ها در Go ویژگی‌هایی ویژه مربوط به نحوه تعریف و مشاهده آنها به عنوان انواع دارند.\n \u003c/p\u003e\n \n\n \u003ch2\u003eبررسی کد\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e1:\u003c/b\u003e تعریف، مقداردهی اولیه و تکرار\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e2:\u003c/b\u003e آرایه‌های با انواع مختلف\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e3:\u003c/b\u003e تخصیص حافظه‌های متوالی\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e4:\u003c/b\u003e مکانیک محدوده\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n \u003ch2\u003eتعریف و مقداردهی اولیه مقادیر\u003c/h2\u003e\n \n \n \u003cp\u003e\n تعریف یک آرایه از پنج رشته که به حالت مقدار صفر مقداردهی اولیه شده است.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003evar strings [5]string\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n یک رشته (string) ساختاری نامتغیر (immutable) است که دو کلمه‌ای است و نشانگر یک اشاره‌گر به یک آرایه پشتیبان از بایت‌ها و تعداد کل بایت‌ها در آرایه پشتیبان است. از آنجا که این آرایه به حالت مقدار صفر مقداردهی شده است، هر عنصر به حالت مقدار صفر خود تنظیم شده است. این بدان معنی است که هر رشته، کلمه اول را به \u003ccode\u003enil\u003c/code\u003e و کلمه دوم را به \u003ccode\u003e0\u003c/code\u003e تنظیم شده است.\n \u003c/p\u003e\n \n\n \u003ca href=\"/tour/eng/static/img/a1.png\" target=\"_blank\"\u003e\n \u003cimg class=\"codeimg\" src=\"/tour/eng/static/img/a1.png\"\u003e\n \u003c/a\u003e\n\n\n \u003ch2\u003eتخصیص رشته\u003c/h2\u003e\n \n \n \u003cp\u003e\n چه اتفاقی می افتد وقتی یک رشته به رشته دیگری اختصاص داده می شود؟\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003estrings[0] = \u0026#34;Apple\u0026#34;\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n زمانی که یک رشته به رشته دیگری اختصاص داده می‌شود، مقدار دو کلمه کپی می‌شود، در نتیجه دو مقدار رشته متفاوت ایجاد می‌شود که هر دو یک آرایه پشتیبان یکسان را به اشتراک می‌گذارند.\n \u003c/p\u003e\n \n\n \u003ca href=\"/tour/eng/static/img/a2.png\" target=\"_blank\"\u003e\n \u003cimg class=\"codeimg\" src=\"/tour/eng/static/img/a2.png\"\u003e\n \u003c/a\u003e\n\n \n \u003cp\u003e\n هزینه کپی کردن یک رشته صرف نظر از اندازه رشته، یک کپی دو کلمه ای، یکسان است.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eتکرار بر روی مجموعه‌ها\u003c/h2\u003e\n \n \n \u003cp\u003e\n Go دو معنای مختلف برای تکرار بر روی مجموعه ارائه می‌دهد. من می‌توانم با استفاده از معنای مقدار یا معنای اشاره تکرار کنم.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e// Value Semantic Iteration\nfor i, fruit := range strings {\n println(i, fruit)\n}\n\n\n// Pointer Semantic Iteration\nfor i := range strings {\n println(i, strings[i])\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n هنگامی که از تکرار معنای مقدار استفاده می کنید، دو اتفاق می افتد. اول، مجموعه ای که در حال تکرار بر روی آن هستید کپی می شود و شما بر روی کپی تکرار می کنید. در مورد آرایه، کپی کردن می تواند پرهزینه باشد، زیرا کل آرایه کپی می شود. در مورد اسلایس، هزینه واقعی وجود ندارد، زیرا فقط مقدار اسلایس داخلی کپی می شود و نه آرایه پشتیبان. دوم، شما یک نسخه از هر عنصر در حال تکرار دریافت می کنید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n هنگامی که از تکرار معنای اشاره استفاده می کنید، بر روی مجموعه اصلی تکرار می کنید و من به هر عنصر مرتبط با مجموعه به طور مستقیم دسترسی پیدا می کنم.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eتکرار معنای مقدار.\u003c/h2\u003e\n \n \n \u003cp\u003e\n با توجه به کد و خروجی زیر.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003estrings := [5]string{\u0026#34;Apple\u0026#34;, \u0026#34;Orange\u0026#34;, \u0026#34;Banana\u0026#34;, \u0026#34;Grape\u0026#34;, \u0026#34;Plum\u0026#34;}\nfor i, fruit := range strings {\n println(i, fruit)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e0 Apple\n1 Orange\n2 Banana\n3 Grape\n4 Plum\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n متغیر strings یک آرایه از 5 رشته است. حلقه تک تک رشته ها را در مجموعه تکرار می کند و موقعیت شاخص و مقدار رشته را نمایش می دهد. از آنجایی که این یک تکرار معنایی ارزش است، for range بر روی یک کپی کم عمق خود از آرایه تکرار می شود و در هر تکرار، متغیر fruit کپی هر رشته (ساختار داده دو کلمه) است.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n توجه کنید که چگونه متغیر fruit با استفاده از معنایی ارزش به تابع print منتقل می شود. تابع print نیز کپی خود از مقدار رشته را دریافت می کند. تا زمانی که رشته به تابع print منتقل شود، 4 نسخه از مقدار رشته وجود دارد (آرایه، کپی کم عمق، متغیر fruit و کپی تابع print). همه 4 نسخه یک آرایه پشتیبان از بایت را به اشتراک می گذارند.\n \u003c/p\u003e\n \n\n \u003ca href=\"/tour/eng/static/img/a3.png\" target=\"_blank\"\u003e\n \u003cimg class=\"codeimg\" src=\"/tour/eng/static/img/a3.png\"\u003e\n \u003c/a\u003e\n\n \n \u003cp\u003e\n ایجاد کپی از مقدار رشته مهم است زیرا از فرار کردن مقدار رشته به پشته جلوگیری می کند. این کار تخصیص غیرمولد را روی پشته از بین می برد.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eتکرار معنایی اشاره گر\u003c/h2\u003e\n \n \n \u003cp\u003e\n با توجه به کد و خروجی زیر.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003estrings := [5]string{\u0026#34;Apple\u0026#34;, \u0026#34;Orange\u0026#34;, \u0026#34;Banana\u0026#34;, \u0026#34;Grape\u0026#34;, \u0026#34;Plum\u0026#34;}\nfor i := range strings {\n println(i, strings[i])\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e0 Apple\n1 Orange\n2 Banana\n3 Grape\n4 Plum\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n دوباره، متغیر strings یک آرایه از 5 رشته است. حلقه تک تک رشته ها را در مجموعه تکرار می کند و موقعیت شاخص و مقدار رشته را نمایش می دهد. از آنجایی که این یک تکرار معنایی اشاره گر است، for range مستقیماً بر روی آرایه strings تکرار می شود و در هر تکرار، مقدار رشته برای هر موقعیت شاخص به طور مستقیم برای فراخوانی print دسترسی پیدا می کند.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eآرایه های انواع مختلف\u003c/h2\u003e\n \n \n \u003cp\u003e\n جالب است که ببینیم کامپایلر هنگام انتساب آرایه‌هایی از همان نوع با طول‌های مختلف چه خطایی می‌دهد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003evar five [5]int\nfour := [4]int{10, 20, 30, 40}\n\nfive = four\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خطای کامپایلر:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eنمی‌توان از چهار (نوع [4]int) به عنوان نوع [5]int در انتساب استفاده کرد.\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در اینجا شما یک آرایه از 4 و 5 عدد صحیح را که به مقدار صفر خود مقداردهی شده اند اعلام می کنید. سپس سعی کنید آنها را به یکدیگر اختصاص دهید و کامپایلر می گوید: \u0026#34;نمی توان از چهار (نوع [4]int) به عنوان نوع [5]int در انتساب استفاده کرد.\u0026#34;\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n مهم است که واضح باشیم که کامپایلر چه می گوید. این می گوید که یک آرایه از 4 عدد صحیح و یک آرایه از 5 عدد صحیح نشان دهنده داده های انواع مختلف هستند. اندازه یک آرایه بخشی از اطلاعات نوع آن است. در Go، اندازه یک آرایه باید در زمان کامپایل مشخص باشد.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eساختار حافظه مجاور (Contiguous Memory Construction)\u003c/h2\u003e\n \n \n \u003cp\u003e\n شما می خواهید ثابت کنید که یک آرایه یک چیدمان مجاور از حافظه را فراهم می کند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efive := [5]string{\u0026#34;Annie\u0026#34;, \u0026#34;Betty\u0026#34;, \u0026#34;Charley\u0026#34;, \u0026#34;Doug\u0026#34;, \u0026#34;Bill\u0026#34;}\n\nfor i, v := range five {\n fmt.Printf(\u0026#34;Value[%s]\\tAddress[%p] IndexAddr[%p]\\n\u0026#34;,\n v, \u0026amp;v, \u0026amp;five[i])\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eValue[Annie] Address[0xc000010250] IndexAddr[0xc000052180]\nValue[Betty] Address[0xc000010250] IndexAddr[0xc000052190]\nValue[Charley] Address[0xc000010250] IndexAddr[0xc0000521a0]\nValue[Doug] Address[0xc000010250] IndexAddr[0xc0000521b0]\nValue[Bill] Address[0xc000010250] IndexAddr[0xc0000521c0]\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در اینجا شما یک آرایه از 5 رشته را که با مقادیر مقداردهی اولیه شده اند اعلام می کنید. سپس از تکرار معنایی ارزش برای نمایش اطلاعات در مورد هر رشته استفاده کنید. خروجی هر مقدار رشته فردی، آدرس متغیر v و آدرس هر عنصر در آرایه را نشان می دهد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n می توانید ببینید که چگونه آرایه یک بلوک حافظه مجاور است و چگونه یک رشته یک ساختار داده دو کلمه ای یا 16 بایتی در معماری 64 بیتی من است. آدرس هر عنصر به فاصله 16 بایت فاصله دارد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n این واقعیت که متغیر v در هر تکرار دارای یک آدرس است، درک این را تقویت می کند که v یک متغیر محلی از نوع string است که در طول تکرار حاوی کپی هر مقدار رشته است.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eکش های CPU\u003c/h2\u003e\n \n \n \u003cp\u003e\n تفاوت های مکانیکی زیادی بین پردازنده ها و طراحی آنها وجود دارد. در این بخش، در سطح بالایی در مورد پردازنده ها و معنایی صحبت خواهید کرد که تقریباً بین همه آنها یکسان است. این درک معنایی یک مدل ذهنی خوب از نحوه عملکرد پردازنده و همدردی را که می توانید ارائه کنید به شما ارائه می دهد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n هر هسته داخل پردازنده دارای کش حافظه محلی خود (L1 و L2) و کش حافظه مشترک (L3) است که برای ذخیره/دسترسی به داده ها و دستورالعمل ها استفاده می شود. رشته های سخت افزاری در هر هسته می توانند به کش های محلی L1 و L2 خود دسترسی داشته باشند. داده ها از L3 یا حافظه اصلی باید برای دسترسی در کش L1 یا L2 کپی شوند.\n \u003c/p\u003e\n \n\n \u003ca href=\"/tour/eng/static/img/a4.png\" target=\"_blank\"\u003e\n \u003cimg class=\"codeimg\" src=\"/tour/eng/static/img/a4.png\"\u003e\n \u003c/a\u003e\n\n \n \u003cp\u003e\n هزینه تأخیر در دسترسی به داده هایی که در کش های مختلف وجود دارند از کمترین به بیشترین تغییر می کند: L1 -\u0026gt; L2 -\u0026gt; L3 -\u0026gt; حافظه اصلی. همانطور که اسکات مایرز گفت: \u0026#34;اگر عملکرد مهم است، کل حافظه ای که شما دارید کل حافظه کش است. حافظه اصلی برای دسترسی بسیار کند است، عملاً ممکن است اصلاً وجود نداشته باشد.\u0026#34;\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n عملکرد امروز در مورد این است که داده ها چگونه به طور کارآمد در سخت افزار جریان می یابند. اگر هر داده ای که سخت افزار نیاز دارد (در هر زمان معین) فقط در حافظه اصلی وجود داشته باشد، برنامه های من در مقایسه با داده هایی که قبلاً در کش های L1 یا L2 وجود دارند، کندتر اجرا می شوند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e3 گیگاهرتز (3 سیکل کلاک بر نانوسکوند) * 4 دستورالعمل در هر سیکل = 12 دستورالعمل در هر نانوسکوند!\n\n1 ns ............. 1 ns .............. 12 instructions (one) \n1 µs ......... 1,000 ns .......... 12,000 instructions (thousand)\n1 ms ..... 1,000,000 ns ...... 12,000,000 instructions (million)\n1 s .. 1,000,000,000 ns .. 12,000,000,000 instructions (billion)\n\nIndustry Defined Latencies\nL1 cache reference ......................... 0.5 ns ................... 6 ins\nL2 cache reference ........................... 7 ns ................... 84 ins\nMain memory reference ...................... 100 ns ................. 1200 ins\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n چگونه می توان کدی نوشت که تضمین کند داده های مورد نیاز برای اجرای یک دستورالعمل همیشه در کش های L1 یا L2 وجود دارد؟ شما باید کدی بنویسید که از نظر مکانیکی با پیش بینی کننده پردازنده سازگار باشد. پیش بینی کننده سعی می کند قبل از اینکه دستورالعمل ها داده ها را درخواست کنند، پیش بینی کند که چه داده هایی مورد نیاز است، بنابراین آنها از قبل در کش L1 یا L2 وجود دارند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n بسته به اینکه دسترسی از کجا اتفاق می افتد، گرانولاریتی های مختلفی از دسترسی به حافظه وجود دارد. کد من می تواند یک بایت از حافظه را به عنوان کوچکترین واحد دسترسی به حافظه بخواند/بنویسد. با این حال، از نظر سیستم های کش، گرانولاریتی 64 بایت است. این بلوک 64 بایتی حافظه خط کش نامیده می شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n پیش بینی کننده زمانی بهتر کار می کند که دستورالعمل های در حال اجرا الگوهای دسترسی قابل پیش بینی به حافظه ایجاد کنند. یکی از راه‌های ایجاد یک الگوی دسترسی قابل پیش بینی به حافظه، ساخت یک بلوک حافظه مجاور و سپس تکرار روی آن حافظه با انجام یک پیمایش خطی با گام قابل پیش بینی است.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n آرایه مهمترین ساختار داده برای سخت افزار است زیرا از الگوهای دسترسی قابل پیش بینی پشتیبانی می کند. با این حال، اسلایس مهمترین ساختار داده در Go است. اسلایس ها در Go از یک آرایه در زیر استفاده می کنند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n هنگامی که یک آرایه می سازید، هر عنصر به طور مساوی از عنصر بعدی یا قبلی فاصله دارد. هنگامی که روی یک آرایه تکرار می کنید، شروع به حرکت در خطوط کش به خطوط کش متصل با گام قابل پیش بینی می کنید. پیش بینی کننده این الگوی دسترسی قابل پیش بینی به داده ها را دریافت می کند و شروع به کشیدن کارآمد داده ها به پردازنده می کند، در نتیجه هزینه های تأخیر دسترسی به داده را کاهش می دهد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n تصور کنید که یک ماتریس حافظه مربعی بزرگ و یک لیست پیوندی از گره ها دارید که مطابق با تعداد عناصر ماتریس هستند. اگر یک پیمایش در سراسر لیست پیوندی انجام دهید و سپس ماتریس را در هر دو جهت (ستون و ردیف) پیمایش کنید، عملکرد پیمایش های مختلف چگونه مقایسه می شود؟\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc RowTraverse() int {\n var ctr int\n for row := 0; row \u0026lt; rows; row\u0026#43;\u0026#43; {\n for col := 0; col \u0026lt; cols; col\u0026#43;\u0026#43; {\n if matrix[row][col] == 0xFF {\n ctr\u0026#43;\u0026#43;\n }\n }\n }\n return ctr\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n پیمایش ردیفی بهترین عملکرد را خواهد داشت زیرا از حافظه، خط کش به خط کش متصل عبور می کند که یک الگوی دسترسی قابل پیش بینی ایجاد می کند. خطوط کش را می توان قبل از نیاز به داده ها، پیش بینی کرد و در کش L1 یا L2 کپی کرد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc ColumnTraverse() int {\n var ctr int\n for col := 0; col \u0026lt; cols; col\u0026#43;\u0026#43; {\n for row := 0; row \u0026lt; rows; row\u0026#43;\u0026#43; {\n if matrix[row][col] == 0xFF {\n ctr\u0026#43;\u0026#43;\n }\n }\n }\n return ctr\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n پیمایش ستونی بدترین حالت است زیرا این الگوی دسترسی در هر دسترسی به حافظه از مرزهای صفحه OS عبور می کند. این باعث عدم پیش بینی برای پیش بازی خط کش می شود و اساساً به حافظه دسترسی تصادفی تبدیل می شود.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc LinkedListTraverse() int {\n var ctr int\n d := list\n for d != nil {\n if d.v == 0xFF {\n ctr\u0026#43;\u0026#43;\n }\n d = d.p\n }\n return ctr\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n لیست پیوندی تقریباً دو برابر کندتر از پیمایش ردیفی است، زیرا خطای کش وجود دارد، اما خطاهای TLB (Translation Lookaside Buffer) کمتری وجود دارد. بخش زیادی از گره های متصل شده در لیست در داخل صفحات OS یکسانی وجود دارند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eBenchmarkLinkListTraverse-16 128 28738407 ns/op\nBenchmarkColumnTraverse-16 30 126878630 ns/op\nBenchmarkRowTraverse-16 310 11060883 ns/op\u003c/pre\u003e\n \n\n\n \u003ch2\u003eTranslation Lookaside Buffer (TLB)\u003c/h2\u003e\n \n \n \u003cp\u003e\n هر برنامه در حال اجرا توسط سیستم عامل یک نقشه حافظه کامل از حافظه مجازی دریافت می کند و آن برنامه در حال اجرا فکر می کند که تمام حافظه فیزیکی روی دستگاه را دارد. با این حال، حافظه فیزیکی باید با تمام برنامه های در حال اجرا به اشتراک گذاشته شود. سیستم عامل حافظه فیزیکی را با شکستن حافظه فیزیکی به صفحات و نگاشت صفحات به حافظه مجازی برای هر برنامه در حال اجرا به اشتراک می گذارد. هر سیستم عامل می تواند اندازه یک صفحه را تعیین کند، اما 4k، 8k، 16k اندازه های منطقی و رایج هستند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n TLB یک حافظه پنهان کوچک در داخل پردازنده است که به کاهش تأخیر در ترجمه یک آدرس مجازی به یک آدرس فیزیکی در محدوده یک صفحه OS و آفست داخل صفحه کمک می کند. یک خطای حافظه نهان TLB می تواند باعث تأخیرهای زیادی شود زیرا اکنون سخت افزار باید منتظر اسکن جدول صفحه سیستم عامل برای یافتن صفحه مناسب برای آدرس مجازی مورد نظر باشد. اگر برنامه روی یک ماشین مجازی (مانند ابر) اجرا شود، ابتدا باید جدول صفحه ماشین مجازی اسکن شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n به یاد داشته باشید که چه گفته شد:\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n لیست پیوندی تقریباً دو برابر کندتر از پیمایش ردیفی است، زیرا خطاهای کش وجود دارد، اما خطاهای TLB کمتری وجود دارد (در ادامه توضیح داده شده است). بخش زیادی از گره های متصل شده در لیست در داخل صفحات OS یکسانی وجود دارند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n LinkedList به دلیل دسترسی TLB بسیار سریعتر از پیمایش ستونی است. حتی اگر خطاهای کش با پیمایش لیست پیوندی وجود داشته باشد، از آنجایی که اکثریت حافظه برای یک گروه از گره‌ها در داخل صفحه یکسانی قرار می‌گیرند، تأخیرهای TLB بر عملکرد تأثیر نمی‌گذارند. به همین دلیل است که برای برنامه هایی که از مقدار زیادی حافظه استفاده می کنند، مانند برنامه های مبتنی بر DNA، ممکن است بخواهید از توزیعی از لینوکس استفاده کنید که با اندازه صفحات در حدود یک مگابایت یا دو مگابایت حافظه پیکربندی شده است.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n با این حال، طراحی مبتنی بر داده مهم است. نوشتن یک الگوریتم کارآمد باید نحوه دسترسی به داده ها را در نظر بگیرد. به یاد داشته باشید، عملکرد امروز در مورد این است که چگونه می توانید به طور کارآمد داده ها را به پردازنده منتقل کنید.\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://youtu.be/WDIkqP4JbkE?t=1129\" target=\"_blank\"\u003eCPU Caches and Why You Care (18:50-20:30)\u003c/a\u003e - Scott Meyers \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://youtu.be/WDIkqP4JbkE?t=2676\" target=\"_blank\"\u003eCPU Caches and Why You Care (44:36-45:40)\u003c/a\u003e - Scott Meyers \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://youtu.be/jEG4Qyo_4Bc?t=266\" target=\"_blank\"\u003ePerformance Through Cache-Friendliness (4:25-5:48)\u003c/a\u003e - Damian Gryski \u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eنکات کش سی پی یو\u003c/h2\u003e\n \u003cul\u003e\n \u003cli\u003eکش‌های CPU با کش کردن حافظه اصلی در خطوط کش کار می‌کنند.\u003c/li\u003e\n \u003cli\u003eخطوط کش امروزه بسته به سخت‌افزار، 32 یا 64 بایت عرض دارند.\u003c/li\u003e\n \u003cli\u003eهسته‌ها به طور مستقیم به حافظه اصلی دسترسی ندارند. آنها تمایل دارند فقط به کش‌های محلی خود دسترسی داشته باشند.\u003c/li\u003e\n \u003cli\u003eهم داده‌ها و هم دستورالعمل‌ها در کش‌ها ذخیره می‌شوند.\u003c/li\u003e\n \u003cli\u003eخطوط کش با توجه به نیاز به ذخیره خطوط کش جدید در کش‌ها، از L1-\u003eL2-\u003eL3 جابه‌جا می‌شوند.\u003c/li\u003e\n \u003cli\u003eسخت‌افزار دوست دارد داده‌ها و دستورالعمل‌ها را به صورت خطی در طول خطوط کش طی کند.\u003c/li\u003e\n \u003cli\u003e\u003cp\u003eحافظه اصلی بر روی حافظه نسبتا سریع و ارزان ساخته شده است. کش‌ها بر روی حافظه بسیار سریع و گران قیمت ساخته شده‌اند.\u003c/p\u003e\u003c/li\u003e\n \u003cli\u003e\u003cp\u003eدسترسی به حافظه اصلی بسیار کند است، ما به کش نیاز داریم.\u003c/p\u003e\n \u003cul\u003e\n \u003cli\u003eدسترسی به یک بایت از حافظه اصلی باعث خواندن و کش کردن کل خط کش می‌شود.\u003c/li\u003e\n \u003cli\u003eنوشتن در یک بایت در یک خط کش نیاز به نوشتن کل خط کش دارد.\u003c/li\u003e\n \u003c/ul\u003e\n \u003c/li\u003e\n \u003cli\u003e\u003cp\u003eکوچک = سریع\u003c/p\u003e\n \u003cul\u003e\n \u003cli\u003eکد فشرده و خوب موضعی که در کش جای می‌گیرد سریع‌ترین است.\u003c/li\u003e\n \u003cli\u003eساختارهای داده فشرده که در کش جای می‌گیرند سریع‌ترین هستند.\u003c/li\u003e\n \u003cli\u003eپیمایش‌هایی که فقط به داده‌های کش شده دست می‌زنند سریع‌ترین هستند.\u003c/li\u003e\n \u003c/ul\u003e\n \u003c/li\u003e\n \u003cli\u003e\u003cp\u003eالگوهای دسترسی قابل پیش‌بینی مهم هستند.\u003c/p\u003e\n \u003cul\u003e\n \u003cli\u003eهر زمان که عملی باشد، می‌خواهید از یک پیمایش آرایه خطی استفاده کنید.\u003c/li\u003e\n \u003cli\u003eالگوهای منظمی از دسترسی به حافظه را فراهم کنید.\u003c/li\u003e\n \u003cli\u003eسخت‌افزار می‌تواند پیش‌بینی‌های بهتری در مورد حافظه مورد نیاز انجام دهد.\u003c/li\u003e\n \u003c/ul\u003e\n \u003c/li\u003e\n \u003cli\u003e\u003cp\u003eخطای کش می‌تواند منجر به خطای کش TLB نیز شود.\u003c/p\u003e\n \u003cul\u003e\n \u003cli\u003eکش ترجمه آدرس مجازی به آدرس فیزیکی.\u003c/li\u003e\n \u003cli\u003eمنتظر ماندن برای سیستم عامل تا به ما بگوید حافظه کجاست.\u003c/li\u003e\n \u003c/ul\u003e\n \u003c/li\u003e\n \u003c/ul\u003e \n\n \u003ch2\u003eدیاگرام ها\u003c/h2\u003e\n \n \n \u003cp\u003e\n \u003cb\u003eتاخیرهای\u003c/b\u003e *تعریف شده* \u003cb\u003eصنعت\u003c/b\u003e\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eL1 cache reference ......................... 0.5 ns ................... 6 ins\nBranch mispredict ............................ 5 ns ................... 60 ins\nL2 cache reference ........................... 7 ns ................... 84 ins\nMutex lock/unlock ........................... 25 ns .................. 300 ins\nMain memory reference ...................... 100 ns ................. 1200 ins \nCompress 1K bytes with Zippy ............. 3,000 ns (3 µs) ........... 36k ins\nSend 2K bytes over 1 Gbps network ....... 20,000 ns (20 µs) ........ 240k ins\nSSD random read ........................ 150,000 ns (150 µs) ........ 1.8M ins\nRead 1 MB sequentially from memory ..... 250,000 ns (250 µs) .......... 3M ins\nRound trip within same datacenter ...... 500,000 ns (0.5 ms) .......... 6M ins\nRead 1 MB sequentially from SSD- ..... 1,000,000 ns (1 ms) ........... 12M ins\nDisk seek ........................... 10,000,000 ns (10 ms) ......... 120M ins\nRead 1 MB sequentially from disk .... 20,000,000 ns (20 ms) ......... 240M ins\nSend packet CA-\u0026gt;Netherlands-\u0026gt;CA .... 150,000,000 ns (150 ms) ........ 1.8B ins\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n \u003cb\u003eتصویر\u003c/b\u003e \u003cb\u003eتاخیرهای\u003c/b\u003e \u003cb\u003eکش\u003c/b\u003e\n \u003c/p\u003e\n \n\n \u003ca href=\"/tour/eng/static/img/cache_latencies_graph.png\" target=\"_blank\"\u003e\n \u003cimg class=\"codeimg\" src=\"/tour/eng/static/img/cache_latencies_graph.png\"\u003e\n \u003c/a\u003e\n\n\n \u003ch2\u003eExtra Reading\u003c/h2\u003e\n \n \n \u003cp\u003e\n \u003cb\u003eCPU\u003c/b\u003e \u003cb\u003eCaches\u003c/b\u003e \u003cb\u003e/\u003c/b\u003e \u003cb\u003eMemory\u003c/b\u003e\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.youtube.com/watch?v=WDIkqP4JbkE\" target=\"_blank\"\u003eCPU Caches and Why You Care - Video\u003c/a\u003e - Scott Meyers \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.youtube.com/watch?v=OFgxAFdxYAQ\" target=\"_blank\"\u003eA Crash Course in Modern Hardware - Video\u003c/a\u003e - Cliff Click \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"http://frankdenneman.nl/2016/07/06/introduction-2016-numa-deep-dive-series/\" target=\"_blank\"\u003eNUMA Deep Dive Series\u003c/a\u003e - Frank Denneman \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"http://www.aristeia.com/TalkNotes/codedive-CPUCachesHandouts.pdf\" target=\"_blank\"\u003eCPU Caches and Why You Care - Deck\u003c/a\u003e - Scott Meyers \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.youtube.com/watch?v=MC1EKLQ2Wmg\" target=\"_blank\"\u003eMythbusting Modern Hardware to Gain \u0026#39;Mechanical Sympathy\u0026#39;\u003c/a\u003e - Martin Thompson \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"http://www.akkadia.org/drepper/cpumemory.pdf\" target=\"_blank\"\u003eWhat Every Programmer Should Know About Memory\u003c/a\u003e - Ulrich Drepper \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"http://www.extremetech.com/extreme/188776-how-l1-and-l2-cpu-caches-work-and-why-theyre-an-essential-part-of-modern-chips\" target=\"_blank\"\u003eHow CPU Caches Work and Why\u003c/a\u003e - Joel Hruska \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"http://www.lighterra.com/papers/modernmicroprocessors\" target=\"_blank\"\u003eModern Microprocessors A 90 Minute Guide\u003c/a\u003e - Jason Robert Carey Patterson \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"http://lwn.net/Articles/252125\" target=\"_blank\"\u003eMemory part 2: CPU caches\u003c/a\u003e - Ulrich Drepper \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"http://www.gotw.ca/publications/concurrency-ddj.htm\" target=\"_blank\"\u003eThe Free Lunch Is Over\u003c/a\u003e - Herb Sutter \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://m.youtube.com/watch?feature=youtu.be\u0026amp;v=QBu2Ae8-8LM\" target=\"_blank\"\u003eData Center Computers: Modern Challenges in CPU Design\u003c/a\u003e - Dick Sites \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://en.wikipedia.org/wiki/Wirth%27s_law\" target=\"_blank\"\u003eWirth\u0026#39;s Law\u003c/a\u003e - Wikipedia \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"http://www.drdobbs.com/parallel/eliminate-false-sharing/217500206\" target=\"_blank\"\u003eEliminate False Sharing\u003c/a\u003e - Herb Sutter \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"http://www.ilikebigbits.com/2014_04_21_myth_of_ram_1.html\" target=\"_blank\"\u003eThe Myth Of Ram\u003c/a\u003e - Emil Ernerfeldt \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.infoq.com/presentations/hardware-transactional-memory\" target=\"_blank\"\u003eUnderstanding Transaction Hardware Memory\u003c/a\u003e - Gil Gene \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://youtu.be/jEG4Qyo_4Bc?t=266\" target=\"_blank\"\u003ePerformance Through Cache-Friendliness (4:25-5:48)\u003c/a\u003e - Damian Gryski \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.youtube.com/watch?v=2EWejmkKlxs\" target=\"_blank\"\u003eGoing Nowhere Faster\u003c/a\u003e - Chandler Carruth \u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n \u003cb\u003eData-Oriented\u003c/b\u003e \u003cb\u003eDesign\u003c/b\u003e\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.youtube.com/watch?v=rX0ItVEVjHc\" target=\"_blank\"\u003eData-Oriented Design and C++\u003c/a\u003e - Mike Acton \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.youtube.com/watch?v=fHNmRkzxHWs\" target=\"_blank\"\u003eEfficiency with Algorithms, Performance with Data Structures\u003c/a\u003e - Chandler Carruth \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.youtube.com/watch?v=LrVi9LHP8Bk\" target=\"_blank\"\u003eTaming the performance Beast\u003c/a\u003e - Klaus Iglberger \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"http://harmful.cat-v.org/software/OO_programming/_pdf/Pitfalls_of_Object_Oriented_Programming_GCAP_09.pdf\" target=\"_blank\"\u003ePitfalls of OOP\u003c/a\u003e - Tony Albrecht \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.youtube.com/watch?v=YQs6IC-vgmo\" target=\"_blank\"\u003eWhy you should avoid Linked Lists\u003c/a\u003e - Bjarne Stroustrup \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"http://gamesfromwithin.com/data-oriented-design\" target=\"_blank\"\u003eData-Oriented Design (Or Why You Might Be Shooting Yourself in The Foot With OOP)\u003c/a\u003e - Noel \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.quora.com/Was-object-oriented-programming-a-failure\" target=\"_blank\"\u003eWas object-oriented programming a failure?\u003c/a\u003e - Quora \u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eنکات\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003eاگر داده ها را درک نکنید، مشکل را درک نمی کنید.\u003c/li\u003e\n \n \u003cli\u003eاگر هزینه حل مشکل را درک نکنید، نمی توانید در مورد مشکل استدلال کنید.\u003c/li\u003e\n \n \u003cli\u003eاگر سخت‌افزار را درک نکنید، نمی‌توانید در مورد هزینه حل مشکل استدلال کنید.\u003c/li\u003e\n \n \u003cli\u003eآرایه‌ها ساختارهای داده‌ای با طول ثابت هستند که نمی‌توانند تغییر کنند.\u003c/li\u003e\n \n \u003cli\u003eآرایه‌هایی با اندازه‌های مختلف به عنوان انواع مختلف در نظر گرفته می‌شوند.\u003c/li\u003e\n \n \u003cli\u003eحافظه به صورت بلوک متصل اختصاص می‌یابد.\u003c/li\u003e\n \n \u003cli\u003eGo به شما کنترل بر مکان محلی می‌دهد.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n","Files":[{"Name":"example1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to declare and iterate over\n// arrays of different types.\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\n\t// Declare an array of five strings that is initialized\n\t// to its zero value.\n\tvar fruits [5]string\n\tfruits[0] = \"Apple\"\n\tfruits[1] = \"Orange\"\n\tfruits[2] = \"Banana\"\n\tfruits[3] = \"Grape\"\n\tfruits[4] = \"Plum\"\n\n\t// Iterate over the array of strings.\n\tfor i, fruit := range fruits {\n\t\tfmt.Println(i, fruit)\n\t}\n\n\t// Declare an array of 4 integers that is initialized\n\t// with some values.\n\tnumbers := [4]int{10, 20, 30, 40}\n\n\t// Iterate over the array of numbers.\n\tfor i := 0; i \u003c len(numbers); i++ {\n\t\tfmt.Println(i, numbers[i])\n\t}\n}\n","Hash":"M3wQYrY4LVeJJpujAB3Bj6RhBDE="},{"Name":"example2.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how arrays of different sizes are\n// not of the same type.\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\n\t// Declare an array of 5 integers that is initialized\n\t// to its zero value.\n\tvar five [5]int\n\n\t// Declare an array of 4 integers that is initialized\n\t// with some values.\n\tfour := [4]int{10, 20, 30, 40}\n\n\t// Assign one array to the other\n\tfive = four\n\n\t// ./example2.go:21: cannot use four (type [4]int) as type [5]int in assignment\n\n\tfmt.Println(four)\n\tfmt.Println(five)\n}\n","Hash":"iCUwlorg4R1wyH2vlL81vBIs+t8="},{"Name":"example3.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how the behavior of the for range and\n// how memory for an array is contiguous.\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\n\t// Declare an array of 5 strings initialized with values.\n\tfriends := [5]string{\"Annie\", \"Betty\", \"Charley\", \"Doug\", \"Edward\"}\n\n\t// Iterate over the array displaying the value and\n\t// address of each element.\n\tfor i, v := range friends {\n\t\tfmt.Printf(\"Value[%s]\\tAddress[%p] IndexAddr[%p]\\n\", v, \u0026v, \u0026friends[i])\n\t}\n}\n","Hash":"yopDkmW+NnO/BLh1hJ1XHxmy5EA="},{"Name":"example4.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how the for range has both value and pointer semantics.\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\n\t// Using the pointer semantic form of the for range.\n\tfriends := [5]string{\"Annie\", \"Betty\", \"Charley\", \"Doug\", \"Edward\"}\n\tfmt.Printf(\"Bfr[%s] : \", friends[1])\n\n\tfor i := range friends {\n\t\tfriends[1] = \"Jack\"\n\n\t\tif i == 1 {\n\t\t\tfmt.Printf(\"Aft[%s]\\n\", friends[1])\n\t\t}\n\t}\n\n\t// Using the value semantic form of the for range.\n\tfriends = [5]string{\"Annie\", \"Betty\", \"Charley\", \"Doug\", \"Edward\"}\n\tfmt.Printf(\"Bfr[%s] : \", friends[1])\n\n\tfor i, v := range friends {\n\t\tfriends[1] = \"Jack\"\n\n\t\tif i == 1 {\n\t\t\tfmt.Printf(\"v[%s]\\n\", v)\n\t\t}\n\t}\n\n\t// Using the value semantic form of the for range but with pointer\n\t// semantic access. DON'T DO THIS.\n\tfriends = [5]string{\"Annie\", \"Betty\", \"Charley\", \"Doug\", \"Edward\"}\n\tfmt.Printf(\"Bfr[%s] : \", friends[1])\n\n\tfor i, v := range \u0026friends {\n\t\tfriends[1] = \"Jack\"\n\n\t\tif i == 1 {\n\t\t\tfmt.Printf(\"v[%s]\\n\", v)\n\t\t}\n\t}\n}\n","Hash":"SxUjCs3iY3iv3zlb7yrlSdZNdIM="}]},{"Title":"تمرینات","Content":"\n \u003ch2\u003eتمرینات\u003c/h2\u003e\n \n \n \u003cp\u003e\n از قالب به عنوان نقطه شروع برای تکمیل تمرینات استفاده کنید. یک راه حل ممکن ارائه شده است.\n \u003c/p\u003e\n \n\n \u003ch2\u003eتمرین 1\u003c/h2\u003e\n \n \n \u003cp\u003e\n یک آرایه از 5 رشته با هر عنصر مقداردهی شده به مقدار صفر خود اعلام کنید.\n\n\n یک آرایه دوم از 5 رشته اعلام کنید و این آرایه را با مقادیر رشته ای مقداریدهی کنید.\n\n\n آرایه دوم را به آرایه اول اختصاص دهید و نتایج آرایه اول را نمایش دهید.\n\n\n ارزش رشته و آدرس هر عنصر را نمایش دهید.\n \u003c/p\u003e\n \n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n","Files":[{"Name":"exercise1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Declare an array of 5 strings with each element initialized to its zero value.\n//\n// Declare a second array of 5 strings and initialize this array with literal string\n// values. Assign the second array to the first and display the results of the first array.\n// Display the string value and address of each element.\npackage main\n\n// Add imports.\n\nfunc main() {\n\n\t// Declare an array of 5 strings set to its zero value.\n\n\t// Declare an array of 5 strings and pre-populate it with names.\n\n\t// Assign the populated array to the array of zero values.\n\n\t// Iterate over the first array declared.\n\t// Display the string value and address of each element.\n}\n","Hash":"xhpHHnz5zP1bGB1GKH/72FcBvtI="},{"Name":"answer1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Declare an array of 5 strings with each element initialized to its zero value.\n//\n// Declare a second array of 5 strings and initialize this array with literal string\n// values. Assign the second array to the first and display the results of the first array.\n// Display the string value and address of each element.\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\n\t// Declare string arrays to hold names.\n\tvar names [5]string\n\n\t// Declare an array pre-populated with friend's names.\n\tfriends := [5]string{\"Joe\", \"Ed\", \"Jim\", \"Erick\", \"Bill\"}\n\n\t// Assign the array of friends to the names array.\n\tnames = friends\n\n\t// Display each string value and address index in names.\n\tfor i, name := range names {\n\t\tfmt.Println(name, \u0026names[i])\n\t}\n}\n","Hash":"C9l3P0ajqI3QHiYA92po7uSzsLU="}]}]} ,"composition-mocking":{"Title":"Mocking","Description":"از آنجا که کامپایلر می‌تواند تجزیه و تحلیل کد استاتیک را انجام دهد تا بفهمد آیا یک مقدار محکم یک رابط را پیاده‌سازی می‌کند یا خیر، توسعه‌دهنده‌ای که نوع محکم را اعلام می‌کند نیازی به ارائه رابط‌ها هم ندارد.","Pages":[{"Title":"Mocking","Content":"\n \u003ch2\u003eMocking\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/training/individual-on-demand/ultimate-go-bundle/\" target=\"_blank\"\u003eتماشای ویدئو\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003eنیاز به کمک مالی دارید؟ از \u003ca href=\"https://www.ardanlabs.com/scholarship/\" target=\"_blank\"\u003eفرم Scholarship\u003c/a\u003e ما استفاده کنید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n بهترین راه برای بهره‌برداری از تعبیه‌سازی، از طریق الگوی طراحی ترکیبی است. ایده این است که از اجزای کوچکتر، انواع بزرگ‌تری را تشکیل داده و بر ترکیب رفتار تمرکز کنید.\n \u003c/p\u003e\n \n\n \u003ch2\u003eبررسی کد\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003cb\u003eExample\u003c/b\u003e \u003cb\u003e1:\u003c/b\u003e Mocking\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\t\n\t\t\n\t\n\n\n \u003ch2\u003eInterface Ownership\u003c/h2\u003e\n \n \n \u003cp\u003e\n یکی از ویژگی‌های متفاوت Go نسبت به زبان‌های دیگر، ایده توافق بر تنظیم است. این واقعاً در روش Go برای مطابقت با رابط‌ها نشان داده می‌شود. به دلیل اینکه کامپایلر می‌تواند تجزیه و تحلیل کد استاتیک را انجام دهد تا بفهمد آیا یک مقدار محکم یک رابط را پیاده‌سازی می‌کند یا خیر، توسعه‌دهنده‌ای که نوع محکم را اعلام می‌کند نیازی به ارائه رابط‌ها هم ندارد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003epackage pubsub\n\ntype PubSub struct {\n host string\n}\n\nfunc New(host string) *PubSub {\n return \u0026amp;PubSub{\n host: host,\n }\n}\n\nfunc (ps *PubSub) Publish(key string, v interface{}) error {\n // PRETEND THERE IS A SPECIFIC IMPLEMENTATION.\n return nil\n}\n\nfunc (ps *PubSub) Subscribe(key string) error {\n // PRETEND THERE IS A SPECIFIC IMPLEMENTATION.\n return nil\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n شما به تازگی یک API جدید پیاده‌سازی کرده‌اید که یک پیاده‌سازی محکم برای عملیات انتشار و مشترک‌سازی فراهم می‌کند. هیچ رابطی ارائه نشده است زیرا این API به یک رابط نیاز ندارد. این یک پیاده‌سازی محکم واحد است.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n اما اگر توسعه‌دهنده برنامه که می‌خواهد از این API جدید استفاده کند، به دلیل نیاز به تقلید این پیاده‌سازی در زمان آزمون‌ها، نیاز به یک رابط داشته باشد؟ در Go، این توسعه‌دهنده می‌تواند رابط را اعلام کند و کامپایلر مطابقت آن را شناسایی کند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003epackage main\n\ntype publisher interface {\n Publish(key string, v interface{}) error\n Subscribe(key string) error\n}\n\ntype mock struct{}\n\nfunc (m *mock) Publish(key string, v interface{}) error {\n // ADD MY MOCK FOR THE PUBLISH CALL.\n return nil\n}\n\nfunc (m *mock) Subscribe(key string) error {\n // ADD MY MOCK FOR THE SUBSCRIBE CALL.\n return nil\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این کد در بسته‌ی اصلی یک رابط را اعلام می‌کند. این رابط نماینده‌ی API است که برنامه از بسته‌ی pubsub استفاده می‌کند. توسعه‌دهنده، یک پیاده‌سازی خود برای pubsub را برای آزمون پیاده‌سازی کرده است. کلید این است که توسعه‌دهنده برنامه به طور مستقیم از هیچ پیاده‌سازی محکمی استفاده نمی‌کند، بلکه از طریق رابط خود، خود را از آن جدا می‌کند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc main() {\n pubs := []publisher{\n pubsub.New(\u0026#34;localhost\u0026#34;),\n \u0026amp;mock{},\n }\n\n for _, p := range pubs {\n p.Publish(\u0026#34;key\u0026#34;, \u0026#34;value\u0026#34;)\n p.Subscribe(\u0026#34;key\u0026#34;)\n }\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n برای ارائه یک مثال، تابع اصلی یک مجموعه را ساختاردهی می‌کند که با پیاده‌سازی pubsub و پیاده‌سازی موک شروع می‌شود. رابط publisher این کار را ممکن می‌سازد. سپس یک حلقه for range پیاده‌سازی می‌شود تا نشان دهد چگونه کد برنامه از هر نوع پیاده‌سازی محکمی جدا شده است.\n \u003c/p\u003e\n \n\n\n","Files":[{"Name":"example1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how you can personally mock concrete types when\n// you need to for your own packages or tests.\npackage main\n\nimport (\n\t\"play.ground/pubsub\"\n)\n\n// publisher is an interface to allow this package to mock the pubsub\n// package support.\ntype publisher interface {\n\tPublish(key string, v interface{}) error\n\tSubscribe(key string) error\n}\n\n// =============================================================================\n\n// mock is a concrete type to help support the mocking of the pubsub package.\ntype mock struct{}\n\n// Publish implements the publisher interface for the mock.\nfunc (m *mock) Publish(key string, v interface{}) error {\n\n\t// ADD YOUR MOCK FOR THE PUBLISH CALL.\n\treturn nil\n}\n\n// Subscribe implements the publisher interface for the mock.\nfunc (m *mock) Subscribe(key string) error {\n\n\t// ADD YOUR MOCK FOR THE SUBSCRIBE CALL.\n\treturn nil\n}\n\n// =============================================================================\n\nfunc main() {\n\n\t// Create a slice of publisher interface values. Assign\n\t// the address of a pubsub.PubSub value and the address of\n\t// a mock value.\n\tpubs := []publisher{\n\t\tpubsub.New(\"localhost\"),\n\t\t\u0026mock{},\n\t}\n\n\t// Range over the interface value to see how the publisher\n\t// interface provides the level of decoupling the user needs.\n\t// The pubsub package did not need to provide the interface type.\n\tfor _, p := range pubs {\n\t\tp.Publish(\"key\", \"value\")\n\t\tp.Subscribe(\"key\")\n\t}\n}\n\n// -----------------------------------------------------------------------------\n-- pubsub/pubsub.go --\n\n// Package pubsub simulates a package that provides publication/subscription\n// type services.\npackage pubsub\n\n// PubSub provides access to a queue system.\ntype PubSub struct {\n\thost string\n\n\t// PRETEND THERE ARE MORE FIELDS.\n}\n\n// New creates a pubsub value for use.\nfunc New(host string) *PubSub {\n\tps := PubSub{\n\t\thost: host,\n\t}\n\n\t// PRETEND THERE IS A SPECIFIC IMPLEMENTATION.\n\n\treturn \u0026ps\n}\n\n// Publish sends the data for the specified key.\nfunc (ps *PubSub) Publish(key string, v interface{}) error {\n\n\t// PRETEND THERE IS A SPECIFIC IMPLEMENTATION.\n\treturn nil\n}\n\n// Subscribe sets up an request to receive messages for the specified key.\nfunc (ps *PubSub) Subscribe(key string) error {\n\n\t// PRETEND THERE IS A SPECIFIC IMPLEMENTATION.\n\treturn nil\n}\n\n// -----------------------------------------------------------------------------\n-- go.mod --\n \nmodule \"play.ground\"\n\ngo 1.24.0\n","Hash":"wssSd4UC5vQCGhI8HTaOsVwMjDw="}]}]} ,"generics-basics":{"Title":"مفاهیم پایه","Description":"یادگیری نحوه نوشتن یک تابع چاپ عمومی پایه.","Pages":[{"Title":"ژنریک - مفاهیم پایه","Content":"\n \u003ch2\u003eژنریک - مفاهیم پایه\u003c/h2\u003e\n \n \n \u003cp\u003e\n یادگیری نحوه نوشتن یک تابع چاپ عمومی پایه.\n \u003c/p\u003e\n \n\n \u003ch2\u003eویدئو\u003c/h2\u003e\n \n \n \u003cp\u003e\n تماشای سخنرانی‌ام در مورد ژنریک که شما را از طریق همهٔ مثال‌ها در این بخش از تور می‌گذراند.\n \u003c/p\u003e\n \n\u003ciframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/gIEPspmbMHM\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" allowfullscreen\u003e\u003c/iframe\u003e\n\n \u003ch2\u003eمرور کد\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e1\u003c/b\u003e: پیاده‌سازی محدودیتی از چاپ\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e2\u003c/b\u003e: پیاده‌سازی با تصدی نوع از چاپ\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e3\u003c/b\u003e: پیاده‌سازی با بازتاب از چاپ\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e4\u003c/b\u003e: پیاده‌سازی ژنریک از چاپ\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n \u003ch2\u003eتوضیح داده شده\u003c/h2\u003e\n \n \n \u003cp\u003e\n اگر می‌خواهید یک تابع چاپ تنها بنویسید که می‌تواند یک تایپ‌لیست از هر نوع داده دلخواهی را خروجی دهد و از بازتاب (reflection) استفاده نکنید، می‌توانید از نحوه‌نویسی ژنریک جدید استفاده کنید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc print[T any](slice []T) {\n fmt.Print(\u0026#34;Generic: \u0026#34;)\n \n for _, v := range slice {\n fmt.Print(v, \u0026#34; \u0026#34;)\n }\n\n fmt.Print(\u0026#34;\\n\u0026#34;)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این یک پیاده‌سازی از تابع چاپ تنها است که می‌تواند تایپ‌لیستی از هر نوع داده دلخواه را با استفاده از نحوه‌نویسی ژنریک جدید به خروجی بدهد. نکته‌ای که در مورد این نحوه‌نویسی جدید جالب است این است که کد داخل تابع می‌تواند از نحوه‌نویسی و توابع داخلی استفاده کند که با یک نوع کانکرت (مشخص) کار می‌کند. این در مورد استفاده از رابط خالی برای نوشتن کد ژنریک صادق نیست.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n باید یک روش وجود داشته باشد که به کامپایلر بگویید که نوع T را به صورت صریح اعلام نمی‌کنید، اما باید در زمان کامپایل توسط کامپایلر تعیین شود. نحوه‌نویسی جدید از پرانتز مربعی برای این منظور استفاده می‌کند. این پرانتزها یک فهرست از شناسه‌های نوع ژنریک را تعریف می‌کنند که نماینده انواع خاص مربوط به تابع هستند و باید در زمان کامپایل تعیین شوند. این گونه به کامپایلر می‌گویید که انواع با این نام‌ها قبل از کامپایل برنامه اعلام نخواهند شد و باید در زمان کامپایل تعیین شوند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n توجه: می‌توانید چندین شناسه نوعی داخل پرانتز تعریف کنید، اگرچه مثال فعلی تنها یک شناسه نوعی را مورد استفاده قرار داده است. برای مثال: [T، S، R هر کدام]\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n همچنین می‌توانید این شناسه‌های نوعی را به هر نامی که برای خوانایی کد مفید است، نامگذاری کنید. در این مورد، کد از حروف بزرگ T برای توصیف استفاده کرده است که نشان دهنده تایپ‌لیستی از نوعی مشخص (که در زمان کامپایل تعیین می‌شود) که وارد تابع می‌شود، خواهد بود. این یک کلمه به عنوان مثال استفاده از یک حرف بزرگ واحد است که در مورد مجموعه‌ها شایع است و همچنین یک اصطلاح است که به برنامه‌نویسی‌های قدیمی‌تر مانند C++ و جاوا برمی‌گردد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n همچنین از کلمه any داخل پرانتز استفاده شده است. این نمایانگر یک محدودیت در مورد نوع T است که توسط کامپایلر مورد نیاز است\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003enumbers := []int{1, 2, 3}\nprint[int](numbers)\n\nstrings := []string{\u0026#34;A\u0026#34;, \u0026#34;B\u0026#34;, \u0026#34;C\u0026#34;}\nprint[string](strings)\n\nfloats := []float64{1.7, 2.2, 3.14}\nprint[float64](floats)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این نمونه‌ها نشان می‌دهند که چگونه به تابع ژنریک چاپ فراخوانی کنید، جایی که اطلاعات نوع T به صورت صریح در محل فراخوانی تابع ارائه شده است. نحوه‌نویسی اینجا به ایده ایمیته که تعریف تابع `func name[T any](slice []T)` دو مجموعه پارامتر تعریف می‌کند، اشاره دارد. مجموعه اول نوع است که به شناسه‌های نوع متناظر نقش می‌بیند و مجموعه دوم داده‌ای است که به متغیرهای ورودی متناظر می‌پردازد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n خوشبختانه، کامپایلر می‌تواند نوع را استخراج کرده و نیاز به ارسال اطلاعات نوع به صورت صریح در محل فراخوانی را از بین ببرد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003enumbers := []int{1, 2, 3}\nprint(numbers)\n\nstrings := []string{\u0026#34;A\u0026#34;, \u0026#34;B\u0026#34;, \u0026#34;C\u0026#34;}\nprint(strings)\n\nfloats := []float64{1.7, 2.2, 3.14}\nprint(floats)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این کد نشان می‌دهد که چگونه می‌توان توابع ژنریک چاپ را بدون نیاز به انتقال اطلاعات نوع به صورت صریح فراخوانی کرد. در محل فراخوانی تابع، کامپایلر قادر است نوعی که برای T استفاده کند را تشخیص دهد و نسخه متمایزی از تابع را برای پشتیبانی از اسلایس‌های آن نوع ایجاد کند. کامپایلر قادر است با استفاده از اطلاعات موجود در محل فراخوانی از داده‌هایی که داده می‌شود، نوع را استنباط کند.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eتمرین 1\u003c/h2\u003e\n \n \n \u003cp\u003e\n یک تابع ژنریک به نام marshal را پیاده‌سازی کنید که هر مقداری را قبول کرده و آن مقدار را به JSON تبدیل کرده و JSON و یک خطا بازگرداند. یک نوع ساختاری به نام User با دو فیلد Name و Age اعلام کنید. سپس یک مقدار از نوع User را ایجاد کرده و مقدار را به تابع marshal بدهید.\n \u003c/p\u003e\n \n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n","Files":[{"Name":"example1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how concrete implementations of print functions that can\n// only work with slices of the specified type.\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc printNumbers(numbers []int) {\n\tfmt.Print(\"Numbers: \")\n\n\tfor _, num := range numbers {\n\t\tfmt.Print(num, \" \")\n\t}\n\n\tfmt.Print(\"\\n\")\n}\n\nfunc printStrings(strings []string) {\n\tfmt.Print(\"Strings: \")\n\n\tfor _, str := range strings {\n\t\tfmt.Print(str, \" \")\n\t}\n\n\tfmt.Print(\"\\n\")\n}\n\nfunc main() {\n\tnumbers := []int{1, 2, 3}\n\tprintNumbers(numbers)\n\n\tstrings := []string{\"A\", \"B\", \"C\"}\n\tprintStrings(strings)\n}\n","Hash":"nHivjUU5W1B5z8jFm1TFVOhUE6E="},{"Name":"example2.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to write a function that provides an empty interface\n// solution which uses type assertions for the different concrete slices to be\n// supported. We've basically moved the functions from above into case statements.\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc printAssert(v interface{}) {\n\tfmt.Print(\"Assert: \")\n\n\tswitch list := v.(type) {\n\tcase []int:\n\t\tfor _, num := range list {\n\t\t\tfmt.Print(num, \" \")\n\t\t}\n\n\tcase []string:\n\t\tfor _, str := range list {\n\t\t\tfmt.Print(str, \" \")\n\t\t}\n\t}\n\n\tfmt.Print(\"\\n\")\n}\n\nfunc main() {\n\tnumbers := []int{1, 2, 3}\n\tprintAssert(numbers)\n\n\tstrings := []string{\"A\", \"B\", \"C\"}\n\tprintAssert(strings)\n}\n","Hash":"KfiX29hKp8DnzlZSWGm7WEwlIQA="},{"Name":"example3.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to write a function that provides a reflection\n// solution which allows a slice of any type to be provided and printed. This\n// is a generic function thanks to the reflect package.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n)\n\nfunc printReflect(v interface{}) {\n\tfmt.Print(\"Reflect: \")\n\n\tval := reflect.ValueOf(v)\n\tif val.Kind() != reflect.Slice {\n\t\treturn\n\t}\n\n\tfor i := 0; i \u003c val.Len(); i++ {\n\t\tfmt.Print(val.Index(i).Interface(), \" \")\n\t}\n\n\tfmt.Print(\"\\n\")\n}\n\nfunc main() {\n\tnumbers := []int{1, 2, 3}\n\tprintReflect(numbers)\n\tprint(numbers)\n\n\tstrings := []string{\"A\", \"B\", \"C\"}\n\tprintReflect(strings)\n}\n","Hash":"cxzabIpoqtllWWxtgTre3edzuR4="},{"Name":"example4.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to write a function that provides a generics\n// solution which allows a slice of any type T (to be determined later) to be\n// passed and printed.\npackage main\n\nimport (\n\t\"fmt\"\n)\n\n// To avoid the ambiguity with array declarations, type parameters require a\n// constraint to be applied. The `any` constraint states there is no constraint\n// on what type T can become. The predeclared identifier `any` is an alias for\n// `interface{}`.\n//\n// This code more closely resembles the concrete implementations that we started\n// with and is easier to read than the reflect implementation.\n\nfunc print[T any](slice []T) {\n\tfmt.Print(\"Generic: \")\n\n\tfor _, v := range slice {\n\t\tfmt.Print(v, \" \")\n\t}\n\n\tfmt.Print(\"\\n\")\n}\n\n// =============================================================================\n\nfunc main() {\n\tnumbers := []int{1, 2, 3}\n\tprint(numbers)\n\n\tstrings := []string{\"A\", \"B\", \"C\"}\n\tprint(strings)\n}\n","Hash":"+rhgLI9ZrEl06woMwIe1lhKWfjw="},{"Name":"exercise1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Implement a generic function that can marshal JSON.\npackage main\n\n// Add imports.\n\n// Implement the generic function named marshal that can accept any value\n// of type T and marshal that value into JSON.\n\n// Declare a struct type named User with two fields, Name and Age.\n\nfunc main() {\n\t// Construct a value of type User.\n\n\t// Call the generic marshal function.\n\n\t// Print the JSON produced by the marshal function.\n}\n","Hash":"9gD0O6iS29Jfj6gB+2ACNByyHdc="},{"Name":"answer1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Implement a generic function that can marshal JSON.\npackage main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// Implement the generic function named marshal that can accept any value\n// of type T and marshal that value into JSON.\nfunc marshal[T any](v T) ([]byte, error) {\n\tdata, err := json.Marshal(v)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn data, nil\n}\n\n// Declare a struct type named User with two fields, Name and Age.\ntype User struct {\n\tName string\n\tAge int\n}\n\nfunc main() {\n\n\t// Construct a value of type User.\n\tu := User{\n\t\tName: \"Bill\",\n\t\tAge: 10,\n\t}\n\n\t// Call the generic marshal function.\n\tdata, err := marshal(u)\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\treturn\n\t}\n\n\t// Print the JSON produced by the marshal function.\n\tfmt.Println(string(data))\n}\n","Hash":"RpSVxCKUMCrVM3yNqmj4J7bHnG0="}]}]} ,"generics-hash-table":{"Title":"جداول هش","Description":"یک جدول هش مثال کلاسیکی از یک نوع مخزن است که می‌تواند از ژنریک بهره‌برداری واقعی داشته باشد.","Pages":[{"Title":"ژنریک‌ها - جداول هش","Content":"\n \u003ch2\u003eژنریک‌ها - جداول هش\u003c/h2\u003e\n \n \n \u003cp\u003e\n یک جدول هش مثال کلاسیکی از یک نوع مخزن است که می‌تواند از ویژگی‌های واقعی ژنریک بهره‌برداری کند. این پیاده‌سازی توسط Matt Layher (@mdlayer) در یک پست وبلاگی که نوشته است، انجام شده است. این یک مثال عالی از امکاناتی است که با استفاده از ژنریک ممکن است.\n \u003c/p\u003e\n \n\n \u003ch2\u003eویدیو\u003c/h2\u003e\n \n \n \u003cp\u003e\n سخنرانی من در مورد ژنریک‌ها را که شما را از طریق تمام مثال‌ها در این بخش از تور هدایت می‌کند، تماشا کنید.\n \u003c/p\u003e\n \n\u003ciframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/gIEPspmbMHM\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" allowfullscreen\u003e\u003c/iframe\u003e\n\n \u003ch2\u003eبررسی کد\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e1\u003c/b\u003e: جدول هش\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\t\n\t\t\n\t\n\n \u003ch2\u003eتوضیح داده شده\u003c/h2\u003e\n \n \n \u003cp\u003e\n این کد یکم پیچیده‌تر از آنچه تا الآن دیده‌اید است. این چیزی است که انتظار دارید در پیاده‌سازی‌های واقعی ببینید. در طول این بخش، دو دیدگاه از کد را خواهید دید. یکی قبل و دیگری بعد از اعمال نحوه جدید ژنریک.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype hashFunc func(key K, buckets int) int\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این نوع یک امضای تابع هش را تعریف می‌کند که توسط جدول هش برای محاسبه موقعیت سبد برای ذخیره و بازیابی داده‌ها استفاده می‌شود. کاربر باید این تابع را پیاده‌سازی کرده و آن را در هنگام ساخت جدول هش ارائه دهد. این تابع یک کلید و تعداد سبدها را که می‌تواند از آنها انتخاب کند، می‌پذیرد. چون می‌خواهید این سیستم در اصطلاح انواع استفاده شده برای کلید و مقدار ژنریک باشد، یک پارامتر به نام key با نوع حرف بزرگ K تعریف می‌کنید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n بعداً می‌توانید نحوه جدید ژنریک را برای تبدیل K به یک نوع ژنریک واقعی اعمال کنید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype hashFunc[K comparable] func(key K, buckets int) int \u0026lt;-- تغییر یافته است\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n پس از نام نوع، کروشه‌ها با نوع ژنریک K و محدودیت comparable را اضافه کنید. از آنجایی که مقادیر نوع کلید باید در یک عمل مقایسه استفاده شوند، اینکه این الآن توضیح داده شود، حتی اگر پیاده‌سازی تابع هش به آن نیاز نداشته باشد، منطقی است. همگانیت همه چیز در خوانایی، درک و قابلیت تعمیر در طول زمان می‌باشد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n این نوع یک جفت کلید/مقدار از داده‌ها را نمایان می‌کند که توسط جدول هش ذخیره خواهد شد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype keyValuePair struct {\n Key K\n Value V\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n وظیفه این نوع نگه داشتن داده واقعی با کلید مربوطه است. در ادامه، کد یک فیلد کلید با نوع K و یک فیلد مقدار با نوع V تعریف می‌کند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n حال می‌توانید نحوه جدید ژنریک را برای تبدیل K و V به نوع ژنریک واقعی اعمال کنید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype keyValuePair[K comparable, V any] struct { \u0026lt;-- تغییر یافته است\n Key K\n Value V\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n پس از نام نوع، کروشه‌ها با انواع ژنریک K و V اضافه می‌شوند. در این تعریف، K همچنان نمایانگر کلید است و V نمایانگر یک مقدار است که می‌تواند هر چیزی باشد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n این نوع یک جدول هش را نمایان می‌کند که یک تابع هش و یک مجموعه از سبدها برای ذخیره واژه‌های کلید/مقدار مدیریت می‌کند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype Table struct {\n hashFunc hashFunc\n buckets int\n data [][]keyValuePair\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n نوع Table دارای سه فیلد است: یک تابع هش، تعداد سبدها و داده که به عنوان یک برش از برش‌های جفت کلید/مقدار نمایان داده شده است. برش بیرونی سبدها را نمایان می‌کند و برش داخلی جفت کلید/مقدارها را نمایان می‌کند که در داخل یک سبد ذخیره می‌شوند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n حالا نحوه جدید ژنریک را اعمال کنید تا انواع ژنریک کلید و مقدار را اعلام کنید و آنها را به اعلام فیلد اعمال کنید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype Table[K comparable, V any] struct { \u0026lt;-- تغییر یافته است\n hashFunc hashFunc[K] \u0026lt;-- تغییر یافته است\n buckets int\n data [][]keyValuePair[K, V] \u0026lt;-- تغییر یافته است\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n پس از نام نوع، کروشه‌ها با انواع ژنریک K و V اضافه شوند. تعریف نوع hashFunc نیاز به اطلاعات در مورد نوع واقعی برای کلید دارد. تعریف نوع keyValuePair نیاز به اطلاعات در مورد نوع واقعی کلید و مقدار دارد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n این یک تابع کارخانه است که می‌تواند یک جدول را برای استفاده ایجاد کند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc New(\n buckets int,\n hf hashFunc\n) *Table {\n \n return \u0026amp;Table{\n hashFunc: hf,\n buckets: buckets,\n data: make([][]keyValuePair, buckets),\n }\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n تابع کارخانه تعداد سبدها را برای مدیریت و یک تابع هش برای انتخاب یک سبد برای ذخیره و جستجوی داده‌ها قبول می‌کند. هنگامی که یک مقدار جدول ساخته می‌شود، تعداد سبدها برای ساخت برش استفاده می‌شود و طول برش بیرونی به تعداد سبدها که استفاده خواهد شد، تنظیم می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n حالا نحوه جدید ژنریک را اعمال کنید تا انواع ژنریک کلید و مقدار را اعلام کنید و آنها را به انواعی که باید ساخته شوند، اعمال کنید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc New[K comparable, V any]( \u0026lt;-- تغییر یافته است\n buckets int,\n hf hashFunc[K], \u0026lt;-- تغییر یافته است\n) *Table[K, V] { \u0026lt;-- تغییر یافته است\n \n return \u0026amp;Table[K, V]{ \u0026lt;-- تغییر یافته است\n hashFunc: hf,\n buckets: buckets,\n data: make([][]keyValuePair[K, V], buckets), \u0026lt;-- تغییر یافته است\n }\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n پس از نام نوع، کروشه‌ها و انواع ژنریک K و V را اضافه کنید. سپس K به پارامتر ورودی hf اعمال شود تا تعریف نوع hashFunc کامل شود. انواع K و V به نوع Table که در حال ساخت و برگشت داده می‌شود، اعمال می‌شوند. در نهایت، مقداردهی اولیه فیلد داده نیاز به اعمال انواع K و V به نحوه ساخت انواع keyValuePair دارد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n این یک متد است که می‌تواند مقادیر را براساس یک کلید مشخص به جدول هش اضافه کند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype Table[K comparable, V any] struct {\n hashFunc hashFunc[K]\n buckets int\n table [][]keyValuePair[K, V]\n}\n\nfunc (t *Table) Insert(key K, value V) {\n bucket := t.hashFunc(key, t.buckets)\n for idx, kvp := range t.table[bucket] {\n if key == kvp.Key {\n t.table[bucket][idx].Value = value\n return\n }\n }\n\n kvp := keyValuePair{\n Key: key,\n Value: value,\n }\n t.table[bucket] = append(t.table[bucket], kvp)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n متد Insert به اعلام یک کلید و مقدار با همان انواع ژنریکی که با نوع Table اعلام شده‌اند، تعریف شده است. مرحله اول درج، تعیین سبدی است که برای ذخیره‌سازی استفاده خواهد شد. این کار با فراخوانی تابع هش با کلید مشخص انجام می‌شود. تابع هش یک مقدار عددی را که سبدی را نمایان می‌کند برمی‌گرداند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n سپس تابع بررسی می‌کند که آیا کلید مشخص شده قبلاً برای ذخیره کردن مقداری در سبد انتخابی استفاده شده است یا نه. این با گردش در مجموعه موجود از جفت‌های کلید/مقدار در سبد انجام می‌شود. اگر کلید از قبل وجود داشته باشد، مقدار مربوط به آن کلید به‌روزرسانی می‌شود. اگر کلید پیدا نشود، سپس یک مقدار جدید کلید/مقدار ساخته، مقداردهی و به انتهای برش برای سبد انتخابی اضافه می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n حالا نحوه جدید ژنریک را اعمال کنید تا انواع ژنریک کلید و مقدار را اعلام کنید و آنها را به انواعی که باید ساخته شوند، اعمال کنید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc (t *Table[K, V]) Insert(key K, value V) { \u0026lt;-- CHANGED\n bucket := t.hashFunc(key, t.buckets)\n for idx, kvp := range t.table[bucket] {\n if key == kvp.Key {\n t.table[bucket][idx].Value = value\n return\n }\n }\n\n kvp := keyValuePair[K, V]{ \u0026lt;-- CHANGED\n Key: key,\n Value: value,\n }\n t.table[bucket] = append(t.table[bucket], kvp)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n پس از نام نوع دریافت‌کننده، کروشه‌ها و انواع ژنریک K و V را اضافه کنید. تغییر دیگر تنها اعمال K و V به نحوه ساخت انواع keyValuePair می‌باشد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n این یک متد است که می‌تواند مقادیر را از جدول هش براساس یک کلید مشخص بازیابی کند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc (t *Table) Retrieve(key K) (V, bool) {\n bucket := t.hashFunc(key, t.buckets)\n for idx, kvp := range t.data[bucket] {\n if key == kvp.Key {\n return t.data[bucket][idx].Value, true\n }\n }\n\n var zero V\n return zero, false\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n متد Retrieve به اعلام یک کلید پاسخ داده می‌شود و یک نسخه از مقدار ذخیره شده برای آن کلید برگردانده می‌شود. مرحله اول بازیابی، تعیین سبدی است که برای ذخیره استفاده شده است. این با فراخوانی تابع هش با کلید مشخص انجام می‌شود. تابع هش یک مقدار عددی را که سبدی را نمایان می‌کند برمی‌گرداند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n سپس تابع در طول مجموعه‌ای از جفت‌های کلید/مقدار ذخیره شده درون سبد قرار دارد و به دنبال کلید مشخص می‌گردد. اگر کلید پیدا شود، یک نسخه از مقدار برگردانده می‌شود و به فراخواننده true ارائه می‌شود. اگر کلید پیدا نشود، مقدار صفر برگردانده می‌شود و به فراخواننده false ارائه می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n حالا نحوه جدید ژنریک را اعمال کنید تا انواع ژنریک کلید و مقدار را اعلام کنید و آنها را به انواعی که باید ساخته شوند، اعمال کنید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc (t *Table[K, V]) Get(key K) (V, bool) { \u0026lt;-- CHANGED\n bucket := t.hashFunc(key, t.buckets)\n for idx, kvp := range t.data[bucket] {\n if key == kvp.Key {\n return t.data[bucket][idx].Value, true\n }\n }\n\n var zero V\n return zero, false\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n پس از نام نوع دریافت‌کننده، کروشه‌ها و انواع ژنریک K و V را اضافه کنید. تغییرات کد دیگری نیاز نیست.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n این یک برنامه کوچک است برای تست پیاده‌سازی جدول هش.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n ```go\n\n\n func main() {\n\n\n const buckets = 8\n\n\n // ... (سایر قسمت‌های برنامه)\n\n\n }\n\n\n \u003ccode\u003e`\u003c/code\u003e\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n ابتدا با یک ثابت شروع کنید که تعداد سبدهایی که در جدول هش استفاده می‌شود را تعریف می‌کند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eimport (\n \u0026#34;hash/fnv\u0026#34;\n)\n\nfunc main() {\n . . .\n \n hashFunc1 := func(key string, buckets int) int {\n h := fnv.New32()\n h.Write([]byte(key))\n return int(h.Sum32()) % buckets\n }\n\n . . .\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n سپس، یک تابع هش تعریف کنید که یک رشته را برای کلید اعلام می‌کند. پیاده‌سازی از بسته fnv از کتابخانه استاندارد استفاده می‌کند که توابع هش غیر-رمزنگاری FNV-1 و FNV-1a را پیاده‌سازی می‌کند که توسط Glenn Fowler، Landon Curt Noll و Phong Vo ایجاد شده است. FNV به معنای تابع هش Fowler-Noll-Vo است.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n عملیات باقی‌مانده با مقدار سبدها موجب می‌شود که مقدار نهایی در محدوده تعداد سبدها قرار بگیرد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eimport (\n \u0026#34;hash/fnv\u0026#34;\n)\n\nfunc main() {\n . . .\n \n table1 := New[/*key*/ string, /*value*/ int](buckets, hashFunc1)\n \n . . .\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n سپس یک جدول هش بسازید و به صراحت بیان کنید که کلید از نوع رشته و مقدار از نوع عدد صحیح (int) خواهد بود. در پارامترهای ورودی چیزی وجود ندارد که به کامپایلر کمک کند این اطلاعات را استنتاج کند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n برای نمایش ماهیت جدول هش به عنوان ژنریک، یک تابع هش و جدول دومی تعریف کنید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eimport (\n \u0026#34;hash/fnv\u0026#34;\n)\n\nfunc main() {\n . . .\n\n hashFunc2 := func(key int, buckets int) int {\n return key % buckets\n }\n\n table2 := New[/*key*/ int, /*value*/ string](buckets, hashFunc2)\n\n . . .\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این تابع هش یک عدد صحیح برای کلید اعلام می‌کند و با مقدار سبد در مقابل کلید یک عملیات باقی‌مانده ساده انجام می‌دهد. سپس یک جدول جدید ساخته می‌شود که در آن کلید به عنوان یک عدد صحیح و مقدار به عنوان یک رشته مشخص شده است. عکس اولین جدول.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eimport (\n \u0026#34;hash/fnv\u0026#34;\n)\n\nfunc main() {\n . . .\n\n words := []string{\u0026#34;foo\u0026#34;, \u0026#34;bar\u0026#34;, \u0026#34;baz\u0026#34;}\n for i, word := range words {\n table1.Insert(word, i)\n table2.Insert(i, word)\n }\n\n for i, s := range append(words, \u0026#34;nope!\u0026#34;) {\n v1, ok1 := table1.Retrieve(s)\n fmt.Printf(\u0026#34;t1.Rtr(%v) = (%v, %v)\\n\u0026#34;, s, v1, ok1)\n \n v2, ok2 := table2.Retrieve(i)\n fmt.Printf(\u0026#34;t2.Rtr(%v) = (%v, %v)\\n\u0026#34;, i, v2, ok2)\n }\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003et1.Rtr(foo) = (0, true)\nt2.Rtr(0) = (foo, true)\nt1.Rtr(bar) = (1, true)\nt2.Rtr(1) = (bar, true)\nt1.Rtr(baz) = (2, true)\nt2.Rtr(2) = (baz, true)\nt1.Rtr(nope!) = (0, false)\nt2.Rtr(3) = (, false)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n سرانجام کدی بنویسید تا ارزش‌ها را در دو جدول متناظر ذخیره و بازیابی کنید.\n \u003c/p\u003e\n \n\n\n\n","Files":[{"Name":"example1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This code is provided by Matt Layher (@mdlayher)\n// https://mdlayher.com/blog/go-generics-draft-design-building-a-hashtable/#a-generic-hashtable\n\n// Sample program to show how to write a generic hash table.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"hash/fnv\"\n)\n\n// =============================================================================\n\ntype hashFunc[K comparable] func(key K, buckets int) int\n\ntype keyValuePair[K comparable, V any] struct {\n\tKey K\n\tValue V\n}\n\ntype Table[K comparable, V any] struct {\n\thashFunc hashFunc[K]\n\tbuckets int\n\tdata [][]keyValuePair[K, V]\n}\n\nfunc New[K comparable, V any](buckets int, hf hashFunc[K]) *Table[K, V] {\n\treturn \u0026Table[K, V]{\n\t\thashFunc: hf,\n\t\tbuckets: buckets,\n\t\tdata: make([][]keyValuePair[K, V], buckets),\n\t}\n}\n\nfunc (t *Table[K, V]) Insert(key K, value V) {\n\tbucket := t.hashFunc(key, t.buckets)\n\n\tfor idx, kvp := range t.data[bucket] {\n\t\tif key == kvp.Key {\n\t\t\tt.data[bucket][idx].Value = value\n\t\t\treturn\n\t\t}\n\t}\n\n\tkvp := keyValuePair[K, V]{\n\t\tKey: key,\n\t\tValue: value,\n\t}\n\tt.data[bucket] = append(t.data[bucket], kvp)\n}\n\nfunc (t *Table[K, V]) Retrieve(key K) (V, bool) {\n\tbucket := t.hashFunc(key, t.buckets)\n\n\tfor idx, kvp := range t.data[bucket] {\n\t\tif key == kvp.Key {\n\t\t\treturn t.data[bucket][idx].Value, true\n\t\t}\n\t}\n\n\tvar zero V\n\treturn zero, false\n}\n\n// =============================================================================\n\nfunc main() {\n\tconst buckets = 8\n\n\thashFunc1 := func(key string, buckets int) int {\n\t\th := fnv.New32()\n\t\th.Write([]byte(key))\n\t\treturn int(h.Sum32()) % buckets\n\t}\n\ttable1 := New[ /*key*/ string /*value*/, int](buckets, hashFunc1)\n\n\thashFunc2 := func(key int, buckets int) int {\n\t\treturn key % buckets\n\t}\n\ttable2 := New[ /*key*/ int /*value*/, string](buckets, hashFunc2)\n\n\twords := []string{\"foo\", \"bar\", \"baz\"}\n\tfor i, word := range words {\n\t\ttable1.Insert(word, i)\n\t\ttable2.Insert(i, word)\n\t}\n\n\tfor i, s := range append(words, \"nope!\") {\n\t\tv1, ok1 := table1.Retrieve(s)\n\t\tfmt.Printf(\"t1.Rtr(%v) = (%v, %v)\\n\", s, v1, ok1)\n\n\t\tv2, ok2 := table2.Retrieve(i)\n\t\tfmt.Printf(\"t2.Rtr(%v) = (%v, %v)\\n\", i, v2, ok2)\n\t}\n}\n","Hash":"023N8uB5OtOdtAWV5jCiPF10+HI="}]}]} ,"generics-multi-type-params":{"Title":"پارامترهای چند نوعی","Description":"شما محدود به استفاده از یک نوع ژنریک در هر زمان نیستید.","Pages":[{"Title":"ویدیو","Content":"\n \u003ch2\u003eویدیو\u003c/h2\u003e\n \n \n \u003cp\u003e\n مشاهده می‌کنید که من در مورد ژنریک‌ها چه چیزی ارائه داده‌ام که شما را از طریق تمام مثال‌های این بخش از تور راه می‌دهد.\n \u003c/p\u003e\n \n\u003ciframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/gIEPspmbMHM\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" allowfullscreen\u003e\u003c/iframe\u003e\n \u003ch2\u003eبررسی کد\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e1\u003c/b\u003e: تابع چاپ\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\t\n\t\t\n\t\n\n\n \u003ch2\u003eتوضیح داده شده\u003c/h2\u003e\n \n \n \u003cpre class=\"codeblock\"\u003efunc Print[L any, V fmt.Stringer](labels []L, vals []V) {\n for i, v := range vals {\n fmt.Println(labels[i], v.String())\n }\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n تابع Print یک مجموعه از نوعی L و یک مجموعه از نوعی V را قبول می‌کند. نوع L می‌تواند هر چیزی باشد، اما نوع V محدود به مقادیری است که می‌دانند چگونه به رشته تبدیل شوند. مجموعه‌ای از نوع V به صورت تکراری پیمایش می‌شود و با برچسب متناظر از مجموعه نوع L چاپ می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n نام نوع ژنریک می‌تواند هر چیزی باشد. توصیف نامگذاری برای انواع ژنریک چیزی است که باید برای دستورات بهتر تعریف شود. در حال حاضر، تلاش کنید تا وقتی که برای خوانایی کار می‌کند، به حروف بزرگ تکراری نسبت به نامگذاری انواع ژنریک پایبند باشید.\n \u003c/p\u003e\n \n\n\n","Files":[{"Name":"example1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to write a generic functions that take multiple\n// generic parameters.\npackage main\n\nimport (\n\t\"fmt\"\n)\n\n// Print is a function that accepts a slice of some type L and a slice of some\n// type V (to be determined later). Each value of the labels slice will be\n// joined with the vals slice at the same index position. This code shows how\n// the generics type list can contain more than just one generic type and have\n// different constraints for each.\n\nfunc Print[L any, V fmt.Stringer](labels []L, vals []V) {\n\tfor i, v := range vals {\n\t\tfmt.Println(labels[i], v.String())\n\t}\n}\n\n// This code defines a concrete type named user that implements the fmt.Stringer\n// interface. The String method just returns the name field from the user type.\n\ntype user struct {\n\tname string\n}\n\nfunc (u user) String() string {\n\treturn u.name\n}\n\n// =============================================================================\n\nfunc main() {\n\tlabels := []int{1, 2, 3}\n\tnames := []user{{\"bill\"}, {\"jill\"}, {\"joan\"}}\n\n\tPrint(labels, names)\n}\n","Hash":"Qh17LMhal5YGOZWJi2CXJnPQDfk="}]}]} ,"generics-slice-constraints":{"Title":"محدودیت‌های برش","Description":"ممکن است زمانهایی وجود داشته باشد که نیاز به محدود کردن نوع ژنریک به برش (slice) باشد.","Pages":[{"Title":"ویدیو","Content":"\n \u003ch2\u003eویدیو\u003c/h2\u003e\n \n \n \u003cp\u003e\n مشاهده می‌کنید که من در مورد ژنریک‌ها چه چیزی ارائه داده‌ام که شما را از طریق تمام مثال‌های این بخش از تور راه می‌دهد.\n \u003c/p\u003e\n \n\u003ciframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/gIEPspmbMHM\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" allowfullscreen\u003e\u003c/iframe\u003e\n \u003ch2\u003eبررسی کد\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e1\u003c/b\u003e: تابع عمل\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\t\n\t\t\n\t\n\n\n \u003ch2\u003eتوضیح داده شده\u003c/h2\u003e\n \n \n \u003cp\u003e\n در اینجا نوع اعداد تعریف شده توسط کاربر، نوع زیرینی دارد که یک برش از اعداد صحیح است.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype Numbers []int\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n کامپایلر به شما اجازه می‌دهد که متغیرهای مبتنی بر یک برش از اعداد صحیح را به متغیرهایی با نوع Numbers تبدیل کنید. این معمولاً خوب است و آنچه که شما می‌خواهید است. به دلیل این قابلیت، می‌توانید یک تابع ژنریک بنویسید که بتواند روی یک برش عمل کند و نوع زیرین را رعایت کند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype operateFunc[T any] func(t T) T\n\nfunc operate[T any](slice []T, fn operateFunc[T]) []T {\n ret := make([]T, len(slice))\n for i, v := range slice {\n ret[i] = fn(v)\n }\n \n return ret\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در اینجا تابع operate یک نوع ژنریک T اعلام می‌کند که می‌تواند هر چیزی باشد. از این نوع برای اعلام یک پارامتر به نام slice استفاده می‌شود که یک برش از همان نوع T را می‌پذیرد. همچنین تابع یک تابع ژنریک از همان نوع T را نیز می‌پذیرد و همچنین یک برش از T را برمی‌گرداند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype Numbers []int\n\nfunc Double(n Numbers) Numbers {\n fn := func(n int) int {\n return 2 * n\n }\n\n numbers := operate(n, fn)\n fmt.Printf(\u0026#34;%T\u0026#34;, numbers)\n return numbers\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e[]int\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n تابع دابل (Double) یک مقدار از نوع اعداد (Numbers) را قبول کرده و آن مقدار را به تابع عمل (operate) منتقل می‌کند. در این مورد، کامپایلر از نوع زیرین (underlying type) در نوع T بهره می‌برد و مقدار اعداد (Numbers) می‌تواند به تابع منتقل شود. با این حال، چیزی که برگشت داده می‌شود، یک برش از نوع عدد صحیح (int) است که در خروجی دیده می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n اگر نیاز دارید که اطمینان حاصل شود تنها مقدار اعداد (Numbers) قابل ارسال است و توسط تابع عمل (operate) برگشت داده می‌شود، می‌توانید تغییرات زیر را انجام دهید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n ```go\n\n\n type Slice[T any] interface {\n\n\n ~ []T\n\n\n }\n\n\n \u003ccode\u003e`\u003c/code\u003e\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n این رابط یک محدودیت اعلام می‌کند تا نوع ژنریک را به یک برش واقعی از نوع T محدود کند. استفاده از عنصر تقریبی (~) محدود به تمام انواعی است که نوع زیرین آنها T است. با این رابط، می‌توانید تابع عمل (operate) را تغییر دهید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype operateFunc[T any] func(t T) T\ntype Slice[T any] interface {\n ~ []T\n}\n\n// func operate[T any](slice []T, fn operateFunc[T]) []T {\n// ret := make([]T, len(slice))\n// for i, v := range slice {\n// ret[i] = fn(v)\n// }\n// return ret\n// }\n\nfunc operate[S Slice[T], T any](slice S, fn operateFunc[T]) S {\n ret := make(S, len(slice))\n for i, v := range slice {\n ret[i] = fn(v)\n }\n \n return ret\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n حالا تابع عمل (operate) را تغییر دهید تا دو نوع ژنریک اعلام کند. نوع S که یک مقدار برش از نوع T را نمایان می‌کند، و T که یک نوع است که می‌تواند هر چیزی باشد. تابع یک مقدار از نوع S برمی‌گرداند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype Numbers []int\n\nfunc Double(n Numbers) Numbers {\n fn := func(n int) int {\n return 2 * n\n }\n\n numbers := operate(n, fn)\n fmt.Printf(\u0026#34;%T\u0026#34;, numbers)\n \n return numbers\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003emain.Numbers\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در این مورد، وقتی که مقدار اعداد (Numbers) را به تابع عمل (operate) منتقل می‌کنید، برشی که برگشت داده می‌شود از نوع اعداد (Numbers) است. نوع زیرین نادیده گرفته می‌شود و نوع تعریف شده توسط کاربر احترام گذاشته می‌شود.\n \u003c/p\u003e\n \n\n\n","Files":[{"Name":"example1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to define slice based constraints.\npackage main\n\nimport (\n\t\"fmt\"\n)\n\n// operateFunc defines a function type that takes a value of some type T and\n// returns a value of the same type T (to be determined later).\n//\n// Slice defines a constraint that the data is a slice of some type T (to be\n// determined later).\n\ntype operateFunc[T any] func(t T) T\n\ntype Slice[T any] interface {\n\t~[]T\n}\n\n// When it's important that the slice being passed in is exactly the same\n// as the slice being returned, use a slice constraint. This ensures that the\n// result slice S is the same as the incoming slice S.\n\nfunc operate[S Slice[T], T any](slice S, fn operateFunc[T]) S {\n\tret := make(S, len(slice))\n\tfor i, v := range slice {\n\t\tret[i] = fn(v)\n\t}\n\treturn ret\n}\n\n// If you don't care about the constraint defined above, then operate2 provides\n// a simpler form. Operate2 still works because you can assign a slice of some\n// type T to the input and output arguments. However, the concrete types of the\n// input and output arguments will be based on the underlying types. In this\n// case not a slice of Numbers, but a slice of integers. This is not the case\n// with operate function above.\n\nfunc operate2[T any](slice []T, fn operateFunc[T]) []T {\n\tret := make([]T, len(slice))\n\tfor i, v := range slice {\n\t\tret[i] = fn(v)\n\t}\n\treturn ret\n}\n\n// I suspect most of the time operate2 is what you want: it's simpler, and more\n// flexible: You can always assign a []int back to a Numbers variable and vice\n// versa. But if you need to preserve that incoming type in the result for some\n// reason, you will need to use operate.\n\n// =============================================================================\n\n// This code defines a named type that is based on a slice of integers. An\n// integer is the underlying type.\n//\n// Double is a function that takes a value of type Numbers, multiplies each\n// value in the underlying integer slice and returns that new Numbers value.\n//\n// Line 73 is commented out because the compiler is smart enough to infer the\n// types for S and T. The commented code shows the types being inferred.\n//\n// operate2 is not used in the example.\n\ntype Numbers []int\n\nfunc DoubleUnderlying(n Numbers) Numbers {\n\tfn := func(n int) int {\n\t\treturn 2 * n\n\t}\n\n\tnumbers := operate2(n, fn)\n\tfmt.Printf(\"%T\", numbers)\n\treturn numbers\n}\n\nfunc DoubleUserDefined(n Numbers) Numbers {\n\tfn := func(n int) int {\n\t\treturn 2 * n\n\t}\n\n\tnumbers := operate(n, fn)\n\tfmt.Printf(\"%T\", numbers)\n\treturn numbers\n}\n\n// =============================================================================\n\nfunc main() {\n\tn := Numbers{10, 20, 30, 40, 50}\n\tfmt.Println(n)\n\n\tn = DoubleUnderlying(n)\n\tfmt.Println(n)\n\n\tn = DoubleUserDefined(n)\n\tfmt.Println(n)\n}\n","Hash":"N5/uaH85IbojxXFef5MfUoRu7XU="}]}]} ,"generics-struct-types":{"Title":"Struct Types","Description":"از انواع ساختاری (Struct Types) می‌توانید از یک نوع ژنریک با استفاده از یک نوع ساختاری اعلام کنید.","Pages":[{"Title":"ویدیو","Content":"\n \u003ch2\u003eویدیو\u003c/h2\u003e\n \n \n \u003cp\u003e\n مشاهده کنید که من در مورد ژنریک‌ها چه چیزی ارائه داده‌ام که شما را از طریق تمام مثال‌های این بخش از تور راه می‌دهد.\n \u003c/p\u003e\n \n\u003ciframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/gIEPspmbMHM\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" allowfullscreen\u003e\u003c/iframe\u003e\n \u003ch2\u003eبررسی کد\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e1\u003c/b\u003e: لیست پیوندی\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\t\n\t\t\n\t\n\n\n \u003ch2\u003eتوضیح داده شده\u003c/h2\u003e\n \n \n \u003cp\u003e\n اگر بخواهید یک نوع ژنریک خود را با استفاده از یک نوع ساختاری اعلام کنید، چه اتفاقی می‌افتد؟\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype node[T any] struct {\n Data T\n next *node[T]\n prev *node[T]\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این نوع ساختاری به عنوان نوعی برای یک گره در لیست پیوندی اعلام شده است. هر گره شامل یک قطعه داده فردی است که توسط لیست ذخیره و مدیریت می‌شود. استفاده از پرانتز مربعی اعلام می‌کند که نوع T یک نوع ژنریک است که باید در زمان کامپایل تعیین شود. استفاده از محدودیت \u0026#34;any\u0026#34; نشان می‌دهد که هیچ محدودیتی بر روی نوع T وجود ندارد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n با تعریف نوع T، می‌توان داده Data را به عنوان یک فیلد از نوع T معرفی کرد که باید در زمان بعدی تعیین شود. فیلدهای next و prev باید به یک گره از همان نوع T اشاره کنند. این اشاره‌گرها به گره بعدی و گره قبلی در لیست پیوندی اشاره دارند. برای ایجاد این ارتباط، فیلدها به عنوان اشاره‌گرها به یک گره اعلام شده‌اند که به نوع T مرتبط است از طریق استفاده از پرانتز مربعی.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype list[T any] struct {\n first *node[T]\n last *node[T]\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n نوع ساختاری دوم به نام list است و یک مجموعه از گره‌ها را با اشاره به اولین و آخرین گره در یک لیست نمایان می‌کند. این فیلدها باید به یک گره از نوع T اشاره کنند، به همان شکلی که فیلدهای next و prev از نوع گره هستند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n بار دیگر، شناسه T به عنوان یک نوع ژنریک (که باید در زمان بعدی تعیین شود) تعریف می‌شود که می‌تواند برای هر نوع محسوسی جایگزین شود. سپس فیلدهای اولین و آخرین به عنوان اشاره‌گرهایی به یک گره از نوع T اعلام می‌شوند با استفاده از دستور زبان مربعی.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc (l *list[T]) add(data T) *node[T] {\n n := node[T]{\n Data: data,\n prev: l.last,\n }\n \n if l.first == nil {\n l.first = \u0026amp;n\n l.last = \u0026amp;n\n return \u0026amp;n\n }\n\n l.last.next = \u0026amp;n\n l.last = \u0026amp;n\n \n return \u0026amp;n\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این یک پیاده‌سازی از یک متد به نام add برای نوع لیست است. اعلام رسمی نوع ژنریک برای لیست (مانند توابع) نیازی ندارد چون متد به لیست از طریق مخزن متصل می‌شود. مخزن متد add به عنوان یک اشاره‌گر به لیستی از نوع T اعلام شده است و بازگشت به عنوان یک اشاره‌گر به یک گره از همان نوع T اعلام شده است.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n کد پس از ایجاد یک گره همیشه یکسان خواهد بود، بدون توجه به نوع داده‌ای که در لیست ذخیره می‌شود چرا که این تنها تغییر مکان‌نمایی اشاره‌گر است. تنها ایجاد یک گره است که توسط نوع داده‌ای که مدیریت می‌شود تحت تأثیر قرار می‌گیرد. با توجه به ژنریک‌ها، ایجاد یک گره می‌تواند به نوع T متصل شود که در زمان کامپایل بعدی جایگزین می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n بدون ژنریک‌ها، کل این متد نیاز به تکرار خواهد داشت چرا که ایجاد یک گره نیاز به کدگذاری سخت به یک نوع معلوم و اعلام شده قبل از کامپایل دارد. از آنجا که میزان کد (برای کل پیاده‌سازی لیست) که برای انواع داده مختلف باید تغییر کند، بسیار کم است، توانایی اعلام یک گره و لیست برای مدیریت داده‌های نوع T هزینه تکرار کد و نگهداری را کاهش می‌دهد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype user struct {\n name string\n}\n\nfunc main() {\n // Store values of type user into the list.\n var lv list[user]\n n1 := lv.add(user{\u0026#34;bill\u0026#34;})\n n2 := lv.add(user{\u0026#34;ale\u0026#34;})\n fmt.Println(n1.Data, n2.Data)\n \n // Store pointers of type user into the list.\n var lp list[*user]\n n3 := lp.add(\u0026amp;user{\u0026#34;bill\u0026#34;})\n n4 := lp.add(\u0026amp;user{\u0026#34;ale\u0026#34;})\n fmt.Println(n3.Data, n4.Data)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e{bill} {ale}\n\u0026amp;{bill} \u0026amp;{ale}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در اینجا یک برنامه کوچک وجود دارد. یک نوع به نام user اعلام شده است و سپس یک لیست به حالت مقدار صفر برای مدیریت مقادیر نوع user ساخته می‌شود. سپس یک لیست دیگر به حالت مقدار صفر ساخته می‌شود و این لیست اشاره‌گرها به مقادیر نوع user را مدیریت می‌کند. تنها تفاوت بین این دو لیست این است که یکی مقادیر نوع user را مدیریت می‌کند و دیگری اشاره‌گرهای نوع user را.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n از آنجا که نوع user به طور صریح در زمان ساخت نوع لیست مشخص شده است، متد add به نوبه‌ی خود مقادیر نوع user را می‌پذیرد. از آنجا که یک اشاره‌گر از نوع user به طور صریح در زمان ساخت نوع لیست مشخص شده است، متد add اشاره‌گرهای نوع user را می‌پذیرد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n شما می‌توانید در خروجی برنامه مشاهده کنید که فیلد Data برای گره‌ها در لیست‌های مربوطه با معنای داده مورد استفاده در ساخت تطابق دارد.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eتمرینات\u003c/h2\u003e\n \n \n \u003cp\u003e\n از قالب به عنوان نقطه شروع برای تکمیل تمرینات استفاده کنید. یک راه حل ممکن نیز ارائه شده است.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eتمرین 1\u003c/h2\u003e\n \n \n \u003cp\u003e\n یک نوع ژنریک به نام stack را اعلام کنید که از یک ساختار با یک فیلد به نام data به عنوان یک آرایه از نوع T استفاده می‌کند. یک متد به نام push را اعلام کنید که یک مقدار از نوع T را پذیرفته و این مقدار را به آرایه اضافه می‌کند. یک متد به نام pop را اعلام کنید که آخرین مقدار از نوع T که به آرایه اضافه شده است و یک خطا را برمی‌گرداند. سپس یک تابع اصلی بنویسید که از این متدها استفاده می‌کند.\n \u003c/p\u003e\n \n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n","Files":[{"Name":"example1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to declare a generic type using a struct type.\npackage main\n\nimport (\n\t\"fmt\"\n)\n\n// This code defines two user defined types that implement a linked list. The\n// node type contains data of some type T (to be determined later) and points\n// to other nodes of the same type T. The list type contains pointers to the\n// first and last nodes of some type T. The add method is declared with a\n// pointer receiver based on a list of some type T and is implemented to add\n// nodes to the list of the same type T.\n\ntype node[T any] struct {\n\tData T\n\tnext *node[T]\n\tprev *node[T]\n}\n\ntype list[T any] struct {\n\tfirst *node[T]\n\tlast *node[T]\n}\n\nfunc (l *list[T]) add(data T) *node[T] {\n\tn := node[T]{\n\t\tData: data,\n\t\tprev: l.last,\n\t}\n\tif l.first == nil {\n\t\tl.first = \u0026n\n\t\tl.last = \u0026n\n\t\treturn \u0026n\n\t}\n\tl.last.next = \u0026n\n\tl.last = \u0026n\n\treturn \u0026n\n}\n\n// This user type represents the data to be stored into the linked list.\n\ntype user struct {\n\tname string\n}\n\n// =============================================================================\n\nfunc main() {\n\n\t// Store values of type user into the list.\n\tvar lv list[user]\n\tn1 := lv.add(user{\"bill\"})\n\tn2 := lv.add(user{\"ale\"})\n\tfmt.Println(n1.Data, n2.Data)\n\n\t// Store pointers of type user into the list.\n\tvar lp list[*user]\n\tn3 := lp.add(\u0026user{\"bill\"})\n\tn4 := lp.add(\u0026user{\"ale\"})\n\tfmt.Println(n3.Data, n4.Data)\n}\n","Hash":"qqvmtPVssHNelLs02Ezd/57oYJo="},{"Name":"exercise1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Implement a generic stack type.\npackage main\n\n// Declare a generic type named stack that uses a struct with a single\n// field named data declared as a slice of some type T.\n\n// Declare a method named push that accepts a value of some type T and appends\n// the value to the slice.\n\n// Declare a method named pop that returns the latest value of some type T\n// that was appended to the slice and an error.\n\n// =============================================================================\n\nfunc main() {\n\n\t// Constructs a value of type stack that stores integers.\n\n\t// Push the values of 10 and 20 to the stack.\n\n\t// Pop a value from the stack.\n\n\t// Print the value that was popped.\n\n\t// Pop another value from the stack.\n\n\t// Print the value that was popped.\n\n\t// Pop another value from the stack. This should\n\t// return an error.\n}\n","Hash":"T63iw0Q7htkWcGDzxnXDfvYNYJI="},{"Name":"answer1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Implement a generic stack type.\npackage main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\n// Declare a generic type named stack that uses a struct with a single\n// field named data declared as a slice of some type T.\ntype stack[T any] struct {\n\tdata []T\n}\n\n// Declare a method named push that accepts a value of some type T and appends\n// the value to the slice.\nfunc (s *stack[T]) push(v T) {\n\ts.data = append(s.data, v)\n}\n\n// Declare a method named pop that returns the latest value of some type T\n// that was appended to the slice and an error.\nfunc (s *stack[T]) pop() (T, error) {\n\tvar zero T\n\n\tif len(s.data) == 0 {\n\t\treturn zero, errors.New(\"stack is empty\")\n\t}\n\n\tv := s.data[len(s.data)-1]\n\n\ts.data = s.data[:len(s.data)-1]\n\n\treturn v, nil\n}\n\n// =============================================================================\n\nfunc main() {\n\n\t// Constructs a value of type stack that stores integers.\n\tvar s stack[int]\n\n\t// Push the values of 10 and 20 to the stack.\n\ts.push(10)\n\ts.push(20)\n\n\t// Pop a value from the stack.\n\tv, err := s.pop()\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\treturn\n\t}\n\n\t// Print the value that was popped.\n\tfmt.Println(v)\n\n\t// Pop another value from the stack.\n\tv, err = s.pop()\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\treturn\n\t}\n\n\t// Print the value that was popped.\n\tfmt.Println(v)\n\n\t// Pop another value from the stack. This should\n\t// return an error.\n\tv, err = s.pop()\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\treturn\n\t}\n}\n","Hash":"8fTVAz0nMpHCiL1Pc7p+PdHsOtA="}]}]} ,"algorithms-data":{"Title":"ساختارهای داده","Description":"این بخش نمونه‌های ساختارهای داده را ارائه می‌دهد.","Pages":[{"Title":"Hash Map","Content":"\n \u003ch2\u003eHash Map\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/training/individual-on-demand/ultimate-go-bundle/\" target=\"_blank\"\u003eتماشای ویدئو\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003eنیاز به کمک مالی دارید؟ از \u003ca href=\"https://www.ardanlabs.com/scholarship/\" target=\"_blank\"\u003eفرم Scholarship\u003c/a\u003e ما استفاده کنید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n این برنامه نمونه‌ای از جدول هش پایه را پیاده‌سازی می‌کند.\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003eتابع hashKey(key) یک عدد بین 0 تا len(buckets)-1 برمی‌گرداند.\u003c/li\u003e\n \n \u003cli\u003eما از یک برش از ورودی‌ها به عنوان یک سبد استفاده می‌کنیم تا مواردی که دو یا چند کلید به همان سبد هش می‌شوند، را اداره کنیم.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \u003cul\u003e\n \n \u003cli\u003eبرای اطلاعات بیشتر به این لینک مراجعه کنید: \u003ca href=\"https://en.wikipedia.org/wiki/Hash_table\" target=\"_blank\"\u003ehttps://en.wikipedia.org/wiki/Hash_table\u003c/a\u003e\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n \u003cb\u003eدیاگرام\u003c/b\u003e\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eبا یک hash map، داده‌ها بر اساس سبد و سپس موقعیت داخل سبد فهرست می‌شوند.\n\nhashKey(key) ──────────────┐\n │\n ▽\n ┌────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐\n │ │ │ │ │ │ │ │ │ ◁── bucket\n └────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘\n │ │\n ▽ ▽\n ┌─────────────┐ ┌─────────────┐\n │ key │ value │ │ key │ value │ ◁── entry\n ├─────────────┤ ├─────────────┤\n │ key │ value │ │ key │ value │\n ├─────────────┤ └─────────────┘\n │ key │ value │\n ├─────────────┤\n │ key │ value │\n ├─────────────┤\n │ key │ value │\n └─────────────┘\u003c/pre\u003e\n \n\n\t\n\t\t\n\t\n\n","Files":[{"Name":"hash_map.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This sample program shows you how to write a basic hash table.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"hash/maphash\"\n)\n\nfunc main() {\n\th := New()\n\n\tk1, v1 := \"key1\", 1\n\tk2, v2 := \"key2\", 2\n\th.Store(k1, v1)\n\th.Store(k2, v2)\n\n\tv, err := h.Retrieve(k1)\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\treturn\n\t}\n\n\tfmt.Println(\"key:\", k1, \"value:\", v)\n\n\tv1b := 11\n\th.Store(k1, v1b)\n\n\tv, err = h.Retrieve(k1)\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\treturn\n\t}\n\n\tfmt.Println(\"key:\", k1, \"value:\", v)\n\n\tif err := h.Delete(k1); err != nil {\n\t\tfmt.Println(err)\n\t\treturn\n\t}\n\n\tv, err = h.Retrieve(k1)\n\tif err != nil {\n\t\tfmt.Println(err)\n\t}\n\n\tfn := func(key string, value int) bool {\n\t\tfmt.Println(\"key:\", key, \"value:\", value)\n\t\treturn true\n\t}\n\th.Do(fn)\n}\n\n// =============================================================================\n\nconst numBuckets = 256\n\n// An entry where we store key and value in the hash.\ntype entry struct {\n\tkey string\n\tvalue int\n}\n\n// Hash is a simple Hash table implementation.\ntype Hash struct {\n\tbuckets [][]entry\n\thash maphash.Hash\n}\n\n// New returns a new hash table.\nfunc New() *Hash {\n\treturn \u0026Hash{\n\t\tbuckets: make([][]entry, numBuckets),\n\t}\n}\n\n// Store adds a value in the hash table based on the key.\nfunc (h *Hash) Store(key string, value int) {\n\n\t// For the specified key, identify what bucket in\n\t// the slice we need to store the key/value inside of.\n\tidx := h.hashKey(key)\n\n\t// Extract a copy of the bucket from the hash table.\n\tbucket := h.buckets[idx]\n\n\t// Iterate over the indexes for the specified bucket.\n\tfor idx := range bucket {\n\n\t\t// Compare the keys and if there is a match replace the\n\t\t// existing entry value for the new value.\n\t\tif bucket[idx].key == key {\n\t\t\tbucket[idx].value = value\n\t\t\treturn\n\t\t}\n\t}\n\n\t// This key does not exist, so add this new value.\n\th.buckets[idx] = append(bucket, entry{key, value})\n}\n\n// Retrieve extracts a value from the hash table based on the key.\nfunc (h *Hash) Retrieve(key string) (int, error) {\n\n\t// For the specified key, identify what bucket in\n\t// the slice we need to store the key/value inside of.\n\tidx := h.hashKey(key)\n\n\t// Iterate over the entries for the specified bucket.\n\tfor _, entry := range h.buckets[idx] {\n\n\t\t// Compare the keys and if there is a match return\n\t\t// the value associated with the key.\n\t\tif entry.key == key {\n\t\t\treturn entry.value, nil\n\t\t}\n\t}\n\n\t// The key was not found so return the error.\n\treturn 0, fmt.Errorf(\"%q not found\", key)\n}\n\n// Delete deletes an entry from the hash table.\nfunc (h *Hash) Delete(key string) error {\n\n\t// For the specified key, identify what bucket in\n\t// the slice we need to store the key/value inside of.\n\tbucketIdx := h.hashKey(key)\n\n\t// Extract a copy of the bucket from the hash table.\n\tbucket := h.buckets[bucketIdx]\n\n\t// Iterate over the entries for the specified bucket.\n\tfor entryIdx, entry := range bucket {\n\n\t\t// Compare the keys and if there is a match remove\n\t\t// the entry from the bucket.\n\t\tif entry.key == key {\n\n\t\t\t// Remove the entry based on its index position.\n\t\t\tbucket = removeEntry(bucket, entryIdx)\n\n\t\t\t// Replace the existing bucket for the new one.\n\t\t\th.buckets[bucketIdx] = bucket\n\t\t\treturn nil\n\t\t}\n\t}\n\n\t// The key was not found so return the error.\n\treturn fmt.Errorf(\"%q not found\", key)\n}\n\n// Len return the number of elements in the hash. This function currently\n// uses a linear traversal but could be improved with meta-data.\nfunc (h *Hash) Len() int {\n\tsum := 0\n\tfor _, bucket := range h.buckets {\n\t\tsum += len(bucket)\n\t}\n\treturn sum\n}\n\n// Do calls fn on each key/value. If fn return false stops the iteration.\nfunc (h *Hash) Do(fn func(key string, value int) bool) {\n\tfor _, bucket := range h.buckets {\n\t\tfor _, entry := range bucket {\n\t\t\tif ok := fn(entry.key, entry.value); !ok {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n}\n\n// hashKey calculates the bucket index position to use\n// for the specified key.\nfunc (h *Hash) hashKey(key string) int {\n\n\t// Reset the maphash to initial state so we'll get the same\n\t// hash value for the same key.\n\th.hash.Reset()\n\n\t// Write the key to the maphash to update the current state.\n\t// We don't check error value since WriteString never fails.\n\th.hash.WriteString(key)\n\n\t// Ask the maphash for its current state which we will\n\t// use to calculate the final bucket index.\n\tn := h.hash.Sum64()\n\n\t// Use the modulus operator to return a value in the range\n\t// of our bucket length defined by the const numBuckets.\n\treturn int(n % numBuckets)\n}\n\n// removeEntry performs the physical act of removing an\n// entry from a bucket,\nfunc removeEntry(bucket []entry, idx int) []entry {\n\n\t// https://github.com/golang/go/wiki/SliceTricks\n\t// Cut out the entry by taking all entries from\n\t// infront of the index and moving them behind the\n\t// index specified.\n\tcopy(bucket[idx:], bucket[idx+1:])\n\n\t// Set the proper length for the new slice since\n\t// an entry was removed. The length needs to be\n\t// reduced by 1.\n\tbucket = bucket[:len(bucket)-1]\n\n\t// Look to see if the current allocation for the\n\t// bucket can be reduced due to the amount of\n\t// entries removed from this bucket.\n\treturn reduceAllocation(bucket)\n}\n\n// reduceAllocation looks to see if memory can be freed to\n// when a bucket has lost a percent of entries.\nfunc reduceAllocation(bucket []entry) []entry {\n\n\t// If the bucket if more than ½ full, do nothing.\n\tif cap(bucket) \u003c 2*len(bucket) {\n\t\treturn bucket\n\t}\n\n\t// Free memory when the bucket shrinks a lot. If we don't do that,\n\t// the underlying bucket array will stay in memory and will be in\n\t// the biggest size the bucket ever was\n\tnewBucket := make([]entry, len(bucket))\n\tcopy(newBucket, bucket)\n\treturn newBucket\n}\n","Hash":"t0was3T0R0/iciXY8DmChdEtlRo="}]},{"Title":"لیست مرتبط","Content":"\n \u003ch2\u003eلیست مرتبط\u003c/h2\u003e\n \n \n \u003cp\u003e\n این برنامه نمونه‌ای از یک لیست مرتبط دوطرفه اساسی پیاده‌سازی می‌کند.\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003eبرای اطلاعات بیشتر به این لینک مراجعه کنید: \u003ca href=\"https://en.wikipedia.org/wiki/Linked_list\" target=\"_blank\"\u003ehttps://en.wikipedia.org/wiki/Linked_list\u003c/a\u003e\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n \u003cb\u003eدیاگرام\u003c/b\u003e\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eدر یک لیست مرتبط، مقادیر با استفاده از\nاشاره‌گرها به یکدیگر مرتب شده و به ترتیب‌های مختلفی به یکدیگر متصل می‌شوند.\n\n┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐\n│ Val │ ◁─▷ │ Val │ ◁─▷ │ Val │ ◁─▷ │ Val │ ◁─▷ │ Val │\n└─────┘ └─────┘ └─────┘ └─────┘ └─────┘\n △ △\n │ │\n ──────────────────── ─────────────────────\n │ │\n │ │\n ┌───────────────┐\n │ First │ Last │\n └───────────────┘\u003c/pre\u003e\n \n\n\t\n\t\t\n\t\n\n","Files":[{"Name":"list.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This sample program shows you how to write a basic double linked list.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nfunc main() {\n\tvar l List\n\n\tconst nodes = 5\n\tfor i := 0; i \u003c nodes; i++ {\n\t\tdata := fmt.Sprintf(\"Node%d\", i)\n\t\tl.Add(data)\n\t}\n\n\tf := func(n *Node) error {\n\t\tfmt.Println(\"Data:\", n.Data)\n\t\treturn nil\n\t}\n\n\tl.Operate(f)\n\n\tfmt.Println(\"------------------\")\n\n\tl.OperateReverse(f)\n}\n\n// =============================================================================\n\n// Node represents the data being stored.\ntype Node struct {\n\tData string\n\tnext *Node\n\tprev *Node\n}\n\n// List represents a list of nodes.\ntype List struct {\n\tCount int\n\tfirst *Node\n\tlast *Node\n}\n\n// Add places a new node at the end of the list.\nfunc (l *List) Add(data string) *Node {\n\n\t// When creating the new node, have the new node\n\t// point to the last node in the list.\n\tn := Node{\n\t\tData: data,\n\t\tprev: l.last,\n\t}\n\n\t// Increment the count for the new node.\n\tl.Count++\n\n\t// If this is the first node, attach it.\n\tif l.first == nil \u0026\u0026 l.last == nil {\n\t\tl.first = \u0026n\n\t\tl.last = \u0026n\n\t\treturn \u0026n\n\t}\n\n\t// Fix the fact that the last node does not point back to\n\t// the NEW node.\n\tl.last.next = \u0026n\n\n\t// Fix the fact the Last pointer is not pointing to the true\n\t// end of the list.\n\tl.last = \u0026n\n\n\treturn \u0026n\n}\n\n// AddFront places a new node at the front of the list.\nfunc (l *List) AddFront(data string) *Node {\n\n\t// When creating the new node, have the new node\n\t// point to the first node in the list.\n\tn := Node{\n\t\tData: data,\n\t\tnext: l.first,\n\t}\n\n\t// Increment the count for the new node.\n\tl.Count++\n\n\t// If this is the first node, attach it.\n\tif l.first == nil \u0026\u0026 l.last == nil {\n\t\tl.first = \u0026n\n\t\tl.last = \u0026n\n\t\treturn \u0026n\n\t}\n\n\t// Fix the fact that the first node does not point back to\n\t// the NEW node.\n\tl.first.prev = \u0026n\n\n\t// Fix the fact the First pointer is not pointing to the true\n\t// beginning of the list.\n\tl.first = \u0026n\n\n\treturn \u0026n\n}\n\n// Find traverses the list looking for the specified data.\nfunc (l *List) Find(data string) (*Node, error) {\n\tn := l.first\n\tfor n != nil {\n\t\tif n.Data == data {\n\t\t\treturn n, nil\n\t\t}\n\t\tn = n.next\n\t}\n\treturn nil, fmt.Errorf(\"unable to locate %q in list\", data)\n}\n\n// FindReverse traverses the list in the opposite direction\n// looking for the specified data.\nfunc (l *List) FindReverse(data string) (*Node, error) {\n\tn := l.last\n\tfor n != nil {\n\t\tif n.Data == data {\n\t\t\treturn n, nil\n\t\t}\n\t\tn = n.prev\n\t}\n\treturn nil, fmt.Errorf(\"unable to locate %q in list\", data)\n}\n\n// Remove traverses the list looking for the specified data\n// and if found, removes the node from the list.\nfunc (l *List) Remove(data string) (*Node, error) {\n\tn, err := l.Find(data)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Detach the node by linking the previous node's next\n\t// pointer to the node in front of the one being removed.\n\tn.prev.next = n.next\n\tn.next.prev = n.prev\n\tl.Count--\n\n\treturn n, nil\n}\n\n// Operate accepts a function that takes a node and calls\n// the specified function for every node found.\nfunc (l *List) Operate(f func(n *Node) error) error {\n\tn := l.first\n\tfor n != nil {\n\t\tif err := f(n); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tn = n.next\n\t}\n\treturn nil\n}\n\n// OperateReverse accepts a function that takes a node and\n// calls the specified function for every node found.\nfunc (l *List) OperateReverse(f func(n *Node) error) error {\n\tn := l.last\n\tfor n != nil {\n\t\tif err := f(n); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tn = n.prev\n\t}\n\treturn nil\n}\n\n// AddSort adds a node based on lexical ordering.\nfunc (l *List) AddSort(data string) *Node {\n\n\t// If the list was empty add the data\n\t// as the first node.\n\tif l.first == nil {\n\t\treturn l.Add(data)\n\t}\n\n\t// Traverse the list looking for placement.\n\tn := l.first\n\tfor n != nil {\n\n\t\t// If this data is greater than the current node,\n\t\t// keep traversing until it is less than or equal.\n\t\tif strings.Compare(data, n.Data) \u003e 0 {\n\t\t\tn = n.next\n\t\t\tcontinue\n\t\t}\n\n\t\t// Create the new node and place it before the\n\t\t// current node.\n\t\tnew := Node{\n\t\t\tData: data,\n\t\t\tnext: n,\n\t\t\tprev: n.prev,\n\t\t}\n\n\t\tl.Count++\n\n\t\t// If this node is now to be the first,\n\t\t// fix the first pointer.\n\t\tif l.first == n {\n\t\t\tl.first = \u0026new\n\t\t}\n\n\t\t// If the current node points to a previous node,\n\t\t// then that previous nodes next must point to the\n\t\t// new node.\n\t\tif n.prev != nil {\n\t\t\tn.prev.next = \u0026new\n\t\t}\n\n\t\t// The current previous points must point back\n\t\t// to this new node.\n\t\tn.prev = \u0026new\n\n\t\treturn n\n\t}\n\n\t// This must be the largest string, so add to the end.\n\treturn l.Add(data)\n}\n","Hash":"bQ9fyslwgD9Gc3fAHR1MFfqYIu8="}]},{"Title":"صف","Content":"\n \u003ch2\u003eصف\u003c/h2\u003e\n \n \n \u003cp\u003e\n این برنامه نمونه‌ای از یک صف دایره‌ای اساسی پیاده‌سازی می‌کند.\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003eبرای اطلاعات بیشتر به این لینک مراجعه کنید: \u003ca href=\"https://en.wikipedia.org/wiki/Queue_(abstract_data_type)\" target=\"_blank\"\u003ehttps://en.wikipedia.org/wiki/Queue_(abstract_data_type)\u003c/a\u003e\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n \u003cb\u003eدیاگرام\u003c/b\u003e\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eدر یک صف، اولین مقداری که وارد می‌شود، اولین مقداری است که خارج می‌شود.\n\n ┌──────────────────────────────────────────┐\n┌─────┐ │ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │ ┌─────┐\n│ V06 │ ─▷ │ │ V05 │ ─▷ │ V04 │ ─▷ │ V03 │ ─▷ │ V02 │ │ ─▷ │ V01 │\n└─────┘ | └─────┘ └─────┘ └─────┘ └─────┘ | └─────┘\n └──────────────────────────────────────────┘\u003c/pre\u003e\n \n\n\t\n\t\t\n\t\n\n","Files":[{"Name":"queue_circular.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This sample program shows you how to write a basic circular queue.\npackage main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\nfunc main() {\n\tconst items = 5\n\n\tq, err := New(items)\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\treturn\n\t}\n\n\tfor i := 0; i \u003c items; i++ {\n\t\tname := fmt.Sprintf(\"Name%d\", i)\n\t\tif err := q.Enqueue(Data{Name: name}); err != nil {\n\t\t\tfmt.Println(err)\n\t\t\treturn\n\t\t}\n\n\t\tfmt.Println(\"queue:\", name)\n\t}\n\n\tfmt.Println(\"------------------\")\n\n\tf := func(d Data) error {\n\t\tfmt.Println(\"enqueue:\", d.Name)\n\t\treturn nil\n\t}\n\n\tq.Operate(f)\n}\n\n// Data represents what is being stored on the queue.\ntype Data struct {\n\tName string\n}\n\n// Queue represents a list of data.\ntype Queue struct {\n\tCount int\n\tdata []Data\n\tfront int\n\tend int\n}\n\n// New returns a queue with a set capacity.\nfunc New(cap int) (*Queue, error) {\n\tif cap \u003c= 0 {\n\t\treturn nil, errors.New(\"invalid capacity\")\n\t}\n\n\tq := Queue{\n\t\tfront: 0,\n\t\tend: 0,\n\t\tdata: make([]Data, cap),\n\t}\n\treturn \u0026q, nil\n}\n\n// Enqueue inserts data into the queue if there\n// is available capacity.\nfunc (q *Queue) Enqueue(data Data) error {\n\n\t// If the front of the queue is right behind the end or\n\t// if the front is at the end of the capacity and the end\n\t// is at the beginning of the capacity, the queue is full.\n\t// F E - Enqueue (Full) | E F - Enqueue (Full)\n\t// [A][B][C] | [A][B][C]\n\tif q.front+1 == q.end ||\n\t\tq.front == len(q.data) \u0026\u0026 q.end == 0 {\n\t\treturn errors.New(\"queue at capacity\")\n\t}\n\n\tswitch {\n\tcase q.front == len(q.data):\n\n\t\t// If we are at the end of the capacity, then\n\t\t// circle back to the beginning of the capacity by\n\t\t// moving the front pointer to the beginning.\n\t\tq.front = 0\n\t\tq.data[q.front] = data\n\tdefault:\n\n\t\t// Add the data to the current front position\n\t\t// and then move the front pointer.\n\t\tq.data[q.front] = data\n\t\tq.front++\n\t}\n\n\tq.Count++\n\n\treturn nil\n}\n\n// Dequeue removes data into the queue if data exists.\nfunc (q *Queue) Dequeue() (Data, error) {\n\n\t// If the front and end are the same, the\n\t// queue is empty\n\t// EF - (Empty)\n\t// [ ][ ][ ]\n\tif q.front == q.end {\n\t\treturn Data{}, errors.New(\"queue is empty\")\n\t}\n\n\tvar data Data\n\tswitch {\n\tcase q.end == len(q.data):\n\n\t\t// If we are at the end of the capacity, then\n\t\t// circle back to the beginning of the capacity by\n\t\t// moving the end pointer to the beginning.\n\t\tq.end = 0\n\t\tdata = q.data[q.end]\n\tdefault:\n\n\t\t// Remove the data from the current end position\n\t\t// and then move the end pointer.\n\t\tdata = q.data[q.end]\n\t\tq.end++\n\t}\n\n\tq.Count--\n\n\treturn data, nil\n}\n\n// Operate accepts a function that takes data and calls\n// the specified function for every piece of data found.\nfunc (q *Queue) Operate(f func(d Data) error) error {\n\tend := q.end\n\tfor {\n\t\tif end == q.front {\n\t\t\tbreak\n\t\t}\n\n\t\tif end == len(q.data) {\n\t\t\tend = 0\n\t\t}\n\n\t\tif err := f(q.data[end]); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tend++\n\t}\n\treturn nil\n}\n","Hash":"sDoaePCu/Qu1kIe5SImJc9mvXuI="}]},{"Title":"پشته","Content":"\n \u003ch2\u003eپشته\u003c/h2\u003e\n \n \n \u003cp\u003e\n این برنامه نمونه‌ای از یک پشته اساسی پیاده‌سازی می‌کند.\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003eبرای اطلاعات بیشتر به این لینک مراجعه کنید: \u003ca href=\"https://en.wikipedia.org/wiki/Stack_(abstract_data_type)\" target=\"_blank\"\u003ehttps://en.wikipedia.org/wiki/Stack_(abstract_data_type)\u003c/a\u003e\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n \u003cb\u003eدیاگرام\u003c/b\u003e\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eدر یک پشته، اولین مقداری که وارد می‌شود، آخرین مقداری است که خارج می‌شود.\n\n ┌─────┐\n │ V05 │\n └─────┘\n │\n ▽ ┌─────┐\n ┌───────────┐ ─▷ │ V04 │\n │ ┌─────┐ │ └─────┘\n │ │ V03 │ │\n │ └─────┘ │\n │ ┌─────┐ │\n │ │ V02 │ │\n │ └─────┘ │\n │ ┌─────┐ │\n │ │ V01 │ │\n │ └─────┘ │\n └───────────┘\u003c/pre\u003e\n \n\n\t\n\t\t\n\t\n\n","Files":[{"Name":"stack.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This sample program shows you how to write a basic stack.\npackage main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\nfunc main() {\n\tconst items = 5\n\n\tvar s Stack\n\n\tfor i := 0; i \u003c items; i++ {\n\t\tname := fmt.Sprintf(\"Name%d\", i)\n\t\ts.Push(Data{Name: name})\n\n\t\tfmt.Println(\"push:\", name)\n\t}\n\n\tfmt.Println(\"------------------\")\n\n\tf := func(d Data) error {\n\t\tfmt.Println(\"pop:\", d.Name)\n\t\treturn nil\n\t}\n\n\ts.Operate(f)\n}\n\n// Data represents what is being stored on the stack.\ntype Data struct {\n\tName string\n}\n\n// Stack represents a stack of data.\ntype Stack struct {\n\tdata []Data\n}\n\n// Make allows the creation of a stack with an initial\n// capacity for efficiency. Otherwise a stack can be\n// used in its zero value state.\nfunc Make(cap int) *Stack {\n\treturn \u0026Stack{\n\t\tdata: make([]Data, 0, cap),\n\t}\n}\n\n// Count returns the number of items in the stack.\nfunc (s *Stack) Count() int {\n\treturn len(s.data)\n}\n\n// Push adds data into the top of the stack.\nfunc (s *Stack) Push(data Data) {\n\ts.data = append(s.data, data)\n}\n\n// Pop removes data from the top of the stack.\nfunc (s *Stack) Pop() (Data, error) {\n\tif len(s.data) == 0 {\n\t\treturn Data{}, errors.New(\"stack empty\")\n\t}\n\n\t// Calculate the top level index.\n\tidx := len(s.data) - 1\n\n\t// Copy the data from that index position.\n\tdata := s.data[idx]\n\n\t// Remove the top level index from the slice.\n\ts.data = s.data[:idx]\n\n\treturn data, nil\n}\n\n// Peek provides the data stored on the stack based\n// on the level from the bottom. A value of 0 would\n// return the top piece of data.\nfunc (s *Stack) Peek(level int) (Data, error) {\n\tif level \u003c 0 || level \u003e (len(s.data)-1) {\n\t\treturn Data{}, errors.New(\"invalid level position\")\n\t}\n\tidx := (len(s.data) - 1) - level\n\treturn s.data[idx], nil\n}\n\n// Operate accepts a function that takes data and calls\n// the specified function for every piece of data found.\n// It traverses from the top down through the stack.\nfunc (s *Stack) Operate(f func(data Data) error) error {\n\tfor i := len(s.data) - 1; i \u003e -1; i-- {\n\t\tif err := f(s.data[i]); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n","Hash":"yRFX4XWmfL/+gT7mEEJd2qnyUy0="}]},{"Title":"درخت دودویی","Content":"\n \u003ch2\u003eدرخت دودویی\u003c/h2\u003e\n \n \n \u003cp\u003e\n این برنامه نمونه‌ای از یک درخت دودویی اساسی پیاده‌سازی می‌کند.\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003eبرای اطلاعات بیشتر به این لینک مراجعه کنید: \u003ca href=\"https://en.wikipedia.org/wiki/Binary_tree\" target=\"_blank\"\u003ehttps://en.wikipedia.org/wiki/Binary_tree\u003c/a\u003e\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n \u003cb\u003eدیاگرام\u003c/b\u003e\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eدر یک درخت دودویی، داده‌ها به یکی از دو\nطرف چپ یا راست درخت اختصاص می‌یابد. با اضافه کردن هر گره، درخت تعادل می‌یابد.\n\n 0 1 2 3 4 5 6 ◁─ ترتیب اضافه کردن\n┌────┐┌────┐┌────┐┌────┐┌────┐┌────┐┌────┐\n│ 65 ││ 45 ││ 35 ││ 75 ││ 85 ││ 78 ││ 95 │\n└────┘└────┘└────┘└────┘└────┘└────┘└────┘\n\n ┌────┐\n │ 75 │ ◁─ درخت نهایی\n └────┘\n / \\\n ┌────┐ ┌────┐\n │ 45 │ │ 85 │\n └────┘ └────┘\n / \\ / \\\n ┌────┐ ┌────┐ ┌────┐ ┌────┐\n │ 35 │ │ 65 │ │ 78 │ │ 95 │\n └────┘ └────┘ └────┘ └────┘\u003c/pre\u003e\n \n\n\t\n\t\t\n\t\n\n","Files":[{"Name":"tree_binary.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This sample program shows you how to write a basic binary tree.\npackage main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n)\n\nfunc main() {\n\tvalues := []Data{\n\t\t{Key: 65, Name: \"Bill\"},\n\t\t{Key: 45, Name: \"Ale\"},\n\t\t{Key: 35, Name: \"Joan\"},\n\t\t{Key: 75, Name: \"Hanna\"},\n\t\t{Key: 85, Name: \"John\"},\n\t\t{Key: 78, Name: \"Steph\"},\n\t\t{Key: 95, Name: \"Sally\"},\n\t}\n\n\tvar tree Tree\n\tfor _, value := range values {\n\t\ttree.Insert(value)\n\t}\n\n\tPrettyPrint(tree)\n\tpre := tree.PreOrder()\n\tfmt.Println(\"Pre-order :\", pre)\n\tin := tree.InOrder()\n\tfmt.Println(\"In-order :\", in)\n\tpost := tree.PostOrder()\n\tfmt.Println(\"Post-order:\", post)\n\n\tfmt.Print(\"\\n\")\n\td35, err := tree.Find(35)\n\tif err != nil {\n\t\tfmt.Println(\"ERROR: Unable to find 35\")\n\t\treturn\n\t}\n\tfmt.Println(\"found:\", d35)\n\n\td78, err := tree.Find(78)\n\tif err != nil {\n\t\tfmt.Println(\"ERROR: Unable to find 78\")\n\t\treturn\n\t}\n\tfmt.Println(\"found:\", d78)\n\n\td3, err := tree.Find(3)\n\tif err == nil {\n\t\tfmt.Println(\"ERROR: found 3\", d3)\n\t\treturn\n\t}\n\tfmt.Println(\"not-found: 3\")\n\n\tfmt.Print(\"\\n\")\n\ttree.Delete(75)\n\tPrettyPrint(tree)\n\n\ttree.Delete(85)\n\tPrettyPrint(tree)\n}\n\n// =============================================================================\n\n// Data represents the information being stored.\ntype Data struct {\n\tKey int\n\tName string\n}\n\n// Tree represents all values in the tree.\ntype Tree struct {\n\troot *node\n}\n\n// Insert adds a value into the tree and keeps the tree balanced.\nfunc (t *Tree) Insert(data Data) {\n\tt.root = t.root.insert(t, data)\n\n\tif t.root.balRatio() \u003c -1 || t.root.balRatio() \u003e 1 {\n\t\tt.root = t.root.rebalance()\n\t}\n}\n\n// Find traverses the tree looking for the specified tree.\nfunc (t *Tree) Find(key int) (Data, error) {\n\tif t.root == nil {\n\t\treturn Data{}, errors.New(\"cannot find from an empty tree\")\n\t}\n\n\treturn t.root.find(key)\n}\n\n// Delete removes the key from the tree and keeps it balanced.\nfunc (t *Tree) Delete(key int) error {\n\tif t.root == nil {\n\t\treturn errors.New(\"cannot delete from an empty tree\")\n\t}\n\n\tfakeParent := \u0026node{right: t.root}\n\tif err := t.root.delete(key, fakeParent); err != nil {\n\t\treturn err\n\t}\n\n\tif fakeParent.right == nil {\n\t\tt.root = nil\n\t}\n\treturn nil\n}\n\n// PreOrder traversal get the root node then traversing its child\n// nodes recursively.\n// Use cases: copying tree, mapping prefix notation.\n//\n//\t #1\n//\t / \\\n//\t #2 #5\n//\t / \\ / \\\n//\t#3 #4 #6 #7\nfunc (t *Tree) PreOrder() []Data {\n\torder := []Data{}\n\tf := func(n *node) {\n\t\torder = append(order, n.data)\n\t}\n\tt.root.preOrder(f)\n\treturn order\n}\n\n// InOrder traversal travel from the leftmost node to the rightmost nodes\n// regardless of depth.\n// In-order traversal gives node values in ascending order.\n//\n//\t #4\n//\t / \\\n//\t #2 #6\n//\t / \\ / \\\n//\t#1 #3 #5 #7\nfunc (t *Tree) InOrder() []Data {\n\torder := []Data{}\n\tf := func(n *node) {\n\t\torder = append(order, n.data)\n\t}\n\tt.root.inOrder(f)\n\treturn order\n}\n\n// PostOrder traversal get the leftmost node then its sibling then go up to its\n// parent, recursively.\n// Use cases: tree deletion, mapping postfix notation.\n//\n//\t #7\n//\t / \\\n//\t #3 #6\n//\t / \\ / \\\n//\t#1 #2 #4 #5\nfunc (t *Tree) PostOrder() []Data {\n\torder := []Data{}\n\tf := func(n *node) {\n\t\torder = append(order, n.data)\n\t}\n\tt.root.postOrder(f)\n\treturn order\n}\n\n// =============================================================================\n\n// node represents the data stored in the tree.\ntype node struct {\n\tdata Data\n\tlevel int\n\ttree *Tree\n\tleft *node\n\tright *node\n}\n\n// height returned the level of the tree the node exists in.\n// Level 1 is at the last layer of the tree.\n//\n//\t #7 -- height = 3\n//\t / \\\n//\t #3 #6 -- height = 2\n//\t / \\ / \\\n//\t#1 #2 #4 #5 -- height = 1\nfunc (n *node) height() int {\n\tif n == nil {\n\t\treturn 0\n\t}\n\treturn n.level\n}\n\n// insert adds the node into the tree and makes sure the\n// tree stays balanced.\nfunc (n *node) insert(t *Tree, data Data) *node {\n\tif n == nil {\n\t\treturn \u0026node{data: data, level: 1, tree: t}\n\t}\n\n\tswitch {\n\tcase data.Key \u003c n.data.Key:\n\t\tn.left = n.left.insert(t, data)\n\n\tcase data.Key \u003e n.data.Key:\n\t\tn.right = n.right.insert(t, data)\n\n\tdefault:\n\t\treturn n.rebalance()\n\t}\n\n\tn.level = max(n.left.height(), n.right.height()) + 1\n\treturn n.rebalance()\n}\n\n// find traverses the tree looking for the specified key.\nfunc (n *node) find(key int) (Data, error) {\n\tif n == nil {\n\t\treturn Data{}, errors.New(\"key not found\")\n\t}\n\n\tswitch {\n\tcase n.data.Key == key:\n\t\treturn n.data, nil\n\n\tcase key \u003c n.data.Key:\n\t\treturn n.left.find(key)\n\n\tdefault:\n\t\treturn n.right.find(key)\n\t}\n}\n\n// balRatio provides information about the balance ratio\n// of the node.\nfunc (n *node) balRatio() int {\n\treturn n.right.height() - n.left.height()\n}\n\n// rotateLeft turns the node to the left.\n//\n//\t#3 #4\n//\t \\ / \\\n//\t #4 #3 #5\n//\t \\\n//\t #5\nfunc (n *node) rotateLeft() *node {\n\tr := n.right\n\tn.right = r.left\n\tr.left = n\n\tn.level = max(n.left.height(), n.right.height()) + 1\n\tr.level = max(r.left.height(), r.right.height()) + 1\n\treturn r\n}\n\n// rotateRight turns the node to the right.\n//\n//\t #5 #4\n//\t / / \\\n//\t #4 #3 #5\n//\t /\n//\t#3\nfunc (n *node) rotateRight() *node {\n\tl := n.left\n\tn.left = l.right\n\tl.right = n\n\tn.level = max(n.left.height(), n.right.height()) + 1\n\tl.level = max(l.left.height(), l.right.height()) + 1\n\treturn l\n}\n\n// rotateLeftRight turns the node to the left and then right.\n//\n//\t #5 #5 #4\n//\t / / / \\\n//\t#3 #4 #3 #5\n//\t \\ /\n//\t #4 #3\nfunc (n *node) rotateLeftRight() *node {\n\tn.left = n.left.rotateLeft()\n\tn = n.rotateRight()\n\tn.level = max(n.left.height(), n.right.height()) + 1\n\treturn n\n}\n\n// rotateLeftRight turns the node to the left and then right.\n//\n//\t#3 #3 #4\n//\t \\ \\ / \\\n//\t #5 #4 #3 #5\n//\t / \\\n//\t#4 #5\nfunc (n *node) rotateRightLeft() *node {\n\tn.right = n.right.rotateRight()\n\tn = n.rotateLeft()\n\tn.level = max(n.left.height(), n.right.height()) + 1\n\treturn n\n}\n\n// rebalance will rotate the nodes based on the ratio.\nfunc (n *node) rebalance() *node {\n\tswitch {\n\tcase n.balRatio() \u003c -1 \u0026\u0026 n.left.balRatio() == -1:\n\t\treturn n.rotateRight()\n\n\tcase n.balRatio() \u003e 1 \u0026\u0026 n.right.balRatio() == 1:\n\t\treturn n.rotateLeft()\n\n\tcase n.balRatio() \u003c -1 \u0026\u0026 n.left.balRatio() == 1:\n\t\treturn n.rotateLeftRight()\n\n\tcase n.balRatio() \u003e 1 \u0026\u0026 n.right.balRatio() == -1:\n\t\treturn n.rotateRightLeft()\n\t}\n\treturn n\n}\n\n// findMax finds the maximum element in a (sub-)tree. Its value replaces\n// the value of the to-be-deleted node. Return values: the node itself and\n// its parent node.\nfunc (n *node) findMax(parent *node) (*node, *node) {\n\tswitch {\n\tcase n == nil:\n\t\treturn nil, parent\n\n\tcase n.right == nil:\n\t\treturn n, parent\n\t}\n\treturn n.right.findMax(n)\n}\n\n// replaceNode replaces the parent’s child pointer to n with a pointer\n// to the replacement node. parent must not be nil.\nfunc (n *node) replaceNode(parent, replacement *node) error {\n\tif n == nil {\n\t\treturn errors.New(\"replaceNode() not allowed on a nil node\")\n\t}\n\n\tswitch n {\n\tcase parent.left:\n\t\tparent.left = replacement\n\n\tdefault:\n\t\tparent.right = replacement\n\t}\n\n\treturn nil\n}\n\n// delete removes an element from the tree. It is an error to try\n// deleting an element that does not exist. In order to remove an\n// element properly, Delete needs to know the node’s parent node.\n// Parent must not be nil.\nfunc (n *node) delete(key int, parent *node) error {\n\tif n == nil {\n\t\treturn errors.New(\"value to be deleted does not exist in the tree\")\n\t}\n\n\tswitch {\n\tcase key \u003c n.data.Key:\n\t\treturn n.left.delete(key, n)\n\n\tcase key \u003e n.data.Key:\n\t\treturn n.right.delete(key, n)\n\n\tdefault:\n\t\tswitch {\n\t\tcase n.left == nil \u0026\u0026 n.right == nil:\n\t\t\tn.replaceNode(parent, nil)\n\t\t\treturn nil\n\t\tcase n.left == nil:\n\t\t\tn.replaceNode(parent, n.right)\n\t\t\treturn nil\n\t\tcase n.right == nil:\n\t\t\tn.replaceNode(parent, n.left)\n\t\t\treturn nil\n\t\t}\n\t\treplacement, replParent := n.left.findMax(n)\n\t\tn.data = replacement.data\n\t\treturn replacement.delete(replacement.data.Key, replParent)\n\t}\n}\n\n// preOrder traverses the node by traversing the child nodes recursively.\nfunc (n *node) preOrder(f func(*node)) {\n\tif n != nil {\n\t\tf(n)\n\t\tn.left.preOrder(f)\n\t\tn.right.preOrder(f)\n\t}\n}\n\n// inOrder traversal the node by the leftmost node to the rightmost nodes\n// regardless of depth.\nfunc (n *node) inOrder(f func(*node)) {\n\tif n != nil {\n\t\tn.left.inOrder(f)\n\t\tf(n)\n\t\tn.right.inOrder(f)\n\t}\n}\n\n// postOrder traversal the node by the leftmost node then its sibling\n// then up to its parent, recursively.\nfunc (n *node) postOrder(f func(*node)) {\n\tif n != nil {\n\t\tn.left.postOrder(f)\n\t\tn.right.postOrder(f)\n\t\tf(n)\n\t}\n}\n\n// =============================================================================\n\n// max returns the larger of the two values.\nfunc max(a, b int) int {\n\tif a \u003e b {\n\t\treturn a\n\t}\n\treturn b\n}\n\n// =============================================================================\n\n// PrettyPrint takes a Tree value and displays a pretty print\n// version of the tree.\nfunc PrettyPrint(t Tree) {\n\n\t// Build an index map of positions for print layout.\n\tvalues := make(map[int]int)\n\tmaxIdx := buildIndexMap(values, 0, 0, t.root)\n\n\t// Calculate the total number of levels based on\n\t// the max index provided.\n\tvar levels int\n\tfor {\n\t\tpow := math.Pow(2, float64(levels))\n\t\tif maxIdx \u003c int(pow) {\n\t\t\tbreak\n\t\t}\n\t\tlevels++\n\t}\n\tlevels--\n\n\t// Capture the positional data to use.\n\tdata := generateData(levels)\n\n\t// Set the edge of the top of the tree.\n\tfor sp := 0; sp \u003c data[0].edge; sp++ {\n\t\tfmt.Print(\" \")\n\t}\n\tfmt.Printf(\"%02d\", values[0])\n\tfmt.Print(\"\\n\")\n\n\tdataIdx := 1\n\tfor i := 1; i \u003c len(data); i = i + 2 {\n\n\t\t// Set the edge of this row.\n\t\tfor sp := 0; sp \u003c data[i].edge; sp++ {\n\t\t\tfmt.Print(\" \")\n\t\t}\n\n\t\t// Draw the hashes for this row.\n\t\tdataHashIdx := dataIdx\n\t\tfor h := 0; h \u003c data[i].draw; h++ {\n\t\t\tif values[dataHashIdx] != maxInt {\n\t\t\t\tfmt.Printf(\"/\")\n\t\t\t} else {\n\t\t\t\tfmt.Printf(\" \")\n\t\t\t}\n\t\t\tfor sp := 0; sp \u003c data[i].padding; sp++ {\n\t\t\t\tfmt.Print(\" \")\n\t\t\t}\n\t\t\tif values[dataHashIdx+1] != maxInt {\n\t\t\t\tfmt.Printf(\"\\\\\")\n\t\t\t} else {\n\t\t\t\tfmt.Printf(\" \")\n\t\t\t}\n\t\t\tdataHashIdx += 2\n\n\t\t\tif data[i].gaps != 0 \u0026\u0026 data[i].gaps \u003e h {\n\t\t\t\tfor sp := 0; sp \u003c data[i].gapPad; sp++ {\n\t\t\t\t\tfmt.Print(\" \")\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfmt.Print(\"\\n\")\n\n\t\t// Set the edge of the next row.\n\t\tfor sp := 0; sp \u003c data[i+1].edge; sp++ {\n\t\t\tfmt.Print(\" \")\n\t\t}\n\n\t\t// Draw the numbers for this row.\n\t\tfor n := 0; n \u003c data[i+1].draw; n++ {\n\t\t\tif values[dataIdx] != maxInt {\n\t\t\t\tfmt.Printf(\"%02d\", values[dataIdx])\n\t\t\t} else {\n\t\t\t\tfmt.Printf(\" \")\n\t\t\t}\n\t\t\tfor sp := 0; sp \u003c data[i+1].padding; sp++ {\n\t\t\t\tfmt.Print(\" \")\n\t\t\t}\n\t\t\tif values[dataIdx+1] != maxInt {\n\t\t\t\tfmt.Printf(\"%02d\", values[dataIdx+1])\n\t\t\t} else {\n\t\t\t\tfmt.Printf(\" \")\n\t\t\t}\n\t\t\tdataIdx += 2\n\n\t\t\tif data[i+1].gaps != 0 \u0026\u0026 data[i+1].gaps \u003e n {\n\t\t\t\tfor sp := 0; sp \u003c data[i+1].gapPad; sp++ {\n\t\t\t\t\tfmt.Print(\" \")\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfmt.Print(\"\\n\")\n\t}\n\n\tfmt.Print(\"\\n\")\n}\n\nconst maxInt = int(^uint(0) \u003e\u003e 1)\n\n// buildIndex traverses the tree and generates a map of index positions\n// for each node in the tree for printing.\n//\n//\t 40\n//\t / \\\n//\t 05 80\n//\t / \\ / \\\n//\t02 25 65 98\n//\n// values{0:40, 1:05, 2:80, 3:02, 4:25, 5:65, 6:98}\nfunc buildIndexMap(values map[int]int, idx int, maxIdx int, n *node) int {\n\n\t// We need to keep track of the highest index position used\n\t// to help calculate tree depth.\n\tif idx \u003e maxIdx {\n\t\tmaxIdx = idx\n\t}\n\n\t// We have reached the end of a branch. Use the maxInt to mark\n\t// no value in that position.\n\tif n == nil {\n\t\tvalues[idx] = maxInt\n\t\treturn maxIdx\n\t}\n\n\t// Save the value of this node in the map at the\n\t// calculated index position.\n\tvalues[idx] = n.data.Key\n\n\t// Check if there are still nodes to check down the left\n\t// branch. When we move down the tree, the next index doubles.\n\tif n.left != nil {\n\t\tnextidx := 2*idx + 1\n\t\tmaxIdx = buildIndexMap(values, nextidx, maxIdx, n.left)\n\t}\n\n\t// Check if there are still nodes to check down the right\n\t// branch. When we move down the tree, the next index doubles.\n\tnextidx := 2*idx + 2\n\tmaxIdx = buildIndexMap(values, nextidx, maxIdx, n.right)\n\n\t// We need to set missing indexes in the map to maxInt.\n\t// So they are ignored in the printing of the map.\n\tif idx == 0 {\n\t\tfor i := 0; i \u003c maxIdx; i++ {\n\t\t\tif _, ok := values[i]; !ok {\n\t\t\t\tvalues[i] = maxInt\n\t\t\t}\n\t\t}\n\t}\n\n\treturn maxIdx\n}\n\n// pos provides positional data for printing a tree.\ntype pos struct {\n\tedge int\n\tdraw int\n\tpadding int\n\tgaps int\n\tgapPad int\n}\n\n// generateData generates all the positional data needed to display\n// nodes at different levels.\nfunc generateData(level int) []pos {\n\ttotalData := (level * 2) - 1\n\tdata := make([]pos, totalData)\n\tedge := 1\n\tdraw := level - 2\n\tpadding := 0\n\tgapPad := 2\n\n\tfor i := totalData - 1; i \u003e= 0; i = i - 2 {\n\n\t\t// Generate starting edge positions.\n\t\tdata[i].edge = int(math.Pow(2, float64(edge)))\n\t\tif i \u003e 0 {\n\t\t\tdata[i-1].edge = data[i].edge + 1\n\t\t}\n\t\tedge++\n\n\t\t// Generate draw information.\n\t\tif draw \u003e 0 {\n\t\t\tdata[i].draw = int(math.Pow(2, float64(draw)))\n\t\t\tdata[i-1].draw = data[i].draw\n\t\t} else {\n\t\t\tdata[i].draw = 1\n\t\t\tif i \u003e 0 {\n\t\t\t\tdata[i-1].draw = 1\n\t\t\t}\n\t\t}\n\t\tdraw--\n\n\t\t// Generate padding information.\n\t\tpadding += data[i].edge\n\t\tdata[i].padding = padding\n\t\tif i \u003e 0 {\n\t\t\tdata[i-1].padding = padding\n\t\t}\n\n\t\t// Generate gaps information.\n\t\tdata[i].gaps = data[i].draw - 1\n\t\tif i \u003e 0 {\n\t\t\tdata[i-1].gaps = data[i].gaps\n\t\t}\n\n\t\t// Generate gap padding information.\n\t\tif i \u003e 2 {\n\t\t\tdata[i-1].gapPad = int(math.Pow(2, float64(gapPad)))\n\t\t\tdata[i].gapPad = data[i-1].gapPad - 2\n\t\t}\n\t\tgapPad++\n\t}\n\n\treturn data\n}\n","Hash":"JAk1zarYonMq48lOOYRWLLVjXms="}]}]} ,"composition-pollution":{"Title":"Interface Pollution","Description":"آلودگی رابط از واقعیت ناشی می‌شود که افراد نرم‌افزار را با طراحی رابط‌ها تعریف می‌کنند به جای آنکه آن‌ها را کشف کنند.","Pages":[{"Title":"Interface Pollution","Content":"\n \u003ch2\u003eInterface Pollution\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/training/individual-on-demand/ultimate-go-bundle/\" target=\"_blank\"\u003eWatch The Video\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003eNeed Financial Assistance, Use Our \u003ca href=\"https://www.ardanlabs.com/scholarship/\" target=\"_blank\"\u003eScholarship Form\u003c/a\u003e\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n آلودگی رابط از واقعیت ناشی می‌شود که افراد نرم‌افزار را با طراحی رابط‌ها تعریف می‌کنند به جای آنکه آن‌ها را کشف کنند.\n \u003c/p\u003e\n \n\n \u003ch2\u003eبررسی کد\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e1:\u003c/b\u003e ایجاد Interface Pollution\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e2:\u003c/b\u003e حذف Interface Pollution\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n \u003ch2\u003eInterface Pollution\u003c/h2\u003e\n \n \n \u003cp\u003e\n آلودگی رابط از واقعیت ناشی می‌شود که افراد نرم‌افزار را با طراحی رابط‌ها تعریف می‌کنند به جای آنکه آن‌ها را کشف کنند. ابتدا باید یک راه حل محکم برای مشکل طراحی کنید. سپس، اگر لازم باشد، می‌توانید کشف کنید که برنامه در کجا نیاز به چندریختی است.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n این‌ها چیزهایی است که از دیگر توسعه‌دهندگان شنیده‌ام:\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u0026#34;من از رابط‌ها استفاده می‌کنم چون مجبور هستیم از رابط‌ها استفاده کنیم.\u0026#34;\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n نه، ما مجبور نیستیم از رابط‌ها استفاده کنیم. ما زمانی از رابط‌ها استفاده می‌کنیم که عملی و معقول باشد. استفاده از رابط‌ها هزینه‌هایی دارد: سطح میانی و اختصاصی که هنگام ذخیره مقادیر محکم در داخل آن‌ها ایجاد می‌شود. مگر اینکه هزینهٔ اختصاص ارزشی داشته باشد که با جدا کردن از هم به دست می‌آورم، نباید از رابط‌ها استفاده کنید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u0026#34;من نیاز دارم که بتوانم کد خود را تست کنم، بنابراین باید از رابط‌ها استفاده کنم\u0026#34;.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n نه. ابتدا باید API خود را برای کاربر طراحی کنید، نه برای تست. اگر API قابل تست نباشد، باید سوال بپرسید که آیا قابل استفاده است یا خیر. همچنین، لایه‌های مختلفی از API‌ها وجود دارند. API‌های پایین‌تر سطح که به صورت ناصرفاً برای بیان تست‌پذیری تمرکز می‌کنند می‌توانند و باید تمرکز کنند. اما API‌های بالاتر سطح که به صورت عمومی قابل دسترس هستند، باید بر روی قابلیت استفاده تمرکز کنند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n توابعی که داده‌های خام را دریافت کرده و داده‌های خام را برمی‌گردانند، قابل تست بودن بیشتری دارند. از تحویل داده جدا کنید و تبدیل داده را از محلی که داده از آن جا می‌آید و به آن جا می‌رود جدا کنید. این یک تمرین بازآرایی است که باید در طول چرخهٔ توسعهٔ کد انجام دهید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n در زیر، یک مثال ارائه شده است که با استفاده نادرست از یک رابط، آلودگی رابط ایجاد می‌کند که در واقع نیازمندی به آن وجود ندارد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype Server interface {\n Start() error\n Stop() error\n Wait() error\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n رابط \u003ccode\u003eServer\u003c/code\u003e یک قرارداد برای سرورهای TCP تعریف می‌کند. مشکل در اینجا این است که من نیازی به یک قرارداد ندارم، شما نیاز به یک پیاده‌سازی دارید. همچنین، احتمالاً تنها یک پیاده‌سازی وجود خواهد داشت، به خصوص از آنجا که شما خودتان آن را پیاده‌سازی می‌کنید. شما نیازی ندارید که شخص دیگری این را برای شما پیاده‌سازی کند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n علاوه بر این، این رابط بر اساس اسم و نه فعل است. انواع محکم اسم هستند زیرا مشکل واقعی را نمایندگی می‌کنند. رابط‌ها رفتار را توصیف می‌کنند و \u0026#34;سرور\u0026#34; رفتار نیست.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n در زیر چند راه برای شناسایی آلودگی رابط آمده است:\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003eیک بسته یک رابط را تعریف می‌کند که با کل API از نوع محکم خود تطبیق دارد.\u003c/li\u003e\n \n \u003cli\u003eرابط‌ها صادر می‌شوند، اما انواع محکمی که رابط را پیاده‌سازی می‌کنند، صادر نمی‌شوند.\u003c/li\u003e\n \n \u003cli\u003eتابع کارخانه برای نوع محکم، مقدار رابط را با مقدار نوع محکم غیرصادرشده داخل آن برمی‌گرداند.\u003c/li\u003e\n \n \u003cli\u003eرابط قابل حذف است و هیچ تغییری برای کاربر API ایجاد نمی‌کند.\u003c/li\u003e\n \n \u003cli\u003eرابط قادر به جدا کردن API از تغییر نیست.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n راهنمایی‌های مرتبط با آلودگی رابط:\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n استفاده از یک رابط:\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003eزمانی که کاربران API نیاز به ارائه جزئیات پیاده‌سازی دارند.\u003c/li\u003e\n \n \u003cli\u003eزمانی که API‌ها دارای چندین پیاده‌سازی هستند که نیاز به نگهداری دارند.\u003c/li\u003e\n \n \u003cli\u003eزمانی که بخش‌هایی از API که ممکن است تغییر کنند، شناسایی شده‌اند و نیاز به جداسازی دارند.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n سوال برای یک رابط:\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003eزمانی که هدف اصلی آن فقط تولید API قابل تست است (ابتدا API قابل استفاده را بنویسید).\u003c/li\u003e\n \n \u003cli\u003eزمانی که پشتیبانی از API برای جدا شدن از تغییر را فراهم نمی‌کند.\u003c/li\u003e\n \n \u003cli\u003eزمانی که واضح نیست که چگونه رابط کد را بهبود می‌بخشد.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n","Files":[{"Name":"example1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This is an example that creates interface pollution\n// by improperly using an interface when one is not needed.\npackage main\n\n// Server defines a contract for tcp servers.\ntype Server interface {\n\tStart() error\n\tStop() error\n\tWait() error\n}\n\n// server is our Server implementation.\ntype server struct {\n\thost string\n\n\t// PRETEND THERE ARE MORE FIELDS.\n}\n\n// NewServer returns an interface value of type Server\n// with a server implementation.\nfunc NewServer(host string) Server {\n\n\t// SMELL - Storing an unexported type pointer in the interface.\n\treturn \u0026server{host}\n}\n\n// Start allows the server to begin to accept requests.\nfunc (s *server) Start() error {\n\n\t// PRETEND THERE IS A SPECIFIC IMPLEMENTATION.\n\treturn nil\n}\n\n// Stop shuts the server down.\nfunc (s *server) Stop() error {\n\n\t// PRETEND THERE IS A SPECIFIC IMPLEMENTATION.\n\treturn nil\n}\n\n// Wait prevents the server from accepting new connections.\nfunc (s *server) Wait() error {\n\n\t// PRETEND THERE IS A SPECIFIC IMPLEMENTATION.\n\treturn nil\n}\n\nfunc main() {\n\n\t// Create a new Server.\n\tsrv := NewServer(\"localhost\")\n\n\t// Use the API.\n\tsrv.Start()\n\tsrv.Stop()\n\tsrv.Wait()\n}\n\n// =============================================================================\n\n// NOTES:\n\n// Smells:\n// * The package declares an interface that matches the entire API of its own concrete type.\n// * The interface is exported but the concrete type is unexported.\n// * The factory function returns the interface value with the unexported concrete type value inside.\n// * The interface can be removed and nothing changes for the user of the API.\n// * The interface is not decoupling the API from change.\n","Hash":"Ud2Kl3RAVw0t+77KTkApxAGhY1Q="},{"Name":"example2.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This is an example that removes the interface pollution by\n// removing the interface and using the concrete type directly.\npackage main\n\n// Server is our Server implementation.\ntype Server struct {\n\thost string\n\n\t// PRETEND THERE ARE MORE FIELDS.\n}\n\n// NewServer returns an interface value of type Server\n// with a server implementation.\nfunc NewServer(host string) *Server {\n\n\t// SMELL - Storing an unexported type pointer in the interface.\n\treturn \u0026Server{host}\n}\n\n// Start allows the server to begin to accept requests.\nfunc (s *Server) Start() error {\n\n\t// PRETEND THERE IS A SPECIFIC IMPLEMENTATION.\n\treturn nil\n}\n\n// Stop shuts the server down.\nfunc (s *Server) Stop() error {\n\n\t// PRETEND THERE IS A SPECIFIC IMPLEMENTATION.\n\treturn nil\n}\n\n// Wait prevents the server from accepting new connections.\nfunc (s *Server) Wait() error {\n\n\t// PRETEND THERE IS A SPECIFIC IMPLEMENTATION.\n\treturn nil\n}\n\nfunc main() {\n\n\t// Create a new Server.\n\tsrv := NewServer(\"localhost\")\n\n\t// Use the API.\n\tsrv.Start()\n\tsrv.Stop()\n\tsrv.Wait()\n}\n\n// =============================================================================\n\n// NOTES:\n\n// Here are some guidelines around interface pollution:\n// * Use an interface:\n// * When users of the API need to provide an implementation detail.\n// * When API’s have multiple implementations that need to be maintained.\n// * When parts of the API that can change have been identified and require decoupling.\n// * Question an interface:\n// * When its only purpose is for writing testable API’s (write usable API’s first).\n// * When it’s not providing support for the API to decouple from change.\n// * When it's not clear how the interface makes the code better.\n","Hash":"w2xmDI3uwDuuc6v6n6fFQal1Zuo="}]}]} ,"embedding":{"Title":"واردکردن (Embedding)","Description":"نوع‌های واردکردن، قسمت نهایی از اشتراک و بازاستفاده از وضعیت و رفتار بین انواع را فراهم می‌کنند.","Pages":[{"Title":"واردکردن (Embedding)","Content":"\n \u003ch2\u003eواردکردن (Embedding)\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/training/individual-on-demand/ultimate-go-bundle/\" target=\"_blank\"\u003eتماشای ویدئو\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003eنیاز به کمک مالی دارید؟ از \u003ca href=\"https://www.ardanlabs.com/scholarship/\" target=\"_blank\"\u003eفرم Scholarship\u003c/a\u003e ما استفاده کنید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n انواع واردکردن (Embedding types) قسمت نهایی از به اشتراک گذاشتن و بازاستفاده از وضعیت و رفتار بین انواع را فراهم می‌کنند. از طریق استفاده از ارتقاء داخلی از نوع داخلی، می‌توان به طور مستقیم به فیلدها و متدهای نوع داخلی توسط مراجعه‌های نوع خارجی دسترسی داشت.\n \u003c/p\u003e\n \n\n \u003ch2\u003eمرور کد\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e1:\u003c/b\u003e تعریف فیلدها\t\t \u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e2:\u003c/b\u003e انواع واردکردن\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e3:\u003c/b\u003e انواع واردکرده و اینترفیس ها\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e4:\u003c/b\u003e اجرای اینترفیس های نوع خارجی و داخلی\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n \u003ch2\u003eمکانیک‌های واردکردن\u003c/h2\u003e\n \n \n \u003cp\u003e\n این مثال اول واردکردن را نشان نمی‌دهد، فقط تعریف دو نوع ساختاری است که به عنوان یک فیلد از یک نوع به نوع دیگر کار می‌کنند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype user struct {\n name string\n email string\n}\n\ntype admin struct {\n person user // NOT Embedding\n level string\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این واردکردن است.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype user struct {\n name string\n email string\n}\n\ntype admin struct {\n user // Value Semantic Embedding\n level string\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n فیلد شخص حذف شده و فقط نام نوع باقی مانده است. همچنین می‌توانید از نحوه‌های اشاره‌گری برای واردکردن یک نوع استفاده کنید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype user struct {\n name string\n email string\n}\n\ntype admin struct {\n *user // Pointer Semantic Embedding\n level string\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در این مورد، یک اشاره‌گر از نوع وارد شده است. در هر دو مورد، دسترسی به مقدار واردشده از طریق استفاده از نام نوع انجام می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n بهترین راه برای درک واردکردن این است که نوع کاربر را به عنوان یک نوع داخلی و admin را به عنوان یک نوع خارجی در نظر بگیرید. این رابطه نوع داخلی/خارجی دقیقاً آن چیزی است که جادویی است، زیرا با واردکردن، همه چیز مرتبط با نوع داخلی (هم فیلدها و هم متدها) می‌توانند به نوع خارجی ارتقاء یابند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype user struct {\n name string\n email string\n}\n\nfunc (u *user) notify() {\n fmt.Printf(\u0026#34;Sending user email To %s\u0026lt;%s\u0026gt;\\n\u0026#34;,\n u.name,\n u.email)\n}\n\ntype admin struct {\n *user // Pointer Semantic Embedding\n level string\n}\n\nfunc main() {\n ad := admin{\n user: \u0026amp;user{\n name: \u0026#34;john smith\u0026#34;,\n email: \u0026#34;john@yahoo.com\u0026#34;,\n },\n level: \u0026#34;super\u0026#34;,\n }\n\n ad.user.notify()\n ad.notify() // Outer type promotion\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eارسال ایمیل کاربر به john smith \u0026lt;john@yahoo.com\u0026gt;\nارسال ایمیل کاربر به john smith \u0026lt;john@yahoo.com\u0026gt;\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n هنگامی که یک متد با نام notify به نوع کاربر اضافه می‌کنید و سپس یک تابع اصلی کوچک ایجاد می‌کنید، می‌بینید که خروجی یکسان است، بگیرید که متد notify را از طریق مقدار اشاره‌گر داخلی مستقیماً فراخوانی کنید یا از طریق مقدار نوع خارجی. متد notify که برای نوع کاربر اعلام شده است، به طور مستقیم توسط مقدار نوع admin قابل دسترسی است.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n اگرچه این مانند ارث‌بری به نظر می‌رسد، باید مراقب باشید. این موضوع درباره بازاستفاده از وضعیت نیست، بلکه درباره ارتقاء رفتار است.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype notifier interface {\n notify()\n}\n\nfunc sendNotification(n notifier) {\n n.notify()\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n اکنون شما یک رابط و یک تابع چندشکلی اضافه می‌کنید که هر مقدار محسوسی را که مجموعه کامل متدهای رفتار تعریف شده توسط رابط notifier پیاده‌سازی کند، قبول می‌کند. که فقط یک متد به نام notify است.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n به دلیل واردکردن و ارتقاء، مقادیر نوع admin اکنون رابط notifier را پیاده‌سازی می‌کنند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc main() {\n ad := admin{\n user: \u0026amp;user{\n name: \u0026#34;john smith\u0026#34;,\n email: \u0026#34;john@yahoo.com\u0026#34;,\n },\n level: \u0026#34;super\u0026#34;,\n }\n\n sendNotification(\u0026amp;ad)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eارسال ایمیل کاربر به john smith \u0026lt;john@yahoo.com\u0026gt;\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n شما می‌توانید آدرس مقدار admin را به تابع چندشکلی بفرستید، چرا که واردکردن رفتار notify را تا نوع admin ارتقاء می‌دهد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype admin struct {\n *user // Pointer Semantic Embedding\n level string\n}\n\nfunc (a *admin) notify() {\n fmt.Printf(\u0026#34;Sending admin Email To %s\u0026lt;%s\u0026gt;\\n\u0026#34;,\n a.name,\n a.email)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n زمانی که نوع خارجی یک متد را که قبلاً توسط نوع داخلی پیاده‌سازی شده است، پیاده‌سازی می‌کند، ارتقاء انجام نمی‌شود.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc main() {\n ad := admin{\n user: \u0026amp;user{\n name: \u0026#34;john smith\u0026#34;,\n email: \u0026#34;john@yahoo.com\u0026#34;,\n },\n level: \u0026#34;super\u0026#34;,\n }\n\n sendNotification(\u0026amp;ad)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eارسال ایمیل ادمین به john smith \u0026lt;john@yahoo.com\u0026gt;\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n می‌بینید که متد نوع خارجی اکنون اجرا می‌شود.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eیادداشت‌ها\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003eانواع واردکردن به ما اجازه می‌دهند تا وضعیت یا رفتار را بین انواع به اشتراک بگذاریم.\u003c/li\u003e\n \n \u003cli\u003eنوع داخلی هیچ‌گاه هویت خود را از دست نمی‌دهد.\u003c/li\u003e\n \n \u003cli\u003eاین ارث‌بری نیست.\u003c/li\u003e\n \n \u003cli\u003eاز طریق ارتقاء، فیلدها و متدهای نوع داخلی از طریق نوع خارجی قابل دسترسی هستند.\u003c/li\u003e\n \n \u003cli\u003eنوع خارجی می‌تواند رفتار نوع داخلی را نادیده بگیرد.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eمطالعه اضافی\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2014/05/methods-interfaces-and-embedded-types.html\" target=\"_blank\"\u003eMethods, Interfaces and Embedded Types in Go\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://rakyll.org/typesystem/\" target=\"_blank\"\u003eEmbedding is not inheritance\u003c/a\u003e - JBD \u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n","Files":[{"Name":"example1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how what we are doing is NOT embedding\n// a type but just using a type as a field.\npackage main\n\nimport \"fmt\"\n\n// user defines a user in the program.\ntype user struct {\n\tname string\n\temail string\n}\n\n// notify implements a method notifies users\n// of different events.\nfunc (u *user) notify() {\n\tfmt.Printf(\"Sending user email To %s\u003c%s\u003e\\n\",\n\t\tu.name,\n\t\tu.email)\n}\n\n// admin represents an admin user with privileges.\ntype admin struct {\n\tperson user // NOT Embedding\n\tlevel string\n}\n\nfunc main() {\n\n\t// Create an admin user.\n\tad := admin{\n\t\tperson: user{\n\t\t\tname: \"john smith\",\n\t\t\temail: \"john@yahoo.com\",\n\t\t},\n\t\tlevel: \"super\",\n\t}\n\n\t// We can access fields methods.\n\tad.person.notify()\n}\n","Hash":"TrXNd5apot5h5BLYcyFb+rTOGlU="},{"Name":"example2.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to embed a type into another type and\n// the relationship between the inner and outer type.\npackage main\n\nimport \"fmt\"\n\n// user defines a user in the program.\ntype user struct {\n\tname string\n\temail string\n}\n\n// notify implements a method notifies users\n// of different events.\nfunc (u *user) notify() {\n\tfmt.Printf(\"Sending user email To %s\u003c%s\u003e\\n\",\n\t\tu.name,\n\t\tu.email)\n}\n\n// admin represents an admin user with privileges.\ntype admin struct {\n\tuser // Embedded Type\n\tlevel string\n}\n\nfunc main() {\n\n\t// Create an admin user.\n\tad := admin{\n\t\tuser: user{\n\t\t\tname: \"john smith\",\n\t\t\temail: \"john@yahoo.com\",\n\t\t},\n\t\tlevel: \"super\",\n\t}\n\n\t// We can access the inner type's method directly.\n\tad.user.notify()\n\n\t// The inner type's method is promoted.\n\tad.notify()\n}\n","Hash":"JQHlNx98Dn6sxAy63T7Ht1LvcHM="},{"Name":"example3.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how embedded types work with interfaces.\npackage main\n\nimport \"fmt\"\n\n// notifier is an interface that defined notification\n// type behavior.\ntype notifier interface {\n\tnotify()\n}\n\n// user defines a user in the program.\ntype user struct {\n\tname string\n\temail string\n}\n\n// notify implements a method notifies users\n// of different events.\nfunc (u *user) notify() {\n\tfmt.Printf(\"Sending user email To %s\u003c%s\u003e\\n\",\n\t\tu.name,\n\t\tu.email)\n}\n\n// admin represents an admin user with privileges.\ntype admin struct {\n\tuser\n\tlevel string\n}\n\nfunc main() {\n\n\t// Create an admin user.\n\tad := admin{\n\t\tuser: user{\n\t\t\tname: \"john smith\",\n\t\t\temail: \"john@yahoo.com\",\n\t\t},\n\t\tlevel: \"super\",\n\t}\n\n\t// Send the admin user a notification.\n\t// The embedded inner type's implementation of the\n\t// interface is \"promoted\" to the outer type.\n\tsendNotification(\u0026ad)\n}\n\n// sendNotification accepts values that implement the notifier\n// interface and sends notifications.\nfunc sendNotification(n notifier) {\n\tn.notify()\n}\n","Hash":"s11QYi8CSucrfvdwng/+d2bUbWg="},{"Name":"example4.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show what happens when the outer and inner\n// type implement the same interface.\npackage main\n\nimport \"fmt\"\n\n// notifier is an interface that defined notification\n// type behavior.\ntype notifier interface {\n\tnotify()\n}\n\n// user defines a user in the program.\ntype user struct {\n\tname string\n\temail string\n}\n\n// notify implements a method notifies users\n// of different events.\nfunc (u *user) notify() {\n\tfmt.Printf(\"Sending user email To %s\u003c%s\u003e\\n\",\n\t\tu.name,\n\t\tu.email)\n}\n\n// admin represents an admin user with privileges.\ntype admin struct {\n\tuser\n\tlevel string\n}\n\n// notify implements a method notifies admins\n// of different events.\nfunc (a *admin) notify() {\n\tfmt.Printf(\"Sending admin Email To %s\u003c%s\u003e\\n\",\n\t\ta.name,\n\t\ta.email)\n}\n\nfunc main() {\n\n\t// Create an admin user.\n\tad := admin{\n\t\tuser: user{\n\t\t\tname: \"john smith\",\n\t\t\temail: \"john@yahoo.com\",\n\t\t},\n\t\tlevel: \"super\",\n\t}\n\n\t// Send the admin user a notification.\n\t// The embedded inner type's implementation of the\n\t// interface is NOT \"promoted\" to the outer type.\n\tsendNotification(\u0026ad)\n\n\t// We can access the inner type's method directly.\n\tad.user.notify()\n\n\t// The inner type's method is NOT promoted.\n\tad.notify()\n}\n\n// sendNotification accepts values that implement the notifier\n// interface and sends notifications.\nfunc sendNotification(n notifier) {\n\tn.notify()\n}\n","Hash":"auEPqH4SvHwZ/Ygfcj1VmnYyLWo="}]},{"Title":"تمرین‌ها","Content":"\n \u003ch2\u003eتمرین‌ها\u003c/h2\u003e\n \n \n \u003cp\u003e\n از الگو به عنوان نقطه شروع برای انجام تمرین‌ها استفاده کنید. یک راه‌حل ممکن ارائه شده است.\n \u003c/p\u003e\n \n\n \u003ch2\u003eتمرین 1\u003c/h2\u003e\n \n \n \u003cp\u003e\n کد را از الگو ویرایش کنید. یک نوع جدید به نام CachingFeed اضافه کنید که Feed را به عنوان یک نوع داخلی شامل می‌شود و متد Fetch را تغییر می‌دهد.\n \u003c/p\u003e\n \n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n","Files":[{"Name":"exercise1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This program defines a type Feed with two methods: Count and Fetch. Create a\n// new type CachingFeed that embeds *Feed but overrides the Fetch method.\n//\n// The CachingFeed type should have a map of Documents to limit the number of\n// calls to Feed.Fetch.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"time\"\n)\n\n// Document is the core data model we are working with.\ntype Document struct {\n\tKey string\n\tTitle string\n}\n\n// ==================================================\n\n// Feed is a type that knows how to fetch Documents.\ntype Feed struct{}\n\n// Count tells how many documents are in the feed.\nfunc (f *Feed) Count() int {\n\treturn 42\n}\n\n// Fetch simulates looking up the document specified by key. It is slow.\nfunc (f *Feed) Fetch(key string) (Document, error) {\n\ttime.Sleep(time.Second)\n\n\tdoc := Document{\n\t\tKey: key,\n\t\tTitle: \"Title for \" + key,\n\t}\n\treturn doc, nil\n}\n\n// ==================================================\n\n// FetchCounter is the behavior we depend on for our process function.\ntype FetchCounter interface {\n\tFetch(key string) (Document, error)\n\tCount() int\n}\n\nfunc process(fc FetchCounter) {\n\tfmt.Printf(\"There are %d documents\\n\", fc.Count())\n\n\tkeys := []string{\"a\", \"a\", \"a\", \"b\", \"b\", \"b\"}\n\n\tfor _, key := range keys {\n\t\tdoc, err := fc.Fetch(key)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Could not fetch %s : %v\", key, err)\n\t\t\treturn\n\t\t}\n\n\t\tfmt.Printf(\"%s : %v\\n\", key, doc)\n\t}\n}\n\n// ==================================================\n\n// CachingFeed keeps a local copy of Documents that have already been\n// retrieved. It embeds Feed to get the Fetch and Count behavior but\n// \"overrides\" Fetch to have its cache.\ntype CachingFeed struct {\n\t// TODO embed *Feed and add a field for a map[string]Document.\n}\n\n// NewCachingFeed initializes a CachingFeed for use.\nfunc NewCachingFeed(f *Feed) *CachingFeed {\n\n\t// TODO create a CachingFeed with an initialized map and embedded feed.\n\t// Return its address.\n\n\treturn nil // Remove this line.\n}\n\n// Fetch calls the embedded type's Fetch method if the key is not cached.\nfunc (cf *CachingFeed) Fetch(key string) (Document, error) {\n\n\t// TODO implement this method. Check the map field for the specified key and\n\t// return it if found. If it's not found, call the embedded types Fetch\n\t// method. Store the result in the map before returning it.\n\n\treturn Document{}, nil // Remove this line.\n}\n\n// ==================================================\n\nfunc main() {\n\tfmt.Println(\"Using Feed directly\")\n\tprocess(\u0026Feed{})\n\n\t// Call process again with your CachingFeed.\n\t//fmt.Println(\"Using CachingFeed\")\n}\n","Hash":"AcSdLzJaPy03h731V6kEO+REJgc="},{"Name":"answer1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how you can use embedding to reuse behavior from\n// another type and override specific methods.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"time\"\n)\n\n// Document is the core data model we are working with.\ntype Document struct {\n\tKey string\n\tTitle string\n}\n\n// ==================================================\n\n// Feed is a type that knows how to fetch Documents.\ntype Feed struct{}\n\n// Count tells how many documents are in the feed.\nfunc (f *Feed) Count() int {\n\treturn 42\n}\n\n// Fetch simulates looking up the document specified by key. It is slow.\nfunc (f *Feed) Fetch(key string) (Document, error) {\n\ttime.Sleep(time.Second)\n\n\tdoc := Document{\n\t\tKey: key,\n\t\tTitle: \"Title for \" + key,\n\t}\n\treturn doc, nil\n}\n\n// ==================================================\n\n// CachingFeed keeps a local copy of Documents that have already been\n// retrieved. It embeds Feed to get the Fetch and Count behavior but\n// \"overrides\" Fetch to have its cache.\ntype CachingFeed struct {\n\tdocs map[string]Document\n\t*Feed\n}\n\n// NewCachingFeed initializes a CachingFeed for use.\nfunc NewCachingFeed(f *Feed) *CachingFeed {\n\treturn \u0026CachingFeed{\n\t\tdocs: make(map[string]Document),\n\t\tFeed: f,\n\t}\n}\n\n// Fetch calls the embedded type's Fetch method if the key is not cached.\nfunc (cf *CachingFeed) Fetch(key string) (Document, error) {\n\tif doc, ok := cf.docs[key]; ok {\n\t\treturn doc, nil\n\t}\n\n\tdoc, err := cf.Feed.Fetch(key)\n\tif err != nil {\n\t\treturn Document{}, err\n\t}\n\n\tcf.docs[key] = doc\n\treturn doc, nil\n}\n\n// ==================================================\n\n// FetchCounter is the behavior we depend on for our process function.\ntype FetchCounter interface {\n\tFetch(key string) (Document, error)\n\tCount() int\n}\n\nfunc process(fc FetchCounter) {\n\tfmt.Printf(\"There are %d documents\\n\", fc.Count())\n\n\tkeys := []string{\"a\", \"a\", \"a\", \"b\", \"b\", \"b\"}\n\n\tfor _, key := range keys {\n\t\tdoc, err := fc.Fetch(key)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Could not fetch %s : %v\", key, err)\n\t\t\treturn\n\t\t}\n\n\t\tfmt.Printf(\"%s : %v\\n\", key, doc)\n\t}\n}\n\nfunc main() {\n\tfmt.Println(\"Using Feed directly\")\n\tprocess(\u0026Feed{})\n\n\tfmt.Println(\"Using CachingFeed\")\n\tc := NewCachingFeed(\u0026Feed{})\n\tprocess(c)\n}\n","Hash":"RkxU0OGU2rNjofexQd50IYkOab0="}]}]} ,"exporting":{"Title":"صادرات (Exporting)","Description":"صادرات امکان تعیین می‌دهد که آیا یک شناسه (identifier) به کد خارج از پکیجی که در آن تعریف شده است، دسترسی دارد یا خیر.","Pages":[{"Title":"صادرات (Exporting)","Content":"\n \u003ch2\u003eصادرات (Exporting)\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/training/individual-on-demand/ultimate-go-bundle/\" target=\"_blank\"\u003eتماشای ویدئو\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003eنیاز به کمک مالی دارید؟ از \u003ca href=\"https://www.ardanlabs.com/scholarship/\" target=\"_blank\"\u003eفرم Scholarship\u003c/a\u003e ما استفاده کنید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n صادرات (Exporting) امکان تعیین می‌دهد که آیا یک شناسه (identifier) به کد خارج از پکیجی که در آن تعریف شده است، دسترسی دارد یا خیر.\n \u003c/p\u003e\n \n\n \u003ch2\u003eبررسی کد\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e1:\u003c/b\u003e تعریف و دسترسی به شناسه‌های صادر شده\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e2:\u003c/b\u003e تعریف شناسه‌های غیرصادر شده و محدودیت‌ها\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e3:\u003c/b\u003e دسترسی به مقادیر شناسه‌های غیرصادر شده\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e4:\u003c/b\u003e فیلدهای نوع ساختار غیرصادر شده\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e5:\u003c/b\u003e embedded types غیرصادر شده\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n \u003ch2\u003eمکانیک صادرات\u003c/h2\u003e\n \n \n \u003cp\u003e\n یک پکیج (package) واحد اصلی کد کامپایل شده در Go است. این یک واحد فیزیکی از کد کامپایل شده را نمایان می‌کند، معمولاً به عنوان یک کتابخانه کامپایل شده در سیستم عامل میزبان. صادرات تعیین می‌کند که آیا به شناسه‌ها دسترسی از مرزهای پکیج ممکن است یا نه.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003epackage counters\n\ntype AlertCounter int\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در این مورد، از یک حروف بزرگ برای نام‌گذاری نوع AlterCounter استفاده می‌شود، بنابراین این نوع صادر شده است و می‌توان به صورت مستقیم توسط کد خارج از پکیج counters ارجاع داد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003epackage counters\n\ntype alertCounter int\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n اکنون که نام نوع را به شروع با حرف کوچک تغییر داده‌اید، نوع به حالت غیرصادر (unexported) تغییر کرده است. این به این معناست که تنها کد درون پکیج counters می‌تواند به این نوع به صورت مستقیم ارجاع دهد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003epackage counters\n\ntype alertCounter int\n\nfunc New(value int) alertCounter {\n return alertCounter(value)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n هرچند کد بالا از نظر سینتکس صحیح است و کامپایل می شود، اما ارزشی ندارد. بازگرداندن مقدار از یک نوع داده غیرقابل مشاهده گیج کننده است زیرا فراخواننده (که احتمالاً در یک پکیج دیگر وجود دارد) نمی تواند به طور مستقیم به نام نوع داده اشاره کند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003epackage main\n\nimport (\n \u0026#34;fmt\u0026#34;\n\n \u0026#34;github.com/ardanlabs/.../exporting/example3/counters\u0026#34;\n)\n\nfunc main() {\n counter := counters.New(10)\n fmt.Printf(\u0026#34;Counter: %d\\n\u0026#34;, counter)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در این مورد، تابع اصلی در پکیج main با موفقیت تابع counters.New را فراخوانی می کند و کامپایلر می تواند یک متغیر از نوع داده غیرقابل مشاهده را اعلام و ایجاد کند. این بدان معنا نیست که شما باید این کار را انجام دهید و نه اینکه برای این کار محافظت واقعی دریافت می کنید. این باید اجتناب شود، و اگر New مقداری را برمی گرداند، باید از نوع داده قابل مشاهده باشد.\n\n\n package users\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype User struct {\n Name string\n ID int\n\n password string\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n وقتی صحبت از فیلدها در یک struct می شود، حرف اول مشخص می کند که آیا فیلد برای کدی در خارج از پکیجی که در آن اعلام شده است قابل دسترسی است یا خیر. در این مورد، Name و ID قابل دسترسی هستند، اما password خیر. این یک عرف است که فیلدهای قابل مشاهده و غیرقابل مشاهده را به این روش جدا کنید، اگر این کار منطقی یا عملی باشد. به طور معمول، همه فیلدها یکی یا دیگری هستند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003epackage users\n\ntype user struct {\n Name string\n ID int\n}\n\ntype Manager struct {\n Title string\n user\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در این سناریو، اگرچه نوع کاربر غیرقابل مشاهده است، اما دو فیلد قابل مشاهده دارد. این بدان معناست که زمانی که نوع کاربر در نوع صادر شده Manager جاسازی می شود، فیلدهای کاربر ارتقا می یابند و قابل دسترسی هستند. داشتن انواع غیرقابل مشاهده با فیلدهای قابل مشاهده رایج است زیرا بسته بازتابی تنها می تواند روی فیلدهای قابل مشاهده عمل کند. در غیر این صورت، Marshallers کار نمی کند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n این مثال یک موقعیت بد ایجاد می کند که در آن کدی در خارج از بسته users می تواند یک Manager بسازد، اما از آنجایی که نوع جاسازی شده user غیرقابل مشاهده است، فیلدهای آن نوع را نمی توان مقداردهی کرد. این مشکلات ساخت جزئی ایجاد می کند که منجر به اشکالات می شود. شما باید در صادر کردن و عدم صدور سازگار باشید.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eنکات\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003eکد در Go در پکیج ها کامپایل می شود و سپس به هم متصل می شود.\u003c/li\u003e\n \n \u003cli\u003eشناسه ها بر اساس حروف بزرگ و کوچک بودن صادر می شوند (یا صادر نمی شوند).\u003c/li\u003e\n \n \u003cli\u003eما پکیج ها را برای دسترسی به شناسه های صادر شده وارد می کنیم.\u003c/li\u003e\n \n \u003cli\u003eهر پکیجی می تواند از مقدار یک نوع داده غیرقابل مشاهده استفاده کند، اما استفاده از آن آزاردهنده است.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eخواندن بیشتر\u003c/h2\u003e\n \n \n \u003cp\u003e\n \u003ca href=\"https://www.ardanlabs.com/blog/2014/03/exportedunexported-identifiers-in-go.html\" target=\"_blank\"\u003eExported/Unexported Identifiers In Go\u003c/a\u003e - William Kennedy \n \u003c/p\u003e\n \n\n\n","Files":[{"Name":"example1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to access an exported identifier.\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"play.ground/counters\"\n)\n\nfunc main() {\n\n\t// Create a variable of the exported type and initialize the value to 10.\n\tcounter := counters.AlertCounter(10)\n\n\tfmt.Printf(\"Counter: %d\\n\", counter)\n}\n\n// -----------------------------------------------------------------------------\n-- counters/counters.go --\n\n// Package counters provides alert counter support.\npackage counters\n\n// AlertCounter is an exported named type that\n// contains an integer counter for alerts.\ntype AlertCounter int\n\n// -----------------------------------------------------------------------------\n-- go.mod --\n \nmodule \"play.ground\"\n\ngo 1.24.0\n\nreplace \"play.ground/counters\" =\u003e ./counters\n","Hash":"3qzY8hyVJoSzio8hdoyPyqwTdmg="},{"Name":"example2.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to access an exported identifier.\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"play.ground/counters\"\n)\n\nfunc main() {\n\n\t// Create a variable of the exported type and initialize the value to 10.\n\tcounter := counters.alertCounter(10)\n\n\t// ./example2.go:16: undefined: counters.alertCounter\n\n\tfmt.Printf(\"Counter: %d\\n\", counter)\n}\n\n// -----------------------------------------------------------------------------\n-- counters/counters.go --\n\n// Package counters provides alert counter support.\npackage counters\n\n// alertCounter is an unexported named type that\n// contains an integer counter for alerts.\ntype alertCounter int\n\n// -----------------------------------------------------------------------------\n-- go.mod --\n \nmodule \"play.ground\"\n\ngo 1.24.0\n","Hash":"JmOngEFNogvwW82C8TIpQEreG44="},{"Name":"example3.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how the program can access a value\n// of an unexported identifier from another package.\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"play.ground/counters\"\n)\n\nfunc main() {\n\n\t// Create a variable of the unexported type using the exported\n\t// New function from the package counters.\n\tcounter := counters.New(10)\n\n\tfmt.Printf(\"Counter: %d\\n\", counter)\n}\n\n// -----------------------------------------------------------------------------\n-- counters/counters.go --\n\n// Package counters provides alert counter support.\npackage counters\n\n// alertCounter is an unexported named type that\n// contains an integer counter for alerts.\ntype alertCounter int\n\n// New creates and returns values of the unexported type alertCounter.\nfunc New(value int) alertCounter {\n\treturn alertCounter(value)\n}\n\n// -----------------------------------------------------------------------------\n-- go.mod --\n \nmodule \"play.ground\"\n\ngo 1.24.0\n","Hash":"1S8gdbs0iz+Gka/kXoU2M5je3go="},{"Name":"example4.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how unexported fields from an exported struct\n// type can't be accessed directly.\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"play.ground/users\"\n)\n\nfunc main() {\n\n\t// Create a value of type User from the users package.\n\tu := users.User{\n\t\tName: \"Chole\",\n\t\tID: 10,\n\n\t\tpassword: \"xxxx\",\n\t}\n\n\t// ./example4.go:21: unknown field password in struct literal of type users.User\n\n\tfmt.Printf(\"User: %#v\\n\", u)\n}\n\n// -----------------------------------------------------------------------------\n-- users/users.go --\n\n// Package users provides support for user management.\npackage users\n\n// User represents information about a user.\ntype User struct {\n\tName string\n\tID int\n\n\tpassword string\n}\n\n// -----------------------------------------------------------------------------\n-- go.mod --\n \nmodule \"play.ground\"\n\ngo 1.24.0\n","Hash":"GcKXz1laqW0DP/p4WHQAWzdV+G8="},{"Name":"example5.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to create values from exported types with\n// embedded unexported types.\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"play.ground/users\"\n)\n\nfunc main() {\n\n\t// Create a value of type Manager from the users package.\n\tu := users.Manager{\n\t\tTitle: \"Dev Manager\",\n\t}\n\n\t// Set the exported fields from the unexported user inner type.\n\tu.Name = \"Chole\"\n\tu.ID = 10\n\n\tfmt.Printf(\"User: %#v\\n\", u)\n}\n\n// -----------------------------------------------------------------------------\n-- users/users.go --\n\n// Package users provides support for user management.\npackage users\n\n// User represents information about a user.\ntype user struct {\n\tName string\n\tID int\n}\n\n// Manager represents information about a manager.\ntype Manager struct {\n\tTitle string\n\n\tuser\n}\n\n// -----------------------------------------------------------------------------\n-- go.mod --\n \nmodule \"play.ground\"\n\ngo 1.24.0\n","Hash":"heIqHBMpsUD+PTdd7CyTI34s3R4="}]},{"Title":"تمرین‌ها","Content":"\n \u003ch2\u003eتمرین‌ها\u003c/h2\u003e\n \n \n \u003cp\u003e\n از الگو به‌عنوان نقطه شروع برای تکمیل تمرین‌ها استفاده کنید. یک راه حل احتمالی ارائه شده است.\n \u003c/p\u003e\n \n\n \u003ch2\u003eتمرین 1\u003c/h2\u003e\n \n \n \u003cp\u003e\n \u003cb\u003eبخش\u003c/b\u003e \u003cb\u003eA:\u003c/b\u003e یک پکیج به نام toy با یک نوع struct صادر شده به نام Toy ایجاد کنید. فیلدهای صادر شده Name و Weight را اضافه کنید. سپس دو فیلد غیرقابل مشاهده به نام‌های onHand و sold اضافه کنید. یک تابع کارخانه به نام New برای ایجاد مقادیر از نوع toy و پذیرفتن پارامترهایی برای فیلدهای صادر شده اعلام کنید. سپس متدهایی را اعلام کنید که مقادیر فیلدهای غیرقابل مشاهده را برمی‌گردانند و به‌روزرسانی می‌کنند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u003cb\u003eبخش\u003c/b\u003e \u003cb\u003eB:\u003c/b\u003e یک برنامه ایجاد کنید که پکیج toy را وارد کند. از تابع New برای ایجاد یک مقدار از نوع toy استفاده کنید. سپس از متدها برای تنظیم شمارنده‌ها و نمایش مقادیر فیلدهای آن مقدار toy استفاده کنید.\n \u003c/p\u003e\n \n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n","Files":[{"Name":"exercise1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Create a package named toy with a single exported struct type named Toy. Add\n// the exported fields Name and Weight. Then add two unexported fields named\n// onHand and sold. Declare a factory function called New to create values of\n// type toy and accept parameters for the exported fields. Then declare methods\n// that return and update values for the unexported fields.\n//\n// Create a program that imports the toy package. Use the New function to create a\n// value of type toy. Then use the methods to set the counts and display the\n// field values of that toy value.\npackage main\n\nimport (\n\t\"play.ground/toy\"\n)\n\nfunc main() {\n\n\t// Use the New function from the toy package to create a value of\n\t// type toy.\n\n\t// Use the methods from the toy value to set some initialize\n\t// values.\n\n\t// Display each field separately from the toy value.\n}\n\n// -----------------------------------------------------------------------------\n-- toy/toy.go --\n\n// Package toy contains support for managing toy inventory.\npackage toy\n\n// Declare a struct type named Toy with four fields. Name string,\n// Weight int, onHand int and sold int.\n\n// Declare a function named New that accepts values for the\n// exported fields. Return a pointer of type Toy that is initialized\n// with the parameters.\n\n// Declare a method named OnHand with a pointer receiver that\n// returns the current on hand count.\n\n// Declare a method named UpdateOnHand with a pointer receiver that\n// updates and returns the current on hand count.\n\n// Declare a method named Sold with a pointer receiver that\n// returns the current sold count.\n\n// Declare a method named UpdateSold with a pointer receiver that\n// updates and returns the current sold count.\n\n// -----------------------------------------------------------------------------\n-- go.mod --\n \nmodule \"play.ground\"\n\ngo 1.24.0\n","Hash":"XVQEvrhbML2PflX2VG4jZyPf5to="},{"Name":"answer1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Create a package named toy with a single exported struct type named Toy. Add\n// the exported fields Name and Weight. Then add two unexported fields named\n// onHand and sold. Declare a factory function called New to create values of\n// type toy and accept parameters for the exported fields. Then declare methods\n// that return and update values for the unexported fields.\n//\n// Create a program that imports the toy package. Use the New function to create a\n// value of type toy. Then use the methods to set the counts and display the\n// field values of that toy value.\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"play.ground/toy\"\n)\n\nfunc main() {\n\n\t// Create a value of type toy.\n\tt := toy.New(\"Bat\", 28)\n\n\t// Update the counts.\n\tt.UpdateOnHand(100)\n\tt.UpdateSold(2)\n\n\t// Display each field separately.\n\tfmt.Println(\"Name\", t.Name)\n\tfmt.Println(\"Weight\", t.Weight)\n\tfmt.Println(\"OnHand\", t.OnHand())\n\tfmt.Println(\"Sold\", t.Sold())\n}\n\n// -----------------------------------------------------------------------------\n-- toy/toy.go --\n\n// Package toy contains support for managing toy inventory.\npackage toy\n\n// Toy represents a toy we sell.\ntype Toy struct {\n\tName string\n\tWeight int\n\n\tonHand int\n\tsold int\n}\n\n// New creates values of type toy.\nfunc New(name string, weight int) *Toy {\n\treturn \u0026Toy{\n\t\tName: name,\n\t\tWeight: weight,\n\t}\n}\n\n// OnHand returns the current number of this\n// toy on hand.\nfunc (t *Toy) OnHand() int {\n\treturn t.onHand\n}\n\n// UpdateOnHand updates the on hand count and\n// returns the current value.\nfunc (t *Toy) UpdateOnHand(count int) int {\n\tt.onHand += count\n\treturn t.onHand\n}\n\n// Sold returns the current number of this\n// toy sold.\nfunc (t *Toy) Sold() int {\n\treturn t.sold\n}\n\n// UpdateSold updates the sold count and\n// returns the current value.\nfunc (t *Toy) UpdateSold(count int) int {\n\tt.sold += count\n\treturn t.sold\n}\n\n// -----------------------------------------------------------------------------\n-- go.mod --\n \nmodule \"play.ground\"\n\ngo 1.24.0\n","Hash":"8qY7eR8WSuIVNoNhD8c8M8u4iaE="}]}]} ,"welcome":{"Title":"خوش آمدید","Description":"خوش آمدید به تور نهایی گو. اطلاعات بیشتر درباره تور قبل از شروع","Pages":[{"Title":"سلام, 世界","Content":"\n \u003ch2\u003eسلام, 世界\u003c/h2\u003e\n \n\t\n\t\t\n\t\n\n \n \u003cp\u003e\n خوش آمدید به تور \u003ca href=\"/\" target=\"_self\"\u003eGo programming language\u003c/a\u003e.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n تور به فهرستی از ماژول‌ها تقسیم شده است که می‌توانید به آنها دسترسی پیدا کنید با کلیک روی \u003ca href=\"javascript:highlight(\u0026#34;.logo\u0026#34;)\" target=\"_self\"\u003eUltimate Go\u003c/a\u003e در بالا سمت چپ صفحه.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n همچنین می‌توانید هر زمان جدول محتوا را مشاهده کنید با کلیک روی \u003ca href=\"javascript:highlightAndClick(\u0026#34;.nav\u0026#34;)\" target=\"_self\"\u003eمنو\u003c/a\u003e در بالا سمت راست صفحه.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n در طول تور، شما مجموعه‌ای از اسلایدها و تمرین‌ها برای تکمیل خواهید یافت.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n شما می‌توانید از آنها با استفاده از:\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"javascript:highlight(\u0026#34;.prev-page\u0026#34;)\" target=\"_self\"\u003e\u0026#34;قبلی\u0026#34;\u003c/a\u003e یا PageUp برای رفتن به صفحه قبلی،\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"javascript:highlight(\u0026#34;.next-page\u0026#34;)\" target=\"_self\"\u003e\u0026#34;بعدی\u0026#34;\u003c/a\u003e یا PageDown برای رفتن به صفحه بعدی،\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n جهت پیمایش استفاده کنید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n تور تعاملی است. برای کامپایل و اجرای برنامه در سرور از \u003ca href=\"javascript:highlightAndClick(\u0026#34;#run\u0026#34;)\" target=\"_self\"\u003eاجرا\u003c/a\u003e کلیک کنید (یا Shift + Enter را فشار دهید).\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n نتیجه زیر کد نمایش داده می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n این برنامه‌های نمونه مختلفی از Go را نمایش می‌دهند. برنامه‌های موجود در تور برای شروع آزمایشات شما طراحی شده‌اند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n برنامه را ویرایش کرده و مجدداً اجرا کنید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n وقتی بر روی \u003ca href=\"javascript:highlightAndClick(\u0026#34;#format\u0026#34;)\" target=\"_self\"\u003eفرمت‌بندی\u003c/a\u003e (میانبر: Ctrl + Enter) کلیک می‌کنید، متن در ویرایشگر با استفاده از ابزار \u003ca href=\"/cmd/gofmt/\" target=\"_self\"\u003egofmt\u003c/a\u003e فرمت می‌شود. می‌توانید ویژگی تشخیص دستورات را با کلیک بر روی دکمه \u003ca href=\"javascript:highlightAndClick(\u0026#34;.syntax-checkbox\u0026#34;)\" target=\"_self\"\u003eتشخیص دستورات\u003c/a\u003e روشن یا خاموش کنید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n وقتی آماده به ادامه هستید، بر روی \u003ca href=\"javascript:highlightAndClick(\u0026#34;.next-page\u0026#34;)\" target=\"_self\"\u003eفلش چپ\u003c/a\u003e پایین یا تایپ کلید PageDown کلیک کنید.\n \u003c/p\u003e\n \n\n","Files":[{"Name":"hello.go","Content":"package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"Hello, 世界\")\n}\n","Hash":"DwfZCJ2NK3FTIJnSUkZkwzma41c="}]},{"Title":"نسخه آفلاین گو (اختیاری)","Content":"\n \u003ch2\u003eنسخه آفلاین گو (اختیاری)\u003c/h2\u003e\n \n \n \u003cp\u003e\n این تور همچنین به عنوان یک برنامه مستقل در دسترس است که می‌توانید بدون دسترسی به اینترنت از آن استفاده کنید. این برنامه نمونه‌های کد را بر روی دستگاه شما کامپایل و اجرا می‌کند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n برای اجرای تور به صورت محلی، ابتدا باید \u003ca href=\"https://go.dev/doc/install\" target=\"_blank\"\u003eGo را نصب کنید\u003c/a\u003e و سپس دستور زیر را اجرا کنید:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003ego install github.com/ardanlabs/gotour/cmd/tour@latest\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این دستور یک فایل اجرایی تور را در دایرکتوری bin مربوط به \u003ca href=\"https://go.dev/cmd/go/#hdr-GOPATH_and_Modules\" target=\"_blank\"\u003eGOPATH\u0026#39;s\u003c/a\u003e قرار می‌دهد. هنگامی که برنامه تور را اجرا می‌کنید، یک مرورگر وب با نسخه محلی تور نمایش داده می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n بدون شک، می‌توانید ادامه تور را از طریق این وب‌سایت ادامه دهید.\n \u003c/p\u003e\n \n\n","Files":[]},{"Title":"زمین بازی گو","Content":"\n \u003ch2\u003eزمین بازی گو\u003c/h2\u003e\n \n\t\n\t\t\n\t\n\n \n \u003cp\u003e\n این تور بر پایه \u003ca href=\"https://play.golang.org/\" target=\"_blank\"\u003eGo Playground\u003c/a\u003e ساخته شده است، یک سرویس وب که بر روی سرورهای \u003ca href=\"https://go.dev/\" target=\"_blank\"\u003egolang.org\u003c/a\u003e اجرا می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n این سرویس یک برنامه Go را دریافت می‌کند، برنامه را کامپایل، لینک می‌کند و برنامه را در یک محیط ایمن اجرا می‌کند، سپس نتیجه را باز می‌گرداند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n محدودیت‌هایی برای برنامه‌های قابل اجرا در Playground وجود دارد:\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n در Playground زمان از تاریخ 2009-11-10 23:00:00 UTC شروع می‌شود (تعیین معنی این تاریخ یک تمرین برای خواننده است). این باعث تسهیل در حافظه‌پنهان‌سازی برنامه‌ها با خروجی مشخص می‌شود.\n\n\n همچنین محدودیت‌هایی برای زمان اجرا، مصرف CPU و حافظه وجود دارد و برنامه قادر به دسترسی به میزبان‌های شبکه خارجی نیست.\n\n\n Playground از آخرین نسخه پایدار Go استفاده می‌کند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n برای مطالعه بیشتر، \u0026#34;\u003ca href=\"https://go.dev/blog/playground\" target=\"_blank\"\u003eGo Playground\u003c/a\u003e\u0026#34; را بخوانید.\n \u003c/p\u003e\n \n\n","Files":[{"Name":"sandbox.go","Content":"package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc main() {\n\tfmt.Println(\"Welcome to the playground!\")\n\n\tfmt.Println(\"The time is\", time.Now())\n}\n","Hash":"bOpKtZ8cTKG+qhINnOZDcJhhNQc="}]},{"Title":"مشارکت در پروژه","Content":"\n \u003ch2\u003eمشارکت در پروژه\u003c/h2\u003e\n \n \n \u003cp\u003e\n \u003cb\u003eچگونه\u003c/b\u003e \u003cb\u003eشروع\u003c/b\u003e \u003cb\u003eکنیم\u003c/b\u003e\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n اگر علاقه‌مند به افزودن بخش‌ها یا محتواهای بیشتر به پروژه هستید، لطفاً یک \u0026#34;issue\u0026#34; (موضوع) به مخزن (repo) اضافه کنید. فقط یک ایده کلی برای محتوایی که می‌خواهید اضافه کنید و دلیل اضافه کردن آن را ارائه دهید. اگر دارید یک مثال کد دارید، لطفاً به اشتراک بگذارید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u003ca href=\"https://github.com/ardanlabs/gotour\" target=\"_blank\"\u003ehttps://github.com/ardanlabs/gotour\u003c/a\u003e\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n ما موضوع را بررسی خواهیم کرد و اگر تایید شود، می‌توانید یک درخواست Pull (PR) ارائه دهید. ما علاقه داریم که مثال‌ها و موضوعات بیشتری در زمینه الگوریتم‌ها داشته باشیم.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u003cb\u003eزبانهای\u003c/b\u003e \u003cb\u003eجدید\u003c/b\u003e\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n ما همچنین دوست داریم که محتوا به زبان‌های مختلف ترجمه شود. اگر علاقه‌مند هستید، لطفاً یک موضوع (issue) ایجاد کنید. ما می‌توانیم به عنوان پاداش برای این تلاش پاداشی ارائه دهیم.\n \u003c/p\u003e\n \n\n","Files":[]},{"Title":"درباره آزمایشگاه آردان","Content":"\n \u003ch2\u003eدرباره آزمایشگاه آردان\u003c/h2\u003e\n \n \n \u003cp\u003e\n \u003cb\u003eمشاوره،\u003c/b\u003e \u003cb\u003eآموزش،\u003c/b\u003e \u003cb\u003eاستخدام\u003c/b\u003e \u003cb\u003eو\u003c/b\u003e \u003cb\u003eتوسعه\u003c/b\u003e \u003cb\u003eنرم‌افزار\u003c/b\u003e \u003cb\u003eبا\u003c/b\u003e \u003cb\u003eعملکرد\u003c/b\u003e \u003cb\u003eبالا\u003c/b\u003e\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n اگر به دنبال کمک در زمینه Go، Rust، Kubernetes، Terraform یا فناوری‌های مرتبط با بلاک‌چین هستید، Ardan Labs شریک استراتژیک شما برای راه‌حل‌های نرم‌افزاری با عملکرد بالاست.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n درباره رویدادهای آموزشی شرکتی، جلسات آموزش آنلاین باز و گزینه‌های یادگیری درخواست بدهید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n آزمایشگاه آردان (\u003ca href=\"https://www.ardanlabs.com\" target=\"_blank\"\u003ewww.ardanlabs.com\u003c/a\u003e)\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n ایمیل: \u003ca href=\"mailto:hello@ardanlabs.com\" target=\"_blank\"\u003ehello@ardanlabs.com\u003c/a\u003e\n\n\n برای شرکت در هر یک از دوره‌های آموزشی با عملکرد بالا ما به این لینک زیر مراجعه کنید:\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u003ca href=\"https://www.ardanlabs.com/training\" target=\"_blank\"\u003ewww.ardanlabs.com/training\u003c/a\u003e\n \u003c/p\u003e\n \n\n \u003ch2\u003eمتریال بیشتر\u003c/h2\u003e\n \n \n \u003cp\u003e\n تمام مواد دوره \u0026#34;Ultimate Go\u0026#34; در این مخزن قابل دسترسی است:\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://github.com/ardanlabs/gotraining/blob/master/topics/courses/go/README.md\" target=\"_blank\"\u003eمخزن Ultimate Go\u003c/a\u003e\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n پیش از شروع به یادگیری زبان، به سند طراحی مراجعه کنید تا به شیوه درستی برای یادگیری زبان آماده شوید:\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://github.com/ardanlabs/gotraining/blob/master/topics/go/README.md\" target=\"_blank\"\u003eسند طراحی\u003c/a\u003e\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n این مخزن دارای موضوعات بیشتری است که در Go tour قابل اجرا نیستند، پس آنها را بررسی کنید وقتی که کار تمام شده است:\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://github.com/ardanlabs/gotraining/blob/master/topics/courses/go/tooling/README.md\" target=\"_blank\"\u003eتست، بنچمارکینگ و پروفایلینگ\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://github.com/ardanlabs/gotraining/blob/master/topics/courses/go/packages/README.md\" target=\"_blank\"\u003eپکیج‌های کتابخانه استاندارد\u003c/a\u003e\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eکتاب Ultimate Go Notebook\u003c/h2\u003e\n \n \n \u003cp\u003e\n همه این متریال ها و بیشتر چیزهایی که می خواهید در کتاب Ultimate Go Notebook وجود دارد.\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/ultimate-go-notebook/\" target=\"_blank\"\u003eUltimate Go Notebook\u003c/a\u003e\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eکمک خواستن\u003c/h2\u003e\n \n \n \u003cp\u003e\n اگر مشکلات یا سوالاتی دارید، تردید نکنید که با بیل کندی در Ardan Labs تماس بگیرید. او قادر خواهد بود به شما کمک و پشتیبانی مورد نیاز را ارائه دهد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u003ca href=\"mailto:bill@ardanlabs.com\" target=\"_blank\"\u003ebill@ardanlabs.com\u003c/a\u003e\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eتجربه ما\u003c/h2\u003e\n \n \n \u003cp\u003e\n از سال ۲۰۱۴ به هزاران توسعه‌دهنده در سراسر جهان Go آموزش داده‌ایم. هیچ شرکت دیگری وجود ندارد که این کار را بیشتر از ما انجام داده باشد و مطالب ما ثابت کرده که توانایی توسعه‌دهندگان را ۶ تا ۱۲ ماه بهبود می‌بخشد. ما می‌دانیم که توسعه‌دهندگان چه دانشی برای تولید نرم‌افزار به صورت موثر و کارآمد در Go نیاز دارند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n دوره‌های ما برای توسعه‌دهندگان میانی با حداقل چند ماه تا چند سال تجربه نوشتن کد در Go مناسب هستند. دوره‌های ما دانش عمیقی از زبان برنامه‌نویسی را با تأکید بزرگ بر مکانیک زبان، فلسفه‌ها و راهنماهای طراحی فراهم می‌کنند. ما تمرکز خود را بر آموزش نحوه نوشتن کد با تأکید بر انطباق، اصالت، خوانائی و سادگی قرار داده‌ایم. ما بسیاری از مسائل مرتبط با \u0026#34;اگر عملکرد مهم باشد\u0026#34; را پوشش داده و به مسائلی مانند همدلی مکانیکی، طراحی مبتنی بر داده، انزوا و نوشتن/اشکال‌دهی نرم‌افزارهای تولیدی توجه داریم.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eاستاد ما\u003c/h2\u003e\n \n \n \u003cp\u003e\n ویلیام کندی مدیر مشارکت‌کننده در شرکت آزمایشگاه آردان در میامی، فلوریدا است. آزمایشگاه آردان یک شرکت توسعه و آموزش با عملکرد بالا است که با شرکت‌های نوپا و شرکت‌های Fortune 500 همکاری می‌کند. وی همچنین هم‌نویس کتاب \u0026#34;Go in Action\u0026#34;، نویسنده وبلاگ GoingGo.Net و عضو بنیان‌گذار گوپلکس (GoBridge) است که به افزایش پذیرش زبان برنامه‌نویسی Go از طریق تنوع کمک می‌کند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u003cb\u003eآموزش\u003c/b\u003e \u003cb\u003eویدئویی\u003c/b\u003e\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://education.ardanlabs.com/\" target=\"_blank\"\u003eUltimate Go Video\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://youtube.ardanlabs.com/\" target=\"_blank\"\u003eArdan Labs YouTube Channel\u003c/a\u003e\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n \u003cb\u003eوبلاگ\u003c/b\u003e\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/\" target=\"_blank\"\u003eArdan Labs Technical Blog\u003c/a\u003e\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n \u003cb\u003eپادکست\u003c/b\u003e\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://ardanlabs.buzzsprout.com/\" target=\"_blank\"\u003eArdan Labs Podcast: On Going Series\u003c/a\u003e\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eاطلاعات بیشتر درباره گو\u003c/h2\u003e\n \n \n \u003cp\u003e\n Go یک زبان برنامه‌نویسی متن‌باز است که ساختن نرم‌افزارهای ساده، قابل اعتماد و کارآمد را آسان می‌کند. اگرچه از زبان‌های موجود ایده‌ها را اقتباس می‌کند، اما دارای طبیعت منحصر به فرد و ساده‌ای است که برنامه‌های Go را در مقایسه با برنامه‌های نوشته شده به زبان‌های دیگر متفاوت می‌کند. این زبان توازنی بین قابلیت‌های یک زبان سیستم با ویژگی‌های سطح بالاتری که در زبان‌های مدرن امروزی می‌بینیم، دارد. این محیط برنامه‌نویسی به شما اجازه می‌دهد که بسیار بهره‌ور، با عملکرد بالا و کاملاً در کنترل باشید؛ در Go، شما می‌توانید کمترین کد را بنویسید و کارهای بیشتری انجام دهید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n Go ترکیبی از عملکرد و بهره‌وری در یک زبان برنامه‌نویسی است که توسعه‌دهندگان نرم‌افزار می‌توانند آن را یاد بگیرند، استفاده کنند و درک کنند. Go متفاوت از C است، اما ما بسیاری از مزایای C را با مزایای زبان‌های برنامه‌نویسی سطح بالاتر داریم.\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://henvic.dev/posts/go/\" target=\"_blank\"\u003eاکوسیستم زبان برنامه‌نویسی Go - Henrique Vicente\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.infoq.com/presentations/go-concurrency-gc\" target=\"_blank\"\u003eدلایل استفاده از Go - Carmen Andoh\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://github.com/ardanlabs/gotraining#:~:text=Go%20Ten%20Years%20and%20Climbing\" target=\"_blank\"\u003eده سال گذشته از Go و رشد آن - Rob Pike\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://github.com/ardanlabs/gotraining#:~:text=The%20eigenvector%20of%20%22Why%20we%20moved%20from%20language%20X%20to%20language%20Y%22\" target=\"_blank\"\u003eویژگی اصلی \u0026#34;چرا از زبان X به زبان Y منتقل شدیم\u0026#34; - Erik Bernhardsson\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://talks.golang.org/2012/splash.article\" target=\"_blank\"\u003eبیشتر بیاموزید - تیم Go\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://github.com/ardanlabs/gotraining#:~:text=Simplicity%20is%20Complicated\" target=\"_blank\"\u003eشروع کردن در Go - Aarti Parikh\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"http://aarti.github.io/2016/08/13/getting-started-in-go\" target=\"_blank\"\u003eGetting Started In Go - Aarti Parikh\u003c/a\u003e\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eخواندن مهم\u003c/h2\u003e\n \n \n \u003cp\u003e\n لطفاً این صفحه مهم \u003ca href=\"https://github.com/ardanlabs/gotraining/blob/master/reading/README.md\" target=\"_blank\"\u003eخواندن مهم\u003c/a\u003e را بررسی کنید. در اینجا مقالات و ویدئوهایی در مورد همدلی مکانیکی، طراحی مبتنی بر داده، زمان اجرای Go و بهینه‌سازی‌ها و مقالاتی در مورد تاریخ محاسبات قرار دارد.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eپیوستن به جامعه گو در Slack\u003c/h2\u003e\n \n \n \u003cp\u003e\n ما از یک کانال Slack برای به اشتراک گذاشتن پیوندها، کد‌ها و مثال‌ها در طول دوره آموزشی استفاده می‌کنیم. این خدمت رایگان است. همچنین این ارتباط با این کانال Slack، کانال اجتماعی استفاده شده در پس از دوره آموزشی برای پرسش از کمک و تعامل با اساتید Go در سرتاسر جهان در این جامعه خواهد بود.\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003eبا استفاده از پیوند زیر، نام و آدرس ایمیل خود را وارد کنید: \u003ca href=\"https://invite.slack.gobridge.org/\" target=\"_blank\"\u003ehttps://invite.slack.gobridge.org\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003eایمیل خود را بررسی کنید و به پیوند ارسال شده به برنامه Slack پیروی کنید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n","Files":[]}]} ,"channels":{"Title":"کانال‌ها","Description":"کانال‌ها به Goroutine‌ها اجازه می‌دهند تا از طریق استفاده از معنای سیگنالینگ با یکدیگر ارتباط برقرار کنند.","Pages":[{"Title":"کانال ها","Content":"\n \u003ch2\u003eکانال ها\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/training/individual-on-demand/ultimate-go-bundle/\" target=\"_blank\"\u003eتماشای ویدئو\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003eنیاز به کمک مالی دارید؟ از \u003ca href=\"https://www.ardanlabs.com/scholarship/\" target=\"_blank\"\u003eفرم Scholarship\u003c/a\u003e ما استفاده کنید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n مهم است که کانال را نه به عنوان یک ساختار داده، بلکه به عنوان یک مکانیزم برای سیگنالینگ در نظر بگیرید. این مطابق با این ایده است که شما از یک کانال ارسال و دریافت می‌کنید، نه خواندن و نوشتن. اگر مشکلی که پیش روی شماست نمی‌تواند با سیگنالینگ حل شود، اگر کلمه سیگنالینگ از دهان شما بیرون نمی‌آید، باید استفاده از کانال‌ها را زیر سوال ببرید.\n \u003c/p\u003e\n \n\n \u003ch2\u003eبررسی کد\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e1:\u003c/b\u003e Wait for result\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e2:\u003c/b\u003e Fan out\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e3:\u003c/b\u003e Wait for task\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e4:\u003c/b\u003e Pooling\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e5:\u003c/b\u003e Fan out semaphore\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e6:\u003c/b\u003e Bounded work pooling\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e7:\u003c/b\u003e Drop\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e8:\u003c/b\u003e Cancellation\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e9:\u003c/b\u003e Retry timeout\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e10:\u003c/b\u003e Channel cancellation\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n \u003ch2\u003eمکانیزم های کانال\u003c/h2\u003e\n \n \n \u003cp\u003e\n هزینه داشتن ضمانت در سطح سیگنالینگ تاخیر ناشناخته است. فرستنده نمی داند چقدر باید منتظر بماند تا گیرنده سیگنال را بپذیرد. مجبور شدن به انتظار برای گیرنده، تاخیر مسدودکننده ایجاد می کند. در این مورد، مقادیر ناشناخته تاخیر مسدودکننده. فرستنده باید برای مدت زمان نامعلومی منتظر بماند تا گیرنده برای دریافت سیگنال در دسترس قرار گیرد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n انتظار برای گیرنده به معنای مکانیکی، عملیات دریافت قبل از ارسال اتفاق می افتد. با کانال ها، دریافت نانو ثانیه قبل اتفاق می افتد، اما قبل از آن است. این بدان معنی است که گیرنده سیگنال را می گیرد و سپس دور می شود و به فرستنده اجازه می دهد تا با ضمانت پیش برود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n چه اتفاقی می‌افتد اگر فرآیند نتواند برای مدت زمان نامعلومی منتظر بماند؟ اگر آن نوع تاخیر کار نکند چه می شود؟ سپس ضمانت نمی تواند در سطح سیگنالینگ باشد، باید خارج از آن باشد. مکانیک پشت این کار این است که ارسال اکنون قبل از دریافت اتفاق می افتد. فرستنده می تواند سیگنال را بدون نیاز به در دسترس بودن گیرنده انجام دهد. بنابراین فرستنده می تواند دور شود و منتظر نماند. در نهایت، امیدوارید، گیرنده ظاهر شود و سیگنال را دریافت کند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n این کاهش هزینه تاخیر در ارسال است، اما عدم اطمینان در مورد دریافت سیگنال ها و در نتیجه دانستن اینکه آیا در بالادست گیرنده ها مشکلی وجود دارد یا خیر، ایجاد می کند. این می تواند باعث شود فرآیند کاری را بپذیرد که هرگز شروع یا تمام نشده است. در نهایت، می تواند باعث فشار پس زمینه عظیم و خرابی سیستم ها شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n دومین چیزی که باید روی آن تمرکز کنید این است که آیا برای ارسال سیگنال نیاز به ارسال داده دارید؟ اگر سیگنال نیاز به انتقال داده داشته باشد، سیگنالینگ 1 به 1 بین Goroutines است. اگر Goroutine جدیدی نیز نیاز به دریافت سیگنال داشته باشد، باید سیگنال دوم ارسال شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n اگر نیازی به انتقال داده با سیگنال نیست، سیگنال می تواند 1 به 1 یا 1 به چند بین Goroutines باشد. سیگنالینگ بدون داده در درجه اول برای لغو یا خاموشی استفاده می شود. این کار با بستن کانال انجام می شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n سومین چیزی که باید روی آن تمرکز کنید وضعیت کانال است. یک کانال می تواند در یکی از 3 حالت باشد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n یک کانال می تواند در حالت nil با ساختن کانال به حالت مقدار صفر آن باشد. ارسال و دریافت در برابر کانال ها در این حالت مسدود می شود. این برای موقعیت هایی مناسب است که می خواهید توقف های کوتاه مدت کار را پیاده سازی کنید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n یک کانال می تواند در حالت باز با استفاده از تابع make داخلی باشد. ارسال و دریافت در برابر کانال ها در این حالت تحت شرایط زیر کار می کند:\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n کانال های بدون بافر:\n\n\n ضمانت ها در سطح سیگنالینگ با دریافت قبل از ارسال. ارسال و دریافت Goroutines باید در یک مکان و زمان مشابه برای پردازش سیگنال گرد هم آیند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n کانال های بافر شده:\n\n\n ضمانت ها خارج از سطح سیگنالینگ با ارسال قبل از دریافت. اگر بافر پر نباشد، ارسال ها می توانند کامل شوند در غیر این صورت مسدود می شوند. اگر بافر خالی نباشد، دریافت ها می توانند کامل شوند در غیر این صورت مسدود می شوند.\n\n\n یک کانال می تواند در حالت بسته با استفاده از تابع close داخلی باشد. برای آزاد کردن حافظه نیازی به بستن کانال ندارید، این برای تغییر وضعیت است. ارسال در کانال بسته باعث وحشت می شود، اما دریافت در کانال بسته بلافاصله بازگشت می کند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n با تمام این اطلاعات، می توانید بر روی الگوهای کانال تمرکز کنید. تمرکز بر روی سیگنالینگ مهم است. ایده این است که آیا بر اساس نگرانی های تاخیر به ضمانت در سطح سیگنالینگ نیاز دارید یا خیر. اگر بر اساس مدیریت لغوها یا عدم آن، به انتقال داده با سیگنال نیاز دارید یا خیر. می خواهید نحو را به این معناشناسی تبدیل کنید.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eراهنماهای طراحی\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003eاطلاعات بیشتر در \u003ca href=\"https://github.com/ardanlabs/gotraining/blob/master/topics/go/#channel-design\" target=\"_blank\"\u003edesign guidelines\u003c/a\u003e for channels.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eدیاگرام ها\u003c/h2\u003e\n \n \n \u003cp\u003e\n \u003cb\u003eضمانت\u003c/b\u003e \u003cb\u003eتحویل\u003c/b\u003e\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n ضمانت تحویل بر اساس یک سوال است: «آیا به ضمانتی نیاز دارم که سیگنال ارسال شده توسط یک goroutine خاص دریافت شده باشد؟»\n \u003c/p\u003e\n \n\n \u003ca href=\"/tour/eng/static/img/guarantee_of_delivery.png\" target=\"_blank\"\u003e\n \u003cimg class=\"codeimg\" src=\"/tour/eng/static/img/guarantee_of_delivery.png\"\u003e\n \u003c/a\u003e\n\n \n \u003cp\u003e\n \u003cb\u003eسیگانلینگ\u003c/b\u003e \u003cb\u003eبا\u003c/b\u003e \u003cb\u003eیا\u003c/b\u003e \u003cb\u003eبدون\u003c/b\u003e \u003cb\u003eداده\u003c/b\u003e\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n زمانی که می‌خواهید با داده سیگنال ارسال کنید، سه گزینه پیکربندی کانال وجود دارد که می‌توانید بسته به نوع ضمانتی که نیاز دارید انتخاب کنید.\n \u003c/p\u003e\n \n\n \u003ca href=\"/tour/eng/static/img/signaling_with_data.png\" target=\"_blank\"\u003e\n \u003cimg class=\"codeimg\" src=\"/tour/eng/static/img/signaling_with_data.png\"\u003e\n \u003c/a\u003e\n\n \n \u003cp\u003e\n سیگنالینگ بدون داده هدف اصلی لغو را برآورده می کند. این امکان را برای یک goroutine فراهم می کند تا به goroutine دیگری سیگنال دهد تا کاری را که انجام می دهد لغو کند و ادامه دهد. لغو را می توان با استفاده از کانال های بدون بافر و بافر شده پیاده سازی کرد.\n \u003c/p\u003e\n \n\n \u003ca href=\"/tour/eng/static/img/signaling_without_data.png\" target=\"_blank\"\u003e\n \u003cimg class=\"codeimg\" src=\"/tour/eng/static/img/signaling_without_data.png\"\u003e\n \u003c/a\u003e\n\n \n \u003cp\u003e\n \u003cb\u003eحالت\u003c/b\u003e\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n رفتار یک کانال به طور مستقیم تحت تأثیر وضعیت فعلی آن است. وضعیت یک کانال می تواند \u003ccode\u003enil\u003c/code\u003e, \u003ccode\u003eopen\u003c/code\u003e یا \u003ccode\u003eclosed\u003c/code\u003e باشد.\n \u003c/p\u003e\n \n\n \u003ca href=\"/tour/eng/static/img/state.png\" target=\"_blank\"\u003e\n \u003cimg class=\"codeimg\" src=\"/tour/eng/static/img/state.png\"\u003e\n \u003c/a\u003e\n\n\n \u003ch2\u003eExtra Reading\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2017/10/the-behavior-of-channels.html\" target=\"_blank\"\u003eThe Behavior Of Channels\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://golang.org/ref/mem#tmp_7\" target=\"_blank\"\u003eChannel Communication\u003c/a\u003e \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://blog.golang.org/share-memory-by-communicating\" target=\"_blank\"\u003eShare Memory By Communicating\u003c/a\u003e - Andrew Gerrand \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2014/02/the-nature-of-channels-in-go.html\" target=\"_blank\"\u003eThe Nature Of Channels In Go\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"http://matt-welsh.blogspot.com/2010/07/retrospective-on-seda.html\" target=\"_blank\"\u003eA Retrospective on SEDA\u003c/a\u003e - Matt Welsh \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.youtube.com/watch?v=KBZlN0izeiY\" target=\"_blank\"\u003eUnderstanding Channels\u003c/a\u003e - Kavya Joshi \u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eBuffer Bloat - 2011\u003c/h2\u003e\n \n \n \u003cp\u003e\n مراقب استفاده از بافرهای بزرگ با ایده کاهش تاخیر باشید.\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003eبافرهای بزرگ از اطلاع رسانی به موقع فشار پس زمینه جلوگیری می کنند.\u003c/li\u003e\n \n \u003cli\u003eآنها توانایی شما را برای کاهش به موقع فشار پس زمینه از بین می برند.\u003c/li\u003e\n \n \u003cli\u003eآنها می توانند تاخیر را افزایش دهند و آن را کاهش ندهند.\u003c/li\u003e\n \n \u003cli\u003eاز کانال های بافر شده برای حفظ پیوستگی استفاده کنید.\u003c/li\u003e\n \n \u003cli\u003eاز آنها فقط برای عملکرد استفاده نکنید.\u003c/li\u003e\n \n \u003cli\u003eاز آنها برای رسیدگی به انفجارهای داده به خوبی تعریف شده استفاده کنید.\u003c/li\u003e\n \n \u003cli\u003eاز آنها برای مقابله با مسائل سرعت نور بین انتقالات استفاده کنید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n \u003cb\u003eویدئوها\u003c/b\u003e\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.youtube.com/watch?v=qbIozKVz73g\" target=\"_blank\"\u003eBufferbloat: Dark Buffers in the Internet\u003c/a\u003e \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"http://www.bufferbloat.net/projects/cerowrt/wiki/Bloat-videos\" target=\"_blank\"\u003eBuffer Bloat Videos\u003c/a\u003e \u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n","Files":[{"Name":"example1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This sample program demonstrates the wait for result channel pattern.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"time\"\n)\n\nfunc main() {\n\twaitForResult()\n}\n\n// waitForResult: In this pattern, the parent goroutine waits for the child\n// goroutine to finish some work to signal the result.\nfunc waitForResult() {\n\tch := make(chan string)\n\n\tgo func() {\n\t\ttime.Sleep(time.Duration(rand.Intn(500)) * time.Millisecond)\n\t\tch \u003c- \"data\"\n\t\tfmt.Println(\"child : sent signal\")\n\t}()\n\n\td := \u003c-ch\n\tfmt.Println(\"parent : recv'd signal :\", d)\n\n\ttime.Sleep(time.Second)\n\tfmt.Println(\"-------------------------------------------------\")\n}\n","Hash":"VlmpG8G/hHkOV2tzgehEFXkii0A="},{"Name":"example2.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This sample program demonstrates the fan out channel pattern.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"time\"\n)\n\nfunc main() {\n\tfanOut()\n}\n\n// fanOut: In this pattern, the parent goroutine creates 2000 child goroutines\n// and waits for them to signal their results.\nfunc fanOut() {\n\tchildren := 2000\n\tch := make(chan string, children)\n\n\tfor c := 0; c \u003c children; c++ {\n\t\tgo func(child int) {\n\t\t\ttime.Sleep(time.Duration(rand.Intn(200)) * time.Millisecond)\n\t\t\tch \u003c- \"data\"\n\t\t\tfmt.Println(\"child : sent signal :\", child)\n\t\t}(c)\n\t}\n\n\tfor children \u003e 0 {\n\t\td := \u003c-ch\n\t\tchildren--\n\t\tfmt.Println(d)\n\t\tfmt.Println(\"parent : recv'd signal :\", children)\n\t}\n\n\ttime.Sleep(time.Second)\n\tfmt.Println(\"-------------------------------------------------\")\n}\n","Hash":"i9Whe27FA3vNr66vDb9O2feIbt8="},{"Name":"example3.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This sample program demonstrates the wait for task channel pattern.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"time\"\n)\n\nfunc main() {\n\twaitForTask()\n}\n\n// waitForTask: In this pattern, the parent goroutine sends a signal to a\n// child goroutine waiting to be told what to do.\nfunc waitForTask() {\n\tch := make(chan string)\n\n\tgo func() {\n\t\td := \u003c-ch\n\t\tfmt.Println(\"child : recv'd signal :\", d)\n\t}()\n\n\ttime.Sleep(time.Duration(rand.Intn(500)) * time.Millisecond)\n\tch \u003c- \"data\"\n\tfmt.Println(\"parent : sent signal\")\n\n\ttime.Sleep(time.Second)\n\tfmt.Println(\"-------------------------------------------------\")\n}\n","Hash":"ChPf/rRJZX5gh/AY8N/s8m7wnLc="},{"Name":"example4.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This sample program demonstrates the pooling channel pattern.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n\t\"time\"\n)\n\nfunc main() {\n\tpooling()\n}\n\n// pooling: In this pattern, the parent goroutine signals 100 pieces of work\n// to a pool of child goroutines waiting for work to perform.\nfunc pooling() {\n\tch := make(chan string)\n\n\tg := runtime.GOMAXPROCS(0)\n\tfor c := 0; c \u003c g; c++ {\n\t\tgo func(child int) {\n\t\t\tfor d := range ch {\n\t\t\t\tfmt.Printf(\"child %d : recv'd signal : %s\\n\", child, d)\n\t\t\t}\n\t\t\tfmt.Printf(\"child %d : recv'd shutdown signal\\n\", child)\n\t\t}(c)\n\t}\n\n\tconst work = 100\n\tfor w := 0; w \u003c work; w++ {\n\t\tch \u003c- \"data\"\n\t\tfmt.Println(\"parent : sent signal :\", w)\n\t}\n\n\tclose(ch)\n\tfmt.Println(\"parent : sent shutdown signal\")\n\n\ttime.Sleep(time.Second)\n\tfmt.Println(\"-------------------------------------------------\")\n}\n","Hash":"rq6cw/JialxY4RSB8XT5V1Rnr3c="},{"Name":"example5.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This sample program demonstrates the fan out semaphore channel pattern.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"runtime\"\n\t\"time\"\n)\n\nfunc main() {\n\tfanOutSem()\n}\n\n// fanOutSem: In this pattern, a semaphore is added to the fan out pattern\n// to restrict the number of child goroutines that can be schedule to run.\nfunc fanOutSem() {\n\tchildren := 2000\n\tch := make(chan string, children)\n\n\tg := runtime.GOMAXPROCS(0)\n\tsem := make(chan bool, g)\n\n\tfor c := 0; c \u003c children; c++ {\n\t\tgo func(child int) {\n\t\t\tsem \u003c- true\n\t\t\t{\n\t\t\t\tt := time.Duration(rand.Intn(200)) * time.Millisecond\n\t\t\t\ttime.Sleep(t)\n\t\t\t\tch \u003c- \"data\"\n\t\t\t\tfmt.Println(\"child : sent signal :\", child)\n\t\t\t}\n\t\t\t\u003c-sem\n\t\t}(c)\n\t}\n\n\tfor children \u003e 0 {\n\t\td := \u003c-ch\n\t\tchildren--\n\t\tfmt.Println(d)\n\t\tfmt.Println(\"parent : recv'd signal :\", children)\n\t}\n\n\ttime.Sleep(time.Second)\n\tfmt.Println(\"-------------------------------------------------\")\n}\n","Hash":"IpDd6Fq56OGiBYtCXkeY0TySNYQ="},{"Name":"example6.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This sample program demonstrates the bounded work pooling channel pattern.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n\t\"sync\"\n\t\"time\"\n)\n\nfunc main() {\n\tboundedWorkPooling()\n}\n\n// boundedWorkPooling: In this pattern, a pool of child goroutines is created\n// to service a fixed amount of work. The parent goroutine iterates over all\n// work, signalling that into the pool. Once all the work has been signaled,\n// then the channel is closed, the channel is flushed, and the child\n// goroutines terminate.\nfunc boundedWorkPooling() {\n\twork := []string{\"paper\", \"paper\", \"paper\", \"paper\", 2000: \"paper\"}\n\n\tg := runtime.GOMAXPROCS(0)\n\tvar wg sync.WaitGroup\n\twg.Add(g)\n\n\tch := make(chan string, g)\n\n\tfor c := 0; c \u003c g; c++ {\n\t\tgo func(child int) {\n\t\t\tdefer wg.Done()\n\t\t\tfor wrk := range ch {\n\t\t\t\tfmt.Printf(\"child %d : recv'd signal : %s\\n\", child, wrk)\n\t\t\t}\n\t\t\tfmt.Printf(\"child %d : recv'd shutdown signal\\n\", child)\n\t\t}(c)\n\t}\n\n\tfor _, wrk := range work {\n\t\tch \u003c- wrk\n\t}\n\tclose(ch)\n\twg.Wait()\n\n\ttime.Sleep(time.Second)\n\tfmt.Println(\"-------------------------------------------------\")\n}\n","Hash":"hgBLIhRwJw3E1tYLi30Kxf88YK0="},{"Name":"example7.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This sample program demonstrates the drop channel pattern.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc main() {\n\tdrop()\n}\n\n// drop: In this pattern, the parent goroutine signals 2000 pieces of work to\n// a single child goroutine that can't handle all the work. If the parent\n// performs a send and the child is not ready, that work is discarded and dropped.\nfunc drop() {\n\tconst cap = 100\n\tch := make(chan string, cap)\n\n\tgo func() {\n\t\tfor p := range ch {\n\t\t\tfmt.Println(\"child : recv'd signal :\", p)\n\t\t}\n\t}()\n\n\tconst work = 2000\n\tfor w := 0; w \u003c work; w++ {\n\t\tselect {\n\t\tcase ch \u003c- \"data\":\n\t\t\tfmt.Println(\"parent : sent signal :\", w)\n\t\tdefault:\n\t\t\tfmt.Println(\"parent : dropped data :\", w)\n\t\t}\n\t}\n\n\tclose(ch)\n\tfmt.Println(\"parent : sent shutdown signal\")\n\n\ttime.Sleep(time.Second)\n\tfmt.Println(\"-------------------------------------------------\")\n}\n","Hash":"BXPXIBRkQQ4xg01Cx7KkcRNwwjM="},{"Name":"example8.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This sample program demonstrates the cancellation channel pattern.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"math/rand\"\n\t\"time\"\n)\n\nfunc main() {\n\tcancellation()\n}\n\n// cancellation: In this pattern, the parent goroutine creates a child\n// goroutine to perform some work. The parent goroutine is only willing to\n// wait 150 milliseconds for that work to be completed. After 150 milliseconds\n// the parent goroutine walks away.\nfunc cancellation() {\n\tduration := 150 * time.Millisecond\n\tctx, cancel := context.WithTimeout(context.Background(), duration)\n\tdefer cancel()\n\n\tch := make(chan string, 1)\n\n\tgo func() {\n\t\ttime.Sleep(time.Duration(rand.Intn(200)) * time.Millisecond)\n\t\tch \u003c- \"data\"\n\t}()\n\n\tselect {\n\tcase d := \u003c-ch:\n\t\tfmt.Println(\"work complete\", d)\n\n\tcase \u003c-ctx.Done():\n\t\tfmt.Println(\"work cancelled\")\n\t}\n\n\ttime.Sleep(time.Second)\n\tfmt.Println(\"-------------------------------------------------\")\n}\n","Hash":"FO1T2zSNxBlkefSUkeEB1EIv4bg="},{"Name":"example9.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This sample program demonstrates the retry timeout channel pattern.\npackage main\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc main() {\n\tctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n\tdefer cancel()\n\n\tretryTimeout(ctx, time.Second, func(ctx context.Context) error { return errors.New(\"always fail\") })\n}\n\n// retryTimeout: You need to validate if something can be done with no error\n// but it may take time before this is true. You set a retry interval to create\n// a delay before you retry the call and you use the context to set a timeout.\nfunc retryTimeout(ctx context.Context, retryInterval time.Duration, check func(ctx context.Context) error) {\n\n\tfor {\n\t\tfmt.Println(\"perform user check call\")\n\t\tif err := check(ctx); err == nil {\n\t\t\tfmt.Println(\"work finished successfully\")\n\t\t\treturn\n\t\t}\n\n\t\tfmt.Println(\"check if timeout has expired\")\n\t\tif ctx.Err() != nil {\n\t\t\tfmt.Println(\"time expired 1 :\", ctx.Err())\n\t\t\treturn\n\t\t}\n\n\t\tfmt.Printf(\"wait %s before trying again\\n\", retryInterval)\n\t\tt := time.NewTimer(retryInterval)\n\n\t\tselect {\n\t\tcase \u003c-ctx.Done():\n\t\t\tfmt.Println(\"timed expired 2 :\", ctx.Err())\n\t\t\tt.Stop()\n\t\t\treturn\n\t\tcase \u003c-t.C:\n\t\t\tfmt.Println(\"retry again\")\n\t\t}\n\t}\n}\n","Hash":"90t6SK9TVDJp/+U95oN0aYoa3Kw="},{"Name":"example10.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This sample program demonstrates the channel cancellation channel pattern.\npackage main\n\nimport (\n\t\"context\"\n\t\"net/http\"\n)\n\nfunc main() {\n\tstop := make(chan struct{})\n\n\tchannelCancellation(stop)\n}\n\n// channelCancellation shows how you can take an existing channel being\n// used for cancellation and convert that into using a context where\n// a context is needed.\nfunc channelCancellation(stop \u003c-chan struct{}) {\n\n\t// Create a cancel context for handling the stop signal.\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\t// If a signal is received on the stop channel, cancel the\n\t// context. This will propagate the cancel into the p.Run\n\t// function below.\n\tgo func() {\n\t\tselect {\n\t\tcase \u003c-stop:\n\t\t\tcancel()\n\t\tcase \u003c-ctx.Done():\n\t\t}\n\t}()\n\n\t// Imagine a function that is performing an I/O operation that is\n\t// cancellable.\n\tfunc(ctx context.Context) error {\n\t\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, \"https://www.ardanlabs.com/blog/index.xml\", nil)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_, err = http.DefaultClient.Do(req)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t}(ctx)\n}\n","Hash":"Qg0BSL8H0kH81jZrPkyhThwj+5I="}]},{"Title":"تمرینات","Content":"\n \u003ch2\u003eتمرینات\u003c/h2\u003e\n \n \n \u003cp\u003e\n از الگو به عنوان نقطه شروع برای تکمیل تمرین ها استفاده کنید. یک راه حل ممکن ارائه شده است.\n \u003c/p\u003e\n \n\n \u003ch2\u003eتمرین 1\u003c/h2\u003e\n \n \n \u003cp\u003e\n یک برنامه بنویسید که در آن دو goroutine ده بار یک عدد صحیح را به جلو و عقب منتقل می کنند. نمایش زمانی که هر goroutine عدد صحیح را دریافت می کند. عدد صحیح را با هر پاس افزایش دهید. هنگامی که عدد صحیح برابر با ده شد، برنامه را به طور کامل خاتمه دهید.\n \u003c/p\u003e\n \n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n \u003ch2\u003eتمرین 2\u003c/h2\u003e\n \n \n \u003cp\u003e\n یک برنامه بنویسید که از الگوی fan out برای تولید 100 عدد تصادفی به طور همزمان استفاده می کند.\n\n\n هر goroutine را طوری تنظیم کنید که یک عدد تصادفی تولید کند و آن عدد را از طریق یک کانال بافر شده به goroutine اصلی برگرداند. اندازه کانال بافر را طوری تنظیم کنید که هیچ ارسال هرگز مسدود نشود. بیش از آنچه نیاز دارید بافر اختصاص ندهید. goroutine اصلی را طوری تنظیم کنید که هر عدد تصادفی را که دریافت می کند نمایش دهد و سپس برنامه را خاتمه دهد.\n \u003c/p\u003e\n \n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n \u003ch2\u003eتمرین 3\u003c/h2\u003e\n \n \n \u003cp\u003e\n یک برنامه بنویسید که تا 100 عدد تصادفی را به طور همزمان تولید می کند. همه 100 مقدار را ارسال نکنید تا تعداد ارسال/دریافت ها نامشخص باشد.\n \u003c/p\u003e\n \n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n \u003ch2\u003eتمرین 4\u003c/h2\u003e\n \n \n \u003cp\u003e\n یک برنامه بنویسید که تا 100 عدد تصادفی را به طور همزمان با استفاده از یک worker pool تولید می کند. مقادیر زوج را رد کنید. به کارگران دستور دهید که با جمع آوری 100 عدد فرد خاموش شوند.\n \u003c/p\u003e\n \n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n","Files":[{"Name":"exercise1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Write a program where two goroutines pass an integer back and forth\n// ten times. Display when each goroutine receives the integer. Increment\n// the integer with each pass. Once the integer equals ten, terminate\n// the program cleanly.\npackage main\n\n// Add imports.\n\nfunc main() {\n\n\t// Create an unbuffered channel.\n\n\t// Create the WaitGroup and add a count\n\t// of two, one for each goroutine.\n\n\t// Launch the goroutine and handle Done.\n\n\t// Launch the goroutine and handle Done.\n\n\t// Send a value to start the counting.\n\n\t// Wait for the program to finish.\n}\n\n// goroutine simulates sharing a value.\nfunc goroutine( /* parameters */ ) {\n\tfor {\n\n\t\t// Wait for the value to be sent.\n\t\t// If the channel was closed, return.\n\n\t\t// Display the value.\n\n\t\t// Terminate when the value is 10.\n\n\t\t// Increment the value and send it\n\t\t// over the channel.\n\t}\n}\n","Hash":"wHIisaEHI+s1a5140HbK9taZnF4="},{"Name":"answer1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Write a program where two goroutines pass an integer back and forth\n// ten times. Display when each goroutine receives the integer. Increment\n// the integer with each pass. Once the integer equals ten, terminate\n// the program cleanly.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n)\n\nfunc main() {\n\n\t// Create an unbuffered channel.\n\tshare := make(chan int)\n\n\t// Create the WaitGroup and add a count\n\t// of two, one for each goroutine.\n\tvar wg sync.WaitGroup\n\twg.Add(2)\n\n\t// Launch two goroutines.\n\tgo func() {\n\t\tgoroutine(\"Bill\", share)\n\t\twg.Done()\n\t}()\n\n\tgo func() {\n\t\tgoroutine(\"Joan\", share)\n\t\twg.Done()\n\t}()\n\n\t// Start the sharing.\n\tshare \u003c- 1\n\n\t// Wait for the program to finish.\n\twg.Wait()\n}\n\n// goroutine simulates sharing a value.\nfunc goroutine(name string, share chan int) {\n\tfor {\n\n\t\t// Wait to receive a value.\n\t\tvalue, ok := \u003c-share\n\t\tif !ok {\n\n\t\t\t// If the channel was closed, return.\n\t\t\tfmt.Printf(\"Goroutine %s Down\\n\", name)\n\t\t\treturn\n\t\t}\n\n\t\t// Display the value.\n\t\tfmt.Printf(\"Goroutine %s Inc %d\\n\", name, value)\n\n\t\t// Terminate when the value is 10.\n\t\tif value == 10 {\n\t\t\tclose(share)\n\t\t\tfmt.Printf(\"Goroutine %s Down\\n\", name)\n\t\t\treturn\n\t\t}\n\n\t\t// Increment the value and send it\n\t\t// over the channel.\n\t\tshare \u003c- (value + 1)\n\t}\n}\n","Hash":"cTRhf/5zDFPSHBvh6euG4nFGVE8="},{"Name":"exercise2.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Write a program that uses a fan out pattern to generate 100 random numbers\n// concurrently. Have each goroutine generate a single random number and return\n// that number to the main goroutine over a buffered channel. Set the size of\n// the buffered channel so no send ever blocks. Don't allocate more capacity\n// than you need. Have the main goroutine store each random number it receives\n// in a slice. Print the slice values then terminate the program.\npackage main\n\n// Add imports.\n\n// Declare constant for number of goroutines.\n\nfunc init() {\n\t// Seed the random number generator.\n}\n\nfunc main() {\n\n\t// Create the buffered channel with room for\n\t// each goroutine to be created.\n\n\t// Iterate and launch each goroutine.\n\t{\n\n\t\t// Create an anonymous function for each goroutine that\n\t\t// generates a random number and sends it on the channel.\n\t}\n\n\t// Create a variable to be used to track received messages.\n\t// Set the value to the number of goroutines created.\n\n\t// Iterate receiving each value until they are all received.\n\t// Store them in a slice of ints.\n\n\t// Print the values in our slice.\n}\n","Hash":"CRrdSwrGYzSv/p8ZixyEhoC745c="},{"Name":"answer2.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Write a program that uses a fan out pattern to generate 100 random numbers\n// concurrently. Have each goroutine generate a single random number and return\n// that number to the main goroutine over a buffered channel. Set the size of\n// the buffer channel so no send every blocks. Don't allocate more buffers than\n// you need. Have the main goroutine display each random number is receives and\n// then terminate the program.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n)\n\nconst (\n\tgoroutines = 100\n)\n\nfunc main() {\n\n\t// Create the buffer channel with a buffer for\n\t// each goroutine to be created.\n\tvalues := make(chan int, goroutines)\n\n\t// Iterate and launch each goroutine.\n\tfor gr := 0; gr \u003c goroutines; gr++ {\n\n\t\t// Create an anonymous function for each goroutine that\n\t\t// generates a random number and sends it on the channel.\n\t\tgo func() {\n\t\t\tvalues \u003c- rand.Intn(1000)\n\t\t}()\n\t}\n\n\t// Create a variable to be used to track received messages.\n\t// Set the value to the number of goroutines created.\n\twait := goroutines\n\n\t// Iterate receiving each value until they are all received.\n\t// Store them in a slice of ints.\n\tvar nums []int\n\tfor wait \u003e 0 {\n\t\tnums = append(nums, \u003c-values)\n\t\twait--\n\t}\n\n\t// Print the values in our slice.\n\tfmt.Println(nums)\n}\n","Hash":"6/yRcdk/gFmoqpm88tUBFTsW9Tw="},{"Name":"exercise3.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Write a program that uses goroutines to generate up to 100 random numbers.\n// Do not send values that are divisible by 2. Have the main goroutine receive\n// values and add them to a slice.\npackage main\n\n// Declare constant for number of goroutines.\nconst goroutines = 100\n\nfunc init() {\n\t// Seed the random number generator.\n}\n\nfunc main() {\n\n\t// Create the channel for sharing results.\n\n\t// Create a sync.WaitGroup to monitor the Goroutine pool. Add the count.\n\n\t// Iterate and launch each goroutine.\n\t{\n\n\t\t// Create an anonymous function for each goroutine.\n\t\t{\n\n\t\t\t// Ensure the waitgroup is decremented when this function returns.\n\n\t\t\t// Generate a random number up to 1000.\n\n\t\t\t// Return early if the number is even. (n%2 == 0)\n\n\t\t\t// Send the odd values through the channel.\n\t\t}\n\t}\n\n\t// Create a goroutine that waits for the other goroutines to finish then\n\t// closes the channel.\n\n\t// Receive from the channel until it is closed.\n\t// Store values in a slice of ints.\n\n\t// Print the values in our slice.\n}\n","Hash":"yiRyIkxw6MOZSea3uragZF33zVs="},{"Name":"answer3.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Write a program that uses goroutines to generate up to 100 random numbers.\n// Do not send values that are divisible by 2. Have the main goroutine receive\n// values and add them to a slice.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"sync\"\n)\n\nconst (\n\tgoroutines = 100\n)\n\nfunc main() {\n\n\t// Create the channel for sharing results.\n\tvalues := make(chan int)\n\n\t// Create a sync.WaitGroup to monitor the Goroutine pool. Add the count.\n\tvar wg sync.WaitGroup\n\twg.Add(goroutines)\n\n\t// Iterate and launch each goroutine.\n\tfor gr := 0; gr \u003c goroutines; gr++ {\n\n\t\t// Create an anonymous function for each goroutine.\n\t\tgo func() {\n\n\t\t\t// Ensure the waitgroup is decremented when this function returns.\n\t\t\tdefer wg.Done()\n\n\t\t\t// Generate a random number up to 1000.\n\t\t\tn := rand.Intn(1000)\n\n\t\t\t// Return early if the number is divisible by 2. n%2 == 0\n\t\t\tif n%2 == 0 {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Send the odd values through the channel.\n\t\t\tvalues \u003c- n\n\t\t}()\n\t}\n\n\t// Create a goroutine that waits for the other goroutines to finish then\n\t// closes the channel.\n\tgo func() {\n\t\twg.Wait()\n\t\tclose(values)\n\t}()\n\n\t// Receive from the channel until it is closed.\n\t// Store values in a slice of ints.\n\tvar nums []int\n\tfor n := range values {\n\t\tnums = append(nums, n)\n\t}\n\n\t// Print the values in our slice.\n\tfmt.Printf(\"Result count: %d\\n\", len(nums))\n\tfmt.Println(nums)\n}\n","Hash":"hmE+PyxgRxNB43Ym9oaHduNbLdA="},{"Name":"exercise4.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Write a program that creates a fixed set of workers to generate random\n// numbers. Discard any number divisible by 2. Continue receiving until 100\n// numbers are received. Tell the workers to shut down before terminating.\npackage main\n\n// Add imports.\n\nfunc main() {\n\n\t// Create the channel for sharing results.\n\n\t// Create a channel \"shutdown\" to tell goroutines when to terminate.\n\n\t// Define the size of the worker pool. Use runtime.GOMAXPROCS(0) to size the pool based on number of processors.\n\n\t// Create a sync.WaitGroup to monitor the Goroutine pool. Add the count.\n\n\t// Create a fixed size pool of goroutines to generate random numbers.\n\t{\n\t\t{\n\n\t\t\t// Start an infinite loop.\n\t\t\t{\n\n\t\t\t\t// Generate a random number up to 1000.\n\n\t\t\t\t// Use a select to either send the number or receive the shutdown signal.\n\t\t\t\t{\n\n\t\t\t\t\t// In one case send the random number.\n\n\t\t\t\t\t// In another case receive from the shutdown channel.\n\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Create a slice to hold the random numbers.\n\n\t// Receive from the values channel with range.\n\t{\n\n\t\t// continue the loop if the value was even.\n\n\t\t// Store the odd number.\n\n\t\t// break the loop once we have 100 results.\n\t}\n\n\t// Send the shutdown signal by closing the shutdown channel.\n\n\t// Wait for the Goroutines to finish.\n\n\t// Print the values in our slice.\n}\n","Hash":"1VqjJFwcqfrYxq1Dgtn9DPWb4o4="},{"Name":"answer4.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Write a program that creates a fixed set of workers to generate random\n// numbers. Discard any number divisible by 2. Continue receiving until 100\n// numbers are received. Tell the workers to shut down before terminating.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"runtime\"\n\t\"sync\"\n)\n\nfunc main() {\n\n\t// Create the channel for sharing results.\n\tvalues := make(chan int)\n\n\t// Create a channel \"shutdown\" to tell goroutines when to terminate.\n\tshutdown := make(chan struct{})\n\n\t// Define the size of the worker pool. Use runtime.GOMAXPROCS(0) to size the pool based on number of processors.\n\tpoolSize := runtime.GOMAXPROCS(0)\n\n\t// Create a sync.WaitGroup to monitor the Goroutine pool. Add the count.\n\tvar wg sync.WaitGroup\n\twg.Add(poolSize)\n\n\t// Create a fixed size pool of goroutines to generate random numbers.\n\tfor i := 0; i \u003c poolSize; i++ {\n\t\tgo func(id int) {\n\n\t\t\t// Start an infinite loop.\n\t\t\tfor {\n\n\t\t\t\t// Generate a random number up to 1000.\n\t\t\t\tn := rand.Intn(1000)\n\n\t\t\t\t// Use a select to either send the number or receive the shutdown signal.\n\t\t\t\tselect {\n\n\t\t\t\t// In one case send the random number.\n\t\t\t\tcase values \u003c- n:\n\t\t\t\t\tfmt.Printf(\"Worker %d sent %d\\n\", id, n)\n\n\t\t\t\t// In another case receive from the shutdown channel.\n\t\t\t\tcase \u003c-shutdown:\n\t\t\t\t\tfmt.Printf(\"Worker %d shutting down\\n\", id)\n\t\t\t\t\twg.Done()\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}(i)\n\t}\n\n\t// Create a slice to hold the random numbers.\n\tvar nums []int\n\tfor i := range values {\n\n\t\t// continue the loop if the value was even.\n\t\tif i%2 == 0 {\n\t\t\tfmt.Println(\"Discarding\", i)\n\t\t\tcontinue\n\t\t}\n\n\t\t// Store the odd number.\n\t\tfmt.Println(\"Keeping\", i)\n\t\tnums = append(nums, i)\n\n\t\t// break the loop once we have 100 results.\n\t\tif len(nums) == 100 {\n\t\t\tbreak\n\t\t}\n\t}\n\n\t// Send the shutdown signal by closing the channel.\n\tfmt.Println(\"Receiver sending shutdown signal\")\n\tclose(shutdown)\n\n\t// Wait for the Goroutines to finish.\n\twg.Wait()\n\n\t// Print the values in our slice.\n\tfmt.Printf(\"Result count: %d\\n\", len(nums))\n\tfmt.Println(nums)\n}\n","Hash":"2pnwFjEAhDq+xR9mN8jXCdZma6s="}]}]} ,"context":{"Title":"Context پکیج","Description":"بسته‌ی context نوع Context را تعریف می‌کند که مهلت‌ها، سیگنال‌های لغو و مقادیر دیگر مربوط به درخواست را از طریق مرزهای API و بین پروسه‌ها منتقل می‌کند.","Pages":[{"Title":"Context پکیج","Content":"\n \u003ch2\u003eContext پکیج\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/training/individual-on-demand/ultimate-go-bundle/\" target=\"_blank\"\u003eتماشای ویدئو\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003eنیاز به کمک مالی دارید؟ از \u003ca href=\"https://www.ardanlabs.com/scholarship/\" target=\"_blank\"\u003eفرم Scholarship\u003c/a\u003e ما استفاده کنید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n بسته‌ی context نوع Context را تعریف می‌کند که مهلت‌ها، سیگنال‌های لغو و سایر مقادیر مربوط به درخواست را از طریق مرزهای API و بین پروسه‌ها منتقل می‌کند.\n \u003c/p\u003e\n \n\n \u003ch2\u003eبررسی کد\u003c/h2\u003e\n \n \n \u003cp\u003e\n \u0026#34;مقادیر Context برای داده‌های مربوط به درخواستی هستند که از طریق برنامه‌ها در یک سیستم توزیع شده عبور می‌کنند.\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e1:\u003c/b\u003e Store / Retrieve Values\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e2:\u003c/b\u003e WithCancel\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e3:\u003c/b\u003e WithDeadline\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e4:\u003c/b\u003e WithTimeout\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e5:\u003c/b\u003e Request/Response\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e6:\u003c/b\u003e Cancellation\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n \u003ch2\u003eمفهوم‌شناسی Context\u003c/h2\u003e\n \n \n \u003cp\u003e\n زبان برنامه‌نویسی Go دارای کلمه‌کلیدی داخلی go برای ایجاد گوروتین‌ها است، اما کلمات کلیدی یا پشتیبانی مستقیم برای پایان دادن به گوروتین‌ها ندارد. در یک سرویس واقعی، قابلیت تایم‌اوت و پایان دادن به گوروتین‌ها برای حفظ سلامت و عملکرد یک سرویس بسیار حیاتی است. هیچ درخواست یا وظیفه‌ای نمی‌تواند بی‌پایان اجرا شود، بنابراین تشخیص دادن و مدیریت تاخیر مسئولیت هر برنامه‌نویسی است.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n یک راه‌حل ارائه شده توسط تیم Go برای حل این مشکل بسته‌ی Context است. این بسته توسط سمیر آجمانی در کنفرانس Gotham Go در سال ۲۰۱۴ نوشته و معرفی شد. او همچنین یک مقاله برای وبلاگ Go نوشت.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n اسلاید دیک: \u003ca href=\"https://talks.golang.org/2014/gotham-context.slide#1\" target=\"_blank\"\u003ehttps://talks.golang.org/2014/gotham-context.slide#1\u003c/a\u003e\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n مقاله وبلاگ: \u003ca href=\"https://blog.golang.org/context\" target=\"_blank\"\u003ehttps://blog.golang.org/context\u003c/a\u003e\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n با استفاده از این اعمال کرده و مکالمات با سمیر در طول سال‌ها، یک مجموعه از مفهوم‌شناسی‌ها پدید آمده است.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u003cb\u003eایجاد\u003c/b\u003e \u003cb\u003eContext\u003c/b\u003e \u003cb\u003eهای\u003c/b\u003e \u003cb\u003eدرخواست\u003c/b\u003e \u003cb\u003eورودی\u003c/b\u003e \u003cb\u003eبه\u003c/b\u003e \u003cb\u003eسرور\u003c/b\u003e\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n زمان ایجاد یک Context همیشه در ابتدای ممکن‌ترین زمان در پردازش یک درخواست یا وظیفه است. کار با Context در ابتدای چرخه توسعه شما را مجبور به طراحی APIها به عنوان اولین پارامتر به عنوان Context می‌کند. حتی اگر مطمئن نیستید که یک تابع به 100٪ نیاز به یک Context دارد، از حذف Context از چند تابع آسان‌تر است تا سعی در افزودن Context بعداً.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e75 // Handle is our mechanism for mounting Handlers for a given HTTP verb and path\n76 // pair, this makes for really easy, convenient routing.\n77 func (a *App) Handle(verb, path string, handler Handler, mw ...Middleware) {\n...\n85 // The function to execute for each request.\n86 h := func(w http.ResponseWriter, r *http.Request, params map[string]string) {\n87 ctx, span := trace.StartSpan(r.Context(), \u0026#34;internal.platform.web\u0026#34;)\n88 defer span.End()\n...\n106 // Add this handler for the specified verb and route.\n107 a.TreeMux.Handle(verb, path, h)\n108 }\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در اینجا کدی از پروژه‌ی سرویسی را مشاهده می‌کنید که در آموزش‌های Ardan Labs تدریس می‌شود. در خط ۸۶، یک تابع دستگیره تعریف شده که به همه‌ی مسیرها به عنوان در خط ۱۰۷ نشان داده شده متصل می‌شود. این تابع آغاز پردازش هر درخواست ورودی را آغاز می‌کند. در خط ۸۷، یک span برای درخواست ایجاد می‌شود که به عنوان پارامتر اول یک Context را می‌پذیرد. این اولین بار است که در کد سرویس به یک Context نیاز داریم.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n آنچه که در اینجا بسیار عالی است این است که مقدار http.Request در حالت پیش‌فرض یک Context را در خود دارد. این قابلیت از نسخه ۱.۷ Go به کد اضافه شده است. این به این معناست که کد نیاز به ایجاد دستی یک Context در سطح بالا ندارد. اگر از نسخه ۱.۸ Go استفاده می‌کردیم، در آن صورت قبل از فراخوانی StartSpan نیاز به ایجاد یک Context خالی با استفاده از تابع context.Background داشتیم.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003ehttps://golang.org/pkg/context/#Background\n\n87 ctx := context.Background()\n88 ctx, span := trace.StartSpan(ctx, \u0026#34;internal.platform.web\u0026#34;)\n89 defer span.End()\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این است که چگونه کد در نسخه ۱.۸ Go باید به نظر برسد. همانطور که در مستندات بسته توضیح داده شده است:\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n Background یک Context خالی با غیر-nil برمی‌گرداند. هیچگاه لغو نمی‌شود، مقادیری ندارد و مهلتی ندارد. به طور معمول توسط تابع اصلی، مقدماتی و تست‌ها و به عنوان Context سطح بالا برای درخواست‌های ورودی استفاده می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n در Go، استفاده از نام متغیر ctx برای همه‌ی مقادیر Context یک الگوی عادتی است. از آنجا که یک Context یک رابط است، از علائم نشانگر می‌تواند استفاده نشود.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003ehttps://golang.org/pkg/context/#Context\n\ntype Context interface {\n Deadline() (deadline time.Time, ok bool)\n Done() \u0026lt;-chan struct{}\n Err() error\n Value(key interface{}) interface{}\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n هر تابعی که یک Context را می‌پذیرد، باید یک نسخه خودتان از مقدار رابط را دریافت کند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u003cb\u003eتماس\u003c/b\u003e \u003cb\u003eهای\u003c/b\u003e \u003cb\u003eخروجی\u003c/b\u003e \u003cb\u003eبه\u003c/b\u003e \u003cb\u003eسرورها\u003c/b\u003e \u003cb\u003eباید\u003c/b\u003e \u003cb\u003eیک\u003c/b\u003e \u003cb\u003eContext\u003c/b\u003e \u003cb\u003eبپذیرند\u003c/b\u003e\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n اندیشه پشت این مفهوم این است که تماس‌های سطح بالاتر باید به تماس‌های سطح پایین‌تر بگویند چقدر طول می‌کشد که آنها مایل به انتظار باشند. مثال بزرگی از این موضوع در بسته‌ی http و تغییرات نسخه ۱.۷ اعمال شده در متد Do برای رعایت تایم‌اوت در یک درخواست است.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e01 package main\n02 \n03 import (\n04 \u0026#34;context\u0026#34;\n05 \u0026#34;io\u0026#34;\n06 \u0026#34;log\u0026#34;\n07 \u0026#34;net/http\u0026#34;\n08 \u0026#34;os\u0026#34;\n09 \u0026#34;time\u0026#34;\n10 )\n11 \n12 func main() {\n13\n14 // Create a new request.\n15 req, err := http.NewRequest(\u0026#34;GET\u0026#34;, \u0026#34;https://www.ardanlabs.com/blog/post/index.xml\u0026#34;, nil)\n16 if err != nil {\n17 log.Println(\u0026#34;ERROR:\u0026#34;, err)\n18 return\n19 }\n20\n21 // Create a context with a timeout of 50 milliseconds.\n22 ctx, cancel := context.WithTimeout(req.Context(), 50*time.Millisecond)\n23 defer cancel()\n24\n25 // Bind the new context into the request.\n26 req = req.WithContext(ctx)\n27\n28 // Make the web call and return any error. Do will handle the\n29 // context level timeout.\n30 resp, err := http.DefaultClient.Do(req)\n31 if err != nil {\n32 log.Println(\u0026#34;ERROR:\u0026#34;, err)\n33 return\n34 }\n35\n36 // Close the response body on the return.\n37 defer resp.Body.Close()\n38\n39 // Write the response to stdout.\n40 io.Copy(os.Stdout, resp.Body)\n41 }\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این برنامه یک درخواست برای دریافت خوراک وبلاگ Ardan با تایم‌اوت ۵۰ میلی‌ثانیه صادر می‌کند. در خطوط ۱۵-۱۹، درخواست برای انجام تماس GET به URL ارائه‌شده ایجاد می‌شود. در خطوط ۲۲-۲۳، یک Context با تایم‌اوت ۵۰ میلی‌ثانیه ایجاد می‌شود. یک API جدیدی که در نسخه ۱.۷ به Request اضافه شد، متد WithContext است. این متد اجازه می‌دهد تا فیلد Context مقدار Request به‌روز شود. در خط ۲۶، دقیقاً همین کار توسط کد انجام می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n در خط ۳۰، درخواست واقعی با استفاده از متد Do از مقدار DefaultClient بسته‌ی http صورت می‌گیرد. متد Do از مقدار تایم‌اوت ۵۰ میلی‌ثانیه که در داخل Context در Request تنظیم شده است، پیروی می‌کند. آنچه که در اینجا مشاهده می‌کنید، کد (تابع سطح بالا) به متد Do (تابع سطح پایین) می‌گوید چقدر مایل به انتظار برای انجام عملیات Do هستیم.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u003cb\u003eهیچوقت\u003c/b\u003e \u003cb\u003eیک\u003c/b\u003e \u003cb\u003eContext\u003c/b\u003e \u003cb\u003eیک\u003c/b\u003e \u003cb\u003eداخل\u003c/b\u003e \u003cb\u003estruct\u003c/b\u003e \u003cb\u003eنگه\u003c/b\u003e \u003cb\u003eندارید\u003c/b\u003e\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n به جای اینکه Context را به طور ضمنی در هر تابعی که نیاز دارد به آن منتقل کنید. در واقعیت، هر تابعی که عملیات I/O را انجام می‌دهد، باید یک مقدار Context را به عنوان پارامتر اول خود قبول کند و تایم‌اوت یا مهلتی که توسط فراخواننده تنظیم شده را رعایت کند. در مورد Request، مسائل سازگاری به عقب را در نظر گرفت. بنابراین به جای تغییر APIها، مکانیک نشان‌داده‌شده در بخش آخر اجرا شد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n همیشه استثنائاتی وجود دارد. با این حال، در دامنه این پست و هر API از کتابخانه استاندارد که یک Context می‌پذیرد، الگوی عمومی این است که پارامتر اول مقدار Context را پذیرفته‌شده باشد.\n \u003c/p\u003e\n \n\n \u003ca href=\"/tour/eng/static/img/context_figure1.png\" target=\"_blank\"\u003e\n \u003cimg class=\"codeimg\" src=\"/tour/eng/static/img/context_figure1.png\"\u003e\n \u003c/a\u003e\n\n \n \u003cp\u003e\n شکل نمونه‌ای از بسته‌ی net را نشان می‌دهد که پارامتر اول هر متد یک Context به عنوان پارامتر اول می‌پذیرد و از الگوی نام متغیر ctx استفاده می‌کند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u003cb\u003eزنجیره\u003c/b\u003e \u003cb\u003eتماس\u003c/b\u003e \u003cb\u003eهای\u003c/b\u003e \u003cb\u003eتابع\u003c/b\u003e \u003cb\u003eبین\u003c/b\u003e \u003cb\u003eآنها\u003c/b\u003e \u003cb\u003eباید\u003c/b\u003e \u003cb\u003eContext\u003c/b\u003e \u003cb\u003eرا\u003c/b\u003e \u003cb\u003eمنتقل\u003c/b\u003e \u003cb\u003eکند\u003c/b\u003e\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n این قانون مهمی است زیرا یک Context مبتنی بر درخواست یا وظیفه است. شما می‌خواهید Context و هر تغییری که در طول پردازش درخواست یا وظیفه انجام شود، منتقل و رعایت شود.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e23 // List returns all the existing users in the system.\n24 func (u *User) List(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {\n25 ctx, span := trace.StartSpan(ctx, \u0026#34;handlers.User.List\u0026#34;)\n26 defer span.End()\n27\n28 users, err := user.List(ctx, u.db)\n29 if err != nil {\n30 return err\n31 }\n32\n33 return web.Respond(ctx, w, users, http.StatusOK)\n34 }\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در این لیست، یک تابع دستگیره به نام \u0026#34;List\u0026#34; مشاهده می‌شود که هنگامی که یک کاربر درخواست HTTP را به این نقطه ارسال می‌کند، اجرا می‌شود. این دستگیره به عنوان پارامتر اول یک Context را قبول می‌کند، زیرا قسمتی از درخواست است و عملیات ورودی/خروجی انجام خواهد داد. می‌توانید در خطوط ۲۵، ۲۸ و ۳۳ ببینید که همان مقدار Context به طول پشته تماس منتقل می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n یک مقدار Context جدید ایجاد نمی‌شود زیرا این تابع به تغییر آن نیاز ندارد. اگر یک مقدار Context در سطح بالا توسط این تابع ایجاد می‌شد، هر اطلاعات موجود در Context از تماس با سطح بالاتر که با این درخواست مرتبط است، از دست می‌رفت. این دقیقاً آن چیزی نیست که شما می‌خواهید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e33 // List retrieves a list of existing users from the database.\n34 func List(ctx context.Context, db *sqlx.DB) ([]User, error) {\n35 ctx, span := trace.StartSpan(ctx, \u0026#34;internal.user.List\u0026#34;)\n36 defer span.End()\n37\n38 users := []User{}\n39 const q = `SELECT * FROM users`\n40\n41 if err := db.SelectContext(ctx, \u0026amp;users, q); err != nil {\n42 return nil, errors.Wrap(err, \u0026#34;selecting users\u0026#34;)\n43 }\n44\n45 return users, nil\n46 }\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در اینجا اعلامیه‌ی متد List را مشاهده می‌کنید که در خط ۲۸ فراخوانی شده است. همچنان این متد یک Context را به عنوان پارامتر اول خود قبول می‌کند. سپس این مقدار به طول پشته تماس یک بار دیگر در خطوط ۳۵ و ۴۱ منتقل می‌شود. چرا که خط ۴۱ یک تماس به پایگاه داده است، توابع باید به هر مهلتی که در Context از تماس‌کننده‌های بالاتر تنظیم شده است احترام بگذارند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u003cb\u003eجایگزین\u003c/b\u003e \u003cb\u003eکردن\u003c/b\u003e \u003cb\u003eیک\u003c/b\u003e \u003cb\u003eContext\u003c/b\u003e \u003cb\u003eبا\u003c/b\u003e \u003cb\u003eاستفاده\u003c/b\u003e \u003cb\u003eاز\u003c/b\u003e \u003cb\u003eWithCancel,\u003c/b\u003e \u003cb\u003eWithDeadline,\u003c/b\u003e \u003cb\u003eWithTimeout,\u003c/b\u003e \u003cb\u003eیا\u003c/b\u003e \u003cb\u003eWithValue\u003c/b\u003e\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n زیرا هر تابع می‌تواند Context را برای نیازهای خاص خود اضافه یا تغییر دهد، و تغییرات انجام‌شده باید تاثیری بر روی توابعی که قبل از آن فراخوانی شده باشند نداشته باشد، Context از نشانگر مقداری استفاده می‌کند. این به این معناست که هر تغییری در مقدار یک Context، یک مقدار Context جدید ایجاد می‌کند که سپس به جلو منتقل می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e01 func main() {\n02\n03 // Set a duration.\n04 duration := 150 * time.Millisecond\n05\n06 // Create a context that is both manually cancellable and will signal\n07 // cancel at the specified duration.\n08 ctx, cancel := context.WithTimeout(context.Background(), duration)\n09 defer cancel()\n10\n11 // Create a channel to receive a signal that work is done.\n12 ch := make(chan data, 1)\n13\n14 // Ask the goroutine to do some work for us.\n15 go func() {\n16\n17 // Simulate work.\n18 time.Sleep(50 * time.Millisecond)\n19\n20 // Report the work is done.\n21 ch \u0026lt;- data{\u0026#34;123\u0026#34;}\n22 }()\n23\n24 // Wait for the work to finish. If it takes too long, move on.\n25 select {\n26 case d := \u0026lt;-ch:\n27 fmt.Println(\u0026#34;work complete\u0026#34;, d)\n28\n29 case \u0026lt;-ctx.Done():\n30 fmt.Println(\u0026#34;work cancelled\u0026#34;)\n31 }\n32 }\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این یک برنامه‌ی کوچک است که ماهیت نشانگر مقداری (value semantic) تابع WithTimeout را نشان می‌دهد. در خط ۰۸، فراخوانی تابع WithTimeout یک مقدار Context جدید و یک تابع لغو (cancel) برگرداند. چرا که فراخوانی تابع نیاز به یک Context والد دارد، کد از تابع Background برای ایجاد یک Context خالی در سطح بالا استفاده می‌کند. این همان چیزی است که تابع Background برایش استفاده می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n به طور پیشرو، مقدار Context توسط تابع WithTimeout ایجاد شده استفاده می‌شود. اگر توابع آتی در زنجیره‌ی تماس نیاز به مهلت زمانی یا مهلت اختصاصی خود دارند، آنها همچنین باید از تابع مناسب With و این مقدار Context جدید به عنوان والد استفاده کنند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n حیاتی است که هر تابع لغوی که توسط یک تابع With برگردانده می‌شود، قبل از اینکه تابع برگردد، اجرا شود. به همین دلیل استفاده از کلمه کلیدی defer همانند آنچه در خط ۲۶ مشاهده می‌کنید، عادت است. عدم انجام این کار منجر به نشتی حافظه در برنامه‌ی شما می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u003cb\u003eزمانیکه\u003c/b\u003e \u003cb\u003eیک\u003c/b\u003e \u003cb\u003eContext\u003c/b\u003e \u003cb\u003eلغو\u003c/b\u003e \u003cb\u003eمیشود,\u003c/b\u003e \u003cb\u003eتمام\u003c/b\u003e \u003cb\u003eContextهای\u003c/b\u003e \u003cb\u003eمشتق\u003c/b\u003e \u003cb\u003eشده\u003c/b\u003e \u003cb\u003eاز\u003c/b\u003e \u003cb\u003eآن\u003c/b\u003e \u003cb\u003eهم\u003c/b\u003e \u003cb\u003eباید\u003c/b\u003e \u003cb\u003eلغو\u003c/b\u003e \u003cb\u003eشود\u003c/b\u003e\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n استفاده از نشانگر مقداری برای API Context به این معناست که هر مقدار Context جدید همه‌چیزی را که در Context والد دارد به همراه تغییرات جدیدی که دارد دارد. این به این معناست که اگر یک Context والد لغو شود، همه‌ی Contextهای فرزندی که از آن Context والد مشتق می‌شوند نیز لغو می‌شوند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e01 func main() {\n02\n03 // Create a Context that can be cancelled.\n04 ctx, cancel := context.WithCancel(context.Background())\n05 defer cancel()\n06\n07 // Use the Waitgroup for orchestration.\n08 var wg sync.WaitGroup\n09 wg.Add(10)\n10\n11 // Create ten goroutines that will derive a Context from\n12 // the one created above.\n13 for i := 0; i \u0026lt; 10; i\u0026#43;\u0026#43; {\n14 go func(id int) {\n15 defer wg.Done()\n16\n17 // Derive a new Context for this goroutine from the Context\n18 // owned by the main function.\n19 ctx := context.WithValue(ctx, key, id)\n20\n21 // Wait until the Context is cancelled.\n22 \u0026lt;-ctx.Done()\n23 fmt.Println(\u0026#34;Cancelled:\u0026#34;, id)\n24 }(i)\n25 }\n26\n27 // Cancel the Context and any derived Context\u0026#39;s as well.\n28 cancel()\n29 wg.Wait()\n30 }\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این برنامه یک مقدار Context ایجاد می‌کند که می‌توان آن را در خط ۰۴ لغو کرد. سپس در خطوط ۱۳ تا ۲۵، ده goroutine ایجاد می‌شوند. هر goroutine شناسه منحصر به فرد خود را در داخل مقدار Context خود در خط ۱۹ قرار می‌دهد. فراخوانی WithValue مقدار Context تابع اصلی را به عنوان والد خود دریافت می‌کند. سپس در خط ۲۲، هر goroutine منتظر می‌ماند تا مقدار Context خود لغو شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n در خط ۲۸، goroutine اصلی مقدار Context خود را لغو می‌کند و سپس در خط ۲۹ منتظر می‌ماند تا همه ده goroutine اعلامیه را دریافت کرده و بعد از آن برنامه را ببندد. هنگامی که تابع لغو فراخوانی می‌شود، همه ده goroutine در خط ۴۱ آزاد می‌شوند و چاپ می‌کنند که لغو شده‌اند. یک فراخوانی برای لغو همه آنها.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n این همچنین نشان می‌دهد که مقدار Context همانند می‌تواند به توابعی که در goroutine های مختلف اجرا می‌شوند منتقل شود. یک Context ایمن برای استفاده همزمان توسط چند goroutine است.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n توجه داشته باشید که حتی اگر یک تابع این امکان را بدهد، Context nil را منتقل نکنید. اگر نسبت به استفاده از کدام Context مطمئن نیستید، یک Context TODO منتقل کنید. یکی از بخش‌های مورد علاقه‌ام از بسته Context تابع TODO است. من باور دارم که یک برنامه‌نویس همیشه در حال نوشتن کد است. این مطابق با نوشتن نسخه‌های مقاله توسط نویسنده است. شما همیشه همه چیز را نمی‌دانید ولی امیدواراً کافی می‌دانید تا کارها را به جلو ببرید. در پایان، شما همیشه در حال یادگیری، بازبینی و تست در طول راه هستید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n بارها پیش آمده که می‌دانستم نیاز به یک Context دارم اما نمی‌دانستم از کجا باید آن را بگیرم. من مسئول ایجاد Context سطح بالا نبودم، بنابراین استفاده از تابع Background از میان بی‌پایه بود. به یک Context سطح بالای موقت نیاز داشتم تا بفهمم واقعیتاً Context واقعی از کجا می‌آید. در این مواقع باید از تابع TODO به جای تابع Background استفاده کنید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u003cb\u003eاز\u003c/b\u003e \u003cb\u003eمقادیر\u003c/b\u003e \u003cb\u003eContext\u003c/b\u003e \u003cb\u003eفقط\u003c/b\u003e \u003cb\u003eبرای\u003c/b\u003e \u003cb\u003eداده‌های\u003c/b\u003e \u003cb\u003eمرتبط\u003c/b\u003e \u003cb\u003eبا\u003c/b\u003e \u003cb\u003eدرخواست\u003c/b\u003e \u003cb\u003eاستفاده\u003c/b\u003e \u003cb\u003eکنید\u003c/b\u003e\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n از مقدار Context برای انتقال پارامترهای اختیاری به توابع استفاده نکنید. این ممکن است مهمترین معنایی از همه باشد. از مقدار Context برای انتقال داده به تابع استفاده نکنید زمانی که این داده توسط تابع برای اجرای کد خود به صورت موفقیت‌آمیز نیاز دارد. به عبارت دیگر، یک تابع باید قادر به اجرای منطق خود با مقدار Context خالی باشد. در مواردی که یک تابع نیاز به وجود اطلاعات در مقدار Context دارد، اگر این اطلاعات از دست برود، برنامه باید شکست بخورد و به برنامه اعلام کند که باید خاموش شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n یک مثال کلاسیک از استفاده نادرست از انتقال داده به توابع با استفاده از Context، اتصالات پایگاه داده است. به عنوان یک قاعده عمومی، شما می‌خواهید این ترتیب را در حین انتقال داده در سراسر برنامه‌ی خود دنبال کنید.\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003eداده را به عنوان پارامتر تابع منتقل کنید: این روش واضح‌ترین روش برای انتقال داده در سراسر برنامه بدون مخفی کردن آن است.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \u003cul\u003e\n \n \u003cli\u003eداده را از طریق دریافت‌کننده (receiver) منتقل کنید: اگر تابعی که به داده نیاز دارد نمی‌تواند امضای خود را تغییر دهد، سپس از یک متد استفاده کرده و داده را از طریق دریافت‌کننده منتقل کنید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n مثال سریع از استفاده از دریافت‌کننده:\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n دستگیره‌های درخواست (Request handlers) یک مثال کلاسیک از قاعده دوم هستند. از آنجا که یک تابع دستگیره به یک اعلان خاص بسته شده است، امضای دستگیره نمی‌تواند تغییر کند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e23 // List returns all the existing users in the system.\n24 func (u *User) List(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {\n25 ctx, span := trace.StartSpan(ctx, \u0026#34;handlers.User.List\u0026#34;)\n26 defer span.End()\n27\n28 users, err := user.List(ctx, u.db)\n29 if err != nil {\n30 return err\n31 }\n32\n33 return web.Respond(ctx, w, users, http.StatusOK)\n34 }\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در اینجا شما متد دستگیره List از پروژه سرویس مشاهده می‌کنید. امضای این متدها به آنچه توسط چارچوب وب تعریف شده است بسته شده و نمی‌توان آنها را تغییر داد. با این حال، برای انجام تماس تجاری در خط ۲۸، اتصال پایگاه داده لازم است. این کد اتصال‌پول را نه از مقدار Contextی که منتقل می‌شود، بلکه از دریافت‌کننده (receiver) پیدا می‌کند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e15 // User represents the User API method handler set.\n16 type User struct {\n17 db *sqlx.DB\n18 authenticator *auth.Authenticator\n19\n20 // ADD OTHER STATE LIKE THE LOGGER AND CONFIG HERE.\n21 }\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n شما اعلامیه نوع دریافت‌کننده را مشاهده می‌کنید. هر چیزی که یک دستگیره درخواست نیاز دارد به عنوان فیلدها تعریف شده است. این امکان را فراهم می‌کند تا اطلاعات مخفی نشده و لایه تجاری با مقدار Context خالی کار کند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e14 // API constructs an http.Handler with all application routes defined.\n15 func API(shutdown chan os.Signal, log *log.Logger, db *sqlx.DB, authenticator *auth.Authenticator) http.Handler {\n16\n...\n26 // Register user management and authentication endpoints.\n27 u := User{\n28 db: db,\n29 authenticator: authenticator,\n30 }\n31\n32 app.Handle(\u0026#34;GET\u0026#34;, \u0026#34;/v1/users\u0026#34;, u.List)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این کد یک مقدار کاربر ایجاد می‌کند و سپس متد لیست را به مسیر متصل می‌کند. یک بار دیگر، از آنجا که امضای یک تابع دستگیره قابل تغییر نیست، استفاده از یک دریافت‌کننده و متدها بهترین راه برای انتقال داده بدون پنهان شدن آن است.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u003cb\u003eاطلاعات\u003c/b\u003e \u003cb\u003eاشکال‌زدایی\u003c/b\u003e \u003cb\u003eیا\u003c/b\u003e \u003cb\u003eردیابی\u003c/b\u003e \u003cb\u003eرا\u003c/b\u003e \u003cb\u003eمی‌توان\u003c/b\u003e \u003cb\u003eبا\u003c/b\u003e \u003cb\u003eامان\u003c/b\u003e \u003cb\u003eدر\u003c/b\u003e \u003cb\u003eیک\u003c/b\u003e \u003cb\u003eContext\u003c/b\u003e \u003cb\u003eمنتقل\u003c/b\u003e \u003cb\u003eکرد\u003c/b\u003e\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n اطلاعاتی که می‌توان در یک مقدار Context ذخیره کرد و از آن دریافت کرد، اطلاعات اشکال‌زدایی و ردیابی هستند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e23 // Values represent state for each request.\n24 type Values struct {\n25 TraceID string\n26 Now time.Time\n27 StatusCode int\n28 }\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در اینجا یک اعلامیه از یک نوع آماده شده و در داخل هر مقدار Context ایجاد شده برای یک درخواست جدید ذخیره می‌شود. سه فیلد اطلاعات ردیابی و اشکال‌زدایی برای درخواست فراهم می‌کنند. این اطلاعات به عنوان درخواست پیش می‌رود جمع‌آوری می‌شوند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e75 // Handle is our mechanism for mounting Handlers for a given HTTP verb and path\n76 // pair, this makes for really easy, convenient routing.\n77 func (a *App) Handle(verb, path string, handler Handler, mw ...Middleware) {\n78\n...\n79 // The function to execute for each request.\n80 h := func(w http.ResponseWriter, r *http.Request, params map[string]string) {\n…\n84 // Set the context with the required values to\n85 // process the request.\n86 v := Values{\n87 TraceID: span.SpanContext().TraceID.String(),\n88 Now: time.Now(),\n89 }\n90 ctx = context.WithValue(ctx, KeyValues, \u0026amp;v)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n مشاهده کنید که نوع Values در خط ۸۶ ساخته می‌شود و سپس در داخل Context در خط ۹۰ ذخیره می‌شود. اکثر این اطلاعات توسط میان‌افزار logging نیاز دارد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e20 // Create the handler that will be attached in the middleware chain.\n21 h := func(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {\n...\n25 // If the context is missing this value, request the service\n26 // to be shutdown gracefully.\n27 v, ok := ctx.Value(web.KeyValues).(*web.Values)\n28 if !ok {\n29 return web.NewShutdownError(\u0026#34;web value missing from context\u0026#34;)\n30 }\n...\n34 log.Printf(\u0026#34;%s : (%d) : %s %s -\u0026gt; %s (%s)\u0026#34;,\n35 v.TraceID, v.StatusCode,\n36 r.Method, r.URL.Path,\n37 r.RemoteAddr, time.Since(v.Now),\n38 )\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n پیامدهای انتقال اطلاعات از طریق مقدار Context در کد در خط‌های ۲۷ تا ۳۰ نشان داده شده است. کد تلاش می‌کند داده‌های Values را از مقدار Context بازیابی کند و بررسی می‌کند که آیا داده در آنجا وجود دارد یا خیر. اگر داده در دسترس نباشد، یک مشکل از نظر اصولی وجود دارد و سرویس باید تا حد امکان به سرعت تعطیل شود. این کار در کد سرویس انجام می‌شود با ارسال یک مقدار خطا ویژه به بالا از طریق برنامه.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n اگر اطلاعات اتصال به پایگاه داده یا اطلاعات کاربر به لایه تجاری خود از طریق یک Context منتقل می‌کنید، دو مشکل دارید:\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003eشما باید برای اصالت بررسی کنید و نیاز به یک مکانیزم داشته باشید تا سرویس به سرعت تعطیل شود.\u003c/li\u003e\n \n \u003cli\u003eآزمون و اشکال‌زدایی به مراتب دشوارتر و پیچیده‌تر می‌شود. شما از وضوح و قابلیت خوانایی بهتر در کدتان دور می‌شوید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eیادداشت‌ها\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003eدرخواست‌های وارد به یک سرور باید یک Context ایجاد کنند.\u003c/li\u003e\n \n \u003cli\u003eتماس‌های خروجی به سرورها باید یک Context را قبول کنند.\u003c/li\u003e\n \n \u003cli\u003eزنجیره تماس‌های تابعی بین آن‌ها باید مقدار Context را منتقل کند.\u003c/li\u003e\n \n \u003cli\u003eیک Context را با استفاده از WithCancel، WithDeadline، WithTimeout یا WithValue جایگزین کنید.\u003c/li\u003e\n \n \u003cli\u003eهنگامی که یک Context لغو می‌شود، تمامی Contextهای مشتق از آن همچنین لغو می‌شوند.\u003c/li\u003e\n \n \u003cli\u003eContextها را در داخل نوع struct نگه‌نداری نکنید؛ به جای آن، به هر تابع که نیاز به آن دارد، به صراحت یک Context را منتقل کنید.\u003c/li\u003e\n \n \u003cli\u003eحتی اگر یک تابع اجازه دهد، یک Context خالی را منتقل نکنید. اگر مطمئن نیستید کدام Context را باید استفاده کنید، context.TODO را منتقل کنید.\u003c/li\u003e\n \n \u003cli\u003eاز مقادیر Context فقط برای داده‌های مرتبط با درخواست که در فرآیندها و APIها عبور می‌کند، و نه برای انتقال پارامترهای اختیاری به توابع استفاده کنید.\u003c/li\u003e\n \n \u003cli\u003eهمان Context می‌تواند به توابع در حال اجرا در گوروتین‌های مختلف منتقل شود؛ Contextها برای استفاده همزمان توسط چند گوروتین ایمن هستند.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eمطالب اضافی\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2019/09/context-package-semantics-in-go.html\" target=\"_blank\"\u003eContext Package Semantics In Go\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://golang.org/pkg/context\" target=\"_blank\"\u003ePackage context\u003c/a\u003e - Go Team \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://blog.golang.org/context\" target=\"_blank\"\u003eGo Concurrency Patterns: Context\u003c/a\u003e - Sameer Ajmani \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://rakyll.org/leakingctx/\" target=\"_blank\"\u003eUsing contexts to avoid leaking goroutines\u003c/a\u003e - JBD \u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eتمرین‌ها\u003c/h2\u003e\n \n \n \u003cp\u003e\n از الگو به عنوان نقطه شروع برای تکمیل تمرین‌ها استفاده کنید. یک راه حل ممکن ارائه شده است.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eتمرین 1\u003c/h2\u003e\n \n \n \u003cp\u003e\n از الگو استفاده کنید و دستورات را دنبال کنید. شما قرار است یک دستگیره وب بنویسید که یک تماس مصنوعی به پایگاه داده انجام دهد اما در صورتی که تماس زیادی طول بکشد، براساس یک متن، زمان‌دهی انجام شود. همچنین وضعیت را در متن ذخیره خواهید کرد.\n \u003c/p\u003e\n \n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n","Files":[{"Name":"example1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to store and retrieve\n// values from a context.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n)\n\n// TraceID is represents the trace id.\ntype TraceID string\n\n// TraceIDKey is the type of value to use for the key. The key is\n// type specific and only values of the same type will match.\ntype TraceIDKey int\n\nfunc main() {\n\n\t// Create a traceID for this request.\n\ttraceID := TraceID(\"f47ac10b-58cc-0372-8567-0e02b2c3d479\")\n\n\t// Declare a key with the value of zero of type userKey.\n\tconst traceIDKey TraceIDKey = 0\n\n\t// Store the traceID value inside the context with a value of\n\t// zero for the key type.\n\tctx := context.WithValue(context.Background(), traceIDKey, traceID)\n\n\t// Retrieve that traceID value from the Context value bag.\n\tif uuid, ok := ctx.Value(traceIDKey).(TraceID); ok {\n\t\tfmt.Println(\"TraceID:\", uuid)\n\t}\n\n\t// Retrieve that traceID value from the Context value bag not\n\t// using the proper key type.\n\tif _, ok := ctx.Value(0).(TraceID); !ok {\n\t\tfmt.Println(\"TraceID Not Found\")\n\t}\n}\n","Hash":"naTovoYRG4UWvm8qL/T9CksMl3Q="},{"Name":"example2.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to use the WithCancel function.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc main() {\n\n\t// Create a context that is cancellable only manually.\n\t// The cancel function must be called regardless of the outcome.\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\t// Ask the goroutine to do some work for us.\n\tgo func() {\n\n\t\t// Wait for the work to finish. If it takes too long move on.\n\t\tselect {\n\t\tcase \u003c-time.After(100 * time.Millisecond):\n\t\t\tfmt.Println(\"moving on\")\n\n\t\tcase \u003c-ctx.Done():\n\t\t\tfmt.Println(\"work complete\")\n\t\t}\n\t}()\n\n\t// Simulate work.\n\ttime.Sleep(50 * time.Millisecond)\n\n\t// Report the work is done.\n\tcancel()\n\n\t// Just hold the program to see the output.\n\ttime.Sleep(time.Second)\n}\n","Hash":"xq0j0EPwtvc8EwRYsn4mOafgVS8="},{"Name":"example3.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to use the WithDeadline function.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n)\n\ntype data struct {\n\tUserID string\n}\n\nfunc main() {\n\n\t// Set a deadline.\n\tdeadline := time.Now().Add(150 * time.Millisecond)\n\n\t// Create a context that is both manually cancellable and will signal\n\t// a cancel at the specified date/time.\n\tctx, cancel := context.WithDeadline(context.Background(), deadline)\n\tdefer cancel()\n\n\t// Create a channel to received a signal that work is done.\n\tch := make(chan data, 1)\n\n\t// Ask the goroutine to do some work for us.\n\tgo func() {\n\n\t\t// Simulate work.\n\t\ttime.Sleep(200 * time.Millisecond)\n\n\t\t// Report the work is done.\n\t\tch \u003c- data{\"123\"}\n\t}()\n\n\t// Wait for the work to finish. If it takes too long move on.\n\tselect {\n\tcase d := \u003c-ch:\n\t\tfmt.Println(\"work complete\", d)\n\n\tcase \u003c-ctx.Done():\n\t\tfmt.Println(\"work cancelled\")\n\t}\n}\n","Hash":"FC9EpJsdBquD4gDdwT3fNeMBhP8="},{"Name":"example4.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to use the WithTimeout function\n// of the Context package.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n)\n\ntype data struct {\n\tUserID string\n}\n\nfunc main() {\n\n\t// Set a duration.\n\tduration := 150 * time.Millisecond\n\n\t// Create a context that is both manually cancellable and will signal\n\t// a cancel at the specified duration.\n\tctx, cancel := context.WithTimeout(context.Background(), duration)\n\tdefer cancel()\n\n\t// Create a channel to received a signal that work is done.\n\tch := make(chan data, 1)\n\n\t// Ask the goroutine to do some work for us.\n\tgo func() {\n\n\t\t// Simulate work.\n\t\ttime.Sleep(50 * time.Millisecond)\n\n\t\t// Report the work is done.\n\t\tch \u003c- data{\"123\"}\n\t}()\n\n\t// Wait for the work to finish. If it takes too long move on.\n\tselect {\n\tcase d := \u003c-ch:\n\t\tfmt.Println(\"work complete\", d)\n\n\tcase \u003c-ctx.Done():\n\t\tfmt.Println(\"work cancelled\")\n\t}\n}\n","Hash":"PNJK9G0olMrw95FUwcrb0t9i42g="},{"Name":"example5.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program that implements a web request with a context that is\n// used to timeout the request if it takes too long.\npackage main\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n)\n\nfunc main() {\n\n\t// Create a new request.\n\treq, err := http.NewRequest(\"GET\", \"https://www.ardanlabs.com/blog/post/index.xml\", nil)\n\tif err != nil {\n\t\tlog.Println(\"ERROR:\", err)\n\t\treturn\n\t}\n\n\t// Create a context with a timeout of 50 milliseconds.\n\tctx, cancel := context.WithTimeout(req.Context(), 50*time.Millisecond)\n\tdefer cancel()\n\n\t// Bind the new context into the request.\n\treq = req.WithContext(ctx)\n\n\t// Make the web call and return any error. Do will handle the\n\t// context level timeout.\n\tresp, err := http.DefaultClient.Do(req)\n\tif err != nil {\n\t\tlog.Println(\"ERROR:\", err)\n\t\treturn\n\t}\n\n\t// Close the response body on the return.\n\tdefer resp.Body.Close()\n\n\t// Write the response to stdout.\n\tio.Copy(os.Stdout, resp.Body)\n}\n","Hash":"2ckcQd/UNVOnJ7Ryir0OTJCdP7w="},{"Name":"example6.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show when a Context is canceled, all Contexts\n// derived from it are also canceled.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"sync\"\n)\n\n// Need a key type.\ntype myKey int\n\n// Need a key value.\nconst key myKey = 0\n\nfunc main() {\n\n\t// Create a Context that can be cancelled.\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\t// Use the Waitgroup for orchestration.\n\tvar wg sync.WaitGroup\n\twg.Add(10)\n\n\t// Create ten goroutines that will derive a Context from\n\t// the one created above.\n\tfor i := 0; i \u003c 10; i++ {\n\t\tgo func(id int) {\n\t\t\tdefer wg.Done()\n\n\t\t\t// Derive a new Context for this goroutine from the Context\n\t\t\t// owned by the main function.\n\t\t\tctx := context.WithValue(ctx, key, id)\n\n\t\t\t// Wait until the Context is cancelled.\n\t\t\t\u003c-ctx.Done()\n\t\t\tfmt.Println(\"Cancelled:\", id)\n\t\t}(i)\n\t}\n\n\t// Cancel the Context and any derived Context's as well.\n\tcancel()\n\twg.Wait()\n}\n","Hash":"wN20MYujGaIgc19TayUFcOgXurQ="},{"Name":"exercise1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Use the template and follow the directions. You will be writing a web handler\n// that performs a mock database call but will timeout based on a context if the call\n// takes too long. You will also save state into the context.\npackage main\n\n// Add imports.\n\n// Declare a new type named `key` that is based on an int.\n\n// Declare a constant named `userIPKey` of type `key` set to\n// the value of 0.\n\n// Declare a struct type named `User` with two `string` based\n// fields named `Name` and `Email`.\n\nfunc main() {\n\troutes()\n\n\tlog.Println(\"listener : Started : Listening on: http://localhost:4000\")\n\thttp.ListenAndServe(\":4000\", nil)\n}\n\n// routes sets the routes for the web service.\nfunc routes() {\n\thttp.HandleFunc(\"/user\", findUser)\n}\n\n// Implement the findUser function to leverage the context for\n// both timeouts and state.\nfunc findUser(rw http.ResponseWriter, r *http.Request) {\n\n\t// Create a context that timeouts in fifty milliseconds.\n\n\t// Defer the call to cancel.\n\n\t// Save the `r.RemoteAddr` value in the context using `userIPKey`\n\t// as the key. This call returns a new context so replace the\n\t// current `ctx` value with this new one. The original context is\n\t// the parent context for this new child context.\n\n\t// Create a channel with a buffer size of 1 that works with\n\t// pointers of type `User` \n\n\t// Use this goroutine to make the database call. Use the channel\n\t// to get the user back.\n\tgo func() {\n\n\t\t// Get the `r.RemoteAddr` value from the context and log\n\t\t// the value you get back.\n\n\t\t// Call the `readDatabase` function provided below and\n\t\t// send the returned `User` pointer on the channel.\n\n\t\t// Log that the goroutine is terminating.\n\t}()\n\n\t// Wait for the database call to finish or the timeout.\n\tselect {\n\n\t// Add a case to wait on the channel for the `User` pointer.\n\n\t\t// Call the `sendResponse` function provided below to\n\t\t// send the `User` to the caller. Use `http.StatusOK`\n\t\t// as the status code.\n\n\t\t// Log we sent the response with a StatusOk\n\t\t\n\t\treturn\n\n\t// Add a case to wait on the `ctx.Done()` channel.\n\n\t\t// Use this struct value for the error response.\n\t\te := struct{ Error string }{ctx.Err().Error()}\n\n\t\t// Call the `sendResponse` function provided below to\n\t\t// send the error to the caller. Use `http.StatusRequestTimeout`\n\t\t// as the status code.\n\n\t\t// Log we sent the response with a StatusRequestTimeout\n\n\t\treturn\n\t}\n}\n\n// readDatabase performs a pretend database call with\n// a second of latency.\nfunc readDatabase() *User {\n\tu := User{\n\t\tName: \"Bill\",\n\t\tEmail: \"bill@ardanlabs.com\",\n\t}\n\n\t// Create 100 milliseconds of latency.\n\ttime.Sleep(100 * time.Millisecond)\n\n\treturn \u0026u\n}\n\n// sendResponse marshals the provided value into json and returns\n// that back to the caller.\nfunc sendResponse(rw http.ResponseWriter, v interface{}, statusCode int) {\n\trw.Header().Set(\"Content-Type\", \"application/json\")\n\trw.WriteHeader(statusCode)\n\tjson.NewEncoder(rw).Encode(v)\n}\n","Hash":"HZiITadZQjvWN3RiPE+b3VhCrlk="},{"Name":"answer1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program that implements a simple web service using the\n// context to handle timeouts and pass context into the request.\npackage main\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"log\"\n\t\"net/http\"\n\t\"time\"\n)\n\n// The key type is unexported to prevent collisions with context keys defined in\n// other packages.\ntype key int\n\n// userIPkey is the context key for the user IP address. Its value of zero is\n// arbitrary. If this package defined other context keys, they would have\n// different integer values.\nconst userIPKey key = 0\n\n// User defines a user in the system.\ntype User struct {\n\tName string\n\tEmail string\n}\n\nfunc main() {\n\troutes()\n\n\tlog.Println(\"listener : Started : Listening on: http://localhost:4000\")\n\thttp.ListenAndServe(\":4000\", nil)\n}\n\n// routes sets the routes for the web service.\nfunc routes() {\n\thttp.HandleFunc(\"/user\", findUser)\n}\n\n// findUser makes a database call to find a user.\nfunc findUser(rw http.ResponseWriter, r *http.Request) {\n\n\t// Create a context that timeouts in fifty milliseconds.\n\tctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)\n\tdefer cancel()\n\n\t// Save the user ip address in the context. This call returns\n\t// a new context we now need to use. The original context is\n\t// the parent context for this new child context.\n\tctx = context.WithValue(ctx, userIPKey, r.RemoteAddr)\n\n\t// Create a goroutine to make the database call. Use the channel\n\t// to get the user back.\n\tch := make(chan *User, 1)\n\tgo func() {\n\n\t\t// Get the ip address from the context for logging.\n\t\tif ip, ok := ctx.Value(userIPKey).(string); ok {\n\t\t\tlog.Println(\"Start DB for IP\", ip)\n\t\t}\n\n\t\t// Make the database call and return the value\n\t\t// back on the channel.\n\t\tch \u003c- readDatabase()\n\t\tlog.Println(\"DB goroutine terminated\")\n\t}()\n\n\t// Wait for the database call to finish or the timeout.\n\tselect {\n\tcase u := \u003c-ch:\n\n\t\t// Respond with the user.\n\t\tsendResponse(rw, u, http.StatusOK)\n\t\tlog.Println(\"Sent StatusOK\")\n\t\treturn\n\n\tcase \u003c-ctx.Done():\n\n\t\t// If you have the ability to cancel the database\n\t\t// operation the goroutine is performing do that now.\n\t\t// In this example we can't.\n\n\t\t// Respond with the error.\n\t\te := struct{ Error string }{ctx.Err().Error()}\n\t\tsendResponse(rw, e, http.StatusRequestTimeout)\n\t\tlog.Println(\"Sent StatusRequestTimeout\")\n\t\treturn\n\t}\n}\n\n// readDatabase performs a pretend database call with\n// a second of latency.\nfunc readDatabase() *User {\n\tu := User{\n\t\tName: \"Bill\",\n\t\tEmail: \"bill@ardanlabs.com\",\n\t}\n\n\t// Create 100 milliseconds of latency.\n\ttime.Sleep(100 * time.Millisecond)\n\n\treturn \u0026u\n}\n\n// sendResponse marshals the provided value into json and returns\n// that back to the caller.\nfunc sendResponse(rw http.ResponseWriter, v interface{}, statusCode int) {\n\trw.Header().Set(\"Content-Type\", \"application/json\")\n\trw.WriteHeader(statusCode)\n\tjson.NewEncoder(rw).Encode(v)\n}\n","Hash":"PAg9pG41wNobXGcZnApaSiJ2GbE="}]}]} ,"algorithms-bits-seven":{"Title":"عملیات بیت","Description":"در این بخش، نمونه‌هایی ارائه می‌شود که عملیات‌های بیتی را انجام می‌دهند.","Pages":[{"Title":"زوج یا فرد بودن","Content":"\n \u003ch2\u003eزوج یا فرد بودن\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/training/individual-on-demand/ultimate-go-bundle/\" target=\"_blank\"\u003eتماشای ویدئو\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003eنیاز به کمک مالی دارید؟ از \u003ca href=\"https://www.ardanlabs.com/scholarship/\" target=\"_blank\"\u003eفرم Scholarship\u003c/a\u003e ما استفاده کنید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n این برنامه نمونه به شما نشان می‌دهد چگونه با استفاده از مدیریت بیت، بررسی کنید که یک عدد صحیح زوج یا فرد است.\n \u003c/p\u003e\n \n\n\t\n\t\t\n\t\n\n","Files":[{"Name":"iseven.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This sample program shows you how to check if an integer is even or\n// odd using bit manipulation.\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\n\tfmt.Println(8, \":\", IsEven(8))\n\tfmt.Println(15, \":\", IsEven(15))\n\tfmt.Println(4, \":\", IsEven(4))\n}\n\n// IsEven checks is an integer is even.\nfunc IsEven(num int) bool {\n\n\t// Use the bitwise AND operator to see if the least significant\n\t// bit (LSB) is 0.\n\n\t// Helpful source: https://catonmat.net/low-level-bit-hacks\n\t// 0 \u0026 1 = 0 (even number)\n\t// 1 \u0026 1 = 1 (odd number)\n\n\treturn num\u00261 == 0\n}\n","Hash":"xOgl65Wqk/qcXxDKTqS7WCJcrz8="}]}]} ,"algorithms-numbers":{"Title":"عملیات اعداد","Description":"این بخش نمونه‌هایی ارائه می‌دهد که عملیات‌های اعداد را انجام می‌دهند.","Pages":[{"Title":"پالیندرم","Content":"\n \u003ch2\u003eپالیندرم\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/training/individual-on-demand/ultimate-go-bundle/\" target=\"_blank\"\u003eتماشای ویدئو\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003eنیاز به کمک مالی دارید؟ از \u003ca href=\"https://www.ardanlabs.com/scholarship/\" target=\"_blank\"\u003eفرم Scholarship\u003c/a\u003e ما استفاده کنید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n این برنامه نمونه یک بررسی را پیاده‌سازی می‌کند تا ببیند آیا یک عدد صحیح پالیندرم است یا خیر.\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003eاطلاعات بیشتر در \u003ca href=\"https://en.wikipedia.org/wiki/Palindrome\" target=\"_blank\"\u003ehttps://en.wikipedia.org/wiki/Palindrome\u003c/a\u003e\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n \u003cb\u003eدیاگرام\u003c/b\u003e\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eپالیندرم یک واژه، عدد، عبارت یا\nدنباله دیگری از نمادها است که به صورت معکوس و به جلو خوانده شده، یکسان است.\n\n┌───┐┌───┐┌───┐\n│ 1 ││ 0 ││ 1 │ ────▷ پالیندرم\n└───┘└───┘└───┘\n\n┌───┐┌───┐┌───┐\n│ 1 ││ 2 ││ 3 │ ────▷ نه\n└───┘└───┘└───┘\n\n┌───┐\n│ 5 │ ────▷ پالیندرم\n└───┘\u003c/pre\u003e\n \n\n\t\n\t\t\n\t\n\n","Files":[{"Name":"palindrome.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This sample program shows you how to determine if an integer is a\n// palindrome or not.\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\ttt := []int{-1, 1, 9, 10, 1001, 125}\n\n\tfor _, input := range tt {\n\t\tsuccess := IsPalindrome(input)\n\n\t\tswitch success {\n\t\tcase true:\n\t\t\tfmt.Printf(\"%d is a palindrome\\n\", input)\n\n\t\tcase false:\n\t\t\tfmt.Printf(\"%d is NOT a palindrome\\n\", input)\n\t\t}\n\t}\n}\n\n// IsPalindrome checks if a integer is a Palindrome.\nfunc IsPalindrome(input int) bool {\n\n\t// A negative integer is not a palindrome.\n\tif input \u003c 0 {\n\t\treturn false\n\t}\n\n\t// An integer that is only one digit in length is a palindrome.\n\tif input \u003e= 0 \u0026\u0026 input \u003c 10 {\n\t\treturn true\n\t}\n\n\t// Reverse the digits in the integer.\n\trev := Reverse(input)\n\n\treturn input == rev\n}\n\n// Reverse takes the specified integer and reverses it.\nfunc Reverse(num int) int {\n\n\t// Construct result to its zero value.\n\tvar result int\n\n\t// Loop until num is zero.\n\tfor num != 0 {\n\n\t\t// Perform a modulus operation to get the last digit from the value set in num.\n\t\t// https://www.geeksforgeeks.org/find-first-last-digits-number/\n\t\t// Ex. For num = 125, last = 5\n\t\tlast := num % 10\n\n\t\t// Multiple the current result by 10 to shift the digits in\n\t\t// the current result to the left.\n\t\t// Ex. For result = 5, result = 50\n\t\tresult = result * 10\n\n\t\t// Add the digit we took from the end of num to the result.\n\t\t// Ex. For result = 21 and last = 5, result = 215\n\t\tresult += last\n\n\t\t// Remove the digit we just reversed from num.\n\t\t// Ex. For num = 125, num = 12\n\t\tnum = num / 10\n\t}\n\n\treturn result\n}\n","Hash":"BaP9VQ9ywVnbX0f9EEy1S0Nc+RE="}]}]} ,"algorithms-strings":{"Title":"عملیات رشته‌ها","Description":"این بخش مثال‌هایی ارائه می‌دهد که عملیات رشته‌ها را انجام می‌دهند.","Pages":[{"Title":"Permutation","Content":"\n \u003ch2\u003ePermutation\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/training/individual-on-demand/ultimate-go-bundle/\" target=\"_blank\"\u003eتماشای ویدئو\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003eنیاز به کمک مالی دارید؟ از \u003ca href=\"https://www.ardanlabs.com/scholarship/\" target=\"_blank\"\u003eفرم Scholarship\u003c/a\u003e ما استفاده کنید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n برنامه نمونه‌ای را پیاده‌سازی کرده است که بررسی می‌کند که یک رشته Permutation است یا نه.\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003eاطلاعات بیشتر در \u003ca href=\"https://en.wikipedia.org/wiki/Permutation\" target=\"_blank\"\u003ehttps://en.wikipedia.org/wiki/Permutation\u003c/a\u003e\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n \u003cb\u003eدیاگرام\u003c/b\u003e\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003ePermutation یک رشته مجموعه‌ای از تمام رشته‌هاست که حاوی همه\n کاراکترهای رشته اصلی باشند، بدون اهمیت ترتیب ترتیب کاراکترها.\n\n┌───┐┌───┐┌───┐\n│ G ││ O ││ D │ ──────┐\n└───┘└───┘└───┘ │ ────▷ Permutation\n┌───┐┌───┐┌───┐ │\n│ D ││ O ││ G │ ──────┘\n└───┘└───┘└───┘\n\n┌───┐┌───┐┌───┐\n│ G ││ O ││ D │ ──────┐\n└───┘└───┘└───┘ │ ────▷ نه\n┌───┐┌───┐ │\n│ D ││ O │ ──────┘\n└───┘└───┘\n\n┌───┐┌───┐┌───┐\n│ 1 ││ 0 ││ 0 │ ──────┐\n└───┘└───┘└───┘ │ ────▷ Permutation\n┌───┐┌───┐┌───┐ │\n│ 0 ││ 0 ││ 1 │ ──────┘\n└───┘└───┘└───┘\u003c/pre\u003e\n \n\n\t\n\t\t\n\t\n\n","Files":[{"Name":"permutation.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This sample program shows you how to determine if a string is a\n// permutation or not.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"sort\"\n)\n\nfunc main() {\n\ttt := []struct {\n\t\tinput1 string\n\t\tinput2 string\n\t}{\n\t\t{\"\", \"\"},\n\t\t{\"god\", \"dog\"},\n\t\t{\"god\", \"do\"},\n\t\t{\"1001\", \"0110\"},\n\t}\n\n\tfor _, test := range tt {\n\t\tsuccess := IsPermutation(test.input1, test.input2)\n\n\t\tswitch success {\n\t\tcase true:\n\t\t\tfmt.Printf(\"%q and %q is a permutation\\n\", test.input1, test.input2)\n\n\t\tcase false:\n\t\t\tfmt.Printf(\"%q and %q is NOT a permutation\\n\", test.input1, test.input2)\n\t\t}\n\t}\n}\n\n// =============================================================================\n\n// RuneSlice a custom type of a slice of runes.\ntype RuneSlice []rune\n\n// For sorting an RuneSlice.\nfunc (p RuneSlice) Len() int { return len(p) }\nfunc (p RuneSlice) Less(i, j int) bool { return p[i] \u003c p[j] }\nfunc (p RuneSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }\n\n// IsPermutation check if two strings are permutations.\nfunc IsPermutation(str1, str2 string) bool {\n\n\t// If the length are not equal they cannot be permutation.\n\tif len(str1) != len(str2) {\n\t\treturn false\n\t}\n\n\t// Convert each string into a collection of runes.\n\ts1 := []rune(str1)\n\ts2 := []rune(str2)\n\n\t// Sort each collection of runes.\n\tsort.Sort(RuneSlice(s1))\n\tsort.Sort(RuneSlice(s2))\n\n\t// Convert the collection of runes back to a string\n\t// and compare.\n\treturn string(s1) == string(s2)\n}\n","Hash":"wCQOzXyuqZ9oGkkfc09bEN6NbW8="}]},{"Title":"پالیندروم","Content":"\n \u003ch2\u003eپالیندروم\u003c/h2\u003e\n \n \n \u003cp\u003e\n برنامه نمونه‌ای پیاده‌سازی می‌کند که بررسی می‌کند که یک رشته یک پالیندروم است یا نه.\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003eاطلاعات بیشتر در \u003ca href=\"https://en.wikipedia.org/wiki/Palindrome\" target=\"_blank\"\u003ehttps://en.wikipedia.org/wiki/Palindrome\u003c/a\u003e\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n \u003cb\u003eدیاگرام\u003c/b\u003e\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eپالیندروم یک کلمه، عدد، عبارت یا دنباله دیگری از نمادها است که به صورت معکوس همانند از چپ به راست می‌خوانده می‌شود.\n\n┌───┐┌───┐┌───┐\n│ B ││ O ││ B │ ────▷ پالیندروم\n└───┘└───┘└───┘\n\n┌───┐┌───┐┌───┐\n│ T ││ E ││ D │ ────▷ نه\n└───┘└───┘└───┘\n\n┌───┐\n│ G │ ────▷ Paliپالیندرومndrome\n└───┘\u003c/pre\u003e\n \n\n\t\n\t\t\n\t\n\n","Files":[{"Name":"palindrome.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// This sample program shows you how to determine if a string is a\n// palindrome or not.\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\ttt := []string{\"\", \"G\", \"bob\", \"otto\", \"汉字汉\", \"test\"}\n\n\tfor _, input := range tt {\n\t\tsuccess := IsPalindrome(input)\n\n\t\tswitch success {\n\t\tcase true:\n\t\t\tfmt.Printf(\"%q is a palindrome\\n\", input)\n\n\t\tcase false:\n\t\t\tfmt.Printf(\"%q is NOT a palindrome\\n\", input)\n\t\t}\n\t}\n}\n\n// =============================================================================\n\n// IsPalindrome checks if a string is a Palindrome.\nfunc IsPalindrome(input string) bool {\n\n\t// If the input string is empty or as a length of 1 return true.\n\tif input == \"\" || len(input) == 1 {\n\t\treturn true\n\t}\n\n\t// Convert the input string into slice of runes for processing.\n\t// A rune represent a code point in the UTF-8 character set.\n\trunes := []rune(input)\n\n\t// Run over runes forward and backward comparing runes.\n\t// If runes[i] != runes[len(runes)-i-1] then it's not a palindrome.\n\tfor i, j := 0, len(runes)-1; i \u003c j; i, j = i+1, j-1 {\n\t\tif runes[i] != runes[j] {\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn true\n}\n","Hash":"KaZOiqhjgC1ycMeWIjDnrniPf9s="}]}]} ,"generics-behavior-constraints":{"Title":"رفتار به عنوان محدودیت","Description":"هر نوع عمومی نیاز به تعیین یک محدودیت دارد تا کامپایلر بداند چه جایگزین‌های نوع محسوسی را می‌تواند در زمان کامپایل بپذیرد یا رد کند.","Pages":[{"Title":"ژنریک‌ها - رفتار به عنوان محدودیت","Content":"\n \u003ch2\u003eژنریک‌ها - رفتار به عنوان محدودیت\u003c/h2\u003e\n \n \n \u003cp\u003e\n هر نوع عمومی نیاز به تعیین یک محدودیت دارد تا کامپایلر بداند چه جایگزین‌های نوع محسوسی را می‌تواند در زمان کامپایل بپذیرد یا رد کند. این مورد حتی در صورت عدم واقعیت محدودیتی در نوع عمومی مورد نیاز است، به همین دلیل شناسه محدودیت پیش‌تعیین شده به نام \u0026#34;هر\u0026#34; وجود دارد.\n \u003c/p\u003e\n \n\n \u003ch2\u003eویدیو\u003c/h2\u003e\n \n \n \u003cp\u003e\n تماشای سخنرانی من در مورد ژنریک‌ها که شما را از طریق تمام مثال‌ها در این بخش از تور راه می‌اندازد.\u0026#34;\n \u003c/p\u003e\n \n\u003ciframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/gIEPspmbMHM\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" allowfullscreen\u003e\u003c/iframe\u003e\n\n \u003ch2\u003eبررسی کد\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e1\u003c/b\u003e: Concrete stringify function\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e2\u003c/b\u003e: Type assertion stringify function\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e3\u003c/b\u003e: Interface stringify function\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e4\u003c/b\u003e: Generic stringify function\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n \u003ch2\u003eتوضیح داده‌شده\u003c/h2\u003e\n \n \n \u003cp\u003e\n نکته جالب این است که مفهوم یک محدودیت در زبان قبلاً وجود دارد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype User struct {\n name string\n}\n\nfunc (u User) String() string {\n return u.name\n}\n\ntype Stringer interface {\n String() string\n}\n\nfunc Concrete(u User) {\n u.String()\n}\n\nfunc Polymorphic(s Stringer) {\n s.String()\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n کد یک نوع محسوس به نام کاربر (User) تعریف کرده و یک متد به نام String پیاده‌سازی می‌کند که نام کاربر را باز می‌گرداند. سپس نوع رابطی به نام Stringer تعریف می‌شود که یک عمل رفتاری به نام String دارد که یک رشته را باز می‌گرداند. به لطف متدی که برای کاربر تعریف شده است، می‌توان گفت که نوع محسوس کاربر رابط Stringer را با استفاده از نحوه معنایی مقدار اجرا می‌کند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n تابع محسوس فقط یک تابع است که داده محسوس را بر اساس نوع آن پذیرفته و از آن استفاده می‌کند. همینطور، تابع چندریختی همان‌طور که از نامش پیداست، یک تابع است که داده محسوس را بر اساس امکاناتی که دارد پذیرفته و از آن استفاده می‌کند. این تفاوت اصلی بین یک تابع محسوس و چندریختی است. یکی تنها به یک نوع داده محدود است و دیگری نه. با این حال، بر روی چه داده‌های محسوسی که می‌توان به تابع چندریختی منتقل کرد محدودیت وجود دارد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n رابط Stringer این محدودیت را با تعریف یک مجموعه از رفتارهایی که داده محسوس باید قادر به اجرا کردن آنها باشد، تعریف می‌کند. وقتی به عنوان نوع ورودی استفاده می‌شود، کامپایلر می‌تواند تضمین کند که همیشه محدودیت رفتاری هنگامی که تابع فراخوانی می‌شود برآورده می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n تعدادی تابع ژنریک وجود دارند که نیاز به همان نوع محدودیت رفتاری دارند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc stringify[T fmt.Stringer](slice []T) []string {\n ret := make([]string, 0, len(slice))\n \n for _, value := range slice {\n ret = append(ret, value.String())\n }\n \n return ret\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n اینجا تابع ژنریک stringify وجود دارد. این تابع یک برش از نوع T را می‌پذیرد و یک برش از مقادیر رشته‌ای را که شامل نسخه رشته‌ای شده از هر مقدار از مجموعه ورودی است، باز می‌گرداند. کلید برای کار کردن این تابع تماس متد String با هر مقدار از نوع T است.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n مشکل این است که کامپایلر باید بداند و تأیید کند که مقادیر از نوع T واقعاً دارای یک متد به نام String هستند. وقتی نوع ژنریک T اعلام می‌شود، رابط fmt.Stringer به عنوان محدودیت ارائه می‌شود. حالا کامپایلر می‌داند که باید هر نوع جایگزین و داده‌هایی که به تابع منتقل می‌شوند، را برای این مجموعه رفتار بررسی کند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n این بسیار عالی است زیرا رابط دوباره برای همان هدف استفاده می‌شود و زبان به یک کلمه جدید نیاز ندارد.\n \u003c/p\u003e\n \n\n\n","Files":[{"Name":"example1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to implement a stringify function that is\n// specific to each of the concrete types implemented above. In each case,\n// the stringify function returns a slice of strings. These function use\n// the String method against the individual user or customer value.\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc stringifyUsers(users []user) []string {\n\tret := make([]string, 0, len(users))\n\tfor _, user := range users {\n\t\tret = append(ret, user.String())\n\t}\n\treturn ret\n}\n\nfunc stringifyCustomers(customers []customer) []string {\n\tret := make([]string, 0, len(customers))\n\tfor _, customer := range customers {\n\t\tret = append(ret, customer.String())\n\t}\n\treturn ret\n}\n\n// Defining two types that implement the fmt.Stringer interface. Each\n// implementation creates a stringified version of the concrete type.\n\ntype user struct {\n\tname string\n\temail string\n}\n\nfunc (u user) String() string {\n\treturn fmt.Sprintf(\"{type: \\\"user\\\", name: %q, email: %q}\", u.name, u.email)\n}\n\ntype customer struct {\n\tname string\n\temail string\n}\n\nfunc (u customer) String() string {\n\treturn fmt.Sprintf(\"{type: \\\"customer\\\", name: %q, email: %q}\", u.name, u.email)\n}\n\n// =============================================================================\n\nfunc main() {\n\tusers := []user{\n\t\t{name: \"Bill\", email: \"bill@ardanlabs.com\"},\n\t\t{name: \"Ale\", email: \"ale@whatever.com\"},\n\t}\n\n\ts1 := stringifyUsers(users)\n\n\tfmt.Println(\"users:\", s1)\n\n\t// -------------------------------------------------------------------------\n\n\tcustomers := []customer{\n\t\t{name: \"Google\", email: \"you@google.com\"},\n\t\t{name: \"MSFT\", email: \"you@msft.com\"},\n\t}\n\n\ts2 := stringifyCustomers(customers)\n\n\tfmt.Println(\"customers:\", s2)\n}\n","Hash":"aY/Cji6nBdSLeK004zvpHJPtbPs="},{"Name":"example2.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to implement an empty interface solution which\n// uses type assertions for the different concrete slices to be supported.\n// We've basically moved the functions from above into case statements.\n// This function uses the String method against the value.\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc stringifyAssert(v interface{}) []string {\n\tswitch list := v.(type) {\n\tcase []user:\n\t\tret := make([]string, 0, len(list))\n\t\tfor _, value := range list {\n\t\t\tret = append(ret, value.String())\n\t\t}\n\t\treturn ret\n\n\tcase []customer:\n\t\tret := make([]string, 0, len(list))\n\t\tfor _, value := range list {\n\t\t\tret = append(ret, value.String())\n\t\t}\n\t\treturn ret\n\t}\n\n\treturn nil\n}\n\n// Defining two types that implement the fmt.Stringer interface. Each\n// implementation creates a stringified version of the concrete type.\n\ntype user struct {\n\tname string\n\temail string\n}\n\nfunc (u user) String() string {\n\treturn fmt.Sprintf(\"{type: \\\"user\\\", name: %q, email: %q}\", u.name, u.email)\n}\n\ntype customer struct {\n\tname string\n\temail string\n}\n\nfunc (u customer) String() string {\n\treturn fmt.Sprintf(\"{type: \\\"customer\\\", name: %q, email: %q}\", u.name, u.email)\n}\n\n// =============================================================================\n\nfunc main() {\n\tusers := []user{\n\t\t{name: \"Bill\", email: \"bill@ardanlabs.com\"},\n\t\t{name: \"Ale\", email: \"ale@whatever.com\"},\n\t}\n\n\ts1 := stringifyAssert(users)\n\n\tfmt.Println(\"users:\", s1)\n\n\t// -------------------------------------------------------------------------\n\n\tcustomers := []customer{\n\t\t{name: \"Google\", email: \"you@google.com\"},\n\t\t{name: \"MSFT\", email: \"you@msft.com\"},\n\t}\n\n\ts2 := stringifyAssert(customers)\n\n\tfmt.Println(\"customers:\", s2)\n}\n","Hash":"0BsFmMunEiH0bZ3Khb3UXGSb3ks="},{"Name":"example3.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to implement a reflection solution which allows\n// a slice of any type to be provided and stringified. This is a generic\n// function thanks to the reflect package. Notice the call to the String\n// method via reflection.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n)\n\nfunc stringifyReflect(v interface{}) []string {\n\tval := reflect.ValueOf(v)\n\tif val.Kind() != reflect.Slice {\n\t\treturn nil\n\t}\n\n\tret := make([]string, 0, val.Len())\n\n\tfor i := 0; i \u003c val.Len(); i++ {\n\t\tm := val.Index(i).MethodByName(\"String\")\n\t\tif !m.IsValid() {\n\t\t\treturn nil\n\t\t}\n\n\t\tdata := m.Call(nil)\n\t\tret = append(ret, data[0].String())\n\t}\n\n\treturn ret\n}\n\n// Defining two types that implement the fmt.Stringer interface. Each\n// implementation creates a stringified version of the concrete type.\n\ntype user struct {\n\tname string\n\temail string\n}\n\nfunc (u user) String() string {\n\treturn fmt.Sprintf(\"{type: \\\"user\\\", name: %q, email: %q}\", u.name, u.email)\n}\n\ntype customer struct {\n\tname string\n\temail string\n}\n\nfunc (u customer) String() string {\n\treturn fmt.Sprintf(\"{type: \\\"customer\\\", name: %q, email: %q}\", u.name, u.email)\n}\n\n// =============================================================================\n\nfunc main() {\n\tusers := []user{\n\t\t{name: \"Bill\", email: \"bill@ardanlabs.com\"},\n\t\t{name: \"Ale\", email: \"ale@whatever.com\"},\n\t}\n\n\ts1 := stringifyReflect(users)\n\n\tfmt.Println(\"users:\", s1)\n\n\t// -------------------------------------------------------------------------\n\n\tcustomers := []customer{\n\t\t{name: \"Google\", email: \"you@google.com\"},\n\t\t{name: \"MSFT\", email: \"you@msft.com\"},\n\t}\n\n\ts2 := stringifyReflect(customers)\n\n\tfmt.Println(\"customers:\", s2)\n}\n","Hash":"vFVTlYOBPLHuigkJMbL0NUj4QwY="},{"Name":"example4.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to implement a generics solution which allows\n// a slice of some type T (to be determined later) to be passed and stringified.\n// This code more closely resembles the concrete implementations that we started\n// with and is easier to read than the reflect implementation. However, an\n// interface constraint of type fmt.Stringer is applied to allow the compiler\n// to know the value of type T passed requires a String method.\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc stringify[T fmt.Stringer](slice []T) []string {\n\tret := make([]string, 0, len(slice))\n\n\tfor _, value := range slice {\n\t\tret = append(ret, value.String())\n\t}\n\n\treturn ret\n}\n\n// Defining two types that implement the fmt.Stringer interface. Each\n// implementation creates a stringified version of the concrete type.\n\ntype user struct {\n\tname string\n\temail string\n}\n\nfunc (u user) String() string {\n\treturn fmt.Sprintf(\"{type: \\\"user\\\", name: %q, email: %q}\", u.name, u.email)\n}\n\ntype customer struct {\n\tname string\n\temail string\n}\n\nfunc (u customer) String() string {\n\treturn fmt.Sprintf(\"{type: \\\"customer\\\", name: %q, email: %q}\", u.name, u.email)\n}\n\n// =============================================================================\n\nfunc main() {\n\tusers := []user{\n\t\t{name: \"Bill\", email: \"bill@ardanlabs.com\"},\n\t\t{name: \"Ale\", email: \"ale@whatever.com\"},\n\t}\n\n\ts1 := stringify(users)\n\n\tfmt.Println(\"users:\", s1)\n\n\t// -------------------------------------------------------------------------\n\n\tcustomers := []customer{\n\t\t{name: \"Google\", email: \"you@google.com\"},\n\t\t{name: \"MSFT\", email: \"you@msft.com\"},\n\t}\n\n\ts2 := stringify(customers)\n\n\tfmt.Println(\"customers:\", s2)\n}\n","Hash":"nCaapl+iPPWYxarnhbYevkUBMf8="}]},{"Title":"تمرینات","Content":"\n \u003ch2\u003eتمرینات\u003c/h2\u003e\n \n \n \u003cp\u003e\n از الگو به عنوان نقطه شروع برای انجام تمرینات استفاده کنید. یک راه حل ممکن ارائه شده است.\n \u003c/p\u003e\n \n\n \u003ch2\u003eتمرین 1\u003c/h2\u003e\n \n \n \u003cp\u003e\n یک تابع ژنریک به نام marshal پیاده‌سازی کنید که می‌تواند JSON را marshal کند، اما تنها مقادیری را قبول کند که رابط json.Marshaler را پیاده‌سازی کرده‌اند.\n \u003c/p\u003e\n \n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n","Files":[{"Name":"exercise1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Implement a generic function named marshal that can marshal JSON but only\n// accepts values that implement the json.Marshaler interface.\npackage main\n\n// Add imports.\n\n// Declare the generic function named marshal that can accept only values\n// of type T that implement the json.Marshaler interface.\n\n// Define a type names user with two fields, name and email.\n\n// Implement a method that implements the json.Marshaler interface. Have the\n// method return a value of type user as JSON.\n\nfunc main() {\n\n\t// Construct a value of type user.\n\n\t// Call the generic marshal function.\n\n\t// Display the returned JSON.\n}\n","Hash":"BK7I3Fdqpz/dU8+DLlI+048Lv+o="},{"Name":"answer1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Implement a generic function named marshal that can marshal JSON but only\n// accepts values that implement the json.Marshaler interface.\npackage main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// Implement the generic function named marshal that can accept only values\n// of type T that implement the json.Marshaler interface.\nfunc marshal[T json.Marshaler](v T) ([]byte, error) {\n\tdata, err := json.Marshal(v)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn data, nil\n}\n\n// =============================================================================\n\n// Define a type names user with two fields, name and email.\ntype user struct {\n\tname string\n\temail string\n}\n\n// Declare a method that implements the json.Marshaler interface. Have the\n// method return a value of type user as JSON.\nfunc (u user) MarshalJSON() ([]byte, error) {\n\tv := fmt.Sprintf(\"{\\\"name\\\": %q, \\\"email\\\": %q}\", u.name, u.email)\n\treturn []byte(v), nil\n}\n\n// =============================================================================\n\nfunc main() {\n\n\t// Construct a value of type user.\n\tuser := user{\n\t\tname: \"Bill\",\n\t\temail: \"bill@ardanlabs.com\",\n\t}\n\n\t// Call the generic marshal function.\n\ts1, err := marshal(user)\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\treturn\n\t}\n\n\t// Display the returned JSON.\n\tfmt.Println(\"user:\", string(s1))\n}\n","Hash":"ITVn0ZvAKxUUeQbRq9k3sz5Qqc4="}]}]} ,"generics-type-constraints":{"Title":"نوع به عنوان محدودیت","Description":"این یک مفهوم جدید در Go است که محدودیت می‌تواند بر اساس یک مجموعه از انواع مشخص باشد.این تنها برای ژنریک‌ها کار می‌کند.","Pages":[{"Title":"ویدئو","Content":"\n \u003ch2\u003eویدئو\u003c/h2\u003e\n \n \n \u003cp\u003e\n تماشای سخنرانی من در مورد ژنریک‌ها که تمام مثال‌ها را در این بخش از تور را مرور می‌کند.\n \u003c/p\u003e\n \n\u003ciframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/gIEPspmbMHM\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" allowfullscreen\u003e\u003c/iframe\u003e\n \u003ch2\u003eمرور کد\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e1\u003c/b\u003e: محدودیت مبتنی بر نوع\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e2\u003c/b\u003e: محدودیت نوع پیش‌تعیین شده \u0026#34;comparable\u0026#34;\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e3\u003c/b\u003e: ترکیب محدودیت‌های نوع و رفتار\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n \u003ch2\u003eتوضیح داده شده\u003c/h2\u003e\n \n \n \u003cp\u003e\n توابع ژنریک یک نوع جدید از محدودیت ایجاد می‌کنند که نمی‌توانند توسط اعلان مجموعه متدهای رفتار حل شوند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc Add[T ???](v1 T, v2 T) T {\n return v1 \u0026#43; v2\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در اینجا یک تابع ژنریک وجود دارد که می‌خواهد دو مقدار از نوع T را بپذیرد، آن‌ها را با یکدیگر جمع کند، و سپس مجموع را به تماس‌گر برگرداند. این یک مشکل جالب است زیرا کامپایلر باید تماس به تابع را برای تنها مقادیری که می‌توانند در یک عمل جمع مورد استفاده قرار گیرند محدود کند. در حال حاضر مکانیکی برای اعلان این نوع محدودیت وجود ندارد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n تصمیم گرفته شد که از رابطه برای اعلان محدودیت استفاده شود و چیزی جدید اضافه شود.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype addOnly interface {\n string | int | int8 | int16 | int32 | int64 | float64\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n می‌توانید یک رابط اعلان کنید که یک مجموعه از انواع را تشکیل دهد که محدودیت را تشکیل می‌دهند. سپس این رابط را برای تابع ژنریک اعمال کنید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc Add[T addOnly](v1 T, v2 T) T {\n return v1 \u0026#43; v2\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n حالا کامپایلر می‌تواند تایید کند که مجموعه‌ای از انواع با عملیات‌هایی که تابع برای انجام عملیات‌هایی روی مقادیر این انواع نیاز دارد، سازگار است. وقتی رابط از انواع تعریف شده در خود زبان (built-in) استفاده می‌کند، رابط‌ها قابل استفاده میان بسته‌ها هستند. اما وقتی لیست انواع نمایانگر انواع تعریف شده توسط کاربر از بسته است، باید به یاد داشته باشید که این توابع ژنریک به انواع بسته بندی متصل شده‌اند و چیز دیگری نیستند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n رابط‌های اعلان شده با مجموعه‌ای از انواع نمی‌توانند در یک تابع چندشکلی سنتی استفاده شوند. این هرچند که منطقی نباشد، اما چیزی نیست که به معنای واقعی Go باشد، به این معنی که این تغییر در رابطهای زبانی همسان نیست.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n یکی از ایده‌ها این است که برای محدودیت‌های عملیات مشترک شناخته‌شده، شناسه‌های پیش‌تعیین شده داشته باشیم.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc index[T comparable](list []T, find T) int {\n for i, v := range list {\n if v == find {\n return i\n }\n }\n \n return -1\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n محدودیت قابل مقایسه توسط زبان اعلان شده است و محدودیتی را اعمال می‌کند که انواع باید قادر باشند که در یک عبارت مقایسه‌ای استفاده شوند. در این مثال، هر دو v و find متغیرهای نوع T هستند و مقایسه می‌شوند. ایده این است که یک بسته در کتابخانه استاندارد می‌تواند مجموعه‌ای مشترک از محدودیت‌ها را ارائه دهد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n هیچ محدودیتی برای اعلان یک رابط با همزمان داشتن یک مجموعه از انواع و مجموعه متد‌های رفتار وجود ندارد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype matcher[T any] interface {\n type person, food\n match(v T) bool\n}\n \nfunc match[T matcher[T]](list []T, find T) int {\n for i, v := range list {\n if v.match(find) {\n return i\n }\n }\n \n return -1\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n یک رابط ژنریک اعلان شده که در آن T نوع مقداری است که به یک متد با نام match منتقل می‌شود. این رابط همچنین استفاده از خود را فقط به مقادیر از نوع‌های تعریف شده توسط کاربر محدود می‌کند که شامل نوع‌های person و food می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n وقتی به تابع match نگاه می‌کنید، نیاز واضحی به محدود کردن تابع به انواع person و food وجود ندارد. اگر این چنین باشد، تابع match باید یک تابع چندشکلی سنتی باشد، نه یک تابع ژنریک. اگر دلیل معقولی وجود داشته باشد، تابع ژنریک می‌تواند برای اعمال این نوع محدودیت استفاده شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n یک نکته جانبی، نمی‌دانم که آیا این قابلیت ضروری است یا منطقی است یا خیر. این چیزی است که جامعه در طول زمان باید بفهمد.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eتمرین 1\u003c/h2\u003e\n \n \n \u003cp\u003e\n یک تابع ژنریک به نام copyfy را پیاده‌سازی کنید که تنها محدود به ایجاد کپی‌ها از آرایه‌های نوع string یا int باشد.\n \u003c/p\u003e\n \n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n","Files":[{"Name":"example1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to declare a constraint not based on behavior but\n// based on the type of data that is acceptable. This type of constrain is\n// important when functions (like Add) need to perform operations (like +)\n// that are not supported by all types.\npackage main\n\nimport \"fmt\"\n\ntype addOnly interface {\n\tstring | int | int8 | int16 | int32 | int64 | float64\n}\n\nfunc Add[T addOnly](v1 T, v2 T) T {\n\treturn v1 + v2\n}\n\nfunc main() {\n\tfmt.Println(Add(10, 20))\n\tfmt.Println(Add(\"A\", \"B\"))\n\tfmt.Println(Add(3.14159, 2.96))\n}\n","Hash":"wBj5w6XDFTJzoTUr4xHATIpa6dU="},{"Name":"example2.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to use the predeclared type constraint\n// \"comparable\". A type parameter with the comparable constraint accepts as\n// a type argument any comparable type. It permits the use of == and != with\n// values of that type parameter.\npackage main\n\nimport \"fmt\"\n\nfunc index[T comparable](list []T, find T) int {\n\tfor i, v := range list {\n\t\tif v == find {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn -1\n}\n\ntype person struct {\n\tname string\n\temail string\n}\n\nfunc main() {\n\tdurations := []int{5000, 10, 40}\n\tfindDur := 10\n\n\ti := index(durations, findDur)\n\tfmt.Printf(\"Index: %d for %d\\n\", i, findDur)\n\n\tpeople := []person{\n\t\t{name: \"bill\", email: \"bill@email.com\"},\n\t\t{name: \"jill\", email: \"jill@email.com\"},\n\t\t{name: \"tony\", email: \"tony@email.com\"},\n\t}\n\tfindPerson := person{name: \"tony\", email: \"tony@email.com\"}\n\n\ti = index(people, findPerson)\n\tfmt.Printf(\"Index: %d for %s\\n\", i, findPerson.name)\n}\n","Hash":"H4hYUHtSNdIJpoFBhmwYO6Mwye8="},{"Name":"example3.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to mix type and behavior constraints.\npackage main\n\nimport \"fmt\"\n\n// Defining two concrete types that implement a match method.\n\ntype person struct {\n\tname string\n\temail string\n}\n\nfunc (p person) match(v person) bool {\n\treturn p.name == v.name\n}\n\ntype food struct {\n\tname string\n\tcategory string\n}\n\nfunc (f food) match(v food) bool {\n\treturn f.name == v.name\n}\n\n// The matcher interface defines two constraints. First, it constrains the data\n// to what type is acceptable. Second, it constrains the behavior of the data.\n// The match method requires that a value of type T (to be determined later)\n// will be the input of the method.\n\n// Note: The type list inside the interface is not needed for match to work.\n// I'm trying to show how the type list and behavior can be combined.\n\ntype matcher[T any] interface {\n\tperson | food\n\tmatch(v T) bool\n}\n\n// The match function declares that the value of type T must implement the\n// matcher interface and is used for the slice and value arguments to the\n// function.\n\nfunc match[T matcher[T]](list []T, find T) int {\n\tfor i, v := range list {\n\t\tif v.match(find) {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn -1\n}\n\n// =============================================================================\n\nfunc main() {\n\tpeople := []person{\n\t\t{name: \"bill\", email: \"bill@email.com\"},\n\t\t{name: \"jill\", email: \"jill@email.com\"},\n\t\t{name: \"tony\", email: \"tony@email.com\"},\n\t}\n\tfindPerson := person{name: \"tony\"}\n\n\ti := match(people, findPerson)\n\tfmt.Printf(\"Match: Idx: %d for %s\\n\", i, findPerson.name)\n\n\tfoods := []food{\n\t\t{name: \"apple\", category: \"fruit\"},\n\t\t{name: \"carrot\", category: \"veg\"},\n\t\t{name: \"chicken\", category: \"meat\"},\n\t}\n\tfindFood := food{name: \"apple\"}\n\n\ti = match(foods, findFood)\n\tfmt.Printf(\"Match: Idx: %d for %s\\n\", i, findFood.name)\n}\n","Hash":"Delq1ICAgtxU892ZFrBJhwnecE8="},{"Name":"exercise1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Implement a generic function named copyfy that is constrained to only\n// making copies of slices of type string or int.\npackage main\n\n// Declare an interface named copyer that creates a constraint on\n// string and int.\n\n// Implement a generic function named copyfy that accepts a slice of some\n// type T but constrained on the copyer interface.\n\nfunc main() {\n\n\t// Construct a slice of string with three values.\n\n\t// Call the copyfy function to make a copy of the slice.\n\n\t// Display the slice and the copy.\n\n\t// Construct a slice of int with three values.\n\n\t// Call the copyfy function to make a copy of the slice.\n\n\t// Display the slice and the copy.\n}\n","Hash":"n4/iURoLOmTfj/Lo8hwL/3MmjEU="},{"Name":"answer1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Implement a generic function named copyfy that is constrained to only\n// making copies of slices of type string or int.\npackage main\n\nimport (\n\t\"fmt\"\n)\n\n// Declare an interface named copyer that creates a constraint on\n// string and int.\ntype copyer interface {\n\tstring | int\n}\n\n// Implement a generic function named copyfy that accepts a slice of some\n// type T but constrained on the copyer interface.\nfunc copyfy[T copyer](src []T) []T {\n\tdest := make([]T, len(src))\n\n\tcopy(dest, src)\n\n\treturn dest\n}\n\n// =============================================================================\n\nfunc main() {\n\n\t// Construct a slice of string with three values.\n\tsrc1 := []string{\"Bill\", \"Jill\", \"Joan\"}\n\n\t// Call the copyfy function to make a copy of the slice.\n\tdest1 := copyfy(src1)\n\n\t// Display the slice and the copy.\n\tfmt.Println(\"src string :\", src1)\n\tfmt.Println(\"dest string:\", dest1)\n\n\t// -------------------------------------------------------------------------\n\n\t// Construct a slice of int with three values.\n\tsrc2 := []int{10, 20, 30}\n\n\t// Call the copyfy function to make a copy of the slice.\n\tdest2 := copyfy(src2)\n\n\t// Display the slice and the copy.\n\tfmt.Println(\"src int :\", src2)\n\tfmt.Println(\"dest int:\", dest2)\n}\n","Hash":"4RsnZlqcCtUSyNPLWaXrLEZmX0E="}]}]} ,"interfaces":{"Title":"Interfaces (ربط ها)","Description":"رابط‌ها به برنامه‌ها ساختار می‌دهند و طراحی توسط ترکیب را تشویق می‌کنند.","Pages":[{"Title":"Interfaces (ربط ها)","Content":"\n \u003ch2\u003eInterfaces (ربط ها)\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/training/individual-on-demand/ultimate-go-bundle/\" target=\"_blank\"\u003eتماشای ویدئو\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003eنیاز به کمک مالی دارید؟ از \u003ca href=\"https://www.ardanlabs.com/scholarship/\" target=\"_blank\"\u003eفرم Scholarship\u003c/a\u003e ما استفاده کنید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n رابط‌ها به برنامه‌ها ساختار می‌دهند و طراحی توسط ترکیب را تشویق می‌کنند. آنها امکان ایجاد تقسیم‌بندی‌های تمیز و اجباری بین اجزا را ممکن می‌سازند. استاندارد‌سازی رابط‌ها می‌تواند انتظارات واضح و یکپارچه را مشخص کند. جداکنندگی به معنای کاهش وابستگی‌ها بین اجزا و انواعی که استفاده می‌کنند است. این منجر به صحت، کیفیت و قابلیت نگهداری می‌شود.\n \u003c/p\u003e\n \n\n \u003ch2\u003eمرور کد\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e1:\u003c/b\u003e کد تکراری که نیاز به چندشکلی دارد\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e2:\u003c/b\u003e چندشکلی\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e3:\u003c/b\u003e مجموعه متدها\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e4:\u003c/b\u003e آدرس مقدار\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e5:\u003c/b\u003e ذخیره‌سازی توسط مقدار\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e6:\u003c/b\u003e تایید انواع\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e7:\u003c/b\u003e تایید‌های شرطی نوع\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e8:\u003c/b\u003e رابط خالی و کلیدهای تعویض نوع\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e9:\u003c/b\u003e ذخیره مقادیر\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n \u003ch2\u003eمفاهیم رابط\u003c/h2\u003e\n \n \n \u003cp\u003e\n رابط‌ها به شما اجازه می‌دهند داده‌های واقعی را براساس آنچه که داده‌ها می‌توانند انجام دهند، گروه‌بندی کنید. این موضوع در مورد تمرکز بر روی این است که داده‌ها چه کارهایی انجام می‌دهند و نه اینکه داده‌ها چه هستند. رابط‌ها همچنین به کد من کمک می‌کنند تا از تغییرات خود جدا شود، زیرا براساس آنچه که داده می‌تواند انجام دهد، درخواست داده‌های واقعی می‌کند. این محدود به یک نوع داده نیست.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n باید بهترین تلاش‌های خود را بکنم تا درک کنم که چه تغییراتی در داده‌ها در راه است و از رابط‌ها برای جدا کردن برنامه خود از آن تغییرات استفاده کنم. رابط‌ها باید رفتار را توصیف کنند و نه وضعیت. آنها باید فعل باشند و نه اسم.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n رابط‌های عمومی که بر روی رفتار تمرکز دارند بهترین‌اند. رابط‌هایی که بیش از یک متد دارند، بیش از یک دلیل برای تغییر دارند. رابط‌هایی که بر اساس اسم‌هاست، تمایل به استفاده کمتر از مجدد، بیشتر به تغییرات حساس هستند و مفهوم رابط را از بین می‌برند. ابهام در مورد تغییر، مجوز حدس زدن نیست بلکه دستور به توقف و یادگیری بیشتر است. باید بین کدی که در مقابل تقلب دفاع می‌کند و کدی که در مقابل تصادفات محافظت می‌کند، تمییز قائل شوید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n زمانی از رابط استفاده کنید که:\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003eکاربران API نیاز به ارائه جزئیات اجرایی دارند.\u003c/li\u003e\n \n \u003cli\u003eAPI‌ها چندین اجرای داخلی نیاز به نگهداری دارند.\u003c/li\u003e\n \n \u003cli\u003eبخش‌هایی از API که ممکن است تغییر کنند شناسایی شده‌اند و نیاز به جداکردن دارند.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eاستفاده از رابط\u003c/h2\u003e\n \n \n \u003cp\u003e\n رابط را به دلیل استفاده از یک رابط به کار نبرید:\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003eبه خاطر استفاده از یک رابط.\u003c/li\u003e\n \n \u003cli\u003eبرای عمومی‌سازی یک الگوریتم.\u003c/li\u003e\n \n \u003cli\u003eزمانی که کاربران می‌توانند رابط‌های خود را اعلام کنند.\u003c/li\u003e\n \n \u003cli\u003eاگر واضح نباشد که رابط چگونه کد را بهبود می‌بخشد.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eرابط‌ها بی ارزش هستند\u003c/h2\u003e\n \n \n \u003cp\u003e\n اولین مورد مهمی که باید درک کنید این است که یک نوع رابط یک نوع بی ارزش (valueless) را اعلام می‌کند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype reader interface {\n read(b []byte) (int, error)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n نوع \u003ccode\u003ereader\u003c/code\u003e یک نوع رابط نیست، بلکه یک نوع رابط است. اعلام آن بر اساس ویژگی‌ها نیست بلکه بر اساس رفتار است. نوع‌های رابط یک مجموعه متدهای رفتاری را اعلام می‌کنند که داده‌های محسوب‌شونده باید آن را اجرا کنند تا رابط را برآورده کنند. در مورد نوع‌های رابط، هیچ چیز متمایزی وجود ندارد، بنابراین بی ارزش هستند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003evar r reader\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n به علت بی‌ارزش بودن آن‌ها، ساخت متغیر (مانند r) عجیب است زیرا در مدل برنامه‌نویسی ما، r وجود ندارد، بی ارزش است. هیچ چیز درباره r به تنهایی وجود ندارد که بتوانید آن را مدیریت یا تغییر دهید. این یک مفهوم بسیار مهم است که باید درک شود. من هرگز با مقادیر رابط کار نمی‌کنم، فقط با مقادیر محسوب می‌شوند. رابط دارای یک نمایشگر کامپایلر (نوع داخلی) دارد، اما از مدل برنامه‌نویسی ما، رابط‌ها بی ارزش هستند.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eپیاده‌سازی رابط‌ها\u003c/h2\u003e\n \n \n \u003cp\u003e\n Go یک زبان است که در آن کنوانسیون بر کانفیگوریشن غلبه دارد. وقتی به پیاده‌سازی یک نوع محسوب‌شونده در یک رابط می‌آید، هیچ استثناءی وجود ندارد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype reader interface {\n read(b []byte) (int, error)\n}\n\ntype file struct {\n name string\n}\n\nfunc (file) read(b []byte) (int, error) {\n s := \u0026#34;\u0026lt;rss\u0026gt;\u0026lt;channel\u0026gt;\u0026lt;title\u0026gt;Going Go\u0026lt;/title\u0026gt;\u0026lt;/channel\u0026gt;\u0026lt;/rss\u0026gt;\u0026#34;\n copy(b, s)\n return len(s), nil\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n کد یک نوع به نام \u0026#34;file\u0026#34; اعلام می‌کند و سپس یک متد به نام \u0026#34;read\u0026#34; اعلام می‌کند. به علت این دو اعلامیه، می‌توانید این را بگویید:\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u0026#34;نوع محسوب‌شونده \u0026#34;file\u0026#34; اکنون رابط \u0026#34;reader\u0026#34; را با استفاده از سمانتیک مقدار اجرا می‌کند.\u0026#34;\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n هر کلمه گفته شده مهم است. در Go، تنها کافیست مجموعه کامل متدهای رفتاری توسط یک رابط تعریف شده را اعلام کنید تا آن رابط را پیاده‌سازی کنید. در این مورد، همین کار را کرده‌ام زیرا رابط \u0026#34;reader\u0026#34; تنها یک عمل رفتاری به نام \u0026#34;read\u0026#34; را اعلام می‌کند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype reader interface {\n read(b []byte) (int, error)\n}\n\ntype pipe struct {\n name string\n}\n\nfunc (pipe) read(b []byte) (int, error) {\n s := `{name: \u0026#34;Bill\u0026#34;, title: \u0026#34;developer\u0026#34;}`\n copy(b, s)\n return len(s), nil\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در این کد، یک نوع به نام \u0026#34;pipe\u0026#34; اعلام شده و سپس یک متد با نام \u0026#34;read\u0026#34; اعلام شده است. به دلیل این دو اعلامیه، می‌توانید موارد زیر را بگویید:\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u0026#34;نوع محسوب‌شونده \u0026#34;pipe\u0026#34; اکنون رابط \u0026#34;reader\u0026#34; را با استفاده از سمانتیک مقدار اجرا می‌کند.\u0026#34;\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n اکنون شما دو نوع محسوب‌شونده دارید که هر یک از آن‌ها پیاده‌سازی منحصربه‌فرد خود را برای رابط \u0026#34;reader\u0026#34; دارند. دو نوع محسوب‌شونده، هر یک با پیاده‌سازی خود، یکی برای خواندن سیستم‌های فایل و دیگری برای شبکه‌ها.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eچندوجودی\u003c/h2\u003e\n \n \n \u003cp\u003e\n چندوجودی به معنای این است که یک قسمت کد بر اساس داده محسوب‌شونده که بر روی آن عمل می‌کند، رفتار خود را تغییر می‌دهد. این تعریف توسط تام کورتز، مخترع زبان برنامه‌نویسی BASIC، ارائه شده است. این تعریف را در ادامه استفاده خواهیم کرد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e// retrieve can read any device and process the data.\nfunc retrieve(r reader) error {\n data := make([]byte, 100)\n\n len, err := r.read(data)\n if err != nil {\n return err\n }\n\n fmt.Println(string(data[:len]))\n return nil\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n نگاهی به نوع داده‌ای که این تابع می‌پذیرد بیندازید. این تابع یک مقدار از نوع \u0026#34;reader\u0026#34; می‌خواهد. این امکان وجود ندارد زیرا \u0026#34;reader\u0026#34; یک رابط است و رابط‌ها از نوع‌های بدون مقدار هستند. نمی‌تواند بخواهد یک مقدار \u0026#34;reader\u0026#34;، چرا که چنین مقادیری وجود ندارند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n اگر این تابع درخواستی برای یک مقدار \u0026#34;reader\u0026#34; نمی‌کند، پس تابع درخواستی برای چه چیزی دارد؟ این تابع برای تنها چیزی درخواست دارد که می‌تواند درخواست دهد، یعنی داده محسوب‌شونده.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n تابع \u0026#34;retrieve\u0026#34; تابع چندوجودی است زیرا درخواست می‌کند داده محسوب‌شونده را بر اساس اینکه داده چیست (نوع محسوب‌شونده) نه بر اساس اینکه داده چه کارهایی انجام می‌دهد (نوع رابط)، برایش فرستاده شود.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003ef := file{\u0026#34;data.json\u0026#34;}\np := pipe{\u0026#34;cfg_service\u0026#34;}\n\nretrieve(f)\nretrieve(p)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n شما می‌توانید دو مقدار محسوب‌شونده بسازید، یکی از نوع \u0026#34;file\u0026#34; و دیگری از نوع \u0026#34;pipe\u0026#34;. سپس می‌توانید یک کپی از هر مقدار را به تابع چندوجودی ارسال کنید. این به این دلیل است که هر یک از این مقادیر تمام مجموعه‌ی متد‌های رفتاری تعریف‌شده توسط رابط \u0026#34;reader\u0026#34; را پیاده‌سازی می‌کنند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n وقتی که مقدار محسوب‌شونده \u0026#34;file\u0026#34; به تابع \u0026#34;retrieve\u0026#34; ارسال می‌شود، مقدار در داخل یک نوع داده داخلی با دو کلمه کلیدی ذخیره می‌شود که نمایانگر مقدار رابطی است.\n \u003c/p\u003e\n \n\n \u003ca href=\"/tour/eng/static/img/i1.png\" target=\"_blank\"\u003e\n \u003cimg class=\"codeimg\" src=\"/tour/eng/static/img/i1.png\"\u003e\n \u003c/a\u003e\n\n \n \u003cp\u003e\n در واقعیت، داده‌ی رابط از دو کلمه کلیدی تشکیل شده است. کلمه کلیدی دوم به مقدار ذخیره‌شده اشاره می‌کند. در این مورد، این یک کپی از مقدار \u0026#34;file\u0026#34; است چرا که رفتار رفتاری مقدار در حال اجرا است. کلمه کلیدی اول به یک ساختار داده‌ای ویژه اشاره می‌کند که به نام iTable معروف است.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n iTable دو مقصد دارد:\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003eاین توصیف می‌کند که نوع مقداری که در حال ذخیره است چیست. در این مورد، این یک مقدار \u0026#34;file\u0026#34; است.\u003c/li\u003e\n \n \u003cli\u003eاین اسامی تابع به اشکال اجرایی متدهای مجموعه‌ی متدهای مربوط به نوع مقداری که در حال ذخیره است، ارائه می‌دهد.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n وقتی تماس به متد \u0026#34;read\u0026#34; علیه مقدار رابطی انجام می‌شود، یک جستجوی iTable انجام می‌شود تا پیاده‌سازی محکم مربوط به متد \u0026#34;read\u0026#34; مرتبط با نوع زیر پیدا شود. سپس تماس به متد بر روی مقداری که در کلمه دوم ذخیره شده است، انجام می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n می‌توانید بگویید که \u0026#34;retrieve\u0026#34; یک تابع چندوجودی است چرا که مقدار محاسبه‌شونده \u0026#34;pipe\u0026#34; می‌تواند به \u0026#34;retrieve\u0026#34; ارسال شود و در این صورت تماس به \u0026#34;read\u0026#34; علیه مقدار رابطی تغییر رفتار می‌دهد. در این مورد، تماس به \u0026#34;read\u0026#34; در حالتی دیگر است که این بار از شبکه به جای خواندن یک فایل است.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eقوانین مجموعه متدها\u003c/h2\u003e\n \n \n \u003cp\u003e\n پیاده‌سازی یک رابط با استفاده از رفتار رفتاری برخی محدودیت‌ها را در خصوص پایداری رابط اعمال می‌کند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype notifier interface {\n notify()\n}\n\ntype user struct {\n name string\n email string\n}\n\nfunc (u *user) notify() {\n fmt.Printf(\u0026#34;Sending User Email To %s\u0026lt;%s\u0026gt;\\n\u0026#34;, u.name, u.email)\n}\n\nfunc sendNotification(n notifier) {\n n.notify()\n}\n\nfunc main() {\n u := user{\u0026#34;Bill\u0026#34;, \u0026#34;bill@email.com\u0026#34;}\n sendNotification(u)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n رابط notifier توسط نوع کاربر با استفاده از رفتار نشانگر اجرا می‌شود. وقتی از رفتار مقداری برای تماس چندوجودی استفاده می‌شود، پیام کامپایلر زیر تولید می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e\u0026#34;cannot use u (type user) as type notifier in argument to sendNotification:\nuser does not implement notifier (notify method has pointer receiver)\u0026#34;\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این به این دلیل است که در مشخصات مربوط به مجموعه متدها مجموعه‌ای خاص از قوانین وجود دارد. این قوانین تعریف می‌کنند کدام متدها به مقدار و اشاره‌گر نوعی وابسته هستند. این‌ها برای حفظ بالاترین سطح از اعتبار در برنامه‌ام استفاده می‌شوند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n این قوانین در مشخصات تعریف شده‌اند:\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003eبرای هر مقدار از نوع T، تنها متدهایی که با یک نشانگر مقدار برای آن نوع پیاده‌سازی شده‌اند، به مجموعه متدهای آن مقدار تعلق دارند.\u003c/li\u003e\n \n \u003cli\u003eبرای هر آدرس از نوع T، تمام متدهای پیاده‌سازی شده برای آن نوع به مجموعه متدهای آن مقدار تعلق دارند.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n به عبارت دیگر، هنگامی که با یک آدرس (اشاره‌گر) کار می‌کنید، تمام متدهای پیاده‌سازی شده به آن وابسته و قابل فراخوانی هستند. هنگام کار با یک مقدار، تنها متدهای پیاده‌سازی شده با نشانگر مقدار وابسته و قابل فراخوانی هستند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n در درس قبل در مورد متدها، شما می‌توانستید یک متد را بر روی یک قطعه داده محاسبه کنید بدون در نظر گرفتن رفتار داده که توسط نشانگر اعلام می‌شود. این به این دلیل است که کامپایلر می‌تواند تنظیمات را تغییر دهد تا تماس متد را انجام دهد. در این مورد، یک مقدار درون یک رابط ذخیره می‌شود و متدها باید وجود داشته باشند. هیچ تنظیماتی انجام نمی‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n سوال اکنون این است: چرا متدهای پیاده‌سازی شده با نشانگرهای اشاره‌گر به مقادیر نوع T وابسته نمی‌شوند؟ در اینجا چه مشکلی در اعتبار وجود دارد؟\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n یکی از دلایل این است که نمی‌توانید تضمین کنید که هر مقدار از نوع T قابل دسترسی باشد. اگر یک مقدار آدرس نداشته باشد، نمی‌تواند به اشتراک گذاشته شود.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype duration int\n\nfunc (d *duration) notify() {\n fmt.Println(\u0026#34;Sending Notification in\u0026#34;, *d)\n}\n\nfunc main() {\n duration(42).notify()\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خطای کامپایلر:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003ecannot call pointer method on duration(42)\ncannot take the address of duration(42)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در این مثال، مقدار 42 یک ثابت از نوع int است. با این وجود، این مقدار به یک مقدار از نوع duration تبدیل می‌شود، اما درون یک متغیر ذخیره نمی‌شود. این به این معناست که مقدار هرگز درون استک یا هیپ نیست. هیچ آدرسی وجود ندارد. ثابت‌ها تنها در زمان کامپایل زنده هستند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n دلیل دوم بزرگتر است. کامپایلر به شما می‌گوید که نمی‌توانید از رفتار مقداری استفاده کنید اگر انتخاب کرده‌اید که از رفتار نشانگری استفاده کنید. به عبارت دیگر، شما مجبور به به اشتراک گذاری مقدار با رابط می‌شوید زیرا کپی کردن یک مقداری که یک اشاره‌گر به آن اشاره می‌کند، ایمن نیست. اگر انتخاب کرده‌اید که متد را با رفتار نشانگری پیاده‌سازی کنید، دارید اعلام می‌کنید که مقداری از این نوع ایمن نیست که کپی شود.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc main() {\n u := user{\u0026#34;Bill\u0026#34;, \u0026#34;bill@email.com\u0026#34;}\n sendNotification(\u0026amp;u)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n برای رفع پیام کامپایلر، شما باید رفتار نشانگری را در تماس با تابع چندوجهی استفاده کرده و مقدار u را به اشتراک بگذارید. پاسخ این نیست که متد را به رفتار مقداری تغییر دهید.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eآرایه نشانگرها\u003c/h2\u003e\n \n \n \u003cp\u003e\n زمانی که یک آرایه از نوع رابط تعریف می‌کنید، من قادر به گروه‌بندی مقادیر مختلف از نوع‌های مختلف با توجه به اینکه چه کارهایی می‌توانند انجام دهند، هستم. این دلیلی است که Go به مفهوم زیرنوعی نیاز ندارد. این موضوع درباره DNA مشترک نیست، بلکه درباره رفتار مشترک است.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype printer interface {\n print()\n}\n\ntype canon struct {\n name string\n}\n\nfunc (c canon) print() {\n fmt.Printf(\u0026#34;Printer Name: %s\\n\u0026#34;, c.name)\n}\n\ntype epson struct {\n name string\n}\n\nfunc (e *epson) print() {\n fmt.Printf(\u0026#34;Printer Name: %s\\n\u0026#34;, e.name)\n}\n\nfunc main() {\n c := canon{\u0026#34;PIXMA TR4520\u0026#34;}\n e := epson{\u0026#34;WorkForce Pro WF-3720\u0026#34;}\n\n printers := []printer{\n c,\n \u0026amp;e,\n }\n c.name = \u0026#34;PROGRAF PRO-1000\u0026#34;\n e.name = \u0026#34;Home XP-4100\u0026#34;\n\n for _, p := range printers {\n p.print()\n }\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003ePrinter Name: PIXMA TR4520\nPrinter Name: Home XP-4100\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n کد نشان می‌دهد که چگونه یک آرایه از نوع رابط printer به من اجازه می‌دهد مجموعه‌ای از نوع‌های مختلف چاپگر را ایجاد کنم. با گردش در مجموعه و بهره‌بردن از چندوجهی، زیرا تماس با p.print به تغییر رفتار خود می‌پردازد بسته به مقدار مختلفی که کد با آن عمل می‌کند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n این مثال همچنین نشان می‌دهد که انتخاب رفتار داده چگونه رفتار برنامه را تغییر می‌دهد. وقتی داده با استفاده از رفتار مقداری ذخیره می‌شود، تغییرات در مقدار اصلی دیده نمی‌شوند. این به این دلیل است که یک کپی در داخل رابط ذخیره می‌شود. هنگامی که رفتار نشانگرها استفاده می‌شود، تمام تغییرات در مقدار اصلی دیده می‌شوند.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eیادداشت‌ها\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003eمجموعه متد برای یک مقدار، تنها شامل متد‌هایی است که با گیرنده مقداری پیاده‌سازی شده‌اند.\u003c/li\u003e\n \n \u003cli\u003eمجموعه متد برای یک نشانگر، شامل متد‌هایی است که با گیرنده‌های هم مقدار و هم نشانگر پیاده‌سازی شده‌اند.\u003c/li\u003e\n \n \u003cli\u003eمتد‌هایی که با گیرنده نشانگر اعلام شده‌اند، تنها با مقادیر نشانگر رابط پیاده‌سازی می‌شوند.\u003c/li\u003e\n \n \u003cli\u003eمتد‌های اعلام شده با گیرنده مقداری، رابط را با هم مقدار و هم نشانگر پیاده‌سازی می‌کنند.\u003c/li\u003e\n \n \u003cli\u003eقوانین مجموعه متد برای انواع رابط اعمال می‌شوند.\u003c/li\u003e\n \n \u003cli\u003eرابط‌ها از نوع مرجع هستند، با نشانگر به اشتراک گذاشته نشود.\u003c/li\u003e\n \n \u003cli\u003eاین چگونگی ایجاد رفتار چندوجهی در Go است.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eنقل قول‌ها\u003c/h2\u003e\n \n \n \u003cp\u003e\n \u0026#34;چندوجهی به این معناست که شما یک برنامه خاصی را می‌نویسید و بسته به داده‌هایی که بر روی آن عمل می‌کند، رفتار متفاوتی از خود نشان می‌دهد.\u0026#34; - تام کورتز (مخترع BASIC)\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u0026#34;رابط خالی چیزی نمی‌گوید.\u0026#34; - راب پایک\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u0026#34;طراحی هنر ترتیب دادن کد به نحوی است که امروز کار کند و همیشه قابل تغییر باشد.\u0026#34; - سندی متز\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u0026#34;یک انتزاع مناسب، کد را به نحوی از یکدیگر جدا می‌کند که هر تغییری به سراسر پایگاه کد نخورد.\u0026#34; - رونا استاینبرگ\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eخواندن اضافی\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://golang.org/doc/effective_go.html#interfaces\" target=\"_blank\"\u003eInterfaces\u003c/a\u003e \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://blog.golang.org/laws-of-reflection\" target=\"_blank\"\u003eThe Laws of Reflection\u003c/a\u003e - Rob Pike \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2014/05/methods-interfaces-and-embedded-types.html\" target=\"_blank\"\u003eMethods, Interfaces and Embedded Types in Go\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://rakyll.org/interface-pollution/\" target=\"_blank\"\u003eInterface Pollution\u003c/a\u003e - JBD \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://bravenewgeek.com/abstraction-considered-harmful/\" target=\"_blank\"\u003eAbstraction Considered Harmful\u003c/a\u003e - Tyler Treat \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2018/03/interface-values-are-valueless.html\" target=\"_blank\"\u003eInterface Values Are Valueless\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2017/07/interface-semantics.html\" target=\"_blank\"\u003eInterface Semantics\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.hyrumslaw.com/\" target=\"_blank\"\u003eHyrum\u0026#39;s Law\u003c/a\u003e - Hyrum \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.youtube.com/watch?v=Pjz9WrXeOW0\" target=\"_blank\"\u003eEngineering Innovation - Why Constraints Are Critical\u003c/a\u003e - André Eriksson (MUST WATCH)\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eتمرینات\u003c/h2\u003e\n \n \n \u003cp\u003e\n از قالب به عنوان نقطه شروع برای انجام تمرینات استفاده کنید. یک راه حل ممکن نیز ارائه شده است.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eتمرین 1\u003c/h2\u003e\n \n \n \u003cp\u003e\n بخش A: ابتدا یک رابط با نام speaker و یک متد به نام speak اعلام کنید. یک ساختار به نام english اعلام کنید که یک شخصی را نمایش می‌دهد که انگلیسی صحبت می‌کند و یک ساختار به نام chinese برای شخصی که چینی صحبت می‌کند. رابط speaker را برای هر یک از ساختارها با استفاده از گیرنده مقداری و این رشته‌های حرفه‌ای \u0026#34;Hello World\u0026#34; و \u0026#34;你好世界\u0026#34; پیاده‌سازی کنید. یک متغیر از نوع speaker اعلام کنید و آدرس یک مقدار از نوع english را اختصاص دهید و متد را فراخوانی کنید. این کار را برای یک مقدار از نوع chinese دوباره انجام دهید.\n\n\n بخش B: یک تابع جدید به نام sayHello اضافه کنید که یک مقدار از نوع speaker را قبول کند. این تابع را به گونه‌ای پیاده‌سازی کنید که متد speak را روی مقدار رابط فراخوانی کند. سپس مقادیر جدیدی از هر نوع ایجاد کرده و از تابع استفاده کنید.\n \u003c/p\u003e\n \n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n","Files":[{"Name":"example1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program that could benefit from polymorphic behavior with interfaces.\npackage main\n\nimport \"fmt\"\n\n// file defines a system file.\ntype file struct {\n\tname string\n}\n\n// read implements the reader interface for a file.\nfunc (file) read(b []byte) (int, error) {\n\ts := \"\u003crss\u003e\u003cchannel\u003e\u003ctitle\u003eGoing Go Programming\u003c/title\u003e\u003c/channel\u003e\u003c/rss\u003e\"\n\tcopy(b, s)\n\treturn len(s), nil\n}\n\n// pipe defines a named pipe network connection.\ntype pipe struct {\n\tname string\n}\n\n// read implements the reader interface for a network connection.\nfunc (pipe) read(b []byte) (int, error) {\n\ts := `{name: \"bill\", title: \"developer\"}`\n\tcopy(b, s)\n\treturn len(s), nil\n}\n\nfunc main() {\n\n\t// Create two values one of type file and one of type pipe.\n\tf := file{\"data.json\"}\n\tp := pipe{\"cfg_service\"}\n\n\t// Call each retrieve function for each concrete type.\n\tretrieveFile(f)\n\tretrievePipe(p)\n}\n\n// retrieveFile can read from a file and process the data.\nfunc retrieveFile(f file) error {\n\tdata := make([]byte, 100)\n\n\tlen, err := f.read(data)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfmt.Println(string(data[:len]))\n\treturn nil\n}\n\n// retrievePipe can read from a pipe and process the data.\nfunc retrievePipe(p pipe) error {\n\tdata := make([]byte, 100)\n\n\tlen, err := p.read(data)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfmt.Println(string(data[:len]))\n\treturn nil\n}\n","Hash":"MtGV+ylLTPGWf9ajuCRZbXGicYQ="},{"Name":"example2.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how polymorphic behavior with interfaces.\npackage main\n\nimport \"fmt\"\n\n// reader is an interface that defines the act of reading data.\ntype reader interface {\n\tread(b []byte) (int, error)\n}\n\n// file defines a system file.\ntype file struct {\n\tname string\n}\n\n// read implements the reader interface for a file.\nfunc (file) read(b []byte) (int, error) {\n\ts := \"\u003crss\u003e\u003cchannel\u003e\u003ctitle\u003eGoing Go Programming\u003c/title\u003e\u003c/channel\u003e\u003c/rss\u003e\"\n\tcopy(b, s)\n\treturn len(s), nil\n}\n\n// pipe defines a named pipe network connection.\ntype pipe struct {\n\tname string\n}\n\n// read implements the reader interface for a network connection.\nfunc (pipe) read(b []byte) (int, error) {\n\ts := `{name: \"bill\", title: \"developer\"}`\n\tcopy(b, s)\n\treturn len(s), nil\n}\n\nfunc main() {\n\n\t// Create two values one of type file and one of type pipe.\n\tf := file{\"data.json\"}\n\tp := pipe{\"cfg_service\"}\n\n\t// Call the retrieve function for each concrete type.\n\tretrieve(f)\n\tretrieve(p)\n}\n\n// retrieve can read any device and process the data.\nfunc retrieve(r reader) error {\n\tdata := make([]byte, 100)\n\n\tlen, err := r.read(data)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfmt.Println(string(data[:len]))\n\treturn nil\n}\n","Hash":"vQ23YgSP5WxiDxdOEGaXfKaAz5k="},{"Name":"example3.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to understand method sets.\npackage main\n\nimport \"fmt\"\n\n// notifier is an interface that defines notification\n// type behavior.\ntype notifier interface {\n\tnotify()\n}\n\n// user defines a user in the program.\ntype user struct {\n\tname string\n\temail string\n}\n\n// notify implements the notifier interface with a pointer receiver.\nfunc (u *user) notify() {\n\tfmt.Printf(\"Sending User Email To %s\u003c%s\u003e\\n\",\n\t\tu.name,\n\t\tu.email)\n}\n\nfunc main() {\n\n\t// Create a value of type User and send a notification.\n\tu := user{\"Bill\", \"bill@email.com\"}\n\n\t// Values of type user do not implement the interface because pointer\n\t// receivers don't belong to the method set of a value.\n\n\tsendNotification(u)\n\n\t// ./example1.go:36: cannot use u (type user) as type notifier in argument to sendNotification:\n\t// user does not implement notifier (notify method has pointer receiver)\n}\n\n// sendNotification accepts values that implement the notifier\n// interface and sends notifications.\nfunc sendNotification(n notifier) {\n\tn.notify()\n}\n","Hash":"MmedYFHTvmCUO7F79d2mk4nnDnM="},{"Name":"example4.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how you can't always get the address of a value.\npackage main\n\nimport \"fmt\"\n\n// duration is a named type with a base type of int.\ntype duration int\n\n// notify implements the notifier interface.\nfunc (d *duration) notify() {\n\tfmt.Println(\"Sending Notification in\", *d)\n}\n\nfunc main() {\n\tduration(42).notify()\n\n\t// ./example3.go:18: cannot call pointer method on duration(42)\n\t// ./example3.go:18: cannot take the address of duration(42)\n}\n","Hash":"HQrJIZ0J6Qc8qQEd6JQv05+ev0c="},{"Name":"example5.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how the concrete value assigned to\n// the interface is what is stored inside the interface.\npackage main\n\nimport \"fmt\"\n\n// printer displays information.\ntype printer interface {\n\tprint()\n}\n\n// cannon defines a cannon printer.\ntype cannon struct {\n\tname string\n}\n\n// print displays the printer's name.\nfunc (c cannon) print() {\n\tfmt.Printf(\"Printer Name: %s\\n\", c.name)\n}\n\n// epson defines a epson printer.\ntype epson struct {\n\tname string\n}\n\n// print displays the printer's name.\nfunc (e *epson) print() {\n\tfmt.Printf(\"Printer Name: %s\\n\", e.name)\n}\n\nfunc main() {\n\n\t// Create a cannon and epson printer.\n\tc := cannon{\"PIXMA TR4520\"}\n\te := epson{\"WorkForce Pro WF-3720\"}\n\n\t// Add the printers to the collection using both\n\t// value and pointer semantics.\n\tprinters := []printer{\n\n\t\t// Store a copy of the cannon printer value.\n\t\tc,\n\n\t\t// Store a copy of the epson printer value's address.\n\t\t\u0026e,\n\t}\n\n\t// Change the name field for both printers.\n\tc.name = \"PROGRAF PRO-1000\"\n\te.name = \"Home XP-4100\"\n\n\t// Iterate over the slice of printers and call\n\t// print against the copied interface value.\n\tfor _, p := range printers {\n\t\tp.print()\n\t}\n\n\t// When we store a value, the interface value has its own\n\t// copy of the value. Changes to the original value will\n\t// not be seen.\n\n\t// When we store a pointer, the interface value has its own\n\t// copy of the address. Changes to the original value will\n\t// be seen.\n}\n","Hash":"LO/QjoD8DBpC6HQNPYQjP2tI5EY="},{"Name":"example6.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show the syntax of type assertions.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n)\n\n// user defines a user in our application.\ntype user struct {\n\tid int\n\tname string\n}\n\n// finder represents the ability to find users.\ntype finder interface {\n\tfind(id int) (*user, error)\n}\n\n// userSVC is a service for dealing with users.\ntype userSVC struct {\n\thost string\n}\n\n// find implements the finder interface using pointer semantics.\nfunc (*userSVC) find(id int) (*user, error) {\n\treturn \u0026user{id: id, name: \"Anna Walker\"}, nil\n}\n\nfunc main() {\n\tsvc := userSVC{\n\t\thost: \"localhost:3434\",\n\t}\n\n\tif err := run(\u0026svc); err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n\n// run performs the find operation against the concrete data that\n// is passed into the call.\nfunc run(f finder) error {\n\tu, err := f.find(1234)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfmt.Printf(\"Found user %+v\\n\", u)\n\n\t// Ideally the finder abstraction would encompass all of\n\t// the behavior you care about. But what if, for some reason,\n\t// you really need to get to the concrete value stored inside\n\t// the interface?\n\n\t// Can you access the \"host\" field from the concrete userSVC type pointer\n\t// that is stored inside this interface variable? No, not directly.\n\t// All you know is the data has a method named \"find\".\n\t// ./example5.go:61:26: f.host undefined (type finder has no field or method host)\n\tlog.Println(\"queried\", f.host)\n\n\t// You can use a type assertion to get a copy of the userSVC pointer\n\t// that is stored inside the interface.\n\tsvc := f.(*userSVC)\n\tlog.Println(\"queried\", svc.host)\n\n\treturn nil\n}\n","Hash":"89x4vLkMOPAmjWR9VX+3gSaH68c="},{"Name":"example7.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show type assertions using the comma-ok idiom.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n)\n\n// user defines a user in our application.\ntype user struct {\n\tid int\n\tname string\n}\n\n// finder represents the ability to find users.\ntype finder interface {\n\tfind(id int) (*user, error)\n}\n\n// userSVC is a service for dealing with users.\ntype userSVC struct {\n\thost string\n}\n\n// find implements the finder interface using pointer semantics.\nfunc (*userSVC) find(id int) (*user, error) {\n\treturn \u0026user{id: id, name: \"Anna Walker\"}, nil\n}\n\n// mockSVC defines a mock service we will access.\ntype mockSVC struct{}\n\n// find implements the finder interface using pointer semantics.\nfunc (*mockSVC) find(id int) (*user, error) {\n\treturn \u0026user{id: id, name: \"Jacob Walker\"}, nil\n}\n\nfunc main() {\n\tvar svc mockSVC\n\n\tif err := run(\u0026svc); err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n\nfunc run(f finder) error {\n\tu, err := f.find(1234)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfmt.Printf(\"Found user %+v\\n\", u)\n\n\t// If the concrete type value stored inside the interface value is of the\n\t// type *userSVC, then \"ok\" will be true and \"svc\" will be a copy of the\n\t// pointer stored inside the interface.\n\tif svc, ok := f.(*userSVC); ok {\n\t\tlog.Println(\"queried\", svc.host)\n\t}\n\n\treturn nil\n}\n","Hash":"WTVchFbDMkXOXU0bcxtuSNoRcQs="},{"Name":"example8.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show the syntax and mechanics of type\n// switches and the empty interface.\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\n\t// fmt.Println can be called with values of any type.\n\tfmt.Println(\"Hello, world\")\n\tfmt.Println(12345)\n\tfmt.Println(3.14159)\n\tfmt.Println(true)\n\n\t// How can we do the same?\n\tmyPrintln(\"Hello, world\")\n\tmyPrintln(12345)\n\tmyPrintln(3.14159)\n\tmyPrintln(true)\n\n\t// - An interface is satisfied by any piece of data when the data exhibits\n\t// the full method set of behavior defined by the interface.\n\t// - The empty interface defines no method set of behavior and therefore\n\t// requires no method by the data being stored.\n\n\t// - The empty interface says nothing about the data stored inside\n\t// the interface.\n\t// - Checks would need to be performed at runtime to know anything about\n\t// the data stored in the empty interface.\n\t// - Decouple around well defined behavior and only use the empty\n\t// interface as an exception when it is reasonable and practical to do so.\n}\n\nfunc myPrintln(a interface{}) {\n\tswitch v := a.(type) {\n\tcase string:\n\t\tfmt.Printf(\"Is string : type(%T) : value(%s)\\n\", v, v)\n\tcase int:\n\t\tfmt.Printf(\"Is int : type(%T) : value(%d)\\n\", v, v)\n\tcase float64:\n\t\tfmt.Printf(\"Is float64 : type(%T) : value(%f)\\n\", v, v)\n\tdefault:\n\t\tfmt.Printf(\"Is unknown : type(%T) : value(%v)\\n\", v, v)\n\t}\n}\n","Hash":"/Zn24hUozDGbKMbNXA8SfkvNMm4="},{"Name":"example9.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program that explores how interface assignments work when\n// values are stored inside the interface.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"unsafe\"\n)\n\n// notifier provides support for notifying events.\ntype notifier interface {\n\tnotify()\n}\n\n// user represents a user in the system.\ntype user struct {\n\tname string\n}\n\n// notify implements the notifier interface.\nfunc (u user) notify() {\n\tfmt.Println(\"Alert\", u.name)\n}\n\nfunc inspect(n *notifier, u *user) {\n\tword := uintptr(unsafe.Pointer(n)) + uintptr(unsafe.Sizeof(\u0026u))\n\tvalue := (**user)(unsafe.Pointer(word))\n\tfmt.Printf(\"Addr User: %p Word Value: %p Ptr Value: %v\\n\", u, *value, **value)\n}\n\nfunc main() {\n\n\t// Create a notifier interface and concrete type value.\n\tvar n1 notifier\n\tu := user{\"bill\"}\n\n\t// Store a copy of the user value inside the notifier\n\t// interface value.\n\tn1 = u\n\n\t// We see the interface has its own copy.\n\t// Addr User: 0x1040a120 Word Value: 0x10427f70 Ptr Value: {bill}\n\tinspect(\u0026n1, \u0026u)\n\n\t// Make a copy of the interface value.\n\tn2 := n1\n\n\t// We see the interface is sharing the same value stored in\n\t// the n1 interface value.\n\t// Addr User: 0x1040a120 Word Value: 0x10427f70 Ptr Value: {bill}\n\tinspect(\u0026n2, \u0026u)\n\n\t// Store a copy of the user address value inside the\n\t// notifier interface value.\n\tn1 = \u0026u\n\n\t// We see the interface is sharing the u variables value\n\t// directly. There is no copy.\n\t// Addr User: 0x1040a120 Word Value: 0x1040a120 Ptr Value: {bill}\n\tinspect(\u0026n1, \u0026u)\n}\n","Hash":"A532r/lS4W00rft4xiLK35TyKRo="},{"Name":"exercise1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Declare an interface named speaker with a method named speak. Declare a struct\n// named english that represents a person who speaks english and declare a struct named\n// chinese for someone who speaks chinese. Implement the speaker interface for each\n// struct using a value receiver and these literal strings \"Hello World\" and \"你好世界\".\n// Declare a variable of type speaker and assign the address of a value of type english\n// and call the method. Do it again for a value of type chinese.\n//\n// Add a new function named sayHello that accepts a value of type speaker.\n// Implement that function to call the speak method on the interface value. Then create\n// new values of each type and use the function.\npackage main\n\n// Add imports.\n\n// Declare the speaker interface with a single method called speak.\n\n// Declare an empty struct type named english.\n\n// Declare a method named speak for the english type\n// using a value receiver. \"Hello World\"\n\n// Declare an empty struct type named chinese.\n\n// Declare a method named speak for the chinese type\n// using a pointer receiver. \"你好世界\"\n\n// sayHello accepts values of the speaker type.\nfunc sayHello( /* Declare parameter */ ) {\n\n\t// Call the speak method from the speaker parameter.\n}\n\nfunc main() {\n\n\t// Declare a variable of the interface speaker type\n\t// set to its zero value.\n\n\t// Declare a variable of type english.\n\n\t// Assign the english value to the speaker variable.\n\n\t// Call the speak method against the speaker variable.\n\n\t// Declare a variable of type chinese.\n\n\t// Assign the chinese pointer to the speaker variable.\n\n\t// Call the speak method against the speaker variable.\n\n\t// Call the sayHello function with new values and pointers\n\t// of english and chinese.\n}\n","Hash":"40l6NxzMGqMaXtoawen1Mu33lm0="},{"Name":"answer1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Declare an interface named speaker with a method named speak. Declare a struct\n// named english that represents a person who speaks english and declare a struct named\n// chinese for someone who speaks chinese. Implement the speaker interface for each\n// struct using a value receiver and these literal strings \"Hello World\" and \"你好世界\".\n// Declare a variable of type speaker and assign the address of a value of type english\n// and call the method. Do it again for a value of type chinese.\n//\n// Add a new function named sayHello that accepts a value of type speaker.\n// Implement that function to call the speak method on the interface value. Then create\n// new values of each type and use the function.\npackage main\n\nimport \"fmt\"\n\n// speaker implements the voice of anyone.\ntype speaker interface {\n\tspeak()\n}\n\n// english represents an english speaking person.\ntype english struct{}\n\n// speak implements the speaker interface using a\n// value receiver.\nfunc (english) speak() {\n\tfmt.Println(\"Hello World\")\n}\n\n// chinese represents a chinese speaking person.\ntype chinese struct{}\n\n// speak implements the speaker interface using a\n// pointer receiver.\nfunc (*chinese) speak() {\n\tfmt.Println(\"你好世界\")\n}\n\nfunc main() {\n\n\t// Declare a variable of the interface speaker type\n\t// set to its zero value.\n\tvar sp speaker\n\n\t// Declare a variable of type english.\n\tvar e english\n\n\t// Assign the english value to the speaker variable.\n\tsp = e\n\n\t// Call the speak method against the speaker variable.\n\tsp.speak()\n\n\t// Declare a variable of type chinese.\n\tvar c chinese\n\n\t// Assign the chinese pointer to the speaker variable.\n\tsp = \u0026c\n\n\t// Call the speak method against the speaker variable.\n\tsp.speak()\n\n\t// Call the sayHello function with new values and pointers\n\t// of english and chinese.\n\tsayHello(english{})\n\tsayHello(\u0026english{})\n\tsayHello(\u0026chinese{})\n\n\t// Why does this not work?\n\t// sayHello(chinese{})\n}\n\n// sayHello abstracts speaking functionality.\nfunc sayHello(sp speaker) {\n\tsp.speak()\n}\n","Hash":"Kue+3BwryhGgKrCd4yIGXiTPBts="}]}]} ,"variables":{"Title":"متغیرها","Description":"متغیرها قلب زبان های برنامه نویسی هستن که کار خواندن و نوشتن مقادیر در حافظه را انجام می دهند.","Pages":[{"Title":"متغیرها","Content":"\n \u003ch2\u003eمتغیرها\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/training/individual-on-demand/ultimate-go-bundle/\" target=\"_blank\"\u003eتماشای ویدئو\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003eنیاز به کمک مالی دارید؟ از \u003ca href=\"https://www.ardanlabs.com/scholarship/\" target=\"_blank\"\u003eفرم Scholarship\u003c/a\u003e ما استفاده کنید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n متغیر ها قلب زبان های برنامه نویسی هستن که کار خواندن و نوشتن مقادیر در حافظه را انجام می دهند. در زبان گو دسترسی به حافظه یک تایپ امن است.\n\n\n منظورمان این است که کامپایل خیلی جدی اجازه نمی دهد متغیر را خارج از scope تعریف کنید.\n \u003c/p\u003e\n \n\n \u003ch2\u003eبررسی کد\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e1\u003c/b\u003e: یک برنامه ساده که متغیر را تعریف کرده است.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\t\n\t\t\n\t\n\n\n \u003ch2\u003eتایپ های داخلی (Built-in)\u003c/h2\u003e\n \n \n \u003cp\u003e\n تایپ ها خوانایی و یکپارچگی را فراهم میکند:\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003eمقدار حافظه برای تخصیص چقدر است? (e.g. 1, 2, 4, 8 bytes)\u003c/li\u003e\n \n \u003cli\u003eحافطه چه چیزی را نشان می دهد? (e.g. int, uint, bool,..)\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n تایپ ها می توانند in64 یا int32 باشند:\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003euint8 یک نوع عدد unsigned با تخصیص ۱ بایت از حافظه می باشد\u003c/li\u003e\n \n \u003cli\u003eint32 یک نوع عدد signed با مصرف ۴ بایت از حافظه می باشد. زمانیکه شما تایپ (uint, int) تعریف میکنید بسته به معماری سیستم عامل حافظه تخصیص می یابد:\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \u003cul\u003e\n \n \u003cli\u003eمعماری 32 بیتی: یک int از نوع signed که ۴ بایت حافظه به آن تخصیص می یابد.\u003c/li\u003e\n \n \u003cli\u003eمعماری 64 بیتی: یک int از نوع signed که ۸ بایت از حافظه به آن تخصیص می یابد.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eاندازه کلمه\u003c/h2\u003e\n \n \n \u003cp\u003e\n کلمه اندازه نشان دهنده مقدار تخصیص حافظه مورد نیاز برای ذخیره اعداد صحیح است\n\n\n و اشاره گر برای یک معماری معین. مثلا:\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003eمعماری 32 بیتی: اندازه کلمه 4 بایت تخصیص حافظه است\u003c/li\u003e\n \n \u003cli\u003eمعماری 64 بیتی: اندازه کلمه 8 بایت تخصیص حافظه است\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n این مهم است زیرا Go دارای ساختارهای داده داخلی (maps, channels, slices, interfaces, functions) دارد که اعداد صحیح و اشاره‌گرها را ذخیره می‌کنند.\n\n\n اندازه این ساختارهای داده بر اساس معماری استفاده شده برای ساخت برنامه تعیین می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n در Go، مقدار حافظه اختصاص داده شده برای یک مقدار از نوع int، یک اشاره‌گر یا یک کلمه همیشه بر روی همان معماری یکسان خواهد بود.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eمفهوم مقدار صفرم\u003c/h2\u003e\n \n \n \u003cp\u003e\n هر مقداری که در Go ایجاد می‌کنید حداقل به حالت مقدار صفر خود مقداردهی اولیه می‌شود مگر اینکه مقدار مقداردهی اولیه را در زمان ایجاد مشخص کنید. مقدار صفر به معنای تنظیم هر بیت در هر بایت به صفر است.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n این کار برای اطمینان از اصالت داده انجام می‌شود و به صورت رایگان نیست. زمانی طول می‌کشد تا الکترون‌ها از طریق دستگاه حرکت کنند تا بیت‌ها را مجدداً تنظیم کنند، اما همیشه باید اصالت را از عملکرد بالاتر در نظر بگیرید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eمقدار صفرم تایپ\nBoolean false\nInteger 0\nFloat 0\nComplex 0i\nString \u0026#34;\u0026#34; (خالی)\nPointer nil\u003c/pre\u003e\n \n\n\n \u003ch2\u003eتعریف و راه اندازی\u003c/h2\u003e\n \n \n \u003cp\u003e\n کلمه کلیدی var می‌تواند برای ایجاد مقادیر به حالت مقدار صفر برای تمام انواع مورد استفاده قرار گیرد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003evar a int\nvar b string\nvar c float64\nvar d bool\n\nfmt.Printf(\u0026#34;var a int \\t %T [%v]\\n\u0026#34;, a, a)\nfmt.Printf(\u0026#34;var b string \\t %T [%v]\\n\u0026#34;, b, b)\nfmt.Printf(\u0026#34;var c float64 \\t %T [%v]\\n\u0026#34;, c, c)\nfmt.Printf(\u0026#34;var d bool \\t %T [%v]\\n\\n\u0026#34;, d, d)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003evar a int int [0]\nvar b string string []\nvar c float64 float64 [0]\nvar d bool bool [false]\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n رشته‌ها از مجموعه‌ای از بایت‌ها استفاده می‌کنند که مجموعه کاراکترهای UTF8 را نمایان می‌کنند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n یک رشته (string) در Go یک ساختار داده داخلی دو کلمه‌ای است که شامل موارد زیر است:\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003eکلمه اول یک اشاره به آرایه پشتیبان از بایت‌هاست.\u003c/li\u003e\n \n \u003cli\u003eکلمه دوم طول یا تعداد بایت‌ها در آرایه پشتیبان را نمایان می‌کند.\u003c/li\u003e\n \n \u003cli\u003eاگر رشته به حالت مقدار صفر خود تنظیم شود، آنگاه کلمه اول نال (nil) و کلمه دوم صفر (0) خواهد بود.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n با استفاده از اپراتور تعریف متغیر کوتاه، شما می‌توانید به صورت همزمان یک مقدار تعریف کنید، بسازید و مقداردهی اولیه کنید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eaa := 10 // int [10]\nbb := \u0026#34;hello\u0026#34; // string [hello]\ncc := 3.14159 // float64 [3.14159]\ndd := true // bool [true]\n\nfmt.Printf(\u0026#34;aa := 10 \\t %T [%v]\\n\u0026#34;, aa, aa)\nfmt.Printf(\u0026#34;bb := \\\u0026#34;hello\\\u0026#34; \\t %T [%v]\\n\u0026#34;, bb, bb)\nfmt.Printf(\u0026#34;cc := 3.14159 \\t %T [%v]\\n\u0026#34;, cc, cc)\nfmt.Printf(\u0026#34;dd := true \\t %T [%v]\\n\\n\u0026#34;, dd, dd)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eaa := 10 int [10]\nbb := \u0026#34;hello\u0026#34; string [hello]\ncc := 3.14159 float64 [3.14159]\ndd := true bool [true]\u003c/pre\u003e\n \n\n\n \u003ch2\u003eConversion در مقابل Casting\u003c/h2\u003e\n \n \n \u003cp\u003e\n زبان گو چیزی به نام casting ندارد. به جای اینکه کامپایلر را به نقشه‌بردن یک مجموعه بایت به نمایشگر دیگری دعوت کنید، بایت‌ها باید به یک مکان حافظه جدید برای نمایشگر جدید کپی شوند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eaaa := int32(10)\nfmt.Printf(\u0026#34;aaa := int32(10) %T [%v]\\n\u0026#34;, aaa, aaa)\n\nخروجی:\naaa := int32(10) int32 [10]\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n Go واقعاً یک بسته در کتابخانه استاندارد به نام unsafe دارد اگر نیاز به انجام یک عملیات تبدیل واقعی دارید. واقعاً باید از استفاده از آن اجتناب کنید و به خودتان صادق باشید که چرا در نظر دارید از آن استفاده کنید. انجام یک تبدیل برای این نوع عملیات‌ها بالاترین سطح اصالت را فراهم می‌کند.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eنکات\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003eهدف تمامی برنامه‌ها و تمامی بخش‌های آن برنامه‌ها، تبدیل داده از یک فرم به فرم دیگر است.\u003c/li\u003e\n \n \u003cli\u003eکد اصولاً حافظه را تخصیص می‌دهد، از حافظه می‌خواند و به حافظه می‌نویسد.\u003c/li\u003e\n \n \u003cli\u003eدرک نوع (تایپ) برای نوشتن کد خوب و درک کد بسیار حیاتی است.\u003c/li\u003e\n \n \u003cli\u003eاگر داده را نفهمیدید، مسئله را نمی‌فهمیدید.\u003c/li\u003e\n \n \u003cli\u003eبا درک داده، مسئله را بهتر می‌فهمید.\u003c/li\u003e\n \n \u003cli\u003eزمانی که متغیرها به حالت مقدار صفر خود تعریف می‌شوند، از کلمه کلیدی var استفاده کنید.\u003c/li\u003e\n \n \u003cli\u003eزمانی که متغیرها تعریف و مقداردهی اولیه می‌شوند، از اپراتور تعریف متغیر کوتاه استفاده کنید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eخواندن موارد بیشتر\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://golang.org/ref/spec#Boolean_types\" target=\"_blank\"\u003eBuilt-In Types\u003c/a\u003e \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://golang.org/doc/effective_go.html#variables\" target=\"_blank\"\u003eVariables\u003c/a\u003e \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2013/08/gustavos-ieee-754-brain-teaser.html\" target=\"_blank\"\u003eGustavo\u0026#39;s IEEE-754 Brain Teaser\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.youtube.com/watch?v=sFUSP8Au_PE\" target=\"_blank\"\u003eWhat\u0026#39;s in a name\u003c/a\u003e \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"http://arcanesentiment.blogspot.com/2015/01/a-brief-history-of-type.html\" target=\"_blank\"\u003eA brief history of “type”\u003c/a\u003e - Arcane Sentiment \u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n","Files":[{"Name":"example1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// The playground is actually a 64-bit env with 32-bit pointers\n// The os/arch combo is named nacl/amd64p32\n\n// Sample program to show how to declare variables.\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\n\t// Declare variables that are set to their zero value.\n\tvar a int\n\tvar b string\n\tvar c float64\n\tvar d bool\n\n\tfmt.Printf(\"var a int \\t %T [%v]\\n\", a, a)\n\tfmt.Printf(\"var b string \\t %T [%v]\\n\", b, b)\n\tfmt.Printf(\"var c float64 \\t %T [%v]\\n\", c, c)\n\tfmt.Printf(\"var d bool \\t %T [%v]\\n\\n\", d, d)\n\n\t// Declare variables and initialize.\n\t// Using the short variable declaration operator.\n\taa := 10\n\tbb := \"hello\"\n\tcc := 3.14159\n\tdd := true\n\n\tfmt.Printf(\"aa := 10 \\t %T [%v]\\n\", aa, aa)\n\tfmt.Printf(\"bb := \\\"hello\\\" \\t %T [%v]\\n\", bb, bb)\n\tfmt.Printf(\"cc := 3.14159 \\t %T [%v]\\n\", cc, cc)\n\tfmt.Printf(\"dd := true \\t %T [%v]\\n\\n\", dd, dd)\n\n\t// Specify type and perform a conversion.\n\taaa := int32(10)\n\n\tfmt.Printf(\"aaa := int32(10) %T [%v]\\n\", aaa, aaa)\n}\n\n/*\n\tZero Values:\n\tType Initialized Value\n\tBoolean false\n\tInteger 0\n\tFloating Point 0\n\tComplex 0i\n\tString \"\" (empty string)\n\tPointer nil\n*/\n","Hash":"Jz8tOHBaIG3Y/qtBLfeDA+q+bkk="}]},{"Title":"تمرینات","Content":"\n \u003ch2\u003eتمرینات\u003c/h2\u003e\n \n \n \u003cp\u003e\n از قالب به عنوان نقطه شروع برای انجام تمرینات استفاده کنید. یک راه‌حل ممکن ارائه شده است.\n \u003c/p\u003e\n \n\n \u003ch2\u003eتمرین 1\u003c/h2\u003e\n \n \n \u003cp\u003e\n \u003cb\u003eبخش\u003c/b\u003e \u003cb\u003eA:\u003c/b\u003e سه متغیر را اعلام کنید که به مقدار صفر خود مقداردهی شوند و\n\n\n سه متغیر دیگر را با مقدار ابتدایی اعلام کنید. متغیرهایی از نوع string، int و bool اعلام کنید.\n\n\n مقادیر این متغیرها را نمایش دهید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u003cb\u003eبخش\u003c/b\u003e \u003cb\u003eB:\u003c/b\u003e یک متغیر جدید از نوع float32 اعلام کنید و متغیر را با تبدیل مقدار ابتدایی عدد Pi (3.14) مقداردهی اولیه کنید.\n \u003c/p\u003e\n \n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n","Files":[{"Name":"exercise1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Declare three variables that are initialized to their zero value and three\n// declared with a literal value. Declare variables of type string, int and\n// bool. Display the values of those variables.\n//\n// Declare a new variable of type float32 and initialize the variable by\n// converting the literal value of Pi (3.14).\npackage main\n\n// Add imports\n\n// main is the entry point for the application.\nfunc main() {\n\n\t// Declare variables that are set to their zero value.\n\n\t// Display the value of those variables.\n\n\t// Declare variables and initialize.\n\t// Using the short variable declaration operator.\n\n\t// Display the value of those variables.\n\n\t// Perform a type conversion.\n\n\t// Display the value of that variable.\n}\n","Hash":"ZlFzQamBPuTIkjogQuPcbbPS/k4="},{"Name":"answer1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Declare three variables that are initialized to their zero value and three\n// declared with a literal value. Declare variables of type string, int and\n// bool. Display the values of those variables.\n//\n// Declare a new variable of type float32 and initialize the variable by\n// converting the literal value of Pi (3.14).\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\n\t// Declare variables that are set to their zero value.\n\tvar age int\n\tvar name string\n\tvar legal bool\n\n\t// Display the value of those variables.\n\tfmt.Println(age)\n\tfmt.Println(name)\n\tfmt.Println(legal)\n\n\t// Declare variables and initialize.\n\t// Using the short variable declaration operator.\n\tmonth := 10\n\tdayOfWeek := \"Tuesday\"\n\thappy := true\n\n\t// Display the value of those variables.\n\tfmt.Println(month)\n\tfmt.Println(dayOfWeek)\n\tfmt.Println(happy)\n\n\t// Perform a type conversion.\n\tpi := float32(3.14)\n\n\t// Display the value of that variable.\n\tfmt.Printf(\"%T [%v]\\n\", pi, pi)\n}\n","Hash":"DM2Gkjg0Fknlwe/gt+enkT/ImB8="}]}]} ,"composition-decoupling":{"Title":"جداسازی (Decoupling)","Description":"ترکیب به‌علاوه مکانیک جاسازی نوع را فراتر از مکانیک جاسازی نوع می‌برد و بیش از یک پارادایم است.","Pages":[{"Title":"جداسازی (Decoupling)","Content":"\n \u003ch2\u003eجداسازی (Decoupling)\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/training/individual-on-demand/ultimate-go-bundle/\" target=\"_blank\"\u003eتماشای ویدئو\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003eنیاز به کمک مالی دارید؟ از \u003ca href=\"https://www.ardanlabs.com/scholarship/\" target=\"_blank\"\u003eفرم Scholarship\u003c/a\u003e ما استفاده کنید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n روش بهتر برای بهره‌برداری از جاسازی نوع از طریق الگوی طراحی ترکیبی است. این کلید برای حفظ پایداری در نرم‌افزار شما است، با داشتن قابلیت سازگاری با تغییرات داده و تبدیلاتی که در پیش رو هستند.\n \u003c/p\u003e\n \n\n \u003ch2\u003eبررسی کد\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e1:\u003c/b\u003e ترکیب ساختار\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e2:\u003c/b\u003e جداسازی با اینترفیس\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e3:\u003c/b\u003e ترکیب اینترفیس\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e4:\u003c/b\u003e جداسازی با ترکیب اینترفیس\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e5:\u003c/b\u003e حذف آلودگی اینترفیس\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e6:\u003c/b\u003e دقیق‌تر API\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n \n \u003cp\u003e\n **مکانیک‌های جداسازی\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n ایده این است که از نوع‌های کوچک‌تر تا نوع‌های بزرگتر ترکیب شود و بر روی ترکیب رفتار متمرکز شود.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype Xenia struct {\n Host string\n Timeout time.Duration\n}\n\nfunc (*Xenia) Pull(d *Data) error {\n switch rand.Intn(10) {\n case 1, 9:\n return io.EOF\n case 5:\n return errors.New(\u0026#34;Error reading data from Xenia\u0026#34;)\n default:\n d.Line = \u0026#34;Data\u0026#34;\n fmt.Println(\u0026#34;In:\u0026#34;, d.Line)\n return nil\n }\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n نوع Xenia یک سیستم را نمایش می‌دهد که نیاز است داده‌ها را از آن بخوانید. پیاده‌سازی مهم نیست. آنچه مهم است این است که متد Pull ممکن است موفق، ناموفق یا هیچ داده‌ای برای خواندن نداشته باشد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype Pillar struct {\n Host string\n Timeout time.Duration\n}\n\nfunc (*Pillar) Store(d *Data) error {\n fmt.Println(\u0026#34;Out:\u0026#34;, d.Line)\n return nil\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n نوع Pillar یک سیستم را نمایش می‌دهد که نیاز است داده‌ها را در آن ذخیره کنید. مهمترین نکته در اینجا این است که متد Store ممکن است موفق یا ناموفق باشد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n این دو نوع نشان دهنده یک لایه ابتدایی از کد هستند که رفتار پایه مورد نیاز برای حل مشکلات تجاری خواندن داده‌ها از Xenia و ذخیره آن‌ها در Pillar را فراهم می‌کنند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc Pull(x *Xenia, data []Data) (int, error) {\n for i := range data {\n if err := x.Pull(\u0026amp;data[i]); err != nil {\n return i, err\n }\n }\n\n return len(data), nil\n}\n\nfunc Store(p *Pillar, data []Data) (int, error) {\n for i := range data {\n if err := p.Store(\u0026amp;data[i]); err != nil {\n return i, err\n }\n }\n\n return len(data), nil\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n لایه بعدی از کد توسط این دو تابع، Pull و Store، نمایش داده می‌شود. آن‌ها بر روی لایه ابتدایی کد ساخته می‌شوند با دریافت یک مجموعه از مقادیر داده برای خواندن یا ذخیره کردن در سیستم‌های مربوطه. این توابع بر روی انواع مشخص Xenia و Pillar تمرکز می‌کنند زیرا این سیستم‌ها همان سیستم‌هایی هستند که برنامه در حال حاضر نیاز دارد با آن‌ها کار کند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc Copy(sys *System, batch int) error {\n data := make([]Data, batch)\n\n for {\n i, err := Pull(\u0026amp;sys.Xenia, data)\n if i \u0026gt; 0 {\n if _, err := Store(\u0026amp;sys.Pillar, data[:i]); err != nil {\n return err\n }\n }\n\n if err != nil {\n return err\n }\n }\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n تابع Copy بر روی توابع Pull و Store ساخته می‌شود تا تمام داده‌های در انتظار برای هر اجرا را منتقل کند. اگر توجه کنید، پارامتر اول تابع Copy نوعی به نام System است.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype System struct {\n Xenia\n Pillar\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n ایده اولیه نوع System این است که یک سیستم را ترکیب کند که بداند چگونه باید Pull و Store کند. در این حالت، توانایی Pull و Store را از Xenia و Pillar ترکیب می‌کند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc main() {\n sys := System{\n Xenia: Xenia{\n Host: \u0026#34;localhost:8000\u0026#34;,\n Timeout: time.Second,\n },\n Pillar: Pillar{\n Host: \u0026#34;localhost:9000\u0026#34;,\n Timeout: time.Second,\n },\n }\n\n if err := Copy(\u0026amp;sys, 3); err != io.EOF {\n fmt.Println(err)\n }\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n سرانجام، تابع اصلی می‌تواند به گونه‌ای نوشته شود که یک Xenia و Pillar را در ترکیب یک System ایجاد کند. سپس System می‌تواند به تابع Copy ارسال شود و جریان داده بین دو سیستم آغاز شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n با این کلیه کد، حالا یک پیشنویس اولیه از یک راه‌حل قابل اجرا برای یک مسئله خاص دارید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n **جداسازی با رابطه‌ها\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n مرحله بعد این است که بفهمید چه چیزی در برنامه قابل تغییر است. در این حالت، سیستم‌های خود بتوانند تغییر کنند. امروز Xenia و Pillar هستند، اما فردا ممکن است Alice و Bob باشند. با این دانش، می‌خواهید راه‌حل موجود را از این تغییر جدا کنید. برای این کار، می‌خواهید توابع موجود را به توابع چندریختی (polymorphic) تغییر دهید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc Pull(p Puller, data []Data) (int, error) {\n for i := range data {\n if err := p.Pull(\u0026amp;data[i]); err != nil {\n return i, err\n }\n }\n\n return len(data), nil\n}\n\nfunc Store(s Storer, data []Data) (int, error) {\n for i := range data {\n if err := s.Store(\u0026amp;data[i]); err != nil {\n return i, err\n }\n }\n\n return len(data), nil\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در حال حاضر، تابع Pull یک مقدار Xenia را می‌پذیرد و تابع Store یک مقدار Pillar را می‌پذیرد. در پایان، مهم نبود Xenia و Pillar، آنچه مهم است یک مقدار قطعی است که بداند چگونه باید Pull و Store کند. شما می‌توانید این توابع قطعی را به توابع چندریختی تغییر دهید با درخواست داده بر اساس آنچه می‌تواند انجام دهد به جای آنچه است.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype Puller interface {\n Pull(d *Data) error\n}\n\ntype Storer interface {\n Store(d *Data) error\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این دو رابط توصیف می‌کنند که داده قطعی چه کارهایی باید انجام دهد و این انواع هستند که در تعریف توابع Pull و Store جایگزین می‌شوند. حالا این توابع چندریختی هستند. هنگامی که Alice و Bob به عنوان یک Puller و یک Storer تعریف و پیاده‌سازی می‌شوند، می‌توانند به توابع منتقل شوند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n هنوز کار تمام نشده است. تابع Copy نیز باید چندریختی باشد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc Copy(ps PullStorer, batch int) error {\n data := make([]Data, batch)\n\n for {\n i, err := Pull(ps, data)\n if i \u0026gt; 0 {\n if _, err := Store(ps, data[:i]); err != nil {\n return err\n }\n }\n\n if err != nil {\n return err\n }\n }\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n تابع Copy دیگر درخواستی برای یک مقدار System نمی‌کند، بلکه هر مقدار قطعی است که بداند چگونه هم Pull و هم Store کند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype PullStorer interface {\n Puller\n Storer\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n رابط PullStorer از طریق ترکیب تعریف می‌شود. این رابط از رابط‌های Puller و Storer تشکیل شده است. کار به سمت ترکیب رابط‌های بزرگتر از رابط‌های کوچکتر پیش می‌رود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n توجه کنید که متغیر PullStorer در حال حاضر به توابع Pull و Store منتقل می‌شود. چگونه این امکان وجود دارد وقتی که اطلاعات نوع متفاوت است؟\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e// func Pull(p Puller, data []Data) (int, error) {\ni, err := Pull(ps, data)\n\n// func Store(s Storer, data []Data) (int, error) {\nif _, err := Store(ps, data[:i]); err != nil {\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n شما همیشه باید به خاطر داشته باشید که شما هرگز یک مقدار رابط را در سراسر برنامه خود منتقل نمی‌کنید، زیرا آن‌ها وجود ندارند و بی‌ارزش هستند. شما تنها می‌توانید داده‌های قطعی را منتقل کنید. بنابراین، داده قطعی ذخیره شده در داخل متغیر رابط ps است که به توابع Pull و Store منتقل می‌شود. آیا درست نیست که مقدار قطعی ذخیره شده در داخل ps باید بداند چگونه باید Pull و Store کند؟\n \u003c/p\u003e\n \n\n \u003ca href=\"/tour/eng/static/img/comp1.png\" target=\"_blank\"\u003e\n \u003cimg class=\"codeimg\" src=\"/tour/eng/static/img/comp1.png\"\u003e\n \u003c/a\u003e\n\n \n \u003cp\u003e\n از آنجایی که یک System از Xenia و Pillar تشکیل می‌شود، System رابط PullStorer را پیاده‌سازی می‌کند. با این تغییرات، شما می‌توانید اکنون انواع قطعی جدیدی را ایجاد کنید که رابط PullStorer را پیاده‌سازی می‌کنند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype System1 struct {\n Xenia\n Pillar\n}\n\ntype System2 struct {\n Alice\n Bob\n}\n\ntype System3 struct {\n Xenia\n Bob\n}\n\ntype System4 struct {\n Alice\n Pillar\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n وقتی بیشتر درباره این مسئله فکر می‌کنید، تعریف انواع System مختلف برای تمام ترکیب‌های ممکن عملی نیست. این روش کار خواهد کرد، اما مشکلات نگهداری آن نیاز به یک راه‌حل بهتر دارد.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eترکیب رابطه‌ها\u003c/h2\u003e\n \n \n \u003cp\u003e\n چه اتفاقی می‌افتد اگر تصمیم بگیرید نوع سیستم قطعی خود را از دو نوع رابطه ترکیب کنید؟\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype System struct {\n Puller\n Storer\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n This is an interesting solution. This would allow the application to inject the\n\n\n concrete Puller or Storer into the system at application startup. \n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc main() {\n sys := System{\n Puller: \u0026amp;Xenia{\n Host: \u0026#34;localhost:8000\u0026#34;,\n Timeout: time.Second,\n },\n Storer: \u0026amp;Pillar{\n Host: \u0026#34;localhost:9000\u0026#34;,\n Timeout: time.Second,\n },\n }\n\n if err := Copy(\u0026amp;sys, 3); err != io.EOF {\n fmt.Println(err)\n }\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این یک راه‌حل جالب است. این به اپلیکیشن اجازه می‌دهد که در زمان راه‌اندازی اپلیکیشن، Puller یا Storer قطعی را به سیستم درج کند.\n \u003c/p\u003e\n \n\n \u003ca href=\"/tour/eng/static/img/comp2.png\" target=\"_blank\"\u003e\n \u003cimg class=\"codeimg\" src=\"/tour/eng/static/img/comp2.png\"\u003e\n \u003c/a\u003e\n\n \n \u003cp\u003e\n با این تغییر، اپلیکیشن به طور کامل از تغییرات سیستم جدیدی که ممکن است در طول زمان رخ دهد، جدا شده است.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eبازبینی دقیق\u003c/h2\u003e\n \n \n \u003cp\u003e\n سوال بعدی که باید پرسید، آیا توابع چندریختی به طور دقیق همانند آنچه که ممکن است باشند، هستند؟ این بخشی از فرآیند مهندسی است که نمی‌توان آن را نادیده گرفت. پاسخ خیر است، دو تغییر می‌تواند انجام شود.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc Copy(sys *System, batch int) error {\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n تابع Copy دیگر نیازی به چندریختی ندارد زیرا تنها یک نوع سیستم وجود خواهد داشت. نوع رابط PullStorer می‌تواند از برنامه حذف شود. به یاد داشته باشید که شما چندریختی را در داخل نوع قرار دادید وقتی از ترکیب با انواع رابطه استفاده کردید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc Copy(p Puller, s Storer, batch int) error {\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این تغییر دیگری است که می‌توان در تابع Copy انجام داد. این تغییر باعث می‌شود تابع دوباره دقیقتر و چندریختی شود. حالا تابع دقیقاً آنچه را که نیاز دارد بر اساس اینکه داده قطعی چه کارهایی می‌تواند انجام دهد، درخواست می‌کند.\n \u003c/p\u003e\n \n\n \u003ca href=\"/tour/eng/static/img/comp3.png\" target=\"_blank\"\u003e\n \u003cimg class=\"codeimg\" src=\"/tour/eng/static/img/comp3.png\"\u003e\n \u003c/a\u003e\n\n \n \u003cp\u003e\n با این تغییر، نوع ساختار System همچنین می‌تواند از برنامه حذف شود.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc main() {\n x := Xenia{\n Host: \u0026#34;localhost:8000\u0026#34;,\n Timeout: time.Second,\n }\n\n p := Pillar{\n Host: \u0026#34;localhost:9000\u0026#34;,\n Timeout: time.Second,\n }\n\n if err := Copy(\u0026amp;x, \u0026amp;p, 3); err != io.EOF {\n fmt.Println(err)\n }\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n با حذف انواع PullStorer و System، برنامه ساده‌تر می‌شود. تابع اصلی می‌تواند تمرکز خود را بر روی ساخت داده‌های قطعی Puller و Storer مورد نیاز برای جابجایی داده داشته باشد. سیستم نوع و رابط‌های برنامه‌نویسی دقیق‌تر هستند.\n\n\n این ایده از دقت و وضوح از Edsger W. Dijkstra می‌آید:\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u0026#34;هدف از انتزاع، نه عدم وضوح است، بلکه ایجاد یک سطح معنایی جدید است که در آن می‌توان به طور کاملاً دقیق عمل کرد.\u0026#34; - Edsger W. Dijkstra\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eیادداشت‌ها\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003eاین بسیار بیشتر از مکانیک تعبیه‌گری نوع است.\u003c/li\u003e\n \n \u003cli\u003eانواع را اعلام کرده و با ترکیب در ذهن جریان‌های کاری را پیاده‌سازی کنید.\u003c/li\u003e\n \n \u003cli\u003eابتدا مسئله‌ای که سعی در حل آن دارید را درک کنید. این به معنای درک داده‌ها است.\u003c/li\u003e\n \n \u003cli\u003eهدف از کاهش و کمینه‌سازی تغییرات پیوسته در نرم‌افزار شما است.\u003c/li\u003e\n \n \u003cli\u003eرابط‌ها بالاترین سطح ترکیب را فراهم می‌کنند.\u003c/li\u003e\n \n \u003cli\u003eنوع‌ها را بر اساس رفتار مشترک، نه بر اساس یک DNA مشترک، گروه‌بندی کنید.\u003c/li\u003e\n \n \u003cli\u003eهمه می‌توانند با همکاری کردن تمرکز خود را بر روی آنچه که انجام می‌دهیم و نه بر روی آنچه که هستیم، قرار دهند.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eنقل قول‌ها\u003c/h2\u003e\n \n \n \u003cp\u003e\n \u0026#34;یک رابط برنامه‌نویسی خوب، نه فقط آسان در استفاده است، بلکه همچنین سخت در سوءاستفاده است.\u0026#34; - JBD\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u0026#34;همیشه می‌توانید تعبیه کنید، اما نمی‌توانید رابط‌های بزرگ را پس از ایجاد آن‌ها تجزیه کنید. رابط‌ها را کوچک نگه دارید.\u0026#34; - JBD\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u0026#34;با طراحی با رابط‌ها، آن‌ها را کشف کنید.\u0026#34; - Rob Pike\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u0026#34;تکرار، همیشه ارزانتر از اشتباه در تعبیه است.\u0026#34; - Sandi Metz\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eراهنمایی‌های طراحی\u003c/h2\u003e\n \n \n \u003cp\u003e\n Learn about the \u003ca href=\"https://github.com/ardanlabs/gotraining/blob/master/topics/go/#interface-and-composition-design\" target=\"_blank\"\u003edesign guidelines\u003c/a\u003e for composition.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eخواند بیشتر\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://programmingisterrible.com/post/176657481103/repeat-yourself-do-more-than-one-thing-and\" target=\"_blank\"\u003eRepeat yourself, do more than one thing, and rewrite everything\u003c/a\u003e - tef \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://golang.org/doc/effective_go.html#embedding\" target=\"_blank\"\u003eEmbedding\u003c/a\u003e \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2014/05/methods-interfaces-and-embedded-types.html\" target=\"_blank\"\u003eMethods, Interfaces and Embedding\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2015/09/composition-with-go.html\" target=\"_blank\"\u003eComposition In Go\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2016/10/reducing-type-hierarchies.html\" target=\"_blank\"\u003eReducing Type Hierarchies\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2016/10/avoid-interface-pollution.html\" target=\"_blank\"\u003eAvoid Interface Pollution\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n","Files":[{"Name":"example1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program demonstrating struct composition.\npackage main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"math/rand\"\n\t\"time\"\n)\n\n// Data is the structure of the data we are copying.\ntype Data struct {\n\tLine string\n}\n\n// =============================================================================\n\n// Xenia is a system we need to pull data from.\ntype Xenia struct {\n\tHost string\n\tTimeout time.Duration\n}\n\n// Pull knows how to pull data out of Xenia.\nfunc (*Xenia) Pull(d *Data) error {\n\tswitch rand.Intn(10) {\n\tcase 1, 9:\n\t\treturn io.EOF\n\n\tcase 5:\n\t\treturn errors.New(\"error reading data from Xenia\")\n\n\tdefault:\n\t\td.Line = \"Data\"\n\t\tfmt.Println(\"In:\", d.Line)\n\t\treturn nil\n\t}\n}\n\n// Pillar is a system we need to store data into.\ntype Pillar struct {\n\tHost string\n\tTimeout time.Duration\n}\n\n// Store knows how to store data into Pillar.\nfunc (*Pillar) Store(d *Data) error {\n\tfmt.Println(\"Out:\", d.Line)\n\treturn nil\n}\n\n// =============================================================================\n\n// System wraps Xenia and Pillar together into a single system.\ntype System struct {\n\tXenia\n\tPillar\n}\n\n// =============================================================================\n\n// pull knows how to pull bulks of data from Xenia.\nfunc pull(x *Xenia, data []Data) (int, error) {\n\tfor i := range data {\n\t\tif err := x.Pull(\u0026data[i]); err != nil {\n\t\t\treturn i, err\n\t\t}\n\t}\n\n\treturn len(data), nil\n}\n\n// store knows how to store bulks of data into Pillar.\nfunc store(p *Pillar, data []Data) (int, error) {\n\tfor i := range data {\n\t\tif err := p.Store(\u0026data[i]); err != nil {\n\t\t\treturn i, err\n\t\t}\n\t}\n\n\treturn len(data), nil\n}\n\n// Copy knows how to pull and store data from the System.\nfunc Copy(sys *System, batch int) error {\n\tdata := make([]Data, batch)\n\n\tfor {\n\t\ti, err := pull(\u0026sys.Xenia, data)\n\t\tif i \u003e 0 {\n\t\t\tif _, err := store(\u0026sys.Pillar, data[:i]); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n}\n\n// =============================================================================\n\nfunc main() {\n\tsys := System{\n\t\tXenia: Xenia{\n\t\t\tHost: \"localhost:8000\",\n\t\t\tTimeout: time.Second,\n\t\t},\n\t\tPillar: Pillar{\n\t\t\tHost: \"localhost:9000\",\n\t\t\tTimeout: time.Second,\n\t\t},\n\t}\n\n\tif err := Copy(\u0026sys, 3); err != io.EOF {\n\t\tfmt.Println(err)\n\t}\n}\n","Hash":"3zMWAO6TA+nQEtLHldV/EgGwWPg="},{"Name":"example2.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program demonstrating decoupling with interfaces.\npackage main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"math/rand\"\n\t\"time\"\n)\n\n// Data is the structure of the data we are copying.\ntype Data struct {\n\tLine string\n}\n\n// =============================================================================\n\n// Puller declares behavior for pulling data.\ntype Puller interface {\n\tPull(d *Data) error\n}\n\n// Storer declares behavior for storing data.\ntype Storer interface {\n\tStore(d *Data) error\n}\n\n// =============================================================================\n\n// Xenia is a system we need to pull data from.\ntype Xenia struct {\n\tHost string\n\tTimeout time.Duration\n}\n\n// Pull knows how to pull data out of Xenia.\nfunc (*Xenia) Pull(d *Data) error {\n\tswitch rand.Intn(10) {\n\tcase 1, 9:\n\t\treturn io.EOF\n\n\tcase 5:\n\t\treturn errors.New(\"error reading data from Xenia\")\n\n\tdefault:\n\t\td.Line = \"Data\"\n\t\tfmt.Println(\"In:\", d.Line)\n\t\treturn nil\n\t}\n}\n\n// Pillar is a system we need to store data into.\ntype Pillar struct {\n\tHost string\n\tTimeout time.Duration\n}\n\n// Store knows how to store data into Pillar.\nfunc (*Pillar) Store(d *Data) error {\n\tfmt.Println(\"Out:\", d.Line)\n\treturn nil\n}\n\n// =============================================================================\n\n// System wraps Xenia and Pillar together into a single system.\ntype System struct {\n\tXenia\n\tPillar\n}\n\n// =============================================================================\n\n// pull knows how to pull bulks of data from any Puller.\nfunc pull(p Puller, data []Data) (int, error) {\n\tfor i := range data {\n\t\tif err := p.Pull(\u0026data[i]); err != nil {\n\t\t\treturn i, err\n\t\t}\n\t}\n\n\treturn len(data), nil\n}\n\n// store knows how to store bulks of data from any Storer.\nfunc store(s Storer, data []Data) (int, error) {\n\tfor i := range data {\n\t\tif err := s.Store(\u0026data[i]); err != nil {\n\t\t\treturn i, err\n\t\t}\n\t}\n\n\treturn len(data), nil\n}\n\n// Copy knows how to pull and store data from the System.\nfunc Copy(sys *System, batch int) error {\n\tdata := make([]Data, batch)\n\n\tfor {\n\t\ti, err := pull(\u0026sys.Xenia, data)\n\t\tif i \u003e 0 {\n\t\t\tif _, err := store(\u0026sys.Pillar, data[:i]); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n}\n\n// =============================================================================\n\nfunc main() {\n\tsys := System{\n\t\tXenia: Xenia{\n\t\t\tHost: \"localhost:8000\",\n\t\t\tTimeout: time.Second,\n\t\t},\n\t\tPillar: Pillar{\n\t\t\tHost: \"localhost:9000\",\n\t\t\tTimeout: time.Second,\n\t\t},\n\t}\n\n\tif err := Copy(\u0026sys, 3); err != io.EOF {\n\t\tfmt.Println(err)\n\t}\n}\n","Hash":"aUnq+aVJEaKcUUCHcEKiYit70wE="},{"Name":"example3.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program demonstrating interface composition.\npackage main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"math/rand\"\n\t\"time\"\n)\n\n// Data is the structure of the data we are copying.\ntype Data struct {\n\tLine string\n}\n\n// =============================================================================\n\n// Puller declares behavior for pulling data.\ntype Puller interface {\n\tPull(d *Data) error\n}\n\n// Storer declares behavior for storing data.\ntype Storer interface {\n\tStore(d *Data) error\n}\n\n// PullStorer declares behavior for both pulling and storing.\ntype PullStorer interface {\n\tPuller\n\tStorer\n}\n\n// =============================================================================\n\n// Xenia is a system we need to pull data from.\ntype Xenia struct {\n\tHost string\n\tTimeout time.Duration\n}\n\n// Pull knows how to pull data out of Xenia.\nfunc (*Xenia) Pull(d *Data) error {\n\tswitch rand.Intn(10) {\n\tcase 1, 9:\n\t\treturn io.EOF\n\n\tcase 5:\n\t\treturn errors.New(\"error reading data from Xenia\")\n\n\tdefault:\n\t\td.Line = \"Data\"\n\t\tfmt.Println(\"In:\", d.Line)\n\t\treturn nil\n\t}\n}\n\n// Pillar is a system we need to store data into.\ntype Pillar struct {\n\tHost string\n\tTimeout time.Duration\n}\n\n// Store knows how to store data into Pillar.\nfunc (*Pillar) Store(d *Data) error {\n\tfmt.Println(\"Out:\", d.Line)\n\treturn nil\n}\n\n// =============================================================================\n\n// System wraps Xenia and Pillar together into a single system.\ntype System struct {\n\tXenia\n\tPillar\n}\n\n// =============================================================================\n\n// pull knows how to pull bulks of data from any Puller.\nfunc pull(p Puller, data []Data) (int, error) {\n\tfor i := range data {\n\t\tif err := p.Pull(\u0026data[i]); err != nil {\n\t\t\treturn i, err\n\t\t}\n\t}\n\n\treturn len(data), nil\n}\n\n// store knows how to store bulks of data from any Storer.\nfunc store(s Storer, data []Data) (int, error) {\n\tfor i := range data {\n\t\tif err := s.Store(\u0026data[i]); err != nil {\n\t\t\treturn i, err\n\t\t}\n\t}\n\n\treturn len(data), nil\n}\n\n// Copy knows how to pull and store data from any System.\nfunc Copy(ps PullStorer, batch int) error {\n\tdata := make([]Data, batch)\n\n\tfor {\n\t\ti, err := pull(ps, data)\n\t\tif i \u003e 0 {\n\t\t\tif _, err := store(ps, data[:i]); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n}\n\n// =============================================================================\n\nfunc main() {\n\tsys := System{\n\t\tXenia: Xenia{\n\t\t\tHost: \"localhost:8000\",\n\t\t\tTimeout: time.Second,\n\t\t},\n\t\tPillar: Pillar{\n\t\t\tHost: \"localhost:9000\",\n\t\t\tTimeout: time.Second,\n\t\t},\n\t}\n\n\tif err := Copy(\u0026sys, 3); err != io.EOF {\n\t\tfmt.Println(err)\n\t}\n}\n","Hash":"DOKjIq3KIONIEicYfnXEp5PpyOY="},{"Name":"example4.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program demonstrating decoupling with interface composition.\npackage main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"math/rand\"\n\t\"time\"\n)\n\n// Data is the structure of the data we are copying.\ntype Data struct {\n\tLine string\n}\n\n// =============================================================================\n\n// Puller declares behavior for pulling data.\ntype Puller interface {\n\tPull(d *Data) error\n}\n\n// Storer declares behavior for storing data.\ntype Storer interface {\n\tStore(d *Data) error\n}\n\n// PullStorer declares behavior for both pulling and storing.\ntype PullStorer interface {\n\tPuller\n\tStorer\n}\n\n// =============================================================================\n\n// Xenia is a system we need to pull data from.\ntype Xenia struct {\n\tHost string\n\tTimeout time.Duration\n}\n\n// Pull knows how to pull data out of Xenia.\nfunc (*Xenia) Pull(d *Data) error {\n\tswitch rand.Intn(10) {\n\tcase 1, 9:\n\t\treturn io.EOF\n\n\tcase 5:\n\t\treturn errors.New(\"error reading data from Xenia\")\n\n\tdefault:\n\t\td.Line = \"Data\"\n\t\tfmt.Println(\"In:\", d.Line)\n\t\treturn nil\n\t}\n}\n\n// Pillar is a system we need to store data into.\ntype Pillar struct {\n\tHost string\n\tTimeout time.Duration\n}\n\n// Store knows how to store data into Pillar.\nfunc (*Pillar) Store(d *Data) error {\n\tfmt.Println(\"Out:\", d.Line)\n\treturn nil\n}\n\n// =============================================================================\n\n// System wraps Pullers and Stores together into a single system.\ntype System struct {\n\tPuller\n\tStorer\n}\n\n// =============================================================================\n\n// pull knows how to pull bulks of data from any Puller.\nfunc pull(p Puller, data []Data) (int, error) {\n\tfor i := range data {\n\t\tif err := p.Pull(\u0026data[i]); err != nil {\n\t\t\treturn i, err\n\t\t}\n\t}\n\n\treturn len(data), nil\n}\n\n// store knows how to store bulks of data from any Storer.\nfunc store(s Storer, data []Data) (int, error) {\n\tfor i := range data {\n\t\tif err := s.Store(\u0026data[i]); err != nil {\n\t\t\treturn i, err\n\t\t}\n\t}\n\n\treturn len(data), nil\n}\n\n// Copy knows how to pull and store data from any System.\nfunc Copy(ps PullStorer, batch int) error {\n\tdata := make([]Data, batch)\n\n\tfor {\n\t\ti, err := pull(ps, data)\n\t\tif i \u003e 0 {\n\t\t\tif _, err := store(ps, data[:i]); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n}\n\n// =============================================================================\n\nfunc main() {\n\tsys := System{\n\t\tPuller: \u0026Xenia{\n\t\t\tHost: \"localhost:8000\",\n\t\t\tTimeout: time.Second,\n\t\t},\n\t\tStorer: \u0026Pillar{\n\t\t\tHost: \"localhost:9000\",\n\t\t\tTimeout: time.Second,\n\t\t},\n\t}\n\n\tif err := Copy(\u0026sys, 3); err != io.EOF {\n\t\tfmt.Println(err)\n\t}\n}\n","Hash":"IVuZhE9B5gqsPmEFZvEYsBxgvRc="},{"Name":"example5.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program demonstrating removing interface pollution.\npackage main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"math/rand\"\n\t\"time\"\n)\n\n// Data is the structure of the data we are copying.\ntype Data struct {\n\tLine string\n}\n\n// =============================================================================\n\n// Puller declares behavior for pulling data.\ntype Puller interface {\n\tPull(d *Data) error\n}\n\n// Storer declares behavior for storing data.\ntype Storer interface {\n\tStore(d *Data) error\n}\n\n// =============================================================================\n\n// Xenia is a system we need to pull data from.\ntype Xenia struct {\n\tHost string\n\tTimeout time.Duration\n}\n\n// Pull knows how to pull data out of Xenia.\nfunc (*Xenia) Pull(d *Data) error {\n\tswitch rand.Intn(10) {\n\tcase 1, 9:\n\t\treturn io.EOF\n\n\tcase 5:\n\t\treturn errors.New(\"error reading data from Xenia\")\n\n\tdefault:\n\t\td.Line = \"Data\"\n\t\tfmt.Println(\"In:\", d.Line)\n\t\treturn nil\n\t}\n}\n\n// Pillar is a system we need to store data into.\ntype Pillar struct {\n\tHost string\n\tTimeout time.Duration\n}\n\n// Store knows how to store data into Pillar.\nfunc (*Pillar) Store(d *Data) error {\n\tfmt.Println(\"Out:\", d.Line)\n\treturn nil\n}\n\n// =============================================================================\n\n// System wraps Pullers and Stores together into a single system.\ntype System struct {\n\tPuller\n\tStorer\n}\n\n// =============================================================================\n\n// pull knows how to pull bulks of data from any Puller.\nfunc pull(p Puller, data []Data) (int, error) {\n\tfor i := range data {\n\t\tif err := p.Pull(\u0026data[i]); err != nil {\n\t\t\treturn i, err\n\t\t}\n\t}\n\n\treturn len(data), nil\n}\n\n// store knows how to store bulks of data from any Storer.\nfunc store(s Storer, data []Data) (int, error) {\n\tfor i := range data {\n\t\tif err := s.Store(\u0026data[i]); err != nil {\n\t\t\treturn i, err\n\t\t}\n\t}\n\n\treturn len(data), nil\n}\n\n// Copy knows how to pull and store data from any System.\nfunc Copy(sys *System, batch int) error {\n\tdata := make([]Data, batch)\n\n\tfor {\n\t\ti, err := pull(sys, data)\n\t\tif i \u003e 0 {\n\t\t\tif _, err := store(sys, data[:i]); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n}\n\n// =============================================================================\n\nfunc main() {\n\tsys := System{\n\t\tPuller: \u0026Xenia{\n\t\t\tHost: \"localhost:8000\",\n\t\t\tTimeout: time.Second,\n\t\t},\n\t\tStorer: \u0026Pillar{\n\t\t\tHost: \"localhost:9000\",\n\t\t\tTimeout: time.Second,\n\t\t},\n\t}\n\n\tif err := Copy(\u0026sys, 3); err != io.EOF {\n\t\tfmt.Println(err)\n\t}\n}\n","Hash":"UswtrL3mrcLRQYrJ8r0m17eOJQA="},{"Name":"example6.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program demonstrating being more precise with API design.\npackage main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"math/rand\"\n\t\"time\"\n)\n\n// Data is the structure of the data we are copying.\ntype Data struct {\n\tLine string\n}\n\n// =============================================================================\n\n// Puller declares behavior for pulling data.\ntype Puller interface {\n\tPull(d *Data) error\n}\n\n// Storer declares behavior for storing data.\ntype Storer interface {\n\tStore(d *Data) error\n}\n\n// =============================================================================\n\n// Xenia is a system we need to pull data from.\ntype Xenia struct {\n\tHost string\n\tTimeout time.Duration\n}\n\n// Pull knows how to pull data out of Xenia.\nfunc (*Xenia) Pull(d *Data) error {\n\tswitch rand.Intn(10) {\n\tcase 1, 9:\n\t\treturn io.EOF\n\n\tcase 5:\n\t\treturn errors.New(\"error reading data from Xenia\")\n\n\tdefault:\n\t\td.Line = \"Data\"\n\t\tfmt.Println(\"In:\", d.Line)\n\t\treturn nil\n\t}\n}\n\n// Pillar is a system we need to store data into.\ntype Pillar struct {\n\tHost string\n\tTimeout time.Duration\n}\n\n// Store knows how to store data into Pillar.\nfunc (*Pillar) Store(d *Data) error {\n\tfmt.Println(\"Out:\", d.Line)\n\treturn nil\n}\n\n// =============================================================================\n\n// pull knows how to pull bulks of data from any Puller.\nfunc pull(p Puller, data []Data) (int, error) {\n\tfor i := range data {\n\t\tif err := p.Pull(\u0026data[i]); err != nil {\n\t\t\treturn i, err\n\t\t}\n\t}\n\n\treturn len(data), nil\n}\n\n// store knows how to store bulks of data from any Storer.\nfunc store(s Storer, data []Data) (int, error) {\n\tfor i := range data {\n\t\tif err := s.Store(\u0026data[i]); err != nil {\n\t\t\treturn i, err\n\t\t}\n\t}\n\n\treturn len(data), nil\n}\n\n// Copy knows how to pull and store data from any System.\nfunc Copy(p Puller, s Storer, batch int) error {\n\tdata := make([]Data, batch)\n\n\tfor {\n\t\ti, err := pull(p, data)\n\t\tif i \u003e 0 {\n\t\t\tif _, err := store(s, data[:i]); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n}\n\n// =============================================================================\n\nfunc main() {\n\tx := Xenia{\n\t\tHost: \"localhost:8000\",\n\t\tTimeout: time.Second,\n\t}\n\n\tp := Pillar{\n\t\tHost: \"localhost:9000\",\n\t\tTimeout: time.Second,\n\t}\n\n\tif err := Copy(\u0026x, \u0026p, 3); err != io.EOF {\n\t\tfmt.Println(err)\n\t}\n}\n","Hash":"LbmZX7WovpLbUsvAKurO2QuNmi4="}]},{"Title":"تمرینات","Content":"\n \u003ch2\u003eتمرینات\u003c/h2\u003e\n \n \n \u003cp\u003e\n از قالب به عنوان نقطه شروع برای تکمیل تمرینات استفاده کنید. یک حل ممکن فراهم شده است.\n \u003c/p\u003e\n \n\n \u003ch2\u003eتمرین 1\u003c/h2\u003e\n \n \n \u003cp\u003e\n با استفاده از قالب، مجموعه‌ای از انواع قطعی را که مجموعه‌ای از انواع رابطهای پیش‌تعریف شده را پیاده‌سازی می‌کنند، اعلام کنید. سپس مقادیر این انواع را ایجاد کرده و از آن‌ها برای تکمیل مجموعه‌ای از وظایف پیش‌تعریف شده استفاده کنید.\n \u003c/p\u003e\n \n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n","Files":[{"Name":"exercise1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Using the template, declare a set of concrete types that implement the set\n// of predefined interface types. Then create values of these types and use\n// them to complete a set of predefined tasks.\npackage main\n\n// Add import(s).\n\n// administrator represents a person or other entity capable of administering\n// hardware and software infrastructure.\ntype administrator interface {\n\tadministrate(system string)\n}\n\n// developer represents a person or other entity capable of writing software.\ntype developer interface {\n\tdevelop(system string)\n}\n\n// =============================================================================\n\n// adminlist represents a group of administrators.\ntype adminlist struct {\n\tlist []administrator\n}\n\n// Enqueue adds an administrator to the adminlist.\nfunc (l *adminlist) Enqueue(a administrator) {\n\tl.list = append(l.list, a)\n}\n\n// Dequeue removes an administrator from the adminlist.\nfunc (l *adminlist) Dequeue() administrator {\n\ta := l.list[0]\n\tl.list = l.list[1:]\n\treturn a\n}\n\n// =============================================================================\n\n// devlist represents a group of developers.\ntype devlist struct {\n\tlist []developer\n}\n\n// Enqueue adds a developer to the devlist.\nfunc (l *devlist) Enqueue(d developer) {\n\tl.list = append(l.list, d)\n}\n\n// Dequeue removes a developer from the devlist.\nfunc (l *devlist) Dequeue() developer {\n\td := l.list[0]\n\tl.list = l.list[1:]\n\treturn d\n}\n\n// =============================================================================\n\n// Declare a concrete type named sysadmin with a name field of type string.\n\n// Declare a method named administrate for the sysadmin type, implementing the\n// administrator interface. administrate should print out the name of the\n// sysadmin, as well as the system they are administering.\n\n// Declare a concrete type named programmer with a name field of type string.\n\n// Declare a method named develop for the programmer type, implementing the\n// developer interface. develop should print out the name of the\n// programmer, as well as the system they are coding.\n\n// Declare a concrete type named company. Declare it as the composition of\n// the administrator and developer interface types.\n\n// =============================================================================\n\nfunc main() {\n\n\t// Create a variable named admins of type adminlist.\n\n\t// Create a variable named devs of type devlist.\n\n\t// Enqueue a new sysadmin onto admins.\n\n\t// Enqueue two new programmers onto devs.\n\n\t// Create a variable named cmp of type company, and initialize it by\n\t// hiring (dequeuing) an administrator from admins and a developer from devs.\n\n\t// Enqueue the company value on both lists since the company implements\n\t// each interface.\n\n\t// A set of tasks for administrators and developers to perform.\n\ttasks := []struct {\n\t\tneedsAdmin bool\n\t\tsystem string\n\t}{\n\t\t{needsAdmin: false, system: \"xenia\"},\n\t\t{needsAdmin: true, system: \"pillar\"},\n\t\t{needsAdmin: false, system: \"omega\"},\n\t}\n\n\t// Iterate over tasks.\n\tfor _, task := range tasks {\n\n\t\t// Check if the task needs an administrator else use a developer.\n\t\tif {\n\n\t\t\t// Dequeue an administrator value from the admins list and\n\t\t\t// call the administrate method.\n\n\t\t\tcontinue\n\t\t}\n\n\t\t// Dequeue a developer value from the devs list and\n\t\t// call the develop method.\n\t}\n}\n","Hash":"yBj4Yami3rBJUHVzYmxGceRn7FI="},{"Name":"answer1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Using the template, declare a set of concrete types that implement the set\n// of predefined interface types. Then create values of these types and use\n// them to complete a set of predefined tasks.\npackage main\n\n// Add import(s).\nimport \"fmt\"\n\n// administrator represents a person or other entity capable of administering\n// hardware and software infrastructure.\ntype administrator interface {\n\tadministrate(system string)\n}\n\n// developer represents a person or other entity capable of writing software.\ntype developer interface {\n\tdevelop(system string)\n}\n\n// =============================================================================\n\n// adminlist represents a group of administrators.\ntype adminlist struct {\n\tlist []administrator\n}\n\n// Enqueue adds an administrator to the adminlist.\nfunc (l *adminlist) Enqueue(a administrator) {\n\tl.list = append(l.list, a)\n}\n\n// Dequeue removes an administrator from the adminlist.\nfunc (l *adminlist) Dequeue() administrator {\n\ta := l.list[0]\n\tl.list = l.list[1:]\n\treturn a\n}\n\n// =============================================================================\n\n// devlist represents a group of developers.\ntype devlist struct {\n\tlist []developer\n}\n\n// Enqueue adds a developer to the devlist.\nfunc (l *devlist) Enqueue(d developer) {\n\tl.list = append(l.list, d)\n}\n\n// Dequeue removes a developer from the devlist.\nfunc (l *devlist) Dequeue() developer {\n\td := l.list[0]\n\tl.list = l.list[1:]\n\treturn d\n}\n\n// =============================================================================\n\n// Declare a concrete type named sysadmin with a name field of type string.\ntype sysadmin struct {\n\tname string\n}\n\n// Declare a method named administrate for the sysadmin type, implementing the\n// administrator interface. administrate should print out the name of the\n// sysadmin, as well as the system they are administering.\nfunc (s *sysadmin) administrate(system string) {\n\tfmt.Println(s.name, \"is administering\", system)\n}\n\n// Declare a concrete type named programmer with a name field of type string.\ntype programmer struct {\n\tname string\n}\n\n// Declare a method named develop for the programmer type, implementing the\n// developer interface. develop should print out the name of the\n// programmer, as well as the system they are coding.\nfunc (p *programmer) develop(system string) {\n\tfmt.Println(p.name, \"is developing\", system)\n}\n\n// Declare a concrete type named company. Declare it as the composition of\n// the administrator and developer interface types.\ntype company struct {\n\tadministrator\n\tdeveloper\n}\n\n// =============================================================================\n\nfunc main() {\n\n\t// Create a variable named admins of type adminlist.\n\tvar admins adminlist\n\n\t// Create a variable named devs of type devlist.\n\tvar devs devlist\n\n\t// Enqueue a new sysadmin onto admins.\n\tadmins.Enqueue(\u0026sysadmin{\"John\"})\n\n\t// Enqueue two new programmers onto devs.\n\tdevs.Enqueue(\u0026programmer{\"Mary\"})\n\tdevs.Enqueue(\u0026programmer{\"Steve\"})\n\n\t// Create a variable named cmp of type company, and initialize it by\n\t// hiring (dequeuing) an administrator from admins and a developer from devs.\n\tcmp := company{\n\t\tadministrator: admins.Dequeue(),\n\t\tdeveloper: devs.Dequeue(),\n\t}\n\n\t// Enqueue the company value on both lists since the company implements\n\t// each interface.\n\tadmins.Enqueue(\u0026cmp)\n\tdevs.Enqueue(\u0026cmp)\n\n\t// A set of tasks for administrators and developers to perform.\n\ttasks := []struct {\n\t\tneedsAdmin bool\n\t\tsystem string\n\t}{\n\t\t{needsAdmin: false, system: \"xenia\"},\n\t\t{needsAdmin: true, system: \"pillar\"},\n\t\t{needsAdmin: false, system: \"omega\"},\n\t}\n\n\t// Iterate over tasks.\n\tfor _, task := range tasks {\n\n\t\t// Check if the task needs an administrator else use a developer.\n\t\tif task.needsAdmin {\n\n\t\t\t// Dequeue an administrator value from the admins list and\n\t\t\t// call the administrate method.\n\t\t\tadm := admins.Dequeue()\n\t\t\tadm.administrate(task.system)\n\n\t\t\tcontinue\n\t\t}\n\n\t\t// Dequeue a developer value from the devs list and\n\t\t// call the develop method.\n\t\tdev := devs.Dequeue()\n\t\tdev.develop(task.system)\n\t}\n}\n","Hash":"wHVZdN3Bh9/X1DSPXrM4YmRc0ek="}]}]} ,"generics-underlying-types":{"Title":"انواع پایه","Description":"می‌توانید یک نوع ژنریک با استفاده از یک نوع پایه اعلان کنید.","Pages":[{"Title":"ویدیو","Content":"\n \u003ch2\u003eویدیو\u003c/h2\u003e\n \n \n \u003cp\u003e\n مکالمه‌ای که در مورد ژنریک‌ها انجام دادم را تماشا کنید که شما را از طریق تمام مثال‌های این بخش از تور راهنمایی می‌کند.\n \u003c/p\u003e\n \n\n \u003ch2\u003eبررسی کد\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003eمثال 1: نوع وکتور قابل مشاهده\u003c/li\u003e\n \n \u003cli\u003eمثال 2: نوع وکتور رابط\u003c/li\u003e\n \n \u003cli\u003eمثال 3: نوع وکتور ژنریک\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n \u003ch2\u003eتوضیح\u003c/h2\u003e\n \n \n \u003cp\u003e\n اگر می‌خواستید نوع ژنریک خود را با استفاده از یک نوع پایه اعلان کنید، چه اتفاقی می‌افتاد؟\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype vector[T any] []T\n\nfunc (v vector[T]) last() (T, error) {\n var zero T\n \n if len(v) == 0 {\n return zero, errors.New(\u0026#34;empty\u0026#34;)\n }\n \n return v[len(v)-1], nil\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این مثال یک نوع وکتور ژنریک نشان می‌دهد که ساخت وکتور را به یک نوع واحد داده محدود می‌کند. استفاده از براکت‌های مربعی نوع T را به عنوان یک نوع ژنریک تعیین‌شده در زمان کامپایل اعلان می‌کند. استفاده از محدودیت \u0026#34;any\u0026#34; نشان می‌دهد که هیچ محدودیتی بر روی نوع T وجود ندارد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n آخرین متد با یک گیرنده مقداری از نوع vector[T] اعلان شده است تا یک مقدار از نوع وکتور با یک زیرمجموعه‌ی از نوع T نمایان دهد. این متد یک مقدار از همان نوع T را برمی‌گرداند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc main() {\n fmt.Print(\u0026#34;vector[int] : \u0026#34;)\n\n vGenInt := vector[int]{10, -1}\n\n i, err := vGenInt.last()\n if err != nil {\n fmt.Print(\u0026#34;ERROR:\u0026#34;, err)\n return\n }\n\n if i \u0026lt; 0 {\n fmt.Print(\u0026#34;negative integer: \u0026#34;)\n }\n\n fmt.Printf(\u0026#34;value: %d\\n\u0026#34;, i)\n\n // -------------------------------------------------------------------------\n\n fmt.Print(\u0026#34;vector[string] : \u0026#34;)\n\n vGenStr := vector[string]{\u0026#34;A\u0026#34;, \u0026#34;B\u0026#34;, string([]byte{0xff})}\n\n s, err := vGenStr.last()\n if err != nil {\n fmt.Print(\u0026#34;ERROR:\u0026#34;, err)\n return\n }\n\n if !utf8.ValidString(s) {\n fmt.Print(\u0026#34;non-valid string: \u0026#34;)\n }\n\n fmt.Printf(\u0026#34;value: %q\\n\u0026#34;, s)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003evector[int] : negative integer: value: -1\nvector[string] : non-valid string: value: \u0026#34;\\xff\u0026#34;\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این نحوه‌ی ساخت یک مقدار از نوع وکتور با نوع زیرین int است، زمانی که مقادیر را در زمان ساخت وکتور تنظیم می‌کنم. یک جنبه مهم این کد فراخوانی‌های ساخت وکتور است.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e// Zero Value Construction\nvar vGenInt vector[int]\nvar vGenStr vector[string]\n\n// Non-Zero Value Construction\nvGenInt := vector{10, -1}\nvGenStr := vector{\u0026#34;A\u0026#34;, \u0026#34;B\u0026#34;, string([]byte{0xff})}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n زمانی که به ساخت این انواع ژنریک به حالت مقدار صفر می‌پردازیم، امکان استنباط نوع توسط کامپایلر وجود ندارد. با این حال، در مواردی که در زمان ساخت اولیه اطلاعات مقداردهی می‌شود، کامپایلر می‌تواند نوع را استنباط کند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n یک جنبه از مشخصات مربوط به ساخت یک نوع ژنریک به حالت مقدار صفر می‌پردازد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype vector[T any] []T\n\nfunc (v vector[T]) last() (T, error) {\n var zero T\n \n if len(v) == 0 {\n return zero, errors.New(\u0026#34;empty\u0026#34;)\n }\n \n return v[len(v)-1], nil\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n شما باید به تصریح متد در اختیار گذاشته شده برای متد آخر و چگونگی بازگرداندن یک مقدار از نوع ژنریک T توجه کنید. در بازگشت اولین موقعیت، شما باید مقدار صفر را برای نوع T برگردانید. پیاده سازی کنونی دو راه حل برای نوشتن این کد ارائه داده است. راه حل اولیه که در حال حاضر مشاهده می‌کنید، یک متغیر با نام صفر تا حالت مقدار صفر نوع T ساخته می‌شود و سپس این متغیر برای بازگشت استفاده می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n گزینه دیگر استفاده از تابع داخلی new و دیرفرنس دادن اشاره‌گر بازگشتی در بیانیه بازگشت است.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003etype vector[T any] []T\n\nfunc (v vector[T]) last() (T, error) {\n if len(v) == 0 {\n return *new(T), errors.New(\u0026#34;empty\u0026#34;)\n }\n \n return v[len(v)-1], nil\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این نسخه از متد آخر از تابع داخلی new برای ساخت صفر و معکوس کردن اشاره‌گر بازگشتی برای ارضای نوع بازگشتی T استفاده می‌کند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n توجه: شما ممکن است بپندارید چرا از T{} برای انجام ساخت مقدار صفر استفاده نکنید؟ مشکل این است که این نحو از نحوه‌ش تا کلیه انواع کار نمی‌کند، مثل انواع اسکالر (int، string، bool). بنابراین این گزینه نمی‌باشد.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eتمرینات\u003c/h2\u003e\n \n \n \u003cp\u003e\n از قالب به عنوان نقطه شروع برای انجام تمرینات استفاده کنید. یک راه حل ممکن ارائه شده است.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eتمرین 1\u003c/h2\u003e\n \n \n \u003cp\u003e\n یک نوع ژنریک با نام keymap اعلام کنید که از نوع اصلی نقشه با کلید نوع string و مقدار نوع T استفاده می‌کند. یک متد با نام set اعلام کنید که یک کلید از نوع string و یک مقدار از نوع T را قبول می‌کند. یک متد با نام get اعلام کنید که یک کلید از نوع string را قبول کرده و مقدار نوع T و درست یا غلط اگر کلید پیدا شود برمی‌گرداند. سپس یک تابع اصلی بنویسید که از متدها استفاده می‌کند.\n \u003c/p\u003e\n \n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n","Files":[{"Name":"example1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to declare two user defined types based on an\n// underlying concrete type. Each type implements a method named last that\n// returns the value stored at the highest index position in the vector or an\n// error when the vector is empty.\npackage main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"unicode/utf8\"\n)\n\ntype vectorInt []int\n\nfunc (v vectorInt) last() (int, error) {\n\tif len(v) == 0 {\n\t\treturn 0, errors.New(\"empty\")\n\t}\n\n\treturn v[len(v)-1], nil\n}\n\n// =============================================================================\n\ntype vectorString []string\n\nfunc (v vectorString) last() (string, error) {\n\tif len(v) == 0 {\n\t\treturn \"\", errors.New(\"empty\")\n\t}\n\n\treturn v[len(v)-1], nil\n}\n\n// =============================================================================\n\nfunc main() {\n\tfmt.Print(\"vectorInt : \")\n\n\tvInt := vectorInt{10, -1}\n\n\ti, err := vInt.last()\n\tif err != nil {\n\t\tfmt.Print(\"ERROR:\", err)\n\t\treturn\n\t}\n\n\tif i \u003c 0 {\n\t\tfmt.Print(\"negative integer: \")\n\t}\n\n\tfmt.Printf(\"value: %d\\n\", i)\n\n\t// -------------------------------------------------------------------------\n\n\tfmt.Print(\"vectorString : \")\n\n\tvStr := vectorString{\"A\", \"B\", string([]byte{0xff})}\n\n\ts, err := vStr.last()\n\tif err != nil {\n\t\tfmt.Print(\"ERROR:\", err)\n\t\treturn\n\t}\n\n\tif !utf8.ValidString(s) {\n\t\tfmt.Print(\"non-valid string: \")\n\t}\n\n\tfmt.Printf(\"value: %q\\n\", s)\n}\n","Hash":"2uShryLQLosC7Jq6SH/TLp5a/gw="},{"Name":"example2.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to declare a type that is based on the empty\n// interface so any value can be added into the vector. Since the last\n// function is using the empty interface for the return, users will need to\n// perform type assertions to get back to the concrete value being stored\n// inside the interface.\npackage main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"unicode/utf8\"\n)\n\ntype vectorInterface []interface{}\n\nfunc (v vectorInterface) last() (interface{}, error) {\n\tif len(v) == 0 {\n\t\treturn nil, errors.New(\"empty\")\n\t}\n\n\treturn v[len(v)-1], nil\n}\n\n// =============================================================================\n\nfunc main() {\n\tfmt.Print(\"vectorInterface : \")\n\n\tvItf := vectorInterface{10, \"A\", 20, \"B\", 3.14}\n\n\titf, err := vItf.last()\n\tif err != nil {\n\t\tfmt.Print(\"ERROR:\", err)\n\t\treturn\n\t}\n\n\tswitch v := itf.(type) {\n\tcase int:\n\t\tif v \u003c 0 {\n\t\t\tfmt.Print(\"negative integer: \")\n\t\t}\n\tcase string:\n\t\tif !utf8.ValidString(v) {\n\t\t\tfmt.Print(\"non-valid string: \")\n\t\t}\n\tdefault:\n\t\tfmt.Printf(\"unknown type %T: \", v)\n\t}\n\n\tfmt.Printf(\"value: %v\\n\", itf)\n}\n","Hash":"2yGqZsJHshnZWQj5McUCkDGaQPg="},{"Name":"example3.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to declare a generics version of the user defined\n// type. It represents a slice of some type T (to be determined later). The\n// last method is declared with a value receiver based on a vector of the\n// same type T and returns a value of that same type T as well.\npackage main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"unicode/utf8\"\n)\n\ntype vector[T any] []T\n\nfunc (v vector[T]) last() (T, error) {\n\tvar zero T\n\n\tif len(v) == 0 {\n\t\treturn zero, errors.New(\"empty\")\n\t}\n\n\treturn v[len(v)-1], nil\n}\n\n// =============================================================================\n\nfunc main() {\n\tfmt.Print(\"vector[int] : \")\n\n\tvGenInt := vector[int]{10, -1}\n\n\ti, err := vGenInt.last()\n\tif err != nil {\n\t\tfmt.Print(\"ERROR:\", err)\n\t\treturn\n\t}\n\n\tif i \u003c 0 {\n\t\tfmt.Print(\"negative integer: \")\n\t}\n\n\tfmt.Printf(\"value: %d\\n\", i)\n\n\t// -------------------------------------------------------------------------\n\n\tfmt.Print(\"vector[string] : \")\n\n\tvGenStr := vector[string]{\"A\", \"B\", string([]byte{0xff})}\n\n\ts, err := vGenStr.last()\n\tif err != nil {\n\t\tfmt.Print(\"ERROR:\", err)\n\t\treturn\n\t}\n\n\tif !utf8.ValidString(s) {\n\t\tfmt.Print(\"non-valid string: \")\n\t}\n\n\tfmt.Printf(\"value: %q\\n\", s)\n}\n","Hash":"IUvCjlqWarCTxdpSBw3mdX6HHyI="},{"Name":"exercise1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Implement a generic map type.\npackage main\n\n// Declare a generic type named keymap that uses an underlying type of map\n// with a key of type string and a value of some type T.\n\n// Implement a method named set that accepts a key of type string and a value\n// of type T.\n\n// Implement a method named get that accepts a key of type string and return\n// a value of type T and true or false if the key is found.\n\nfunc main() {\n\t// Construct a value of type keymap that stores integers.\n\n\t// Add a value with key \"jack\" and a value of 10.\n\n\t// Add a value with key \"jill\" and a value of 20.\n\n\t// Get the value for \"jack\" and verify it's found.\n\n\t// Print the value for the key \"jack\".\n\n\t// Get the value for \"jill\" and verify it's found.\n\n\t// Print the value for the key \"jill\".\n}\n","Hash":"+ApONji/Jtk4ZT5k/8yXAlbmFVQ="},{"Name":"answer1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Implement a generic map type.\npackage main\n\nimport (\n\t\"fmt\"\n)\n\n// Declare a generic type named keymap that uses an underlying type of map\n// with a key of type string and a value of some type T.\ntype keymap[T any] map[string]T\n\n// Implement a method named set that accepts a key of type string and a value\n// of type T.\nfunc (km keymap[T]) set(k string, v T) {\n\tkm[k] = v\n}\n\n// Implement a method named get that accepts a key of type string and return\n// a value of type T and true or false if the key is found.\nfunc (km keymap[T]) get(k string) (T, bool) {\n\tvar zero T\n\n\tv, found := km[k]\n\tif !found {\n\t\treturn zero, false\n\t}\n\n\treturn v, true\n}\n\n// =============================================================================\n\nfunc main() {\n\n\t// Construct a value of type keymap that stores integers.\n\tkm := make(keymap[int])\n\n\t// Add a value with key \"jack\" and a value of 10.\n\tkm.set(\"jack\", 10)\n\n\t// Add a value with key \"jill\" and a value of 20.\n\tkm.set(\"jill\", 20)\n\n\t// Get the value for \"jack\" and verify it's found.\n\tjack, found := km.get(\"jack\")\n\tif !found {\n\t\tfmt.Println(\"jack not found\")\n\t\treturn\n\t}\n\n\t// Print the value for the key \"jack\".\n\tfmt.Println(\"jack\", jack)\n\n\t// Get the value for \"jill\" and verify it's found.\n\tjill, found := km.get(\"jill\")\n\tif !found {\n\t\tfmt.Println(\"jill not found\")\n\t\treturn\n\t}\n\n\t// Print the value for the key \"jill\".\n\tfmt.Println(\"jill\", jill)\n}\n","Hash":"J5EvbYv9OgCxkHAP0uuGvKLCQS8="}]}]} ,"slices":{"Title":"slice ها","Description":"slice ها یک ساختار داده بسیار مهم در Go هستند. آنها اساس مدیریت و دستکاری داده ها به روشی انعطاف پذیر، کارآمد و پویا را تشکیل می دهند. برای همه برنامه نویسان Go بسیار مفید است که بدانند slice ها چگونه کار می کنند و چگونه از آنها استفاده کنند.","Pages":[{"Title":"slice ها","Content":"\n \u003ch2\u003eslice ها\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/training/individual-on-demand/ultimate-go-bundle/\" target=\"_blank\"\u003eتماشای ویدئو\u003c/a\u003e\u003c/li\u003e\n \n \u003cli\u003eنیاز به کمک مالی دارید؟ از \u003ca href=\"https://www.ardanlabs.com/scholarship/\" target=\"_blank\"\u003eفرم Scholarship\u003c/a\u003e ما استفاده کنید.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cp\u003e\n slice ها یک ساختار داده بسیار مهم در Go هستند. آنها اساس مدیریت و دستکاری داده ها به روشی انعطاف پذیر، کارآمد و پویا را تشکیل می دهند. برای همه برنامه نویسان Go بسیار مفید است که بدانند برش ها چگونه کار می کنند و چگونه می توان از آنها استفاده کرد.\n \u003c/p\u003e\n \n\n \u003ca href=\"/tour/eng/static/img/sl1.png\" target=\"_blank\"\u003e\n \u003cimg class=\"codeimg\" src=\"/tour/eng/static/img/sl1.png\"\u003e\n \u003c/a\u003e\n\n \u003ch2\u003eبررسی کد\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e1:\u003c/b\u003e اعلام و طول\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e2:\u003c/b\u003e اتواع ارجاع\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e3:\u003c/b\u003e اضافه کردن slice ها\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e4:\u003c/b\u003e گرفتن slice از slice ها\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e5:\u003c/b\u003e slice ها و ارجاع‌ ها\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e6:\u003c/b\u003e رسته ها و slice ها\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e7:\u003c/b\u003e توابع متغیر\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e8:\u003c/b\u003e مکانیک‌ های محدوده\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e9:\u003c/b\u003e پیمایش های کارآمد\u003c/li\u003e\n \n \u003cli\u003e\u003cb\u003eمثال\u003c/b\u003e \u003cb\u003e10:\u003c/b\u003e سه شاخص slice کردن\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n \u003ch2\u003eSlice سازنده\u003c/h2\u003e\n \n \n \u003cp\u003e\n ایجاد slice را می توان به چند طریق انجام داد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e// Slice of string set to its zero value state.\nvar slice []string\n\n// Slice of string set to its empty state.\nslice := []string{}\n\n// Slice of string set with a length and capacity of 5.\nslice := make([]string, 5)\n\n// Slice of string set with a length of 5 and capacity of 8.\nslice := make([]string, 5, 8)\n\n// Slice of string set with values with a length and capacity of 5.\nslice := []string{\u0026#34;A\u0026#34;, \u0026#34;B\u0026#34;, \u0026#34;C\u0026#34;, \u0026#34;D\u0026#34;, \u0026#34;E\u0026#34;}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n می توانید ببینید که تابع داخلی make به من اجازه می دهد تا طول و ظرفیت آرایه پشتیبان برش را پیش اختصاص دهم. اگر کامپایلر اندازه slice را در زمان کامپایل بداند، آرایه پشتیبان می تواند روی پشته ساخته شود.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eSlice طول در مقابل ظرفیت\u003c/h2\u003e\n \n \n \u003cp\u003e\n طول یک slice نشان دهنده تعداد عناصر قابل خواندن و نوشتن است. ظرفیت نشان دهنده تعداد کل عناصر موجود در آرایه پشتیبان از آن موقعیت اشاره گر است.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n به دلیل وجود syntactic sugar، slice ها مانند آرایه به نظر می رسند و احساس می شوند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eslice := make([]string, 5)\nslice[0] = \u0026#34;Apple\u0026#34;\nslice[1] = \u0026#34;Orange\u0026#34;\nslice[2] = \u0026#34;Banana\u0026#34;\nslice[3] = \u0026#34;Grape\u0026#34;\nslice[4] = \u0026#34;Plum\u0026#34;\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n می توانید تفاوت بین ساخت slice و آرایه را بگویید، زیرا یک آرایه در زمان کامپایل اندازه شناخته شده ای دارد و slice ها لزوماً اینگونه نیستند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n اگر سعی کنید به عنصری فراتر از طول slice دسترسی پیدا کنید، با خطای زمان اجرا مواجه خواهید شد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eslice := make([]string, 5)\nslice[5] = \u0026#34;Raspberry\u0026#34;\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خطای کامپایلر:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eError: panic: runtime error: index out of range slice[5] = \u0026#34;Runtime error\u0026#34;\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n در این مثال، طول slice 5 است و من سعی می کنم به عنصر ششم دسترسی پیدا کنم، که وجود ندارد.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eData Semantic Guideline For Slices\u003c/h2\u003e\n \n \n \u003cp\u003e\n به عنوان یک راهنما، اگر داده‌ای که با آن کار می‌کنم یک آرایه است، بهتر است که از دستورات معنای مقداری (Value Semantics) برای جابه‌جایی داده در برنامه‌ام استفاده کنم. این شامل تعریف فیلدها در یک نوع (Type) نیز می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc Foo(data []byte) []byte\n\ntype Foo struct {\n X []int\n Y []string\n Z []bool\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این قاعده برای تمامی ساختارهای داده داخلی Go اعمال می‌شود (آرایه‌ها، maps، کانال‌ها، اینترفیس ها و توابع).\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n یکی از دلایلی که برای استفاده از معنای اشاره‌گری (Pointer Semantics) ممکن است نیاز به به اشتراک‌گذاری آرایه برای یک عملیات رمزگشایی یا بازنگاری (decoding or unmarshaling) داشته باشید. استفاده از اشاره‌گرها برای این نوع عملیاتها مجاز است، اما اگر این موضوع برای دیگران واضح نباشد، آن را در مستندات توضیح دهید.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eچیدمان حافظه متوالی\u003c/h2\u003e\n \n \n \u003cp\u003e\n ایده پشت تعریف آرایه این است که یک آرایه داشته باشیم که به عنوان کارا‌ترین ساختار داده ارتباط دارد با سخت‌افزار. با این حال، هنوز نیاز به امکان انعطاف‌پذیری و کارا بودن با حجم داده مورد نیاز در زمان اجرا و رشد آینده دارید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc main() {\n slice := make([]string, 5, 8)\n slice[0] = \u0026#34;Apple\u0026#34;\n slice[1] = \u0026#34;Orange\u0026#34;\n slice[2] = \u0026#34;Banana\u0026#34;\n slice[3] = \u0026#34;Grape\u0026#34;\n slice[4] = \u0026#34;Plum\u0026#34;\n\n inspectSlice(slice)\n}\n\nfunc inspectSlice(slice []string) {\n fmt.Printf(\u0026#34;Length[%d] Capacity[%d]\\n\u0026#34;, len(slice), cap(slice))\n for i := range slice {\n fmt.Printf(\u0026#34;[%d] %p %s\\n\u0026#34;, i, \u0026amp;slice[i], slice[i])\n }\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eLength[5] Capacity[8]\n[0] 0xc00007e000 Apple\n[1] 0xc00007e010 Orange\n[2] 0xc00007e020 Banana\n[3] 0xc00007e030 Grape\n[4] 0xc00007e040 Plum\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n تابع inspectSlice نشان می‌دهد که یک تیکه (slice) دارای یک آرایه پشتیبان متوالی با یک گام پیش‌بینی‌پذیر است. همچنین نشان می‌دهد که یک تیکه دارای طول و ظرفیتی است که ممکن است متفاوت باشد. توجه کنید که تابع چاپ فقط در طول تیکه حرکت می‌کند.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eافزودن با استفاده از slice ها\u003c/h2\u003e\n \n \n \u003cp\u003e\n زبان یک تابع داخلی به نام append ارائه می‌دهد تا ارزش‌ها را به یک تیکه موجود اضافه کند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003evar data []string\n\nfor record := 1; record \u0026lt;= 102400; record\u0026#43;\u0026#43; {\n data = append(data, fmt.Sprintf(\u0026#34;Rec: %d\u0026#34;, record))\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n تابع append حتی زمانی که تیکه به حالت مقدار صفری خود مقداردهی اولیه شده باشد، با یک تیکه کار می‌کند. طراحی واسط برنامه‌نویسی (API) تابع append جالب است چرا که از تغییرات معنای مقداری استفاده می‌کند. تابع append یک نسخه از مقدار تیکه را به دست می‌آورد، مقدار تیکه خودش را تغییر می‌دهد، سپس یک نسخه از مقدار تیکه را به تماس‌گیرنده باز می‌گرداند.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n چرا API به این شکل طراحی شده است؟ این به این دلیل است که الگو به کار برده شده است که از معنای مقداری برای جابه‌جایی مقدار تیکه در سراسر برنامه استفاده شود. این الگو حتی در عملیات تغییر هم باید رعایت شود. به علاوه، تغییر معنای مقداری روش ایمن‌تری برای انجام تغییر است، زیرا تغییر در نسخهٔ تابع در جدایی از داده انجام می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n تابع append همیشه یک بلوک متوالی از حافظه برای آرایه پشتیبان تیکه حفظ می‌کند، حتی پس از افزایش. این برای سخت‌افزار اهمیت دارد.\n \u003c/p\u003e\n \n\n \u003ca href=\"/tour/eng/static/img/sl2.png\" target=\"_blank\"\u003e\n \u003cimg class=\"codeimg\" src=\"/tour/eng/static/img/sl2.png\"\u003e\n \u003c/a\u003e\n\n \n \u003cp\u003e\n هر بار که تابع append فراخوانی می‌شود، تابع بررسی می‌کند که آیا طول و ظرفیت تیکه یکسان است یا خیر. اگر یکسان باشند، به این معناست که در آرایه پشتیبان برای مقدار جدید جا نمانده است. در این صورت، append یک آرایه پشتیبان جدید ایجاد می‌کند (با دو برابر شدن یا افزایش 25٪) و سپس مقادیر را از آرایه قدیمی به آرایه جدید کپی می‌کند. سپس مقدار جدید می‌تواند به آن اضافه شود.\n \u003c/p\u003e\n \n\n \u003ca href=\"/tour/eng/static/img/sl3.png\" target=\"_blank\"\u003e\n \u003cimg class=\"codeimg\" src=\"/tour/eng/static/img/sl3.png\"\u003e\n \u003c/a\u003e\n\n \n \u003cp\u003e\n اگر طول و ظرفیت یکسان نباشد، به این معناست که یک عنصر اضافی از ظرفیت برای append وجود دارد. یک عنصر از ظرفیت گرفته شده و به طول تیکه اضافه می‌شود. این باعث می‌شود که عملیات append بسیار کارا باشد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n زمانی که آرایه پشتیبان دارای ظرفیت 1024 عنصر یا کمتر باشد، آرایه‌های پشتیبان جدید با دو برابر شدن اندازه آرایه موجود ساخته می‌شوند. یک‌باری که آرایه پشتیبان بیش از 1024 عنصر رشد کند، رشد با نرخ 25٪ انجام می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u003cb\u003eتوجه:\u003c/b\u003e \u003cb\u003eنحوه\u003c/b\u003e \u003cb\u003eافزایش\u003c/b\u003e \u003cb\u003eظرفیت\u003c/b\u003e \u003cb\u003eآرایه\u003c/b\u003e \u003cb\u003eپشتیبان\u003c/b\u003e \u003cb\u003eتابع\u003c/b\u003e \u003cb\u003eappend\u003c/b\u003e *تغییر کرده است از نسخه Go 1.18 به بعد.\n \u003c/p\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://tip.golang.org/doc/go1.18#runtime\" target=\"_blank\"\u003ehttps://tip.golang.org/doc/go1.18#runtime\u003c/a\u003e\u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n \u003ch2\u003eبرش slice ها\u003c/h2\u003e\n \n \n \u003cp\u003e\n تیکه‌ها (Slices) امکان اجتناب از کپی‌های اضافی و اختصاص حافظه در پشتیبانی‌کننده‌ی آرایه را فراهم می‌کنند هنگامی که نیاز به جداسازی عناصر مشخصی از آرایه پشتیبان برای عملیات‌های مختلف دارید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n نحوه برش تیکه با نمادگذاری لیست [a:b) نمایش داده می‌شود که به این معناست که عناصر از فهرست a تا b را شامل می‌شود، اما عنصر b شامل نمی‌شود.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eslice1 := []string{\u0026#34;A\u0026#34;, \u0026#34;B\u0026#34;, \u0026#34;C\u0026#34;, \u0026#34;D\u0026#34;, \u0026#34;E\u0026#34;}\nslice2 := slice1[2:4]\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n متغیر تیکه (slice2) یک مقدار تیکه جدید است که اکنون از همان آرایه پشتیبانی کننده‌ای استفاده می‌کند که تیکه اول (slice1) از آن استفاده می‌کند. با این حال، تیکه دوم (slice2) فقط به شما اجازه می‌دهد تا به عناصر در شاخص‌های 2 و 3 (C و D) از آرایه پشتیبانی کننده‌ی تیکه اصلی دسترسی داشته باشید. طول تیکه دوم (slice2) برابر با 2 است و نه 5 که در تیکه اول (slice1) است، و ظرفیت آن نیز برابر با 3 است چرا که اکنون از این موقعیت نقطه به 3 عنصر دیگر دسترسی داریم.\n \u003c/p\u003e\n \n\n \u003ca href=\"/tour/eng/static/img/sl4.png\" target=\"_blank\"\u003e\n \u003cimg class=\"codeimg\" src=\"/tour/eng/static/img/sl4.png\"\u003e\n \u003c/a\u003e\n\n \n \u003cp\u003e\n یک راه بهتر برای در نظر گرفتن برش، تمرکز بر روی طول با استفاده از این نمادگذاری است: [a:a+len] که به معنای از شاخص a تا a به علاوه طول است. این کار به کاهش خطاها در محاسبه تیکه‌های جدید کمک خواهد کرد.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n استفاده از این تابع inspect.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003efunc inspectSlice(slice []string) {\n fmt.Printf(\u0026#34;Length[%d] Capacity[%d]\\n\u0026#34;, len(slice), cap(slice))\n for i, s := range slice {\n fmt.Printf(\u0026#34;[%d] %p %s\\n\u0026#34;,\n i,\n \u0026amp;slice[i],\n s)\n }\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n شما می‌توانید این را در عمل مشاهده کنید.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eslice1 := []string{\u0026#34;A\u0026#34;, \u0026#34;B\u0026#34;, \u0026#34;C\u0026#34;, \u0026#34;D\u0026#34;, \u0026#34;E\u0026#34;}\nslice2 := slice1[2:4]\ninspectSlice(slice1)\ninspectSlice(slice2)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eLength[5] Capacity[5]\n[0] 0xc00007e000 A\n[1] 0xc00007e010 B\n[2] 0xc00007e020 C\n[3] 0xc00007e030 D\n[4] 0xc00007e040 E\nLength[2] Capacity[3]\n[0] 0xc00007e020 C \u0026lt;-- SAME AS INDEX 2 IN SLICE 1\n[1] 0xc00007e030 D \u0026lt;-- SAME AS INDEX 3 IN SLICE 1\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n توجه کنید که دو تیکه متفاوت از همان آرایه پشتیبانی‌کننده به اشتراک گذاشته می‌شوند. این را می‌توانید با مقایسه آدرس‌ها مشاهده کنید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n نکته جالب اینجا این است که هیچ اختصاصی انجام نمی‌شود. کامپایلر در زمان کامپایل اندازه آرایه پشتیبانی‌کننده برای تیکه اول (slice1) را می‌شناسد. انتقال یک نسخه از مقدار تیکه به تابع inspectSlice باعث می‌شود که همه چیز روی پشته (stack) باقی بماند.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eتغییرات در آرایه پشتیبان\u003c/h2\u003e\n \n \n \u003cp\u003e\n زمانی که از تیکه دوم (slice2) برای تغییر مقدار رشته در شاخص 0 استفاده می‌کنید، هر مقدار تیکه‌ای که از همان آرایه پشتیبان به اشتراک گذاشته می‌شود (که آدرس این شاخص جزء طول تیکه است) تغییر را مشاهده خواهد کرد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eslice1 := []string{\u0026#34;A\u0026#34;, \u0026#34;B\u0026#34;, \u0026#34;C\u0026#34;, \u0026#34;D\u0026#34;, \u0026#34;E\u0026#34;}\nslice2 := slice1[2:4]\nslice2[0] = \u0026#34;CHANGED\u0026#34;\ninspectSlice(slice1)\ninspectSlice(slice2)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eLength[5] Capacity[5]\n[0] 0xc00007e000 A\n[1] 0xc00007e010 B\n[2] 0xc00007e020 CHANGED\n[3] 0xc00007e030 D\n[4] 0xc00007e040 E\nLength[2] Capacity[3]\n[0] 0xc00007e020 CHANGED\n[1] 0xc00007e030 D\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n همیشه باید موازنه کنید که زمانی که یک مقدار در یک موقعیت شاخص تغییر می‌دهید، آیا آرایه پشتیبان با یک تیکه دیگر به اشتراک گذاشته شده است یا نه.\n \u003c/p\u003e\n \n\n \u003ca href=\"/tour/eng/static/img/sl5.png\" target=\"_blank\"\u003e\n \u003cimg class=\"codeimg\" src=\"/tour/eng/static/img/sl5.png\"\u003e\n \u003c/a\u003e\n\n \n \u003cp\u003e\n اگر از تابع داخلی append استفاده کنید چه اتفاقی می‌افتد؟\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eslice1 := []string{\u0026#34;A\u0026#34;, \u0026#34;B\u0026#34;, \u0026#34;C\u0026#34;, \u0026#34;D\u0026#34;, \u0026#34;E\u0026#34;}\nslice2 := slice1[2:4]\nslice2 = append(slice2, \u0026#34;CHANGED\u0026#34;)\ninspectSlice(slice1)\ninspectSlice(slice2)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eLength[5] Capacity[5]\n[0] 0xc00007e000 A\n[1] 0xc00007e010 B\n[2] 0xc00007e020 C\n[3] 0xc00007e030 D\n[4] 0xc00007e040 CHANGED\nLength[3] Capacity[3]\n[0] 0xc00007e020 C\n[1] 0xc00007e030 D\n[2] 0xc00007e040 CHANGED\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n تابع append اثر جانبی مشابهی ایجاد می‌کند، اما این اثر جانبی پنهان است. در این مورد، اضافه کردن طول بیشتر از ظرفیت به تیکه دوم (slice2) باعث تغییر مقدار در آدرس 0xc00007e040 شده است. متأسفانه، تیکه اول (slice1) این آدرس را از قبل به عنوان بخشی از طول خود داشت.\n \u003c/p\u003e\n \n\n \u003ca href=\"/tour/eng/static/img/sl6.png\" target=\"_blank\"\u003e\n \u003cimg class=\"codeimg\" src=\"/tour/eng/static/img/sl6.png\"\u003e\n \u003c/a\u003e\n\n \n \u003cp\u003e\n یک راه برای پیشگیری از اثر جانبی این است که در ساخت تیکه دوم (slice2) از تیکه با سه شاخص استفاده کنید تا طول و ظرفیت آن به 2 برسد.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eslice1 := []string{\u0026#34;A\u0026#34;, \u0026#34;B\u0026#34;, \u0026#34;C\u0026#34;, \u0026#34;D\u0026#34;, \u0026#34;E\u0026#34;}\nslice2 := slice1[2:4:4]\ninspectSlice(slice1)\ninspectSlice(slice2)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eLength[5] Capacity[5]\n[0] 0xc00007e000 A\n[1] 0xc00007e010 B\n[2] 0xc00007e020 C\n[3] 0xc00007e030 D\n[4] 0xc00007e040 E\nLength[2] Capacity[2]\n[0] 0xc00007e020 C\n[1] 0xc00007e030 D\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n نحوهٔ نمادگذاری برای تیکه با سه شاخص به صورت [a:b:c] است، که b و c باید یکسان باشند، زیرا [a-b] طول را تعیین می‌کند و [a-c] ظرفیت را تنظیم می‌کند. حالا طول و ظرفیت تیکه دوم (slice2) یکسان است.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n حالا می‌توانید دوباره از تابع داخلی append استفاده کنید، همانند قبل.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eslice1 := []string{\u0026#34;A\u0026#34;, \u0026#34;B\u0026#34;, \u0026#34;C\u0026#34;, \u0026#34;D\u0026#34;, \u0026#34;E\u0026#34;}\nslice2 := slice1[2:4:4]\nslice2 = append(slice2, \u0026#34;CHANGED\u0026#34;)\ninspectSlice(slice1)\ninspectSlice(slice2)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eLength[5] Capacity[5]\n[0] 0xc00007e000 A\n[1] 0xc00007e010 B\n[2] 0xc00007e020 C\n[3] 0xc00007e030 D\n[4] 0xc00007e040 E\nLength[3] Capacity[4]\n[0] 0xc000016080 C\n[1] 0xc000016090 D\n[2] 0xc0000160a0 CHANGED\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n توجه کنید که پس از فراخوانی تابع append، تیکه دوم (slice2) دارای یک آرایه پشتیبان جدید است.\n \u003c/p\u003e\n \n\n \u003ca href=\"/tour/eng/static/img/sl7.png\" target=\"_blank\"\u003e\n \u003cimg class=\"codeimg\" src=\"/tour/eng/static/img/sl7.png\"\u003e\n \u003c/a\u003e\n\n \n \u003cp\u003e\n این می‌تواند با مقایسه آدرس‌های هر تیکه مشاهده شود. در این مورد، تغییر در تیکه دوم (slice2) اثر جانبی روی تیکه اول (slice1) ایجاد نکرد.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eکپی کردن تیکه‌ها به صورت دستی\u003c/h2\u003e\n \n \n \u003cp\u003e\n یک تابع داخلی به نام copy وجود دارد که اجازهٔ کپی سطحی تیکه‌ها را می‌دهد. از آنجایی که یک رشته دارای یک آرایه پشتیبان از بایت‌ها است که تغییرناپذیر هستند، می‌تواند به عنوان یک منبع مورد استفاده قرار گیرد، اما هرگز به عنوان مقصد نه.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eslice1 := []string{\u0026#34;A\u0026#34;, \u0026#34;B\u0026#34;, \u0026#34;C\u0026#34;, \u0026#34;D\u0026#34;, \u0026#34;E\u0026#34;}\nslice3 := make([]string, len(slice1))\ncopy(slice3, slice1)\n\ninspectSlice(slice1)\ninspectSlice(slice3)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eLength[5] Capacity[5]\n[0] 0xc00005c050 A\n[1] 0xc00005c060 B\n[2] 0xc00005c070 C\n[3] 0xc00005c080 D\n[4] 0xc00005c090 E\nLength[5] Capacity[5]\n[0] 0xc00005c0a0 A\n[1] 0xc00005c0b0 B\n[2] 0xc00005c0c0 C\n[3] 0xc00005c0d0 D\n[4] 0xc00005c0e0 E\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n تا زمانی که تیکه مقصد دارای نوع و طول مناسب باشد، تابع داخلی copy قادر به انجام یک کپی سطحی خواهد بود.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eتیکه‌ها از تغییر معنای اشاره‌گری استفاده می‌کنند\u003c/h2\u003e\n \n \n \u003cp\u003e\n مهم است به یاد داشته باشید که حتی اگر از معنای مقداری برای جابه‌جایی تیکه در برنامه استفاده کنید، زمانی که یک تیکه را می‌خوانید و می‌نویسید، از تغییر معنای اشاره‌گری استفاده می‌کنید. به اشتراک گذاری عناصر تیکه با بخش‌های مختلف برنامه ممکن است اثرات جانبی ناخواسته‌ای ایجاد کند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e// Construct a slice of 1 user, set a pointer to that user,\n// use the pointer to update likes.\n\nusers := make([]user, 1)\nptrUsr0 := \u0026amp;users[0]\nptrUsr0.likes\u0026#43;\u0026#43;\n\nfor i := range users {\n fmt.Printf(\u0026#34;User: %d Likes: %d\\n\u0026#34;, i, users[i].likes)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eUser: 0 Likes: 1\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n یک تیکه برای نگهداری مجموعه‌ای از کاربران استفاده می‌شود. سپس یک اشاره‌گر به اولین کاربر تنظیم می‌شود و برای به‌روزرسانی تعداد پسندیدن‌ها (likes) استفاده می‌شود. خروجی نشان می‌دهد که استفاده از اشاره‌گر کار می‌کند.\n \u003c/p\u003e\n \n\n \u003ca href=\"/tour/eng/static/img/sl8.png\" target=\"_blank\"\u003e\n \u003cimg class=\"codeimg\" src=\"/tour/eng/static/img/sl8.png\"\u003e\n \u003c/a\u003e\n\n \n \u003cp\u003e\n سپس یک کاربر جدید به مجموعه اضافه می‌شود و دوباره از اشاره‌گر برای افزودن پسندیدن به اولین کاربر استفاده می‌شود.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003e// Append a new user to the collection. Use the pointer again\n// to update likes.\n\nusers = append(users, user{})\nptrUsr0.likes\u0026#43;\u0026#43;\n\nfor i := range users {\n fmt.Printf(\u0026#34;User: %d Likes: %d\\n\u0026#34;, i, users[i].likes)\n}\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n خروجی:\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003eUser: 0 Likes: 1\nUser: 1 Likes: 0\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n با این حال، از آنجایی که تابع append آرایه پشتیبانی را با یک آرایه جدید جایگزین کرد، اشاره‌گر در حال به‌روزرسانی آرایه پشتیبان قدیمی است و تعداد پسندیدن‌ها از دست رفته است. خروجی نشان می‌دهد که تعداد پسندیدن‌ها برای اولین کاربر افزایش نیافت.\n \u003c/p\u003e\n \n\n \u003ca href=\"/tour/eng/static/img/sl9.png\" target=\"_blank\"\u003e\n \u003cimg class=\"codeimg\" src=\"/tour/eng/static/img/sl9.png\"\u003e\n \u003c/a\u003e\n\n \n \u003cp\u003e\n شما باید مواظب باشید که بدانید آیا یک تیکه در طول اجرای برنامه به یک عملیات append اضافه می‌شود یا نه. نحوه به اشتراک گذاری تیکه باید مورد نظر قرار گیرد. به اشتراک گذاری اندیس‌های فردی ممکن است بهترین ایده نباشد. به اشتراک گذاری کل مقدار تیکه همچنین ممکن است در هنگام انجام عملیات append کار نکند. احتمالاً استفاده از تیکه به عنوان یک فیلد در یک ساختار (struct) و به اشتراک گذاری مقدار ساختار راه بهتری برای انجام کار است.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eکارایی گذر خطی\u003c/h2\u003e\n \n \n \u003cp\u003e\n زیبایی یک تیکه در این است که امکان انجام گذر خطی که به صورت مکانیکی با اشتراک‌گذاری داده با استفاده از معنای مقداری برای کاهش تخصیص حافظه در Heap (صفحه اصلی حافظه) را فراهم می‌کند.\n \u003c/p\u003e\n \n\n \n \u003cpre class=\"codeblock\"\u003ex := []byte{0x0A, 0x15, 0x0e, 0x28, 0x05, 0x96, 0x0b, 0xd0, 0x0}\n\na := x[0]\nb := binary.LittleEndian.Uint16(x[1:3])\nc := binary.LittleEndian.Uint16(x[3:5])\nd := binary.LittleEndian.Uint32(x[5:9])\n\nprintln(a, b, c, d)\u003c/pre\u003e\n \n\n \n \u003cp\u003e\n این کد با ایجاد مقادیر تیکه، از ابتدا تا انتها بخوانید، یک گذر خطی انجام می‌دهد که بخش‌های مختلفی از آرایه بایت را می‌خواند.\n \u003c/p\u003e\n \n\n \u003ca href=\"/tour/eng/static/img/sl10.png\" target=\"_blank\"\u003e\n \u003cimg class=\"codeimg\" src=\"/tour/eng/static/img/sl10.png\"\u003e\n \u003c/a\u003e\n\n \n \u003cp\u003e\n تمام داده‌ها در این کد روی پشته (stack) باقی می‌مانند. هیچ کپی اضافی از داده‌های داخل تیکه بایت ایجاد نمی‌شود.\n \u003c/p\u003e\n \n\n\n \u003ch2\u003eیادداشت‌ها\u003c/h2\u003e\n \n \u003cul\u003e\n \n \u003cli\u003eتیکه‌ها مانند آرایه‌های پویا با عملکرد و ویژگی‌های داخلی ویژه هستند.\u003c/li\u003e\n \n \u003cli\u003eتفاوتی بین طول و ظرفیت تیکه‌ها وجود دارد و هر کدام وظیفه‌های خاص خود را دارند.\u003c/li\u003e\n \n \u003cli\u003eتیکه‌ها اجازه مشاهده‌های متعددی از همان آرایه پایه را می‌دهند.\u003c/li\u003e\n \n \u003cli\u003eتیکه‌ها از طریق استفاده از تابع داخلی append قابل افزایش هستند.\u003c/li\u003e\n \n \u003c/ul\u003e\n\n \n \u003cpre class=\"codeblock\"\u003e** خواندن اضافی\u003c/pre\u003e\n \n\n \u003cul\u003e\n \n \u003cli\u003e\u003ca href=\"https://blog.golang.org/go-slices-usage-and-internals\" target=\"_blank\"\u003eGo Slices: usage and internals\u003c/a\u003e - Andrew Gerrand \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://blog.golang.org/strings\" target=\"_blank\"\u003eStrings, bytes, runes and characters in Go\u003c/a\u003e - Rob Pike \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://blog.golang.org/slices\" target=\"_blank\"\u003eArrays, slices (and strings): The mechanics of \u0026#39;append\u0026#39;\u003c/a\u003e - Rob Pike \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2013/08/understanding-slices-in-go-programming.html\" target=\"_blank\"\u003eUnderstanding Slices in Go Programming\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2013/08/collections-of-unknown-length-in-go.html\" target=\"_blank\"\u003eCollections Of Unknown Length in Go\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2013/09/iterating-over-slices-in-go.html\" target=\"_blank\"\u003eIterating Over Slices In Go\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2013/09/slices-of-slices-of-slices-in-go.html\" target=\"_blank\"\u003eSlices of Slices of Slices in Go\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://www.ardanlabs.com/blog/2013/12/three-index-slices-in-go-12.html\" target=\"_blank\"\u003eThree-Index Slices in Go 1.2\u003c/a\u003e - William Kennedy \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://github.com/golang/go/wiki/SliceTricks\" target=\"_blank\"\u003eSliceTricks\u003c/a\u003e \u003c/li\u003e\n \n \u003cli\u003e\u003ca href=\"https://go-review.googlesource.com/c/go/+/347917\" target=\"_blank\"\u003eruntime: Make slice growth formula a bit smoother\u003c/a\u003e - Go Team \u003c/li\u003e\n \n \u003c/ul\u003e\n\n\n","Files":[{"Name":"example1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how the capacity of the slice\n// is not available for use.\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\n\t// Create a slice with a length of 5 elements.\n\tfruits := make([]string, 5)\n\tfruits[0] = \"Apple\"\n\tfruits[1] = \"Orange\"\n\tfruits[2] = \"Banana\"\n\tfruits[3] = \"Grape\"\n\tfruits[4] = \"Plum\"\n\n\t// You can't access an index of a slice beyond its length.\n\tfruits[5] = \"Runtime error\"\n\n\t// Error: panic: runtime error: index out of range\n\n\tfmt.Println(fruits)\n}\n","Hash":"8O6F38SjsARSCW3zHXSjYlIXkao="},{"Name":"example2.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show the components of a slice. It has a\n// length, capacity and the underlying array.\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\n\t// Create a slice with a length of 5 elements and a capacity of 8.\n\tfruits := make([]string, 5, 8)\n\tfruits[0] = \"Apple\"\n\tfruits[1] = \"Orange\"\n\tfruits[2] = \"Banana\"\n\tfruits[3] = \"Grape\"\n\tfruits[4] = \"Plum\"\n\n\tinspectSlice(fruits)\n}\n\n// inspectSlice exposes the slice header for review.\nfunc inspectSlice(slice []string) {\n\tfmt.Printf(\"Length[%d] Capacity[%d]\\n\", len(slice), cap(slice))\n\tfor i, s := range slice {\n\t\tfmt.Printf(\"[%d] %p %s\\n\",\n\t\t\ti,\n\t\t\t\u0026slice[i],\n\t\t\ts)\n\t}\n}\n","Hash":"eAszlSS0R6s0znZizSjOrsiweK8="},{"Name":"example3.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to grow a slice using the built-in function append\n// and how append grows the capacity of the underlying array.\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\n\t// Declare a nil slice of strings.\n\tvar data []string\n\n\t// Capture the capacity of the slice.\n\tlastCap := cap(data)\n\n\t// Append ~100k strings to the slice.\n\tfor record := 1; record \u003c= 1e5; record++ {\n\n\t\t// Use the built-in function append to add to the slice.\n\t\tvalue := fmt.Sprintf(\"Rec: %d\", record)\n\t\tdata = append(data, value)\n\n\t\t// When the capacity of the slice changes, display the changes.\n\t\tif lastCap != cap(data) {\n\n\t\t\t// Calculate the percent of change.\n\t\t\tcapChg := float64(cap(data)-lastCap) / float64(lastCap) * 100\n\n\t\t\t// Save the new values for capacity.\n\t\t\tlastCap = cap(data)\n\n\t\t\t// Display the results.\n\t\t\tfmt.Printf(\"Addr[%p]\\tIndex[%d]\\t\\tCap[%d - %2.f%%]\\n\",\n\t\t\t\t\u0026data[0],\n\t\t\t\trecord,\n\t\t\t\tcap(data),\n\t\t\t\tcapChg)\n\t\t}\n\t}\n}\n","Hash":"R/zOT8bYNKAxAbOXaJGBJQml+8s="},{"Name":"example4.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to takes slices of slices to create different\n// views of and make changes to the underlying array.\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\n\t// Create a slice with a length of 5 elements and a capacity of 8.\n\tslice1 := make([]string, 5, 8)\n\tslice1[0] = \"Apple\"\n\tslice1[1] = \"Orange\"\n\tslice1[2] = \"Banana\"\n\tslice1[3] = \"Grape\"\n\tslice1[4] = \"Plum\"\n\n\tinspectSlice(slice1)\n\n\t// Take a slice of slice1. We want just indexes 2 and 3.\n\t// Parameters are [starting_index : (starting_index + length)]\n\tslice2 := slice1[2:4]\n\tinspectSlice(slice2)\n\n\tfmt.Println(\"*************************\")\n\n\t// Change the value of the index 0 of slice2.\n\tslice2[0] = \"CHANGED\"\n\n\t// Display the change across all existing slices.\n\tinspectSlice(slice1)\n\tinspectSlice(slice2)\n\n\tfmt.Println(\"*************************\")\n\n\t// Make a new slice big enough to hold elements of slice 1 and copy the\n\t// values over using the builtin copy function.\n\tslice3 := make([]string, len(slice1))\n\tcopy(slice3, slice1)\n\tinspectSlice(slice3)\n}\n\n// inspectSlice exposes the slice header for review.\nfunc inspectSlice(slice []string) {\n\tfmt.Printf(\"Length[%d] Capacity[%d]\\n\", len(slice), cap(slice))\n\tfor i, s := range slice {\n\t\tfmt.Printf(\"[%d] %p %s\\n\",\n\t\t\ti,\n\t\t\t\u0026slice[i],\n\t\t\ts)\n\t}\n}\n","Hash":"qRJS5C/sdtXi4CJRhjgNb44TzPo="},{"Name":"example5.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how one needs to be careful when appending\n// to a slice when you have a reference to an element.\npackage main\n\nimport \"fmt\"\n\ntype user struct {\n\tlikes int\n}\n\nfunc main() {\n\n\t// Declare a slice of 3 users.\n\tusers := make([]user, 3)\n\n\t// Share the user at index 1.\n\tshareUser := \u0026users[1]\n\n\t// Add a like for the user that was shared.\n\tshareUser.likes++\n\n\t// Display the number of likes for all users.\n\tfor i := range users {\n\t\tfmt.Printf(\"User: %d Likes: %d\\n\", i, users[i].likes)\n\t}\n\n\t// Add a new user.\n\tusers = append(users, user{})\n\n\t// Add another like for the user that was shared.\n\tshareUser.likes++\n\n\t// Display the number of likes for all users.\n\tfmt.Println(\"*************************\")\n\tfor i := range users {\n\t\tfmt.Printf(\"User: %d Likes: %d\\n\", i, users[i].likes)\n\t}\n\n\t// Notice the last like has not been recorded.\n}\n","Hash":"C2T1QkpUC3BwZ/w7ApxFSL+xRuw="},{"Name":"example6.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n/*\n\thttps://blog.golang.org/strings\n\n\tGo source code is always UTF-8.\n\tA string holds arbitrary bytes.\n\tA string literal, absent byte-level escapes, always holds valid UTF-8 sequences.\n\tThose sequences represent Unicode code points, called runes.\n\tNo guarantee is made in Go that characters in strings are normalized.\n\n\t----------------------------------------------------------------------------\n\n\tMultiple runes can represent different characters:\n\n\tThe lower case grave-accented letter à is a character, and it's also a code\n\tpoint (U+00E0), but it has other representations.\n\n\tWe can use the \"combining\" grave accent code point, U+0300, and attach it to\n\tthe lower case letter a, U+0061, to create the same character à.\n\n\tIn general, a character may be represented by a number of different sequences\n\tof code points (runes), and therefore different sequences of UTF-8 bytes.\n*/\n\n// Sample program to show how strings have a UTF-8 encoded byte array.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"unicode/utf8\"\n)\n\nfunc main() {\n\n\t// Declare a string with both chinese and english characters.\n\ts := \"世界 means world\"\n\n\t// UTFMax is 4 -- up to 4 bytes per encoded rune.\n\tvar buf [utf8.UTFMax]byte\n\n\t// Iterate over the string.\n\tfor i, r := range s {\n\n\t\t// Capture the number of bytes for this rune.\n\t\trl := utf8.RuneLen(r)\n\n\t\t// Calculate the slice offset for the bytes associated\n\t\t// with this rune.\n\t\tsi := i + rl\n\n\t\t// Copy of rune from the string to our buffer.\n\t\tcopy(buf[:], s[i:si])\n\n\t\t// Display the details.\n\t\tfmt.Printf(\"%2d: %q; codepoint: %#6x; encoded bytes: %#v\\n\", i, r, r, buf[:rl])\n\t}\n}\n","Hash":"RVs2+eAKQpRnyeDxYYxyiqA1gQ4="},{"Name":"example7.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to declare and use variadic functions.\npackage main\n\nimport \"fmt\"\n\n// user is a struct type that declares user information.\ntype user struct {\n\tid int\n\tname string\n}\n\nfunc main() {\n\n\t// Declare and initialize a value of type user.\n\tu1 := user{\n\t\tid: 1432,\n\t\tname: \"Betty\",\n\t}\n\n\t// Declare and initialize a value of type user.\n\tu2 := user{\n\t\tid: 4367,\n\t\tname: \"Janet\",\n\t}\n\n\t// Display both user values.\n\tdisplay(u1, u2)\n\n\t// Create a slice of user values.\n\tu3 := []user{\n\t\t{24, \"Bill\"},\n\t\t{32, \"Joan\"},\n\t}\n\n\t// Display all the user values from the slice.\n\tdisplay(u3...)\n\n\tchange(u3...)\n\tfmt.Println(\"**************************\")\n\tfor _, u := range u3 {\n\t\tfmt.Printf(\"%+v\\n\", u)\n\t}\n}\n\n// display can accept and display multiple values of user types.\nfunc display(users ...user) {\n\tfmt.Println(\"**************************\")\n\tfor _, u := range users {\n\t\tfmt.Printf(\"%+v\\n\", u)\n\t}\n}\n\n// change shows how the backing array is shared.\nfunc change(users ...user) {\n\tusers[1] = user{99, \"Same Backing Array\"}\n}\n","Hash":"4qs5nJ/l6k9vfJeKZan/elsz76M="},{"Name":"example8.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how the for range has both value and pointer semantics.\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\n\t// Using the value semantic form of the for range.\n\tfriends := []string{\"Annie\", \"Betty\", \"Charley\", \"Doug\", \"Edward\"}\n\tfor _, v := range friends {\n\t\tfriends = friends[:2]\n\t\tfmt.Printf(\"v[%s]\\n\", v)\n\t}\n\n\t// Using the pointer semantic form of the for range.\n\tfriends = []string{\"Annie\", \"Betty\", \"Charley\", \"Doug\", \"Edward\"}\n\tfor i := range friends {\n\t\tfriends = friends[:2]\n\t\tfmt.Printf(\"v[%s]\\n\", friends[i])\n\t}\n}\n","Hash":"1pl/XfLmE6pGQXWeS87PVSNplOY="},{"Name":"example9.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how slices allow for efficient linear traversals.\npackage main\n\nimport (\n\t\"encoding/binary\"\n\t\"fmt\"\n)\n\nfunc main() {\n\n\t// Given a stream of bytes to be processed.\n\tx := []byte{0x0A, 0x15, 0x0e, 0x28, 0x05, 0x96, 0x0b, 0xd0, 0x0}\n\n\t// Perform a linear traversal across the bytes, never making\n\t// copies of the actual data but still passing those bytes\n\t// to the binary function for processing.\n\n\ta := x[0]\n\tb := binary.LittleEndian.Uint16(x[1:3])\n\tc := binary.LittleEndian.Uint16(x[3:5])\n\td := binary.LittleEndian.Uint32(x[5:9])\n\n\t// The result is zero allocation data access that is mechanically\n\t// sympathetic with the hardware.\n\n\tfmt.Println(a, b, c, d)\n}\n","Hash":"hL9bNxb/iuKyYLUhyeU2ZLUan5M="},{"Name":"example10.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Sample program to show how to use a third index slice.\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\n\t// Create a slice of strings with different types of fruit.\n\tslice := []string{\"Apple\", \"Orange\", \"Banana\", \"Grape\", \"Plum\"}\n\tinspectSlice(slice)\n\n\t// Take a slice of slice. We want just index 2\n\ttakeOne := slice[2:3]\n\tinspectSlice(takeOne)\n\n\t// Take a slice of just index 2 with a length and capacity of 1\n\ttakeOneCapOne := slice[2:3:3] // Use the third index position to\n\tinspectSlice(takeOneCapOne) // set the capacity to 1.\n\n\t// Append a new element which will create a new\n\t// underlying array to increase capacity.\n\ttakeOneCapOne = append(takeOneCapOne, \"Kiwi\")\n\tinspectSlice(takeOneCapOne)\n}\n\n// inspectSlice exposes the slice header for review.\nfunc inspectSlice(slice []string) {\n\tfmt.Printf(\"Length[%d] Capacity[%d]\\n\", len(slice), cap(slice))\n\tfor i, s := range slice {\n\t\tfmt.Printf(\"[%d] %p %s\\n\",\n\t\t\ti,\n\t\t\t\u0026slice[i],\n\t\t\ts)\n\t}\n}\n","Hash":"fTjke16efysnLDguHQAMt5ALCmE="}]},{"Title":"تمرینات","Content":"\n \u003ch2\u003eتمرینات\u003c/h2\u003e\n \n \n \u003cp\u003e\n از قالب به عنوان نقطه شروع برای انجام تمرینات استفاده کنید. یک راه حل ممکن ارائه شده است.\n \u003c/p\u003e\n \n\n \u003ch2\u003eتمرین 1\u003c/h2\u003e\n \n \n \u003cp\u003e\n \u003cb\u003eقسمت\u003c/b\u003e \u003cb\u003eA:\u003c/b\u003e یک تیکه خالی از اعداد صحیح اعلام کنید. یک حلقه ایجاد کنید که 10 مقدار را به تیکه اضافه کند. بر روی تیکه حرکت کنید و هر مقدار را نمایش دهید.\n \u003c/p\u003e\n \n\n \n \u003cp\u003e\n \u003cb\u003eقسمت\u003c/b\u003e \u003cb\u003eB:\u003c/b\u003e یک تیکه از پنج رشته اعلام کرده و تیکه را با مقادیر رشته‌های ثابت مقداردهی اولیه کنید. تمام عناصر را نمایش دهید. یک تیکه از شاخص یک و دو ایجاد کرده و شاخص موقعیت و مقدار هر عنصر در تیکه جدید را نمایش دهید.\n \u003c/p\u003e\n \n\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n\n\n","Files":[{"Name":"exercise1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Declare a nil slice of integers. Create a loop that appends 10 values to the\n// slice. Iterate over the slice and display each value.\n//\n// Declare a slice of five strings and initialize the slice with string literal\n// values. Display all the elements. Take a slice of index one and two\n// and display the index position and value of each element in the new slice.\npackage main\n\n// Add imports.\n\nfunc main() {\n\n\t// Declare a nil slice of integers.\n\n\t// Append numbers to the slice.\n\n\t// Display each value in the slice.\n\n\t// Declare a slice of strings and populate the slice with names.\n\n\t// Display each index position and slice value.\n\n\t// Take a slice of index 1 and 2 of the slice of strings.\n\n\t// Display each index position and slice values for the new slice.\n}\n","Hash":"EUH8tWtJoMtTaDYQj8TsjP6bfbs="},{"Name":"answer1.go","Content":"// All material is licensed under the Apache License Version 2.0, January 2004\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Declare a nil slice of integers. Create a loop that appends 10 values to the\n// slice. Iterate over the slice and display each value.\n//\n// Declare a slice of five strings and initialize the slice with string literal\n// values. Display all the elements. Take a slice of index one and two\n// and display the index position and value of each element in the new slice.\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\n\t// Declare a nil slice of integers.\n\tvar numbers []int\n\n\t// Append numbers to the slice.\n\tfor i := 0; i \u003c 10; i++ {\n\t\tnumbers = append(numbers, i*10)\n\t}\n\n\t// Display each value.\n\tfor _, number := range numbers {\n\t\tfmt.Println(number)\n\t}\n\n\t// Declare a slice of strings.\n\tnames := []string{\"Bill\", \"Joan\", \"Jim\", \"Cathy\", \"Beth\"}\n\n\t// Display each index position and name.\n\tfor i, name := range names {\n\t\tfmt.Printf(\"Index: %d Name: %s\\n\", i, name)\n\t}\n\n\t// Take a slice of index 1 and 2.\n\tslice := names[1:3]\n\n\t// Display the value of the new slice.\n\tfor i, name := range slice {\n\t\tfmt.Printf(\"Index: %d Name: %s\\n\", i, name)\n\t}\n}\n","Hash":"QH1TKKOx3v9d7V8gMrU1qBNm4GE="}]}]} }