ওয়েভ এসেম্বলি – জাভাস্ক্রিপ্টের এক নতুন অধ্যায়

১৯৯৫ সালে যাত্রা শুরু করা জাভাস্ক্রিপ্ট ছিল অনেক ধীর গতির। ইন্টারপ্রিটেশন প্রোগ্রামিং ভাষার সীমাবদ্ধতা ও নিজস্ব অপটিমাইজেশনের অভাব নিয়ে জাভাস্ক্রিপ্ট চলেছে এক যুগেরও বেশি। অবশেষে শুরু হয় বিভিন্ন ব্রাউজারের সাথে পারফরমেন্স যুদ্ধ। যার ফলস্বরূপ ২০০৮ সালে এক নতুন জাভাস্ক্রিপ্টের যুগের সূচনা হয় যার মূলে ছিলো JIT অর্থাৎ জাস্ট-ইন-টাইম কম্পাইলার। এই JIT সংযোগের ফলে জাভাস্ক্রিপ্ট গতি পায় প্রায় দশগুন। যার ফলশ্রুতিতে জাভাস্ক্রিপ্ট ব্রাউজারের সীমানা পাড়িয়ে সার্ভারসাইড প্রোগ্রামিং ভাষা যেমন Node.js হিসেবে ব্যবহার শুরু হয়। ২০১৭ সাল থেকে ব্রাউজারগুলো নতুন একটি ওয়েব স্ট্যান্ডার্ড যুক্ত করা শুরু করেছে যার নাম ওয়েব এসেম্বলি (WebAssembly) যা কিনা আরেকটি নতুন যুগের সূচনা।

ওয়েব এসেম্বলি কি?

এতদিন পর্যন্ত ব্রাউজারগুলোতে শুধুমাত্র জাভাস্ক্রিপ্ট দিয়ে লেখা প্রোগ্রামগুলোই রান করা যেতো। অন্য প্রোগ্রামিং ভাষায় যেমন C/C++ এ লিখা কোনো লাইব্রেরী বা মডিউল আমরা ব্রাউজারগুলোতে ব্যবহার করতে পারতাম না। যদিও asm.js এর কল্যাণে আমরা বিভিন্ন C/C++ লাইব্রেরী ব্রাউজার এনভায়রনমেন্ট-এ ব্যবহার করেছি কিন্তু asm.js মূলত জাভাস্ক্রিপ্টের একটি সাবসেট এবং এর পারফরমেন্স কখনও ন্যাটিভ লাংগুয়েজের কাছাকাছি হবে না। ওয়েব এসেম্বলির ক্ষেত্রে পারফরমেন্সের সমস্যা থাকবে না। যাই হোক, ওয়েব এসেম্বলির মাধ্যমে আমরা বিভিন্ন প্রোগ্রামিং ভাষা যেমন C/C++ এ লেখা কোড ওয়েভ ব্রাউজারগুলোতে জাভাস্ক্রিপ্টের সাথে রান করাতে পারবো।

ওয়েব এসেম্বলির কম্পাইলার

ওয়েব এসেম্বলির ফাইলের এক্সটেনশন হচ্ছে wasm (WebAssembly এর সংক্ষেপ ) এবং wasm ফাইলে মূলত অনেকটা এসেম্বলি ভাষার মতো বাইনারি নির্দেশ থাকে। অন্য প্রোগ্রামিং ভাষায় লিখা কোডকে ব্রাউজারগুলোতে রান করাতে হলে প্রথমে wasm-এ কম্পাইল করে নিতে হবে। বিভিন্ন প্রোগ্রামিং ভাষার কোডকে wasm কম্পাইল করার জন্যে বিভিন্ন কম্পাইলার আছে যেমন, C/C++ ভাষার জন্যে Emscripten অথবা Binaryen, Haskell ভাষার জন্যে asterius ইত্যাদি। এখন পর্যন্ত সি/সি ++, লুয়া, পাইথন, রাস্ট (Rust), Esoteric প্রভৃতি ভাষার জন্যে কম্পাইলার আছে। বিভিন্ন প্রোগ্রামিং ভাষার জন্যে নতুন নতুন কম্পাইলার যোগ হচ্ছে।

ওয়েব এসেম্বলি কিভাবে ব্যবহার করতে হয়?

wasm ফাইল রান করার জন্যে জাভাস্ক্রিপ্টের মতো প্রথমে একে লোড করতে হয় কিন্তু বর্তমানে ব্রাউজারগুলোতে wasm ফাইল লোড করার জন্যে সরাসরি কোনো html ট্যাগ যেমন <script src=”xyz.wasm” type=”text/wasm”></script> নেই। যেহেতু ওয়েব এসেম্বলি বর্তমানে প্রাথমিক পর্যায়ে আছে তাই আশা করা যায় ভবিষ্যতে wasm ফাইল লোড করার প্রক্রিয়া সহজতর হবে। যাই হোক, বর্তমানে wasm ফাইলকে রেসপন্স টাইপ ArrayBuffer হিসেবে ajax রিকুয়েস্টের মাধ্যমে সার্ভার থেকে লোড করে নিতে হয়। সেই ArrayBuffer কে জাভাস্ক্রিপ্টের নির্দিস্ট API ব্যবহার করে ওয়েব-এসেম্বলির একটি মডিউল (WebAssembly module) তৈরি করতে হয়। এখন সেই মডিউলের মাধ্যমে নেটিভ ল্যাঙ্গুয়েজের API-গুলা ব্যবহার করা যায়।

তবে ব্যপার হচ্ছে সাধারণত কম্পাইলারগুলো যেমন, Emscripten প্রয়োজনীয় গ্লু / লিঙ্কিং কোড জেনারেট করে দেয়, তাই wasm ফাইল লোড করা এবং ওয়েব-এসেম্বলি মডিউল তৈরির ঝামেলায় যেতে হয় না। প্রত্যেকটি কম্পাইলারই নেটিভ ফাংশনগুলো ব্যবহার করার জন্যে কিছু ফাংশন প্রোভাইড করে যেমন Emscripten এর cwrap অথবা ccall ফাংশন। ধরা যাক, আমরা নিচের সি ল্যাঙ্গুয়েজ -এ লেখা sum ফাংশন ওয়েব এসেম্বলির মাধ্যমে ব্যবহার করতে চাই।

#include <stdio.h>;

int sum(int num1, int num2) {
  int num3;
  num3 = num1 + num2;
  return (num3);
}

এখন Emscripten -এর নিচের কমান্ডটি রান করালে প্রয়োজনীয় জাভাস্ক্রিপ্ট কোড তৈরি করে hello.html ফাইলে script ট্যাগ সংযুক্ত করে দিবে।

emcc hello.c -o hello.html -s WASM=1 -s EXPORTED_FUNCTIONS='["_sum"]' -s EXTRA_EXPORTED_RUNTIME_METHODS='["cwrap"]'

কমান্ডের ব্যাখ্যা

১। যেহেতু Emscripten – asm.js এবং ওয়েব-এসেম্বলি উভয়ের কম্পাইলার হিসেবে ব্যবহার করা যায়, তাই ওয়েব-এসেম্বলিতে কম্পাইল করতে WASM=1 দিতে হয়।

২। সাধারণত Emscripten প্রোগ্রামের মেইন ফাংশনকেই কল করে এবং অপ্রয়োজনীয় কোড গুলো ফেলে দেয়। যেহেতু এই প্রোগ্রামে কোন মেইন ফাংশন নাই এবং sum ফাংশনটি কোথাও ব্যবহার হচ্ছে না তাই Emscripten এটিকে ফেলে দিবে। কিন্তু আমরা যদি EXPORTED_FUNCTIONS লিস্টে ফাংশনের নাম বলে দেই, তাহলে সেগুলো ফেলবে না।

৩। সর্বশেষে Emscripten এর cwrap ফাংশনটি আমাদের লাগবে যাতে নেটিভ sum ফাংশনটি জাভাস্ক্রিপ্টের মাধ্যমে কল করতে পারি, তাই এটিকে EXTRA_EXPORTED_RUNTIME_METHODS লিস্টে দিতে হয়েছে।

শেষ কথা

ওয়েব-এসেম্বলির পারফরমেন্স asm.js এর তুলনায় অনেক ভালো এবং ভালো হওয়াটাই স্বাভাবিক। এখনও অনেক কিছু পরিবর্তন হচ্ছে এবং ভবিষ্যতে আরো অনেক ইম্প্রুভমেন্ট হবে। আশাকরি ভবিষ্যতে wasm মডিউলের একটি প্লাটফর্ম হবে যেখান থেকে, যে কেউ তার প্রয়োজনীয় মডিউলটি নামিয়ে ব্যবহার করতে পারবে অথবা শেয়ার করে দিতে পারবে তার নিজের মডিউলটি।

রিডাক্স কি এবং কেন?

রিডাক্স (redux) নিয়ে কাজ করার সময় বিশেষ করে রিয়েক্ট (react) -এ রিডাক্সের ভূমিকা কি এটা নিয়ে প্রথম দিকে একটু বিভ্রান্তি হয়, অন্তত আমার হয়েছে। এটা হতে পারে রিয়েক্ট-এর ডকুমেন্টেশনে উদাহরণগুলো এতই স্বয়ং সম্পূর্ণ যে এখানে রিডাক্স কিভাবে ব্যবহার করবো তা নিয়ে ভেবাচেকা খেতে হয়। যাই হোক,  আমি এই লেখায় রিডাক্স কি, রিডাক্স কেন এবং কোথায় আমরা রিডাক্স ব্যবহার করতে পারবো সেটা নিয়ে আলোচনা করবো।

সহজ কথায়  রিডাক্স হচ্ছে অ্যাপ্লিকেশনের স্টেট (state) ম্যানেজ করার জন্য একটি লাইব্রেরি। আমরা যারা জাভাস্ক্রিপ্ট-এ কাজ করি তারা জানি যে, স্টেট হলো অবজেক্টের প্রোপার্টিজ অথবা প্রোপার্টিজের সমষ্টি  যাদেরকে আমরা আরেক কথায় মডেল (model) বলে থাকি। যেহতু রিডাক্স একটি লাইব্রেরি, আমরা একে যেকোনো সিঙ্গেল পেইজ অ্যাপ্লিকেশনে ব্যবহার করতে পারি। আমি এখানে সিঙ্গেল পেইজ অ্যাপ্লিকেশন বললাম কারণ সাধারণত ক্লায়েন্ট এন্ড-এ  সিঙ্গেল পেইজ অ্যাপ্লিকেশনেই স্টেট ম্যানেজমেন্ট করার প্রয়োজন হয়।

প্রথমে আমাদের যা জানতে হবে, সেটা হচ্ছে রিডাক্স মূলত তিনটি মূল নীতি অনুসরন করে,

১। সম্পূর্ণ অ্যাপ্লিকেশনে একটি মাত্র স্টেট ট্রি বা অবজেক্ট ট্রি (single state tree or object tree) থাকবে। এর সুবিধা হলো খুব সহজেই অ্যাপ্লিকেশন ডিবাগ করা যায় এবং ডাটার সিঙ্গেল সোর্স অফ ট্রুথ  এর নিশ্চিত করে। খুব সহজেই আমরা সার্ভার থেকে স্টেট ট্রি অ্যাপ্লিকেশনে ইনজেক্ট করতে পারি।

২। স্টেটটি হবে রিড অনলি (read only) যা শুধু মাত্র অ্যাকশন ডিসপ্যাচ (dispatch) করার মাধ্যমে পরিবর্তন করা যাবে। তার মানে কখনো কারো যদি কারো স্টেট পরিবর্তন করার দরকার হয় সে শুধু নির্দিষ্ট অ্যাকশন ডিসপ্যাচ করবে। এখানে অ্যাকশন হলো একটি সিম্পল জাভাস্ক্রিপ্ট অবজেক্ট যা ওই অ্যাকশনের সকল তথ্য রাখে যেমন, কি টাইপের অ্যাকশন হবে, কোন ডাটা পরিবর্তন করা লাগবে ইত্যাদি।

৩। কোনো নির্দিষ্ট অ্যাকশন ডিসপ্যাচ করার পরে, স্টেট পরিবর্তন করতে হবে প্রয়োজনীয় পিউর ফাংশনের (Pure function) মাধ্যমে যাদেরকে রিডাক্স-এ রিডিউসার বলে। পিউর ফাংশন হল সেই ফাংশন যার ভিতরে কোন প্যাচগোচ নাই অর্থাৎ এটি কোন অ্যাসিনক্রোনাস অপারেশন করবে না, কোন আই/ও অপারেশন করবে না, এমনকি স্টেট অবজেক্টকে মিউটেট করতে পারবে না। তবে স্টেট অবজেক্টকে মিউটেট করার পরিবর্তে নতুন অবজেক্ট তৈরি করতে পারবে। কোন একটি অ্যাকশন ডিসপ্যাচ করলে রিডাক্স অ্যাপ্লিকেশনের প্রতিটি রিডিউসার ফাংশনকে কল করে এবং ফাংশনের  আর্গুমেন্ট হিসেবে পাঠিয়ে দেয় পূর্বের স্টেট এবং অ্যাকশন অবজেক্টটি। রিডিউসার ফাংশনের কাজ হচ্ছে অ্যাকশনের উপর ভিত্তি পরবর্তী স্টেট নির্ধারণ করা। যেহেতু এই ফাংশনগুলোর কাজ অনেকটা জাভাস্ক্রিপ্ট reducer ফাংশনের মতো অর্থাৎ আগের স্টেট থেকে পরবর্তী স্টেট নির্ধারণ করা তাই এদেরকে রিডিউসার বলা হয়।

আরেকটি বিষয়, রিডাক্স শুধু স্টেট ম্যানেজমেন্ট করে না, তার সাথে স্টেট-এ কোনো পরিবর্তন হলে সেটাও অবহিত (notify) করে। অ্যাকশন ফাংশনগুলো পিউর ফাংশন হওয়ার কারণে স্টেট-এ কোনো পরিবর্তন হয়েছে কিনা সেটা বের করা রিডাক্সের জন্য খুব সহজ কেননা শুধুমাত্র ইকুয়ালিটি চেক করেই সেটা বের করা যায়। নিচের উদাহরণটি দেখলেই ব্য্যপারটি বোঝা যাবে।

var bookList = [
 {id: 1, title:'Foundation Series'},
 {id: 2, title:'The Da Vinci Code'}
];

/* Now we want to add a new book. 
   But it is not permitted to mutate bookList! 
   No problem, we can use contact() function which return 
   a new array instead of mutating existing one.
*/
var newBookList = bookList.concat({id:3, title:'New book'});
var isChanged = (bookList !==newBookList); /* return true */

এবার আসা যাক কিভাবে আমরা রিডাক্স ব্যবহার করবো। ধরে নেই, আমরা একটি ছোট বুকলিস্ট অ্যাপ্লিকেশন বানাতে চাই, যেখানে আমরা বইয়ের লিস্ট দেখাবো এবং নতুন বই সংযোগ করতে পারবো। নতুন বই সংযোগ করলে সাথে সাথে পরিবর্তিত লিস্ট দেখাবে।  আমরা যদি এটাকে ভ্যানিলা জাভাস্ক্রিপ্ট মানে কোন প্রকার ফ্রেমওয়ার্ক ছাড়া করি তা অনেকটা নিচের মতো হবে।

/* For managing booklist state, lets declare an array*/
var bookID=1;
var myBooks = [
 {id: 1, title:'Foundation Series'},
 {id: 2, title:'The Da Vinci Code'}
];

/* Add book function */
function addBook(book) {
   myBooks.push(book);
}

/* For simplicity, lets use tabular list and update table inner html */
function renderToDOM() {
     var booklist = getFilterdBooks(),
	 html;
	 html = booklist.reduce(function(html,singleBook) {
	            return html + `<tr>
				    <td>${singleBook.id}</td>
				    <td>${singleBook.title}</td>
			         </tr>`;
		},'');
      document.getElementById('container').innerHTML = html;
}

/* For rendering initial list, invoke renderToDOM() first time */
renderToDOM();

/* whenever user click on add-btn, it will insert a new book
   and re-render to reflect new list.
*/
document.getElementById('add-btn').addEventListener('click', function() {
      addBook({id: bookID++, title: 'A new book' });
      renderToDOM();
});

এখন যদি অ্যাপ্লিকেশনটি রিডাক্স-এ ইমপ্লিমেন্ট করতে চাই, তাহলে তার মূল নীতি অনুসারে  কিছু নিয়ম অনুসরন করতে হবে। যেহেতু, রিডাক্স-এ স্টেট শুধুমাত্র পরিবর্তন করা যাবে কেবল মাত্র অ্যাকশন ডিসপ্যাচ করার মাধ্যমে, তাই অ্যাকশন অবজেক্টগুলো আগে তৈরি করা যাক। সাধারণত অ্যাকশন অবজেক্টগুলো তৈরি করা হয় অ্যাকশন ক্রিয়েটর ফাংশনের মাধ্যমে যেটি আমাদের অ্যাকশন অবজেক্টটি রিটার্ন করবে। উলেখ্য যে, বাঁকি উদাহরণগুলোতে আমি ES6 সিনট্যাক্স ব্যবহার করবো।

তো অ্যাকশন ক্রিয়েটর অনেকটা নিচের মতো হবেঃ

let bookID = 1; /* For making unique bookID */
export const addBook = (title) => ({
	type: 'ADD_BOOK',
	id: bookID++,
	title
})

আর রিডিউসার ফাংশনটি অনেকটা নিচের মতো হবেঃ

const books = (state = [], action) => {
    switch (action.type) {
	case 'ADD_BOOK':
	      return state.concat(
		    {
			id: action.id,
			title: action.title
		    }
		);
	 default:
	     return state;		
     }
}

export default books;

মূলত প্রত্যেকটি রিডিউসার ফাংশন একটি সুইচ কেইস ব্লক যেখানে অ্যাকশনের টাইপ অনুসারে বিভিন্ন স্টেট রিটার্ন করে। যদি কোন অ্যাকশনের টাইপ না মিলে অর্থাৎ সুইচ কেইস ব্লকে না পরে, তাহলে ডিফল্ট হিসেবে পূর্ববর্তী স্টেট রিটার্ন করতে হবে। মনে রাখতে হবে যে, ডিফল্ট হিসেবে পূর্ববর্তী স্টেট রিটার্ন করা বাধ্যতামূলক কেননা রিডাক্স যেকোনো অ্যাকশনের জন্যে অ্যাপ্লিকেশনের সকল রিডিউসারকে কল করে এবং সেজন্য অ্যাকশন অবজেক্টটিতে টাইপ (type) প্রপার্টিজটি থাকতেই হবে, বাকি সব প্রপার্টিজ অ্যাপ্লিকেশানের প্রয়োজন মতো, কোন বাধ্যবাধকতা নাই।

এখন আসা যাক রিডাক্সের আসল ভূমিকায়। অ্যাকশন এবং রিডিউসারকে একীভূত করে রিডাক্সের যেই অবজেক্টেটি তার নাম স্টোর (store)। স্টোরের মূলত তিনটি প্রধান মেথড আছেঃ

১। স্টেটের বর্তমান অবস্থা পেতে  getState()

২। অ্যাকশন ফায়ার অর্থাৎ ডিসপ্যাচ করতে dispatch(actionObject) 

৩। স্টেটের কোনো পরিবর্তনে নটিফাই পেতে subscribe(function)

স্টোর তৈরি করতে রিডাক্সের createStore() মেথড ব্যবহার করতে হয় যা মূলত তিনটি আর্গুমেন্ট সাপোর্ট করে।

১। রিডিউসার

২। ইনিশিয়াল স্টেট

৩। অ্যাপ্লাই মিডিলওয়্যার

শুধুমাত্র প্রথম প্যারামিটার অর্থাৎ রিডিউসারটি বাধ্যতামূলক, বাঁকিগুলো অপশনাল। রিডিউসার ও ইনিশিয়াল স্টেট প্যারামিটারের নাম থেকেই বোঝা যাচ্ছে তাদের উদ্দেশ্য। অ্যাপ্লাই মিডিলওয়্যার ব্যবহার করা হয় স্টোরের ফাংশনালিটি আরো এক্সটেন্ড বা বর্ধিত করার জন্য। একটি উদাহরণ দেয়া যাক, যেহেতু অ্যাকশন অবজেক্টটি মূলত প্লেইন জাভাস্কিপ্ট অবজেক্ট, আমরা চাইলে কোনো অ্যাসিনক্রোনাস অপারেশন যেমন, সার্ভার থেকে ডাটা আনা, প্রমিজ নিয়ে কাজ করা ইত্যাদি করতে পারবো না। এই সমস্যা সমাধান করা যায় মিডিলওয়্যার দিয়ে, যেখানে মিডিলওয়্যারটি অ্যাসিনক্রোনাস অপারেশনটি করে প্রয়োজনীয় অ্যাকশনটি ডিসপ্যাচ করে দিতে পারে। অনেক মিডিলওয়্যার রেডিমেইড পাওয়া যায়, প্রয়োজন হলে নিজেরা তৈরি করে নেয়া  যায়। খুব পরিচিত একটি মিডিলওয়্যার হচ্ছে redux-thunk যার মাধ্যমে অ্যাকশন অবজেক্টই স্থলে ফাংশন ব্যবহার করা যায়। খুব সংক্ষেপে মিডিলওয়্যার কিভাবে কাজ করে বলে যাই, মূলত স্টোর তৈরি করার সময় যদি কোন মিডিলওয়্যার প্রোভাইড করা হয়, রিডাক্স প্রতিটি অ্যাকশন ডিসপ্যাচ করার পূর্বে প্রথমে মিডিলওয়্যারটি কল করে এবং মিডিলওয়্যারটি যদি তার প্রত্যাশিত প্যাটার্ন অনুসারে  মিল পায়,তাহলে সেটি তা প্রসেস করে এবং সেই অ্যাকশনটি আর রিডাক্সের কাছে না দিয়ে প্রয়োজনীয় কাজ শেষে নিজেই অ্যাকশনটি ডিসপ্যাচ করে দেয় আর যদি মিল না পায়, তাহলে অ্যাকশনটি পরবর্তী মিডিলওয়্যারের কাছে ফরওয়ার্ড করে, এভাবে সর্বশেষে রিডাক্সের কাছে পৌছায়।

যাই হোক, আমাদের ভ্যানিলা জাভাস্কিপ্ট অ্যাপ্লিকেশনটি রিডাক্সে-এ পরিবর্তন করতে আমরা renderToDOM() মেথডকে ষ্টোরের মাধ্যমে সাবস্ক্রাইব করবো যাতে ষ্টেট-ট্রিতে কোন পরিবর্তন হলে বুকলিস্ট নিজে নিজে আপডেট হয় আর নতুন বই সংযোগ করতে dispatch() -এর মাধ্যমে addBook() অ্যাকশন পাঠাবো এবং ইনিশিয়াল ষ্টেট হিসেবে আমাদের বইয়ের লিস্টটি দিয়ে দিবো। সবশেষে renderToDOM() -এ একটু পরিবর্তন করবো যাতে ষ্টেটটি এখনথেকে রিডাক্স স্টোর থেকে নেয়। অ্যাপ্লিকেশনের রিডাক্স ভার্সনটি নিচের মতো হবেঃ

{createStore} from 'redux';

const renderToDOM = () => {
	let state = store.getState(),
	  booklist = state.books,
	  html;
	html = booklist.reduce(function(html,singleBook) {
		return html + `<tr>
				<td>${singleBook.id}</td>
				<td>${singleBook.title}</td>
						
		           </tr>`;
	},'');

	document.getElementById('container').innerHTML = html;
};

let initialState = [
  {id: 1, title:'Foundation Series'},
  {id: 2, title:'The Da Vinci Code'}
];
const store = createStore(
	   books,
	   initialState
	);

store.subscribe(()=>{
	renderToDOM();
});

/* whenever user click on add-btn, it will insert a new book
   and re-render to reflect new list.
*/
document.getElementById('add-btn').addEventListener('click', function() {
     store.dispatch(addBook({title: 'A new book' }));
});

লক্ষণীয় যে,  createStore() মেথড শুধুমাত্র একটি  রিডিউসার সাপোর্ট করে। যেহেতু রিডাক্স, রিডিউসারগুলো থেকে ষ্টেট-ট্রি তৈরি করে এবং একটি অ্যাপ্লিকেশনে একটিই স্টেট-ট্রি থাকে তাই রিডিউসারও হয় একটি। কিন্তু একটি অ্যাপ্লিকেশনে একাধিক রিডিউসারের প্রয়োজনীয়তা খুবই স্বাভাবিক। একাধিক রিডিউসার থাকলে তাদেরকে একীভূত করার জন্যে রিডাক্সের combineReducers() নামে একটি মেথড আছে যেখানে একাধিক রিডিউসার ইনপুট দিলে একটি কম্বাইন্ড রিডিউসার রিটার্ন করে এবং সেই কম্বাইন্ড রিডিউসার ব্যবহার করে createStore() -এর মাধ্যমে স্টোর তৈরি করতে হয়।

এখন আমরা যদি আমাদের অ্যাপ্লিকেশনটি শুধু রিয়েক্ট (react)-এ কনভার্ট করি তা অনেকটা নিচের মতো হবে। আমি এখানে দুটি কম্পোনেন্ট ব্যবহার করেছি। একটি প্রেসেন্টেশনাল কম্পোনেন্ট, বইয়ের লিস্ট দেখানোর জন্যে এবং আরেকটি কন্টেইনার কম্পোনেন্ট, স্টেট ম্যানেজমেন্ট এবং অন্যান্য DOM ইভেন্ট হ্যান্ডলিংয়ের জন্যে।

const BookList = (props) => {	

     return (
	   <tbody id="container">
	       {
		  props.books.map((book)=> {
		     return (
		   	     <tr>
                               <td>{book.id}</td>
                               <td>{book.title}</td>
                             </tr>
		   	   );
		   	})
		   }
		</tbody>
	);	
		
}


class App extends Component {
	
	constructor(props) {
	    super(props);
	    this.state = {
	    	books: []
	    };
         }
         
       componentDidMount() {
    	    let books = [
	         {
		   id: 1,
	           title: 'Around the World in Eighty Days'
		   },
		   {
		     id: 2,
		     title: 'Foundation Series'
		   }
	     ];

	    this.setState({
	       books: books
	    });
       }
     
      render() {
	   return (
	      <table>
		  <Booklist books={this.state.books} />
	      </table>
	   );	
	}
}

/* Attached App to a DOM node */
ReactDOM.render(
	 <App />,
	document.getElementById('root')
);

এখন যদি রিয়েক্ট অ্যাপ্লিকেশনটির ষ্টেট ম্যানেজমেন্টের দায়িত্ব রিডাক্সকে দিতে চাই, তাহলে আমাদের যা করতে হবেঃ

১।  রিয়েক্ট-এ রিডাক্স ব্যবহার করার জন্যে, রিডাক্সের একটি লাইব্রেরি আছে যার নাম react-redux

২। react-redux লাইব্রেরির মূল ভূমিকা হচ্ছে রিডাক্স ষ্টেট থেকে প্রয়োজনীয় প্রপার্টিজগুলোকে রিয়েক্ট-এর কম্পোনেন্ট-এ props হিসেবে দিয়ে দেয়া এবং ষ্টেট-এ কোনো পরিবর্তন হলে রিয়েক্ট-এর  render() মেথড কল করে দেয়া যাতে রিয়েক্ট-এর ভিউ সবসময় আপডেটেড থাকে।

৩। রিডাক্সের স্টেট এবং রিয়েক্ট কম্পোনেটের মধ্যে সেতু বন্ধন করার জন্যে react-redux লাইব্রেরি connect() মেথড ব্যবহার করতে হয়।

৪। connect() মেথড এ দুটি আর্গুমেন্ট পাঠানো যায়। একটি হলো, স্টেট-ট্রির কোন প্রোপার্টিজ রিয়েক্ট props-এ কি নামে পাওয়া যাবে তার একটি ম্যাপ এবং দ্বিতীয়টি হলো, রিয়েক্ট থেকে রিডাক্সের অ্যাকশন ডিসপ্যাচ করার জন্যে, যে সব অ্যাকশন রিয়েক্ট-এ প্রয়োজন তার আরেকটি ম্যাপ।

৫। এই connect() মেথড যেকোনো রিয়েক্ট কম্পোনেন্টে ব্যবহার করা যেতে পারে। react-redux লাইব্রেরির পবিত্র দায়িত্ব হচ্ছে স্টেট ম্যাপটি props মাধ্যমে সেখানে হাজির করা। তবে এই জন্য আরেকটি শর্ত মানতে হয়, তা হলো react-redux লাইব্রেরি বিনা পয়সায় Provider নামে একটি রিয়েক্ট কম্পোনেন্ট দেয়, সেটাকে অ্যাপ্লিকেশনের রুট বা টপ কম্পোনেট হিসেবে ব্যবহার করতে হবে এবং রিডাক্স স্টোরটি props হিসেবে দিয়ে দিতে হবে। খুবই ন্যায্য দাবী না হলে রিডাক্স বেচারা স্টোরটি পাবে কোথায়!

প্রেসেন্টেশনাল কম্পোনেন্টে কোনো পরিবর্তন লাগবে না, শুধু কন্টেইনার কম্পোনেন্ট App -এ সব কিছু দিয়ে সাজালে গুছালে নিচের মতো হবে।

class App extends Component {
	
	constructor(props) {
	    super(props);
         }
         
       
      /* We should be use books from props instead of state now*/
      render() {
	   return (
		<table>
	          <Booklist books={this.props.books} />
		</table>
	    );	
	}
}

/* state mapping that we want to attach into react props.
   We need to define map inside a function and state will be available 
   through parameter
*/
const mapStateToProps = (state) => {
	return {
	   books: state.books,
	};
};

/* dispatch mapping that we want to attach into react props.
   We need to define map inside a function and dispatch will be available 
   through parameter
*/

const mapDispatchToProps = (dispatch) => {
	return {
	   addBook: (title, author, status) => dispatch(addBook(title))
        };
};

/* connect is a curried function, first it need to provide props map and then
   dispatch maps. It will return another method where the component will 
   need to be provided
 */
export default connect(mapStateToProps, mapDispatchToProps)(App);

/* We must to use Provider component gifted by react-redux
   and store should be set as pros
  */
ReactDOM.render(
	<Provider store={store}>
	   <App />
	</Provider>,
    document.getElementById('root')
);

সবশেষে আমি একটি ছোট প্রোজেক্ট মোট চার ভাবে মানে ভ্যানিলা থেকে রিয়েক্ট-রিডাক্স -এ করে দেখিয়েছি যাতে পাথক্যটা ভালোভাবে বোঝা যায়। তাছাড়া এখানে একাধিক রিডিউসারের ব্যবহার এবং নতুন বই অ্যাড, বই এডিট, বই ডিলিট করলে পিউর ফাংশনগুলো অবজেক্ট মিউটেট না করে কিভাবে করা যায় তার উদাহরণ দিয়েছি। প্রোজেক্টির গিটহাব লিঙ্ক হচ্ছে VanillaJS-to-react-redux

রেগুলার এক্সপ্রেশন -এ শেষ।

আগের লেখায়  আমরা রেগুলার এক্সপ্রেশনের বেসিক অনেক কিছুই দেখেছিলাম, এই লিখার মাধ্যমে আমরা এর শেষ করবো। প্রথমে আমাদের গিনিপিগ স্ট্রিং কে আবার নিয়ে আসি।

Mr. Alu and mr. Potol are friends since 2008. They study at the same university in the session 2008-2009.

গত লেখায় আমরা এই গিনিপিগ স্ট্রিং থেকে বছর বের করার প্যাটার্ন লিখেছিলাম, যা ছিলো এরকমঃ

var str = 'Mr. Alu and mr. Potol are friends since 2008. They study at the same university in the session 2008-2009.';
var pattern = /\d{4}/;
var matches = str.match(pattern);
console.log(matches);

/* Output in console*/
 ["2008"]

এখানে ফলাফল হিসেবে দেখতে পাচ্ছি শুধু 2008 কে দেখাচ্ছে যদিও আমাদের স্ট্রিং-এ আরও কয়েকটা বছর রয়েছে। সাধারনভাবে রেগুলার এক্সপ্রেশনে প্রথম মিলটি পেলেই খোঁজা বন্ধ করে দেয়। যদি আমাদের সবগুলা ম্যাচিং বা মিলগুলো দরকার হয় তাহলে আমাদেরকে আলাদাভাবে বলে দিতে হয়। এই বলে দেবার কাজটি যা দিয়ে করা হয় তাকে রেগুলার এক্সপ্রেশনে বলে মডিফায়ার। জাভাস্ক্রিপ্টে হাতেগোনা কয়েকটা মডিফায়ার ফ্ল্যাগ আছে। যেমনঃ

মডিফায়ার ফ্ল্যাগ অর্থ
g g মানে গ্লোবাল অর্থাৎ একবার মিল পেলে খোঁজা বন্ধ না করে সম্পূর্ণ স্ট্রিং-এ  খুঁজবে।
i ইন-কেইস সেনসিটিভ মানে ছোট বড় সকল বর্ণ একই বিবেচনা করবে।
m m মানে মাল্টিলাইন। মাল্টিলাইনযুক্ত স্ট্রিং-এর প্রতিটি লাইনের শুরু ও শেষ আলাদাভাবে বিবেচনা করবে।

একই প্যাটার্নে একাধিক মডিফায়ার এক সাথে ব্যবহার করা যায়। এখন আমরা যদি আমাদের গিনিপিগ স্ট্রিং থেকে সবগুলো বছর বের করতে চাই তাহলে প্যাটার্নটি হবেঃ

var str = 'Mr. Alu and mr. Potol is closed friend since 2008. They are studying in same university in the session 2008-2009.';
var pattern = /\d{4}/g;
var matches = str.match(pattern);
console.log(matches);

/* Output in console*/
["2008", "2008", "2009"]

এখানে শেষ স্ল্যাশের পরে g হলো মডিফায়ার ফ্ল্যাগ। অনুরুপভাবে, আমরা যদি Mr. কতবার আছে তা বের করতে চাই তাহলে প্যাটার্নটি হবেঃ

var str = 'Mr. Alu and mr. Potol are friends since 2008. They study at the same university in the session 2008-2009.';
var pattern = /mr\./gi;
var matches = str.match(pattern);
console.log(matches);

/* Output in console*/
["Mr.", "mr."]

এখানে দুটো বিষয় লক্ষ্যনীয়,

১। যেহেতু Mr. এবং mr. দুটোই আছে, তাই ফ্ল্যাগ g এর সাথে i ব্যবহার করেছি,  এর ফলে কেইস বিবেচনা করবে না।

২। যেহেতু ডট (.) একটি বিশেষ ক্যারেক্টার কিন্তু আমরা লিটার‍্যালি বিবেচনা করতে চাচ্ছি তাই একে স্কেপ করা লাগবে।বিশেষ ক্যারেক্টারকে স্কেপ করার জন্যে তার আগে একটা ব্যাকওয়ার্ড স্ল্যাশ দিতে হয়।

আচ্ছা, এখানে যদি আমরা ইন-কেইস সেনসিটিভ মডিফায়ার i ব্যবহার না করি তাহলে কি প্যাটার্নটি তৈরি করা যাবে? হ্যাঁ যাবে, সেক্ষেত্রে আমাদের দুইটি প্যাটার্ন লাগবে। একটি প্যাটার্ন লাগবে Mr. জন্যে এবং আরেকটি প্যাটার্ন লাগবে mr. এর জন্যে। একই প্যাটার্ন-এ এরকম এক বা একাধিক সাব-প্যাটার্ন লেখা যায় এবং সাব-প্যাটার্নগুলো লিখতে হয় প্রথম বন্ধনির ভিতরে। সাব-প্যাটার্ন ব্যবহার করে উপরের প্যাটার্নটি নতুন করে লিখলে নিচের মতো হবে। লক্ষ্যনীয় যে, সাব-প্যাটার্ন দুটির মাঝে OR সাইন | ব্যবহার করা হয়েছে যার মানে দুটির মাঝে যেকোনো একটি।

var str = 'Mr. Alu and mr. Potol are friends since 2008. They study at the same university in the session 2008-2009.';
var pattern = /(Mr\.|mr\.)/g;
var matches = str.match(pattern);
console.log(matches);

/* Output in console*/
["Mr.", "mr."]

রেগুলার এক্সপ্রেশনে প্রথম বন্ধনির ভিতরে এরকম সাব-প্যাটার্নের আলাদা গুরুত্ব রয়েছে এবং সেটি হচ্ছে মূল প্যাটার্নের মিল বা ম্যাচিং গুলার সাথে সাব-প্যাটার্নের ম্যাচিংগুলোও আলাদা ভাবে পাওয়া যায় বা ক্যাপচার করা যায়। এ জন্যে রেগুলার এক্সপ্রেশনে এদেরকে ক্যাপচারিং গ্রুপ বলে । ধরা যাক, আমরা আমাদের গিনিপিগ স্ট্রিং-এ কি কি নাম আছে সেগুলো বের করতে চাচ্ছি এবং নাম বলতে আমরা বুঝি যাদের প্রথমে Mr./mr. থাকবে, তারপরে একটি স্পেস, তারপরে কিছু ওয়ার্ড ক্যারেক্টার এবং শেষ হবে একটি স্পেসের মাধ্যমে অর্থাৎ প্যাটার্নটি হবে

var str = 'Mr. Alu and mr. Potol are friends since 2008. They study at the same university in the session 2008-2009.';
var pattern = /(Mr\.|mr\.)\s\w+\s/g;
var matches = str.match(pattern);
console.log(matches);

/* Output in console*/
["Mr. Alu ", "mr. Potol "]

/*Actual Output*/
[ Match 1: "Mr. Alu ", Group1: "Mr."
  Match 2: "Mr. Potol ", Group2: "mr."]

যদিও জাভাস্ক্রিপ্টে match ফাংশন ক্যাপচারিং গ্রুপের মিলগুলো দেখায় না কিন্তু এর আসল ফলাফল হবে Actual Output সেকশনে দেখানো ফলাফলের মতো। আমাদের অনেক সময় ক্যাপচারিং গ্রুপের মিলগুলো দরকার পরে না কিন্তু আমরা শুধু সাব-প্যাটার্ন ব্যবহার করতে চাই, তাহলে প্রথম বন্ধনির পরে ?: দিতে হয় যাকে বলে নন-ক্যাপচারিং গ্রুপ। প্রকৃতপক্ষে জাভাস্ক্রিপ্টে match ফাংশন সুনির্দিষ্ট ভাবে এই নন-ক্যাপচারিং গ্রুপ মুডে কাজ করে। যাই হোক, নন-ক্যাপচারিং মুডে উপরের প্যাটার্নটি হবে

var str = 'Mr. Alu and mr. Potol are friends since 2008. They study at the same university in the session 2008-2009.';
var pattern = /(?:Mr\.|mr\.)\s\w+\s/g;
var matches = str.match(pattern);
console.log(matches);

/* Output in console*/
["Mr. Alu ", "mr. Potol "]

রেগুলার এক্সপ্রেশনে প্রথম বন্ধনি এবং এর ভিতরের সাব-প্যাটার্নের আরো কিছু ব্যবহার আছে। যেমন, কখনো কখনো আমাদের প্রত্যাশিত মিলটি বের করার জন্যে স্ট্রিং-এর সামনে তাকিয়ে দেখতে হয় যে আমরা যেই রকম ম্যাচিং চাচ্ছি সেটা আছে কিনা অর্থাৎ আমরা কন্ডিশনালি পরীক্ষা করে সামনে এগুতে চাই। ধরা যাক, আমরা আমাদের গিনিপিগ স্ট্রিং-এ শিক্ষাবর্ষের প্রথম বছরটি বের করতে চাই, অন্য বছরগুলো নয়।  শিক্ষাবর্ষ মানে দুটি বছর একটি ড্যাশ দিয়ে আলাদা করা যেমন, শিক্ষাবর্ষ 2008-2009 এ আমরা 2008 কে বের করতে চাচ্ছি। তো আমাদের নিশ্চিত হতে হতে যে 2008 এর পরে একটি ড্যাশ এবং ঠিক তার পরে আরেকটি বছর আছে। তার মানে আমরা যদি স্ট্রিং -এ  কোনো বছর পাই তাহলে আমরা সামনে তাকিয়ে দেখবো ঠিক এর পরে কোনো ড্যাশ এবং আরেকটি বছর আছে কিনা কিন্তু এই ড্যাশ এবং বছরটিকে আমরা আমাদের প্রাপ্ত ম্যাচিং বা মিলে চাচ্ছি না। আমরা শুধু দেখতে চাচ্ছি বেটা আছে কি নাই। রেগুলার এক্সপ্রেশনে এই ধরনের শর্ত দিতে হয় প্রথম বন্ধনির পরে ?= দিয়ে যা দেখতে অনেকটা নন-ক্যাপচারিং গ্রুপের মতো যেখানে ?:  পরিবর্তে ?= দিতে হয়। রেগুলার এক্সপ্রেশনে একে বলে লুক-অ্যাহেড (lookahead) । সুতারাং প্যাটার্নটি লিখা যাক

var str = 'Mr. Alu and mr. Potol are friends since 2008. They study at the same university in the session 2008-2009.';
var pattern = /\d{4}(?=-\d{4})/g;
var matches = str.match(pattern);
console.log(matches);

/* Output in console*/
["2008"]

আমরা দেখতে পাচ্ছি আমাদের প্রাপ্ত ফলাফলের ম্যাচিং বা মিলে -2009 নাই কারণ লুকঅ্যাহেড প্যাটার্নটি শুধুই শর্তটি যাচাই করে কিন্তু ফলাফলে প্রাপ্ত ম্যাচিংকে প্রভাবিত করে না। লুকঅ্যাহেডের ঠিক বিপরীত হচ্ছে নেগেটেড লুকঅ্যাহেড (negated lookahead) যা শুরু করতে হয় প্রথম বন্ধনির পরে ?! দিয়ে যা কাজ করে লুক-অ্যাহেডের ঠিক উল্টা নিয়মে। প্রথম বন্ধনির ব্যবহারের একটা তালিকা এক নজরে দেখে নেইঃ

নিয়মনামপ্রাপ্ত ম্যাচিংএ প্রভাব
(PATTERN)ক্যাপচারিং গ্রুপ  হ্যাঁ
(?:PATTERN) নন-ক্যাপচারিং গ্রুপ হ্যাঁ
(?=PATTERN)লুকঅ্যাহেড না
(?!PATTERN)নেগেটেড লুকঅ্যাহেড না

রেগুলার এক্সপ্রেশনে আরও কিছু বিশেষ ক্যারেক্টার আছে যেগুলোও প্রাপ্ত ম্যাচিং এর উপর কোনো প্রভাব না ফেলে কাজ করে যাদেরকে পজিশনিং ক্যারেক্টার বলে। যেমন,  স্ট্রিং-এর শুরু কিংবা শেষ অবস্থান।  উদাহরণস্বরূপ, যদি আমরা আমাদের গিনিপিগ স্ট্রিং-এর  শুরুতে যে Mr. টি আছে শুধু সেটি বের করতে চাই। তাহলে প্যাটার্নটি হবে

var str = 'Mr. Alu and mr. Potol are friends since 2008. They study at the same university in the session 2008-2009.';
var pattern = /^Mr./g;
var matches = str.match(pattern);
console.log(matches);

/* Output in console*/
["Mr."]

শুরু বুঝাতে বিশেষ ক্যারেক্টারটি হচ্ছে ^ এবং শেষ বুঝাতে বিশেষ ক্যারেক্টারটি হচ্ছে $। অবস্থান সম্পর্কিত আরেকটি বিশেষ নিয়ম হচ্ছে \b যাকে ওয়ার্ড বাউন্ডারি বলে। ওয়ার্ড-বাউন্ডারি হচ্ছে এমন একটি পজিশন বা অবস্থান যেখানে একটি ওয়ার্ড ক্যারেক্টারের ঠিক পরে একটি নন-ওয়ার্ড ক্যারেক্টার থাকে। যেমন, অ্যালফাবেটের পরে স্পেস। শুধু মনে রাখতে হবে কোনো পজিশনিং ক্যারেক্টারই প্রাপ্ত ম্যাচিং-এর উপর প্রভাব ফেলে না।

আমরা মোটামুটি রেগুলার এক্সপ্রেশনের সবগুলা নিয়মই আলোচনা করে ফেলেছি। লেখাটিও শেষ করা দরকার।  একটি বিষয় বলে শেষ করবো। প্রথম লেখায় আমরা যে কোয়ান্টিফায়ারগুলো দেখেছি, সেগুলো সবগুলোই লোভী বা গ্রিডি অর্থাৎ ম্যাচিংটি যতোটা সম্ভব বড় করে। যেমন, 20.* প্যাটার্নটির মানে হচ্ছে 20 এর পরে যেকোনো ক্যারেক্টার অসংখ্য বার থাকবে। এটি যদি আমাদের গিনিপিগ স্ট্রিং-এ প্রয়োগ করি তাহলে প্রাপ্ত ম্যাচিং-এর সংখ্যা হবে একটি এবং ম্যাচিংটি হবে দীর্ঘ 008. They are studying in same university in the session 2008-2009. আমরা চাইলে গ্রিডি থেকে নন-গ্রিডি বা লেজি করে দিতে পারি, তার ফলে প্রাপ্ত ম্যাচিং-এর দৈর্ঘ্য ছোট করে ম্যাচিং-এর সংখ্যা যতগুলো সম্ভব বের করবে। গ্রিডি থেকে নন-গ্রিডি করতে কোয়ান্টিফায়ার ক্যারেক্টারের পরে একটি অতিরিক্ত প্রশ্নবোধক চিহ্ন ? দিতে হবে। যেমন, 20.* এর নন-গ্রিডি রূপ হচ্ছে 20.*?  অনুরুপভাবে, কোয়ান্টিফায়ার ক্যারেক্টার + এর পরে ? বসিয়ে গ্রিডি থেকে নন-গ্রিডি করতে পারি।

/* Greedy mode */
var str = 'Mr. Alu and mr. Potol are friends since 2008. They study at the same university in the session 2008-2009.';
var pattern = /20.*/g;
var matches = str.match(pattern);
console.log(matches);

/* Output in console*/
["2008. They study at the same university in the session 2008-2009."]

/* Non greedy or lazy mode */
var str = 'Mr. Alu and mr. Potol are friends since 2008. They study at the same university in the session 2008-2009.';
var pattern = /20.*?/g;
var matches = str.match(pattern);
console.log(matches);

/* Output in console*/
["20", "20", "20"]

অবশেষে সংক্ষিপ্ত রেগুলার এক্সপ্রেশনের লেখাটি শেষ হলো। ধন্যবাদ।

রেগুলার এক্সপ্রেশন -এ শুরু।

রেগুলার এক্সপ্রেশন যেমন সহজ আবার একটু বিদঘুটেও। আমি অনেককেই দেখেছি সব কাজ পারে কিন্তু রেগুলার এক্সপ্রেশনের ব্যপার আসলে গুগলে খুঁজে অথবা ফোরামে পোষ্ট দেয়। যেই কাজটা খুব সহজেই এক লাইনে করা যায় রেগুলার এক্সপ্রেশন দিয়ে কিন্তু তাই আমরা ঘুরিয়ে কয়েক লাইনে করি রেগুলার এক্সপ্রেশন বাদ দিয়ে। আমার মতে রেগুলার এক্সপ্রেশন শিখতে যতটা না কষ্টের তার চেয়ে বেশি কষ্ট ধৈর্য ধরে শেষ পর্যন্ত পড়া। তাই চিন্তা করলাম খুব সংক্ষেপে রেগুলার এক্সপ্রেশনের শুরু থেকে শেষ নিয়ে একটি লেখা লিখতে। তো, শুরু করা যাক রেগুলার এক্সপ্রেশন।

রেগুলার এক্সপ্রেশন ব্যপারটা কি? সহজ কথায় রেগুলার এক্সপ্রেশন হচ্ছে কতোগুলা প্যাটার্ন বা বর্ণ বিন্যাস যার মাধ্যমে আমরা স্ট্রিং থেকে আমাদের প্রত্যাশিত সাব-স্ট্রিং বা ক্ষুদ্র অক্ষর বিন্যাস খুঁজে বের করতে পারি। কিছু ব্যতিক্রম বাদে মোটামুটি সব প্রোগ্রামিং ভাষায় রেগুলার এক্সপ্রেশনের নিয়মকানুন একই। এই লিখায় আমি মূলত জাভাস্ক্রিপ্ট ব্যবহার করবো। নিচের স্ট্রিং-কে আমরা গিনিপিগ হিসেবে নিলাম।

Mr. Alu and mr. Potol are friends since 2008. They study at the same university in the session 2008-2009.

প্রথমে উক্ত গিনিপিগ স্ট্রিং-এ কোনো M উপস্থিত আছে কিনা সেটা বের করি। সেটা বের করার জন্যে আমাদের রেগুলার এক্সপ্রেশনের একটা প্যাটার্ন লাগবে যার মাধ্যমে আমরা আমাদের গিনিপিগ স্ট্রিং কে যাচাই করবো।  জাভাস্কিপ্ট-এ রেগুলার এক্সপ্রেশন এক ধরনের অবজেক্ট এবং শুধু দুটি ফরোয়ার্ড স্ল্যাসের অর্থাৎ // মধ্যেই প্যাটার্নটি লিখা যায়। যদিও জাভাস্কিপ্ট-এ আরও কয়েকভাবে প্যাটার্নটি লিখা যায়, আমি ওই সব বিষয়ে বিস্তারিত লিখছি না। আচ্ছা আমি যে বললাম, রেগুলার এক্সপ্রেশন দিয়ে স্ট্রিং-এ আমাদের প্রত্যাশিত সাব-স্ট্রিং খুঁজে বের করা যায় কিন্তু খুঁজে বের করে করবোটা কি? আসলে আমরা অনেক স্ট্রিং অপারেশান-ই  করতে পারি যেমন খুঁজে বের করে সাব-স্ট্রিংকে রিপ্লেস বা পরিবর্তন করতে পারি অথবা শুধু চেক করে দেখতে পারি বেটা আছে কি নাই অথবা খুঁজে বের করে রাখতে পারি পরে কোথাও ব্যবহার করার জন্যে। আমি জাভাস্কিপ্টের ফাংশন match ব্যবহার করবো যার কাজ হছে শেষের কাজটি করা অর্থাৎ খুঁজে বের করে দেওয়া।

রেগুলার এক্সপ্রেশনের গুরুত্বপূর্ণ যে বিষয়টি খেয়াল রাখতে হবে সেটি হচ্ছে এটি কাজ করে ক্যারেক্টার বাই ক্যারেক্টার অর্থাৎ প্যাটার্ন এর যদি প্রথম ক্যারেক্টার-এ মিল পায় তবেই শুধু এর (প্যাটার্নটির) দ্বিতীয় ক্যারেক্টার নিয়ে অগ্রসর হবে অন্যথায় আবার প্যাটার্নটির প্রথম ক্যারেক্টার থেকে শুরু করবে । যেহেতু আমরা শুধু একটি মাত্র ক্যারেক্টার M কে চেক করবো এবং এর আগে পরে কিছু নাই সুতরাং আমাদের প্যাটার্নটি হবে খুবই সহজ অর্থাৎ শুধু M

var str = 'Mr. Alu and mr. Potol are friends since 2008. They study at the same university in the session 2008-2009.';
var pattern = /M/;
var matches = str.match(pattern);
console.log(matches);

/* Output in console*/
 ["M"]

অনুরপভাবে যদি Mr কে বের করতে চাই তবে প্যাটার্নটি হবে Mr অথবা 2008 কে বের করার জন্যে প্যাটার্নটি 2008। যেহেতু ক্যারেক্টার বাই ক্যারেক্টার মিল খুঁজে, তাই Mr প্যাটার্নটির ক্ষেত্রে প্রথমে M কে খুঁজবে, যদি পেয়ে যায় তবে আমাদের গিনিপিগ স্ট্রিং-এ প্যাটার্নটির পরবর্তী ক্যারেক্টার r কে খুঁজবে ঠিক M-এর পরে। যদি মিল পেয়ে যায় তাহলে কাজ শেষ। যদি মিল না পায় তাহলে আবার প্রথম থেকে প্যাটার্নটির M দিয়ে শুরু করবে গিনিপিগ স্ট্রিং-এর বাকি ক্যারেক্টারগুলোকে চেক করার জন্যে এবং এভাবে চলতে থাকে গিনিপিগ স্ট্রিং-এর শেষ ক্যারেক্টার পর্যন্ত।  সাধারণভাবে কিছু বিশেষ ক্যারেক্টারবাদে প্রতিটি ক্যারেক্টারকে লিটার‍্যালি বা আক্ষরিকভাবেই বিবেচনা করা হয় যাদের নিয়ে আমরা ধীরে ধীরে আলোচনা করবো।

আচ্ছা, আমাদের গিনিপিগ স্ট্রিং-এ কোনো বছর আছে কিনা আমরা দেখতে চাই। আমরা ধরে নেই, বছর মানে চারটা সংখ্যা পাশাপাশি থাকবে কিন্তু ক্যারেক্টারটি যে সংখ্যা তা ডিফাইন করবো কিভাবে? খুব সহজ, গণিতে যেই রকম আমরা সেট ব্যবহার করি, ঠিক সেই রকম আমরা ক্যারেক্টার সেট বলে দিতে পারি। যাকে রেগুলার এক্সপ্রেশন-এ ক্যারেক্টার সেট বা ক্যারেক্টার ক্লাস বলে। ক্যারেক্টার সেট ডিক্লায়ের করার নিয়ম হলো স্কোয়ার অর্থাৎ [] বন্ধনীর ভিতরে প্রয়োজনীয় ক্যারেক্টারগুলো দিয়ে দেয়া। যেমন,  [0123456789] মানে সংখ্যার সেট, BCDEF] হলো ABCDEF-এর সেট। চাইলে আমরা রেঞ্জ দিয়ে সংক্ষেপে লিখেতে পারি। রেঞ্জ বুঝানোর জন্যে ড্যাশ (-) ব্যবহার করা হয়। যেমন, [0-9] মানে সংখ্যার সেট যা [0123456789] এর অনুরুপ অথবা -zA-Z] মানে ইংরেজি অ্যালফাবেটের সেট। সেপারেটর হিসেবে কমা দেবার প্রয়োজন নাই। আবার নেগেটিভ সেটও তৈরি করা যায় শুরুতে একটা ^ বসিয়ে দিয়ে। যেমন, [^0-9] মানে সংখ্যা বাদে সকল কিছুর সেট।

শুধু মনে রাখতে হবে রেগুলার এক্সপ্রেশন-এ সেটটি একটি একক ক্যারেক্টার হিসেবে বিবেচনা করা হবে অর্থাৎ ক্যারেক্টার সেট মূলত অন্যান্য সাধারণ ক্যারেক্টার মতই জায়গা দখল করবে। তো বের করে ফেলি আমাদের বছর বের করার প্যাটার্ন। আমাদের দরকার পর পর চারটা সংখ্যা অর্থাৎ আমাদের সংখ্যার সেটটি পর পর চার বার দিলেই হবে। তার মানে প্যাটার্নটি হলো  [0-9][0-9][0-9][0-9]

var str = 'Mr. Alu and mr. Potol are friends since 2008. They study at the same university in the session 2008-2009..';
var pattern = /[0-9][0-9][0-9][0-9]/;
var matches = str.match(pattern);
console.log(matches);

/* Output in console*/
 ["2008"]

উপড়ের প্যাটার্নটিতে কিসের যেন অভাব!  একই জিনিষ চারবার করে লিখা, দেখতে ভালো লাগছে না। একটু ঘষামাজা করা দরকার আর ঘষামাজা করার জন্যে যা লাগবে, তাকে রেগুলার এক্সপ্রেশন -এ বলে কোয়ান্টিফায়ার। এই কোয়ান্টিফায়ার হচ্ছে কতগুলো স্পেশাল ক্যারেক্টার যা দিয়ে আমরা একই জিনিস বার বার না লিখে তার পরিবর্তে কোয়ান্টিফায়ার ক্যারেক্টার দিয়ে দিতে পারি। যেমন, * মানে ০ থেকে অগণিত, + মানে ১ থেকে অগণিত, ? মানে ০ থেকে ১ ইত্যাদি। আবার আমরা চাইলে নিজেরাও রেঞ্জের মান বলে দিতে পারি। রেঞ্জের মান ডিফাইন করার নিয়ম হচ্ছে কারলি অর্থাৎ {} ব্রাকেটের মধ্যে শুরু ও শেষ বলে দেয়া। যেমন, {0,4} মানে হচ্ছে ০ থেকে চার, {1,5} মানে হচ্ছে ১ থেকে পাঁচ ইত্যাদি। মজার ব্যপার হচ্ছে রেঞ্জের শেষের মান না দিলে তার মান হবে অগণিত। যেমন, {0,} মানে হচ্ছে ০ থেকে অগণিত। আবার যদি রেঞ্জ না দিয়ে শুধুমাত্র একটি মান দেই তাহলে তার মানে দাড়ায় ঠিক ততটা। যেমন, {2} মানে হচ্ছে ঠিক দুইটা। প্রকৃতপক্ষে কোয়ান্টিফায়ার ক্যারেক্টারগুলো অর্থাৎ *,+,? কোয়ান্টিফায়ার রেঞ্জের সংক্ষিপ্তরূপ। যেমন, {1,} এর সমান হচ্ছে + আবার {o,} এর সমান হচ্ছে *। নিচের কোয়ান্টিফায়ারের তালিকাটি খেয়াল করলে আরও পরিস্কার বোঝা যাবেঃ

কোয়ান্টিফায়ারমানবিকল্প
*০ থেকে অগণিত {0,}
+১ থেকে অগণিত {1,}
?০ থেকে ১ অর্থাৎ থাকতে পারে আবার নাও পারে{০,1}
{1,5}১ থেকে পাঁচ 
{5}ঠিক পাঁচটা 

এবার তাহলে কোয়ান্টিফায়ার ব্যবহার করে বছর বের করার প্যাটার্নটি লিখা যাক যা দেখতে আগের চেয়ে অনেক ভাল লাগবে।

var str = 'Mr. Alu and mr. Potol are friends since 2008. They study at the same university in the session 2008-2009.';
var pattern = /[0-9]{4}/;
var matches = str.match(pattern);
console.log(matches);

/* Output in console*/
 ["2008"]

আমি জানি এখন একটি ব্যাপার অনেকের মাথায় ঘুরছে সেটা হলো যদি বিল্ট-ইন কোয়ান্টিফায়ারের সংক্ষিপ্তরূপ মানে * ,+ ইত্যাদি থাকে, তাহলে বিল্ট-ইন ক্যারেক্টার সেট বা ক্যারেক্টার ক্লাস থাকা উচিত কেননা সংখ্যার সেট বা অ্যালফাবেটিক  সেট এরকম কিছু সেট আছে যেগুলা আগে থেকেই নির্দিষ্ট। তাদের ধারনা একদম ঠিক, কোয়ান্টিফায়ারের মতো কিছু বিল্ট-ইন ক্যারেক্টার সেট আছে যা শুরু হয় ব্যাকওয়ার্ড স্লাস অর্থাৎ \।  যেমন, \d মানে সংখ্যার সেট, \w মানে অ্যালফাবেটিক ও আন্ডারস্কোর (_) এর সেট ইত্যাদি। নিচের একটি সংক্ষিপ্ত তালিকা দিয়ে দিলামঃ

ক্যারেক্টার সেটক্যারেক্টারসমূহবিকল্প
\dসকল সংখ্যা [0-9]
\D সংখ্যা বাদে সকল কিছুর সেট [^0-9]
\wঅ্যালফাবেটিক ও আন্ডারস্কোর (_) এর সেট [a-zA-Z_]
\Wঅ্যালফাবেটিক ও আন্ডারস্কোর (_) বাদে সেট [^a-zA-Z_]
\s হোয়াইট স্পেস ক্যারেক্টারসমূহ যেমন স্পেস, ট্যাব 
\Sহোয়াইট স্পেস বাদে সকল কিছুর সেট 
 \tশুধু ট্যাব এর সেট 

বিল্ট-ইন ক্যারেক্টার সেট ব্যবহার করে এখন আমরা আমদের বছর বের করার প্যাটার্নটি আরও স্মার্ট ও সংক্ষিপ্ত করে ফেলি যা হবে নিম্নরূপঃ

var str = 'Mr. Alu and mr. Potol are friends since 2008. They study at the same university in the session 2008-2009.';
var pattern = /\d{4}/;
var matches = str.match(pattern);
console.log(matches);

/* Output in console*/
 ["2008"]

লেখাটি আর সংক্ষেপ থাকছে না তাই এখানেই শেষ করছি। এখনো রেগুলার এক্সপ্রেশনের আরও কিছু জিনিস বাকি আছে যেমন, গ্রিডি/নন-গ্রিডি কোয়ান্টিফায়ার, মডিফায়ার, ক্যাপচারিং গ্রুপ, লুক অ্যাহেড এবং কিছু পজিশনিং ক্যারেক্টার যা পরবর্তী লেখাতে আলোচনা করবো। তবে শেষ করবো একটি বিশেষ ক্যারেক্টার দিয়ে, যেটি হচ্ছে ডট (.)। রেগুলার এক্সপ্রেশনে ডট মানে বুঝায় নতুন লাইন বাদে যেকোনো ক্যারেক্টার । যেমন, a. মানে, a এর পরে যেকোনো একটি ক্যারেক্টার বুঝায়।  তাহলে .* দিয়ে কি বুঝায় তা ভাবতে দিয়ে আমি বিদায় নিলাম।

দ্বিতীয় পর্বের লিঙ্ক