لوحة المتصدرين
المحتوى الأكثر حصولًا على سمعة جيدة
المحتوى الأعلى تقييمًا في 03/03/21 في كل الموقع
-
كما وضح المدرب سمير والمدرب وائل أنه يجب إستخدام إطار عمل للمشاريع الحقيقية، ولكن، دعني أعطيك بعض المواضيع التي يمكنك البحث عنها لتقوية خلفيتك في PHP. لا تثق أبدًا في المستخدمين للتطبيق: لو كان عندك حقل إدخال، أو query في URL، عليك عمل فلترة لهم قبل أن يمروا على تطبيقك. إذا لم تفعل هذا، فسيكون تطبيقك مُعرض لهجمات الـ XSS والـ SQL Injection. أستخدم HTTPS دائماً: هذا سيجعل إتصال المُستخدمين على موقعك آمن، وسيمنع الـ sniffing. أيضاً، المتصفحات الحديثة، مثل Google Chrome بدأت تضع لوحات تحذير لو كان موقعك يستعمل HTTP فقط. الملفات العامة: تأكد من ضبط الخادم الخاص بك بحيث لا يستضيف أي ملفات خاصة قد تحتوي على بيانات مهمة. قم بتضمين رمز CSRF مميز مع إجراءات التطبيق: إذا ألقيت نظرة على زر "أعجبني" الموجود على أي صفحة في أكاديمية حسوب، ستجد أنه في نهاية الـ URL يوجد CSRF Token مرفقة في النهاية. بدون هذه الـ token، يمكن لأي شخص أن يضع رابط الإعجاب في ملف HTML ويرسله إلى شخص لديه حساب على موقع الأكاديمية، وعند فتح هذا الملف، سيتم عمل إعجاب بشكل تلقائي بدون معرفة المُستخدم. طبعاً هذا مثال بسيط، ولكن يمكن أن يحدث على مستوى أعلى، مثل إضافة admin على الموقع. تشفير كلمات المرور: لا تقم بحفظ كلمات المرور كما هي في قاعدة البيانات. قم بالبحث عن طرق التشفير لتعرف أكثر. وأخيراً، بالنسبة إلى تنظيم الملفات، تأكد من أنك على دراية بـ Object Oriented Programming جيداً + نمط التصميم المشهور MVC. إن لم تكن تريد العمل بـ MVC، فعلى الأقل، تأكد أنك تفصل بين الملفات التي تعرض الـ UI، أو التي نُطلق عليها الـ views، وبين الملفات التي تتعامل مع قاعدة البيانات أو أي API خطير. آمل أن أكون قد أجبت على سؤالك.3 نقاط
-
بالإضافة لما وضحه المدرب سمير، أريد إضافة مايلي، عند العمل مع مطورين آخرين أو فريق عمل، ألا نحتاج لطريقة في بنية المشروع تكون مفهومة من الجميع و الكل منهم يعرف هرمية المشروع و كيفية توزيع الملفات و له خبرة في أسماء الدوال و الأوامر و ... حيث إن استخدام إطار العمل مفيد جداً في توحيد طريقة التعامل و برمجة المشروع و يسهل عملية البحث عن سبب المشاكل و إصلاحها، فهو يسرع عملية الإنتاج و يقلل الأخطاء الناتجة. و يقلل الكلفة بحيث تكون معظم الأمور مبرمجة مسبقا ولا يحتاج المبرمج لكثير من المجهود بإعادة اختراع العجلة و كتابة نفس الأصناف و الدوال والتي من الممكن إعادة استخدامها..2 نقاط
-
الأفضل دائماً إستخدام إطار عمل جاهز عند إنشاء مشاريعك التي تريد أن تطلقها على شبكة الإنترنيت أو مشاريع لعُملائك لأن هناك العديد من الثغرات و المشاكل التي قابلت المُطورين من قبل و قامو بإيجاد حل لها في أطر العمل و هذا من أجل حماية موقعك و ضمان خصوصية مستخدمين تطبيقك. و لهذا تجد مُعظم الخبراء في المجال ينصحون بهذا الأمر فإطار العمل لا يُسهل و يُسرع الإنتاجية فقط و إنما يحميك من مُعظم الثغرات الموجودة، و إذا حاولت أن تُعالج موضوع الحماية بنفسك ستتعب كثيراً و ستقل إنتاجيتك فبدل أن تُركز على خصائص التطبيق نفسه تجد نفسك تبحث عن الثغرات و كيفية مُعالجتها خصوصاً إن كان الشخص مبتدئ و لا يفقه شيء في مواضيع الحماية. أيضاً عملية صيانة و تحديث المواقع في أطر العمل تكون سهلة مُقارنة ب php native. أخيراً إن كنت تريد التعلم فلا مانع من إستخدام php native لكن في مشاريعك الحقيقية الأفضل إستخدام إطار عمل، أما إن كنت تريد أخذ المُخاطرة فأنت حر في ذلك.2 نقاط
-
يدخل المتصفح في وضع المراوغات (Quirks Mode), هذا يعني أنه سوف يكون هناك العديد من الأخطاء التي كانت موجودة في المتصفحات القديمة. لذا ستحصل على نتائج مختلفة بشكل كبير. الغرض من DOCTYPE هو إخبار المتصفح بنوع HTML الذي تكتبه. لا يجوز حذف DOCTYPE.اذا لم يتم كتابتها سيحاول المستعرض فقط تحليل HTML بأفضل ما في وسعه. ولكن لن يتم عرض جميع العناصر بشكل صحيح. DOCTYPE هو جزء مطلوب من جميع مستندات HTML, إنها إرشادات لمتصفح الويب حول إصدار HTML الذي تمت كتابة الصفحة به, تركها من الممكن أن يؤدي الى عدم توافق المتصفح مع الكود وقد يتجاهل الكثير من العناصر ,على سبيل المثال: الميزات والعلامات الجديدة في HTML5 مثل <article> ، <footer> ، <header> ، <nav> ، <section> قد لا تكون مدعومة إذا لم يتم التصريح عن <! DOCTYPE>.2 نقاط
-
في سلسلة المقالات هذه سنتعلم معًا أساسيات البرمجة بلغة Java، وأثناء ذلك سنتعلم أيضًا طرائق جديدة للتفكير، وتحليل المشكلات إلى أجزاء صغيرة، وكتابة خوارزميات وحلول منهجية لها. إن مهارة حل المشكلات هي أهم مهارة لأي طالب علوم حاسوب، وكما سنرى معًا، تعلم البرمجة سيفيد كثيرًا في تطوير هذه المهارة. ما هي البرمجة؟ البرنامج هو سلسلة من التعليمات التي تحدد كيفية تنفيذ عملية حسابية. قد تكون العملية الحسابية رياضية، مثل حل جملة معادلات أو إيجاد جذور كثير حدود. وقد تكون أيضًا معالجة رموز، مثل البحث عن نص واستبداله في مستند أو ترجمة برنامج آخر. تختلف التفاصيل بين لغة وأخرى، لكن بعض التعليمات الأساسية تظهر في جميع لغات البرمجة تقريبًا. الإدخال (input): تحصيل البيانات من لوحة المفاتيح، أو من ملف، أو من حساس، أو من جهاز آخر. الإخراج (output): عرض البيانات على الشاشة، أو إرسالها إلى ملف أو إلى جهاز آخر. الحساب (math): تنفيذ العمليات الحسابية الأساسية مثل الجمع والقسمة. اتخاذ القرارات (decisions): التحقق من شروط معينة وتنفيذ التعليمات المناسبة لكل حالة. التكرار (repetition): تنفيذ عمل ما بصورة متكررة، عادة مع وجود تغيير. صدق أو لا تصدق، هذا كل شيء تقريبًا. أيَّ برنامج استعملته من قبل، مهما كان معقدًا، بُنِيَ من تعليمات صغيرة تشبه هذه التعليمات. وهكذا يمكنك اعتبار البرمجةبأنها عملية تجزئة المهام الكبيرة والمعقدة إلى مهام جزئية أصغر وأصغر. وتستمر العملية حتى نصل إلى مهام جزئية بسيطة بما يكفي لتنفيذها بالتعليمات البسيطة التي يوفرها الحاسوب. ننصحك بالرجوع إلى مقال تعلم البرمجة لمزيد من التفاصيل حول البرمجة عمومًا وكيفية دخول المجال. ما هي علوم الحاسوب؟ أحد أهم نواحي كتابة البرامج هو تحديد طريقة حل مشكلة ما، خصوصًا إذا تعددت الحلول. مثلًا، هناك طرق عديدة لترتيب قائمة من الأرقام، ولكل طريقة مزاياها. حتى نحدد أي طريقة هي الأفضل في وضع معين، نحتاج لتقنيات لتوصيف وتحليل الحلول بشكل صيغ منتظمة. علوم الحاسوب هي علوم الخوارزميات، وتشمل تحليل الخوارزميات واكتشاف خوارزميات جديدة. الخوارزمية هي سلسلة خطوات تحدد طريقة حل مشكلة ما. بعض الخوارزميات أسرع من غيرها، وبعضها تستهلك مساحة أقل في ذاكرة الحاسوب. سوف تتعلم كيف تفكر كعالم حاسوب أثناء تعلمك كيفية تطوير خوارزميات لحل مشكلات لم تحلها من قبل. تصميم الخوارزميات وكتابة الشفرات البرمجية عمليتان صعبتان ومعرضتان للأخطاء. تدعى الأخطاء البرمجية bugs (عِلل برمجية)، وعملية تتبعها وتصحيحها تدعى debugging. ستطور مهارات جديدة في حل المشكلات أثناء تعلم تصحيح الأخطاء في البرامج التي تكتبها. عليك التفكير بإبداع عندما تواجهك أخطاء غير متوقعة. ورغم أن حل الأخطاء البرمجية قد يكون محبطًا، إلا أنه جزء مثير وفيه تحدٍ وذكاء. اكتشاف الأخطاء يشبه عمل التحري في بعض نواحيه. حيث تواجهك الأدلة، وعليك استنتاج العمليات والأحداث التي أدت إلى النتائج التي تراها. أحيانًا يقود التفكير بتصحيح البرامج وتحسين أدائها إلى اكتشاف خوارزميات جديدة. ننصح بقراءة مقال المدخل الشامل لتعلم علوم الحاسوب للمزيد من التفاصيل. لغات البرمجة إن لغة البرمجة التي ستتعلمها هي Java، وهي لغة عالية المستوى (High-level language). هناك لغات أخرى عالية المستوى لعلك سمعت بها مثل Python، أو C و C++، أو Ruby، أو Javascript. يجب ترجمة البرامج المكتوبة بلغات عالية المستوى إلى لغة منخفضة المستوى (low-level language) أو ما يدعى ”لغة الآلة“، قبل أن يستطيع الحاسوب تشغيلها. تحتاج هذه الترجمة وقتًا، لكن هذه سيئة بسيطة للغات عالية المستوى. في المقابل، للغات عالية المستوى حسنتين: كتابة البرامج بلغة عالية المستوى أسهل بكثير. كتابة البرامج تأخذ وقتًا أقل، وتكون البرامج أقصر وأسهل للقراءة، ومن المرجح أكثر أن تكون صحيحة. اللغات عالية المستوى محمولة (portable)، بمعنى أنه يمكن تنفيذ البرامج المكتوبة بها على أنواع مختلفة من الحواسيب دون أي تعديلات أو بعد عمل تعديلات قليلة. أما البرامج المكتوبة بلغة منخفضة المستوى فلا يمكنها العمل إلا على نوع واحد فقط من الحواسيب، ويجب إعادة كتابتها قبل أن نتمكن من تشغيلها على جهاز آخر. هناك نوعين من البرامج التي تترجم اللغات عالية المستوى إلى لغات منخفضة المستوى: المفسرات والمترجمات. يقرأ المفسر (interpreter) البرامج المكتوبة بلغات عالية المستوى وينفذها، أي أنه ينفذ التعليمات التي يمليها البرنامج. يعالج المفسر البرنامج في أجزاء صغيرة، حيث يقرأ بعض السطور ثم ينفذ التعليمات ويعود لقراءة سطور أخرى وهكذا. يبين الشكل 1.1 بنية المفسر. على صعيد آخر، يقرأ المترجم (compiler) البرنامج كله ويترجمه دفعة واحدة قبل بدء تنفيذ البرنامج. في هذه الحالة، يدعى البرنامج المكتوب بلغة عالية المستوىبالشفرة المصدرية (source code)، ويدعى البرنامج المترجم بالشفرة الهدف (object code) أو الملف التنفيذي (executable). بعد ترجمة البرنامج، يمكنك تنفيذه بشكل متكرر دون الحاجة لأي ترجمة أخرى. ونتيجة لذلك، تعمل البرامج المترجمة بصورة أسرع من البرامج المفسرة. لغة Java مجمّعة ومفسرة معًا. فبدلًا من ترجمة البرامج مباشرة إلى لغة الآلة، يولد مترجم Java بايت كود (byte code). شفرة بايت سهلة وسريعة التفسير مثل لغة الآلة، لكنها محمولة أيضًا، حيث يمكننا ترجمة برنامج Java على أحد الأجهزة، ثم ننقل شفرة بايت إلى جهاز آخر، ثم نشغل شفرة بايت على الجهاز الثاني. يدعى المفسر الذي ينفذ شفرة بايت "بآلة Java الافتراضية" (Java Virtual Machine أو اختصارًا JVM). يبين الشكل 1.2 مراحل هذه العملية. ورغم أن هذه العملية قد تبدو معقدة، إلا أن معظم بيئات البرمجة (أحيانًا تدعى بيئات التطوير)، تجري هذه الخطوات تلقائيًا بدلًا منك. سيكلفك الأمر عادة ضغطة زر واحدة أو طلب أمر واحد لترجمة برنامجك وتنفيذه. من جهة أخرى، من المهم أن تعرف الخطوات التي تجري وراء الستار، لكي تتمكن من معرفة سبب المشكلة في حال وقوع أي خطأ. ترجمة -وبتصرف- لجزء من الفصل الأول من كتاب Think Java: How to Think Like a Computer Scientist لكاتبيه Allen B. Downey و Chris Mayfield. اقرأ أيضًا تعلم البرمجة المدخل الشامل لتعلم علوم الحاسوب1 نقطة
-
السلام عليكم و رحمة الله و بركاته ما هو ال Rendering Mode الذي سوف يتبعه المتصفح إذا لم اكتب السطر الخاص بتحديد الإصدار1 نقطة
-
نعم ما قصده المدرب محمد هو عمل مصفوفة بطول orders من الكائنات promesies وتمريرها للتابع Promise.all أي أولا يجب تهيئة المصفوفات ثم استدعائهم ومن ثم انتظار النتائج1 نقطة
-
عليك تقسم الكود إلى تابعين الأول يمر على orders والثاني ينفذ التعليمات الخاصة بكل order. التابع الذي يقوم بتنفيذ التعليمات ضمن حلقة for يجب استدعائه عن طريق await. عند الحاجة لانتظار أكثر من حدث للانتهاء عليك استخدام promise all.1 نقطة
-
أحاول عمل طلب request من خلال مكتبة axios من واجهة موقع مبنية بـ react لكني أحصل على الخطأ التالي: Error: Network Error Stack trace: createError@http://localhost:3001/static/js/index.js:2012:13 handleError@http://localhost:3001/static/js/index.js:910:12 وهذا هو الكود الخاص بمكتبة axios axios.get(`http://localhost:3001/persons?fname="adam"&lname=${lname}`) .then(function(response) {console.log(response);}) .catch(function(error) {console.log(error);});1 نقطة
-
يبدو أن لديك مشكلة في CORS وطبقة على السيرفر تعمل لحماية المواقع من الطلبات الخبيثة، ويمكنك حل المشكلة من خلال تثبيت مكتبة cors في الـ backend الخاص بك، ويمكنك فعل ذلك في laravel من خلال الأمر : composer require fruitcake/laravel-cors وإضافة middleware إلى ملف app/Http/Kernal.php كالتالي: protected $middleware = [ \Fruitcake\Cors\HandleCors::class, // ... ]; أو إن كنت تستخدم express.js قم بتثبيت المكتبة من خلال الأمر التالي: npm install cors ثم قم بإضافة الكود التالي إلى الملف السيرفر الخاص بك: var express = require('express') // إستدعاء المكتبة var cors = require('cors') var app = express() // تسغيل cors middleware app.use(cors()) بالتوفيق1 نقطة
-
لدي تطبيق يقوم بعرض RSS Feed من موقع معين، يظهر لي الخطأ التالي عند تشغيل التطبيق: android.os.NetworkOnMainThreadException هذا هو الكود التي تحصل فيه المشكلة: URL url = new URL(urlToRssFeed); SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParser parser = factory.newSAXParser(); XMLReader xmlreader = parser.getXMLReader(); RssHandler theRSSHandler = new RssHandler(); xmlreader.setContentHandler(theRSSHandler); InputSource is = new InputSource(url.openStream()); xmlreader.parse(is); return theRSSHandler.getFeed();1 نقطة
-
مشكلة تتعلق بالاتصال بالانترنت من MainThread (هي مسلك مسؤول عن الواجهة الرسومية ولا يجب الاعتماد عليه بتنفيذ أي شيى آخر ليكون الأداء جيد ولا يحدث توقف في عمل البرنامج) أولا يجب التأكد من إعطاء سماحية الوصول للانترنت في AndroidMainfest: <uses-permission android:name="android.permission.INTERNET"/> وضمن MainActivity.java يجب إضافة التالي للدالة OnCreate : ليتم تنفيذ المهمة التي سوف نكتبها الحقا: new RetrieveFeedTask().execute(urlToRssFeed); سنقوم بتنفيذ المهمة في مسلك منفضل / سوف نعمل على التوازي/ كالتالي: حيث قمنا بتعريف RetrieveFeedTask كـ AsyncTask. class RetrieveFeedTask extends AsyncTask<String, Void, RSSFeed> { private Exception exception; protected RSSFeed doInBackground(String... urls) { try { URL url = new URL(urls[0]); SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParser parser = factory.newSAXParser(); XMLReader xmlreader = parser.getXMLReader(); RssHandler theRSSHandler = new RssHandler(); xmlreader.setContentHandler(theRSSHandler); InputSource is = new InputSource(url.openStream()); xmlreader.parse(is); return theRSSHandler.getFeed(); } catch (Exception e) { this.exception = e; return null; } finally { is.close(); } } protected void onPostExecute(RSSFeed feed) { // TODO: check this.exception // TODO: do something with the feed } } في Android الإصدار API 30 تم التخلي عن AsyncTask فعلينا عندها استخدام تعريف المسلك Threads كالتلي: Thread thread = new Thread(new Runnable() { @Override public void run() { try { //Your code goes here // هنا نكتب الدالة السابقة } catch (Exception e) { e.printStackTrace(); } } }); thread.start();1 نقطة
-
A class which is called Employee consists of salary and bounce as a variables, setSalary(double salary) to initialize the variable, getSalary() to get the salary, setBounce(double b) to initialize bounce value, getyearSalary() to get the year salary. Your java program should have a main class, which is called Company to create an object called p of programmer class, and then calculate programmer salary, total salary, and year salary. Note: Suppose the bounce of programmer is 10% of his basic salary ممكن توضيح كيفية حل السؤال ؟1 نقطة
-
المطلوب تعريف صنف Employee و إعطائه خواص و طرق كما هو موضح ، مثل الراتب و طريق get - set للتعامل مع البيانات داخله ثم إنشاء غرض منه Programmer .. الصنف Employee .. عليك إكمال باقِ الوظائف.. public class Employee{ private double salary; private double bounce; //setters public void setSalary(double salary){ this.salary=salary; } public void setBounce(double bounce){ this.bounce=bounce; } //TODO //getters public double getSalary(){ return salary; } public double getBounce(){ return bounce; } public void getYearlySalary(){ return salary*12; } //TODO } الدالة الرئيسية: import java.util.Scanner; public class Company{ // اسم الصنف الأساسي/ البرنامج public staic void main(String[] args){ Scanner sc = new Scanner(System.in); Employee emp = new Employee(); // تعريف موظف جديد يمكنك تسميته programmer System.out.println("Enter salary: "+ setSalary(sc.nextDouble())); // TODO استدعاء الدوال } } عليك الاعتماد على نفسك، إن الأمور المتبقية هي مكررة مما سبق عليك الفهم و محاولة إعادة التطبيق1 نقطة
-
لنتفرض أن لدي الكود التالي: public static void main() { stopCommand(); searchCommand(); } أريد تنفيذ الدالة الأولى في حال تم إرفاق الأمر stop في سطر الأوامر والدالة الثانية في حال تم إرفاق search، كيف أحقق هذه النتيجة ؟1 نقطة
-
يتم ذلك عن طريق استقبال المعاملات من خلال الوسيط args حيث نقوم بتمرير مصفوفة من نوع String تأخذ كل منها جزء من البيانات الممررة بالترتيب. سيتم حفظ أول وسيط ممرر بالدليل الأول للمصفوفة [0]args و ثاني وسيط في الدليل الثاني[1]args و هكذا .. public static void main(String[] args) { if (args[0].equals("stop")) { stopCommand(); } else if (args[0].equals("search")) { searchCommand(); } } أما تنفيذ البرنامج سيكون بالشكل التالي: java MyProgram stop __ الأمر | اسم البرنامج1 نقطة
-
1 نقطة
-
يظهر لك ذلك الخطأ لأنك قمت بإنشاء مستودع على github يحتوي على ملفات غير موجودة في المشروع الذي تحاول رفعه مثل ملف readme لذلك قم بالتالي كتابة الأمر التالي git pull origin main لجلب الملفات المختلفة ثم قم بعمل git push origin main مرة أخرى1 نقطة
-
يجب أن تقوم بحفظ كل التغييرات التي قمت بها أولًا أما من خلال عمل commit لكل التغييرات أو وضع كل التغييرات في stash وحفظ التغييرات حتى لا تضيع، ثم يمكنك سحب كل التغييرات من مستودع GitHub من خلال الأمر: git pull وهكذا سيتم إضافة التغييرات إلى مشروعك، ثم قم برفع التغيرات التي حفظتها سابقًا من خلال الأمر: git push origin main1 نقطة
-
يحدث هذا الأمر إذا قمت بتهيئة github repo جديد باستخدام ملف Readme أو ملف License يجب تنفيذ git remote add origin [//your github url] //pull those changes git pull origin master // or optionally, 'git pull origin master --allow-unrelated-histories' if you have initialized repo in github and also committed locally //now, push your work to your new repo git push origin master1 نقطة
-
تحتاج لعمل git pull يبدو أنه لديك تحديث في المستودع وليس متوفر عندك locally و git pull ستجلب التحديث بعدها يمكنك اجراء الاوامر المعتادة1 نقطة
-
1 نقطة
-
هذه هي الطريقة التي يمكننا بها تنفيذ ما يعادل componentDidMount باستخدام الخطاف useEffect: useEffect(() => { // mount }); لتنفيذ ما يكافئ componentDidUpdate باستخدام الخطاف useEffect ، يجب علينا القيام بذلك: useEffect(() => { // mount and update }, [dependency]); تقريبًا نفس الشيء السابق ، لكن هذه المرة نقوم بتمرير مجموعة التبعيات الخاصة بنا كمعامل ثاني ، وداخل تلك المصفوفة ، يجب أن نمرر التبعية التي نريد مشاهدتها فاذا تغيرت تقوم useeffect بعمل تحديث للمكون للقيام بالتنظيف بعد إلغاء تحميل المكون ، لدينا طريقة بسيطة لإجراء ما يعادل componentWillUnmount باستخدام الخطاف useEffect. الشيء الوحيد الذي يتعين علينا القيام به هو إعادة callback في الخطاف useEffect ، مثل هذا: useEffect(() => { window.addEventListener("mousemove", () => {}); return () => { window.removeEventListener("mousemove", () => {}) } }, []);1 نقطة
-
نعم يمكنك ذلك باستخدام المرشحات أو ما يعرف بالفلاتر, هناك العديد من الفلاتر ولكل منها استخدام مختلف هذا شكل الصورة قبل استخدام الفلاتر والآن لنستخدم احدى الفلاتر على الصورة لاحظ لو سمحت الكود الآتي // كود Html <img src="test.png" alt=""> // كود css img{ filter: brightness(0.25); } سوف تكون النتيجة كالأتي تستطيع استخدام اكثر من فلتر في نفس الوقت, لاحظ الكود الاتي لو سمحت img{ filter: invert(48%) sepia(13%) saturate(3207%) hue-rotate(130deg) brightness(95%) contrast(80%); } سوف تكون النتيجة كالتالي تستطيع التعديل في قيمة الفلاتر كما يحلو لك1 نقطة
-
أنت تستخدم نسخة قديمة من المكتبة قم بتحديث الى النسخة الاخيرة import React from "react"; import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom"; export default function App() { return ( <Router> <Switch> <Route name="home" exact path="/"> <Home /> </Route> <Route name="persons" path="/persons"> <Persons /> </Route> <Route > <NotFound /> </Route> </Switch> </Router> ); } او يمكنك كتابة الكود بهذه الطريقة فهي أقرب لما اعتدت عليه فقط هنا ال handler تغير وأصبح اسمه component import React from "react"; import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom"; export default function App() { return ( <Router> <Switch> <Route name="home" exact path="/" component={Home}> <Route name="persons" path="/persons" component={Persons}> <Route component={NotFound}> </Switch> </Router> ); }1 نقطة
-
لدي المُكون التالي لعرض مجموعة من المهام: class AppTasks extends Component { protected $listeners = ['taskAdded' => '$refresh']; public function render() { $totalTasks = auth()->user()->tasks()->count(); $tasks = auth()->user()->tasks()->latest()->get(); return view('livewire.app-tasks', [ 'totalTasks' => $totalTasks, 'tasks' => $tasks ]); } } أريد إضافة pagination بنفس طريقة ال blade لكن يظهر بشكل غير جيد، هل هناك طريقة محددة و عند الضغط على الصفحات يتم الإنتقال بدون تحديث.1 نقطة
-
يُمكنك إستخدام الtrait WithPagination الذي يسمح لك بإضافة التصفح لمكون livewire ثم تحدد القالب على أنه bootstrap بهذا الشكل: use Livewire\WithPagination; class AppTasks extends Component { use WithPagination; protected $paginationTheme = 'bootstrap'; protected $listeners = ['taskAdded' => '$refresh']; public function render() { $totalTasks = auth()->user()->tasks()->count(); $tasks = auth()->user()->tasks()->latest()->paginate(5); return view('livewire.app-tasks', [ 'totalTasks' => $totalTasks, 'tasks' => $tasks ]); } } ثم إضافة السطر: {{ $tasks->links() }} لصفحة العرض. و سيظهر ترقيم الصفحات بشكل عادي، يُمكنك أيضاً إستخدام قالب خاص بك للصفحات إن رغبت في ذلك و الطريقة موجودة في التوثيق الرسمي : Pagination1 نقطة
-
يمكن كتابة جميع routes في ملف واحد routes.js هكدا import React from 'react'; import { Router, Route } from 'react-router'; import { Template1, Template2, Template3 } from './templates'; const createRoutes = () => ( <Router> <Route exact path="/sessionstate1" component={Template1}/> <Route exact path="/sessionstate2" component={Template2}/> <Route exact path="/sessionstate3" component={Template3}/> // أضف هنا االروتس التي تحتاج إليها </Router> ); export default createRoutes بعد ذلك يمكن تحميل routes في ملف index.js هكذا import ReactDOM from 'react-dom'; // تحميل routes import createRoutes from './routes'; const routes = createRoutes(); ReactDOM.render( routes, document.getElementById('root') );1 نقطة
-
يمكنك انشاء ملف لتضع فيه ال routes لوحدهم بهذه الطريقة //?pages import Index from './pages/index'; import Questions from './pages/footer/questions'; // ...... const routes = [ { path: '/', component: Index, exact: true, }, { path: '/questions', component: Questions, exact: true, }, // ....... ]; export default routes; ثم تقوم باستيراد المعلومات في المكون الرئيسي والدوران حول قائمة المعلومات لانشاء routes وبهذه الطريقة يخف الضغط على المكون الرئيسي ويصبح لديك مكان مركزي لتعديل معلومات ال routes import React from 'react'; import { Switch, Route } from 'react-router-dom'; import nprogress from 'nprogress'; import Navbar from './components/layout/Navbar'; import TopNavbar from './components/layout/TopNavbar'; // import BooksFilter from './components/books/BooksFilter'; import Footer from './components/layout/Footer'; //?util import Up from './util/Up'; import SocialButtons from './util/SocialButtons'; import ScrollToTop from './util/ScrollToTop'; import theme from './util/theme'; import routes from './util/routes'; function App(props) { React.useEffect(() => { nprogress.done(); return () => { nprogress.start(); }; }, []); return ( <> <TopNavbar></TopNavbar> {/* <BooksFilter></BooksFilter> */} <SocialButtons></SocialButtons> <ScrollToTop></ScrollToTop> <Navbar></Navbar> <Switch> {routes.map((route, i) => ( <Route key={i} {...route} /> ))} </Switch> <Up></Up> <Footer></Footer> </> ); } export default App;1 نقطة
-
في حالتك يجب نسخ state object قبل التعديل هكذا: const handleChange = (e) => { const { name, value } = e.target; setForm((prevForm) => { const newForm = {...prevForm}; newForm.info[name] = value; return newForm; }); }; الكود السابق سيعمل لكن المشكلة الوحيدة أننا نقوم بعمل shallow copy وليس deep copy، مشكلة الـshallow copy أنها لا تضمن immutability ﻷننا في الحالة السابقة نعدل على info بشكل مباشر وهذا سيعدل على state بشكل مباشر وهذا خاطئ وإن عمل الكود، في حالة أردنا deep copy أنا أفضل حينها استخدام مكتبة immer من هذا الرابط ، في حالتك باستعمال immer: const handleChange = (e) => { const { name, value } = e.target; setForm((prevForm) => { return produce(prevForm,(draftForm) => { draftForm.info[name] = value; }); }); }; لاحظ أن الكود السابق أسهل بكثير فيم لو قمنا بعمل deep copy بأيدينا حيث تضمن لك مكتبة immer أن تكون state لديك immutable بشكل كامل ومع ذلك تكتب كود مشابه للـmutable. إذا كان state object أعقد بكثير من حالتك هنا أفضل استعمال مكتبة redux وredux-toolkit أو أي بديل تحت مسمى state management library (مثل Mobx مثلاً) حيث تسمح لك تلك المكتبات بتقسيم state الكبيرة إلى أجزاء صغيرة يعالج كل جزء فيها معالجة مستقلة عن بقية الأجزاء مما يجعل إدارة الـstate object الكبير أسهل ناهيك عن توفير ميزات أخرى مثل تنظيم هيكلية التطبيق بنظام موحد جميل وأشياء أخرى كثيرة.1 نقطة
-
في حالتك انت تحتاج الى متغير اخر ليساعدك في تحديد اسم الطبقة التي تريد تغييرها ويمكنك ان تعطيها الى handleChange بهذا الشكل onChange={(e) => handleChange(e, 'اسم الطبقة')} ثم يمكنك الاعتماد على ذلك لتغيير ال state بهذا الشكل const handleChange = (e, objKey) => { const { name, value } = e.target; setForm((prevForm) => ({ ...prevForm, [objKey]: { ...prevForm[objKey], [name]: value } })); }; function App() { const [form, setForm] = useState({ info: { firstname: '', lastname: '', email: '', password: '', confirmPassword: '' }, address: { country: '', city: '', line1: '', line2: '' }, }); const handleChange = (e, objKey) => { const { name, value } = e.target; setForm((prevForm) => ({ ...prevForm, [objKey]: { ...prevForm[objKey], [name]: value } })); }; console.log(form); return ( <div> <h2>signup</h2> <input type="text" placeholder="firstname" name="firstname" value={form.info.firstname} onChange={(e) => handleChange(e, 'info')} /> <input type="text" placeholder="lastname" name="lastname" value={form.info.lastname} onChange={(e) => handleChange(e, 'info')} /> <input type="email" placeholder="email" name="email" value={form.info.email} onChange={(e) => handleChange(e, 'info')} /> <input type="password" placeholder="password" name="password" value={form.info.password} onChange={(e) => handleChange(e, 'info')} /> <input type="password" placeholder="confirmPassword" name="confirmPassword" value={form.info.confirmPassword} onChange={(e) => handleChange(e, 'info')} /> <input type="text" placeholder="country" name="country" value={form.address.country} onChange={(e) => handleChange(e, 'address')} /> <input type="text" placeholder="city" name="city" value={form.address.city} onChange={(e) => handleChange(e, 'address')} /> <input type="text" placeholder="line1" name="line1" value={form.address.line1} onChange={(e) => handleChange(e, 'address')} /> <input type="text" placeholder="line2" name="line2" value={form.address.line2} onChange={(e) => handleChange(e, 'address')} /> </div> ); } export default App;1 نقطة
-
1 نقطة
-
تقليديًا، البرنامج الأول الذي يكتبه الناس عندما يتعلمون لغة برمجة جديدة يسمى برنامج hello world (مرحبًا بالعالم). كل ما يفعله هذا البرنامج هو طباعة العبارة "!Hello, World" على الشاشة. في لغة Java، يبدو هذا البرنامج كما يلي: public class Hello { public static void main(String[] args) { // generate some simple output System.out.println("Hello, World!"); } } عند تشغيل هذا البرنامج سيعرض ما يلي: Hello, World! لاحظ أن الخرج لا يحوي علامات الاقتباس. تتكون برامج Java من صنف (class) وتعريفات توابع (methods)، وتتكون التوابع منتعليمات (statements). التعليمة هي سطر من الكود ينفذ عملية بسيطة. في برنامج hello world، هذا السطر هو تعليمة طباعة التي تعرض رسالة على الشاشة: System.out.println("Hello, World!"); يعرض تابع System.out.println النتائج على الشاشة؛ يرمز اسم التّابع println للعبارة "print line" (اطبع سطرًا). من المحيّر أن كلمة اطبع قد تعني "اعرضها على الشاشة" وقد تعني "أرسلها إلى الطابعة". في سلسلتنا هذه، سنحاول عندما نقصد الإخراج على الشاشة أن نقول: "عرض". تنتهي تعليمة الطباعة بفاصلة منقوطة (;) مثل معظم التعليمات. لغة Java "حساسة لحالة الأحرف" (case-sensitive)، وهذا يعني أن الحروف الكبيرة تعتبر مختلفة عن الحروف الصغيرة. في هذا المثال، يجب أن تبدأ كلمة System بحرف كبير؛ وإذا كتبت system أو SYSTEM فلن يعمل. التّابع (method) هو سلسلة تعليمات تحمل اسمًا. هذا البرنامج يعرف تابعًا وحيدًا اسمه main: public static void main(String[] args) اسم التّابع main وشكله خاصين: عند تشغيل البرنامج، يبدأ التنفيذ عند أول تعليمة في main وينتهي عند إنهاء آخر تعليمة فيها. سنرى لاحقًا برامج تعرف فيها أكثر من تابع واحد. الصنف (class) هو مجموعة عمليات. يعرف هذا البرنامج صنفًا اسمه Hello. يمكنك تسمية الصنف بأي اسم تشاء، لكن جرت العادة أن يبدأ اسم الصنف بحرف كبير. يجب أن يطابق اسم الصنف اسم الملف الذي يحويه، ولذلك يجب أن تحفظ هذا الصنف في ملف اسمه Hello.java. تستخدم Java الحاضنات ( { و } ) لتجميع الأشياء مع بعضها. في ملف Hello.java: تضم الحاضنات الخارجية تعريف الصنف، أما الحاضنات الداخلية فتضم تعريف التّابع. السطر الذي يبدأ بشرطتين مائلتين (//) هو تعليق (comment)، وهو عبارة عن نص يوضع لتوضيح الشفرة المصدرية. عندما يرى المترجم علامة // يتجاهل كل شي يليها حتى نهاية السطر. لا تؤثر التعليقات على تنفيذ البرنامج، لكنها تسهل فهم المقصود من التعليمات للمبرمجين الآخرين (ولك أنت في الأوقات اللاحقة). دورة تطوير التطبيقات باستخدام لغة Python احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة اشترك الآن عرض السلاسل المحرفية يمكنك وضع أي عدد من التعليمات داخل main. مثلًا، حتى تعرض أكثر من سطر واحد على الخرج: public class Hello { public static void main(String[] args) { // generate some simple output System.out.println("Hello, World!"); // first line System.out.println("How are you?"); // another line } } كما يبين هذا المثال، يمكنك وضع التعليقات في نهاية السطور كما يمكنك وضعها على سطور كاملة وحدها. تدعى العبارات التي تحيط بها علامات الاقتباس بالسلاسل المحرفية (strings)، لأنها تحوي سلسلة من "المحارف" (characters) المتتابعة. قد يكون المحرف رقمًا، أو حرفًا، أو علامة ترقيم، أو رمزًا، أو فراغًا (space)، أو علامة جدولة (tab)، الخ. تلحق تعليمة System.out.println محرفًا خاصًا بالسلسلة المحرفية، وهو محرف السطر الجديد (newline)، الذي يحرك مؤشر الطباعة إلى بداية السطر التالي. إذا لم ترغب بمحرف السطر الجديد في نهاية العبارة، يمكنك استخدام print بدلًا من println. public class Goodbye { public static void main(String[] args) { System.out.print("Goodbye, "); System.out.println("cruel world"); } } في هذا المثال، لا تضيف التعليمة الأولى محرف السطر الجديد، لذلك يظهر الخرج على سطر واحد كالتالي: Goodbye, cruel world. لاحظ أن هناك فراغًا في نهاية السلسلة المحرفية الأولى، والذي يظهر في خرج البرنامج. سلاسل الهروب يمكنك أن تعرض عدة سطور على الخرج بسطر كود واحد. عليك فقط أن تخبر Java بمواضع فواصل السطور. public class Hello { public static void main(String[] args) { System.out.print("Hello!\nHow are you doing?\n"); } } الخرج هنا سطرين، وكل منهما ينتهي بمحرف السطر الجديد: Hello! How are you doing? المحرفين n\ هما سلسلة هروب (escape sequence)، وهذه السلاسل عبارة عن عدة محارف تعبر عن محرف خاص واحد. تسمح لك الشرطة الخلفية (\) بالهروب من التفسير الحرفي للسلسلة. لاحظ عدم وجود فراغ يفصل بين n\ وبين How. إذا أضفت فراغًا بينهما، سيظهر الفراغ في بداية السطر الثاني. تستخدم سلاسل الهروب أيضًا لطباعة علامات الاقتباس داخل السلاسل المحرفية. بما أن علامات الاقتباس تحدد بدايات السلاسل المحرفية ونهاياتها، فلا يمكنك وضعها في نص السلسلة دون استخدام علامة الشرطة الخلفية. System.out.println("She said \"Hello!\" to me."); سلسلة الهروب المحرف الناتج n\ سطر جديد t\ علامة جدولة؛ تعادل ثمانية فراغات أفقية عادة "\ علامة اقتباس \\ شرطة مائلة خلفية الجدول 1.1: بعض سلاسل الهروب الشائعة الناتج هو: She said "Hello!" to me. تنسيق الكود بعض الفراغات إلزامية في برامج Java. فلا بد، على سبيل المثال، من فصل الكلمات عن بعضها بفراغ واحد على الأقل؛ أي أن البرنامج التالي غير صالح: publicclassGoodbye{ publicstaticvoidmain(String[] args) { System.out.print("Goodbye, "); System.out.println("cruel world"); } } لكن معظم الفراغات الأخرى اختيارية. مثلًا، هذا البرنامج صالح: public class Goodbye { public static void main(String[] args) { System.out.print("Goodbye, "); System.out.println("cruel world"); } } كما أن فصل التعليمات على سطور مختلفة اختياري أيضًا. بالتالي يمكننا كتابة البرنامج السابق هكذا: public class Goodbye{public static void main(String[] args){System.out.print("Goodbye, "); System.out.println("cruel world");}} سيعمل البرنامج، لكن قراءته ازدادت صعوبة في كل مرة. استخدام الفراغات وتوزيع التعليمات على سطور منفصلة مهمان جدًا لتنظيم برنامجك بصريًا، لتسهيل فهمه وتسهيل العثور على الأخطاء فيه عند حدوثها. في العادة، تجد في المنظمات التي تعمل كثيرًا في تطوير البرمجيات شروطًا صارمة تحدد طريقة ترتيب الشفرات المصدرية. مثلًا، تنشر Google معاييرها الخاصة حول كتابة أكواد Java للاستخدام في المشاريع مفتوحة المصدر:http://google.github.io/styleguide/javaguide.html. قد لا تستوعب هذه المعايير الآن، لأنها تذكر بعض مزايا لغة Java التي لما تتعرف عليها. لكنك قد ترغب بزيارتها بين الحين والآخر أثناء قراءة هذه السلسلة. تنقيح الكود كلما أردت اختبار ميزة جديدة، عليك تجربة ارتكاب بعض الأخطاء عن عمد. على سبيل المثال، في برنامج hello world، ماذا يحدث لو أسقطت إحدى علامات الاقتباس؟ ماذا لو أسقطت العلامتين معًا؟ وماذا لو أخطأت بكتابة println؟ هذه التجارب تساعدك على تذكر ما تقرؤه. كما تساعدك في تنقيح الكود، لأنك تتعلم معنى رسائل الأخطاء المختلفة. من الأفضل أن ترتكب الأخطاء الآن عمدًا بدلًا من أن تقع بها لاحقًا دون قصد. تصحيح الأخطاء يشبه العلوم التجريبية أيضًا: بمجرد أن تظن أنك عرفت ما هو الخطأ، تعدل برنامجك وتجرب ثانية. إذا كانت فرضيتك صحيحة، عندئذ ستتمكن من التنبؤ بنتيجة التعديل، وتقترب خطوة من البرنامج الصحيح المطلوب. أما إذا كانت فرضيتك خاطئة، عليك أن تجد حلًا آخر. يجب أن تتلازم البرمجة مع تنقيح الكود من الأخطاء. لا تكتب سطورًا كثيرة وبعدها تنتقل إلى عملية تصحيح معتمدًا على التجربة والخطأ حتى تصل للنتيجة المطلوبة. بل عليك أن تبدأ ببرنامج يعمل قادر على تنفيذ شيء ما، ثم عمل تعديلات صغيرة، وتنقيحها أولًا بأول، حتى تصل إلى برنامج يعطيك النتيجة المطلوبة. بهذا الأسلوب، سيبقى برنامجك يعمل دائمًا، وسيسهل عليك عزل الأخطاء. من الأمثلة الممتازة على هذا المبدأ نظام التشغيل لينكس (Linux)، الذي يحوي ملايين السطور البرمجية. بدأ لينكس كبرنامج بسيط استعمله لينوس تورفالدز (Linus Torvalds) ليستكشف رقاقة إنتل 80386. وعلى حد قول لاري غرينفيلد (Larry Greenfield) في دليل مستخدم لينكس (The Linux Users’ Guide)، ”أحد أولى مشاريع لينوس كان برنامجًا يبدل بين طباعة AAAA و BBBB. ثم تطور هذا إلى لينكس“. أخيرًا، قد تثير البرمجة أحاسيس قوية. إذا كنت تسارع علة صعبة، قد تشعر بالغضب، أو القنوط، أو الحرج. تذكر أنك لست وحدك، وإن جُلّ المبرمجين إن لم يكن كلهم عانوا من حالات مشابهة. لا تتردد في طرح الأسئلة على أحد الأصدقاء! أدوات التطوير تعتمد خطوات ترجمة وتنفيذ وتنقيح أكواد Java على بيئة التطوير لديك ونظام التشغيل الذي تستخدمه. الجزء التّالي هو مقدمة مختصرة لبيئة DrJava وهي بيئة تطوير متكاملة (Integrated Development Environment أو IDE اختصارًا) مناسبة للمبتدئين. تثبيت DrJava أسهل طريقة لبدء البرمجة بلغة Java هي استخدام موقع يترجم لك شفرات Java وينفذها في المتصفح. هناك على سبيل المثال jdoodle.com، و compilejava.net، و tutorialspoint.com، وغيرها. إذا لم تكن قادرًا على تثبيت برامج جديدة على حاسوبك (وهذه هي الحال غالبًا في المدارس العامة ومقاهي الإنترنت)، يمكنك استخدام بيئات التطوير هذه المتاحة عبر الإنترنت لتنفيذ معظم البرامج في هذا الكتاب. لكن إذا أردت ترجمة وتشغيل برامج Java على حاسوبك، سوف تحتاج: عدة تطوير Java (Java Development Kit أو JDK)، التي تتضمن المترجم، وآلة Java الافتراضية (Java Virtual Machine أو JVM) التي تفسر شفرات بايت الناتجة عن الترجمة، وغيرها من الأدوات مثل Javadoc. محرر نصوص بسيط مثل Notepad++ أو Sublime Text، أو بيئة تطوير مثل DrJava، أو Eclipse، أو jGrasp، أو NetBeans. بالنسبة لعدة التطوير (JDK) ننصحك باستخدام Java SE (النسخة القياسية Standard Edition)، التي توفرها Oracle مجانًا. ننصحك أيضًا باستخدام DrJava، وهي بيئة تطوير متكاملة (IDE) مفتوحة المصدر ومكتوبة بلغة Java (انظر الشكل أ.1). لتثبيت JDK، ابحث في الوب عن "download JDK" ويجب أن توصلك النتائج إلى موقع Oracle. انزل لأسفل حتى تصل إلى"Java Platform, Standard Edition" وانقر زر تنزيل JDK. ثم اقبل اتفاقية الترخيص واختر المثبت المناسب لنظام تشغيلك. لا تنسَ تشغيل برنامج التثبيت بعد تنزيله! لثبيت DrJava، اذهب إلى http://drjava.org ونزّل ملف JAR. ننصحك بحفظه على سطح المكتب أو مكان مناسب آخر. فقط انقر على ملف JAR نقرة مزدوجة لتشغيل DrJava. ننصحك عند تشغيل DrJava أول مرة بتغيير ثلاثة خيارات يمكنك الوصول إليها من قائمة: Edit > Preferences في قسم Miscellaneous: عدل Indent Level (المسافة البادئة) إلى 4، وفعّل خيار Automatically Close Block Comments، وألغِ تفعيل خيار Keep Emacs-style Backup Files. الوضع التفاعلي في DrJava إحدى أهم ميزات DrJava هي "لوحة التفاعل" أسفل النافذة. يمكنك تجربة الشفرات فيها بسرعة، دون كتابة تعريف صنف ودون المرور بمراحل حفظ وترجمة وتنفيذ البرنامج. يظهر الشكل التالي مثالًا. هناك ملاحظة دقيقة وحيدة عليك الانتباه لها عند استخدام ميزة التفاعل. إذا لم تضع فاصلة منقوطة في نهاية التعبير الحسابي أو التعليمة، سوف يعرض DrJava النتيجة تلقائيًا. لاحظ في الشكل السابق إنهاء سطور التصريح عن المتغيرات بفواصل منقوطة، بينما لا توجد فواصل في نهايات التعابير المنطقية في السطور التي تليها. توفر عليك هذه الميزة طباعة System.out.println كل مرة. الجميل في هذه الميزة هو أنك لا تحتاج لتعريف صنف جديد، ثم تعريف عملية main، ثم كتابة تعابير عشوائية ضمن تعليمات System.out.println، ثم حفظ الملف المصدري، وبعدها ترجمة الشفرات المصدرية كلها بشكل سليم قبل رؤية النتائج. كما يمكنك أيضًا استخدام السهمين العلوي والسفلي من لوحة المفاتيح لتكرار الأوامر السابقة وتجربة اختلافات متعددة. ترجمة -وبتصرف- لجزء من الفصل الأول من كتاب Think Java: How to Think Like a Computer Scientist لكاتبيه Allen B. Downey و Chris Mayfield.1 نقطة
-
يشرح هذا المقال طريقة كتابة تعليمات تحوي متغيرات تخزن فيها قيمًا مختلفة، كالأرقام والكلمات، وتحوي أيضًا عوامل حسابية (operators)، وهي رموز تمثل عمليات حسابية. ثم يُعرّج على فكرة التركيب (أي استخدام مزايا اللغة التي تعرفنا عليها سابقاً مع بعضها)، كما يتحدث عن ثلاثة أنواع من الأخطاء البرمجية ونذكر فيه المزيد من النصائح عن تنقيح البرامج من الأخطاء. التصريح عن المتغيرات أحد أقوى المزايا لأي لغة برمجة هي القدرة على تعريف ومعالجة المتغيرات (variables). المتغير هو منطقة ذات اسم تخزّن قيمة (value). قد تكون القيم أرقامًا، أو نصوصًا، أو صورًا، أو مقاطع صوتية، وغيرها من أنواع البيانات. عليك أولًا التصريح عن متغير ثم تخزين القيم فيه. String message; هذا النوع من التعليمات يدعى تصريح (declaration)، لأنها تصرح أن نوع المتغير المدعو message هو String. لكل متغير نوع (type) يحدد نوع القيم التي يمكنه تخزينها. مثلًا، النوع int يخزن الأعداد الصحيحة، والنوع String يخزن السلاسل المحرفية. بعض الأنواع تبدأ بحرف كبير وبعضها الآخر يبدأ بحرف صغير. سنعرف معنى هذا التمييز لاحقًا، لكن الآن عليك الانتباه لكتابتها بشكل صحيح. ليس هناك نوع Int ولا string. لإنشاء متغير من النوع الصحيح، التعليمة هي: int x; حيث x هو اسم كيفي اخترناه للمتغير. بشكل عام، عليك اختيار أسماء المتغيرات بحيث تدل على دور المتغير في البرنامج. مثلًا، عندما ترى التصريحات التالية عن المتغيرات: String firstName; String lastName; int hour, minute; هذا المثال يصرح عن متغيرين من نوع String ومتغيرين من نوع int. عندما يتكون اسم المتغير من كلمتين أو أكثر، مثل المتغير firstName، جرت العادة على جعل الحرف الأول من كل كلمة حرفًا كبيرًا عدا الكلمة الأولى. أسماء المتغيرات حساسة لحالة الأحرف، ولذلك فالمتغير firstName مختلف عن المتغير firstName أو FirstName. يوضح هذا المثال أيضًا صيغة التصريح عن عدة متغيرات من نفس النوع في سطر واحد: كلًا من hour و minute هو عدد صحيح (متغير من النوع int). لاحظ أن كل تعليمة تصريح تنتهي بفاصلة منقوطة. يمكنك تسمية متغيراتك كما تشاء. لكن هناك حوالي 50 كلمة محجوزة، تدعى الكلمات المفتاحية (keywords)، ولا يسمح لك باستخدامها كأسماء لمتغيراتك. هذه الكلمات تشمل public، وclass، وstatic، وvoid، وint، التي يستخدمها المترجم لتحليل بنية البرنامج. هناك قائمة كاملة بالكلمات المفتاحية موجودة، لكن لا حاجة لحفظها. معظم المحررات المستخدمة في البرمجة توفر ميزة "تلوين الشفرة" (syntax highlighting)، التي تجعل الأجزاء المختلفة من البرنامج تظهر بألوان مختلفة. الإسناد بعد أن صرحنا عن بعض المتغيرات، سنستخدمها لتخزين بعض القيم فيها. يمكننا عمل ذلك باستخدام تعليمة الإسناد (assignment). message = "Hello!"; // give message the value "Hello!" hour = 11; // assign the value 11 to hour minute = 59; // set minute to 59 يبين هذا المثال ثلاث تعليمات إسناد، والتعليقات تظهر ثلاثة أساليب يستخدمها الناس أحيانًا عندما يقرؤون تعليمات الإسناد. قد تكون المفردات مربكة هنا، لكن الفكرة واضحة : عندما تصرح عن متغير، أنت تنشئ منطقة تخزينية لها اسم. عندما تطبق تعليمة الإسناد على متغير، فأنت تغير القيمة التي يحويها. كقاعدة عامة، يجب أن يكون نوع المتغير من نفس نوع القيمة التي تسندها إليه. مثلًا، لا يمكنك تخزين سلسلة محرفية في المتغير minute أو عددًا صحيحًا في message. من ناحية أخرى، هذه القاعدة قد تكون مصدرًا للإرباك أحيانًا، بسبب وجود العديد من الطرق التي تسمح لك بتحويل القيم من نوع لآخر، وأحيانًا تحول Java الأشياء تلقائيًا. لكن الآن عليك فقط أن تتذكر القاعدة العامة بأن المتغيرات والقيم يجب أن تكون من نفس النوع، وسنتحدث عن الحالات الخاصة لاحقًا. أحد مصادر الإرباك هو أن بعض السلاسل المحرفية تبدو مثل الأرقام، لكنها ليست كذلك. مثلًا، يمكن أن يخزن المتغير message السلسلة المحرفية "123"، المكونة من المحارف '1' و '2' و '3'، لكنها ليست مثل العدد الصحيح 123. message = "123"; // legal message = 123; // not legal يجب تهيئة المتغيرات (initialize)، أي إسناد قيمة لها أول مرة، قبل أن تستخدمها. يمكنك التصريح عن متغير ثم إسناد قيمة له لاحقًا، كما في المثال السابق. كما يمكنك أيضًا التصريح عن المتغير وتهيئته بسطر واحد: String message = "Hello!"; int hour = 11; int minute = 59; مخططات الحالة قد تظن أن تعليمة a = b هي تعليمة مساواة لأن Java تستخدم الرمز = لعملية الإسناد. لكنها ليست مساواة! المساواة عملية تبديلية، أما الإسناد فلا. على سبيل المثال، في الرياضيات إذا كان a = 7 إذًا 7 = a. لكن في Java a = 7; تعليمة إسناد مشروعة، لكن 7 = a غير مشروعة. يجب أن يكون الطرف الأيسر من تعليمة الإسناد متغيرًا (اسمًا لموقع تخزيني). في الرياضيات أيضًا، جملة المساواة صحيحة دائمًا. إذا كان a = b الآن، فإن a سيبقى مساويًا لـ b دائمًا. أما في Java، فتعليمة الإسناد قد تجعل قيمتي متغيرين متساويتان، لكن قد لا يستمران على هذه الحال. int a = 5; int b = a; // a and b are now equal a = 3; // a and b are no longer equal في السطر الثالث تغيرت قيمة a، لكن قيمة b لم تتغير، وبالتالي لم يبق المتغيران متساويان. المتغيرات في البرنامج مع قيمها الحالية تشكل حالة البرنامج (state). يُظهِر الشكل 2.1 حالة البرنامج بعد تنفيذ هذه التعليمات. تدعى هذه المخططات التي تظهر حالة البرنامج بمخططات الحالة (state diagrams). يُمثَّل كل متغير بصندوق يظهر اسم المتغير خارجه وقيمة المتغير داخله. أثناء تنفيذ البرنامج تتغير الحالة، لذلك عليك اعتبار مخططات الحالة كتمثيل لحظي لنقطة محددة في مسار التنفيذ. طباعة المتغيرات يمكنك عرض قيمة متغير باستخدام println أو print. في التعليمات التالية صرحنا عن متغير اسمه firstLine، وأسندنا له القيمة "!Hello, again"، وعرضنا تلك القيمة. String firstLine = "Hello, again!"; System.out.println(firstLine); عندما نتحدث عن عرض متغير فنحن نقصد قيمة المتغير عمومًا. أما لعرض اسمالمتغير، فعليك أن تضعه بين علامتي اقتباس. مثلًا: System.out.print("The value of firstLine is "); System.out.println(firstLine); خرج هذا البرنامج هو: The value of firstLine is Hello, again! بنية تعليمة عرض المتغير هي نفسها بغض النظر عن نوع المتغير. مثلًا: int hour = 11; int minute = 59; System.out.print("The current time is "); System.out.print(hour); System.out.print(":"); System.out.print(minute); System.out.println("."); خرج هذا البرنامج هو: The current time is 11:59. لوضع عدة قيم على نفس السطر، من الشائع استخدام عدة تعليمات print ثم تتبعها تعليمة println في النهاية. لكن لا تنسَ تعليمة println على العديد من الحواسيب، يتم تخزين خرج تعليمات print دون عرضه على الشاشة حتى استدعاء println؛ وعندها يظهر السطر كله دفعة واحدة. إذا أغفلت تعليمة println، فقد يعرض البرنامج الخرج المخزن في أوقات غير متوقعة أو ربما انتهى البرنامج دون طباعة أي شيء. العوامل الحسابية العوامل (operators) هي رموز تمثل حسابات بسيطة. مثلًا، عامل الجمع هو +، وعامل لطرح -، والضرب *، والقسمة /. يحول البرنامج التالي الوقت إلى دقائق: int hour = 11; int minute = 59; System.out.print("Number of minutes since midnight: "); System.out.println(hour * 60 + minute); في هذا المثال، لدينا التعبير (expression) التالي: hour * 60 + minute، الذي يمثل قيمة وحيدة بعد الحساب. عند تنفيذ البرنامج، يستبدل كل متغير بقيمته الحالية، ثم تطبق العوامل عليها. تدعى القيم التي تعمل العوامل عليها بالمعاملات (operands). نتيجة المثال السابق هي: Number of minutes since midnight: 719 التعابير هي تراكيب تتألف بشكل عام من أرقام، ومتغيرات، وعوامل. عند ترجمة وتنفيذ التعابير ستنتج لدينا قيمة وحيدة. مثلًا، التعبير 1 + 1 قيمته 2. وفي التعبير hour - 1 تستبدل Java المتغير بقيمته، وبذلك ينتج 11 - 1، الذي قيمته 1. أما في التعبير hour * 60 + minute فيستبدل المتغيران بقيمتيهما، وهذا يعطي 11 * 60 + 59. تنفذ عملية الضرب أولًا، معطية التعبير 660 + 59. بعد ذلك تجرى عملية الجمع التي تنتج 719. عمليات الجمع والطرح والضرب كلها تعمل كما تتوقع منها تمامًا، لكن عملية القسمة قد تفاجئك. مثلًا، نحاول في التعليمتين التاليتين حساب الجزء الذي مضى من الساعة: System.out.print("Fraction of the hour that has passed: "); System.out.println(minute / 60); الخرج هو: Fraction of the hour that has passed: 0 هذه النتيجة تحير الناس عادة. قيمة minute هي 59، وناتج قسمة 59 على 60 هو 0.98333، وليس 0. المشكلة هي أن Java تنفذ عملية "القسمة الصحيحة" عندما يكون المعاملين عددين صحيحين. عملية القسمة الصحيحة تقرب الناتج دومًا إلى العدد الصحيح السابق، حتى في الحالات التي يكون العدد الصحيح التالي أقرب مثل حالتنا هذه. يمكننا كحل بديل حساب النسبة المئوية بدلًا من العدد العشري: System.out.print("Percent of the hour that has passed: "); System.out.println(minute * 100 / 60); الخرج الجديد هو: Percent of the hour that has passed: 98 لقد قربت النتيجة للأسفل هنا أيضًا، لكن النتيجة الآن صحيحة تقريبًا على الأقل. النقطة العائمة هناك حل مناسب أكثر وهو استخدام أعداد النقطة العائمة (floating-point)، التي تمثل الأعداد العشرية كما تمثل الأعداد الصحيحة أيضًا. في Java، يستخدم النوع double (اختصارًا لعبارة double-precision) افتراضيًا لأعداد النقطة العائمة. يمكنك إنشاء المتغيرات من نوع double وإسناد القيم لها باستخدام نفس الصيغ التي استخدمناها للأنواع الأخرى: double pi; pi = 3.14159; تنفذ Java عملية "قسمة النقطة العائمة" (floating-point division) إذا كان أحد المعاملات أو كلاهما من النوع double. وهكذا يمكننا حل المشكلة التي واجهتنا في القسم السابق: double minute = 59.0; System.out.print("Fraction of the hour that has passed: "); System.out.println(minute / 60.0); الخرج هو: Fraction of the hour that has passed: 0.9833333333333333 ورغم فائدة أعداد النقطة العائمة، إلا أنها قد تسبب الإرباك. مثلًا، Java تفرّق بين القيمة الصحيحة 1 وبين القيمة العشرية 1.0، حتى لو بدا أنهما نفس العدد، فهما يختلفان بالنوع، وعلى وجه الدقة، لا يسمح لك بتنفيذ عمليات إسناد بين النوعين. مثلًا، ما يلي ليس مسموحًا لأن المتغير على الطرف الأيسر من النوع int أما القيمة المسندة له على الطرف الأيمن هي double: int x = 1.1; // compiler error من السهل نسيان هذه القاعدة لأن هناك حالات عديدة تحول فيها Java أحد الأنواع إلى النوع الآخر تلقائيًا. مثلًا: double y = 1; // legal, but bad style من المفترض ألا تكون التعليمة السابقة مشروعة، لكن Java تسمح بها عن طريق التحويل القيمة الصحيحة 1 إلى القيمة العشرية 1.0 تلقائيًا. هذا التساهل مريح، لكنه يسبب المشاكل للمبتدئين غالبًا. مثلًا: double y = 1 / 3; // common mistake قد تتوقع أن يعطى المتغير y القيمة 0.333333، وهي قيمة عشرية مشروعة، لكنه في الواقع سيعطى القيمة 0.0. السبب هو أن العبارة على اليمين هي نسبة بين عددين صحيحين، لذلك تجري Java عملية قسمة صحيحة، والتي تنتج القيمة الصحيحة 0. ثم يتم تحويلها إلى قيمة عشرية، الناتج هو 0.0. إحدى الطرق لحل هذه المشكلة (بعد أن تكتشف أن هذه هي المشكلة) هو جعل الطرف الأيمن عبارة عشرية. التعليمة التالية ستعطي y القيمة 0.333333، كما هو متوقع. double y = 1.0 / 3.0; عليك دائمًا إسناد قيم عشرية لمتغيرات النقطة العائمة في كتابتك. لن يجبرك المترجم على ذلك، لكنك لا تعرف أبدًا متى تظهر لك غلطة بسيطة وتعود عليك وبالًا. أخطاء التقريب معظم أرقام النقطة العائمة صحيحة تقريبيًا. يمكن تمثيل بعض الأرقام بدقة، مثل القيم الصحيحة ذات الأحجام المعقولة. أما الكسور الدورية، مثل 1/3، أو الأرقام غير النسبية، مثل π، فلا يمكن تمثيلها بدقة. الفرق بين العدد الذي نريد والعدد الذي نحصل عليه يدعى خطأ التقريب (rounding error). مثلًا، يجب أن تكون التعليمتان التاليتان متكافئتين: System.out.println(0.1 * 10); System.out.println(0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1); لكن الخرج سيكون كما يلي على معظم الحواسيب: 1.0 0.9999999999999999 المشكلة هي أن 0.1، وهو عدد عشري منته في الأساس 10، هو كسر دوري في الأساس 2، ولذلك يكون تمثيله في النقطة العائمة تقريبي حتمًا. وعندما نجمع الأعداد التقريبية معًا تتراكم أخطاء التقريب. الحساب بالنقطة العائمة له مزايا تفوق عيوبه في العديد من التطبيقات، كالرسوميات الحاسوبية، والتشفير، والتحليل الإحصائي، وإظهار (rendering) الوسائط المتعددة. لكن إذا أردت دقة مطلقة، عليك استخدام الأعداد الصحيحة بدلًا منها. خذ على سبيل المثال حساب بنك فيه رصيد قيمته 123.45$: double balance = 123.45; // potential rounding error في هذا المثال، ستصبح الأرصدة غير دقيقة مع الوقت واستخدام المتغير في العمليات الحسابية كالسحب والإيداع. ستكون النتيجة سخط العملاء أو دعاوى قضائية. يمكنك تفادي المشكلة بتمثيل الرصيد كعدد صحيح: int balance = 12345; // total number of cents هذا الحل صحيح طالما أن عدد السنتات لا يتجاوز أكبر قيمة صحيحة يمكن تخزينها، وهي حوالي 2 مليار. العمليات على السلاسل المحرفية بشكل عام، لا يمكنك تطبيق العمليات الرياضية على السلاسل المحرفية، حتى لو كانت السلاسل المحرفية تبدو وكأنها أرقام. التعابير التالية غير مشروعة: "Hello" - 1 "World" / 123 "Hello" * "World" العامل + يعمل مع السلاسل المحرفية، لكنه قد لا ينتج ما تتوقعه. يجري عامل + عملية ربط السلاسل (concatenation) أي دمج المعاملين بوصلهما معًا. وهكذا فإن "Hello" + "World" ستعطي السلسلة "Hello World". أو إذا كان لديك متغير اسمه name من النوع String، فسوف يدمج التعبير "Hello" + "Name" قيمة name مع كلمة الترحيب. بما أن عملية الجمع معرفة للأرقام والسلاسل المحرفية أيضًا، فإن Java تجري عمليات تحويل تلقائية قد لا تتوقعها: System.out.println(1 + 2 + "Hello"); // the output is 3Hello System.out.println("Hello" + 1 + 2); // the output is Hello12 تنفذ Java هذه العمليات من اليسار إلى اليمين. في السطر الأول، 1 + 2 يساوي 3، و 3 + "Hello" يساوي "3Hello". أما في السطر الثاني، Hello" + 1" يساوي "Hello1"، و Hello1" + 2" يعطي "Hello12". ترتيب الحساب عندما يظهر أكثر من عامل في تعبير حسابي فسوف تنفذ حسب ترتيب العمليات (order of operation). بشكل عام، تنفذ Java العمليات حسب ترتيب ورودها من اليسار إلى اليمين (كما رأينا في القسم السابق). لكن Java تتبع قواعد الرياضيات في العمليات الحسابية: عمليتي الضرب والقسمة لهما ”أولوية“ (precedence) على الجمع والطرح. لذا فإن 3 * 2 + 1 سيعطي 7، وليس 9، كما أن 2 / 4 + 2 تعطي 4، وليس 3. إذا كان للعوامل نفس الأولوية فسوف تنفذ بالترتيب من اليسار إلى اليمين. ففي التعبير الحسابي minute * 100 / 60، يتم تنفيذ عملية الضرب أولًا، وإذا كانت قيمة minute هي 59 فسوف ينتج لدينا 60 / 5900، والذي بدوره يعطي 98. لو أن تنفيذ العملية الحسابية جرى من اليمين لليسار، ستكون النتيجة 1 * 59 والذي هو 59، وهو جواب خاطئ. في أي وقت ترغب فيه بتجاوز قواعد الأولوية (أو أنك لم تكن واثقًا من تلك القواعد) يمكنك استعمال الأقواس. يتم تنفيذ العمليات ضمن الأقواس أولًا، لهذا فإن 3 * (2 + 1) يعطي 9. يمكنك استعمال الأقواس أيضًا لجعل العبارات الحسابية أسهل للقراءة، كما في 60 / (minute * 100)، مع أنها لا تغير النتيجة. لا تجهد نفسك في حفظ ترتيب تنفيذ العمليات، خصوصًا مع العوامل الأخرى. إذا لم يكن ترتيب التنفيذ واضحًا عند النظر إلى التعبير، فاستخدم الأقواس لجعله واضحًا. التركيب في الأجزاء السابقة كنا نتعرف على مكونات لغة البرمجة: المتغيرات، والتعابير، والتعليمات بشكل مستقل، دون أن نناقش طريقة استخدامها معاً. أحد أهم ميزات لغات البرمجة هي قدرتها على تركيب (compose) الأجزاء الصغيرة مع بعضها. مثلاً، نحن نعرف كيف نضرب الأرقام ونعرف كيف نعرض القيم. يمكننا دمج هاتين العمليتين في تعليمة واحدة: System.out.println(17 * 3); يمكن استخدام أي تعبير حسابي داخل تعليمات الطباعة. لقد شاهدنا مثالاً على هذا من قبل: System.out.println(hour * 60 + minute); يمكنك أيضاً وضع تعابير حسابية متنوعة على الطرف الأيمن لعملية الإسناد: int percentage; percentage = (minute * 100) / 60; لكن الطرف الأيسر لا بد أن يكون اسم متغير، وليس تعبيراً. ذلك لأن الطرف الأيسر يدل على موقع تخزين النتيجة، والتعابير لا تمثل مواقع تخزينية. hour = minute + 1; // correct minute + 1 = hour; // compiler error قد لا تبهرك القدرة على تركيب العمليات الآن، لكننا سنرى لاحقاً أمثلة تسمح لنا بكتابة حسابات معقدة بشكل مرتب وأنيق. لكن لا تبالغ كثيراً، فالتعابير الكبيرة المعقدة قد تصعب قراءتها وتنقيحها من الأخطاء. أنواع الأخطاء هناك ثلاثة أنواع يحتمل أن تحدث في البرنامج: أخطاء الترجمة. أخطاء التنفيذ. الأخطاء المنطقية. من المفيد التمييز بينها في سبيل تتبعها بشكل أسرع، وقبل أن نتحدث عنها واحدة واحدة، يمكنك الاطلاع على الفيديو الآتي الذي يشرحها بالتفصيل: تحدث أخطاء الترجمة (compile-time errors) عندما تخالف التراكيب النحوية (syntax) للغة Java. مثلاً، يجب أن تكون أزواج الأقواس متناظرة. ولذلك فإن (2 + 1) صيغة مقبولة أما (8 ليست كذلك. في الحالة الثانية، لن تتمكن من ترجمة البرنامج، وسيعرض المترجم رسالة خطأ. تدل رسائل الخطأ التي يعرضها المترجم على موقع حدوث الخطأ في البرنامج عادة، وأحياناً تخبرك بطبيعة الخطأ بدقة. على سبيل المثال، لنعد إلى برنامج hello world: public class Hello { public static void main(String[] args) { // generate some simple output System.out.println("Hello, World!"); } } إذا نسيت الفاصلة المنقوطة في نهاية تعليمة الطباعة، فقد تظهر لك رسالة خطأ كما يلي: File: Hello.java [line: 5] Error: ‘;’ expected هذا جيد جداً: موقع الخطأ صحيح، ورسالة الخطأ تخبرك بالمشكلة. لكن رسائل الأخطاء ليست يسيرة الفهم دوماً. أحياناً يعطي المترجم مكان اكتشاف الخطأ في البرنامج، وليس مكان حدوثه حقاً. وأحياناً يكون وصف المشكلة محيراً أكثر مما هو مفيد. مثلاً، إذا نسيت قوس الإغلاق المعقوف في نهاية main (سطر 6)، قد تحصل على رسالة تشبة الرسالة التالية: File: Hello.java [line: 7] Error: reached end of file while parsing هناك مشكلتان هنا. أولاً، رسالة الخطأ مكتوبة من وجهة نظر المترجم، وليس وجهة نظرك أنت. عملية الإعراب (parsing) هي عملية قراءة البرنامج قبل الترجمة؛ فإذا وصل المترجم لنهاية الملف قبل انتهاء الإعراب، فهذا يدل على نقصان شيء ما. لكن المترجم لا يعرف ما هو. كما أنه لا يعرف أين. يكتشف المترجم الخطأ عند نهاية البرنامج (سطر 7)، لكن القوس الناقص يجب أن يكون على السطر السابق. تحوي رسائل الأخطاء معلومات مفيدة، لذلك عليك محاولة قراءتها وفهمها. لكن لا تأخذها بشكل حرفي تماماً. ستمضي غالباً وقتاً طويلاً خلال الأسابيع الأولى في سيرتك البرمجية وأنت تتابع أخطاء الترجمة. لكن مع زيادة خبرتك، سوف ترتكب أخطاءً أقل وستعثر عليها أسرع. النوع الثاني من الأخطاء هي أخطاء التنفيذ (run-time errors)، وقد سمّيت كذلك لأنها لا تظهر قبل تنفيذ البرنامج. في Java، تظهر هذه الأخطاء عندما ينفذ المفسر شفرة بايت ويحدث خطأ ما. تدعى هذه الأخطاء "استثناءات" (exceptions) لأنها تدل عادة على حدوث شيء استثنائي (وسيء). أخطاء التنفيذ نادرة في البرامج البسيطة التي ستراها في الفصول القليلة الأولى، لذلك قد لا ترى واحداً إلا بعد حين. عند حدوث خطأ تنفيذي، يعرض المفسر رسالة خطأ تحوي معلومات تشرح ما حدث وتحدد مكان حدوثه. مثلاً، إذا أجريت عملية قسمة على صفر عن غير قصد فسوف تظهر رسالة تشبه ما يلي: Exception in thread “main” java.lang.ArithmeticException: / by zero at Hello.main(Hello.java:5) بعض هذه المعلومات مفيد في تنقيح البرنامج من الأخطاء. يتضمن السطر الأول اسم الاستثناء java.lang.ArithmeticException ورسالة تبين ما حدث بدقة أكبر "by zero /". يظهر السطر التالي العملية التي حدث فيها الخطأ؛ حيث يقصد بعبارة Hello.main العملية main في الصنف Hello. كما أنه يذكر أيضاً اسم الملف الذي عرفت فيه العملية (Hello.java) ورقم السطر الذي حدث فيه الخطأ (5). أحياناً تحوي رسائل الأخطاء معلومات إضافية لن تفهم معناها الآن. لذلك سيكون أحد التحديات معرفة الأجزاء المفيدة دون أن تغرق بالمعلومات الإضافية. وعليك أن تنتبه أيضاً أن السطر الذي سبب انهيار تنفيذ البرنامج قد لا يكون السطر الذي يحتاج للتصحيح. النوع الثالث من الأخطاء هو الأخطاء المنطقية (logic error). إذا كان هناك خطأ منطقي في برنامجك، فستتم ترجمته وتشغيله دون تولد أي رسالة خطأ، لكنه لن يعطي الناتج الصحيح المطلوب، بل سينفذ ما طلبته منه حرفياً. مثلاً، هذا برنامج hello world فيه خطأ منطقي: public class Hello { public static void main(String[] args) { System.out.println("Hello, "); System.out.println("World!"); } } هذا البرنامج يترجم وينفذ بشكل سليم، لكن الخرج هو: Hello, World! على فرض أننا أردنا أن يكون الخرج على سطر واحد، فهذا الناتج غير صحيح. المشكلة هي أن السطر الأول يستخدم println، بينما نحن أرنا غالباً استخدام print. قد يصعب التعرف على الأخطاء المنطقية لأنك ستضطر للعمل بالمقلوب: تنظر إلى مخرجات البرنامج وتحاول استنتاج السبب الذي يجعله يعطي هذه النتائج الخاطئة، وكيف تجعله يعطي النتائج الصحيحة. عادة لا يستطيع المترجم ولا المفسر مساعدتك هنا، لأنهما لا يعرفان ما هو الشيء الصحيح المطلوب. ترجمة -وبتصرف- للفصل Variables and operators من كتاب Think Java: How to Think Like a Computer Scientist لكاتبيه Allen B. Downey و Chris Mayfield.1 نقطة