«عبارات باقاعده» روشی بسیار انعطافپذیر برای تطبیق الگو روی رشتهها هستند که از یک زبان دامنهویژه (DSL) طراحيشده برای همین منظور استفاده میکنند.
Julia هم مانند چندین زبان برنامهنویسی دیگر تلاشی نمیکند که کتابخانهی Regex خودش را پیادهسازی کند. در عوض، کتابخانهی پرکاربرد PCRE2 را در بر میگیرد و بدین ترتیب نحوهی نگارش Regex یکسانی با (برای مثال) Gleam و بسیار شبیه به Javascript ارائه میدهد.
این برنامهی درسی Julia فرض میکند که شما از قبل با نحوهی نگارش پایهی Regex آشنا هستید. ما فقط روی ویژگیهای مخصوص Julia تمرکز خواهیم کرد.
چند منبع برای تازهکردن دانش عبارات باقاعدهی شما در ادامه فهرست شده است.
رابط Julia با عبارات باقاعده در راهنما توضیح داده شده است.
یک عبارت باقاعده در Julia به سادگی رشتهای است که پیش از " آغازین، یک r دارد.
همهی قابلیتهای پایه بخشی از کتابخانهی استاندارد هستند.
در واقع، بسیاری از توابعی که پیشتر در مفهوم Strings بررسی کردیم، بهطور پیشفرض برای جستوجوهای Regex طراحی شدهاند، مانند 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")
در Julia، 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"
یکی از دلایل رایج استفاده از Regex، جایگزین کردن تطبیق با رشتهای دیگر است.
تابع replace() در مفهوم Strings بررسی شد، آنجا که از رشتههای متنی برای جستوجو استفاده میکردیم.
همین تابع میتواند از تمام توان تطبیق Regex بهره ببرد.
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"