زبان برنامه نویسی ارلنگ - لیست ها

لیست ها

از لیست ها برای حل کردن اکثر مسائل استفاده میشه و مسلما از این ساختار داده‌ای از همه بیشتر استفاده می‌شه. لیست‌ها می‌تونن همه چیز رو توی خودش نگه داره! اعداد، اتم‌ها، تاپل ها و حتی لیست های دیگه؛ یک لیست اینجوری نوشته میشه: [Element1, Element2, ..., ElementN] و می‌تونین بیشتر از یک نوع داده را داخلش قرار بدین:

1> [1, 2, 3, {numbers,[4,5,6]}, 5.34, atom].
[1,2,3,{numbers,[4,5,6]},5.34,atom]

ساده بود نه؟

2> [97, 98, 99].
&quotabc&quot

اوه اوه! این یکی از دوست‌نداشتنی ترین چیزای ارلنگه: رشته‌ها (Strings)! رشته ها لیست‌ها هستند و علامتشون هم دقیقا شبیه همه!حالا برای چی دوستش ندارند؟ برای این:

3> [97,98,99,4,5,6].
[97,98,99,4,5,6]
4> [233].
&quoté&quot

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

زیاد نوشابه نخورین

ممکنه شنیده باشین ارلنگ توی دستکاری رشته ها خیلی بد عمل میکنه؛ دلیلش اینه: نوع داده‌ای String برخلاف اکثر زبان ها توی ارلنگ وجود نداره. به دلیل اینکه اول ارلنگ برای استفاده توی شرکت‌های مخابراتی درست شد. اونجا هرگز(یا به ندرت) از رشته ها استفاده میشه و برای همین احساس نکردن که خوبه رسما به زبان اضافه کنند. به هر حال اکثر کمبودهای ارلنگ در دستکاری رشته‌ها با گذر زمان مرتفع شد: الان ماشین مجازی از رشته‌های یونیکد پشتیبانی میکنه و روی هم رفته الان از همه زمان‌ها در دستکاری رشته‌ها سریع‌تر شده. همچنین میشه رشته‌ها رو به صورت داده‌های دودویی نیز ذخیره کرد. این کار باعث میشه واقعا سبک باشه و راحت‌تر با اون کار کرد. در مجموع هنوز فقدان بعضی توابع توی کتابخانه استاندارد ارلنگ هست و همچنین درحالی که پردازش رشته‌ها در ارلنگ شدنیه زبان های بهتری برای کارهایی که به عملیات بیشتری نیازه وجود داره، مثل پرل و پایتون.

برای چسباندن لیست‌ها از عملگر ++ برای حذف عناصر از عملگر -- استفاده می‌کنیم.

Shell:
5> [1,2,3] ++ [4,5].
[1,2,3,4,5]
6> [1,2,3,4,5] -- [1,2,3].
[4,5]
7> [2,4,2] -- [2,4].
[2]
8> [2,4,2] -- [2,4,2].
[]

هر دو عملگر ++ و -- right-associative هستند. یعنی اگر چند تا عملیات ++ یا -- داشته باشیم این عملیات ازبه ترتیب از راست به چپ اجرا می‌شوند. شبیه زیر:

Shell:
9> [1,2,3] -- [1,2] -- [3].
[3]
10> [1,2,3] -- [1,2] -- [2].
[2,3]

بزارید ادامه بدیم. به اولین عنصر لیست Head و به بقیه لیست Tail گفته می‌شود. برای بدست آوردن اونا از دو تابع داخلی استفاده میکنیم:

Shell:
11> hd ( [1,2,3,4] ).
1
12> tl ( [1,2,3,4] ).
[2,3,4]
توجه توجه: توابع داخلی (built-in functions - ‌‌‌BIFs) معمولا با خود ارلنگ پیاده‌سازی نشده‌اند و با زبان C یا هر زبانی که ارلنگ با اون پیاده سازی شده بوده ( مثلا پرولوگ در دهه هشتاد) تعریف شده‌اند. بعضی توابع داخلی هستند که می‌توانند با خود ارلنگ انجام بشوند ولی به خاطر سرعت بیشتر با C پیاده‌سازی شده‌اند. یک نمونه تابع (length(List هست که همان طور که حدس زده‌اید طول لیست پاس داده شده را بر‌ می‌گرداند.

دستیابی یا افزودن Head خیلی سریعه: عملا در همه برنامه‌هایی که نیاز دارین با لیست‌ها کار کار کنین اول از همه روع Head عمل می‌کند. چونکه خیلی زیاد استفاده میشن راه بهتری هم برای برای جدا کردن Head از بقیه لیست وجود داده که به تطابق الگو هم کمک میکنه: [Head | Tail]. حالا چگونه Head جدید رو به لیست اضافه کنیم:

13> List = [2,3,4].
[2,3,4]
14> NewList = [1|List].
[1,2,3,4]

پردازش لیست‌ها معمولا با Head لیست شروع می‌شود. شما به یک راه سریع برای دخیره کردن بقیه لیست که بعدا روی آن عملیاتی رو انجام بدهید نیاز دارید. اگه یادتون بیاد که تاپل ها چگونه کار می‌کردند و چگونه از تطابق الگو برای استخراج مقادیر یک نقطه استفاده می کردیم ({X,Y})، شما الان می‌توانید اولین عنصر (Head) لیست رو به همان صورت جدا کنید.

15> [ Head | Tail ] = NewList.
[1,2,3,4]
16> Head.
1
17> Tail.
[2,3,4]
18> [ NewHead | NewTail ] = Tail.
[2,3,4]
19> NewHead.
2

عملگر | که استفاده کردیم cons یا constructor گفته می‌شود. در واقع یک لیست با یک cons و مقادیر درست می‌شود:

Shell:
20> [ 1 | [ ] ].
[ 1 ]
21> [ 2  |  [ 1 | [ ]  ] ].
[ 2 , 1 ]
22> [ 3 | [ 2 | [ 1 | [ ] ] ] ].
[ 3 , 2 , 1 ]

هر لیست می‌تواند با اسفاده از این فرمول ساخته شود:

[ Term1| [ Term2 | [ ... | [TermN ] ] ] ] ...

پس لیست ها می‌توانند به صورت بازگشتی به صورت یک Head که قبلا Tail بوده، که خود این میتواند به دنبالش Head های دیگری درست شود. برای درک بیشتر، تصور کنید لیست شبیه یک کرم خاکی است: شما می‌توانید اون رو از وسط برش بزنید و بعدش دو تا کرم دارید.

برای درک این مفاهیم همه مثال های زیر را بخوانید‌: (راهنمایی: همه مثال ها با هم برابرند)

[a, b, c, d]
[a, b, c, d | [ ]]
[a, b | [c, d]]
[a, b | [c | [d]]]
[a | [b | [c | [d]]]]
[a | [b | [c | [d | [ ] ]]]]
توجه توجه: به این شکل استفاده از لیست: [2 | 1] لیست نامناسب (improper list) نامیده می‌شود. لیست های نامناسب داخال تطابق الگو به شکل [Head|Tail] کار می‌کنند ولی توی تابع‌های استاندارد ارلنگ کار نمی‌کنند (حتی ()length)؛ چونکه ارلنگ توقع یک لیست مناسب (proper list) را دارد. لیست مناسب به لیستی گفته می‌شود که آخرین سلولش یک لیست خالی باشد. وقتی یک آیتم را به شکل [2] تعریف می‌کنیم، خودکار به شکل یک لیست مناسب تبدیل می‌شود. همین‌طور [ [2] | 1] باید کار کند!