۲۳ - ۱۱تمرینهای فصل
۱.
طبق تعاریف در این آدرس، یه پارسر برای نسخهبندیِ معنایی بنویسین. بعد از اینکه پارسرتون کار کرد، یه نمونه ِ 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
رو امتحان کنین.