مسیرها
/
Julia
Julia
/
تمرین‌ها
/
تمیز و برق‌زده
تمیز و برق‌زده

تمیز و برق‌زده

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

مقدمه

این موضوع بزرگی است! می‌توان درباره‌ی آن کتابی بلند نوشت، و چند نفر هم این کار را کرده‌اند (برای دیدن چند نمونه، در Amazon عبارت «unicode book» را جست‌وجو کنید).

تاریخچه‌ای بسیار کوتاه

پردازش نویسه‌ها در رایانه در دهه‌های پیشین بسیار ساده‌تر بود، زمانی که برنامه‌نویسان فرض می‌کردند انگلیسی تنها زبان مهم است. پس: ۲۶ حرف بزرگ و کوچک، ۱۰ رقم، چند نشانه‌ی سجاوندی، به‌علاوه‌ی یک کد (0x07) برای به صدا درآوردن زنگ، و همه‌ی این‌ها در ۷ بیت جا می‌شد: مجموعه‌ی نویسه‌های ASCII.

طبیعی بود که مردم کم‌کم بپرسند تکلیف à، ä و Ł چیست، بعد افراد دیگر درباره‌ی ऄ، ஹ و ญ بپرسند، و جوان‌ها ایموجی 😱 بخواهند. چه باید کرد؟

خلاصه‌ی ماجرا اینکه، افراد باهوش و صبور زیادی مجبور شدند سال‌ها در کمیته‌ها کار کنند و جزئیات مجموعه‌نویسه‌ی Unicode و کدگذاری‌هایی مانند UTF-8 را مشخص کنند، و خیلی از نرم‌افزارها به یک بازنویسی بسیار پیچیده نیاز پیدا کردند. همچنین باگ‌های جدید زیادی ایجاد شد.

برای اینکه همه‌چیز نشکند، طراحی Unicode/UTF-8 تضمین می‌کند که ۱۲۷ کد اول با ASCII یکسان باشند (حتی همان زنگ).

نویسه‌ها در Julia

زبان‌هایی که پس از حدود سال ۲۰۰۵ طراحی شدند، این مزیت بزرگ را دارند که استاندارد Unicode نسبتاً پایداری از قبل وجود داشته است.

Julia (که نخستین بار در ۲۰۱۲ منتشر شد) می‌توانست فرض کند که همه‌چیز Unicode خواهد بود: نویسه‌ها، رشته‌ها، اسم‌های متغیرها و توابع، عملگرهای ریاضی...

به نقل از راهنما: «Julia کار با متن ساده‌ی ASCII را ساده و کارآمد می‌کند، و پردازش Unicode را تا حد ممکن ساده و کارآمد می‌کند.» به عبارت «تا حد ممکن» دقت کنید؛ این بخش مهمی از این جمله است.

لیترال‌های نویسه با کوتیشن تکی نوشته می‌شوند و از رشته‌هایی که با کوتیشن دوتایی نوشته می‌شوند متمایزند.

این برای کسانی که از دنیای C/C++ می‌آیند بدیهی است، اما ممکن است برای برنامه‌نویسان Python و Javascript گیج‌کننده باشد.

julia> a = 'a'  # Roman alphabet
'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase)

julia> typeof(a)
Char

julia> jha = 'झ'  # Devanagari alphabet
'झ': Unicode U+091D (category Lo: Letter, other)

julia> typeof(jha)
Char

julia> '❤'  # heart emoji
'❤': Unicode U+2764 (category So: Symbol, other)

از این مثال‌ها می‌بینیم که نوع آن Char است، و Julia اطلاعات بیشتری درباره‌ی دسته‌ی نویسه دارد.

اگر دقیق‌تر نگاه کنیم، به نظر می‌رسد این نویسه‌ها را می‌توان با ۴ رقم هگزادسیمال نمایش داد. مجموعه‌ی کامل نویسه‌ها حداکثر به ۶ رقم هگزادسیمال نیاز دارد.

به این اعداد «نقطه‌ی کد» می‌گویند، و در حال حاضر از U+0000 تا U+10FFFF را در بر می‌گیرند. این‌ها در REPL نمایش داده می‌شوند، اما در کد برای به‌دست آوردنشان از codepoints() استفاده کنید.

تبدیل بین Char و Int ساده است:

julia> Int('a')
97

julia> Char(97)
'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase)

کامپایلر برخی شکل‌های حساب اعداد صحیح را روی Charها مجاز می‌داند:

julia> 'b' - 'a'  # interval, in alphabetic order
1

julia> 'b' + 'a'
ERROR: MethodError: no method matching +(::Char, ::Char)

julia> 'a' + 5
'f': ASCII/Unicode U+0066 (category Ll: Letter, lowercase)

julia> 'f' + ('A' - 'a')  # same as `uppercase('f')`
'F': ASCII/Unicode U+0046 (category Lu: Letter, uppercase)

توابع مربوط به نویسه‌ها

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

  • برای الفباهای مناسب، با uppercase() و lowercase() حروف را بزرگ یا کوچک کنید.
  • بزرگی یا کوچکی حروف را با isuppercase() و islowercase() بررسی کنید.
  • نوع نویسه را با این‌ها بررسی کنید:
    • isletter() بسیاری از الفباها را پوشش می‌دهد
    • isdigit()، فقط ارقام ۰ تا ۹ را بررسی می‌کند
    • isnumeric()، گسترده‌تر از isdigit، پس برای ¾ و خط‌های مختلف غیراروپایی true می‌دهد
    • isxdigit()، ارقام هگزادسیمال
    • isascii()، نویسه‌ی پیش از Unicode
    • ispunct()، نشانه‌ی سجاوندی
    • isspace()، هر نویسه‌ی فضای خالی
    • isprint()، نویسه‌های قابل چاپ (مخالفش iscntrl() است)
islowercase('A')  # false
uppercase('γ')  # 'Γ': Unicode U+0393 (category Lu: Letter, uppercase)
ispunct('@')  # true
isdigit('A')  # false
isxdigit('A')  # true

تبدیل متقابل بردار Char و String

برای تبدیل String به بردار Char می‌توانیم از collect() استفاده کنیم.

برای تبدیل بردار Char به String، سازنده‌ی String() وجود دارد.

julia> s = "abcde"
"abcde"

julia> cv = collect(s)
5-element Vector{Char}:
 'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase)
 'b': ASCII/Unicode U+0062 (category Ll: Letter, lowercase)
 'c': ASCII/Unicode U+0063 (category Ll: Letter, lowercase)
 'd': ASCII/Unicode U+0064 (category Ll: Letter, lowercase)
 'e': ASCII/Unicode U+0065 (category Ll: Letter, lowercase)

julia> String(cv)
"abcde"

این با هر نویسه‌ای کار می‌کند، نه فقط ASCII.

julia> collect("❤,😱")
3-element Vector{Char}:
 '❤': Unicode U+2764 (category So: Symbol, other)
 ',': ASCII/Unicode U+002C (category Po: Punctuation, other)
 '😱': Unicode U+1F631 (category So: Symbol, other)

توجه کنید که سازنده‌ی String() روی یک بردار عمل می‌کند. برای تبدیل یک Char تنها به رشته‌ای یک‌نویسه‌ای، تابع string() است، با s کوچک.

julia> string('a')
"a"

ذخیره‌سازی

همه‌چیز تا اینجای این سند نسبتاً ساده به نظر می‌رسد، پس آیا واقعاً چیز زیادی برای نگرانی نیست؟

متأسفانه، این بیش از حد خوش‌بینانه است!

یک پیچیدگی از نیاز به «حداکثر» ۶ رقم هگزادسیمال برای هر نقطه‌ی کد ناشی می‌شود. این یعنی نویسه‌های مختلف وقتی با UTF-8 کدگذاری می‌شوند، به مقدارهای متفاوتی از فضا در حافظه نیاز دارند.

یک بایت فقط می‌تواند اعداد (بدون علامت) تا ۲۵۵، یعنی دو رقم هگزادسیمال، را ذخیره کند، بنابراین UTF-8 از تعداد متغیری بایت (۱ تا ۴) برای ذخیره‌ی یک Char استفاده می‌کند. به این‌ها «واحد کد» می‌گویند، و تابع ncodeunits() عدد لازم برای یک نویسه‌ی مشخص را برمی‌گرداند.

julia> codepoint(jha)  # jha 'झ' is defined in an earlier example
0x0000091d

julia> ncodeunits(jha)
3

julia> ncodeunits('a')  # ASCII character
1

julia> ncodeunits('😱')  # emoji
4

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

این مسئله روی رشته‌ها تأثیر می‌گذارد، جایی که برای نمایه‌سازی چالش‌هایی ایجاد می‌کند.

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

در این تمرین، مجموعه‌ای جزئی از توابع کمکی را پیاده‌سازی می‌کنید تا به یک برنامه‌نویس در پاک‌سازی اسم شناسه‌ها کمک کنید.

در این ۶ تمرین، به‌تدریج توابع transform را برای تبدیل تک‌کاراکترها و clean را برای تبدیل رشته‌ها می‌سازید.

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

اگر رشته‌ی خالی به تابع clean داده شود، باید رشته‌ی خالی برگردانده شود.

1. هر خط تیره‌ای را که دیدید با زیرخط جایگزین کنید

تابع transform را پیاده‌سازی کنید تا هر خط تیره را با زیرخط جایگزین کند.

julia> transform('-')
"_"

2. همه‌ی فاصله‌ها را حذف کنید

همه‌ی کاراکترهای فاصله را حذف کنید. این کار شامل فاصله‌های ابتدایی و انتهایی هم می‌شود.

julia> transform(' ')
""

3. camelCase را به kebab-case تبدیل کنید

تابع transform را طوری تغییر دهید که camelCase را به kebab-case تبدیل کند

julia> transform('D')
"-d"

4. کاراکترهایی را که رقم هستند حذف کنید

تابع transform را طوری تغییر دهید که هر کاراکتر عددی را حذف کند.

julia> transform('7')
""

5. حروف کوچک یونانی را با علامت سؤال جایگزین کنید

تابع transform را طوری تغییر دهید که هر حرف یونانی در بازه‌ی 'α' تا 'ω' را جایگزین کند.

julia> transform('β')
"?"

6. این عملیات را ترکیب کنید تا روی یک رشته عمل کنند

تابع clean را پیاده‌سازی کنید تا این عملیات را روی کل یک رشته اعمال کند.

کاراکترهایی که خارج از این قواعد باشند باید بدون تغییر باقی بمانند.

julia> clean("  a2b Cd-ω😀  ")
"ab-cd_?😀"
ویرایش از طریق GitHub این لینک در پنجره یا زبانه‌ی جدیدی باز می‌شود
Julia Exercism

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

در Exercism ثبت‌نام کنید تا Julia را همراه با 35 مفهوم128 تمرین و مربی‌گری انسانی واقعی یاد بگیرید و در آن استاد شوید، همه‌ی این‌ها رایگان.