این موضوع بزرگی است! میتوان دربارهی آن کتابی بلند نوشت، و چند نفر هم این کار را کردهاند (برای دیدن چند نمونه، در Amazon عبارت «unicode book» را جستوجو کنید).
پردازش نویسهها در رایانه در دهههای پیشین بسیار سادهتر بود، زمانی که برنامهنویسان فرض میکردند انگلیسی تنها زبان مهم است. پس: ۲۶ حرف بزرگ و کوچک، ۱۰ رقم، چند نشانهی سجاوندی، بهعلاوهی یک کد (0x07) برای به صدا درآوردن زنگ، و همهی اینها در ۷ بیت جا میشد: مجموعهی نویسههای ASCII.
طبیعی بود که مردم کمکم بپرسند تکلیف à، ä و Ł چیست، بعد افراد دیگر دربارهی ऄ، ஹ و ญ بپرسند، و جوانها ایموجی 😱 بخواهند. چه باید کرد؟
خلاصهی ماجرا اینکه، افراد باهوش و صبور زیادی مجبور شدند سالها در کمیتهها کار کنند و جزئیات مجموعهنویسهی Unicode و کدگذاریهایی مانند UTF-8 را مشخص کنند، و خیلی از نرمافزارها به یک بازنویسی بسیار پیچیده نیاز پیدا کردند. همچنین باگهای جدید زیادی ایجاد شد.
برای اینکه همهچیز نشکند، طراحی Unicode/UTF-8 تضمین میکند که ۱۲۷ کد اول با ASCII یکسان باشند (حتی همان زنگ).
زبانهایی که پس از حدود سال ۲۰۰۵ طراحی شدند، این مزیت بزرگ را دارند که استاندارد 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()، نویسهی پیش از Unicodeispunct()، نشانهی سجاوندی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 داده شود، باید رشتهی خالی برگردانده شود.
تابع transform را پیادهسازی کنید تا هر خط تیره را با زیرخط جایگزین کند.
julia> transform('-')
"_"
همهی کاراکترهای فاصله را حذف کنید. این کار شامل فاصلههای ابتدایی و انتهایی هم میشود.
julia> transform(' ')
""
تابع transform را طوری تغییر دهید که camelCase را به kebab-case تبدیل کند
julia> transform('D')
"-d"
تابع transform را طوری تغییر دهید که هر کاراکتر عددی را حذف کند.
julia> transform('7')
""
تابع transform را طوری تغییر دهید که هر حرف یونانی در بازهی 'α' تا 'ω' را جایگزین کند.
julia> transform('β')
"?"
تابع clean را پیادهسازی کنید تا این عملیات را روی کل یک رشته اعمال کند.
کاراکترهایی که خارج از این قواعد باشند باید بدون تغییر باقی بمانند.
julia> clean(" a2b Cd-ω😀 ")
"ab-cd_?😀"