Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sticky flag "y", searching at position #201

Merged
merged 1 commit into from
Mar 11, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 49 additions & 49 deletions 9-regular-expressions/16-regexp-sticky/article.md
Original file line number Diff line number Diff line change
@@ -1,55 +1,55 @@

# Sticky flag "y", searching at position
# স্টিকি ফ্ল্যাগ "y", নির্দিষ্ট (পজিশনে) অবস্থানে অনুসন্ধান

The flag `pattern:y` allows to perform the search at the given position in the source string.
`pattern:y` ফ্ল্যাগটির মাধ্যমে আমরা প্রদত্ত স্ট্রিংয়ের কোন একটা নির্দিষ্ট পজিশনে অনুসন্ধান চালাতে পারি।

To grasp the use case of `pattern:y` flag, and better understand the ways of regexps, let's explore a practical example.
`pattern:y` ফ্ল্যাগ এর ব্যবহারিক প্রয়োগ, এবং রেগুলার এক্সপ্রেশনকে আরো ভালোভাবে বুঝতে, চলুন একটি উদাহরণ দেখি।

One of common tasks for regexps is "lexical analysis": we get a text, e.g. in a programming language, and need to find its structural elements. For instance, HTML has tags and attributes, JavaScript code has functions, variables, and so on.
রেগুলার এক্সপ্রেশনের একটি সাধারণ কাজ হল "(লেক্সিকাল অ্যানালাইসিস) lexical analysis": কোন একটি টেক্সটে প্রোগ্রামিং ভাষাগুলোর এলিমেন্টের স্ট্রাকচার খুঁজা। যেমন, HTML এর আছে ট্যাগ অ্যাট্রিবিউট, জাভাস্ক্রিপ্টে আছে ফাংশন, ভ্যারিয়েবল ইত্যাদি।

Writing lexical analyzers is a special area, with its own tools and algorithms, so we don't go deep in there, but there's a common task: to read something at the given position.
লেক্সিকাল অ্যানালাইজার হল একটি বিশেষ টার্ম এবং এর নিজস্ব টুল এবং অ্যালগরিদম আছে, তবে এখানে আমাদের এই ব্যাপারটির বিস্তারিত জানা লাগবে না এবং এর একটি কমন কাজ হল: কোন একটি নির্দিষ্ট অবস্থানে কোন কিছু পড়া।

E.g. we have a code string `subject:let varName = "value"`, and we need to read the variable name from it, that starts at position `4`.
যেমন আমাদের একটি স্ট্রিং আছে `subject:let varName = "value"`, এখানে আমাদের ভ্যারিয়েবলের নামটি লাগবে, যা শুরু হয় `4` নাম্বার ইনডেক্স থেকে।

We'll look for variable name using regexp `pattern:\w+`. Actually, JavaScript variable names need a bit more complex regexp for accurate matching, but here it doesn't matter.
আমরা রেগুলার এক্সপ্রেশনে `pattern:\w+` এর মাধ্যমে ভ্যারিয়েবলটি খুঁজতে পারি। প্রকৃতপক্ষে, ভ্যালিড জাভাস্ক্রিপ্ট ভ্যারিয়েবলের নাম যাচাইকরণের জন্য আমাদের আরো জটিল রেগুলার এক্সপ্রেশন লাগে, তবে এখানে আমরা ব্যাপারটি বুঝতে সাধারণ একটি প্যাটার্ন ব্যবহার করছি।

- A call to `str.match(/\w+/)` will find only the first word in the line (`var`). That's not it.
- We can add the flag `pattern:g`. But then the call `str.match(/\w+/g)` will look for all words in the text, while we need one word at position `4`. Again, not what we need.
- `str.match(/\w+/)` এটি এক্সিকিউট হলে আমরা শুধু প্রথম শব্দটি পাব (`var`)। কিন্তু আমরা এটি চাই না।
- আমরা `pattern:g` ফ্ল্যাগটি ব্যবহার করতে পারি। কিন্তু `str.match(/\w+/g)` এটি টেক্সটের সকল শব্দ অনুসন্ধান করে, যেখানে আমাদের শুধু একটি শব্দ চাই যার অবস্থান শুরু `4` হতে। কিন্তু, এটি দ্বারাও আমাদের সমস্যার সমাধান হবে না।

**So, how to search for a regexp exactly at the given position?**
**সুতরাং, আমরা রেগুলার এক্সপ্রেশনের মাধ্যমে কিভাবে কোন একটি নির্দিষ্ট অবস্থানে অনুসন্ধান করতে পারি?**

Let's try using method `regexp.exec(str)`.
চলুন এই `regexp.exec(str)` মেথডটি দেখি।

For a `regexp` without flags `pattern:g` and `pattern:y`, this method looks only for the first match, it works exactly like `str.match(regexp)`.
`regexp` `pattern:g` এবং `pattern:y` ফ্ল্যাগ ছাড়া মেথডটি প্রথম মিলটি দেখায়, এটি অনেকটা `str.match(regexp)` এর মত কাজ করে।

...But if there's flag `pattern:g`, then it performs the search in `str`, starting from position stored in the `regexp.lastIndex` property. And, if it finds a match, then sets `regexp.lastIndex` to the index immediately after the match.
...কিন্তু যদি `pattern:g` ফ্ল্যাগটি থাকে, তাহলে `str``regexp.lastIndex` প্রপার্টির অবস্থান হতে অনুসন্ধানটি চালায়। এবং এটি যদি কোন মিল খুঁজে পায়, তাহলে মিলের পরবর্তী অবস্থানটি `regexp.lastIndex` এ সেট হয়।

In other words, `regexp.lastIndex` serves as a starting point for the search, that each `regexp.exec(str)` call resets to the new value ("after the last match"). That's only if there's `pattern:g` flag, of course.
অন্যকথায় বলা যায়, `regexp.lastIndex` স্ট্রিংয়ের শুরু হতে অনুসন্ধানটি চালায়, তারপর কোন একটি অবস্থান মিল পেলে `regexp.exec(str)` এর পরের অবস্থানটি `regexp.lastIndex` এ নতুন ভ্যালু হিসেবে সেট হয় আর না পেলে এটি রিসেট হয়ে যায়। তবে এই ব্যাপারটি ঘটে যদি শুধুমাত্র `pattern:g` ব্যবহার করি।

So, successive calls to `regexp.exec(str)` return matches one after another.
সুতরাং, `regexp.exec(str)` একটির পর আরেকটি মিল রিটার্ন করতে থাকে।

Here's an example of such calls:
আসুন উদাহরণের মাধ্যমে আরো বিস্তারিত দেখি:

```js run
let str = 'let varName'; // Let's find all words in this string
let str = 'let varName'; // স্ট্রিংয়ের প্রতিটি শব্দ খুঁজি
let regexp = /\w+/g;

alert(regexp.lastIndex); // 0 (initially lastIndex=0)
alert(regexp.lastIndex); // 0 (ডিফল্ট lastIndex=0)

let word1 = regexp.exec(str);
alert(word1[0]); // let (1st word)
alert(regexp.lastIndex); // 3 (position after the match)
alert(word1[0]); // let (১ম শব্দটি)
alert(regexp.lastIndex); // 3 (মিলের পর নতুন অবস্থান)

let word2 = regexp.exec(str);
alert(word2[0]); // varName (2nd word)
alert(regexp.lastIndex); // 11 (position after the match)
alert(word2[0]); // varName (২য় শব্দটি)
alert(regexp.lastIndex); // 11 (পরবর্তী মিলের পর নতুন অবস্থান)

let word3 = regexp.exec(str);
alert(word3); // null (no more matches)
alert(regexp.lastIndex); // 0 (resets at search end)
alert(word3); // null (আর কোন শব্দ নেই)
alert(regexp.lastIndex); // 0 (অনুসন্ধান শেষে পুনরায় অবস্থান 0)
```

We can get all matches in the loop:
সকল মিলকে লুপের মাধ্যমে খুঁজতে পারি:

```js run
let str = 'let varName';
Expand All @@ -59,23 +59,23 @@ let result;

while (result = regexp.exec(str)) {
alert( `Found ${result[0]} at position ${result.index}` );
// Found let at position 0, then
// Found varName at position 4
// শূন্যতম অবস্থানে let, তারপর
// varName ৪র্থ অবস্থানে
}
```

Such use of `regexp.exec` is an alternative to method `str.matchAll`, with a bit more control over the process.
`regexp.exec` এটির ব্যবহার অনেকটা `str.matchAll` এর মত, তবে প্রসেসটিতে কিছুটা কন্ট্রোল থাকে।

Let's go back to our task.
চলুন আমাদের টাস্কটিতে ফিরে যায়।

We can manually set `lastIndex` to `4`, to start the search from the given position!
আমরা `lastIndex` এর পজিশন `4` সেট করি, অই অবস্থান হতে অনুসন্ধানটি চালু হবে!

Like this:
এভাবে:

```js run
let str = 'let varName = "value"';

let regexp = /\w+/g; // without flag "g", property lastIndex is ignored
let regexp = /\w+/g; // "g" ফ্ল্যাগছাড়া lastIndex প্রপার্টি পাব না

*!*
regexp.lastIndex = 4;
Expand All @@ -85,54 +85,54 @@ let word = regexp.exec(str);
alert(word); // varName
```

Hooray! Problem solved!
ইয়েহহহহহহ! সমস্যাটি সমাধান করে ফেলেছি!

We performed a search of `pattern:\w+`, starting from position `regexp.lastIndex = 4`.
আমরা `pattern:\w+` এর মাধ্যমে `regexp.lastIndex = 4` সেটের মাধ্যমে অনুসন্ধানটি সম্পন্ন করেছি।

The result is correct.
এবং আমরা সঠিক ফলাফল পেয়েছি।

...But wait, not so fast.
...কিন্তু থামুন।

Please note: the `regexp.exec` call start searching at position `lastIndex` and then goes further. If there's no word at position `lastIndex`, but it's somewhere after it, then it will be found:
দয়া করে মনে রাখুন: `regexp.exec` অনুসন্ধান শুরু করে `lastIndex` এর অবস্থান হতে এবং পরবর্তী অবস্থানে যায়। যদি `lastIndex` এ পজিশনে কোন শব্দ না থাকে কিন্তু এরপরে থাকে তাহলে আমরা মিলটি পাব:

```js run
let str = 'let varName = "value"';

let regexp = /\w+/g;

*!*
// start the search from position 3
// ৩য় অবস্থান হতে অনুসন্ধান শুরু
regexp.lastIndex = 3;
*/!*

let word = regexp.exec(str);
// found the match at position 4
let word = regexp.exec(str);
// ৪র্থ অবস্থানে মিল খুঁজে পায়
alert(word[0]); // varName
alert(word.index); // 4
```

For some tasks, including the lexical analysis, that's just wrong. We need to find a match exactly at the given position at the text, not somewhere after it. And that's what the flag `y` is for.
কিন্ত আমাদের টাস্কে লেক্সিকাল অ্যানালাইসিসের মতে এটি ভুল। আমাদের একটি নির্দিষ্ট অবস্থানে নির্দিষ্ট একটি মিল খুঁজতে হবে, অই অবস্থানের আগে বা পরে নই। এবং এজন্যই `y` ফ্ল্যাগটি এসেছে।

**The flag `pattern:y` makes `regexp.exec` to search exactly at position `lastIndex`, not "starting from" it.**
**`pattern:y` ফ্ল্যাগটি নিশ্চিত করে `regexp.exec` এটি `lastIndex` এর নির্দিষ্ট অবস্থানে অনুসন্ধান চালাবে, "শুরু থেকে" নই।**

Here's the same search with flag `pattern:y`:
চলুন একই অনুসন্ধানটি `pattern:y` ব্যবহারের মাধ্যমে দেখি:

```js run
let str = 'let varName = "value"';

let regexp = /\w+/y;

regexp.lastIndex = 3;
alert( regexp.exec(str) ); // null (there's a space at position 3, not a word)
alert( regexp.exec(str) ); // null (৩য় অবস্থানে একটি স্পেস, সুতরাং মিল পাবে না)

regexp.lastIndex = 4;
alert( regexp.exec(str) ); // varName (word at position 4)
alert( regexp.exec(str) ); // varName (৪র্থ অবস্থানে শব্দ)
```

As we can see, regexp `pattern:/\w+/y` doesn't match at position `3` (unlike the flag `pattern:g`), but matches at position `4`.
এখানে আমরা দেখছি, রেগুলার এক্সপ্রেশনটিতে `pattern:/\w+/y` এটি `3` অবস্থানে মিলবে না(`pattern:g` ফ্ল্যাগের মত না), কিন্তু `4` অবস্থানে মিলবে।

Not only that's what we need, there's an important performance gain when using flag `pattern:y`.
এটি রেহুলার এক্সপ্রেশন ইঞ্জিনের পারফরম্যান্সে গুরুত্বপূর্ন ভূমিকা রাখে `pattern:y`

Imagine, we have a long text, and there are no matches in it, at all. Then a search with flag `pattern:g` will go till the end of the text and find nothing, and this will take significantly more time than the search with flag `pattern:y`, that checks only the exact position.
মনে করুন, আমাদের একটি বড় ট্যাক্সটে অনুসন্ধান চালাতে হবে, এবং সেখানে আমাদের কাঙ্ক্ষিত প্যাটার্নটি নাই। যদি আমরা `pattern:g` ফ্ল্যাগের মাধ্যমে চালাই, তাহলে এটি শেষ পর্যন্ত অনুসন্ধান চালাবে, এবং কোন মিল পাবে না এবং এটি অবশ্যই `pattern:y` এর তুলনায় অনেক বেশী সময় নেবে। যেখানে আমরা শুধুমাত্র `pattern:y` এর মাধ্যমে একটি নির্দিষ্ট অবস্থানে অনুসন্ধানটি করতে পারতাম।

In tasks like lexical analysis, there are usually many searches at an exact position, to check what we have there. Using flag `pattern:y` is the key for correct implementations and a good performance.
আমাদের টাস্কের অনুরূপ, লেক্সিকাল অ্যানালাইসিসে নির্দিষ্ট অবস্থানে কি আছে তা অনুসন্ধান করতে হয়। `pattern:y` ফ্ল্যাগের মাধ্যমে আমাদের অনুসন্ধানটির পারফরম্যান্স অনেক ভালো হবে।