مسیرها
/
Crystal
Crystal
/
تمرین‌ها
/
قفل رمز عبور
قفل رمز عبور

قفل رمز عبور

تمرین یادگیری

مقدمه

Crystal به یک متغیر اجازه می‌دهد که از چندین نوع تشکیل شود. به این «نوع اجتماع» می‌گویند. در Crystal معمول است که یک نوع اجتماع توسط کامپایلر استنتاج شود.

Note

یک نوع اجتماع در زمان اجرا هنوز یک نوع واحد است، حتی اگر از چندین نوع تشکیل شده باشد. این یعنی اگر یک نوع اجتماع از String و Int32 ساخته شده باشد، در یک زمان هر دو نخواهد بود. در عوض، یا String خواهد بود یا Int32.

یک نوع اجتماع با جدا کردن نوع‌ها با یک خط عمودی (|) اعلام می‌شود. معمولاً آن‌ها را داخل پرانتز می‌گذارند، اما الزامی نیست. رایج‌ترین نوع اجتماع (T | Nil) است که در آن T یک نوع است و می‌تواند در متدهایی رخ دهد که می‌توانند Nil برگردانند. به این حالت، نیل‌پذیر بودن متغیر هم می‌گویند.

foo : String = "Hello"
foo = nil # Error: type must be String, not (String | Nil)

foo : (String | Nil) = "Hello"
foo = nil

محدود به دو نوع نیست، بلکه می‌تواند هر تعداد نوعی باشد که بخواهید.

foo : (String | Int32 | Nil | Float64) = "Hello"
foo = 1
foo = nil
foo = 1.0

typeof در برابر Object#class

دو راه برای گرفتن نوع یک متغیر وجود دارد. یا با استفاده از typeof یا با استفاده از Object#class. تفاوت این است که typeof نوع متغیر را در زمان کامپایل برمی‌گرداند، در حالی که Object#class نوع را در زمان اجرا برمی‌گرداند. این یعنی اگر بخواهید ببینید آیا یک متغیر نوع اجتماع است، Object#class نمی‌تواند این را به شما بگوید، چون فقط نوع را در زمان اجرا برمی‌گرداند، که یک نوع واحد است.

foo = 0 == 0 ? "a" : 1
typeof(foo) # => (String | Int32)
foo.class # => String

عملیات روی نوع اجتماع

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

foo : (String | Int32) = "Hello"
foo.downcase # Error: undefined method 'downcase' for (String | Int32)

Crystal یک متد ویژه برای نوع‌های اجتماع دارد: متد is_a? که یک نوع را به عنوان آرگومان می‌گیرد و یک «مقدار منطقی» برمی‌گرداند. متد nil? میانبری برای is_a?(Nil) است. قرار دادن متد is_a? در یک عبارت کنترلی به کامپایلر می‌گوید که کدام نوع است و در نتیجه تضمین می‌کند که همان نوع است. و برای شاخه‌ی else تضمین می‌شود که آن نوع نیست.

foo : (String | Int32) = "Hello"
if foo.is_a?(String)
  typeof(foo)  # => String
  foo.downcase # => "hello"
end

این is_a? محدود به گرفتن یک نوع واحد به عنوان آرگومان نیست، بلکه می‌تواند یک نوع اجتماع هم بگیرد. همچنین می‌توان آن را با && برای چندین نوع ترکیب کرد.

Note

متد is_a? وقتی همراه با یک عبارت کنترلی استفاده می‌شود، نمی‌تواند متغیر نمونه‌ای یا متغیر کلاسی باشد. در عوض، این‌ها باید ابتدا به یک متغیر محلی اختصاص داده شوند.

as

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

foo : String | Int32 = "Hello"
foo.as(String).downcase # => "hello"

foo.as(Int32) # Error: can't cast String to Int32
Caution

این رویکرد فقط برای زمانی است که مطمئن هستید نوع، نوع مورد انتظار است، یا می‌خواهید وقتی چنین نیست یک استثنا پرتاب کنید.

استفاده از این رویکرد با تنظیمات نادرست می‌تواند به رفتار غیرمنتظره منجر شود.

as?

as? بسیار شبیه as کار می‌کند، اما اگر نوع، نوع مورد انتظار نباشد، به جای پرتاب استثنا nil برمی‌گرداند. این یعنی یک نوع اجتماع از نوع مورد انتظار و Nil برمی‌گرداند.

foo : (String | Int32) = "Hello"
foo.as?(String).downcase # => "hello"

foo.as?(Int32) # => nil

کوتاه‌نویسی نیل‌پذیر

نیل‌پذیر یعنی یک متغیر می‌تواند یا یک نوع باشد یا Nil. این را می‌توان به صورت (T | Nil) نوشت. اما چون نوع‌های نیل‌پذیر نسبتاً رایج‌اند، یک کوتاه‌نویسی برایش وجود دارد: T?.

# This:
foo : (String | Nil) = "Hello"
foo = nil

# Is the same as:
foo : String? = "Hello"
foo = nil

دستورالعمل‌ها

شرکتی که در آن کار می‌کنید در آستانه‌ی عرضه‌ی گوشی هوشمند تازه‌اش به نام Smarty 5 است؛ این گوشی به یک سیستم دوربین تازه، یک طراحی جدید و یک پردازنده‌ی انقلابی مجهز است که روی سیستم‌عامل جدیدی به نام smartyOS اجرا می‌شود.

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

Caution

سیستم رمز عبوری که در این تمرین با آن کار می‌کنید امن نیست و فقط با هدف آموزشی به کار می‌رود. این سیستم نباید در هیچ برنامه‌ی واقعی به کار رود.

1. تعیین رمز عبور

گوشی به کاربران اجازه می‌دهد از چند نوع مختلف رمز عبور استفاده کنند، از یک رمز عبور عددی ساده تا یک رمز عبور حرف‌وعددی امن‌تر و حتی رمزی که اثر انگشتشان را ذخیره می‌کند.

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

کلاس PasswordLock را با یک سازنده پیاده‌سازی کنید که هر یک از این سه نوع رمز عبور را به عنوان آرگومان می‌گیرد و آن را در یک متغیر نمونه‌ای به نام @password ذخیره می‌کند.

password_lock = PasswordLock.new(1234)
# => #<PasswordLock:0x7f8e1b8c0b80 @password=1234>

2. رمزگذاری رمز عبور

شرکت تصمیم گرفته رمز عبور را رمزگذاری کند تا به‌صورت متن ساده ذخیره نشود و از شما خواسته است متدی برای رمزگذاری آن پیاده‌سازی کنید.

هر نوع رمز عبور، روش یکتای خودش را برای رمزگذاری دارد:

  • رمز عبور Int32: رمز عبور بر دو تقسیم می‌شود و به نزدیک‌ترین عدد صحیح گرد می‌شود.
  • رمز عبور String: رمز عبور برعکس می‌شود.
  • رمز عبور Float64: رمز عبور در چهار ضرب می‌شود.

یک متد نمونه‌ای به نام encrypt پیاده‌سازی کنید که هیچ آرگومانی نمی‌گیرد و متغیر نمونه‌ای @password را طوری تغییر می‌دهد که رمزگذاری شود.

password_lock = PasswordLock.new(1234)
# => #<PasswordLock:0x7f8e1b8c0b80 @password=1234>

password_lock.encrypt
# => #<PasswordLock:0x7f8e1b8c0b80 @password=617>

3. بررسی درست بودن رمز عبور

شرکت همچنین از شما خواسته است متدی پیاده‌سازی کنید که بررسی می‌کند آیا رمز عبور داده‌شده درست است یا نه.

آن‌ها می‌خواهند این متد اگر رمز عبور نادرست بود nil و اگر رمز عبور درست بود "Unlocked" برگرداند.

یک متد نمونه‌ای به نام unlock? پیاده‌سازی کنید که رمز عبور مورد بررسی را به عنوان آرگومان می‌گیرد. این متد باید اگر رمز عبور نادرست بود nil و اگر رمز عبور درست بود "Unlocked" برگرداند.

password_lock = PasswordLock.new(1234)
password_lock.encrypt
password_lock.unlock?(1234)
# => "Unlocked"
ویرایش از طریق GitHub این لینک در پنجره یا زبانه‌ی جدیدی باز می‌شود
Crystal Exercism

آماده‌اید قفل رمز عبور را شروع کنید؟

در Exercism ثبت‌نام کنید تا Crystal را همراه با 26 مفهوم142 تمرین و مربی‌گری انسانی واقعی یاد بگیرید و در آن استاد شوید، همه‌ی این‌ها رایگان.