রেগুলার এক্সপ্রেশন হলো স্ট্রিং প্যাটার্ন ম্যাচ করার একটি অত্যন্ত বহুমুখী উপায়, যার জন্য এ কাজের জন্যই বিশেষভাবে তৈরি একটি ডোমেইন স্পেসিফিক ভাষা (DSL) ব্যবহার করা হয়।
অন্যান্য অনেক প্রোগ্রামিং ভাষার মতো জুলিয়া নিজস্ব রেগেক্স লাইব্রেরি তৈরি করার কোনো চেষ্টা করে না। বরং এটি জনপ্রিয় PCRE2 লাইব্রেরিকে ঘিরে তৈরি, ফলে যে রেগেক্স সিনট্যাক্স পাওয়া যায় তা (উদাহরণস্বরূপ) Gleam-এর মতোই এবং Javascript-এর সাথে খুব কাছাকাছি।
এই জুলিয়া সিলেবাসে ধরে নেওয়া হয়েছে যে আপনি বেসিক রেগেক্স সিনট্যাক্সের সাথে ইতিমধ্যেই পরিচিত। আমরা কেবল জুলিয়ার নিজস্ব বৈশিষ্ট্যগুলো নিয়েই আলোচনা করব।
আপনার রেগুলার এক্সপ্রেশনের জ্ঞান ঝালিয়ে নেওয়ার জন্য কিছু রিসোর্স নিচে দেওয়া হলো।
জুলিয়ায় রেগুলার এক্সপ্রেশনের ইন্টারফেস নিয়ে আলোচনা করা হয়েছে ম্যানুয়ালে।
জুলিয়ায় একটি রেগুলার এক্সপ্রেশন আসলে একটি সাধারণ স্ট্রিং, যার সামনে শুরুর "-এর আগে একটি r বসানো থাকে। এর সব বেসিক ফাংশনালিটি স্ট্যান্ডার্ড লাইব্রেরির অংশ।
আসলে Strings কনসেপ্টে আগেই আলোচনা করা অনেক ফাংশনই মূলত রেগেক্স সার্চের জন্য তৈরি, যেমন occursin().
julia> re = r"test$"
r"test$"
julia> typeof(re)
Regex
# Does a string end with "test"?
julia> occursin(re, "this is a test")
true
julia> occursin(re, "these are tests")
false
ক্লোজিং কোটের পরে মডিফায়ার ক্যারেক্টার বসতে পারে, যেমন বড়-ছোট হাতের অক্ষরের পার্থক্য না ধরে ম্যাচ করার জন্য i.
julia> occursin(r"test", "Testing")
false
julia> occursin(r"test"i, "Testing")
true
সাধারণত আমরা জানতে চাই কী ম্যাচ করেছে। এর জন্য রেগেক্সের ভেতরে বন্ধনীর মধ্যে ক্যাপচার গ্রুপ রাখতে হয়, তারপর match() ফাংশন ব্যবহার করতে হয়।
julia> m = match(r"(\d+g) .* (\d+ml)", "dissolve 25g sugar in 200ml water")
RegexMatch("25g sugar in 200ml", 1="25g", 2="200ml")
julia> m.captures
2-element Vector{Union{Nothing, SubString{String}}}:
"25g"
"200ml"
# how many matches?
julia> length(m.captures)
2
# what matched?
julia> m[1], m[2]
("25g", "200ml")
# Starting positions of the matches (character index)
julia> m.offsets
2-element Vector{Int64}:
10
23
অবশ্যই, ম্যাচ ব্যর্থ হতে পারে। তখন ফলাফল হিসেবে RegexMatch-এর বদলে বিশেষ মান Nothing পাওয়া যাবে, তাই এটি পরীক্ষা করার জন্য প্রস্তুত থাকুন।
# failed match
m = match(r"(not here)", "dissolve 25g sugar in 200ml water")
julia> typeof(m)
Nothing
julia> isnothing(m)
true
match ডিফল্টভাবে স্ট্রিংয়ের শুরু থেকে খোঁজা শুরু করে, তবে আমরা একটি অফসেট n-ও দিতে পারি, যাতে প্রথম nটি ক্যারেক্টার বাদ দেওয়া হয়।
# capture first match
julia> m = match(r"(\wat)", "cat, sat, mat")
RegexMatch("cat", 1="cat")
# ignore first 5 characters, then match
julia> m = match(r"(\wat)", "cat, sat, mat", 5)
RegexMatch("sat", 1="sat")
জুলিয়ায় match() টার্গেট স্ট্রিংয়ের ভেতরে শুধু প্রথম ম্যাচটিই খুঁজে বের করে: অন্য কিছু ভাষার মতো গ্লোবাল মডিফায়ার এখানে নেই।
এর বদলে আছে eachmatch(), যা ম্যাচগুলোর একটি ইটারেটর রিটার্ন করে। এটি লেজিভাবে ইভালুয়েট হয়, তাই আপনার পছন্দের ফরম্যাটে রূপান্তর করার প্রয়োজন হতে পারে।
julia> matches = eachmatch(r"(\wat)", "cat, sat, mat")
Base.RegexMatchIterator{String}(r"(\wat)", "cat, sat, mat", false)
# convert to vector
julia> collect(matches)
3-element Vector{RegexMatch}:
RegexMatch("cat", 1="cat")
RegexMatch("sat", 1="sat")
RegexMatch("mat", 1="mat")
# convert with comprehension
julia> [m.match for m in matches]
3-element Vector{SubString{String}}:
"cat"
"sat"
"mat"
# broadcast an anonymous function
julia> (m -> m.match).(matches)
3-element Vector{SubString{String}}:
"cat"
"sat"
"mat"
রেগেক্স ব্যবহারের একটি কমন কারণ হলো ম্যাচটি ভিন্ন একটি স্ট্রিং দিয়ে প্রতিস্থাপন করা।
Strings কনসেপ্টে replace() ফাংশন নিয়ে আলোচনা করা হয়েছিল, যেখানে খোঁজার জন্য স্ট্রিং লিটারেল ব্যবহার করা হয়। একই ফাংশন রেগেক্স ম্যাচিংয়ের পূর্ণ শক্তি কাজে লাগাতে পারে।
julia> replace("some string", r"[aeiou]" => "*")
"s*m* str*ng"
julia> replace("first second", r"(\w+) (?<agroup>\w+)" => s"\g<agroup> \1")
"second first"
উপরের দ্বিতীয় উদাহরণটি দেখায় যে একটি s" " স্ট্রিংয়ের ভেতরে প্রতিস্থাপনে নাম্বারযুক্ত ও নামযুক্ত, দুই ধরনের ক্যাপচার গ্রুপই ব্যবহার করা যায়।
আরও বিস্তারিত জানতে ম্যানুয়াল দেখুন: এই বিষয়টি এমন যে বেশিরভাগ প্রোগ্রামারকে বারবার ডকুমেন্টেশনে ফিরে যেতে বাধ্য করে!
এমন একটি কোম্পানিতে আপনাকে রেগুলার এক্সপ্রেশন স্পেশালিস্ট হিসেবে নিয়োগ দেওয়া হয়েছে, যারা একটি চ্যাটবট তৈরি করছে।
এটি এখনো উন্নয়নের একেবারে প্রাথমিক পর্যায়ে আছে, তাই আপনার কাজ হলো রেগুলার এক্সপ্রেশন ব্যবহার করে প্রাকৃতিক ভাষা বোঝা ও তৈরি করার ক্ষেত্রে চ্যাটবটের দক্ষতা বাড়ানো।
শেখার উদ্দেশ্যে, অনুগ্রহ করে এই কাজগুলো রেগুলার এক্সপ্রেশন ব্যবহার করে সমাধান করুন।
আমরা জানি, বিকল্প উপায় কখনো কখনো সম্ভব (এমনকি আরও কার্যকরও হতে পারে!)
বুদ্ধিমান হওয়ার পাশাপাশি চ্যাটবট একজন বিশ্বস্ত সহকারীও বটে।
চ্যাটবটকে কিছু জিজ্ঞাসা করার জন্য ব্যবহারকারীকে কমান্ডের প্রথম অবস্থানে “Chatbot” শব্দটি বলতে হবে।
কিওয়ার্ডটি বড় হাতের অক্ষরে হোক বা ছোট হাতের অক্ষরে, তাতে কিছু যায় আসে না। এখানে গুরুত্বপূর্ণ হলো শব্দটির অবস্থান।
is_valid_command() ফাংশনটি তৈরি করুন, যা ব্যবহারকারী কখন কমান্ড দিচ্ছে তা চ্যাটবটকে চিনতে সাহায্য করে।
julia> is_valid_command("Chatbot, play a song from the 80's.")
True
julia> is_valid_command("Hey Chatbot, where is the closest pharmacy?")
False
julia> is_valid_command("CHATBOT, do you have a solution for this challenge?")
True
মানুষ কীভাবে নিজেদের অনুভূতি প্রকাশ করতে ইমোজি ব্যবহার করে, তা বুঝতে চ্যাটবটের বেশ কষ্ট হয়।
চ্যাটবটের কাছে ব্যবহারকারীর মেসেজ পৌঁছালে প্রতিটি ইমোজি “emoji” হিসেবে লেখা থাকে, তারপর থাকে তার id নম্বর।
একটি স্ট্রিং নিয়ে মেসেজের সব জায়গা থেকে ইমোজির এনক্রিপশন সরিয়ে দেওয়ার জন্য remove_emoji() মেথডটি তৈরি করুন।
যেসব লাইনে ইমোজির লেখা নেই, সেগুলো অপরিবর্তিত অবস্থায় রিটার্ন করা উচিত।
শুধু ইমোজি স্ট্রিংটি সরিয়ে দিন। হোয়াইটস্পেস ঠিক করার চেষ্টা করবেন না।
julia> remove_emoji("I love playing videogames emoji3465 it's one of my hobbies")
"I love playing videogames it's one of my hobbies"
মোবাইল অ্যাপে চ্যাটবটের ফিচার ডাউনলোড করার কথা ভেবে, কথোপকথনের সময় ব্যবহারকারীর কাছ থেকে একটি ফোন নম্বর লেখার আশা করা হয়।
সমস্যা হলো, চ্যাটবট কেবল একটি নির্দিষ্ট ফরম্যাটের নম্বর পড়তে ও যাচাই করতে পারে।
নম্বরটি যদি বৈধ হয় (রেগুলার এক্সপ্রেশন অনুযায়ী নির্দিষ্ট ক্যারেক্টার সিকোয়েন্সের সাথে মেলে), তাহলে চ্যাটবট ব্যবহারকারীকে ধন্যবাদ জানিয়ে এবং নম্বরটি নিশ্চিত করে একটি বার্তা দেয়। নম্বরটি যদি অবৈধ হয়, তাহলে ফাংশনটি ব্যবহারকারীকে জানায় যে ফোন নম্বরটি বৈধ নয়।
প্রত্যাশিত ফরম্যাট হলো: (+##) ###-###-###.
julia> check_phone_number("(+34) 659-771-594")
"Thanks! You can now download me to your phone."
julia> check_phone_number("659-771-594")
"Oops, it seems like I can't reach out to 659-771-594"
চ্যাটবট সত্যিই একটি কৌতূহলী সফটওয়্যার। কোনো নির্দিষ্ট বিষয়ে সে ইন্টারনেটে সার্চ করতে পারলেও, প্রাসঙ্গিক তথ্য খুঁজে বের করার জন্য দারুণ সব ওয়েবসাইট বা URL-এর কথা ব্যবহারকারীদের জিজ্ঞাসা করতে ভালোবাসে।
কথোপকথনের উদাহরণ:
getURL() ফাংশনটি তৈরি করুন, যা প্রতিটি ওয়েবসাইটের শুধু লিংক নিয়ে একটি অ্যারে রিটার্ন করতে পারে।
julia> getURL("I learned a lot from exercism.org")
["exercism.org"]
যেসব ব্যক্তি চ্যাটবটের সাথে কথা বলেছেন, তাঁদের সবার ডেটা সংরক্ষণ করার জন্য চ্যাটবট ব্যবহারকারীর প্রোফাইল থেকে এই ধরনের পূর্ণ নাম নিতে পারে: “smith, john”।
এভাবেই আমরা চ্যাটবটকে সত্যিই ভদ্র করে গড়ে তুলতে চাই, যাতে সে ভালো ছাপ ফেলে।
nice_to_meet_you() ফাংশনটি লিখুন, যা ব্যবহারকারীর পূর্ণ নামসহ একটি স্ট্রিং নেয় এবং “Nice to meet you, John Smith” স্ট্রিংটি রিটার্ন করে
julia> str = "Smith, John"
julia> nice_to_meet_you(str)
"Nice to meet you, John Smith"
Exercism-এ সাইন আপ করুন, Julia ট্র্যাকের 35টি কনসেপ্ট128টি অনুশীলনী আর সত্যিকারের মানুষের মেন্টরিং দিয়ে শিখুন ও দক্ষ হয়ে উঠুন, সম্পূর্ণ বিনামূল্যে।