تصميم الدوال
كيف نكتب دوال صغيرة واضحة يسهل فهمها وإعادة استخدامها؟
في نهاية هذه المحاضرة ستكون قادرًا على
- تكتب دوالًا لها الاسم نفسه، وتحدد أيها تختار Java عند الاستدعاء
- تشرح لماذا لا تعرف الدالة متغيرات
main - تثبت باستخدام المتتبّع أن الأنواع البدائية تُمرَّر بالقيمة
- تقسّم برنامجًا طويلًا إلى دوال صغيرة، لكل منها اسم واضح ومسؤولية واحدة
- تتتبّع دالة تستدعي نفسها، وتحدد حالة الأساس فيها
- تعيد استخدام
isPrimeوgcdوpowerفي برامج جديدة
محتويات المحاضرة
- دوال كثيرة قد تحمل الاسم نفسه إذا اختلفت معاملاتها
- تختار Java الدالة من عدد الوسائط وأنواعها
- يجب أن تكون معاملات كل نسخة واضحة الاختلاف
- المتغير يعيش داخل الدالة التي صرّحت عنه فقط
- تأخذ الدالة نسخة من القيمة، لا المتغير نفسه
- الدالة swap تفشل، والمتتبّع يكشف السبب
- البرنامج الذي يُكتب كله في
mainيصعب فهمه - التقسيم يغيّر شكل البرنامج ولا يغيّر سلوكه
- الاسم الجيد يصف مهمة واحدة
- الدالة تستطيع أن تستدعي نفسها بمسألة أصغر
- حالة الأساس توقف السلسلة، والخطوة الذاتية تقترب منها
- بدون حالة الأساس يمتلئ المكدس ويتوقف البرنامج
- الدالة
isPrimeأداة تكتبها مرة وتستخدمها دائمًا - الدالتان
gcdوpowerتبنيان حلولًا لمسائل أكبر - أسئلة المراجعة
دوال كثيرة قد تحمل الاسم نفسه إذا اختلفت معاملاتها
أنت تستخدم هذه الفكرة دون أن تنتبه: println تقبل نصًا أو عددًا أو حرفًا، لأن في Java دوال عدة باسم println. تعريف أكثر من دالة بالاسم نفسه ومعاملات مختلفة يسمى التحميل الزائد (overloading).
ثلاث دوال باسم واحد
توقّع أي دالة تُنفَّذ في كل سطر. ثم اضغط «تتبّع سطرًا سطرًا» و«دخول» لتتأكد. أضف نسخة رابعة تأخذ أربعة أعداد صحيحة، واستدعِها.
public class OverloadedAdd {
static int add(int a, int b) {
return a + b;
}
static double add(double a, double b) {
return a + b;
}
static int add(int a, int b, int c) {
return a + b + c;
}
public static void main(String[] args) {
System.out.println(add(3, 4));
System.out.println(add(3.5, 4.0));
System.out.println(add(1, 2, 3));
}
}7 7.5 6
تختار Java الدالة من عدد الوسائط وأنواعها
- تستبعد ما يخالف عدد الوسائط
- تفضّل المطابقة التامة للأنواع
- وإلا فتقبل تحويلًا إلى نوع أوسعمثل
charإلىint، وintإلىdouble. ولا تحوّل إلى نوع أضيق أبدًا.
أي دالة تختار Java في كل استدعاء؟ انتبه إلى السطر الأخير.
public class PickOverload {
static void show(int n) {
System.out.println("int: " + n);
}
static void show(double n) {
System.out.println("double: " + n);
}
static void show(String n) {
System.out.println("String: " + n);
}
public static void main(String[] args) {
show(5);
show(5.0);
show("5");
show('A');
}
}أظهر الناتج
int: 5 double: 5.0 String: 5 int: 65
لماذا؟ لا توجد دالة تأخذ char، ولا يتحول char إلى String تلقائيًا. أقرب نوع أوسع هو int، فيصبح الحرف A رمزه العددي 65.
توجد دالة واحدة تأخذ double، ونحن نعطيها int. ماذا يطبع؟
public class WidenInt {
static double half(double x) {
return x / 2;
}
public static void main(String[] args) {
System.out.println(half(5));
}
}أظهر الناتج
2.5
لماذا؟ يتحول الوسيط 5 إلى 5.0 قبل الدخول إلى الدالة، فتكون القسمة عشرية ولا يضيع الكسر.
توجد دالة واحدة فقط هي static void show(int n). ماذا يحدث مع show(2.5);؟
show((int) 2.5).يجب أن تكون معاملات كل نسخة واضحة الاختلاف
نوع الإرجاع وحده لا يكفي
تختلف الدالتان في نوع الإرجاع فقط. غيّر معامل النسخة الثانية إلى double واستدعِها بالقيمة 1.5.
public class TwiceTwo {
static int twice(int n) {
return n * 2;
}
static double twice(int n) {
return n * 2.0;
}
public static void main(String[] args) {
System.out.println(twice(4));
}
}is already defined in classأظهر التصحيح
public class TwiceTwo {
static int twice(int n) {
return n * 2;
}
static double twice(double n) {
return n * 2.0;
}
public static void main(String[] args) {
System.out.println(twice(4));
System.out.println(twice(1.5));
}
}8 3.0
لماذا؟ عند قراءة twice(4) تنظر Java إلى الوسائط فقط، فلا تستطيع التمييز بالنوع المُرجَع. لذلك تعدّ الثانية تعريفًا مكررًا للأولى.
استدعاء محيّر
كلتا الدالتين تحتاج إلى تحويل واحد لتقبل total(1, 2)، فلا تعرف Java أيهما تختار. اجعل الوسيط الثاني 2.0.
public class Ambiguous {
static double total(int a, double b) {
return a + b;
}
static double total(double a, int b) {
return a + b;
}
public static void main(String[] args) {
System.out.println(total(1, 2));
}
}reference to total is ambiguousأظهر التصحيح
public class Ambiguous {
static double total(int a, double b) {
return a + b;
}
static double total(double a, int b) {
return a + b;
}
public static void main(String[] args) {
System.out.println(total(1, 2.0));
}
}3.0
لماذا؟ لا يوجد فائز واضح بين الدالتين، فترفض Java التخمين. وبعد التعديل تطابق الدالة الأولى الوسيطين تمامًا.
المتغير يعيش داخل الدالة التي صرّحت عنه فقط
المكان الذي يُعرف فيه اسم المتغير هو مداه (scope). متغيرات main ليست عامة في الصنف كله، بل هي محلية داخل main. والطريق الصحيح لإعطاء قيمة لدالة أخرى هو الوسيط.
الدالة لا ترى متغيرات main
أضف إلى showScore معاملًا int score، ومرّر إليه القيمة عند الاستدعاء.
public class ShowScore {
static void showScore() {
System.out.println(score);
}
public static void main(String[] args) {
int score = 90;
showScore();
}
}cannot find symbolأظهر التصحيح
public class ShowScore {
static void showScore(int score) {
System.out.println(score);
}
public static void main(String[] args) {
int score = 90;
showScore(score);
}
}90
لماذا؟ تبدو الرسالة كأنها خطأ إملائي، مع أن الاسم مكتوب بشكل صحيح. المشكلة هي مكان استخدامه: لا يوجد متغير بهذا الاسم داخل showScore.
متغيران باسم واحد في دالتين مختلفتين. ماذا يطبع؟
public class TwoBoxes {
static void change() {
int count = 100;
System.out.println(count);
}
public static void main(String[] args) {
int count = 1;
change();
System.out.println(count);
}
}أظهر الناتج
100 1
لماذا؟ تشابه الاسمين صدفة: لكل دالة صندوق خاص بها. في المتتبّع ترى count في إطار change() وcount أخرى في إطار main().
تأخذ الدالة نسخة من القيمة، لا المتغير نفسه
عند الاستدعاء تُنسخ قيمة الوسيط إلى صندوق المعامل، ثم لا تبقى أي صلة بين الصندوقين. هذا هو التمرير بالقيمة (pass by value).
تغيّر الدالة معاملها. هل تتغير x في main؟
public class AddTen {
static void addTen(int n) {
n = n + 10;
System.out.println(n);
}
public static void main(String[] args) {
int x = 5;
addTen(x);
System.out.println(x);
}
}أظهر الناتج
15 5
لماذا؟ n نسخة بدأت بالقيمة 5 ثم أصبحت 15، أما x فلم يلمسها أحد. ويختفي الصندوق n عند انتهاء الدالة.
الإرجاع يوصل النتيجة
لكي تصل النتيجة إلى main، تُرجعها الدالة ويُسندها المستدعي إلى المتغير. تتبّع البرنامج سطرًا سطرًا، ثم احذف x = من سطر الاستدعاء وتوقّع الناتج قبل التشغيل.
public class AddTenReturn {
static int addTen(int n) {
n = n + 10;
return n;
}
public static void main(String[] args) {
int x = 5;
x = addTen(x);
System.out.println(x);
}
}15
الدالة swap تفشل، والمتتبّع يكشف السبب
نجح التبديل بالمتغير temp في محاضرة المتغيرات. لننقله إلى دالة، ونتوقع أن يعمل بالطريقة نفسها.
تبديل لا يصل إلى main
توقّع السطرين ثم شغّل. بعدها نفّذ الخطوات أدناه.
public class FailedSwap {
static void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
System.out.println("in swap: a = " + a + ", b = " + b);
}
public static void main(String[] args) {
int a = 5;
int b = 9;
swap(a, b);
System.out.println("in main: a = " + a + ", b = " + b);
}
}in swap: a = 9, b = 5 in main: a = 5, b = 9
- اضغط «تتبّع سطرًا سطرًا»، ثم «السطر التالي» حتى
swap(a, b);في إطارmain()الصندوقانa = 5وb = 9. - اضغط «دخول»يظهر الإطار
swap()فوقmain()، وفيه صندوقان آخران بالاسمين والقيمتين نفسها. - اضغط «السطر التالي» حتى
}فيswapتتبدل القيمتان في إطارswap()، بينما يبقى إطارmain()كما هو:a = 5وb = 9. - اضغط «السطر التالي» مرة أخرىيختفي الإطار
swap()ومعه كل ما فعله. لم يبدّل البرنامج سوى نسختين.
لماذا بقيت a وb في main دون تغيير؟
x وy لن يغيّر شيئًا، ولا إزالة void أيضًا. السبب هو أن الوسيط تُنسخ قيمته إلى صندوق جديد في إطار جديد.البرنامج الذي يُكتب كله في main يصعب فهمه
قائمة في دالة واحدة
شغّل البرنامج بالمدخلات الجاهزة: جمع 3 و4، ثم فحص 7، ثم خيار غير موجود، ثم الخروج. ثم أجب: أين يبدأ جزء الجمع وأين ينتهي؟ وكم مرة نكتب طلب إدخال عدد؟
import java.util.Scanner;
public class MenuBefore {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int choice;
do {
System.out.println("1) Add");
System.out.println("2) Even or odd");
System.out.println("0) Exit");
System.out.print("Choice: ");
choice = input.nextInt();
if (choice == 1) {
System.out.print("a: ");
int a = input.nextInt();
System.out.print("b: ");
int b = input.nextInt();
System.out.println("Sum = " + (a + b));
} else if (choice == 2) {
System.out.print("n: ");
int n = input.nextInt();
if (n % 2 == 0) {
System.out.println("even");
} else {
System.out.println("odd");
}
} else if (choice != 0) {
System.out.println("Unknown choice");
}
} while (choice != 0);
System.out.println("Bye");
}
}1) Add 2) Even or odd 0) Exit Choice: a: b: Sum = 7 1) Add 2) Even or odd 0) Exit Choice: n: odd 1) Add 2) Even or odd 0) Exit Choice: Unknown choice 1) Add 2) Even or odd 0) Exit Choice: Bye
- اكتب ما يفعله البرنامج في جمل قصيرة: يعرض القائمة، يجمع، يفحص.
- اجعل كل جملة دالة تحمل اسمها.
- ابحث عن الأسطر المتكررة واجعلها دالة صغيرة.
- اترك في
mainقرارات البرنامج الكبرى فقط.
التقسيم يغيّر شكل البرنامج ولا يغيّر سلوكه
القائمة نفسها في دوال صغيرة
المدخلات نفسها والناتج نفسه حرفيًا. اقرأ main أولًا: أصبح يشرح البرنامج. يمكن تمرير Scanner وسيطًا مثل أي قيمة أخرى. غيّر نص Exit في printMenu وشغّل: تعديل واحد ظهر في كل مرة. ثم خطّط على الورق لخيار ثالث يضرب عددين: كم دالة ستضيف؟ وكم سطرًا ستغيّر في main؟
import java.util.Scanner;
public class MenuAfter {
static void printMenu() {
System.out.println("1) Add");
System.out.println("2) Even or odd");
System.out.println("0) Exit");
}
static int readInt(Scanner input, String prompt) {
System.out.print(prompt);
return input.nextInt();
}
static boolean isEven(int n) {
return n % 2 == 0;
}
static void handleAdd(Scanner input) {
int a = readInt(input, "a: ");
int b = readInt(input, "b: ");
System.out.println("Sum = " + (a + b));
}
static void handleEvenOdd(Scanner input) {
int n = readInt(input, "n: ");
if (isEven(n)) {
System.out.println("even");
} else {
System.out.println("odd");
}
}
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int choice;
do {
printMenu();
choice = readInt(input, "Choice: ");
if (choice == 1) {
handleAdd(input);
} else if (choice == 2) {
handleEvenOdd(input);
} else if (choice != 0) {
System.out.println("Unknown choice");
}
} while (choice != 0);
System.out.println("Bye");
}
}1) Add 2) Even or odd 0) Exit Choice: a: b: Sum = 7 1) Add 2) Even or odd 0) Exit Choice: n: odd 1) Add 2) Even or odd 0) Exit Choice: Unknown choice 1) Add 2) Even or odd 0) Exit Choice: Bye
الاسم الجيد يصف مهمة واحدة
| اسم ضعيف | اسم أفضل | لماذا |
|---|---|---|
doStuff | calculateAverage | الدالة تنجز فعلًا، فليبدأ اسمها بفعل يصفه |
check | isPrime | دالة boolean تُقرأ كسؤال |
readAndPrintTotal | readInt, printTotal | كلمة And تكشف مهمتين، فافصلهما |
أي تصميم لحساب مجموع الفاتورة أكثر قابلية لإعادة الاستخدام؟
قسّم برنامج الفاتورة
اقرأ price وquantity، وكلاهما int. اكتب ثلاث دوال: subtotal تُرجع حاصل الضرب، وdiscount تُرجع 10 إذا كان المجموع 100 فأكثر وإلا 0، وprintReceipt تطبع الأسطر الثلاثة كما في المثال: Subtotal: 120 ثم Discount: 10 ثم Total: 110.
import java.util.Scanner;
public class Receipt {
// Write subtotal, discount and printReceipt here
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int price = input.nextInt();
int quantity = input.nextInt();
// Call your methods here
}
}30 4
Subtotal: 120 Discount: 10 Total: 110
تلميح 1
المعاملات: subtotal(int price, int quantity)، وdiscount(int amount)، وprintReceipt(int amount, int off).
تلميح 2
main يخزن ناتج الدالتين الأوليين في متغيرين، ثم يمررهما إلى printReceipt.
الحل
import java.util.Scanner;
public class Receipt {
static int subtotal(int price, int quantity) {
return price * quantity;
}
static int discount(int amount) {
if (amount >= 100) {
return 10;
}
return 0;
}
static void printReceipt(int amount, int off) {
System.out.println("Subtotal: " + amount);
System.out.println("Discount: " + off);
System.out.println("Total: " + (amount - off));
}
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int price = input.nextInt();
int quantity = input.nextInt();
int amount = subtotal(price, quantity);
int off = discount(amount);
printReceipt(amount, off);
}
}الدالة تستطيع أن تستدعي نفسها بمسألة أصغر
هذا هو الاستدعاء الذاتي (recursion)، وتسمى الدالة تراجعية. ليس فيه سحر: كل استدعاء ذاتي هو استدعاء عادي له إطار خاص وصناديق خاصة، كما رأينا في كل دالة.
العد التنازلي
اضغط «تتبّع سطرًا سطرًا»، ثم «دخول» باستمرار. راقب تراكم إطارات countDown() في المكدس، ولكل منها صندوق n بقيمة مختلفة، ثم راقبها تختفي واحدًا بعد الآخر. جرّب countDown(5).
public class CountDown {
static void countDown(int n) {
if (n == 0) {
System.out.println("Go!");
} else {
System.out.println(n);
countDown(n - 1);
}
}
public static void main(String[] args) {
countDown(3);
}
}3 2 1 Go!
نقلنا الطباعة إلى ما بعد الاستدعاء الذاتي. ماذا يطبع؟
public class CountUp {
static void countUp(int n) {
if (n > 0) {
countUp(n - 1);
System.out.println(n);
}
}
public static void main(String[] args) {
countUp(3);
}
}أظهر الناتج
1 2 3
لماذا؟ ينتظر الإطار n = 3 انتهاء ما بعده، فلا يطبع إلا في آخر الأمر. أول إطار يصل إلى الطباعة هو n = 1، ثم تعود الإطارات المنتظرة بالترتيب. تأكد من ذلك في المتتبّع.
حالة الأساس توقف السلسلة، والخطوة الذاتية تقترب منها
مسألة صغيرة نعرف جوابها دون استدعاء جديد.
if (n <= 1) return 1;نحل الباقي بالدالة نفسها على مسألة أصغر.
n * factorial(n - 1)المضروب خطوة خطوة
فصلنا الحساب إلى سطرين ليظهر الصندوق smaller عند عودة كل إطار. اضغط «تتبّع سطرًا سطرًا»، ثم «دخول» باستمرار، واكتب ما يُرجعه كل إطار. جرّب 5، ثم 13، وقارن الناتج الثاني بالقيمة الصحيحة 6227020800: لقد تجاوز المدى الذي يتسع له int.
public class Factorial {
static int factorial(int n) {
if (n <= 1) {
return 1;
}
int smaller = factorial(n - 1);
return n * smaller;
}
public static void main(String[] args) {
System.out.println(factorial(4));
}
}24
| الإطار | في طريق الذهاب ينتظر | في طريق العودة يُرجع |
|---|---|---|
factorial(4) | factorial(3) | 4 * 6 = 24 |
factorial(3) | factorial(2) | 3 * 2 = 6 |
factorial(2) | factorial(1) | 2 * 1 = 2 |
factorial(1) | لا شيء: حالة الأساس | 1 |
مجموع الأعداد ذاتيًا
اكتب sumTo(n) باستدعاء ذاتي دون حلقة: تُرجع 0 عندما تكون n تساوي 0، وإلا تُرجع n + sumTo(n - 1). يقرأ main العدد ويطبع الناتج.
import java.util.Scanner;
public class SumTo {
static int sumTo(int n) {
// Replace this line with the base case and the recursive step
return 0;
}
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n = input.nextInt();
System.out.println(sumTo(n));
}
}5
15
تلميح 1
ابدأ بحالة الأساس: if (n == 0).
تلميح 2
ليست لديك حلقة: الاستدعاء الذاتي هو الذي يكرر العمل.
الحل
import java.util.Scanner;
public class SumTo {
static int sumTo(int n) {
if (n == 0) {
return 0;
}
return n + sumTo(n - 1);
}
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n = input.nextInt();
System.out.println(sumTo(n));
}
}بدون حالة الأساس يمتلئ المكدس ويتوقف البرنامج
لنكسر البرنامج عمدًا. في مختبر المضروب أعلاه، احذف حالة الأساس لتصبح الدالة كما يلي، ثم اضغط «تشغيل» لا «تتبّع».
static int factorial(int n) {
int smaller = factorial(n - 1);
return n * smaller;
}سترى في أول الناتج Exception in thread "main" java.lang.StackOverflowError، ثم قائمة طويلة تكرر السطر نفسه من factorial. كل سطر منها استدعاء لم ينتهِ بعد.
ما الخطأ في countDown إذا كان الاستدعاء الذاتي countDown(n + 1)، وحالة الأساس n == 0، والبداية countDown(3)؟
n عن 0 مع كل استدعاء.الدالة isPrime أداة تكتبها مرة وتستخدمها دائمًا
فحص الأعداد الأولية
العدد الأولي أكبر من 1 ولا يقبل القسمة إلا على 1 وعلى نفسه. لاحظ أن return false يوقف الحلقة فور إيجاد قاسم. غيّر الحد إلى 50، ثم أضف سطرًا يطبع isPrime(1).
public class PrimeList {
static boolean isPrime(int n) {
if (n < 2) {
return false;
}
for (int d = 2; d * d <= n; d++) {
if (n % d == 0) {
return false;
}
}
return true;
}
public static void main(String[] args) {
for (int i = 1; i <= 20; i++) {
if (isPrime(i)) {
System.out.print(i + " ");
}
}
System.out.println();
}
}2 3 5 7 11 13 17 19
عدّ الأعداد الأولية
اقرأ n، واستخدم isPrime كما هي لتعدّ الأعداد الأولية من 1 إلى n. اطبع Primes: متبوعًا بالعدد.
import java.util.Scanner;
public class CountPrimes {
static boolean isPrime(int n) {
if (n < 2) {
return false;
}
for (int d = 2; d * d <= n; d++) {
if (n % d == 0) {
return false;
}
}
return true;
}
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n = input.nextInt();
// Count the primes from 1 to n, then print the result
}
}10
Primes: 4
تلميح 1
لا تعدّل isPrime. حلقة وعدّاد وif (isPrime(i)) داخل main تكفي.
الحل
import java.util.Scanner;
public class CountPrimes {
static boolean isPrime(int n) {
if (n < 2) {
return false;
}
for (int d = 2; d * d <= n; d++) {
if (n % d == 0) {
return false;
}
}
return true;
}
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n = input.nextInt();
int count = 0;
for (int i = 1; i <= n; i++) {
if (isPrime(i)) {
count++;
}
}
System.out.println("Primes: " + count);
}
}الدالتان gcd وpower تبنيان حلولًا لمسائل أكبر
أداتان جديدتان
gcd تحسب القاسم المشترك الأكبر بطريقة إقليدس. غيّرت الدالة معامليها، فهل تغيرت x في main؟ تحقق بالمتتبّع. وpower تُرجع long لتتسع للنتائج الكبيرة بقيمة صحيحة تمامًا.
public class MathTools {
static int gcd(int a, int b) {
while (b != 0) {
int remainder = a % b;
a = b;
b = remainder;
}
return a;
}
static long power(int base, int exponent) {
long result = 1;
for (int i = 0; i < exponent; i++) {
result = result * base;
}
return result;
}
public static void main(String[] args) {
int x = 84;
System.out.println(gcd(x, 36));
System.out.println(x);
System.out.println(power(2, 10));
System.out.println(power(2, 40));
System.out.println(power(7, 0));
}
}12 84 1024 1099511627776 1
المضاعف المشترك الأصغر
اكتب lcm(a, b) مستخدمًا gcd، علمًا بأن lcm يساوي a / gcd(a, b) * b. اقرأ عددين واطبع LCM: متبوعًا بالناتج.
import java.util.Scanner;
public class Lcm {
static int gcd(int a, int b) {
while (b != 0) {
int remainder = a % b;
a = b;
b = remainder;
}
return a;
}
// Write the lcm method here
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int a = input.nextInt();
int b = input.nextInt();
// Print the result here
}
}4 6
LCM: 12
تلميح 1
جسم الدالة سطر واحد، ويستدعي gcd.
الحل
import java.util.Scanner;
public class Lcm {
static int gcd(int a, int b) {
while (b != 0) {
int remainder = a % b;
a = b;
b = remainder;
}
return a;
}
static int lcm(int a, int b) {
return a / gcd(a, b) * b;
}
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int a = input.nextInt();
int b = input.nextInt();
System.out.println("LCM: " + lcm(a, b));
}
}مجموع القوى
اقرأ base وn، ثم استخدم power لتحسب مجموع القوى من base مرفوعًا إلى 0 حتى base مرفوعًا إلى n. اطبع Sum: متبوعًا بالناتج. مثال: 2 و3 يعطيان 1 + 2 + 4 + 8، أي Sum: 15.
import java.util.Scanner;
public class SumOfPowers {
static long power(int base, int exponent) {
long result = 1;
for (int i = 0; i < exponent; i++) {
result = result * base;
}
return result;
}
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int base = input.nextInt();
int n = input.nextInt();
// Add up the powers here, then print the result
}
}2 3
Sum: 15
تلميح 1
المجموع يجب أن يكون long، وإلا فلن يتسع للاختبار الأخير.
تلميح 2
الحلقة تبدأ من 0 وتنتهي عند n، وتشمله.
الحل
import java.util.Scanner;
public class SumOfPowers {
static long power(int base, int exponent) {
long result = 1;
for (int i = 0; i < exponent; i++) {
result = result * base;
}
return result;
}
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int base = input.nextInt();
int n = input.nextInt();
long sum = 0;
for (int i = 0; i <= n; i++) {
sum = sum + power(base, i);
}
System.out.println("Sum: " + sum);
}
}أسئلة المراجعة
أي زوج من الدوال يمثل تحميلًا زائدًا صحيحًا؟
ما حالة الأساس في الدالة التراجعية؟
اجمع ما تعلمته اليوم: ماذا يطبع؟
public class ReviewValue {
static int bump(int n) {
n++;
return n;
}
static int bump(int n, int by) {
n += by;
return n;
}
public static void main(String[] args) {
int n = 1;
bump(n);
System.out.println(n);
n = bump(n, 5);
System.out.println(n);
}
}أظهر الناتج
1 6
لماذا؟ الاستدعاء الأول يعدّل نسخة، ولا نستخدم ما يُرجعه، فتبقى n مساوية 1. وفي الثاني تختار Java النسخة ذات المعاملين، ونسند ما تُرجعه إلى n، فتصبح 6.