مسیرها
/
jq
jq
/
تمرین‌ها
/
چت‌بات باقاعده
چت‌بات باقاعده

چت‌بات باقاعده

تمرین یادگیری

مقدمه

عبارات باقاعده

عبارات باقاعده (رجکس‌ها) دنباله‌هایی از نویسه‌ها هستند که یک الگوی جست‌وجو در متن را مشخص می‌کنند.

یادگیری نحوه‌ی نگارش عبارات باقاعده فراتر از محدوده‌ی این مبحث است. ما روی عباراتی تمرکز می‌کنیم که jq برای استفاده از عبارات باقاعده فراهم می‌کند.

گونه‌ی عبارات باقاعده

ابزارهای مختلف نسخه‌های متفاوتی از عبارات باقاعده را پیاده‌سازی می‌کنند. jq کتابخانه‌ی عبارات باقاعده‌ی Oniguruma را به کار می‌گیرد که تا حد زیادی با عبارات باقاعده‌ی Perl v5.8 سازگار است.

نحوه‌ی نگارش خاصی که jq استفاده می‌کند را می‌توان در مخزن GitHub Oniguruma یافت.

Caution

jq هیچ نحوه‌ی نگارش خاصی برای عبارات باقاعده ندارد. آن‌ها به‌سادگی به‌صورت رشته بیان می‌شوند. این یعنی هر بک‌اسلش در عبارت باقاعده باید در رشته escape شود.

برای مثال، کلاس نویسه‌ی رقم (\d) باید به‌صورت "\\d" نوشته شود.

توابع عبارات باقاعده

عبارات باقاعده در jq محدود به مجموعه‌ای از فیلترها هستند.

تطبیق ساده

وقتی می‌خواهید بدانید آیا رشته‌ای با الگویی تطبیق پیدا می‌کند، از فیلتر test استفاده کنید.

STRING | test(REGEX)
STRING | test(REGEX; FLAGS)
STRING | test([REGEX, FLAGS])

این فیلتر یک نتیجه‌ی منطقی تولید می‌کند.

"Hello World!" | test("W")    # => true
"Goodbye Mars" | test("W")    # => false
اطلاعات درباره‌ی تطبیق

وقتی می‌خواهید زیررشته‌ای را استخراج کنید که واقعاً با الگو تطبیق پیدا کرده است، از فیلتر match استفاده کنید.

STRING | match(REGEX)
STRING | match(REGEX; FLAGS)
STRING | match([REGEX, FLAGS])

این فیلتر خروجی می‌دهد:

  • اگر تطبیقی وجود نداشته باشد، هیچ نتیجه‌ای، یا
  • اگر تطبیقی وجود داشته باشد، شیئی شامل ویژگی‌های مختلف.

این مثال با استفاده از نحوه‌ی نگارش ارجاع به عقب، \1، دو مصوت یکسان و پشت‌سرهم را جست‌وجو می‌کند.

"Hello World!" | match("([aeiou])\\1")
# => empty

"Goodbye Mars" | match("([aeiou])\\1")
# => {
#      "offset": 1,
#      "length": 2,
#      "string": "oo",
#      "captures": [
#        {
#          "offset": 1,
#          "length": 1,
#          "string": "o",
#          "name": null
#        }
#      ]
#    }

فیلتر match برای هر تطبیق یک شیء برمی‌گرداند. این مثال کاربرد پرچم "g" را برای یافتن همه‌ی مصوت‌ها نشان می‌دهد.

"Goodbye Mars" | match("[aeiou]"; "g")
# => { "offset": 1, "length": 1, "string": "o", "captures": [] }
#    { "offset": 2, "length": 1, "string": "o", "captures": [] }
#    { "offset": 6, "length": 1, "string": "e", "captures": [] }
#    { "offset": 9, "length": 1, "string": "a", "captures": [] }
زیررشته‌های ثبت‌شده

مشابه فیلتر match، فیلتر capture در صورت وجود تطبیق یک شیء برمی‌گرداند.

STRING | capture(REGEX)
STRING | capture(REGEX; FLAGS)
STRING | capture([REGEX, FLAGS])

شیء برگردانده‌شده نگاشتی از ثبت‌های نام‌دار است.

"JIRAISSUE-1234" | capture("(?<project>\\w+)-(?<issue_num>\\d+)")
# => {
#      "project": "JIRAISSUE",
#      "issue_num": "1234"
#    }
فقط زیررشته‌ها

فیلتر scan مشابه match با پرچم "g" است.

STRING | scan(REGEX)
STRING | scan(REGEX; FLAGS)

scan یک جریان از زیررشته‌ها خروجی می‌دهد.

"Goodbye Mars" | scan("[aeiou]")
# => "o"
#    "o"
#    "e"
#    "a"

از سازنده‌ی آرایه‌ی [...] برای ثبت زیررشته‌ها استفاده کنید.

"Goodbye Mars" | [ scan("[aeiou]") ]
# => ["o", "o", "e", "a"]
تقسیم یک رشته

اگر بخش‌هایی از رشته را می‌دانید که می‌خواهید نگه دارید، از match یا scan استفاده کنید. اگر بخش‌هایی را می‌دانید که می‌خواهید دور بیندازید، از split استفاده کنید.

STRING | split(REGEX; FLAGS)
Caution

فیلتر split با یک آرگومان آرگومانش را به‌عنوان یک رشته‌ی ثابت در نظر می‌گیرد.

برای استفاده از یک عبارت باقاعده با split، باید آرگومان دوم را ارائه کنید؛ استفاده از یک رشته‌ی خالی اشکالی ندارد.

مثالی که یک رشته را بر اساس فاصله‌ی سفید دلخواه تقسیم می‌کند.

"first   second           third fourth" | split("\\s+"; "")
# => ["first", "second", "third", "fourth"]
جانشینی‌ها

فیلترهای sub و gsub می‌توانند رشته‌ی ورودی را تغییر دهند و بخش‌های تطبیق‌یافته‌ی ورودی را با یک رشته‌ی جانشین جایگزین کنند.

برای جایگزینی فقط اولین تطبیق، از sub استفاده کنید. برای جایگزینی همه‌ی تطبیق‌ها، از gsub استفاده کنید.

STRING | sub(REGEX; REPLACEMENT)
STRING | sub(REGEX; REPLACEMENT; FLAGS)
STRING | gsub(REGEX; REPLACEMENT)
STRING | gsub(REGEX; REPLACEMENT; FLAGS)
"Goodnight kittens. Goodnight mittens." | sub("night"; " morning")
# => "Good morning kittens. Goodnight mittens."

"Goodnight kittens. Goodnight mittens." | gsub("night"; " morning")
# => "Good morning kittens. Good morning mittens."

متن جانشین می‌تواند به زیررشته‌های تطبیق‌یافته ارجاع دهد؛ از ثبت‌های نام‌دار و درون‌یابی رشته استفاده کنید.

"Some 3-letter acronyms: gnu, csv, png"
| gsub( "\\b(?<tla>[[:alpha:]]{3})\\b";     # find words 3 letters long
        "\(.tla | ascii_upcase)" )          # upper-case the match
# => "Some 3-letter acronyms: GNU, CSV, PNG"

پرچم‌ها

در همه‌ی فیلترهای بالا، FLAGS رشته‌ای است که از صفر یا چند مورد از پرچم‌های پشتیبانی‌شده تشکیل شده است.

  • g - جست‌وجوی سراسری (همه‌ی تطبیق‌ها را پیدا کنید، نه فقط اولین تطبیق)
  • i - جست‌وجوی بدون حساسیت به بزرگی و کوچکی حروف
  • m - حالت چندخطی ('.' با خطوط جدید تطبیق پیدا می‌کند)
  • n - تطبیق‌های خالی را نادیده بگیرید
  • p - هر دو حالت s و m فعال می‌شوند
  • s - حالت تک‌خطی (^ -> \A، $ -> \Z)
  • l - بلندترین تطبیق‌های ممکن را پیدا کنید
  • x - قالب گسترده‌ی عبارت باقاعده (فاصله‌ی سفید و کامنت‌ها را نادیده بگیرید)

برای مثال

"JIRAISSUE-1234" | capture("(?<project>\\w+)-(?<issue_num>\\d+)")

# or with Extended formatting

"JIRAISSUE-1234" | capture("
                     (?<project>   \\w+ )  # the Jira project
                     -                     # followed by a hyphen
                     (?<issue_num> \\d+ )  # followed by digits
                   "; "x")

دستورالعمل‌ها

شما به‌عنوان متخصص عبارت‌های باقاعده در شرکتی استخدام شده‌اید که در حال توسعه‌ی یک چت‌بات است.

این شرکت در مرحله‌ی بسیار ابتدایی توسعه قرار دارد. مأموریت شما این است که با استفاده از عبارت‌های باقاعده، توانایی چت‌بات را در درک و تولید زبان طبیعی بهبود دهید.

1. بررسی معتبر بودن دستور

چت‌بات علاوه بر باهوش بودن، دستیاری وفادار هم هست. برای پرسیدن چیزی از چت‌بات، کاربر باید واژه‌ی «Chatbot» را در جایگاه اول دستور بیاورد. مهم نیست که این کلیدواژه با حروف بزرگ نوشته شود یا کوچک. نکته‌ی مهم جایگاه این واژه است.

تابع is_valid_command را پیاده‌سازی کنید که به چت‌بات کمک می‌کند تشخیص دهد کاربر در حال دادن یک دستور است.

"Chatbot, play a song from the 80's." | is_valid_command
# => true
"Hey Chatbot, where is the closest pharmacy?" | is_valid_command
# => false
"CHATBOT, do you have a solution for this challenge?" | is_valid_command
# => true

2. حذف ایموجی‌های رمزگذاری‌شده

چت‌بات به‌سختی می‌تواند درک کند که انسان‌ها چگونه از ایموجی‌ها برای بیان احساسات خود استفاده می‌کنند. وقتی چت‌بات پیام‌های کاربر را دریافت می‌کند، هر ایموجی به‌صورت رشته‌ی «emoji» به‌همراه یک شماره‌ی شناسه نمایش داده می‌شود.

متد remove_emoji را پیاده‌سازی کنید که یک رشته می‌گیرد و همه‌ی ایموجی‌های موجود در پیام را حذف می‌کند.

خطوطی که ایموجی ندارند باید بدون تغییر بازگردانده شوند. فقط رشته‌ی ایموجی را حذف کنید. فاصله‌ها را تغییر ندهید.

"I love playing videogames emoji3465 it's one of my hobbies" | remove_emoji
# => "I love playing videogames  it's one of my hobbies"

3. بررسی معتبر بودن شماره‌ی تلفن

در نقطه‌ای از تعامل با چت‌بات، کاربر یک شماره‌ی تلفن ارائه می‌دهد. چت‌بات فقط می‌تواند با شماره‌ای با قالبی مشخص تماس بگیرد.

تابع check_phone_number را پیاده‌سازی کنید.

اگر شماره معتبر باشد، چت‌بات با پیامی از کاربر تشکر می‌کند و شماره را تأیید می‌کند. اگر شماره نامعتبر باشد، چت‌بات به کاربر اطلاع می‌دهد که شماره‌ی تلفن معتبر نیست.

قالب مورد انتظار (+NN) NNN-NNN-NNN است که در آن N یک رقم است.

"chatbot my phone number is (+34) 659-771-594" | check_phone_number
# => "Thanks! Your phone number is OK."
"chatbot, call me at 659-771-594" | check_phone_number
# => "Oops, it seems like I can't reach out to 659-771-594."

4. دریافت پیوند وب‌سایت

چت‌بات نرم‌افزاری بسیار کنجکاو است. با اینکه می‌تواند موضوعی خاص را در اینترنت جست‌وجو کند، دوست دارد از کاربران درباره‌ی وب‌سایت‌های جالبی بپرسد که برای یافتن اطلاعات مرتبط می‌توان از آن‌ها بازدید کرد.

نمونه گفت‌وگو:

چت‌بات: سلام کاربر، می‌خواهم یاد بگیرم چطور با JavaScript کد بنویسم. آیا وب‌سایت جالبی می‌شناسید که بتوانم از آن یاد بگیرم؟

کاربر: من از exercism.org چیزهای زیادی یاد گرفتم، مطالب خوب زیادی آنجا هست.

تابع get_domains را پیاده‌سازی کنید که آرایه‌ای از دامنه‌های وب‌سایت را برمی‌گرداند.

"I learned a lot from exercism.org and google.com" | get_domains
# => ["exercism.org", "google.com"]

5. خوش‌آمدگویی به کاربر

یک چت‌بات مؤدب کاربران را با اسمشان صدا می‌زند. وقتی کاربر خودش را معرفی می‌کند، چت‌بات ما اسم او را تشخیص می‌دهد و با سلامی دوستانه پاسخ می‌دهد.

تابع nice_to_meet_you را بنویسید.

اگر رشته‌ی ورودی شامل «My name is Someone.» باشد، اسم را استخراج کنید و رشته‌ی «Nice to meet you, Someone.» را برگردانید.

"My name is Jean-Luc" | nice_to_meet_you
# => "Nice to meet you, Jean-Luc"

6. تجزیه‌ی بسیار ساده‌ی CSV

تسلیم «بیماری افزودن قابلیت‌های تازه» می‌شویم و یک تابع تجزیه‌ی CSV به چت‌بات اضافه می‌کنیم.

تابع parse_csv را پیاده‌سازی کنید که یک رشته می‌گیرد و آرایه‌ای از فیلدهای حاصل را برمی‌گرداند. جداکننده‌ی فیلد باید «ویرگول به‌همراه فاصله‌ی اختیاری» باشد.

نگران هیچ‌کدام از حالت‌های حاشیه‌ای قالب CSV (مثل فیلدهایی که شامل ویرگول هستند) نخواهیم بود.

"first, second,third,   fourth" | parse_csv
# => ["first", "second", "third", "fourth"]
ویرایش از طریق GitHub این لینک در پنجره یا زبانه‌ی جدیدی باز می‌شود
jq Exercism

آماده‌اید چت‌بات باقاعده را شروع کنید؟

در Exercism ثبت‌نام کنید تا jq را همراه با 12 مفهوم75 تمرین و مربی‌گری انسانی واقعی یاد بگیرید و در آن استاد شوید، همه‌ی این‌ها رایگان.