PythonGuide/Functional Programming

من PFWiki

اذهب إلى: تصفح, البحث

البرمجة الوظيفية وبايثون

هذا الفصل اختيارى.. هنتكلم عن وحدتى functools و itertools

functools

reduce(function, iterable[, init]) ملحوظة هى هى الدالة reduce من اسمها "تقليل" الهدف منها هو تقليل sequence لقيمة واحدة

#reduce (func, seq[, init])
print reduce((lambda x, y: x+y), [1, 2, 3, 4, 5]) #15
#((((1+2)+3)+4)+5)
print reduce((lambda x, y: x*y), [1, 2, 3, 4, 5]) #120

partial(func, *args, **kwargs) هدفها هو تغليف دالة ما ومعاملاتها واعادة كائن -مشابه للدوال- يحويها هذا المفهوم يسمى Currying

>>> def sayhi(to):
...     print "Hi, ", to
 
>>> f=lambda to="Ahmed":sayhi(to)
>>> f
<function <lambda> at 0xb7df08b4>
>>> f()
Hi,  Ahmed

نقدر نسهل نفس المثال بإستخدام partial كالتالى

>>> part_f=partial(sayhi, "Ahmed")
>>> part_f()
Hi,  Ahmed


تقدر تستخدمها بطريقة انك تغلف دالة ومجموعة معاملات قد تستكمل لاحقا

>>> def someparty(*guests):
...     print "Visitors: "
...     for g in guests:
...             print "\t", g
 
>>> someparty("Ahmed")
Visitors: 
	Ahmed
>>> someparty("Ahmed", "Mido")
Visitors: 
	Ahmed
	Mido
>>> p=partial(someparty, "Ahmed")
>>> p("Mido")
Visitors: 
	Ahmed
	Mido
>>> p
<functools.partial object at 0xb79d8d74>
>>> p("Mido", "Youssef")
Visitors: 
	Ahmed
	Mido
	Youssef


احد الإستخدامات العملية لها فى التعامل مع PyQt4 كالتالى مثلا -لأنك ستواجه صعوبة فى تمرير المتغيرات مع connect لل slot

self.connect(btn, SIGNAL("clicked()"),
             partial(self.onClicked, ARG))

طبعا تقدر تستخدم lambda بنفس الكيفية!

هنا مثلا نقوم بربط الإشارة clicked للزر button2 بslot -تغلف self.anyButton اللتى تأخذ معامل واحد اللتى يتم تنفيذها عند محاولة استدعاء الslot- هكذا قد تغلبنا على العملية اليس كذلك ؟


itertools

any(iterable) عمرك كان عندك مثلا list من مجموعة ارقام وعايز تعرف هل فيها رقم اكبر من 3 مثلا ؟

print any(x>3 for x in [1, 3, 4, 5, 6, 7]) #True

all(iterable) عمرك كان عندك مثلا list من مجموعة ارقام وعايز تعرف هل كلها اكبر من 3 مثلا ؟

print all(x>3 for x in [1, 3, 4, 5, 6, 7]) #False

الفكرة هى ان بتنشئ list من True/False ويتم حساب الناتج عن طريقها سواء على قيمة واحدة او كل القيم لاحظ ان هنا ال iterable انشئناه بإستخدام ال list comprehension ولو اتكلمنا عنها تانى هنختصرها ل LC

repeat(obj, times=None) بتقوم بعمل iterator يعيد نفس الكائن بعدد times او للأبد اذا كان None

>>> list(repeat(10, 3))
[10, 10, 10]

هنلاحظ ان الناتج [10, 10, 10] تم تكرار الكائن10 ل 3 مرات وهكذا

takewhile(pred, seq) بتأخذ عناصر من seq كلما يتم تأكيد الشرط المؤكد pred

>>> list(takewhile(lambda x: x<3, [0, 1, 2, 3, 4]))
[0, 1, 2]


dropwhile(pred, seq) العكس بقة بتترك عناصر من seq كلما يتم تأكيد ال pred

>>> list(dropwhile(lambda x: x<3, [0, 1, 2, 3, 4, 6, 7]))
[3, 4, 6, 7]


groupby(seq[, key=None]) على فرض عندنا سلسلة من العناصر وعايزين نجمع كل العناصر من seq بإستخدام key function افتراضيا ال key هو العنصر- lambda x: x-

>>> list(groupby("111122233344"))
 
[('1', <itertools._grouper object at 0xb77ef36c>), ('2', <itertools._grouper object at 0xb77ef3ec>), ('3', <itertools._grouper object at 0xb77ef40c>), ('4', <itertools._grouper object at 0xb77ef42c>)]
 
 
print [list(i) for (x,i) in itertools.groupby("111122233344")]

الناتج

[['1', '1', '1', '1'], ['2', '2', '2'], ['3', '3', '3'], ['4', '4']]

وهكذا الفكرة ان groupby بتجمع العناصر وبتعيدها فى صورة العنصر وiterator ليه و هكذا) فعشان كدا عملنا list I اذا عايز العناصر فقط مش list خد قيمة ال x بس

مثال اخر

things = [("animal", "bear"), ("animal", "duck"), ("plant", "cactus"), ("vehicle", "speed boat"), ("vehicle", "school bus")]
for key, group in groupby(things, lambda x: x[0]):
    for thing in group:
        print "A %s is a %s." % (thing[1], key)
    print " "

الناتج

A bear is a animal.
A duck is a animal.
A cactus is a plant.
A speed boat is a vehicle. 
A school bus is a vehicle.


مرجع للمثال http://stackoverflow.com/questions/773/how-do-i-use-pythons-itertoolsgroupby

chain(*iters) ينشئ iterator ليعيد العناصر من اول عنصر يتم تمريره حتى ينتهى فينتقل للعنصر التالى وهكذا او تقدر تقول بتنشئ سلسلة بين ال iters

>>> for el in chain("Hello", "World", range(3)): 
...     print el
 
 
H
e
l
l
o
W
o
r
l
d
0
1
2


ifilter(pred, seq) نفس فكرة filter ..تعيد iterator فى لكل عنصر يقع فى نطاق ال pred

>>> for x in ifilter(lambda x: x>3, [3,2, 1, 9, 8]): print x
... 
9
8
>>> list(ifilter(lambda x: x>3, [3,2, 1, 9, 8]))
[9, 8]


filter=ifilter معادا فى كيفية اعادة الناتج

>>> filter(lambda x: x>3, [3, 2, 1, 9, 8])
[9, 8]

حيث تعيد filter ناتج من list بينما تعيد ifilter ناتج من iterator

ifilterfalse(pred, seq) حيث تعيد iterator لكل عنصر لايقع فى نطاق pred

>>> list(ifilterfalse(lambda x: x>3, [3, 2, 1, 9, 8]))
[3, 2, 1]


izip(*iters)

خمن؟ ايوه صح هى فعلا مشابهه ل zip ولكن بتعيد iterator 
>>> list(izip("Hell", [1,2,3, 4]))
[('H', 1), ('e', 2), ('l', 3), ('l', 4)]
>>> zip("Hell", [1,2,3,4])
[('H', 1), ('e', 2), ('l', 3), ('l', 4)]


للمزيد راجع وثائق python حولهم

→ External Programs Functional Programming InputOutput ←
أدوات شخصية