সম্ভবত functools.cmp_to_key()
অজগর সাজানোর অন্তর্নিহিত বাস্তবায়নের সাথে ঘনিষ্ঠভাবে জড়িত। এছাড়াও, সিএমপি প্যারামিটারটি উত্তরাধিকার। আধুনিক উপায় হ'ল ইনপুট আইটেমগুলিকে এমন জিনিসগুলিতে রূপান্তর করা যা পছন্দসই সমৃদ্ধ তুলনা ক্রিয়াকে সমর্থন করে।
সিপিথন ২.x এর অধীনে, স্বতন্ত্র সমৃদ্ধ তুলনা অপারেটরগুলি বাস্তবায়ন না করা হলেও পৃথক প্রকারের বস্তুগুলির অর্ডার দেওয়া যেতে পারে। সিপিথন ৩.x এর অধীনে, বিভিন্ন ধরণের অবজেক্টের অবশ্যই তুলনাটি সমর্থন করতে হবে। দেখুন কিভাবে পাইথন তুলনা করা যায় স্ট্রিং এবং int? যা অফিসিয়াল ডকুমেন্টেশনের সাথে লিঙ্ক করে । উত্তরগুলির বেশিরভাগই এই অন্তর্নিহিত আদেশের উপর নির্ভর করে। পাইথন 3.x এ স্যুইচ করার জন্য সংখ্যা এবং স্ট্রিংয়ের মধ্যে তুলনাটি কার্যকর এবং একীকরণ করতে একটি নতুন ধরণের প্রয়োজন হবে।
Python 2.7.12 (default, Sep 29 2016, 13:30:34)
>>> (0,"foo") < ("foo",0)
True
Python 3.5.2 (default, Oct 14 2016, 12:54:53)
>>> (0,"foo") < ("foo",0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: int() < str()
তিনটি ভিন্ন পন্থা আছে। পাইথনের Iterable
তুলনা অ্যালগরিদমের সুবিধা নেওয়ার জন্য প্রথম নেস্টেড ক্লাস ব্যবহার করে । দ্বিতীয়টি এই বাসাটিকে একটি একক শ্রেণিতে তালিকাভুক্ত করে। str
পারফরম্যান্সের উপর ফোকাস করতে তৃতীয়টি সাবক্লাসিংয়ের প্রস্তাব করে। সব সময়সীমা; দ্বিতীয়টি দ্বিগুণ দ্রুত এবং তৃতীয়টি প্রায় ছয়গুণ দ্রুত। সাবক্লাসিংয়ের str
প্রয়োজন হয় না, এবং সম্ভবত এটি প্রথম দিক থেকে একটি খারাপ ধারণা ছিল, তবে এটি নির্দিষ্ট সুবিধা সহ আসে।
সাজানো অক্ষরগুলি কেস দ্বারা আদেশ জোর করে নকল করা হয়, এবং ছোট কেস লেটারকে প্রথমে বাছাই করতে কেস-অদলবদল করা হয়; এটি হ'ল "প্রাকৃতিক বাছাই" এর সাধারণ সংজ্ঞা। আমি গ্রুপিংয়ের ধরণ সম্পর্কে সিদ্ধান্ত নিতে পারিনি; কিছু নীচেরগুলি পছন্দ করতে পারে যা উল্লেখযোগ্য পারফরম্যান্স সুবিধাও নিয়ে আসে:
d = lambda s: s.lower()+s.swapcase()
যেখানে ব্যবহার করা হয়, তুলনা অপারেটরগুলি সেটিতে সেট করা থাকে object
তাই তারা এড়ানোfunctools.total_ordering
হবে না ।
import functools
import itertools
@functools.total_ordering
class NaturalStringA(str):
def __repr__(self):
return "{}({})".format\
( type(self).__name__
, super().__repr__()
)
d = lambda c, s: [ c.NaturalStringPart("".join(v))
for k,v in
itertools.groupby(s, c.isdigit)
]
d = classmethod(d)
@functools.total_ordering
class NaturalStringPart(str):
d = lambda s: "".join(c.lower()+c.swapcase() for c in s)
d = staticmethod(d)
def __lt__(self, other):
if not isinstance(self, type(other)):
return NotImplemented
try:
return int(self) < int(other)
except ValueError:
if self.isdigit():
return True
elif other.isdigit():
return False
else:
return self.d(self) < self.d(other)
def __eq__(self, other):
if not isinstance(self, type(other)):
return NotImplemented
try:
return int(self) == int(other)
except ValueError:
if self.isdigit() or other.isdigit():
return False
else:
return self.d(self) == self.d(other)
__le__ = object.__le__
__ne__ = object.__ne__
__gt__ = object.__gt__
__ge__ = object.__ge__
def __lt__(self, other):
return self.d(self) < self.d(other)
def __eq__(self, other):
return self.d(self) == self.d(other)
__le__ = object.__le__
__ne__ = object.__ne__
__gt__ = object.__gt__
__ge__ = object.__ge__
import functools
import itertools
@functools.total_ordering
class NaturalStringB(str):
def __repr__(self):
return "{}({})".format\
( type(self).__name__
, super().__repr__()
)
d = lambda s: "".join(c.lower()+c.swapcase() for c in s)
d = staticmethod(d)
def __lt__(self, other):
if not isinstance(self, type(other)):
return NotImplemented
groups = map(lambda i: itertools.groupby(i, type(self).isdigit), (self, other))
zipped = itertools.zip_longest(*groups)
for s,o in zipped:
if s is None:
return True
if o is None:
return False
s_k, s_v = s[0], "".join(s[1])
o_k, o_v = o[0], "".join(o[1])
if s_k and o_k:
s_v, o_v = int(s_v), int(o_v)
if s_v == o_v:
continue
return s_v < o_v
elif s_k:
return True
elif o_k:
return False
else:
s_v, o_v = self.d(s_v), self.d(o_v)
if s_v == o_v:
continue
return s_v < o_v
return False
def __eq__(self, other):
if not isinstance(self, type(other)):
return NotImplemented
groups = map(lambda i: itertools.groupby(i, type(self).isdigit), (self, other))
zipped = itertools.zip_longest(*groups)
for s,o in zipped:
if s is None or o is None:
return False
s_k, s_v = s[0], "".join(s[1])
o_k, o_v = o[0], "".join(o[1])
if s_k and o_k:
s_v, o_v = int(s_v), int(o_v)
if s_v == o_v:
continue
return False
elif s_k or o_k:
return False
else:
s_v, o_v = self.d(s_v), self.d(o_v)
if s_v == o_v:
continue
return False
return True
__le__ = object.__le__
__ne__ = object.__ne__
__gt__ = object.__gt__
__ge__ = object.__ge__
import functools
import itertools
import enum
class OrderingType(enum.Enum):
PerWordSwapCase = lambda s: s.lower()+s.swapcase()
PerCharacterSwapCase = lambda s: "".join(c.lower()+c.swapcase() for c in s)
class NaturalOrdering:
@classmethod
def by(cls, ordering):
def wrapper(string):
return cls(string, ordering)
return wrapper
def __init__(self, string, ordering=OrderingType.PerCharacterSwapCase):
self.string = string
self.groups = [ (k,int("".join(v)))
if k else
(k,ordering("".join(v)))
for k,v in
itertools.groupby(string, str.isdigit)
]
def __repr__(self):
return "{}({})".format\
( type(self).__name__
, self.string
)
def __lesser(self, other, default):
if not isinstance(self, type(other)):
return NotImplemented
for s,o in itertools.zip_longest(self.groups, other.groups):
if s is None:
return True
if o is None:
return False
s_k, s_v = s
o_k, o_v = o
if s_k and o_k:
if s_v == o_v:
continue
return s_v < o_v
elif s_k:
return True
elif o_k:
return False
else:
if s_v == o_v:
continue
return s_v < o_v
return default
def __lt__(self, other):
return self.__lesser(other, default=False)
def __le__(self, other):
return self.__lesser(other, default=True)
def __eq__(self, other):
if not isinstance(self, type(other)):
return NotImplemented
for s,o in itertools.zip_longest(self.groups, other.groups):
if s is None or o is None:
return False
s_k, s_v = s
o_k, o_v = o
if s_k and o_k:
if s_v == o_v:
continue
return False
elif s_k or o_k:
return False
else:
if s_v == o_v:
continue
return False
return True
# functools.total_ordering doesn't create single-call wrappers if both
# __le__ and __lt__ exist, so do it manually.
def __gt__(self, other):
op_result = self.__le__(other)
if op_result is NotImplemented:
return op_result
return not op_result
def __ge__(self, other):
op_result = self.__lt__(other)
if op_result is NotImplemented:
return op_result
return not op_result
# __ne__ is the only implied ordering relationship, it automatically
# delegates to __eq__
>>> import natsort
>>> import timeit
>>> l1 = ['Apple', 'corn', 'apPlE', 'arbour', 'Corn', 'Banana', 'apple', 'banana']
>>> l2 = list(map(str, range(30)))
>>> l3 = ["{} {}".format(x,y) for x in l1 for y in l2]
>>> print(timeit.timeit('sorted(l3+["0"], key=NaturalStringA)', number=10000, globals=globals()))
362.4729259099986
>>> print(timeit.timeit('sorted(l3+["0"], key=NaturalStringB)', number=10000, globals=globals()))
189.7340817489967
>>> print(timeit.timeit('sorted(l3+["0"], key=NaturalOrdering.by(OrderingType.PerCharacterSwapCase))', number=10000, globals=globals()))
69.34636392899847
>>> print(timeit.timeit('natsort.natsorted(l3+["0"], alg=natsort.ns.GROUPLETTERS | natsort.ns.LOWERCASEFIRST)', number=10000, globals=globals()))
98.2531585780016
প্রাকৃতিক বাছাই উভয়ই বেশ জটিল এবং অস্পষ্টভাবে একটি সমস্যা হিসাবে সংজ্ঞায়িত। unicodedata.normalize(...)
আগে দৌড়াতে ভুলবেন না , এবং এর str.casefold()
পরিবর্তে ব্যবহার বিবেচনা করুন str.lower()
। সূক্ষ্ম এনকোডিংয়ের সমস্যাগুলি সম্ভবত আমি বিবেচনা করি নি। সুতরাং আমি স্থায়ীভাবে নেটসোর্ট লাইব্রেরির প্রস্তাব দিই । আমি গিথুব সংগ্রহস্থলের দিকে এক ঝলক দেখেছি; কোড রক্ষণাবেক্ষণ বড় হয়েছে।
আমি যে সমস্ত অ্যালগোরিদম দেখেছি সেগুলি অনুলিপি করা এবং অক্ষরগুলি হ্রাস করা এবং কেস অদলবদলের মতো কৌশলগুলির উপর নির্ভর করে। এটি চলমান সময়কে দ্বিগুণ করার সময়, কোনও বিকল্পের জন্য ইনপুট চরিত্রের সেটটিতে মোট প্রাকৃতিক ক্রম প্রয়োজন। আমি এটি ইউনিকোড স্পেসিফিকেশনের অংশ বলে মনে করি না, এবং যেহেতু আরও অনেক ইউনিকোড ডিজিট রয়েছে তাই এই [0-9]
ধরণের বাছাই করা সমানভাবে উদ্বেগজনক হবে। আপনি লোকেল সচেতন তুলনা চান, সঙ্গে আপনার স্ট্রিং প্রস্তুত locale.strxfrm
পাইথন এর প্রতি বাছাইকরণ কিভাবে ।