السابقالفهرسالتالي

الفصل 12

المصفوفات

 

المصفوفة (array) هي مجموعة من القيم حيث تكون كل قيمة معرفة بوساطة دليل. يمكنك عمل مصفوفات من الأعداد الصحيحة أو العشرية أو أي نوع آخر، لكن يجب أن تكون جميع العناصر من نوع واحد في المصفوفة الواحدة.

نحوياً، تكتب الأنواع المصفوفية في Java كما تكتب الأنواع الأخرى عدا أنها تتبع بالقوسين المربعين []. مثلاً، int[] هو نوع "مصفوفة أعداد صحيحة" وdouble[] هو النوع "مصفوفة أعداد عشرية".

يمكنك التصريح عن متغيرات من هذين النوعين بالطرق المعتادة:

int[] count;
double[] values;
يتم إعطاء القيمة null لهذين المتغيرين، حتى تهيئهما. لإنشاء المصفوفة الفعلية، استعمل new.
count = new int[4];
values = new double[size];
تجعل تعليمة الإسناد الأولى count يشير إلى مصفوفة من أربعة أعداد صحيحة؛ تعليمة الإسناد الثنية تجعل values يشير إلى مصفوفة أعداد عشرية. إن عدد العناصر في values يعتمد على size. يمكنك استعمال أي تعبير حسابي صحيح (يكون ناتجه عدداً صحيحاً) كحجم لمصفوفة.

يبين الشكل التالي كيف تمثل المصفوفات في مخططات الحالة:

الأرقام الكبيرة داخل الصناديق هي عناصر (elements) المصفوفة. تستعمل الأرقام الصغيرة خارج الصناديق لتعريف كل صندوق. عندما تحجز مصفوفة أعداد صحيحة، تعطى عناصرها القيمة الافتراضية 0.

12.1 الوصول إلى العناصر

لتخزين القيم في المصفوفة، استخدم عامل []. مثلاً، count[0] تشير إلى العنصر الصفري (رقم صفر – zeroeth) في المصفوفة، وcount[1] تشير إلى العنصر الأول.

يمكنك استخدام عامل [] في أي مكان من عبارة ما:

count[0] = 7;
count[1] = count[0] * 2;
count[2]++;
count[3] -= 60;
هذه التعليمات هي تعليمات إسناد مشروعة. ها هي نتيجة قطعة الشفرة هذه:

أن عناصر المصفوفة مرقمة من 0 إلى 3، ما يعني عدم وجود عنصر دليله 4. يجب أن يكون هذا مألوفاً، نظراً لأننا رأينا الشيء نفسه عندما تعاملنا مع أدلة السلاسل المحرفية. ومع ذلك، فمن الأخطاء الشائعة تجاوز حدود المصفوفة، ما سيسبب الاستثناء ArrayOutOfBoundsException. كما هو الحال مع باقي الاستثناءات، سيعطيك البرنامج رسالة خطأ ثم يخرج.

يمكنك استعمال أي تعبير كدليل، طالما أنه من النوع int. من أكثر الطرق شيوعاً في تحديد أدلة المصفوفات هو استخدام متغير حلقة. مثلاً:

int i = 0;
while (i < 4) {
    System.out.println(count[i]);
    i++;
}
هذه حلقة while قياسية تعد من 0 إلى 4، وعندما يصبح متغير الحلقة i يساوي 4، لا يتحقق الشرط وتنتهي الحلقة. بالتالي، فإن جسم الحلقة ينفذ فقط عندما يكون i يساوي 0، 1، 2 و3.

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

12.2 نسخ المصفوفات

عندما تنسخ متغير مصفوفي، تذكر أنك تنسخ مرجعاً لمصفوفة. مثلاً:

double[] a = new double [3];
double[] b = a;
هذه الشفرة تنشئ مصفوفة مؤلفة من ثلاثة أعداد عشرية، وتجعل متغيرين مختلفين يشيران إليها. هذه الحالة هي شكل من الأسماء المستعارة.

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

double[] b = new double [3];
int i = 0;
while (i < 4) {
b[i] = a[i];
i++;
}

12.3 حلقات for

إن جميع الحلقات التي كتبناها حتى الآن لها عدد من العناصر المشتركة. كلها تبدأ بتهيئة متغير؛ وتختبر شرط يعتمد على ذلك المتغير؛ وفي كل حلقة يوجد شيء يؤثر على ذلك المتغير، مثل زيادة قيمته.

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

for (INITIALIZER; CONDITION; INCREMENTOR) {
   BODY
}
هذه التعليمة مكافئة لما يلي:
INITIALIZER;
while (CONDITION) {
   BODY
   INCREMENTOR
}
ما عدا أنها أكثر اختصاراً و، نظراً لأنها تضع جميع التعليمات المتعلقة بالحلقة في مكان واحد، أسهل للقراءة. مثلاً:
for (int i = 0; i < 4; i++) {
   System.out.println(count[i]);
}
تكافئ
int i = 0;
while (i < 4) {
   System.out.println(count[i]);
   i++;
}

تمرين 12.1

اكتب حلقة for لنسخ عناصر مصفوفة.

12.4 المصفوفات والكائنات

تتصرف المصفوفات بشكل مشابه للكائنات في العديد من النواحي:

بعض الكائنات التي تعاملنا معها، مثل المستطيلات (Rectangle)، تشبه المصفوفات، بمعنى أنها عبارة عن مجموعة من القيم. وهنا يطرح السؤال نفسه، "بم تختلف المصفوفة المؤلفة من 4 أعداد صحيحة عن كائن Rectangle؟"

إذا عدت إلى تعريف "المصفوفة" في بداية هذا الفصل، فستجد اختلافاً واحداً، وهو أن عناصر المصفوفة تعرف باستخدام أدلة، في حين تملك عناصر الكائن (متغيرات الحالة) أسماء.

يوجد اختلاف آخر وهو أن عناصر المصفوفة يجب أن تكون من نفس النوع. أما الكائنات فيمكن أن تملك متغيرات حالة من أنواع مختلفة.

12.5 طول المصفوفة

في الواقع، تملك المصفوفات متغير حالة وحيد له اسم: length. ومن غير المفاجئ، أنه يحتوي على طول المصفوفة (عدد عناصرها). من الجيد استخدام هذه القيمة كحد أعظمي لحلقة، بدلاً من استخدام قيمة ثابتة. وبذلك، لن تضطر إلى المرور على جميع الحلقات في البرنامج لتعديل الشرط، عندما يتغير حجم المصفوفة؛ لأنها ستعمل بشكل صحيح من أجل أي حجم للمصفوفة.

for (int i = 0; i < a.length; i++) {
   b[i] = a[i];
}
في آخر مرة يتم تنفيذ جسم الحلقة فيها، i يساوي a.length – 1، وهو دليل العنصر الأخير. عندما يكون i مساوياً لa.length، لا يتحقق الشرط ولا يتم تنفيذ جسم الحلقة، وهو شيء جيد، لأن تنفيذه كان سيتسبب في استثناء. هذه الشفرة تفترض أن المصفوفة b تحتوي عدداً من العناصر يساوي عدد عناصر a على الأقل.

تمرين 12.2

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

12.6 الأرقام العشوائية

معظم برامج الحاسوب تجري نفس الأفعال في كل مرة يتم تشغيلها، ولذلك يقال أنها حتمية (deterministic). في العادة، تكون الحتمية شيئاً جيداً، لأننا نتوقع أن تعطي العملية الحسابية نفس النتيجة كل مرة. لكن في بعض التطبيقات نحتاج من الحاسب أن يتصرف بشكل غير محسوب. الألعاب هي مثال واضح على هكذا تطبيقات، لكن يوجد غيرها أيضاً.

إن جعل البرنامج غير متوقع بالمرة (truly nondeterministic) ليس سهلاً، لكن توجد طرق لجعله يبدو غير محتم على الأقل. إحدى تلك الطرق توليد الأرقام العشوائية واستعمالها لتحديد خرج البرنامج. توفر Java، عملية تولد أرقاماً شبه عشوائية (pseudorandom)، التي قد لا تكون عشوائية تماماً، لكن مقارنة مع احتياجاتنا، فهي عشوائية بما يكفي.

انظر إلى وثائق العملية random في صنف Math. القيمة المعادة هي عدد عشري محصور بين 0.0 و1.0. لنتوخى الدقة، العدد أكبر من أو يساوي 0.0 وأصغر تماماً من 1.0. في كل مرة تستدعي random تحصل على الرقم التالي في سلسلة شبه عشوائية. لتشاهد مثالاً على ذلك، شغل هذه الحلقة:

for (int i = 0; i < 10; i++) {
   double x = Math.random();
   System.out.println(x);
}
لتوليد عدد عشري عشوائي محصور بين 0.0 وحد أعظمي متغير مثل high، يمكنك ضرب x بhigh. كيف يمكنك توليد عدد عشوائي بين حدين low وhigh؟ كيف تستطيع توليد عدد صحيح عشوائي؟

تمرين 12.3

اكتب عملية باسم randomDouble تأخذ عددين عشريين، low وhigh، وتعيد قيمة عشوائية عشرية x بحيث يكون low ≤ x < high.

تمرين 12.4

اكتب عملية باسم randomInt تأخذ متحولين، low وhigh، وتعيد قيمة عشوائية صحيحة أكبر أو تساوي low وأصغر تماماً من high.

12.7 مصفوفة الأعداد العشوائية

إذا كانت عملية randomInt التي كتبتها صحيحة، عندئذ فإن كل قيمة تنتمي إلى المجال من low إلى high-1 يجب أن تملك نفس الاحتمال. إذا ولدّت سلسلة طويلة من الأرقام، فيجب أن تظهر كل قيمة، تقريباً على الأقل، بنفس العدد من المرات.

إحدى الطرق لاختبار عمليتك هي توليد عدد كبير من القيم العشوائية، وتخزينهم في مصفوفة، وحساب عدد مرات تكرار كل قيمة.

تأخذ العملية التالية متحولاً وحيداً، حجم المصفوفة. وتحجز مصفوفة أعداد صحيحة جديدة، وتملأها بقيم صحيحة عشوائية، وتعيد مرجعاً للمصفوفة الجديدة.

public static int[] randomArray(int n) {
   int[] a = new int[n];
   for (int i = 0; i < a.length; i++) {
      a[i] = randomInt(0, 100);
   }
   return a;
}
نوع الإرجاع هو int[]، ما يعني أن هذه العملية تعيد مصفوفة أعداد صحيحة. لاختبار هذه العملية، سنحتاج إلى عملية تطبع محتويات مصفوفة.
public static void printArray(int[] a) {
   for (int i = 0; i < a.length; i++) {
      System.out.println(a[i]);
   }
}
الشفرة التالية تولد مصفوفة ما وتطبعها:
int numValues = 8;
int[] array = randomArray(numValues);
printArray(array);
كان الخرج على جهازي كالتالي
27
6
54
62
54
2
44
81
وهو يبدو عشوائي قليلاً. قد تختلف النتائج على جهازك.

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

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

12.8 العد

من الأساليب الجيدة لحل مشاكل مثل هذه، التفكير بعمليات بسيطة سهلة الكتابة، ثم جمعهم معاً لتشكيل الحل. هذه العملية تدعى التطوير من الأسفل إلى الأعلى (bottom-up development). انظر http://en.wikipedia.org/wiki/Top-down_and_bottom-up_design.

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

في القسم 8.7 رأينا حلقة اجتازت سلسلة محرفية وعدّت مرات تكرار حرف معطى في السلسلة. يمكنك اعتبار هذا البرنامج مثالاً عن نمط يدعى "العبور والعد – traverse and count". عناصر هذا النمط هي:

في هذه الحالة، يكون الوعاء هو مصفوفة أعداد صحيحة. الاختبار هو هل تنتمي درجة معطاة إلى مجال معين من القيم أم لا.

هنا عملية باسم inRange تحسب عدد العناصر في مصفوفة، التي تنتمي لمجال قيم معطى. المعاملات هي المصفوفة وعددين صحيحين يبينان حدي المجال الأدنى والأعلى.

public static int inRange(int[] a, int low, int high) {
  int count = 0;
  for (int i=0; i < a.length; i++) {
    if (a[i] >= low && a[i] < high) count++;
  }
  return count;
}
لم أحدد فيما إذا كان شيء ما مساوياً للحد الأدنى أو الأعلى سينتمي إلى المجال أم لا، لكن يمكنك أن ترى من الشفرة أن low ينتم إلى المجال في حين high لا ينتمي إليه. هذا سيمنعنا من عد أية عنصر مرتين.

الآن يمكننا حساب عدد العلامات الموجودة ضمن المجالات التي تهمنا:

int[] scores = randomArray(30);
int a = inRange(scores, 90, 100);
int b = inRange(scores, 80, 90);
int c = inRange(scores, 70, 80);
int d = inRange(scores, 60, 70);
int f = inRange(scores, 0, 60);

12.9 مخطط الأعمدة

هذه الشفرة تكرارية، لكنها مقبولة فقط طالما أن عدد المجالات صغير. لكن تخيل أننا نريد متابعة عدد تكرار كل علامة تظهر، كل القيم الممكنة من 0 إلى 100. أتريد كتابة هذا؟

int count0 = inRange(scores, 0, 1);
int count1 = inRange(scores, 1, 2);
int count2 = inRange(scores, 2, 3);
...
int count3 = inRange(scores, 99, 100);
لا أعتقد ذلك. ما تريده حقاً هو طريقة لتخزين 100 عدد صحيح، يفضل أن نستطيع استخدام دليل للوصول إلى كل واحد منها. مساعدة: مصفوفة.

أن مضمون عملية العد هو نفسه سواء استخدمنا عداداً مفرداً أو مصفوفة من العدادات. في هذه الحالة، سنهيئ المصفوفة خارج الحلقة؛ ثم، داخل الحلقة، نستدعي inRange ونخزن النتيجة:

int[] counts = new int[100];
for (int i = 0; i < counts.length; i++) {
   counts[i] = inRange(scores, i, i+1);
}
الشيء الجديد الوحيد هنا هو أننا أعطينا متغير الحلقة مهمتين: دليل للمصفوفة، ومعامل للعملية inRange.

12.10 حـل بدورة واحدة

هذه الشفرة تعمل، لكنها ليست فعالة كما ينبغي. في كل مرة تستدعي فيها inRange، يتم المرور على كامل المصفوفة. ومع ازدياد عدد المجالات، سيكون لدينا الكثير من عمليات الاجتياز.

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

ها هي الشفرة التي تجتاز مصفوفة العلامات، مرة واحدة، وتولد مخطط الأعمدة الخاص بها.

  int[] counts = new int[100];
  for (int i = 0; i < scores.length; i++) {
    int index = scores[i];
    counts[index]++;
}

تمرين 12.5

غلف هذه الشفرة في عملية باسم makeHist تأخذ مصفوفة علامات وتعيد مخطط الأعمدة (histogram) قيم المصفوفة.

12.11 المصطلحات

مصفوفة: مجموعة من القيم، حيث تكون جميع القيم من نفس النوع، وكل قيمة تعرف بدليل.

عنصر: واحد القيم في مصفوفة. يختار العامل [] العناصر.

دليل: متغير من النوع الصحيح أو قيمة تستعمل للإشارة إلى عنصر من مصفوفة.

حتمي: برنامج ينفذ نفس الشيء في كل مرة يستدعى فيها.

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

مخطط الأعمدة: مصفوفة من الأعداد الصحيحة حيث يمثل كل عدد صحيح عدد القيم التي تنتمي إلى نطاق معين.

array: A collection of values, where all the values have the same type, and each value is identified by an index.

element: One of the values in an array. The [] operator selects elements.

index: An integer variable or value used to indicate an element of an array.

deterministic: A program that does the same thing every time it is invoked.

pseudorandom: A sequence of numbers that appear to be random, but which are actually the product of a deterministic computation.

histogram: An array of integers where each integer counts the number of values that fall into a certain range.

12.12 تمرينات

تمرين 12.6

اكتب عملية باسم areFactors تأخذ عدداً صحيحاً n ومصفوفة أعداد صحيحة، وتعيد القيمة true إذا كانت جميع الأرقام في المصفوفة عواملاً للعدد n (أي أن n يقبل القسمة عليهم جميعاً).

مساعدة: انظر التمرين 6.1.

تمرين 12.7

اكتب عملية تأخذ مصفوفة أعداد صحيحة وعدد صحيح اسمه target كمتحولات، وتعيد الدليل الأول الذي يظهر target عنده في المصفوفة، وذلك في حال ظهوره، و1- فيما عدا ذلك.

تمرين 12.8

بعض المبرمجين يخالفون القاعدة العامة التي تقول بأن أسماء المتغيرات والعمليات يجب أن تكون ذات معنى يعبر عن وظيفتها. بدلاً من ذلك، يعتقد هؤلاء أن المتغيرات والعمليات يجب أن تسمى تيمناً بالفاكهة.

لكل واحدة من العمليات التالية، اكتب جملة واحدة تصف ما تفعله تلك العملية بشكل مبسَّط. ولكل متغير، اكتب جملة تعرف الدور الذي يؤديه.

public static int banana(int[] a) {
   int grape = 0;
   int i = 0;
   while (i < a.length) {
      grape = grape + a[i];
      i++;
   }
   return grape;
}
public static int apple(int[] a, int p) {
   int i = 0;
   int pear = 0;
   while (i < a.length) {
      if (a[i] == p) pear++;
      i++;
   }
   return pear;
}
public static int grapefruit(int[] a, int p) {
   for (int i = 0; i < a.length; i++) {
      if (a[i] == p) return i;
   }
   return -1;
}
الغرض من هذا التمرين هو التدرب على قراءة الشفرة والتعرف على أماط الحسابات التي شاهدناها من قبل.

تمرين 12.9

  1. ما هو خرج البرنامج التالي؟
  2. ارسم مخططاً هرمياً يبين حالة البرنامج قبيل عودة mus.
  3. صف ما تفعله mus في بضعة كلمات.
    public static int[] make(int n) {
      int[] a = new int[n];
      for (int i=0; i < n; i++) {
        a[i] = i+1;
      }
      return a;
    }
    public static void dub(int[] jub) {
      for (int i=0; i < jub.length; i++) {
        jub[i] *= 2;
      }
    }
    public static int mus(int[] zoo) {
      int fus = 0;
      for (int i=0; i < zoo.length; i++) {
        fus = fus + zoo[i];
      }
      return fus;
    }
    public static void main(String[] args) {
      int[] bob = make(5);
      dub(bob);
      System.out.println(mus(bob));
    }
    

تمرين 12.10

معظم أساليب اجتياز المصفوفات التي رأيناها حتى الآن يمكن كتابتها بشكل تعاودي أيضاً. إن عمل ذلك ليس شائعاً، لكنه تمرين مفيد.

  1. اكتب عملية باسم maxInRange تأخذ مصفوفة أعداد صحيحة ومجال من الأدلة (lowIndex وhighIndex)، وتوجد القيمة العظمى في المصفوفة، وذلك بمعالجة العناصر بين lowIndex وhighIndex فقط، بما في ذلك نهايتي المجال.

    يجب أن تكون هذه العملية تعاودية. إذا كان طول المجال 1، أي إذا كان lowIndex == highIndex، سنعرف مباشرة أن العنصر الوحيد الوجود في المجال لا بد أن يكون أكبر قيمة موجودة. إذن تلك هي الحالة الأساسية (base case).

    في حال وجود أكثر من عنصر واحد في المجال، يمكننا أن نجزئ المصفوفة إلى أجزاء، نوجد القيمة العظمى في كل قطعة، وبعدها نوجد القيمة العظمى للقيم العظمى.

  2. العمليات مثل maxInRange قد تكون صعبة الاستخدام. لإيجاد العنصر الأكبر في مصفوفة، علينا تزويدها بمجال يحتوي المصفوفة بالكامل.
    double max = maxInRange(array, 0, a.length-1);
    
    اكتب عملية باسم max تأخذ مصفوفة كمعامل وتستعمل maxInRange لإيجاد وإعادة القيمة العظمى. أحياناً تدعى العمليات التي تشبه max بالعمليات المغلفة (wrapper methods)، لأنها تشكل طبقة عازلة حول العملية الغريبة وتجعل استخدامها أسهل. تدعى العملية التي تجري الحساب الفعلي بالعملية المساعدة (helper mthod).

    اكتب نسخة تعاودية من العملية find باستخدام أسلوب العمليات المغلفة والمساعدة (wrapper-helper pattern). يجب أن تأخذ find مصفوفة أعداد صحيحة وعدد صحيح كهدف. عليها أن تعيد دليل الموقع الأول الذي يظهر فيه الهدف في المصفوفة، أما في حال عدم ظهوره تعيد القيمة 1-.

تمرين 12.11

من الطرق الغير فعالة كثيراً في ترتيب عناصر مصفوفة هي البحث عن العنصر الأكبر وتبديله مع العنصر الأول، بعدها إيجاد العنصر الأصغر منه مباشرة وتبديله مع العنصر الثاني، وهكذا. هذه الطريقة تدعى الترتيب الانتقائي (selection sort) – انظر http://en.wikipedia.com/wiki/Selection_sort.

  1. اكتب عملية باسم indexOfMaxInRange تأخذ مصفوفة أعداد صحيحةـ وتوجد العنصر الأكبر في المجال المعطى، وتعيد دليله. يمكنك تعديل نسختك التعاودية من العملية maxInRange أو يمكنك كتابة نسخة تكرارية من الصفر.
  2. اكتب عملية باسم swapElement تأخذ مصفوفة أعداد صحيحة ودليلين، وتبدل بين العنصرين الموجودين عند الدليلين المعطيين.
  3. اكتب عملية باسم selectionSort تأخذ مصفوفة أعداد صحيحة وتستخدم indexOfMaxInRange وswapElement لترتيب المصفوفة من الأكبر إلى الأصغر.

تمرين 12.12

اكتب عملية باسم letterHist تأخذ سلسلة محرفية كمعامل وتعيد مخطط الأعمدة لأحرف السلسلة المحرفية. العنصر الصفري من المخطط يجب أن يحتوي على عدد أحرف a في السلسلة (كبير أو صغير)؛ العنصر الخامس والعشرون يجب أن يحتوي على عدد أحرف z. يجب على برنامجك أن يجتاز السلسلة المحرفية مرة واحدة.

تمرين 12.13

يقال عن الكلمة أنها doubloon إذا ظهر كل حرف من حروف الكلمة مرتين فيها. مثلاً، الكلمات التالية هي جميع الdoubloons التي وجدتها في قاموسي.

Abba, Anna, appall, appearer, appeases, arraigning, beriberi, bilabial, boob,
Caucasus, coco, Dada, deed, Emmett, Hannah, horseshoer, intestines, Isis, mama,
Mimi, murmur, noon, Otto, papa, peep, reappear, redder, sees, Shanghaiings, Toto
اكتب عملية باسم isDoubloon تعيد قيمة true إذا كانت الكلمة المعطاة doubloon وfalse فيما عدا ذلك.

تمرين 12.14

يقال عن كلمتين أنهما anagram (جناس تصحيفي) إذا كان لهما نفس الحروف (ونفس عدد التكرار لكل حرف). مثلاً، إذا أعدنا ترتيب "stop" ستصبح "pots"، و"allen downey" ستصبح "well annoyed".

اكتب عملية تأخذ سلسلتين محرفيتين وتعيد true إذا كان للسلسلتين نفس الحروف (لكن الترتيب مختلف).

تحد اختياري: اجعل البرنامج يقرأ محارف السلسلتين مرة واحدة فقط.

تمرين 12.15

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

تخيل أنك أعطيت مجموعة من القطع بشكل سلسلة محرفية، مثل "quijibo" وأنك أعطيت سلسلة أخرى لتختبرها، مثل "jib". اكتب عملية باسم canSpell تأخذ سلسلتين محرفيتين وتعيد true إذا كان تكوين الكلمة باستخدام مجموعة القطع ممكناً. يمكن أن تملك أكثر من قطعة بنفس الحرف، لكن لا يمكنك استعمال القطعة الواحدة أكثر من مرة.

تحد اختياري: اقرأ حروف السلسلتين مرة واحدة فقط.

تمرين 12.16

في لعبة سكرابل الحقيقية، يوجد قطع فارغة يمكن استعمالها لتمثل أي حرف مطلوب أو ما يدعى (wild card).

فكر بخوارزمية للعملية canSpell يمكن لها أن تعالج المحارف العامة. لا تشغل نفسك بتفاصيل المجريات مثل كيفية تمثيل الأحرف العامة. قم بوصف الخوارزمية وحسب، سواء باستعمال اللغة الطبيعية، أو باستعمال طريقة "الشفرة الزائفة — pseudocode"، أو Java.

السابقالفهرسالتالي