Lecture 02 · Words and Tokens
المحاضرة 02 · الكلمات والرموز (Tokens)
Sentence Segmentation and Regex
تقسيم الجمل والتعابير النمطية (Regex)
P1 MUSTFinding where sentences end, and then the pattern language that does almost all the dirty work in an NLP pipeline.
كيف نعرف أين تنتهي الجملة، ثم لغة الأنماط (regex) التي تؤدي معظم العمل الخشن في أي NLP pipeline.
Twenty-five slides, but three of them are a cheat sheet you only need to recognize. The exam-relevant core is small: what each quantifier means, what ^ does in two different places, how alternation binds, and how capture groups work. Everything else is reference.
خمس وعشرون شريحة، لكن ثلاثاً منها مجرد ورقة مراجعة تحتاج أن تتعرف عليها فقط. واللب المطلوب في الامتحان صغير: معنى كل quantifier، وما يفعله ^ في موضعين مختلفين، وكيف يرتبط alternation، وكيف تعمل capture groups. وما عدا ذلك مرجع.
Part 1: Sentence segmentation
الجزء 1: تقسيم الجمل
Before you can parse a sentence or translate it, you have to know where it starts and stops. That is the whole task on slide 53: find the sentence boundaries.
قبل أن تُعرب جملة أو تترجمها، يجب أن تعرف أين تبدأ وأين تنتهي. وهذه هي كل المهمة في الشريحة 53: إيجاد sentence boundaries.
If every sentence ended in a period and every period ended a sentence, this would be one line of code. It is not, because the period is overloaded. The same character marks an abbreviation, a decimal point, an initial, and a URL.
لو كانت كل جملة تنتهي بنقطة وكل نقطة تنهي جملة، لكان الحل سطراً واحداً من الكود. لكنه ليس كذلك، لأن النقطة محمّلة بوظائف كثيرة: فهي نفسها تعلّم الاختصار، والفاصلة العشرية، والحرف الأول من الاسم، وURL.
Dr. Smith paid $4.50 at 10 a.m. He left.
^ ^ ^
| | |
abbreviation abbreviation actual sentence end
Only the last period is a boundary. Three of the four are not.
يعرض المربع الجملة Dr. Smith paid $4.50 at 10 a.m. He left. مع أسهم تشير إلى النقاط. نقطة Dr. اختصار، ونقطة $4.50 عشرية، ونقطة a.m. اختصار، والأخيرة وحدها نهاية جملة فعلية. أي أن ثلاثاً من الأربع ليست حدوداً.
So the real question on these slides is: given a period, is it sentence-final or not? That is a binary decision, and slide 54 gives two families of answers.
فالسؤال الحقيقي في هذه الشرائح: إذا رأيت نقطة، هل هي نهاية جملة أم لا؟ هذا قرار ثنائي، والشريحة 54 تعطي عائلتين من الأجوبة.
Rule-based heuristics
قواعد استدلالية (Rule-based heuristics)
- punctuation plus capitalization patterns (a period followed by a space and a capital letter is a decent first guess)
أنماط الترقيم مع الحروف الكبيرة: نقطة تتبعها مسافة ثم حرف كبير تخمين أولي جيد.
- abbreviation lists (a lexicon of Dr., Inc., a.m. that you check before deciding)
قوائم الاختصارات: معجم يحتوي Dr. وInc. وa.m. تراجعه قبل أن تقرر.
- quote and bracket balancing (do not cut inside an unclosed quote)
موازنة علامات التنصيص والأقواس: لا تقطع داخل تنصيص لم يُغلق بعد.
Statistical approaches
مناهج إحصائية
- learn a boundary probability from annotated data
احتمال وجود حدّ جملة يُتعلَّم من بيانات موسومة.
- use context features, or a neural model, around each candidate period
استخدام context features، أو نموذج عصبي، حول كل نقطة مرشّحة.
One line from slide 53 is easy to skip and easy to examine: segmentation is often done jointly with tokenization and with abbreviation lexicons. The two tasks feed each other. You cannot decide whether a.m. is one token without deciding whether that final period ends the sentence, and vice versa.
سطر في الشريحة 53 يسهل تجاوزه ويسهل السؤال عنه: التقطيع إلى جمل يُنجَز غالباً بالتوازي مع tokenization ومع معاجم الاختصارات. المهمتان تغذي كل منهما الأخرى: لا يمكنك أن تقرر هل a.m. رمز واحد دون أن تقرر هل نقطته تنهي الجملة، والعكس صحيح.
ELIZA, and why a dumb program felt alive
ELIZA، ولماذا بدا برنامج بسيط كأنه حي
Slide 55 shows ELIZA, Weizenbaum's 1966 program that imitated a Rogerian psychotherapist. It had no understanding of anything. It was essentially a stack of regular expressions that rewrote the user's sentence back at them as a question.
تعرض الشريحة 55 برنامج ELIZA الذي كتبه Weizenbaum عام 1966 ليحاكي معالجاً نفسياً من مدرسة Rogerian. لم يكن يفهم شيئاً إطلاقاً، بل كان في جوهره كومة من regular expressions تعيد كتابة جملة المستخدم وترجعها إليه على هيئة سؤال.
user: I am feeling sad rule: s/.* I am (depressed|sad) .*/I AM SORRY TO HEAR YOU ARE \1/ ELIZA: I AM SORRY TO HEAR YOU ARE SAD
يبيّن المربع قاعدة واحدة من ELIZA: المستخدم يكتب I am feeling sad، والقاعدة s/.* I am (depressed|sad) .*/I AM SORRY TO HEAR YOU ARE \1/ تلتقط الكلمة التي داخل القوسين في group 1، ثم تُدرجها مكان \1 في الرد، فيخرج I AM SORRY TO HEAR YOU ARE SAD. لا فهم، مجرد استبدال نمط.
People still formed emotional attachments to it. The slide names this:
ومع ذلك تعلّق به الناس عاطفياً. والشريحة تسمّي هذه الظاهرة:
Eliza effect: the tendency to unconsciously assume computer behaviors are analogous to human behaviors.
Eliza effect: الميل اللاواعي إلى افتراض أن سلوك الحاسوب مشابه للسلوك البشري.
The reason this sits here, right before the regex block, is the point of the story: pattern substitution alone is enough to produce surprisingly convincing text. That is both an argument for how powerful regex is and a warning about reading understanding into surface behavior.
وسبب وجود هذه القصة هنا، قبل قسم regex مباشرة، هو مغزاها: الاستبدال بالأنماط وحده كافٍ لإنتاج نص مقنع بدرجة مفاجئة. وهذا في الوقت نفسه حجة على قوة regex، وتحذير من قراءة الفهم في سلوك سطحي.
Part 2: Why regex in NLP
الجزء 2: لماذا الـ regex في NLP
Slide 56 lists what regex is actually used for. It is not a toy, it is the first and last stage of most real pipelines.
تسرد الشريحة 56 ما يُستخدم regex فيه فعلياً. ليس لعبة، بل هو المرحلة الأولى والأخيرة في معظم المسارات الحقيقية.
- tokenization and pre-tokenization (this is the big one, and we come back to it at the end of this page)
tokenization وpre-tokenization، وهذا أهمها، وسنعود إليه في آخر الصفحة.
- normalization of dates, numbers, currency
normalization للتواريخ والأرقام والعملات.
- filtering and cleaning raw scraped text
التصفية والتنظيف للنص الخام المسحوب من الويب.
- feature extraction for classical models
feature extraction للنماذج التقليدية.
And the caveat the slide ends on: regex is language- and task-dependent. A pattern tuned for English whitespace will break on Chinese, which has none. A pattern for tweets will break on legal documents.
والتحذير الذي تنتهي به الشريحة: regex يعتمد على اللغة وعلى المهمة. نمط مضبوط على المسافات في الإنجليزية ينكسر على الصينية التي لا مسافات فيها، ونمط للتغريدات ينكسر على الوثائق القانونية.
The four building blocks
لبنات البناء الأربع
Slide 57 gives the whole grammar of regex in four lines. Everything else on this page is sugar on top of these.
تعطي الشريحة 57 كل قواعد regex في أربعة أسطر. وكل ما بعدها في هذه الصفحة تحلية فوق هذه الأربعة.
| Blockاللبنة | What it doesماذا تفعل | Exampleمثال |
|---|---|---|
| Literalالحرف الحرفي (Literal) | a character matches itselfالحرف يطابق نفسه | a matches 'a'a يطابق 'a' |
| Concatenationالتسلسل (Concatenation) | patterns side by side must occur in orderالأنماط المتجاورة يجب أن تظهر بالترتيب | abc matches 'abc'abc يطابق 'abc' |
| Disjunctionالاختيار (Disjunction) | either one or the otherإما هذا أو ذاك | a|b matches 'a' or 'b'a|b يطابق 'a' أو 'b' |
| Groupingالتجميع (Grouping) | parentheses define scopeالأقواس تحدد النطاق | (ab)c |
يسرد الجدول البنى الأربع. Literal: الحرف يطابق نفسه، فـ a يطابق a. وConcatenation: أنماط متجاورة يجب أن تظهر بالترتيب، فـ abc يطابق abc. وDisjunction: أحدهما أو الآخر، فـ a|b يطابق a أو b. وGrouping: الأقواس تحدد النطاق، كما في (ab)c.
Character classes: the square brackets
أصناف الحروف: الأقواس المربعة
Writing a|b|c|d gets old fast. Square brackets are a compact disjunction over single characters. Slides 58, 59 and 60 are three tables covering three uses of them.
كتابة a|b|c|d تصبح مملة بسرعة. والأقواس المربعة [ ] اختصار لـ disjunction على حروف مفردة. والشرائح 58 و59 و60 ثلاثة جداول تغطي ثلاثة استخدامات لها.
Plain disjunction, slide 58
الاختيار البسيط، الشريحة 58
| Patternالنمط | Matchesيطابق | In this stringفي هذه السلسلة |
|---|---|---|
[mM]ary | Mary or maryMary أو mary | "Mary Ann stopped by Mona's" |
[abc] | 'a', 'b', or 'c''a' أو 'b' أو 'c' | "In uomini, in soldati" |
[1234567890] | any one digitأي رقم واحد | "plenty of 7 to 5" |
يعرض جدول الشريحة 58 ثلاثة أنماط. [mM]ary يطابق Mary أو mary. و[abc] يطابق أي واحد من a أو b أو c. و[1234567890] يطابق أي رقم واحد. والقاعدة: ما داخل الأقواس المربعة يطابق حرفاً واحداً فقط، لا السلسلة كلها.
Note what the first row buys you: [mM]ary is case-insensitive only on the first letter. That is a much tighter pattern than turning on a global case-insensitive flag.
لاحظ ما يمنحك إياه السطر الأول: [mM]ary لا يبالي بحالة الحرف في الحرف الأول وحده، وهذا نمط أضيق بكثير من تشغيل علم تجاهل الحالة على النمط كله.
Ranges with a dash, slide 59
النطاقات بالشرطة، الشريحة 59
Inside brackets, a dash between two characters means "everything from here to there in the character ordering".
داخل الأقواس المربعة، الشرطة بين حرفين تعني "كل شيء من هنا إلى هناك في ترتيب الحروف".
| Patternالنمط | Matchesيطابق | In this stringفي هذه السلسلة |
|---|---|---|
[A-Z] | an upper case letterحرف كبير | "we should call it 'Drenched Blossoms'" |
[a-z] | a lower case letterحرف صغير | "my beans were impatient to be hoed!" |
[0-9] | a single digitرقم واحد | "Chapter 1: Down the Rabbit Hole" |
يعرض جدول الشريحة 59 ثلاثة مدًى: [A-Z] حرف كبير واحد، و[a-z] حرف صغير واحد، و[0-9] رقم واحد. وكلها تطابق حرفاً واحداً، والشرطة هي التي تولّد المدى.
The caret, slide 60
علامة الإقحام (^)، الشريحة 60
This slide is the single most examinable thing in the regex block, because the caret means two completely different things depending on where it sits.
هذه الشريحة أكثر ما يُسأل عنه في قسم regex كله، لأن ^ يعني شيئين مختلفين تماماً بحسب موقعه.
| Patternالنمط | Matches (single characters)يطابق (حروفاً مفردة) | In this stringفي هذه السلسلة |
|---|---|---|
[^A-Z] | NOT an upper case letterليس حرفاً كبيراً | "Oyfn pripetchik" |
[^Ss] | neither 'S' nor 's'لا 'S' ولا 's' | "I have no exquisite reason for't" |
[^.] | not a periodليس نقطة | "our resident Djinn" |
[e^] | either 'e' or '^'إما 'e' أو '^' | "look up ^ now" |
a^b | the literal pattern 'a^b'النمط الحرفي 'a^b' | "look up a^b now" |
جدول الشريحة 60 في خمسة أسطر، والعمود الأول هو مفتاح الفهم. [^A-Z] يعني حرفاً ليس كبيراً. و[^Ss] يعني حرفاً ليس S ولا s. و[^.] يعني حرفاً ليس نقطة. أما [e^] فيطابق إما e وإما ^ نفسها، لأن ^ هنا ليست أول حرف داخل القوسين. وa^b نمط حرفي يطابق النص a^b.
The last two rows are the trick. A caret negates only when it is the very first character inside the brackets. Anywhere else, including outside brackets in the middle of a pattern, it is either a literal caret or a line anchor.
السطران الأخيران هما الفخ. ^ ينفي فقط إذا كان أول حرف داخل الأقواس المربعة. وفي أي موضع آخر، بما في ذلك خارج الأقواس في وسط النمط، هو إما ^ حرفية وإما line anchor.
First inside the brackets, the caret bites. Anywhere else, it anchors or it is just a hat.
أول حرف داخل الأقواس المربعة يعضّ، أي ينفي. وفي أي موضع آخر يرسو كـ anchor أو يكون مجرد قبّعة.
Three faces of ^, in one line each:
ثلاثة وجوه لـ ^، كل وجه في سطر:
[^abc]negation, because it is first inside[ ][^abc] نفي، لأنه أول حرف داخل [ ].
^abcanchor, because it is first and outside[ ]^abc رسو (anchor)، لأنه أول حرف وخارج [ ].
[e^]literal, because inside[ ]but not first[e^] حرفي، لأنه داخل [ ] لكنه ليس الأول.
Aliases: the shorthand classes, slide 64
الأسماء المستعارة: الأصناف المختصرة، الشريحة 64
Some classes are so common they get a one-character name. Memorize this table. It is the one that shows up inside every real-world pattern.
بعض الأصناف شائعة جداً فأُعطيت اسماً من حرف واحد. احفظ هذا الجدول، فهو الذي يظهر داخل كل نمط واقعي.
| Aliasالاسم المستعار | Expands toيتوسع إلى | Matchesيطابق | First match inأول تطابق في |
|---|---|---|---|
\d | [0-9] | any digitأي رقم | Party of 5 |
\D | [^0-9] | any non-digitأي شيء ليس رقماً | Blue moon |
\w | [a-zA-Z0-9_] | any alphanumeric or underscoreأي حرف أو رقم أو شرطة سفلية | Daiyu |
\W | [^\w] | a non-alphanumericما ليس حرفاً أو رقماً | !!!! |
\s | [ \r\t\n\f] | whitespace (space, tab)مسافة بيضاء (مسافة، tab) | in Concord |
\S | [^\s] | non-whitespaceما ليس مسافة بيضاء | in Concord |
جدول الشريحة 64 يربط كل اختصار بما يتوسّع إليه. \d هو [0-9] أي أي رقم، و\D هو [^0-9] أي أي شيء غير رقم. و\w هو [a-zA-Z0-9_] أي حرف أو رقم أو شرطة سفلية، و\W هو [^\w] أي نقيضه. و\s هو [ \r\t\n\f] أي مسافة بيضاء، و\S هو [^\s] أي غير مسافة. اقرأ العمود الثاني وستحفظ الجدول كله.
The pattern is completely regular: lowercase is the class, uppercase is its complement. \d and \D partition every character between them, and so do \w/\W and \s/\S.
النمط منتظم تماماً: الحرف الصغير هو الصنف، والكبير هو مُكمّله. فـ \d و\D يقسمان كل الحروف بينهما، وكذلك \w و\W، وكذلك \s و\S.
Note that \w includes the underscore and the digits, not just letters. That surprises people. \w+ on user_id_42 matches the whole thing as one token.
ولاحظ أن \w يشمل الشرطة السفلية والأرقام، لا الحروف وحدها، وهذا يفاجئ الناس. فـ \w+ على user_id_42 يطابقها كلها كرمز واحد.
Quantifiers: how many times, slides 61 and 62
المكمِّمات (Quantifiers): كم مرة، الشريحتان 61 و62
A quantifier says how many copies of the thing before it you want. This is the second heavily examined area.
الـ quantifier يحدد كم نسخة تريد من الشيء الذي قبله. وهذه ثاني أكثر منطقة يُسأل عنها.
| Quantifierالمكمِّم | Meansالمعنى | Can it match nothing?هل يمكن أن يطابق لا شيء؟ | Exampleمثال |
|---|---|---|---|
* | zero or more of the previous char or expressionصفر أو أكثر من الحرف أو التعبير السابق | yesنعم | ab* matches 'a', 'ab', 'abbb'ab* يطابق 'a', 'ab', 'abbb' |
+ | one or more of the previous char or expressionواحد أو أكثر من الحرف أو التعبير السابق | noلا | ab+ matches 'ab', 'abbb', not 'a'ab+ يطابق 'ab', 'abbb'، لا 'a' |
? | zero or one, that is, optionalصفر أو واحد، أي اختياري | yesنعم | colou?r matches 'color' and 'colour'colou?r يطابق 'color' و'colour' |
{n} | exactly n occurrencesn ظهوراً بالضبط | no (unless n = 0)لا (إلا إذا n = 0) | \d{4} matches '2026'\d{4} يطابق '2026' |
{m,n} | between m and n timesبين m وn مرة | only if m = 0فقط إذا m = 0 | \d{2,4} matches '26', '202', '2026'\d{2,4} يطابق '26', '202', '2026' |
. | any single characterأي حرف واحد | noلا | a.c matches 'abc', 'a7c', 'a c'a.c يطابق 'abc', 'a7c', 'a c' |
.* | any string of zero or more charsأي سلسلة من صفر حرف أو أكثر | yesنعم | a.*b matches 'a' then anything then 'b'a.*b يطابق 'a' ثم أي شيء ثم 'b' |
*? | lazy: as few as possible, stop at the first matchكسول (lazy): أقل ما يمكن، ويتوقف عند أول تطابق | yesنعم | <.*?> stops at the first ><.*?> يتوقف عند أول > |
يسرد جدول الشرائح 61 و62 الـ quantifiers، والعمود الثالث (هل يمكن أن يطابق لا شيء؟) هو موضع السؤال. * صفر أو أكثر، ويمكنه أن يطابق لا شيء، فـ ab* يطابق a وab وabbb. و+ واحد أو أكثر، ولا يمكنه، فـ ab+ لا يطابق a وحدها. و? صفر أو واحد أي اختياري، فـ colou?r يطابق color وcolour. و{n} عدد مضبوط، فـ \d{4} يطابق 2026. و{m,n} بين m وn مرة، فـ \d{2,4} يطابق 26 و202 و2026. و. أي حرف واحد. و.* أي سلسلة بطول صفر أو أكثر. و*? هو lazy أي أقل قدر ممكن، فـ <.*?> يتوقف عند أول >.
The comparison that gets tested: * versus + versus ?
المقارنة التي يُختبر عليها: * مقابل + مقابل ?
All three are quantifiers, so people blur them. The real difference is just the two numbers, the floor and the ceiling.
الثلاثة كلها quantifiers، فيخلط الناس بينها. والفرق الحقيقي رقمان فقط: الحد الأدنى والحد الأعلى.
| Quantifierالمكمِّم | Minimumالحد الأدنى | Maximumالحد الأقصى | Equivalent toيكافئ |
|---|---|---|---|
* | 0 | ∞ | {0,} |
+ | 1 | ∞ | {1,} |
? | 0 | 1 | {0,1} |
يختصر الجدول الفرق في رقمين. *: الأدنى 0 والأعلى لا نهائي، أي يكافئ {0,}. و+: الأدنى 1 والأعلى لا نهائي، أي {1,}. و?: الأدنى 0 والأعلى 1، أي {0,1}. عمود الحد الأدنى وحده يفصل بينها.
The consequence that matters: a pattern containing * or ? can succeed without consuming anything. That is why [0-9]* "matches" an empty string and a search for it will happily return a zero-length match at position 0. If you want at least one digit, you must write [0-9]+.
والنتيجة التي تهم: النمط الذي يحتوي * أو ? يمكن أن ينجح دون أن يستهلك أي حرف. ولهذا فإن [0-9]* "يطابق" سلسلة فارغة، والبحث به سيعيد بلا تردد مطابقة بطول صفر عند الموضع 0. وإذا أردت رقماً واحداً على الأقل فاكتب [0-9]+.
Star can skip. Plus must have one. Question mark is at most one.
النجمة * تستطيع التخطي. و+ لا بد له من واحد. و? واحد على الأكثر.
And for laziness: add a question mark to a quantifier and it becomes shy. * grabs everything it can; *? grabs the least it can get away with.
وأما الـ laziness: أضف علامة استفهام إلى أي quantifier فيصبح خجولاً. فـ * يلتقط كل ما يستطيع، و*? يلتقط أقل ما يمكنه النجاة به.
Greedy versus lazy, seen on a real string
الجشع (Greedy) مقابل الكسول (lazy)، على سلسلة حقيقية
Slide 62 names this distinction (.* versus .*?) and slide 71 gives the string that makes it obvious. Both quantifiers find a valid match. They find different valid matches.
تسمّي الشريحة 62 هذا الفرق (.* مقابل .*?)، والشريحة 71 تعطي السلسلة التي تجعله واضحاً. كلا الـ quantifier يجد مطابقة صحيحة، لكنهما يجدان مطابقتين مختلفتين.
.* swallows the text between the tags and returns one giant match. Lazy .*? returns each tag separately. Neither is wrong; they answer different questions..* يبتلع النص بين الوسوم ويعيد تطابقاً واحداً ضخماً. والنمط الكسول .*? يعيد كل وسم على حدة. ليس أي منهما خاطئاً؛ إنهما يجيبان عن سؤالين مختلفين.يرسم الشكل السلسلة <Begin> Hello World <\End> ثم يقارن مطابقتين. الشريط العلوي greedy للنمط <.*> يمتد على السلسلة كلها، لأنه يأخذ كل شيء حتى آخر >، فتكون مطابقة واحدة بطول 26 حرفاً. والشريطان السفليان lazy للنمط <.*?> يغطيان الوسم الأول والوسم الأخير فقط، لأنه يتوقف عند أول >، فتحصل مع علم g على مطابقتين منفصلتين. نفس شكل النمط ونفس الدخل، وجوابان مختلفان.
Trace both patterns against the slide 71 string, character by character.
تتبّع النمطين على سلسلة الشريحة 71 حرفاً حرفاً.
input: <Begin> Hello World <\End>
0123456789... (26 characters, last index 25)
PATTERN A: <.*> (greedy)
step 1 '<' must match a literal '<' -> matches at index 0
step 2 '.*' is greedy, so it first takes EVERYTHING
from index 1 to index 25
step 3 now '>' has nothing left to match -> FAIL
step 4 backtrack: give one character back. '.*' now ends at 24,
next char is index 25 which is '>' -> MATCH
result <Begin> Hello World <\End> the entire string
PATTERN B: <.*?> (lazy)
step 1 '<' matches at index 0
step 2 '.*?' is lazy, so it first takes NOTHING
step 3 '>' must match index 1, which is 'B' -> fail,
so '.*?' grudgingly takes one more character
step 4 repeat until '.*?' holds "Begin" (indices 1-5)
and index 6 is '>' -> MATCH
result <Begin> 7 characters
PATTERN C: <[^>]*> (negated class, no backtracking needed)
'[^>]*' literally cannot cross a '>', so it stops on its own.
result <Begin> same answer as lazy, but faster
يتتبّع المربع ثلاثة أنماط على الدخل <Begin> Hello World <\End> وطوله 26 حرفاً وآخر فهرس فيه 25. النمط A هو <.*> وهو greedy: يطابق < عند الفهرس 0، ثم يأخذ .* كل شيء حتى 25، فلا يبقى شيء لـ > فيفشل، فيتراجع (backtrack) حرفاً واحداً فيطابق، والنتيجة السلسلة كاملة. والنمط B هو <.*?> وهو lazy: يبدأ .*? بلا شيء ثم يأخذ حرفاً حرفاً على مضض حتى يمسك Begin، فيطابق > عند الفهرس 6، والنتيجة <Begin> بسبعة أحرف. والنمط C هو <[^>]*>: الصنف [^>]* لا يستطيع عبور > أصلاً فيتوقف من نفسه، ويعطي نفس جواب lazy لكن أسرع ودون تراجع.
Pattern C is how experienced people write it. The negated character class makes the "stop here" rule part of the pattern instead of relying on backtracking, which is also the fix for the catastrophic-backtracking warning on slide 77.
النمط C هو ما يكتبه أصحاب الخبرة. فصنف الحروف المنفي يجعل قاعدة "توقف هنا" جزءاً من النمط نفسه بدل الاعتماد على backtracking، وهو أيضاً الحل لتحذير catastrophic backtracking في الشريحة 77.
Anchors, slide 63
المراسي (Anchors)، الشريحة 63
Anchors match a position, not a character. They consume nothing. That is the key idea: an anchor is an assertion about where you are.
الـ anchors تطابق موضعاً لا حرفاً، ولا تستهلك شيئاً. وهذه هي الفكرة المفتاحية: الـ anchor تأكيد على موقعك، لا على محتوى.
| Anchorالمرساة | Assertsماذا تؤكد | Consumes characters?هل تستهلك حروفاً؟ |
|---|---|---|
^ | start of a line or stringبداية سطر أو سلسلة | noلا |
$ | end of a line or stringنهاية سطر أو سلسلة | noلا |
\b | a word boundary, the seam between a word char and a non-word charحد كلمة، أي الفاصل بين حرف من حروف الكلمة وحرف ليس منها | noلا |
\B | not a word boundaryليس حد كلمة | noلا |
جدول الشريحة 63 أربعة anchors، والعمود الأخير "هل يستهلك حروفاً؟" جوابه no في الأربعة كلها، وهذا بيت القصيد. ^ يؤكد بداية سطر أو سلسلة، و$ يؤكد نهايتها، و\b يؤكد حدّ كلمة أي الخيط الفاصل بين حرف كلمة وحرف غير كلمة، و\B يؤكد عدم وجود حدّ كلمة.
The slide says these are useful for matching whole words and line-based formats. Here is why \b is needed so often:
تقول الشريحة إنها مفيدة في مطابقة الكلمات الكاملة والصيغ المبنية على الأسطر. وهذا سبب الحاجة الدائمة إلى \b:
text: the theme of the theater pattern the -> matches inside "theme" and "theater" too, 4 matches pattern \bthe\b -> matches only the standalone word "the", 2 matches
يقارن المربع نمطين على النص the theme of the theater. النمط the يطابق داخل theme وtheater أيضاً، فيعطي 4 مطابقات. أما \bthe\b فيطابق الكلمة المستقلة the فقط، فيعطي مطابقتين.
And from the cheat sheet on slide 73: ^ and $ are line anchors. With the multiline flag m turned on, they apply per line rather than once per whole string.
ومن ورقة المراجعة في الشريحة 73: ^ و$ هما line anchors، أي مرساتا سطر. وبتشغيل علم multiline أي m يعملان عند كل سطر بدل مرة واحدة على السلسلة كلها.
Escaping, slides 65 and 66
الهروب (Escaping)، الشريحتان 65 و66
A metacharacter is a character regex has given a job to. If you want the character itself instead of its job, put a backslash in front of it.
الـ metacharacter حرف أعطاه regex وظيفة. وإذا أردت الحرف نفسه بدل وظيفته، فضع شرطة مائلة خلفية قبله.
Slide 65 lists the ones that need escaping:
تسرد الشريحة 65 الحروف التي تحتاج escaping:
يسرد الصندوق الـ metacharacters التي تحتاج شرطة مائلة خلفية قبلها: . و^ و$ و* و+ و? و( و) و[ و] و{ و} و|، والمظلّل في آخرها هو \ نفسها.
| Escapeالهروب | Matchesيطابق | First match inأول تطابق في |
|---|---|---|
\* | an asterisk "*"نجمة "*" | "K*A*P*L*A*N" |
\. | a period "."نقطة "." | "Dr. Livingston, I presume" |
\? | a question markعلامة استفهام | "Why don't they come and lend a hand?" |
\n | a newlineسطر جديد | |
\t | a tabمسافة tab | |
\\ | a literal backslashشرطة مائلة عكسية حرفية |
يعرض الجدول أمثلة escaping: \* يطابق نجمة *، و\. يطابق نقطة .، و\? يطابق علامة استفهام. والسطور الثلاثة الأخيرة من النوع الآخر: \n سطر جديد، و\t مسافة tab، و\\ شرطة مائلة خلفية حرفية.
Notice the two different jobs a backslash does. In \. it removes special meaning from a metacharacter. In \n and \d it adds special meaning to an ordinary letter. Same symbol, opposite directions.
لاحظ وظيفتي الشرطة المائلة الخلفية المختلفتين. في \. هي تُزيل المعنى الخاص من metacharacter. وفي \n و\d هي تُضيف معنى خاصاً إلى حرف عادي. نفس الرمز، واتجاهان متعاكسان.
An unescaped . matches almost anything. If you write a pattern for a filename as file.txt it will happily match fileXtxt and file9txt. You wanted file\.txt.
النقطة . غير المهروبة تطابق كل شيء تقريباً. فإذا كتبت نمط اسم ملف هكذا file.txt فسيطابق fileXtxt وfile9txt بلا مشكلة. والذي أردته هو file\.txt.
The related half of this trap, stated explicitly on slide 62: . does not match a newline by default in many regex flavors. So a.*b will not reach across a line break unless you turn on the dot-all flag. People write a .* expecting it to swallow a multi-line block and then spend an hour confused.
والنصف الآخر من الفخ، وتنص عليه الشريحة 62 صراحة: . لا يطابق سطراً جديداً تلقائياً في كثير من إصدارات regex. فـ a.*b لن يعبر فاصل الأسطر إلا إذا شغّلت علم dot-all. الناس يكتبون .* متوقعين أن يبتلع كتلة متعددة الأسطر، ثم يقضون ساعة في حيرة.
Precedence: why alternation needs parentheses, slide 67
الأسبقية: لماذا يحتاج الاختيار (alternation) إلى أقواس، الشريحة 67
This is the trap that catches the most people, because the broken pattern looks correct.
هذا هو الفخ الذي يسقط فيه أكبر عدد، لأن النمط الخاطئ يبدو صحيحاً.
Slide 67's rule: alternation | has low precedence. Lower than concatenation, lower than any quantifier. So the bar splits the largest possible expressions around it.
قاعدة الشريحة 67: الـ alternation أي | له أسبقية منخفضة. أقل من concatenation، وأقل من أي quantifier. فالشرطة العمودية تقسم أكبر تعبيرين ممكنين حولها.
you write cat|dogs
regex reads (cat) | (dogs)
you probably cat, or dog, followed by an optional s
wanted
correct (cat|dog)s -> "cats" or "dogs"
cats|dogs -> also fine, just verbose
يقارن المربع ما تكتبه بما يقرأه المحرك. تكتب cat|dogs، فيقرأه regex هكذا (cat) | (dogs)، أما ما أردته فهو cat أو dog متبوعة بـ s اختيارية. والصحيح (cat|dog)s ليطابق cats أو dogs، أو cats|dogs وهو سليم أيضاً لكنه أطول.
The same low precedence makes anchors behave surprisingly:
ونفس الأسبقية المنخفضة تجعل الـ anchors تتصرف بشكل مفاجئ:
you write ^cat|dog$ regex reads (^cat) | (dog$) means "starts with cat" OR "ends with dog" you wanted ^(cat|dog)$ "the whole line is cat or dog"
تكتب ^cat|dog$، فيقرأه المحرك (^cat) | (dog$)، أي "يبدأ بـ cat" أو "ينتهي بـ dog". وما أردته هو ^(cat|dog)$، أي "السطر كله cat أو dog".
Slide 67 also introduces the non-capturing group (?:...). It groups for precedence purposes but does not store what it matched, so it does not consume a capture-group number. Use it whenever you need parentheses purely for scope.
وتقدّم الشريحة 67 أيضاً الـ non-capturing group وصيغته (?:...). يجمّع لأجل الأسبقية لكنه لا يحفظ ما طابقه، فلا يستهلك رقم capture group. استخدمه كلما احتجت الأقواس للنطاق فقط.
A quantifier applies only to the single element immediately before it, not to the whole run of characters you just typed.
الـ quantifier يُطبَّق على العنصر الواحد الذي يسبقه مباشرة، لا على كل سلسلة الحروف التي كتبتها قبله.
ab+ means a, then one or more b -> 'ab', 'abbb'
NOT one or more 'ab'
(ab)+ means one or more 'ab' -> 'ab', 'ababab'
\d{3} means exactly three digits
a\d{3} means 'a' then exactly three digits, not 'a\d' three times
يعرض المربع أربع حالات. ab+ يعني a ثم b واحدة أو أكثر، فيطابق ab وabbb، ولا يعني ab واحدة أو أكثر. أما (ab)+ فيعني ab واحدة أو أكثر، فيطابق ab وababab. و\d{3} يعني ثلاثة أرقام بالضبط. وa\d{3} يعني a ثم ثلاثة أرقام بالضبط، لا a\d ثلاث مرات.
Same story with escapes: in \d{4} the "single element" is the whole alias \d, since the backslash and the letter are one unit. But in abc* the single element is just c.
ونفس القصة مع الـ escapes: في \d{4} يكون "العنصر الواحد" هو الاختصار كله \d، لأن الشرطة المائلة والحرف وحدة واحدة. لكن في abc* يكون العنصر الواحد هو c وحدها.
Composing a real pattern, slide 68
تركيب نمط حقيقي، الشريحة 68
Slide 68's advice is process advice: design patterns by composing smaller parts, then build and test iteratively, and avoid overfitting to the examples in front of you.
نصيحة الشريحة 68 نصيحة منهجية: صمّم الأنماط بتركيب أجزاء أصغر، ثم ابنِ واختبر على دفعات، وتجنّب الإفراط في ملاءمة الأمثلة التي بين يديك.
Its example is a deliberately simplified email matcher:
ومثالها مُطابق بريد إلكتروني مبسّط بشكل متعمد:
يعرض الصندوق النمط [A-Za-z0-9._]+@[A-Za-z0-9._]+: صنف حروف الجزء المحلي مع +، ثم علامة @ المظلّلة كحرف حرفي، ثم نفس الصنف مع + مرة أخرى.
Read it in three chunks: one or more characters from the local-part alphabet, then a literal at-sign, then one or more characters from the same alphabet. The + on both sides is doing the real work, because it is what forces at least one character on each side of the @.
اقرأه في ثلاث كتل: حرف أو أكثر من أبجدية الجزء المحلي، ثم علامة @ حرفية، ثم حرف أو أكثر من الأبجدية نفسها. والـ + على الجانبين هو الذي يؤدي العمل الحقيقي، لأنه ما يفرض وجود حرف واحد على الأقل على كل جانب من @.
Run the slide's email pattern on real text and watch it half-fail. This is what "avoid overfitting" means in practice.
شغّل نمط البريد الذي في الشريحة على نص حقيقي وراقبه يفشل نصف فشل. هذا هو معنى "تجنّب الإفراط في الملاءمة" عملياً.
pattern: [A-Za-z0-9._]+@[A-Za-z0-9._]+
input: Contact majid.raimi@kfupm.edu.sa or ali-k@x.com today
MATCH 1
scan from the left. At 'm' of "majid":
[A-Za-z0-9._]+ consumes majid.raimi (the dot IS in the class)
@ consumes @
[A-Za-z0-9._]+ consumes kfupm.edu.sa (stops at the space)
result: majid.raimi@kfupm.edu.sa CORRECT
MATCH 2
At 'a' of "ali-k":
[A-Za-z0-9._]+ consumes ali (stops: '-' is NOT in the class)
@ needs '@' but sees '-' -> fail, advance start
At 'l': same failure. At 'i': same failure.
At '-': '-' is not in the class at all -> fail immediately
At 'k':
[A-Za-z0-9._]+ consumes k
@ consumes @
[A-Za-z0-9._]+ consumes x.com
result: k@x.com TRUNCATED, lost "ali-"
FIX: add the hyphen to the class, at the END so it is not read as a range
[A-Za-z0-9._-]+@[A-Za-z0-9._-]+
now match 2 is ali-k@x.com
يشغّل المربع النمط [A-Za-z0-9._]+@[A-Za-z0-9._]+ على الدخل Contact majid.raimi@kfupm.edu.sa or ali-k@x.com today. المطابقة الأولى تنجح تماماً وتعطي majid.raimi@kfupm.edu.sa، لأن النقطة داخل الصنف. والمطابقة الثانية تفشل عند ali لأن الشرطة - ليست في الصنف، فيزحف المحرك ببداية البحث إلى الأمام حتى يبدأ عند k، فيعطي k@x.com مبتورة وقد ضاعت ali-. والحل: أضف الشرطة إلى الصنف في آخره حتى لا تُقرأ كمدى، فيصبح [A-Za-z0-9._-]+@[A-Za-z0-9._-]+، وتصبح المطابقة الثانية ali-k@x.com.
Two lessons here. First, the engine never gives up on the whole string after a failure; it slides the start position forward and tries again, which is exactly how you get a silently truncated match instead of a clean no-match. Second, a hyphen inside [ ] must go first or last, otherwise it is read as a range.
درسان هنا. الأول أن المحرك لا يستسلم عن السلسلة كلها بعد الفشل، بل يزحف بموضع البداية إلى الأمام ويحاول مرة أخرى، وهذا بالضبط كيف تحصل على مطابقة مبتورة بصمت بدل عدم مطابقة نظيف. والثاني أن الشرطة - داخل [ ] يجب أن تكون أولاً أو آخراً، وإلا قُرئت كمدى.
Substitution and capture groups, slide 69
الاستبدال ومجموعات الالتقاط (capture groups)، الشريحة 69
So far we have been finding text. Substitution is finding text and then rewriting it, which is where the parentheses stop being about precedence and start being about memory.
كنا حتى الآن نبحث عن نص. أما الـ substitution فهو أن تجد النص ثم تعيد كتابته، وهنا تتحول الأقواس من أداة أسبقية إلى أداة ذاكرة.
A capture group stores whatever it matched, numbered left to right by opening parenthesis, and you refer back to it in the replacement.
الـ capture group يحفظ ما طابقه، وتُرقّم الـ groups من اليسار إلى اليمين بحسب قوس الفتح، ثم تشير إليها في نص الاستبدال.
Slide 69's example reorders a date:
مثال الشريحة 69 يعيد ترتيب تاريخ:
يعرض الصندوق الاستبدال s/(\d{4})-(\d{2})-(\d{2})/$2$3$1/: الجزء المظلّل هو النمط بثلاث groups، أربعة أرقام ثم رقمان ثم رقمان، تفصل بينها شرطتان حرفيتان، والجزء الأخير هو الاستبدال الذي يعيد ترتيبها.
يفكّك الرسم نمط التاريخ إلى ثلاث capture groups ملوّنة: (\d{4}) هي group 1 أي السنة وتُحفظ في $1، و(\d{2}) الأولى هي group 2 وتُحفظ في $2، و(\d{2}) الثانية هي group 3 وتُحفظ في $3، والشرطتان المجرّدتان حرفيتان ولا تُحفظان. وفي الأسفل يأخذ الرسم الدخل 2026-25-01 وتتقاطع الأسهم لتعطي الخرج 25/01/2026 حسب الاستبدال $2/$3/$1.
The cheat sheet on slide 73 adds two more group flavors. All three are worth knowing apart:
وتضيف ورقة المراجعة في الشريحة 73 نوعين آخرين من الـ groups. والثلاثة كلها تستحق أن تفرّق بينها:
| Syntaxالصياغة | Groups for precedence?تجمّع للأسبقية؟ | Stores the match?تخزّن التطابق؟ | Use whenاستخدمها عندما |
|---|---|---|---|
(...) | yesنعم | yes, as $1, $2, ...نعم، في صورة $1, $2, ... | you need the text backتحتاج النص مرة أخرى |
(?:...) | yesنعم | noلا | you only need the parentheses for scopeتحتاج الأقواس للنطاق فقط |
(?<name>...) | yesنعم | yes, by nameنعم، بالاسم | many groups and numbers get confusingالمجموعات كثيرة والأرقام تصبح مربكة |
يقارن الجدول ثلاث صيغ. (...) يجمّع للأسبقية ويحفظ ما طابقه كـ $1 و$2، فاستخدمه حين تحتاج النص مرة أخرى. و(?:...) يجمّع ولا يحفظ، فاستخدمه حين تحتاج الأقواس للنطاق فقط. و(?<name>...) يجمّع ويحفظ بالاسم، فاستخدمه حين تكثر الـ groups وتختلط الأرقام.
Lookaround, slide 70 and the cheat sheet
النظر حول الموضع (Lookaround)، الشريحة 70 وورقة المراجعة
A lookahead is a condition about what comes next that does not become part of the match. That last clause is the whole point and the whole exam question.
الـ lookahead شرط على ما يأتي بعدك لا يصبح جزءاً من المطابقة. وهذه الجملة الأخيرة هي كل المغزى وكل سؤال الامتحان.
| Syntaxالصياغة | Nameالاسم | Meansالمعنى | Example from slide 74مثال من الشريحة 74 |
|---|---|---|---|
(?=...) | positive lookaheadنظر أمامي موجب (positive lookahead) | require this pattern to followاشترط أن يلي هذا النمط | foo(?=bar) matches foo only if bar followsfoo(?=bar) يطابق foo فقط إذا تلاه bar |
(?!...) | negative lookaheadنظر أمامي سالب (negative lookahead) | forbid this pattern from followingامنع أن يلي هذا النمط | foo(?!bar) matches foo only if bar does NOT followfoo(?!bar) يطابق foo فقط إذا لم يتله bar |
(?<=...) | positive lookbehindنظر خلفي موجب (positive lookbehind) | require this pattern to precedeاشترط أن يسبق هذا النمط | (?<=#)\d+ matches digits preceded by #(?<=#)\d+ يطابق أرقاماً يسبقها # |
(?<!...) | negative lookbehindنظر خلفي سالب (negative lookbehind) | forbid this pattern from precedingامنع أن يسبق هذا النمط |
يسرد الجدول أربع صيغ lookaround. (?=...) هو positive lookahead ويشترط أن يتبع هذا النمط، فـ foo(?=bar) يطابق foo فقط إذا تبعتها bar. و(?!...) هو negative lookahead ويمنع أن يتبع، فـ foo(?!bar) يطابق foo فقط إذا لم تتبعها bar. و(?<=...) هو positive lookbehind ويشترط أن يسبق، فـ (?<=#)\d+ يطابق أرقاماً تسبقها #. و(?<!...) هو negative lookbehind ويمنع أن يسبق.
Slide 70 says lookahead is useful when you need overlapping constraints without consuming text. Make that concrete:
تقول الشريحة 70 إن الـ lookahead مفيد حين تحتاج قيوداً متقاطعة دون استهلاك نص. وهذا تجسيدها:
input: foobar foobaz
foo(?=bar) matches foo (the one in foobar)
the matched text is "foo", 3 characters.
"bar" was checked but NOT consumed, so the next
scan resumes at the 'b'.
foo(bar) matches foobar (6 characters), with "bar" in $1.
the match itself is longer.
foo(?!bar) matches foo (the one in foobaz)
على الدخل foobar foobaz: النمط foo(?=bar) يطابق foo التي في foobar، والنص المطابَق هو foo بثلاثة أحرف فقط، لأن bar فُحصت ولم تُستهلك، فيُستأنف المسح من b. أما foo(bar) فيطابق foobar بستة أحرف وbar محفوظة في $1، أي أن المطابقة نفسها أطول. والنمط foo(?!bar) يطابق foo التي في foobaz.
Lookahead versus capture group, the real difference
النظر الأمامي مقابل مجموعة الالتقاط، الفرق الحقيقي
Both use parentheses. Both let you talk about a sub-pattern. They differ on one axis only:
كلاهما يستخدم الأقواس، وكلاهما يتيح لك الحديث عن نمط فرعي. والفرق بينهما على محور واحد فقط:
Capture group (bar)مجموعة الالتقاط (bar) | Lookahead (?=bar)النظر الأمامي (?=bar) | |
|---|---|---|
| Is "bar" part of the match?هل "bar" جزء من التطابق؟ | yes, it extends the matched spanنعم، يمدّ المقطع المطابَق | no, the span stops before itلا، المقطع يتوقف قبله |
| Can you retrieve it later?هل يمكنك استرجاعه لاحقاً؟ | yes, as $1نعم، في صورة $1 | noلا |
| Does the scan position advance past it?هل يتقدم موضع المسح بعده؟ | yesنعم | no, which is why overlapping matches workلا، ولهذا تنجح التطابقات المتداخلة |
| Typical useالاستخدام المعتاد | extract and rewriteالاستخراج وإعادة الكتابة | filter without changing what you grabالتصفية دون تغيير ما تلتقطه |
يقارن الجدول capture group أي (bar) بـ lookahead أي (?=bar) في أربعة أسطر. هل bar جزء من المطابقة؟ نعم في الأول فيمتد المدى، ولا في الثاني فيتوقف المدى قبلها. هل يمكن استرجاعها لاحقاً؟ نعم كـ $1 في الأول، ولا في الثاني. هل يتقدم موضع المسح بعدها؟ نعم في الأول، ولا في الثاني، ولهذا تعمل المطابقات المتقاطعة. والاستخدام المعتاد: الأول للاستخراج وإعادة الكتابة، والثاني للتصفية دون تغيير ما تلتقطه.
One warning the slide gives directly: lookbehind support varies. Some engines restrict variable-length lookbehind, so (?<=ab+) may be rejected where (?<=ab) is fine.
وتحذير تقوله الشريحة مباشرة: دعم lookbehind يتفاوت. بعض المحركات تمنع lookbehind المتغيّر الطول، فقد يُرفض (?<=ab+) في محرك يقبل (?<=ab) بلا مشكلة.
The flags, slide 74
الأعلام (Flags)، الشريحة 74
| Flagالعلَم | Nameالاسم | Effectالأثر |
|---|---|---|
g | globalشامل (global) | keep going after the first match instead of stoppingالاستمرار بعد أول تطابق بدلاً من التوقف |
m | multilineمتعدد الأسطر (multiline) | ^ and $ match at every line break, not just the string ends^ و$ تطابقان عند كل فاصل سطر، لا عند طرفي السلسلة فقط |
i | case-insensitiveغير حساس لحالة الأحرف (case-insensitive) | ignore letter case throughoutتجاهل حالة الأحرف في كل مكان |
يسرد جدول الشريحة 74 ثلاثة flags. العلم g هو global: استمر بعد أول مطابقة بدل التوقف. والعلم m هو multiline: يجعل ^ و$ يطابقان عند كل فاصل أسطر، لا عند طرفي السلسلة فقط. والعلم i هو case-insensitive: تجاهل حالة الحروف في النمط كله.
Slide 71's practice list is built to exercise exactly these. The group Apple Juice / apPLe Juice / Apple juice / APPLE Juice is a case-insensitivity exercise: apple juice with the i flag matches all four, while [aA]pple [jJ]uice matches only two of them. And the repeated sentence "The faster they ran, the faster we ran" is a backreference exercise: (\w+)\b.*\b\1 finds a word that occurs twice.
قائمة التدريب في الشريحة 71 مبنية لتمرين هذه الأعلام بالذات. فمجموعة Apple Juice وapPLe Juice وApple juice وAPPLE Juice تمرين على تجاهل الحالة: النمط apple juice مع علم i يطابق الأربعة كلها، بينما [aA]pple [jJ]uice يطابق اثنين منها فقط. والجملة المكرّرة The faster they ran, the faster we ran تمرين على backreference: النمط (\w+)\b.*\b\1 يجد كلمة تظهر مرتين.
Back to tokenization: the GPT-2 pre-tokenizer, slides 75 and 76
العودة إلى التقطيع: المُقطِّع المسبق في GPT-2، الشريحتان 75 و76
This is where the regex block connects to Tokenization and BPE, the previous page. Go back to it if the word "merge" is fuzzy, because the two halves only make sense together.
هنا يتصل قسم regex بصفحة Tokenization and BPE السابقة. ارجع إليها إذا كانت كلمة merge غير واضحة، لأن النصفين لا يكتمل معناهما إلا معاً.
BPE learns merges over characters inside a word. But something has to decide what a "word" is before BPE ever runs, so that merges never accidentally span a space or glue punctuation to a noun. That first split is called pre-tokenization, and in GPT-2 it is one regular expression.
يتعلّم BPE الـ merges على الحروف داخل الكلمة. لكن شيئاً ما يجب أن يقرر ما هي "الكلمة" قبل أن يعمل BPE أصلاً، حتى لا تعبر الـ merges مسافة أو تلصق ترقيماً باسم. وهذا التقسيم الأول اسمه pre-tokenization، وهو في GPT-2 تعبير نمطي واحد.
Slide 76 names what the pattern is separating:
تسمّي الشريحة 76 ما يفصله النمط:
Slide 75 shows the actual pattern, which is a long alternation. Read it as five alternatives separated by bars, because that is all it is:
تعرض الشريحة 75 النمط الفعلي، وهو alternation طويل. اقرأه كخمسة بدائل تفصلها شرطات عمودية، فهذا كل ما هو عليه:
pat = re.compile(
r"'s|'t|'re|'ve|'m|'ll|'d" contractions as their own tokens
r"| ?\p{L}+" optional space, then Unicode letters
r"| ?\p{N}+" optional space, then Unicode digits
r"| ?[^\s\p{L}\p{N}]+" optional space, then punctuation
r"|\s+(?!\S)|\s+" leftover whitespace
)
text = "We're 350 dogs! Um, lunch?"
pat.findall(text)
['We', "'re", ' 350', ' dogs', '!', ' Um', ',', ' lunch', '?']
يبيّن المربع النمط في خمسة أسطر، كل سطر بديل واحد: 's|'t|'re|'ve|'m|'ll|'d للـ contractions كرموز مستقلة، ثم ?\p{L}+ مسافة اختيارية ثم حروف Unicode، ثم ?\p{N}+ مسافة اختيارية ثم أرقام Unicode، ثم ?[^\s\p{L}\p{N}]+ مسافة اختيارية ثم ترقيم، ثم \s+(?!\S)|\s+ لبقية المسافات. وعلى الدخل We're 350 dogs! Um, lunch? يعطي الخرج ['We', "'re", ' 350', ' dogs', '!', ' Um', ',', ' lunch', '?'].
Three details in that output are worth noticing, because they explain design choices you saw on the BPE page:
ثلاث تفاصيل في هذا الخرج تستحق الانتباه، لأنها تفسّر قرارات تصميم رأيتها في صفحة BPE:
- The leading space is kept inside the token.
' 350',' dogs',' lunch'. The?in?\p{L}+is what allows that optional space. This is why GPT-2 vocabularies contain bothdogsanddogsas different tokens, and it is how the tokenizer can reconstruct the original string exactly.المسافة السابقة تُحفظ داخل الرمز. لاحظ ' 350' و' dogs' و' lunch'. والـ ? في ?\p{L}+ هو ما يسمح بتلك المسافة الاختيارية. ولهذا تحتوي معاجم GPT-2 على dogs و dogs كرمزين مختلفين، وبهذا يستطيع الـ tokenizer أن يعيد بناء النص الأصلي حرفياً.
- Contractions come first in the alternation, so
We'resplits intoWeand'rerather than being broken up arbitrarily. Order matters in an alternation: the engine takes the first alternative that works.الـ contractions تأتي أولاً في الـ alternation، فتنقسم We're إلى We و're بدل أن تُقطّع عشوائياً. والترتيب مهم في الـ alternation: المحرك يأخذ أول بديل ينجح.
\p{L}and\p{N}are Unicode property classes, not[a-z]and[0-9]. That is the "be explicit about Unicode behavior" tip from slide 77, applied. A pattern using[a-zA-Z]would silently destroy Arabic, Cyrillic, and Chinese text.\p{L} و\p{N} هما Unicode property classes، وليسا [a-z] و[0-9]. وهذا تطبيق لنصيحة "كن صريحاً في سلوك Unicode" من الشريحة 77. فأي نمط يستخدم [a-zA-Z] سيدمّر بصمت النصوص العربية والسيريلية والصينية.
Slide 76 closes with the caveat that BPE merges then operate within or across these pieces depending on design. Most implementations, GPT-2 included, forbid merges across pre-token boundaries, which is precisely what stops the model from learning a single token for dogs!.
تنتهي الشريحة 76 بالتحذير أن الـ merges في BPE تعمل بعد ذلك داخل هذه القطع أو عبرها بحسب التصميم. ومعظم التطبيقات، ومنها GPT-2، تمنع الـ merges عبر حدود الـ pre-tokens، وهذا بالضبط ما يمنع النموذج من تعلّم رمز واحد لـ dogs!.
Engineering tips, slide 77
نصائح هندسية، الشريحة 77
- Start simple, add cases from error analysis. Do not try to write the perfect pattern first. Write the 80 percent version, run it, look at what it got wrong, extend.
ابدأ بسيطاً وأضف الحالات من تحليل الأخطاء. لا تحاول كتابة النمط المثالي من أول مرة. اكتب نسخة تغطي 80 بالمئة، شغّلها، انظر فيما أخطأت، ثم وسّعها.
- Unit-test patterns on representative text. The slide's own practice link is
regex101.com, which shows you the match and the step count live.اختبر الأنماط على نص تمثيلي. ورابط التدريب الذي تقترحه الشريحة هو regex101.com، ويعرض لك المطابقة وعدد الخطوات مباشرة.
- Be explicit about Unicode. Use
\p{L}style classes and normalize your text first.كن صريحاً في أمر Unicode. استخدم أصنافاً من نوع \p{L} وطبّق normalization على نصك أولاً.
- Avoid catastrophic backtracking. Nested quantifiers like
(a+)+bcan take exponential time on a non-matching input, because the engine tries every way of splitting the a's. Replace greedy dots with negated classes, as in pattern C of the worked example above.تجنّب catastrophic backtracking. الـ quantifiers المتداخلة مثل (a+)+b قد تستهلك وقتاً أسّياً على دخل غير مطابق، لأن المحرك يجرّب كل طرق تقسيم حروف a. استبدل النقاط الطمّاعة بأصناف منفية، كما في النمط C في المثال المحلول أعلاه.
Thirty-second recap
خلاصة في ثلاثين ثانية
Check yourself
اختبر نفسك
What does the pattern [e^] match?
ماذا يطابق النمط [e^]؟
Slide 60 lists this case explicitly. The caret negates only when it is the first character inside the brackets. Here it is second, so it is a literal caret, and the class is a plain disjunction of 'e' and '^'. A is the tempting answer because it assumes the caret always negates; that reading would require [^e]. B confuses the in-bracket caret with the line anchor, which only happens outside brackets.
الجواب C. تسرد الشريحة 60 هذه الحالة صراحة. الـ ^ ينفي فقط إذا كان الحرف الأول داخل الأقواس المربعة. وهنا هو الثاني، فيكون ^ حرفياً، والصنف مجرد disjunction بين e و^. والجواب A مُغرٍ لأنه يفترض أن ^ ينفي دائماً، وتلك القراءة تحتاج [^e]. والجواب B يخلط بين ^ داخل الأقواس وline anchor، وهذا لا يحدث إلا خارج الأقواس.
Applied to the string <Begin> Hello World <\End>, how many characters does <.*> match?
إذا طُبّق النمط <.*> على السلسلة <Begin> Hello World <\End>، فكم حرفاً يطابق؟
.* is greedy. It consumes to the end of the string, then backtracks just far enough for the final > to match, which lands on the very last character. So the match spans the entire 26-character string. A is the answer for the lazy version <.*?>, which is exactly why the greedy/lazy distinction is worth memorizing. C is wrong because . matches any character except a newline, and a space is not a newline.
الجواب B. الـ .* طمّاع (greedy)، فيستهلك حتى نهاية السلسلة، ثم يتراجع بالقدر الكافي فقط ليطابق > الأخيرة، فتهبط على آخر حرف تماماً. فتمتد المطابقة على السلسلة كلها بطول 26 حرفاً. والجواب A هو جواب النسخة الكسولة <.*?>، ولهذا بالضبط يستحق الفرق بين greedy وlazy الحفظ. والجواب C خاطئ لأن . يطابق أي حرف إلا السطر الجديد، والمسافة ليست سطراً جديداً.
A student writes ^cat|dog$ intending "the whole line is either cat or dog". What does the regex actually mean?
طالب يكتب ^cat|dog$ قاصداً "السطر كله إما cat وإما dog". فماذا يعني هذا الـ regex فعلياً؟
Alternation has the lowest precedence of any regex operator, so the bar splits the largest expressions around it: (^cat) or (dog$). The line "catalogue" matches, and so does "hotdog". The fix is ^(cat|dog)$. B is tempting because it treats the two anchors as applying to one single alternative, but the bar separates them into two independent patterns.
الجواب D. الـ alternation أدنى أسبقية من أي عامل في regex، فالشرطة العمودية تقسم أكبر تعبيرين حولها: (^cat) أو (dog$). فيطابق السطر catalogue، ويطابق hotdog أيضاً. والإصلاح هو ^(cat|dog)$. والجواب B مُغرٍ لأنه يتعامل مع الـ anchors كأنهما على بديل واحد، لكن الشرطة العمودية تفصلهما إلى نمطين مستقلين.
Which pattern matches both "ab" and "ababab", but not "abbb"?
أي نمط يطابق ab وababab معاً، ولا يطابق abbb؟
A quantifier binds only to the single element in front of it. The parentheses in (ab)+ make the whole two-character sequence that element, so it repeats as a unit. B is the classic error: in ab+ the + attaches to b alone, so it matches "abbb" and never matches "ababab". D is wrong in the other direction, since a character class matches any mixture of a's and b's including "abbb".
الجواب A. الـ quantifier يرتبط بالعنصر الواحد الذي أمامه فقط. والأقواس في (ab)+ تجعل التسلسل ذا الحرفين كله هو ذلك العنصر، فيتكرر كوحدة. والجواب B هو الخطأ الكلاسيكي: في ab+ ترتبط + بـ b وحدها، فيطابق abbb ولا يطابق ababab أبداً. والجواب D خاطئ في الاتجاه الآخر، لأن صنف الحروف يطابق أي خليط من a وb بما فيه abbb.
For the substitution s/(\d{4})-(\d{2})-(\d{2})/$2$3$1/ applied to 2026-25-01, what is in $2?
في الاستبدال s/(\d{4})-(\d{2})-(\d{2})/$2$3$1/ المطبّق على 2026-25-01، ما الذي يوجد في $2؟
Groups are numbered by the order of their opening parenthesis, left to right, and only parenthesized parts are stored. $1 = 2026, $2 = 25, $3 = 01. B is the useful wrong answer to rule out: the hyphens are bare literals outside any parentheses, so they are matched and then discarded, which is exactly why the replacement can swap the separator to a slash.
الجواب C. تُرقّم الـ groups بترتيب قوس الفتح من اليسار إلى اليمين، ولا يُحفظ إلا ما بين الأقواس. فـ $1 = 2026 و$2 = 25 و$3 = 01. والجواب B هو الخطأ المفيد لاستبعاده: الشرطتان حرفيتان مجرّدتان خارج أي أقواس، فتُطابقان ثم تُلقى بهما، وهذا بالضبط سبب قدرة الاستبدال على تغيير الفاصل إلى شرطة مائلة.
Applied to "foobar", what text does foo(?=bar) actually return as the match?
إذا طُبّق foo(?=bar) على foobar، فما النص الذي يعيده كمطابقة فعلياً؟
A lookahead is a zero-width assertion. It checks that "bar" follows, but does not consume it, so the returned match is just "foo". A is the answer for foo(bar) or plain foobar, where the text becomes part of the span. This non-consuming property is exactly what slide 70 means by "overlapping constraints without consuming text".
الجواب B. الـ lookahead تأكيد بعرض صفر. يتحقق أن bar تتبع، لكنه لا يستهلكها، فالمطابقة المُعادة هي foo فقط. والجواب A هو جواب foo(bar) أو foobar المجرد، حيث يصبح النص جزءاً من المدى. وخاصية عدم الاستهلاك هذه هي بالضبط ما تقصده الشريحة 70 بعبارة overlapping constraints without consuming text.
Why does the GPT-2 pre-tokenizer regex use \p{L} instead of [a-zA-Z]?
لماذا يستخدم regex الخاص بـ GPT-2 pre-tokenizer النمط \p{L} بدل [a-zA-Z]؟
Slide 77 states the principle ("be explicit about Unicode behavior, \p{L} classes, normalization") and slide 75 shows it applied. A Latin-only class would push every non-Latin character into the punctuation branch of the alternation and shred multilingual text. C is nonsense: greediness is a property of quantifiers, not of character classes.
الجواب D. تنص الشريحة 77 على المبدأ، أي الصراحة في سلوك Unicode وأصناف \p{L} والـ normalization، وتعرض الشريحة 75 تطبيقه. فأي صنف لاتيني فقط سيدفع كل حرف غير لاتيني إلى فرع الترقيم في الـ alternation ويمزّق النص متعدد اللغات. والجواب C كلام فارغ: الطمع (greediness) خاصية الـ quantifiers، لا أصناف الحروف.