جولیا هم مثل بیشتر زبانهای برنامهنویسی، انواع متنوعی از مجموعهها را در اختیارتان میگذارد.
بعضی از این مجموعهها ذاتاً ترتیبدار هستند (از جمله Vectors و Arraysهای چندبعدی)، بنابراین میتوانیم به عناصر بر اساس موقعیتشان دسترسی داشته باشیم.
بعضی مجموعههای دیگر بدون ترتیباند (Sets، Dicts) و باید از طریق کلید یا مقدار به عناصرشان دسترسی پیدا کرد.
در سادهترین حالت، فرض کنید یک Vector از Ints داریم و میخواهیم آن را به ترتیب صعودی مرتب کنیم.
julia> v = [3, 2, 4, 1]
4-element Vector{Int64}:
3
2
4
1
julia> sort(v)
4-element Vector{Int64}:
1
2
3
4
# sort descending
julia> sort(v, rev=true)
4-element Vector{Int64}:
4
3
2
1
ساده است!
تابع sort() اسم واضحی دارد و ظاهراً Vector مرتبشده را برمیگرداند.
شوخی کردم! در این مرحله از برنامهی درسی، احتمالاً حدس میزنید (و درست هم حدس میزنید) که نکات ظریفی وجود دارد که باید دربارهشان صحبت کنیم.
بعد از اجرای sort(v)، مقدار v چیست؟
julia> v
4-element Vector{Int64}:
3
2
4
1
julia> issorted(v)
false
بردار اصلی تغییری نکرده است.
sort() یک تابع غیرتغییردهنده است که یک کپی از ورودیاش برمیگرداند.
این در بسیاری از زمینهها طراحی خوبی است: یک تابع خالص داریم که استدلال دربارهی رفتارش آسان است، حتی در کدهای موازی و توزیعشده.
از طرف دیگر، فرض کنید یک آرایه از یک میلیارد رشته داشتیم و میخواستیم آن را مرتب کنیم.
مرتبسازی احتمالاً کند است. علاوه بر این، باید همهچیز را کپی کنیم (کند است!) و حافظهی کافی تخصیص دهیم (چند گیگابایت!) تا کپی را ذخیره کنیم.
این کد کاملاً مشابه را مقایسه کنید:
# v is as previously
julia> issorted(v)
false
julia> sort!(v)
4-element Vector{Int64}:
1
2
3
4
julia> v
4-element Vector{Int64}:
1
2
3
4
julia> issorted(v)
true
حالا Vector ورودی در همانجا مرتب میشود، بدون کپی کردن، و ترتیب اصلی برای همیشه از دست میرود.
این میتواند کارآمد باشد، میتواند باگ داشته باشد و به عوارض جانبی پیشبینینشده منجر شود، و میتواند برای دیگرانی که از کدتان استفاده میکنند گیجکننده باشد. با دقت انتخاب کنید!
sort!() یک «تابع تغییردهنده» است و طبق قراردادی (بسیار محکم)، هر تابعی که ورودیهایش را تغییر میدهد یک ! به انتهای اسمش اضافه میکند، بهعنوان هشداری برای برنامهنویسان.
اگر عناصری که مرتب میشوند اشیاء بزرگی باشند، ممکن است کار کردن با اندیس موقعیتیشان راحتتر باشد.
julia> langs = ["Julia", "C++", "Python", "Elixir"]
4-element Vector{String}:
"Julia"
"C++"
"Python"
"Elixir"
julia> inxs = sortperm(langs) # => Integers
4-element Vector{Int64}:
2
4
1
3
julia> langs[inxs] # sort the entire vector
4-element Vector{String}:
"C++"
"Elixir"
"Julia"
"Python"
julia> langs[inxs[1:2]] # slice the indices
2-element Vector{String}:
"C++"
"Elixir"
چون langs یک Vector است، هر اندیس یک عدد صحیح در بازهی 1:length(langs) است.
خروجی sortperm() جایگشتی از این اعداد است که متناظر با مرتبسازی بردار ورودی است.
شما به عنوان دستیار مهندس تولید در شرکتی استخدام شدهاید که برای لاکپشتها لحاف تولید میکند.
سفارشها در طول روز به شکل اسم یا شمارهی مشتری و تعداد سفارش ثبت میشوند. سپس این سفارشها صبحها، پیش از شروع تولید همان روز، پردازش میشوند.
به محض اینکه همهی سفارشهای روز آماده شوند، میتوانید با دستمزد کامل به خانه بروید؛ بنابراین انگیزهای دارید که روز کاری را تا جای ممکن کوتاه کنید. از آنجا که هر سفارش توسط یک ماشین پردازش میشود، زمانبندی بزرگترین سفارشها در ابتدا باعث میشود سربار شروع کار به سفارشهای کوچکتر منتقل شود و زمان کل تولید کوتاهتر شود. بنابراین تصمیم گرفتهاید چند تابع مرتبسازی بنویسید تا به این تسریع زمانبندی کمک کنند و بتوانید به خانه بروید و برای لاکپشتتان یک پلیور ببافید.
همانطور که گفته شد، آرایهی تعدادها باید از بزرگ به کوچک مرتب شود. به دلیل اندازهی بالقوه بزرگ این آرایهها، باید آنها را درجا مرتب کرد. برای اینکه همهچیز را درست پیگیری کنید، باید بتوانید آرایهی مشتریها را هم متناسب با تعدادها مرتب کنید؛ بنابراین به یک «جایگشت» مرتبسازی از تعدادها نیاز خواهید داشت.
تابعی به اسم sortquantity!(qty) بنویسید که یک Vector از تعدادهای Integer میگیرد.
این تابع ورودی qty را درجا و به ترتیب نزولی مرتب میکند و یک جایگشت مرتبسازی از این ترتیب برمیگرداند.
julia> qty = [8, 9, 5, 6]
4-element Vector{Int64}:
8
9
5
6
julia> sortquantity!(qty)
4-element Vector{Int64}:
2
1
4
3
julia> qty # original vector is sorted in-place
4-element Vector{Int64}:
9
8
6
5
همانطور که در بالا گفته شد، باید مشتریها را در یک ترتیب مشخص پیگیری کنید.
تابعی به اسم sortcustomer(cust, srtperm) بنویسید که یک Vector از مشتریها یعنی cust و یک Vector جایگشت مرتبسازی یعنی srtperm میگیرد.
این تابع آرایهای جدید برمیگرداند که مشتریها در آن به همان ترتیبی قرار دارند که جایگشت مرتبسازی تعیین کرده است.
julia> cust = ["a", "b", "c"]
3-element Vector{String}:
"a"
"b"
"c"
julia> sortcustomer(cust, [2, 3, 1])
3-element Vector{String}:
"b"
"c"
"a"
julia> cust # original vector remains unsorted
3-element Vector{String}:
"a"
"b"
"c"
حالا میتوانید همه را کنار هم بگذارید تا به گرم ماندن آن لاکپشتها کمک کنید! اکنون میتوانید تعدادها و مشتریها را درست مرتب کنید، اما باید بتوانید ترتیب اصلی را برای ارسال بازیابی کنید. این کار با یک جایگشت مرتبسازی معکوس انجام میشود.
تابعی به اسم production_schedule!(cust, qty) بنویسید که یک Vector از مشتریها و یک Vector از تعدادها میگیرد.
این تابع تعدادها را درجا و به ترتیب نزولی مرتب میکند.
این تابع یک Vector جدید از مشتریها برمیگرداند که به همان ترتیب تعدادها مرتب شدهاند، و همچنین یک جایگشت مرتبسازی معکوس برمیگرداند که میتوان با آن بردارهای مرتبشده را به ترتیب اصلیشان بازگرداند.
julia> cust, qty = ["a", "b", "c"], [2, 3, 1]
(["a", "b", "c"], [2, 3, 1])
julia> orderedcust, invperm = production_schedule!(cust, qty)
(["b", "a", "c"], [2, 1, 3])
julia> qty # has been sorted in-place
3-element Vector{Int64}:
3
2
1
julia> orderedcust[invperm], qty[invperm] # inverse permutation gives original ordering
(["a", "b", "c"], [2, 3, 1])