یک فهرست نام فارسی را به sorted بدهید و چیزی که برمیگردد الفبایی نیست. با دوازده نام، خروجی «احمد» را پیش از «امیر» میگذارد که درست است، و «اکبر» را بعد از «امیر» که غلط است. دلیلش این است که مرتبسازی پیشفرض رشتهها را با شماره یونیکد نویسهها مقایسه میکند و شماره «ک» برابر 0x6a9 است در حالی که شماره «م» برابر 0x645 است.
حروف فارسی که بعدا به یونیکد اضافه شدند، یعنی «پ» و «چ» و «ژ» و «گ» و «ک»، در بلوک جدایی نشستهاند و شمارههایشان بالاتر از حروف مشترک با عربی است. الفبای فارسی این ترتیب را نمیشناسد. تصحیحش یک تابع کلید است و هفت سطر:
ALEFBA = "آابپتثجچحخدذرزژسشصضطظعغفقکگلمنوهی"order = {ch: i for i, ch in enumerate(ALEFBA)}def kelid(s): return [order.get(c, 99) for c in s]sorted(names, key=kelid)
حالا «احمد، اکبر، امیر، ایمان» به ترتیب درست میآیند. تابع کلید برای هر قلم یک بار صدا زده میشود و نه برای هر مقایسه، پس حتی روی فهرست بزرگ هم گران نیست.
هزینهای که هیچوقت نباید خودتان بدهید
مرتبسازی درونساخت پایتون از سال ۲۰۰۲ الگوریتمی بهنام Timsort است و روی دادهای که تکههای مرتب دارد از هر پیادهسازی دستی سریعتر است. اندازهگیریشده روی یک مکبوک با تراشه M1 Max و پایتون 3.14 در تاریخ ۲۰۲۶-۰۹-۰۳، روی دو هزار عدد تصادفی: sorted در ۰٫۱۷۵ میلیثانیه و یک مرتبسازی حبابی دستنویس روی همان فهرست در ۱۰۶٫۲ میلیثانیه. ششصد برابر، برای همان دو هزار عدد، و اختلاف با بزرگتر شدن فهرست بیشتر میشود چون یکی مرتبه دوم است و دیگری نیست.
نوشتن مرتبسازی به دست فقط برای فهمیدن ارزش دارد و برای هیچ چیز دیگری، و کدی که واقعا اجرا میشود باید sorted یا list.sort را صدا بزند. تفاوت این دو یکی است: اولی فهرست تازه میسازد و دومی همان فهرست را در جا عوض میکند و None برمیگرداند، که رایجترین اشتباه تازهکارهاست.
پایداری، و کاری که با آن میشود کرد
مرتبسازی پایتون پایدار است، یعنی قلمهایی که کلید برابر دارند به همان ترتیبی میمانند که آمده بودند. با یک فهرست از زوج نام و بخش، مرتب کردن بر اساس بخش، ترتیب نامها را در هر بخش دستنخورده نگه میدارد. کاربردش این است: برای مرتب کردن بر اساس دو ستون لازم نیست کلید ترکیبی بسازید، دو بار مرتب کنید و ستون کماهمیتتر را اول. این کار فقط روی مرتبسازی پایدار جواب میدهد.
جایی که کار گیر میکند، فهرست همکاران است
هر برنامهای که در ایران نوشته میشود دیر یا زود یک فهرست نام فارسی را مرتب چاپ میکند: فهرست کارمندان، فهرست شهرها، فهرست دستهها. چون خروجی تقریبا درست به نظر میرسد و فقط چند ردیف جابهجا هستند، معمولا هیچوقت گزارش نمیشود و اگر بشود هم گردن پایگاه داده میافتد. همان مسئله در پایگاه داده هم هست و آنجا اسمش کولیشن است. عادت درست این است که وقتی ستون نمایشی فارسی است، ترتیب را در همان لایهای تعیین کنید که چاپ میکند، و یک بار روی فهرستی که «آ» و «ا» و «ک» و «گ» در آن باشد امتحانش کنید.
ابزار، و سالی که این حرف درست است
در سال ۲۰۲۶ برای مرتبسازی زبانآگاه، راه استاندارد کتابخانه PyICU است که پیادهسازی یونیکد را میآورد و ترتیب درست هر زبان را میداند؛ متنباز است اما باید نصب شود و کتابخانه سیستمی هم میخواهد. ماژول locale در کتابخانه استاندارد تابع strxfrm را دارد و رایگان است، اما به تنظیمات محلی همان ماشین وابسته است و روی سروری که فقط زبان انگلیسی نصب دارد بیصدا به همان ترتیب کد یونیکد برمیگردد. برای یک فهرست نام، همان تابع کلید هفت سطری بالا هیچ وابستگیای ندارد و روی هر ماشینی یک جواب میدهد.
پرسش بعدی این است که مرتب بودن چه چیزی را ارزان میکند، و جوابش کوتاه است: جستوجو. خطی، دودویی، و چیزی که مرتب بودن میخرد همان معامله را باز میکند، و زبانی که با آن میشود گفت چرا حبابی شکست خورد در شمردن کار، وقتی ورودی بزرگ میشود است.