مسیرها
/
Julia
Julia
/
تمرین‌ها
/
سطح‌های لاگ
سطح‌های لاگ

سطح‌های لاگ

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

مقدمه

یک String در Julia دنباله‌ای مرتب از نویسه‌ها است. همان‌طور که در راهنما آمده است: «البته، دردسر واقعی هنگامی آغاز می‌شود که بپرسید نویسه چیست.»

بخش زیادی از این پیچیدگی در مفهوم Chars با جزئیات بیشتری بررسی می‌شود، اما به‌طور خلاصه:

  • همه‌ی رشته‌ها در Julia یونیکد هستند.
  • رشته‌هایی که اتفاقاً ASCII هم هستند (حروف انگلیسی A تا Z در شکل بزرگ و کوچک، رقم‌ها و چند نویسه‌ی نشانه‌گذاری) کار کردن با آن‌ها آسان است.
  • کار کردن با بیشتر نظام‌های نوشتاری دیگر جهان ممکن است، اما دقت و توجه بیشتری می‌طلبد.

لیترال‌های رشته

به‌طور کلی، رشته‌ها داخل کوتیشن دوتایی " " قرار می‌گیرند. کوتیشن تک ' ' فقط برای Chars به کار می‌رود.

همچنین می‌توان از کوتیشن دوتایی سه‌گانه """ """ استفاده کرد که به شما اجازه می‌دهد " را بدون escape درون رشته به کار ببرید. مهم‌تر از آن، تورفتگی اضافی ناخواسته از رشته‌های چندخطی حذف می‌شود.

julia> """
       Multiline
       
       """
"Multiline\nString\n"

escape کردن نویسه‌ها

مانند بیشتر زبان‌ها از C به بعد، Julia برای escape کردن از بک‌اسلش \ استفاده می‌کند:

  1. برای وارد کردن نویسه‌های غیرچاپی، مانند \n (خط جدید) در مثال قبلی.
  2. برای محافظت از نویسه‌هایی که در غیر این صورت معنای خاصی دارند، مانند \$ (به پایین نگاه کنید).

اندیس‌گذاری

در اصل، می‌توان رشته‌ها را دقیقاً مثل بردارها اندیس‌گذاری کرد:

julia> s[1], s[5]
('J', 'a')

این کار برای یک رشته‌ی ASCII مثل «Julia» کاملاً جواب می‌دهد، اما بیشتر زبان‌های جهان را نادیده می‌گیرد.

یکی از نویسه‌های Beowulf را در نظر بگیرید که حدود ۱۵ قرن پیش به انگلیسی باستان نوشته شده است.

«Hrōðgār» به‌وضوح ۷ نویسه است، اما همه‌شان ASCII نیستند.

julia> king = "Hrōðgār"
"Hrōðgār"

julia> length(king)
7

julia> isascii(king)
false

# collect() converts to a Char vector:
julia> collect(king)
7-element Vector{Char}:
 'H': ASCII/Unicode U+0048 (category Lu: Letter, uppercase)
 'r': ASCII/Unicode U+0072 (category Ll: Letter, lowercase)
 'ō': Unicode U+014D (category Ll: Letter, lowercase)
 'ð': Unicode U+00F0 (category Ll: Letter, lowercase)
 'g': ASCII/Unicode U+0067 (category Ll: Letter, lowercase)
 'ā': Unicode U+0101 (category Ll: Letter, lowercase)
 'r': ASCII/Unicode U+0072 (category Ll: Letter, lowercase)

اندیس‌گذاری روی چنین رشته‌ای خوب شروع می‌شود، اما از جایی به بعد می‌شکند:

julia> king[3]
'ō': Unicode U+014D (category Ll: Letter, lowercase)

julia> king[4]
ERROR: StringIndexError: invalid index [4], valid nearby indices [3]=>'ō', [5]=>'ð'

مشکل این است که اندیس‌گذاری به این شکل بایت‌ها را می‌شمارد، و در رشته‌های غیر ASCII بایت‌ها رابطه‌ی یک‌به‌یک با نویسه‌ها ندارند. یک نویسه‌ی Unicode/UTF-8 ممکن است برای نمایش تا ۴ بایت لازم داشته باشد، و مفهوم Chars این موضوع را بیشتر بررسی می‌کند.

ساختن رشته‌ها

الحاق

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

julia> "s1 " + "s2"
ERROR: MethodError: no method matching +(::String, ::String)
The function `+` exists, but no method is defined for this combination of argument types.
String concatenation is performed with * 

julia> "s1 " * "s2"
"s1 s2"

تابع string() هر تعداد دلخواهی از رشته‌ها را به هم الحاق می‌کند.

julia> string("lots ", "of ", "bits")
"lots of bits"

تابع join() همین کار را انجام می‌دهد و همچنین بردارهایی از رشته‌ها را می‌گیرد و آن‌ها را با یک جداکننده‌ی مشخص به هم الحاق می‌کند (که به‌طور پیش‌فرض "" است).

julia> join(["lots", "of", "bits"], "_")
"lots_of_bits"

توجه داشته باشید که رشته‌ها تغییرناپذیرند و الحاق همراه با کپی کردن است. بنابراین، الحاق‌های پیاپی درون یک حلقه ناکارآمد است و بهتر است قطعه‌ها را در یک بردار جمع کنید و در پایان همه‌ی آن‌ها را به هم الحاق کنید.

تکرار

ساده است:

julia> repeat("Abc", 5)
"AbcAbcAbcAbcAbc"

درج مقدار

بسیاری از زبان‌ها روشی برای درج مقدارهای محاسبه‌شده در رشته‌ها دارند، هرچند بر سر بهترین نحوه‌ی نگارش برای این کار اختلاف نظر بسیار زیادی وجود دارد.

در Julia، پیش از مقدارهای درج‌شده $ می‌آید.

julia> myvar = 42
42

julia> "myvar evaluates to $myvar"
"myvar evaluates to 42"

julia> r = 5.3
5.3

julia> "A circle of radius $r has area $(π * r^2)"
"A circle of radius 5.3 has area 88.24733763933729"

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

برای اینکه $ را در رشته بگنجانید، آن را به‌صورت \$ escape کنید.

به این محدودیت توجه کنید که درج مقدار فعلاً قالب‌بندی را ممکن نمی‌کند، مثلاً تعیین تعداد ارقام اعشاری برای نمایش.

@sprintf()

برای کنترل دقیق‌تر بر ساخت رشته‌ها، Julia تابع sprintf را از C (و چندین زبان بعدی: Wikipedia حدود ۳۰ مورد را فهرست می‌کند) کپی کرده است.

در Julia، این به‌صورت یک macro درون ماژول Printf پیاده‌سازی شده است، از این‌رو پیشوند @ را دارد.

julia> using Printf

julia> r = 5.3
5.3

julia> @sprintf("A circle of radius %.1f has area %.2f", r, π * r^2)
"A circle of radius 5.3 has area 88.25"

توابع رشته

برای سادگی، این بخش روی توابعی تمرکز می‌کند که رشته‌ی جدیدی برمی‌گردانند و ورودی را بدون تغییر می‌گذارند. بسیاری از آن‌ها معادلی دارند که با پسوند ! نام‌گذاری شده و ورودی را در جای خود تغییر می‌دهد.

طول

طول یک رشته چقدر است؟ گاهی بستگی دارد که چطور می‌شمارید.

حالت ساده، رشته‌های ASCII است که هر نویسه ۱ بایت را اشغال می‌کند.

julia> string_asc = "Julia"
"Julia"

julia> length(string_asc)
5

julia> sizeof(string_asc)
5

تابع length() تعداد نویسه‌ها را برمی‌گرداند و تابع sizeof() تعداد بایت‌ها را.

این تفاوت با مجموعه‌های نویسه‌ی دیگر مهم می‌شود:

julia> king = "Hrōðgār"
"Hrōðgār"

julia> length(king)
7

julia> sizeof(king)
10

تقسیم

یک عملیات رایج در برنامه‌نویسی این است که رشته‌ی بلندی را برداریم و آن را بر پایه‌ی یک رشته‌ی جداکننده‌ی یک‌نویسه‌ای یا چندنویسه‌ای به قطعه‌هایی تقسیم کنیم.

به‌طور کلی، از تابع split() استفاده می‌کنیم. جداکننده به‌طور پیش‌فرض هر فضای خالی است، اما می‌توان جداکننده‌های دیگری را مشخص کرد.

julia> split("my little string")
3-element Vector{SubString{String}}:
 "my"
 "little"
 "string"

julia> split("My_snake_case_string", "_")
4-element Vector{SubString{String}}:
 "My"
 "snake"
 "case"
 "string"

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

جست‌وجو و جایگزینی

آیا یک رشته شامل زیررشته‌ی مشخصی است؟ به‌طرز عجیبی، دو تابع برای این آزمایش وجود دارد: occursin() و contains() که فقط در ترتیب آرگومان‌هایشان تفاوت دارند.

julia> needle = "Julia"
"Julia"

julia> haystack = "Python, Julia and R"
"Python, Julia and R"

julia> occursin(needle, haystack)
true

julia> contains(haystack, needle)
true

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

julia> startswith(haystack, "Python")
true

julia> endswith(haystack, "Python")
false

برای به دست آوردن اندیس‌های شروع تا پایانِ یک زیررشته، چندین تابع داریم:

  • findfirst()
  • findlast()
  • findprev()
  • findnext()
julia> findfirst(needle, haystack)
9:13

برای جایگزینی زیررشته‌ها، تغییرها را با نحوه‌ی نگارش x => y فهرست کنید.

julia> replace("abc", "a"=>"b", "b"=>"c", "c"=>"a")
"bca"

از Julia ۱.۷ به بعد، تابع replace می‌تواند هر تعداد دلخواهی تغییر بپذیرد، با این تضمین که هیچ نویسه‌ای در جریان جایگزینی بیش از یک بار تغییر نکند.

به نحوه‌ی نگارش x => y اصطلاحاً pair می‌گویند، نوعی مهم در Julia که در مفهوم Dicts and Pairs با جزئیات بیشتری پوشش داده شده است.

فضای خالی

اغلب لازم است فضای خالی ابتدا و/یا انتهای یک رشته را حذف کنیم.

توابع مربوط به این کار عبارت‌اند از lstrip() (چپ)، rstrip() (راست) و strip() (هر دو).

julia> strip("\n\t  useful_bit\n\n")
"useful_bit"

تغییر بزرگی و کوچکی حروف

نام این توابع تقریباً گویای کارشان است:

julia> s = repeat("aBcD ", 5)
"aBcD aBcD aBcD aBcD aBcD "

julia> uppercase(s)
"ABCD ABCD ABCD ABCD ABCD "

julia> lowercase(s)
"abcd abcd abcd abcd abcd "

julia> titlecase(s)  # Initial letter of each word uppercase
"Abcd Abcd Abcd Abcd Abcd "

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

در این تمرین شما خطوط لاگ را پردازش می‌کنید.

هر خط لاگ رشته‌ای است با این قالب: "[<LEVEL>]: <MESSAGE>".

سه سطح لاگ متفاوت وجود دارد:

  • INFO
  • WARNING
  • ERROR

سه کار دارید که هرکدام یک خط لاگ می‌گیرد و از شما می‌خواهد کاری با آن انجام دهید.

1. گرفتن پیام از یک خط لاگ

تابع message را طوری پیاده‌سازی کنید که پیام یک خط لاگ را برگرداند:

julia> message("[ERROR]: Invalid operation")
"Invalid operation"

هر فاصله‌ی سفید ابتدایی یا انتهایی باید حذف شود:

julia> message("[WARNING]:  Disk almost full\r\n")
"Disk almost full"

2. گرفتن سطح لاگ از یک خط لاگ

تابع log_level را طوری پیاده‌سازی کنید که سطح لاگ یک خط لاگ را برگرداند؛ این مقدار باید با حروف کوچک برگردانده شود:

julia> log_level("[ERROR]: Invalid operation")
"error"

3. قالب‌بندی دوباره‌ی یک خط لاگ

تابع reformat را پیاده‌سازی کنید که خط لاگ را دوباره قالب‌بندی می‌کند؛ به این صورت که پیام را اول و سطح لاگ را بعد از آن داخل پرانتز قرار می‌دهد:

julia> reformat("[INFO]: Operation completed")
"Operation completed (info)"

توجه: همه‌ی رشته‌ها در این تمرین انگلیسی هستند و به مجموعه‌ی نویسه‌های ASCII محدود می‌شوند. مفاهیم بعدی فرصتی فراهم می‌کنند تا با نویسه‌های یونیکد کار کنید.

ویرایش از طریق GitHub این لینک در پنجره یا زبانه‌ی جدیدی باز می‌شود
Julia Exercism

آماده‌اید سطح‌های لاگ را شروع کنید؟

در Exercism ثبت‌نام کنید تا Julia را همراه با 35 مفهوم128 تمرین و مربی‌گری انسانی واقعی یاد بگیرید و در آن استاد شوید، همه‌ی این‌ها رایگان.