۲۳ - ۱۱تمرینهای فصل
۱.
طبق تعاریف در این آدرس، یه پارسر برای نسخهبندیِ معنایی بنویسین. بعد از اینکه پارسرتون کار کرد، یه نمونه ِ Ord برای تایپِ SemVer بنویسین که از مشخصاتی که در سایتِ SemVer آورده شده پیروی میکنه.
-- ،به خاطر اولویت/ترتیببندی
-- .نمیشه اعداد رو مثل نوشتهها مرتب کرد
data NumberOrString =
NOSS String
| NOSI Integer
type Major = Integer
type Minor = Integer
type Patch = Integer
type Release = [NumberOrString]
type Metadata = [NumberOrString]
data SemVer =
SemVer Major Minor Patch Release Metadata
parseSemVer :: Parser SemVer
parseSemVer = undefinedنتایج مورد نظر:
λ> parseString parseSemVer mempty "2.1.1"
Success (SemVer 2 1 1 [] []
λ> parseString parseSemVer mempty "1.0.0-x.7.z.92"
Success (SemVer 1 0 0
[NOSS "x", NOSI 7, NOSS "z", NOSI 92] [])
λ> SemVer 2 1 1 [] [] > SemVer 2 1 0 [] []
True۲.
یه پارسر برای اعداد صحیح ِ بزرگتر از صفر بنویسین. از توابعِ ازپیشتعریفشده ِ digit و integer استفاده نکنین، ولی موردی نداره از بقیهی کتابخونههایی که تا اینجا نشونتون دادیم استفاده کنین. ازتون انتظار نمیره یه کتابخونه ِ پارسینگ رو از اول بنویسین.
parseDigit :: Parser Char
parseDigit = undefined
base10Integer :: Parser Integer
base10Integer = undefined نتایج مورد نظر:
λ> parseString parseDigit mempty "123"
Success '1'
λ> parseString parseDigit mempty "abc"
Failure (interactive):1:1: error: expected:
parseDigit
abc<EOF>
^
λ> parseString base10Integer mempty "123abc"
Success 123
λ> parseString parseDigit mempty "abc"
Failure (interactive):1:1: error: expected:
integer
abc<EOF>
^راهنمایی: فرض کنین اعداد با مبنای ۱۰ رو پارس میکنین. همینطور که اعداد رو از چپ به راست پارس میکنین، از حساب برای یه جور "انبارکننده" ِ کم هزینه استفاده کنین.
۳.
پارسری که نوشتین رو ارتقا بدین تا برای اعدادِ منفی هم کار کنه. سعی کنین یه پارسر ِ جدید بر مبنای پارسر ِ قبلیتون تعریف کنین.
λ> parseString base10Integer mempty "-123abc"
Success (-123)۴.
برای انواع فرمتهای تلفنهای کانادا یه پارسر بنویسین.
-- یا همون کدِ منطقه
type NumberingPlanArea = Int
type Exchange = Int
type LineNumber = Int
data PhoneNumber =
PhoneNumber NumberingPlanArea
Exchange LineNumber
deriving (Eq, Show)
parsePhone :: Parser PhoneNumber
parsePhone = undefinedباید چنین رفتاری داشته باشه:
λ> parseString parsePhone mempty "123-456-7890"
Success (Phone Number 123 456 7890)
λ> parseString parsePhone mempty "1234567890"
Success (Phone Number 123 456 7890)
λ> parseString parsePhone mempty "(123) 456-7890"
Success (Phone Number 123 456 7890)
λ> parseString parsePhone mempty "1-123-456-7890"
Success (Phone Number 123 456 7890)برای مقایسه میتونین به صفحهی Wikipedia با عنوان National conventions for writing telephone numbers برین. برای تمرین بیشتر، پیشنهاد میکنیم برای فرمتِ شماره تلفنهای کشور خودتون هم پارسر بنویسین.
۵.
برای یه فایلِ گزارشی پارسر بنویسین و زمانِ گذرونده برای هر کار رو جمع بزنین. مضاف بر اون، میانگین زمانی که در هر روز روی یک کار گذرونده شده هم پیدا کنید. این فرمت نوشتاری از کامنت پشتیبانی میکنه، که پارسرتون باید اونها رو نادیده بگیره. یه کاراکترِ # که بعدش تاریخ میاد، شروعِ یک روز رو تعیین میکنه.
یه مثال از فرمتِ گزارش:
-- wheee a comment
# 2025-02-05
08:00 Breakfast
09:00 Sanitizing moisture collector
11:00 Exercising in high-grav gym
12:00 Lunch
13:00 Programming
17:00 Commutting home in rover
17:30 R&R
19:00 Dinner
21:00 Shower
21:15 Read
22:00 Sleep
# 2025-02-07 -- تاریخها الزاماً متسلسل نیستن
08:00 Breakfast
09:00 Bumped head, passed out
13:36 Wake up, headache
13:37 Go to medbay
13:40 Patch self up
13:45 Commute home for rest
14:15 Read
21:00 Dinner
21:15 Read
22:00 Sleepخودتون باید یه نوعداده ِ مناسب برای ارائهی این داده پیدا کنین. برای امتیاز اضافه، با یه نمونه ِ Show دو طرفهش کنین، یعنی همون چیزی رو چاپ کنه که پارس کردین. بعد هم یه ژنراتور ِ Gen از QuickCheck برای این داده بنویسین و ببینین که آیا QuickCheck میتونه پارسرتون رو شکست بده یا نه.
۶.
یه پارسر برای آدرسهای IPv4 بنویسین.
import Data.Word
data IPAddress =
IPAddress Word32
deriving (Eq, Ord, Show)یه Word32 در واقع یه int ِ سیودو-بیتی و بیعلامته (32-bit unsigned int). کمترین مقداری که این تایپ میتونه داشته باشه صفره (یعنی شامل اعداد منفی نمیشه)، اما در عوض دو برابر عددِ مثبت داره. دقت کنین:
Prelude> import Data.Int
Prelude> import Data.Word
Prelude> maxBound :: Int32
2147483647
Prelude> maxBound :: Word32
4294967295
Prelude> div 4294967295 2147483647
2استفاده از Word32 برای ارائهی آدرسهای IPv4 راه جمعوجور و مناسبیه. تو این تمرین انتظار میره خودتون طرز کار آدرسهای IP رو یاد بگیرین تا بتونین یه پارسر ِ صحیح بنویسین. میتونین از یه موتور جستجو استفاده کنین، یا اگه کتابی با موضوع اینترنت و شبکه دارین از روی اون بخونین.
مثالهایی از آدرسهایی IPv4 و معادلِ مبنای ۱۰ شون:
172.16.256.1 -> 2886794753
204.120.0.15 -> 3430416399۷.
مثل تمرین قبل، اما برای IPv6.
import Data.Word
data IPAddress6 =
IPAddress6 Word64 Word64
deriving (Eq, Ord, Show)مثالهایی از آدرسهای IPv6 و معادلِ مبنای ۱۰ شون:
0:0:0:0:0:ffff:ac10:fe01 -> 281473568538113
0:0:0:0:0:ffff:cc78:f -> 281474112159759
FE80:0000:0000:0000:0000:0202:B3FF:FE1E:8329 ->
338288524927261089654163772891438416681
2001:DB8::8:800:200C:417A ->
42540766411282592856906245548098208122آدرسهای IPv6 رو میشه کامل یا کوتاه شده، یا مخفف نوشت، و همین یه ذره کار رو سخت میکنه. از این پرسشوپاسخ میتونین بیشتر یاد بگیرین.
مطمئن بشین که پارسرتون نسخهی مخففِ مثالهای قبلی هم درست پارس میکنه:
FE80::0202:B3FF:FE1E:8329
2001:DB8::8:800:200C:417A۸.
نمونه ِ مشتقگرفتهشدهی Show رو از تایپهای IPAddress و IPAddress6 حذف کنین و برای هر تایپ طوری نمونه ِ Show بنویسین که با فرمتِ معمولِ نوشتاری ِ همون تایپ چاپ کنه.
۹.
یه تابع برای تبدیل بین IPAddress و IPAddress6 بنویسین.
۱۰.
برای زبان DOT که Graphviz برای بیانِ گراف در نوشتار ازش استفاده میکنه، یه پارسر بنویسین.
پیشنهاد میکنیم به نوعداده ِ AST در Haphviz نگاه کنین تا بهتون کمک کنه گراف رو در نوعداده ِ هسکل نشون بدین. اگر هم حوصله دارین این تمرین رو بهتر انجام بدین، میتونین fgl رو امتحان کنین.