3  الأعداد

الأعداد هي الحجر الأساس في أي برنامج. بل يُبنى عليها فهم البرنامج للنصوص المكتوبة والأصوات والصور وجميع أنواع البيانات. ولذلك وجب معرفة أساسياتها.

ملاحظة: كل ما بعد علامة # يعتبر تعليقًا يتجاهله الحاسب ولا يفسره. فنستخدمه للملاحظات والشرح في ثنايا القطعة البرمجية.

الحساب والمقارنة

x = 5
y = 10

print(x + y) # الجمع
print(x - y) # الطرح
print(x * y) # الضرب
print(x / y) # القسمة
print(x % y) # باقي القسمة
print(x ** y) # الأس
15
-5
50
0.5
5
9765625

المقارنة بين الأعداد:

x = 5
y = 10

print(x == y) # التطابق
print(x != y) # الاختلاف
print(x > y) # أكبر
print(x < y) # أصغر
print(x <= y) # أصغر أو يساوي
print(x >= y) # أكبر أو يساوي
False
True
False
True
True
False

ترتيب العمليات

ترتيب العمليات هو نفسه كما في الرياضيات:

  1. الأقواس: ()
  2. الأسس: **
  3. الضرب والقسمة: * و /
  4. الجمع والطرح: + و -

للتفصيل الشامل انظر: ترتيب التقييم

إليك ثلاثة أمثلة لترى أثر وضع الأقواس من عدمه. ولاحظ أننا نستعمل جملة التوكيد (assert) التي تسكُت إن كان الشيء الذي أمامها جملة منطقية صحيحة؛ وإلا فهي تظهر رسالة خطأ. وسترى أننا نستعملها بكثرة لتقرير لوازم ما نبينه في الدرس:

assert 3 + 2 * 5 == 13
assert (3 + 2) * 5 == 25
assert 8 - 4 / 2 == 6
assert (8 - 4) / 2 == 2
assert 2 ** 3 * 4 == 32
assert (2 ** 3) * 4 == 32

التعيين النسبي

يراجع: التعيين النسبي.

لأن التعيين النسبي يستعمل بكثرة، فوجب علينا التعرف عليه، وأحيانًا نحتاج لاستعماله. فجمل التعيين التالية متكافئة:

  • i = i + 1 تعادل i += 1
  • i = i - 1 تعادل i -= 1
  • i = i * 2 تعادل i *= 2
  • i = i / 2 تعادل i /= 2

جرب النص البرمجي أدناه لترى النتيجة:

i = 0
i = i + 1
i += 1
print(i)
2

وإن أتيت من لغات أخرى مثل سي أو جافا فإنك تعلم أن تعبير i++ يعني زيادة المتغير i بواحد. لكن في بايثون لا يوجد هذا التعبير. فالنص التالي سيؤدي إلى خطأ:

i++
print(i)
  Cell In[41], line 1
    i++
       ^
SyntaxError: invalid syntax

وحدة الرياضيات (math)

import math

x = 5.4

كل هذه الطرق الثلاث يتم فيها حساب الأس:

  • الأولى pow فعل مبني
  • الثانية math.pow فعل من وحدة الرياضيات
  • الثالثة x ** 2 عن طريق المعامل **

\[ x^2 = x \times x \]

assert(
    pow(x, 2) ==
    math.pow(x, 2) ==
    x ** 2 ==
    x * x
)

وكذلك الجذر التربيعي:

\[ \sqrt{x} = x^{1/2} \]

  • الأولى math.sqrt فعل من وحدة الرياضيات
  • الثانية x ** 0.5 عن طريق المعامل **
assert (
    math.sqrt(x) ==
    x ** 0.5
)

تقريب لأقرب عدد صحيح أصغر:

\[ \text{floor}(x) = \lfloor x \rfloor \]

math.floor(5.4)
5

تقريب لأقرب عدد صحيح أكبر:

\[ \text{ceil}(x) = \lceil x \rceil \]

math.ceil(5.4)
6

حذف ما بعد الفاصلة:

math.trunc(5.4)
5

تقريب إلى رقمين بعد الفاصلة:

round(5.436, 2)
5.44

ملاحظة: الإجراء الأخير round ليس مستوردًا من math وإنما هو مُضمَّن في النطاق العام؛ لذا لا تحتاج لاستيراد شيء. قد تتساءل عن وجود سبب منطقي. لكنني أقول لك: هو سبب واقعي بسبب ظروف تطوير اللغة؛ لا أكثر ولا أقل.

مجموعة الأعداد

أما التعامل مع المجموعات (كمجموعة الأعداد) فسيأتي في باب المجموعات المرتبة. لكننا نعرض لمثال بسيط للتعامل مع المجموعات العددية:

xs = [10, 20, 30, 40, 50]

توفر بايثون الدوال التالية للمجموعة العددية:

  • الطول (عدد العناصر): len (من كلمة length)
  • مجموع العناصر: sum
  • العنصر الأكبر: max
  • العنصر الأصغر: min
print('length:', len(xs))
print('total:', sum(xs))
print('average:', sum(xs) / len(xs))
print('maximum:', max(xs))
print('minimum:', min(xs))
length: 5
total: 150
average: 30.0
maximum: 50
minimum: 10

الإحصاء

ونستعرض مجموعة من الدوال في مكتبة الإحصاء الأساسية في بايثون، منها:

  • المتوسط الحسابي: statistics.mean
  • الوسيط: statistics.median
  • المنوال: statistics.mode
  • الانحراف المعياري: statistics.stdev
import statistics

xs = [
    20, 21, 22, 23, 20, 22, 20,
    18, 24, 18, 21, 23, 19, 20,
    20, 21, 22, 23, 20, 22, 20,
    18, 24, 18, 21, 23, 19, 20
]

print('mean:', statistics.mean(xs))
print('median:', statistics.median(xs))
print('mode:', statistics.mode(xs))
print('standard deviation:', statistics.stdev(xs))
mean: 20.785714285714285
median: 20.5
mode: 20
standard deviation: 1.8126539343499315

أنواع العدد في بايثون

  • العدد الصحيح (int)
  • القيمة المنطقية (bool)
  • العدد العشري (float)
  • العدد المركب (complex)

صفة العددية تجوِّز العمليات بينها من جمع وطرح وقسمة ومقارنة. فالإجراء فيه تفصيل تتكفل به بايثون عنك إذْ تمثيلها الداخلي في الحقيقة مختلف.

فالتمثيل الداخلي للأعداد له أثر:

  1. في مساحة التخزين
  2. دقة العدد؛ وبالتالي صحة الحساب
  3. سرعة الحساب

لكننا في هذه المرحلة لن نخوض في هذه التفاصيل. وإنما أردنا بيان وجه الاختلاف بينها وسبب تعدد أنواع العدد في بايثون ولغات البرمجة عمومًا.

العدد الصحيح (int)

الوظيفة: الفهرسة والعد والترتيب والزيادة والنقصان والفرق ونحو ذلك

age = 20
level = 3
index = -2
start, end = -5, 10
left, middle, right = 3, 5, 7

يؤتى بالإجراء type لمعرفة نوع المتغير:

assert int == type(age) 
assert int == type(level) 
assert int == type(index) 
assert int == type(start) == type(end)
assert int == type(left) == type(middle) == type(right)

المجال: نقصد بالمجال هو أعلى قيمة ممكنة وأقل قيمة ممكنة للنوع. فنوع العدد الصحيح (int ويرمز له بالرياضي \(\mathbb{Z}\)) هو في واقع الحواسيب محكوم بعدد الخانات (Bits) التي يتم استعمالها في تمثيله. ورغم أن بايثون تخفي عنا عدد الخانات؛ إلا أنها تتعامل مع هذا الواقع، ولذلك وجب معرفته:

  • 8-بت: \(\{x \in \mathbb{Z} \mid -2^7 \leq x < 2^7\} = \{-128, \ldots, 127\}\) أي أن أقل عدد ممكن في 8 خانات: \(-128\) وأعلى عدد ممكن: \(127\). وهلم جرا.
  • 16-بت: \(\{x \in \mathbb{Z} \mid -2^{15} \leq x < 2^{15}\}\)
  • 32-بت: \(\{x \in \mathbb{Z} \mid -2^{31} \leq x < 2^{31}\}\)
  • 64-بت: \(\{x \in \mathbb{Z} \mid -2^{63} \leq x < 2^{63}\}\)
  • 128-بت: \(\{x \in \mathbb{Z} \mid -2^{127} \leq x < 2^{127}\}\)

لاحظ أن سبب محدودية ذاكرة الأجهزة القديمة لـ4GB بايت يعود لكون معمارية الجهاز محددة بـ32-بت. ثم لما طورت المعمارية إلى 64-بت أصح حد الذاكرة: 17,179,869,184 GB (16 exabytes)

القيمة المنطقية (bool)

وهي مجموعة العددين: \(\{0, 1\}\) الذين يمثل لهما بالكلمتين: True و False وذلك لتبيين وظيفتهما المنطقية.

assert True  == bool(1) == 1
assert False == bool(0) == 0

الوظيفة: تستعمل في الجمل الشرطية وحلقات التكرار، والمقارنة بين الأشياء.

نلجئ الكلام عنها إلى باب الشرط والتكرار.

العدد العشري (float)

الوظيفة: تمثيل الكميات مثل المال، المسافة، والوقت

distance = 100.0
price = 10.5
time = 1.5
temperature = 36.6
difference = 0.001

نفحص أنواعها:

assert float == type(distance)
assert float == type(price)
assert float == type(time)
assert float == type(temperature)
assert float == type(difference)

المجال: يعبر الرمز \(\mathbb{R}\) عن مجموعة الأعداد العشرية (float). وهي تتبع نظام التمثيل IEEE 754.

  • 32-بت: \(\{x \in \mathbb{R} \mid -2^{31} \leq x < 2^{31}\} = \{-3.4 \times 10^9, \ldots, 3.4 \times 10^9\}\)
  • 64-بت: \(\{x \in \mathbb{R} \mid -2^{63} \leq x < 2^{63}\} = \{-1.8 \times 10^{19}, \ldots, 1.8 \times 10^{19}\}\)
  • 128-بت: \(\{x \in \mathbb{R} \mid -2^{127} \leq x < 2^{127}\} = \{-1.2 \times 10^{38}, \ldots, 1.2 \times 10^{38}\}\)

اختلاف نوع العدد

إذا اختلف النوع تُقدَّرُ الترقيةُ للأشمل، وذلك بحسب ناتج العملية:

  • جمع صحيح وعشري = عشري: int + float = float
  • قسمة صحيح على صحيح = عشري (لأننا نحتاج للفواصل): int / int = float
  • القسمة الصحيحة بين صحيح وصحيح = صحيح: int // int = int

المثال الأول: جمع عدد صحيح وعدد عشري:

a = 1 + 1.0
print(a)
assert type(a) == float
2.0

المثال الثاني: قسمة عدد صحيح على عدد عشري:

c = 1 / 2
print(c)
assert type(c) == float
0.5

المثال الثالث: استعمال القسمة الصحيحة //:

b = 9 // 2
print(b)
assert type(b) == int
4

س: لماذا النتيجة 4؟

ج: لأن قسمة 9 على 2 تُنتِج 4.5 ولكن بايثون تقربها لأقرب عدد صحيح أصغر وهو 4؛ وذلك لأننا اخترنا القسمة الصحيحة بالعلامة // وليس القسمة العشرية بالعلامة /.

الفرق بين النوع العددي والنوع النصي للعدد

تأمل المتغيرين

a = 50
b = '50'
  • الأول: عدد صحيح (int)
  • الثاني: حرفان (str)

نستعمل جمل التوكيد لبيان ذلك:

assert type(a) == int
assert type(b) == str
assert type(a) != type(b)

ومقتضى ذلك: امتناع عملية الجمع: a + b

'5' + 5
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[62], line 1
----> 1 '5' + 5

TypeError: can only concatenate str (not "int") to str

بل يجب التحويل أولاً باستعمال الإجراء int الذي يفسر الأحرف كعدد صحيح:

a = 5
b = '5'

b = int(b)

assert a + b == 10

كتابة القيَم العددية

الحروفيَّة (Literals) هي رموز للقيم لبعض الأنواع المدمجة. مثال: 42 هو حرفيُّ عدد صحيح و 3.14 هو حرفيُّ عدد عشري.

وتخصيص الحرفيّ True للعدد 1 و False للعدد 0 ليس من قبيل الضرورة في اللغة وإنما من قبيل التسهيل (وفوق ذلك فإن بايثون تجعل له نوعًا خاصًّا وعمليات مصاحبة).

assert True  == bool(1) == 1
assert False == bool(0) == 0

كذلك خصصت بايثون e أو E للترميز العلمي (وجاء الحرف e من كلمة: Exponent) المخصص للأعداد العشرية الكبيرة والصغيرة.

assert 1e2 == 100
assert 1e9 == 1E9
assert 1e-4 == 0.0001

ويجوز استعمال الشرطة السفلية _ لفاصلة الألوف:

assert 1_000_000 == 1000000

وأما إن كنت تهتم بالتمثيل الثنائي أو الثماني أو الست عشري فذلك أيضًا له تعبيرات مخصصة:

  • 0b أو 0B للأرقام الثنائية
  • 0o أو 0O للأرقام الثمانية
  • 0x أو 0X للأرقام الست عشرية

وإليك تطبيق ذلك:

assert 0b1010 == 10
assert 0o10 == 8
assert 0x10 == 16

وأخيرًا يمكن استعمال j أو J للأعداد المركبة:

assert 1 + 2j == 2j + 1

خلاصة

عرفنا الرقم والعمليات الممكنة عليه. لكننا سنتعرف على استعماله أكثر في الدروس القادمة، ويتعذر حصر جميع ما يستفاد منه فيه في درس واحد، لأنه من أكثر الأمور شيوعًا في البرمجة.

ننتقل الآن لباب الشرط والتكرار حيث الجمل الشرطية والتعيين المشروط.