المسارات
/
Julia
Julia
/
التمارين
/
نظيف تمامًا
نظيف تمامًا

نظيف تمامًا

تمرين تعلّمي

مقدمة

هذا موضوع ضخم! يمكن كتابة كتاب طويل عنه، وقد فعل ذلك عدة أشخاص (ابحث في Amazon عن "unicode book" لترى بعض الأمثلة).

لمحة تاريخية موجزة

كان التعامل مع المحارف في الحاسوب أبسط بكثير في العقود الماضية، حين كان المبرمجون يفترضون أن الإنجليزية هي اللغة المهمة الوحيدة. إذن: 26 حرفًا، بحالتيه العلوية والسفلية، و10 أرقام، وعدة علامات ترقيم، إضافة إلى رمز (0x07) لرنين الجرس، وقد اتّسع ذلك كله في 7 بتات: مجموعة محارف ASCII.

وبطبيعة الحال، بدأ الناس يسألون عن à و ä و Ł، ثم بدأ آخرون يسألون عن ऄ و ஹ و ญ، وأراد الشباب إيموجي 😱. فماذا نفعل؟

ولاختصار القصة الطويلة، اضطر كثير من الأشخاص الأذكياء والصابرين إلى العمل في لجان لسنوات، يضعون تفاصيل مجموعة محارف Unicode، والترميزات مثل UTF-8، واحتاج كثير من البرمجيات إلى إعادة كتابة بالغة التعقيد. كما ظهرت Bug جديدة كثيرة.

ولمنع انهيار كل شيء، يضمن تصميم Unicode/UTF-8 أن أول 127 رمزًا مطابقة لـ ASCII (حتى الجرس).

المحارف في Julia

تتمتع اللغات التي صُممت بعد عام 2005 تقريبًا بميزة كبيرة، وهي أن معيار Unicode مستقر بدرجة معقولة كان موجودًا بالفعل.

تمكنت Julia (التي صدرت أول مرة عام 2012) من افتراض أن كل شيء سيكون 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 معلومات إضافية عن فئة المحرف.

وبنظرة أدق، يبدو أن هذه المحارف يمكن تمثيلها بأربعة أرقام ست عشرية. أما مجموعة المحارف الكاملة فتحتاج إلى ما يصل إلى 6 أرقام ست عشرية.

تُسمى هذه الأرقام «نقاط الترميز»، وهي تتراوح حاليًا من U+0000 إلى U+10FFFF. وتظهر في REPL، لكن داخل الكود استخدم codepoints() للحصول عليها.

التحويل بين Char و Int بسيط:

julia> Int('a')
97

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

يسمح المترجم بـ بعض أشكال الحساب الصحيح على قيم Chars:

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()، يختبر الأرقام من 0 إلى 9 تحديدًا
    • 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 والسلسلة النصية

للتحويل من السلسلة النصية إلى متجه المحارف، يمكننا استخدام collect().

وللتحويل من متجه المحارف إلى السلسلة النصية، يوجد المُنشئ 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"

التخزين

كل ما ورد في هذا المستند حتى الآن يبدو بسيطًا نسبيًا، فهل لا يوجد حقًا ما يدعو للقلق؟

للأسف، هذا تفاؤل مفرط!

تنشأ إحدى التعقيدات من الحاجة إلى «ما يصل إلى» 6 أرقام ست عشرية لكل نقطة ترميز. وهذا يعني أن المحارف المختلفة تحتاج إلى مساحات مختلفة في الذاكرة عند ترميزها بـ UTF-8.

لا يمكن للبايت أن يخزّن إلا الأعداد (غير مُوقّعة) حتى 255، أي رقمين ست عشريين، لذا يستخدم UTF-8 عددًا متغيرًا من البايتات (من 1 إلى 4) لتخزين 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 تمرينًا، وإرشاد بشري حقيقي، وكل ذلك مجانًا.