تمرينات

تمرين 6.1
اكتب عملية اسمها isDivisible تأخذ عددين صحيحين، n وm وتعيد القيمة true إذا كان n قابلاً للقسمة على m وfalse فيما عدا ذلك.

تمرين 6.2

يمكن التعبير عن العديد من العمليات المعقدة بشكل مختصر باستخدام عملية "الضرب-جمع"، التي تأخذ ثلاثة معاملات وتحسب a*b + c. حتى أن بعض المعالجات توفر معدات خاصة لتنفيذ هذه العملية للأعداد العشرية.
a. أنشئ برنامجاً جديداً باسم Multadd.java.
b. اكتب عملية اسمها multadd تأخذ ثلاثة معاملات من نوع double وتطبع ناتج ضرب-جمعهم.
c. اكتب عملية main تختبر multadd باستدعائها باستخدام معاملات بسيطة، مثل 1.0، 2.0، 3.0.
d. أيضاً في main، استعمل multadd لحساب القيم التالية:


e. اكتب عملية تدعى yikes تأخذ عدداً عشرياً كمعامل وتستخدم multadd لحساب وطباعة

مساعدة: العملية الرياضية التي ترفع e إلى قوة هي Math.exp.
في الجزء الأخير، ستكتب عملية تستدعي عملية أخرى قمتَ بكتابتها. كلما فعلت ذلك، سيكون من الجيد أن تختبر العملية الأولى بتأن قبل البدء بالعمل على الثانية. وإلا، فقد تجد نفسك تدقق عمليتين في نفس الوقت، ما قد يصبح صعباً جداً.
إن أحد أهداف هذا التمرين هو التدرب على "مطابقة النموذج" – "pattern-matching": القدرة على التعرف على مشكلة محددة كحالة خاصة من فئة عامة من المشاكل.

تمرين 6.3
إذا أعطيتَ ثلاثة عيدان، فقد تستطيع ترتيبها بشكل مثلث وقد لا تستطيع. مثلاً، إذا كان طول أحد العيدان 12 بوصة وكان طول كل من العودين الآخرين 1 بوصة، فمن الواضح أنك لن تستطيع جعل العودين القصيرين يلتقيان في المنتصف. بالنسبة لأي ثلاثة أطوال، هناك اختبار بسيط لمعرفة ما إذا كان تشكيل مثلث منها ممكناً:
"إذا كان أي واحد من الأطوال الثلاثة أكبر من مجموع الطولين الآخرين، لا يمكنك تشكيل مثلث
منها، وبخلاف ذلك تستطيع"
اكتب عملية اسمها isTriangle تأخذ ثلاثة أعداد صحيحة كمتحولات، وتعيد قيمة إما true وإما false، اعتماداً على إمكانية أو عدم إمكانية تشكيل مثلث من عيدان أطوالها تساوي الأعداد المعطاة.
الهدف من هذا التمرين هو استعمال التعليمات الشرطية لكتابة عملية مثمرة.

تمرين 6.4
ما هو خرج البرنامج التالي؟ إن الغرض وراء هذا التمرين هو التأكد من أنك تفهم العوامل المنطقية ومجرى التنفيذ في العمليات المثمرة.
كود:
public static void main (String[] args) {
  boolean flag1 = isHoopy (202);
  boolean flag2 = isFrabjuous (202);
  System.out.println (flag1);
  System.out.println (flag2);
  if (flag1 && flag2) {
    System.out.println ("ping!");
  }
  if (flag1 || flag2) {
    System.out.println ("pong!");
  }
}
 
public static boolean isHoopy (int x) {
  boolean hoopyFlag;
  if (x%2 == 0) {
    hoopyFlag = true;
  } else {
    hoopyFlag = false;
  }
  return hoopyFlag;
}
 
public static boolean isFrabjuous (int x) {
  boolean frabjuousFlag;
  if (x > 0) {
    frabjuousFlag = true;
  } else {
    frabjuousFlag = false;
  }
  return frabjuousFlag;
}

تمرين 6.5

المسافة بين نقطتين (x1 , y1) و(x2 , y2) هي
اكتب عملية اسمها distance تأخذ أربعة معاملات من نوع double –x1, y1, x2, y2- وتطبع المسافة بين النقطتين.
عليك أن تفترض وجود عملية اسمها sumSquares تحسب وتعيد مجموع مربعات معاملاتها، مثلاً:
كود:
double x = sumSquares (3.0, 4.0);
ستسند القيمة 25.0 للمتغير x.
الغرض من هذا التمرين هو كتابة عملية جديدة تستخدم عملية موجودة. عليك كتابة عملية واحدة فقط: distance. لا تكتب العملية sumSquares ولا main كما لا تستدعي distance.

تمرين 6.6

إن الغرض من هذا التمرين هو استخدام المخططات الهرمية لفهم تنفيذ برنامج تعاودي.
كود:
public class Prod {
  public static void main (String[] args) {
    System.out.println (prod (1, 4));
  }
  public static int prod (int m, int n) {
    if (m == n) {
      return n;
    } else {
      int recurse = prod (m, n-1);
      int result = n * recurse;
      return result;
    }
  }
}
a. ارسم مخططاً هرمياً يبين حالة البرنامج قبيل اكتمال حالة prod الأخيرة. ما هو خرج هذا البرنامج؟
b. اشرح باستعمال بضعة كلمات ما تفعله العملية prod.
c. أعد كتابة prod بدون استخدام المتغيرات المؤقتة recurse وresult.

تمرين 6.7

الغرض من هذا التمرين هو ترجمة تعريف تعاودي إلى عملية مكتوبة بلغة Java. يعرف تابع أكرمان (Ackerman function) بالنسبة للأعداد غير السالبة كما يلي:

اكتب عملية اسمها ack تأخذ عددين صحيحين كمعاملات وتحسب وإعادة قيمة تابع أكرمان.
اختبر عملية أكرمان باستدعائها من main وطباعة القيمة المعادة.
تحذير: القيمة المعادة تكبر كثيراً بسرعة كبيرة. عليك اختبارها مع قيم صغيرة للمتغيرين n وm (ليس أكبر من 2).

تمرين 6.8

a. أنشئ برنامجاً يدعى Recurse.java واكتب فيه العمليات التالية:
كود:
// first: returns the first character of the given String
public static char first (String s) {
   return s.charAt (0);
}
 
// last: returns a new String that contains all but the
// first letter of the given String
public static String rest (String s) {
   return s.substring (1, s.length());
}
 
// length: returns the length of the given String
public static int length (String s) {
   return s.length();
}
b. اكتب بعض الشفرة في main لاختبار هذه العمليات. تأكد من أنها تعمل، وتأكد من أنك قد فهمت وظائفها.
c. اكتب عملية باسم printString تأخذ سلسلة محرفية كمعامل وتطبع أحرف تلك السلسلة، واحد على كل سطر. يجب أن تكون عملية فارغة (void method).
d. اكتب عملية اسمها printBackward تفعل بنفس ما تفعله printString عدا أنها تطبع السلسلة بالمقلوب (الحرف الأخير على السطر الأول).
e. اكتب عملية اسمها reverseString تأخذ سلسلة محرفية كمعامل وتعيد سلسلة جديدة كقيمة معادة. يجب أن تحتوي السلسلة الجديدة على نفس حروف السلسلة المعطاة، لكن بترتيب معكوس. مثلاً، سيكون خرج الشفرة التالية
كود:
String backwards = reverseString ("Allen Downey");
System.out.println (backwards);
كما يلي
yenwoD nellA

تمرين 6.9

اكتب عملية تعاودية اسمها power تأخذ عدداً عشرياً x وعدداً صحيحاً n وتعيد القيمة xn.
مساعدة: التعريف التعاودي لهذه العملية هوpower (x, n) = x * power (x, n-1) . أيضاً، تذكر أن أي شيء مرفوع للقوة صفر يعطي 1.
تحد اختياري: يمكنك جعل هذه العملية أكثر فاعلية، عندما يكون n زوجياً، باستخدام
تمرين 6.10 (هذا التمرين مبني على الصفحة 44 من كتاب Structure and Interpretation of Computer Programs للمؤلفين Ableson and Sussman).
الخوارزمية التالية تعرف باسم خوارزمية إقليدس لأنها مكتوبة في كتاب العناصر لإقليدس (الكتاب 7، 300 ق.م.). قد تكون هذه أقدم خوارزمية معروفة[1].
تبنى الخوارزمية على الملاحظة التالية، إذا كان r باقي قسمة a على b، عندئذ تكون القواسم المشتركة للعدين a وb هي نفس القواسم المشتركة للعدين b وr. وبالتالي يمكننا استخدام المعادلة
كود:
gcd(a, b) = gcd(b, r)
لتحويل معضلة حساب ق.م.أ (القاسم المشترك الأكبر) لعددين صحيحين إلى حساب القاسم المشترك الأكبر لعددين أصغر فأصغر. مثلاً،
كود:
gcd(36, 20) = gcd(20, 16) = gcd(16, 4) = gcd(4, 0) = 4
ينتج أن القاسم المشترك الأكبر (GCD Great Common Divider) للعددين 36 و20 هو 4. يمكن البرهان على أنه من أجل أي عددين ابتدائيين، فإن تكرار هذه العملية سينتهي إلى حساب ق.م.أ لعددين يكون الثاني منهما يساوي الصفر. ويكون ق.م.أ المطلوب هو العدد الأول منهما.
اكتب عملية اسمها gcd تأخذ عددين صحيحين كمعاملين وتستعمل خوارزمية إقليدس لحساب وإرجاع القاسم المشترك الأكبر للعددين.



[1] لتعرف ما هي "الخوارزمية"، انظر القسم 11.13