اذهب إلى المحتوى

كل الأنشطة

تحدث تلقائيًا

  1. الساعة الماضية
  2. أولاً لديك خطأ في حساب متوسط التقييمات في السطر الذي يحسب فيه المتوسط النهائي finalRate. فالمتغير rate ربما يتم إرساله كقيمة غير صحيحة مثل قيمة نصية من الواجهة الأمامية، لذا لنتأكد من أن rate يتم تحويله إلى رقم صحيح قبل استخدامه في الحساب، وأيضًا التأكد من صحة قيم التقييم المرسلة من الواجهة الأمامية. أيضًا بعد إضافة المراجعة إلى قاعدة البيانات، عليك إعادة بيانات الكتاب المحدثة إلى الواجهة الأمامية، بما في ذلك التقييم الجديد، وبعد تلقي بيانات الكتاب المحدثة من الخادم، يجب تحديث حالة الكتاب في الواجهة الأمامية لتعكس التقييم الجديد. ثم استخدام البيانات المحدثة للكتاب في المكون لعرض التقييم الجديد. إليك التعديلات مع تعليقات لتوضيح أماكن التعديل. تعديل في bookController.js: const addReview = asyncHandler(async(req, res) => { const { id } = req.params const { comment, rate } = req.body const book = await Book.findById(id) const user = await User.findById(req.userId) // التحقق من وجود الكتاب if (!book) { return res.status(404).json({ message: "Book Not Found" }) } // التحقق من أن المستخدم لم يقيم الكتاب مسبقاً const isRated = book.reviews.findIndex(m => m.user == req.userId) if (isRated > -1){ return res.status(403).send({ message: "Review Is Already Added" }); } // تحويل تقييم المستخدم إلى رقم صحيح والتحقق من صحته const numericRate = parseInt(rate, 10); if (isNaN(numericRate) || numericRate < 1 || numericRate > 5) { return res.status(400).send({ message: "Invalid rating value" }); } // حساب التقييم النهائي const totalRate = book.reviews.reduce((sum, review) => sum + review.rate ,0) const finalRate = (totalRate + numericRate) / (book.reviews.length + 1) // تحديث بيانات الكتاب بإضافة المراجعة الجديدة وتحديث التقييم await Book.updateOne( { _id: id }, { $push: { reviews: { user: req.userId, username: user.name, comment, rate: numericRate } }, $set: { rate: finalRate } } ) // إعادة بيانات الكتاب المحدثة بعد الإضافة const updatedBook = await Book.findById(id); res.status(201).json(updatedBook) }) تحديث الدالة postReview في apiCall: export function postReview(bookId, review) { return async (dispatch, getState) => { try { dispatch(bookActions.setLoading()) const {data} = await axios.post(`${BOOK_URL}/${bookId}/reviews`, review, { headers: { "authorization": getState().auth.user.accessToken } }); // عرض رسالة نجاح باستخدام toast toast.success(data?.message) // تحديث بيانات الكتاب المحدثة في الحالة dispatch(bookActions.updateBook(data)); dispatch(bookActions.clearLoading()); } catch (error) { // عرض رسالة خطأ باستخدام toast toast.error(error?.response?.data.message); dispatch(bookActions.clearLoading()); } }; } تعديل في الواجهة الأمامية Modal.js const Modal = ({ showModal, handleClose, book }) => { const [rate, setRating] = useState(0); const [comment, setComment] = useState(""); const dispatch = useDispatch(); const navigate = useNavigate(); const submitReview = (e) => { e.preventDefault(); if (comment === "") { // التحقق من وجود تعليق وعرض رسالة خطأ في حال عدم وجوده return toast.error("Comment is required") } // تحويل تقييم المستخدم إلى رقم صحيح والتحقق من صحته const numericRate = parseInt(rate, 10); if (isNaN(numericRate) || numericRate < 1 || numericRate > 5) { return toast.error("Invalid rating value"); } // إرسال المراجعة باستخدام الدالة postReview dispatch(postReview(book._id, { rate: numericRate, comment })) }; return ( <div className={`modal ${showModal ? "show" : ""}`}> <div className="modal-content"> <span className="close" onClick={handleClose}>&times;</span> <h2>Submit Your Review</h2> <form onSubmit={submitReview}> <div className="rating-input"> <label>Rating:</label> <select value={rate} onChange={(e) => setRating(e.target.value)} required> <option value="" disabled>Select a rating</option> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> <option value="4">4</option> <option value="5">5</option> </select> </div> <div className="comment"> <label>Comment:</label> <textarea value={comment} onChange={(e) => setComment(e.target.value)} required /> </div> <button className='modal-btn' type="submit">Submit</button> </form> </div> </div> ); }; export default Modal; تحديث bookSlice لتضمين الدالة updateBook: const bookSlice = createSlice({ name: "book", initialState: { books: [], error: false, loading: false, }, reducers: { getBooks(state, action) { state.books = action.payload; }, findBook(state, action) { state.books = action.payload; }, addReviews(state, action) { state.books = action.payload; }, updateBook(state, action) { // البحث عن الكتاب المحدث وتحديثه const updatedBook = action.payload; const index = state.books.findIndex(book => book._id === updatedBook._id); if (index !== -1) { state.books[index] = updatedBook; } }, setLoading(state) { state.loading = true; }, clearLoading(state) { state.loading = false; }, setError(state) { state.error = true; }, clearError(state) { state.error = false; }, } }); export const bookActions = bookSlice.actions; export default bookSlice.reducer;
  3. السلام عليكم. في المثال التالي، أواجه الأخطاء التالية: عندما أضيف Review معين (بين 1 و 5) فإنه يتم إرسال عدد أكبر من 5 إلى قاعدة البيانات لايتم إضافة التقييم إلى واجهة الصفحة إلا بعد التحديث الكود bookController // method POST // route api/books/:id/reviews // desc Add a book review // access Private | auth const addReview = asyncHandler(async(req, res) => { const { id } = req.params const { comment, rate } = req.body const book = await Book.findById(id) const user = await User.findById(req.userId) if (!book) { return res.status(404).json({ message: "Book Not Found" }) } const isRated = book.reviews.findIndex(m => m.user == req.userId) if (isRated > -1){ return res.status(403).send({ message: "Review Is Already Added" }); } const totalRate = book.reviews.reduce((sum, review) => sum + review.rate ,0) const finalRate = (totalRate + rate) / (book.reviews.length + 1) await Book.updateOne( { _id: id } , { $push: { reviews: { user: req.userId, username: user.name, comment, rate } }, $set: { rate: finalRate } } ) res.status(201).json({ message: "Review added successfully" }) }) Frontend apiCall // Post Review export function postReview(bookId, review) { return async (dispatch, getState) => { try { dispatch(bookActions.setLoading()) const {data} = await axios.post(`${BOOK_URL}/${bookId}/reviews`, review, { headers: { "authorization": getState().auth.user.accessToken } }); toast.success(data?.message) dispatch(bookActions.addReviews()); dispatch(bookActions.clearLoading()); } catch (error) { toast.error(error?.response?.data.message); dispatch(bookActions.clearLoading()); } }; } bookSlice const bookSlice = createSlice({ name: "book", initialState: { books: [], error: false, loading: false, }, reducers: { getBooks(state, action) { state.books = action.payload; }, findBook(state, action) { state.books = action.payload; }, addReviews(state, action) { state.books = action.payload; }, setLoading(state) { state.loading = true; }, clearLoading(state) { state.loading = false; }, setError(state) { state.error = true; }, clearError(state) { state.error = false; }, } } Component const Modal = ({ showModal, handleClose, book }) => { const [rate, setRating] = useState(0); const [comment, setComment] = useState(""); const dispatch = useDispatch(); const navigate = useNavigate(); const submitReview = (e) => { e.preventDefault(); if (comment === "") { return toast.error("Comment is required") } dispatch(postReview(book, { rate, comment })) navigate(`/${book}`) }; return ( <div className={`modal ${showModal ? "show" : ""}`}> <div className="modal-content"> <span className="close" onClick={handleClose}>&times;</span> <h2>Submit Your Review</h2> <form onSubmit={submitReview}> <div className="rating-input"> <label>Rating:</label> <select value={rate} onChange={(e) => setRating(e.target.value)} required> <option value="" disabled>Select a rating</option> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> <option value="4">4</option> <option value="5">5</option> </select> </div> <div className="comment"> <label>Comment:</label> <textarea value={comment} onChange={(e) => setComment(e.target.value)}/> </div> <button className='modal-btn' type="submit">Submit</button> </form> </div> </div> ); }; export default Modal; شكرا على المساعدة
  4. اليوم
  5. سنشرح في هذا المقال نوع البيانات المسمى بالقاموس dictionary، الذي يوفر طريقة مرنة للوصول إلى البيانات وتنظيمها، ثم سنتعلم كيفية كتابة لعبة إكس-أو عبر دمج ما تعلمناه في المقالات السابقة من هذه السلسلة مع القواميس. نوع البيانات dictionary القواميس تشبه القوائم في أنها مجموعة قابلة للتعديل mutable من القيم، لكن بدلًا من وجود الفهارس الرقمية للعناصر كما في القوائم، يمكن استخدام مختلف أنواع البيانات في القواميس. نسمي المفاتيح في القواميس بالمفاتيح keys، وكل مفتاح مرتبط بقيمة، ونسمي ذلك زوجًا من المفاتيح والقيم key-value pair. نكتب القواميس في بايثون بإحاطها بقوسين مجعدين أو معقوصين {}: >>> myCat = {'size': 'fat', 'color': 'gray', 'disposition': 'loud'} السطر السابق يسند قاموسًا إلى المتغير myCat، ومفاتيح هذا القاموس هي 'size' و 'color' و 'disposition'، والقيم المرتبطة بتلك المفاتيح هي 'fat' و 'gray' و 'loud' على التوالي وبالترتيب. يمكنك الوصول إلى هذه القيم عبر مفاتيحها: >>> myCat['size'] 'fat' >>> 'My cat has ' + myCat['color'] + ' fur.' 'My cat has gray fur.' ما يزال بالإمكان استخدام الأعداد الصحيحة مفاتيحًا لقيم القواميس، لكن ليس من الضروري أن تبدأ من الصفر 0 ويمكنك استخدام أي رقم: >>> spam = {12345: 'Luggage Combination', 42: 'The Answer'} مقارنة القواميس والقوائم على خلاف القوائم، لا تكون القواميس مرتبةً، فأول عنصر في قائمة اسمها spam سيكون spam[0]‎، لكن لا يوجد «أول» عنصر في القاموس. سيكون ترتيب العناصر مهمًا في حال أردنا تحديد إن كانت قائمتان متساويتين، بينما لا يفرق ترتيب كتابة أزواج المفاتيح-القيم في القواميس: >>> spam = ['cats', 'dogs', 'moose'] >>> olive = ['dogs', 'moose', 'cats'] >>> spam == olive False >>> eggs = {'name': 'Zophie', 'species': 'cat', 'age': '8'} >>> steak = {'species': 'cat', 'age': '8', 'name': 'Zophie'} >>> eggs == steak True ولأن القواميس غير مرتبة، فلا يمكن تقسيمها كما في القوائم. محاولة الوصول إلى مفتاح غير موجود في قاموس ما سيؤدي إلى حدوث الخطأ KeyError، وهي تشبه رسالة الخطأ IndexError في القوائم حين محاولة الوصول إلى قيمة خارج مجال فهارس القائمة. لاحظ رسالة الخطأ الآتية التي تظهر لعدم وجود المفتاح 'color': >>> spam = {'name': 'Zophie', 'age': 7} >>> spam['color'] Traceback (most recent call last): File "<pyshell#1>", line 1, in <module> spam['color'] KeyError: 'color' وصحيحٌ أن القواميس غير مرتبة، لكن إمكانية استخدام أي قيمة تريدها للمفاتيح يسمح لنا بترتيب البيانات بطرائق رائعة! لنقل أننا نريد كتابة برنامج يخزن معلومات حول أعياد ميلاد أصدقائك، يمكنك أن تكتب الشيفرة الآتية birthdays.py التي تستخدم القواميس وتجعل أسماء أصدقائك مفاتيحًا للقيم: ➊ birthdays = {'Alice': 'Apr 1', 'Bob': 'Dec 12', 'Carol': 'Mar 4'} while True: print('Enter a name: (blank to quit)') name = input() if name == '': break ➋ if name in birthdays: ➌ print(birthdays[name] + ' is the birthday of ' + name) else: print('I do not have birthday information for ' + name) print('What is their birthday?') bday = input() ➍ birthdays[name] = bday print('Birthday database updated.') أنشأنا قاموسًا وخزناه في المتغير birthdays ➊، ثم تحققنا إن كان الاسم المدخل موجودًا في القاموس عبر الكلمة المحجوزة in ➋ كما كنا نفعل مع القوائم. إذا كان الاسم موجودًا في القاموس فيمكنك الوصول إلى القيمة المرتبطة به عبر استخدام الأقواس المربعة ➌، وإلّا فيمكنك إضافتها باستخدام صيغة الأقواس المربعة مع عامل الإسناد ➍: Enter a name: (blank to quit) Alice Apr 1 is the birthday of Alice Enter a name: (blank to quit) Eve I do not have birthday information for Eve What is their birthday? Dec 5 Birthday database updated. Enter a name: (blank to quit) Eve Dec 5 is the birthday of Eve Enter a name: (blank to quit) للأسف ستحذف كل المعلومات التي أدخلتها في هذا البرنامج حين انتهاء تنفيذه. ستتعلم كيفية حفظ الملفات على القرص في مقال لاحق من هذه السلسلة. القواميس المرتبة في بايثون 3.7 صحيح أن القواميس غير مرتبة ولا يوجد فيها عنصر «أول»، لكن القواميس في الإصدار 3.7 من بايثون وما يليه ستتذكر ترتيب أزواج القيم الموجودة فيها حين إنشاء متسلسل sequence منها. فمثلًا لاحظ أن ترتيب العناصر في القائمتين المنشأتين من القاموسين eggs و steak يطابق ترتيب إدخالها: >>> eggs = {'name': 'Zophie', 'species': 'cat', 'age': '8'} >>> list(eggs) ['name', 'species', 'age'] >>> steak = {'species': 'cat', 'age': '8', 'name': 'Zophie'} >>> list(steak) ['species', 'age', 'name'] ستبقى القواميس غير مرتبة، ولا يمكنك أن تصل إلى العناصر فيها عبر فهرس رقمي مثل eggs[0]‎ أو steak[2]‎، لا يجدر بك الاعتماد على هذا السلوك لأن بايثون لا تتذكر ترتيب إدخال العناصر في الإصدارات القيمة منها، فلاحظ المثال الآتي الذي لا يطابق ترتيب عناصر القاموس الناتج النهائي الترتيبَ الذي أدخلتها به، ناتج التنفيذ الآتي على إصدار بايثون 3.5: >>> spam = {} >>> spam['first key'] = 'value' >>> spam['second key'] = 'value' >>> spam['third key'] = 'value' >>> list(spam) ['first key', 'third key', 'second key'] التوابع keys()‎ و values()‎ و items()‎ هنالك ثلاثة توابع خاصة بالقواميس التي تعيد قيمًا شبيهة بالقوائم list-like من مفاتيح القواميس أو قيمها أو كلًا من المفاتيح والقيم معًا وهي التوابع keys()‎ و values()‎ و items()‎ بالترتيب. القيم المعادة من هذه التوابع ليست قوائم حقيقيةً، فلا يمكننا تعديلها وليس لها التابع append()‎، لكن أنواع البيانات المعادة (وهي dictkeys و dictvalues و dict_items بالترتيب) يمكن أن تستخدم في حلقات التكرار for: >>> spam = {'color': 'red', 'age': 42} >>> for v in spam.values(): ... print(v) red 42 ستمر حلقة for هنا على كل قيمة في القاموس spam، يمكن لحلقة for المرور على المفاتيح فقط، وعلى المفاتيح والقيم معًا: >>> for k in spam.keys(): ... print(k) color age >>> for i in spam.items(): ... print(i) ('color', 'red') ('age', 42) حين استخدامنا للتوابع keys()‎ و values()‎ و items()‎ فيمكن للحلقة for المرور على قيم المفاتيح أو قيم العناصر أو قيم أزواج المفتاح-القيمة على التوالي. لاحظ أن القيم المعادة من items()‎ هي صفوف tuples تحتوي على المفتاح ثم قيمته. إذا أردتَ قائمةً حقيقية من ناتج أحد تلك التوابع، فيمكننا تمرير القيمة الشبيهة بالقوائم إلى الدالة list()‎ كما يلي: >>> spam = {'color': 'red', 'age': 42} >>> spam.keys() dict_keys(['color', 'age']) >>> list(spam.keys()) ['color', 'age'] يأخذ السطر list(spam.keys())‎ القيمة ذات النوع dict_keys المعادة من التابع keys()‎ ويمررها إلى الدالة list()‎، والتي تعيد بدورها قائمةً فيها ['color', 'age']. يمكنك استخدام الإسناد المتعدد مع حلقة for لإسناد المفتاح والقيمة إلى متغيرات منفصلة: >>> spam = {'color': 'red', 'age': 42} >>> for k, v in spam.items(): ... print('Key: ' + k + ' Value: ' + str(v)) Key: age Value: 42 Key: color Value: red التحقق من وجود مفتاح أو قيمة في قاموس نتذكر من المقال السابق أن العاملين in و not in يمكن أن يستخدما للتحقق من وجود قيمة في قائمة. ويمكننا استخدام نفس العاملين للتحقق من وجود قيمة ما في مفتاح أو قيمة في قاموس: >>> spam = {'name': 'Zophie', 'age': 7} >>> 'name' in spam.keys() True >>> 'Zophie' in spam.values() True >>> 'color' in spam.keys() False >>> 'color' not in spam.keys() True >>> 'color' in spam False لاحظ أن كتابة ‎'color' in spam هي مطابقة لكتابة ‎'color' in spam.keys()‎. فإذا أردت التحقق من وجود (أو عدم وجود) مفتاح ما في قاموس فاستعمل الكلمة المحجوزة in (أو not in) مع القاموس نفسه. التابع get()‎ من الرتيب أن نتحقق من وجود مفتاح ما في القاموس قبل الوصول إلى القيمة المرتبطة به، لكن لحسن الحظ هنالك تابع باسم get()‎ يأخذ وسيطين: الأول هو المفتاح الذي نريد الوصول إلى قيمته، والثاني هو قيمة افتراضية ستعاد إن لم يكن المفتاح موجودًا: >>> picnicItems = {'apples': 5, 'cups': 2} >>> 'I am bringing ' + str(picnicItems.get('cups', 0)) + ' cups.' 'I am bringing 2 cups.' >>> 'I am bringing ' + str(picnicItems.get('eggs', 0)) + ' eggs.' 'I am bringing 0 eggs.' ولعدم وجود المفتاح 'eggs' في القاموس picnicItems فستعاد القيمة 0 من التابع get()‎، وإن لم نستعمل التابع get()‎ في المثال السابق فستظهر رسالة خطأ كما يلي: >>> picnicItems = {'apples': 5, 'cups': 2} >>> 'I am bringing ' + str(picnicItems['eggs']) + ' eggs.' Traceback (most recent call last): File "<pyshell#34>", line 1, in <module> 'I am bringing ' + str(picnicItems['eggs']) + ' eggs.' KeyError: 'eggs' التابع setdefault()‎ الحاجة إلى ضبط قيمة في القاموس مرتبطة بمفتاح معين إن لم يكن ذاك المفتاح موجودًا مسبقًا هو أمرٌ شائع، وتكون الشيفرة بالشكل الآتي: spam = {'name': 'Pooka', 'age': 5} if 'color' not in spam: spam['color'] = 'black' يوفر التابع setdefault()‎ طريقة أسهل لفعل ذلك بسطر برمجي وحيد، فأول وسيط يمرر إلى التابع هو المفتاح الذي سنتحقق من وجوده، والوسيط الثاني هو القيمة التي ستُضبَط إن لم يكن المفتاح موجودًا. إذا كان المفتاح موجودًا فسيعيد التابع setdefault()‎ قيمة ذاك المفتاح: >>> spam = {'name': 'Pooka', 'age': 5} >>> spam.setdefault('color', 'black') 'black' >>> spam {'color': 'black', 'age': 5, 'name': 'Pooka'} >>> spam.setdefault('color', 'white') 'black' >>> spam {'color': 'black', 'age': 5, 'name': 'Pooka'} حينما استدعينا التابع setdefault()‎ أول مرة، فتغيرت قيمة القاموس spam إلى ‎{'color': 'black', 'age': 5, 'name': 'Pooka'}‎، وسيعيد التابع setdefault()‎ القيمة 'black' لأنها القيمة المضبوطة للمفتاح 'color' حاليًا. لكن حين استدعاء spam.setdefault('color', 'white')‎ فإن القيمة لن تتغير إلى 'white' لأن القاموس spam يحتوي على مفتاح باسم 'color'. التابع setdefault()‎ هو اختصار جميل للتأكد من وجود مفتاح معين وضبط قيمته. هذا مثال عن برنامج يعدّ عدد مرات وجود كل حرف في سلسلة نصية. احفظ المثال الآتي باسم characterCount.py: message = 'It was a bright cold day in April, and the clocks were striking thirteen.' count = {} for character in message: ➊ count.setdefault(character, 0) ➋ count[character] = count[character] + 1 print(count) سيمر البرنامج على كل محرف في السلسلة النصية المخزنة في المتغير message، ويعد كم مرة يظهر فيها كل محرف. استدعاء الدالة setdefault()‎ ➊ سيضمن وجود المفتاح في القاموس count ويضبط قيمته الافتراضية إلى 0، وبالتالي لا يرمي البرنامج الخطأ KeyError حين تنفيذ التعبير البرمجي count[character] = count[character] + 1 ➋. سيبدو الناتج كما يلي: {' ': 13, ',': 1, '.': 1, 'A': 1, 'I': 1, 'a': 4, 'c': 3, 'b': 1, 'e': 5, 'd': 3, 'g': 2, 'i': 6, 'h': 3, 'k': 2, 'l': 3, 'o': 2, 'n': 4, 'p': 1, 's': 3, 'r': 5, 't': 6, 'w': 2, 'y': 1} سترى من الناتج السابق أن الحرف c الصغير مكرر 3 مرات، بينما الفراغ مكرر 13 مرة، والحرف A الكبير يظهر مرة واحدة. سيعمل البرنامج السابق على جميع السلاسل النصية بغض النظر عن محتويات المتغير message حتى لو كان يحتوي على مليون حرف! تجميل الطباعة إذا استوردت الوحدة pprint في برامجك، فيمكنك الوصول إلى الدالتين pprint()‎ و pformat()‎ التي «تجمل طباعة» pretty print قيم القواميس، ستستفيد من هذه الدوال إن أردت عرض قيم القواميس عرضًا أجمل من طريقة عرض الدالة print()‎، لنعدل المثال السابق: import pprint message = 'It was a bright cold day in April, and the clocks were striking thirteen.' count = {} for character in message: count.setdefault(character, 0) count[character] = count[character] + 1 pprint.pprint(count) سيظهر لنا الناتج الجميل الآتي: {' ': 13, ',': 1, '.': 1, 'A': 1, 'I': 1, --snip-- 't': 6, 'w': 2, 'y': 1} سنستفيد فعليًا من الدالة pprint.pprint()‎ عندما يحتوي القاموس على قوائم أو قواميس متشعبة داخله nested. إذا أردت الحصول على قيمة النص المجمّل بدلًا من طباعته على الشاشة مباشرةً، فاستدعِ الدالة pprint.pformat()‎. السطران الآتيان متكافئان تمامًا: pprint.pprint(someDictionaryValue) print(pprint.pformat(someDictionaryValue)) استخدام بنى المعطيات لنمذجة عناصر حقيقية كان بإمكاننا لعب الشطرنج مع شخص آخر عن بعد قليل ظهور الإنترنت، فكان يعد كل لاعب رقعة الشطرنج في منزله، ثم يبادلان الرسائل البريدية يصف كل منهما خطوته، ولكي يستطيعوا فعل ذلك كان لاعبو الشطرنج بحاجة إلى وصف حالة رقعة الشطرنج والحركات التي يجريها وصفًا لا لبس فيه. تمثل الفراغات في رقعة الشطرنج في التأشير الجبري Algebraic chess notation بإحداثيات تتألف من حرف ورقم كما في الشكل 5-1. الشكل 5-1: إحداثيات رقعة الشطرنج في التأشير الجبري تُعرّف قطع الشطرنج بالأحرف: K للملك king، و Q للملكة queen (يسميها البعض بالوزير)، و R للقلعة rook، و B للفيل bishop، و N للحصان knight. أما الجنود فلا رمز لهم. يكون وصف الحركات متألفًا من الحرف الذي يمثل القطعة، وإحداثيات الوجهة. وزوج من تلك الحركات يصف ما يحدث في دورٍ واحد (بفرض أن صاحب اللون الأبيض يبدأ أولًا)؛ فمثلًا التأشير 2‎. Nf3 Nc6 يعني أن الأبيض حرك الحصان إلى f3 والأسود حرك الحصان إلى c6 في الدور الثاني من اللعبة. هنالك المزيد من القواعد للتأشير الجبري للشطرنج، لكن الفكرة التي أحاول إيصالها هي أننا نستطيع وصف لعبة الشطرنج دون الحاجة إلى أن نكون أمام رقعة، ويمكن أن يكون خصمك في الطرف الثاني من الكوكب، وإذا كانت لديك ذاكرة ومخيلة جيدة فلا تحتاج إلى رقعة شطرنج حقيقية من الأساس: فيمكنك أن تقرأ الحركات من الرسائل البريدية وتحدِّث الرقعة الموجودة في مخيلتك! تمتلك الحواسيب ذواكر رائعة، فيمكن أن يخزن الحاسوب مليارات السلاسل النصية من الشكل ‎'2. Nf3 Nc6'‎، وبهذا يمكن أن تلعب الحواسيب الشطرنج دون لوحة حقيقية؛ فما تفعله الحواسيب هو نمذجة البيانات لتمثيل رقعة الشطرنج، ويمكنك كتابة شيفرة تفعل ذلك بنفسك. هنا تلعب القوائم والقواميس دورها، فمثلًا القاموس ‎{'1h': 'bking', '6c': 'wqueen', '2g': 'bbishop', '5h': 'bqueen', '3e': 'wking'}‎ يمثل الرقعة في الشكل 5-2: الشكل 5-2: رقعة شطرنج منمذجة وفق قيمة قاموس لكن لمثالنا القادمة سنستعمل لعبة أسهل وأبسط من الشطرنج وهي لعبة إكس-أو. لعبة إكس-أو لعبة إكس-أو (تسمى بالإنكليزية tic-tac-toe) تشبه رمز # كبير فيه 9 خانات يمكن أن تكون قيمها X أو O أو أن تكون فارغة. لتمثيل هذه الرقعة بقاموس، فيجب أن نسند لكل خانة زوجًا من المفتاح-القيمة كما في الشكل 5-3. الشكل 5-3: خانات رقعة إكس-أو مع المفاتيح الموافقة لها يمكنك استخدام القيم النصية لتمثيل ما هو موجود في كل خانة في الرقعة: 'x' أو 'o' أو ' ' (فراغ)، وبالتالي نحتاج إلى تسع سلاسل نصية، يمكنك استخدام قاموس من القيم لهذا الأمر، فالقيمة النصية المرتبطة مع المفتاح 'top-R' تمثل القيمة في الركن العلوي الأيمن، والسلسل النصية المرتبطة مع المفتاح 'low-L' تمثل الركن السفلي الأيسر، والسلسلة النصية المرتبطة مع المفتاح 'mid-m' تمثل المنتصف، وهلم جرًا للبقية. هذا القاموس هو بنية معطيات تمثل رقعة لعبة إكس-أو، ولنخزن هذا القاموس في متغير باسم theBoard، ولنحفظ الشيفرة الآتية في ملف باسم ticTacToe.py: theBoard = {'top-L': ' ', 'top-M': ' ', 'top-R': ' ', 'mid-L': ' ', 'mid-M': ' ', 'mid-R': ' ', 'low-L': ' ', 'low-M': ' ', 'low-R': ' '} بنية المعطيات المخزنة في المتغير theBoard تمثل رقعة إكس-أو الموضحة في الشكل 5-4. الشكل 5-4: لوحة إكس-أو فارغة ولما كانت قيمة كل مفتاح في القاموس theBoard هي فراغ واحد فيمثل ذاك القاموس رقعةً فارغةً تمامًا. وإذا بدأت اللاعب X واختار الخانة في المنتصف تمامًا فسيصبح القاموس الذي يمثل الرقعة على الشكل: theBoard = {'top-L': ' ', 'top-M': ' ', 'top-R': ' ', 'mid-L': ' ', 'mid-M': 'X', 'mid-R': ' ', 'low-L': ' ', 'low-M': ' ', 'low-R': ' '} أصبحت بنية المعطيات theBoard تمثل الرقعة الموضحة في الشكل 5-5. الشكل 5-5: الحركة الأولى وتكون رقعة ربح فيها اللاعب O بوضع الشكل O في الصف العلوي كما يلي: theBoard = {'top-L': 'O', 'top-M': 'O', 'top-R': 'O', 'mid-L': 'X', 'mid-M': 'X', 'mid-R': ' ', 'low-L': ' ', 'low-M': ' ', 'low-R': 'X'} وهي ممثلة في الشكل 5-6. الشكل 5-6: ربح اللاعب O وبالتأكيد لا يستطيع أن يرى اللاعب إلا ما يطبع على الشاشة أمامه، ولا يعرف محتويات المتغيرات، فلننشئ دالةً تطبع القاموس الذي يحتوي على الرقعة على الشاشة. أضف ما يلي إلى ملف ticTacToe.py: theBoard = {'top-L': ' ', 'top-M': ' ', 'top-R': ' ', 'mid-L': ' ', 'mid-M': ' ', 'mid-R': ' ', 'low-L': ' ', 'low-M': ' ', 'low-R': ' '} def printBoard(board): print(board['top-L'] + '|' + board['top-M'] + '|' + board['top-R']) print('-+-+-') print(board['mid-L'] + '|' + board['mid-M'] + '|' + board['mid-R']) print('-+-+-') print(board['low-L'] + '|' + board['low-M'] + '|' + board['low-R']) printBoard(theBoard) حينما تشغل هذا البرنامج فستطبع رقعة إكس-أو فارغة: | | -+-+- | | -+-+- | | يمكن أن تتوالى الدالة printBoard()‎ أي بنية معطيات تمثل رقعة إكس-أو تمررها إليها، جرب تغيير الشيفرة إلى ما يلي: theBoard = {'top-L': 'O', 'top-M': 'O', 'top-R': 'O', 'mid-L': 'X', 'mid-M': 'X', 'mid-R': ' ', 'low-L': ' ', 'low-M': ' ', 'low-R': 'X'} def printBoard(board): print(board['top-L'] + '|' + board['top-M'] + '|' + board['top-R']) print('-+-+-') print(board['mid-L'] + '|' + board['mid-M'] + '|' + board['mid-R']) print('-+-+-') print(board['low-L'] + '|' + board['low-M'] + '|' + board['low-R']) printBoard(theBoard) ستعرض الرقعة الآتية على الشاشة: O|O|O -+-+- X|X| -+-+- | |X ولأنك أنشأت بنية معطيات تمثل لوحة إكس-أو وكتبت الشيفرة في printBoard()‎ التي تفسر بنية المعطيات وتظهر الرقعة، فأنت كتبت برنامجًا «ينمذج» models رقعة إكس-أو. كان بإمكانك تنظيم البيانات في بنية المعطيات بطريقة مختلفة، فمثلًا يمكنك استخدام المفتاح 'TOP-LEFT' بدلًا من 'top-L'، لكن طالما كانت شيفرتك تعمل مع بنية المعطيات التي لديك، فأنت كتبت عملية النمذجة بشكل صحيح. فمثلًا تتوقع الدالة printBoard()‎ أن بنية المعطيات التي تمثل الرقعة هي قاموس فيه مفاتيح لجميع الخانات التسع، لكن إن كان في قاموسك مفتاحٌ ناقص وليكن 'mid-L' فلن يعمل برنامجك: O|O|O -+-+- Traceback (most recent call last): File "ticTacToe.py", line 10, in <module> printBoard(theBoard) File "ticTacToe.py", line 6, in printBoard print(board['mid-L'] + '|' + board['mid-M'] + '|' + board['mid-R']) KeyError: 'mid-L' لنضف الآن الشيفرة التي تسمح للاعبين بإدخال حركاتهم. لنعدل برنامجنا ليبدو كما يلي: theBoard = {'top-L': ' ', 'top-M': ' ', 'top-R': ' ', 'mid-L': ' ', 'mid-M': ' ', 'mid-R': ' ', 'low-L': ' ', 'low-M': ' ', 'low-R': ' '} def printBoard(board): print(board['top-L'] + '|' + board['top-M'] + '|' + board['top-R']) print('-+-+-') print(board['mid-L'] + '|' + board['mid-M'] + '|' + board['mid-R']) print('-+-+-') print(board['low-L'] + '|' + board['low-M'] + '|' + board['low-R']) turn = 'X' for i in range(9): ➊ printBoard(theBoard) print('Turn for ' + turn + '. Move on which space?') ➋ move = input() ➌ theBoard[move] = turn ➍ if turn == 'X': turn = 'O' else: turn = 'X' printBoard(theBoard) الشيفرة الجديدة تطبع اللوحة في بداية كل دور ➊ ثم تطلب المدخلات من اللاعب الحالي ➋ ثم تحدث الرقعة وفقًا لذلك ➌ ثم تبدل اللاعب الحالي ➍ قبل الانتقال إلى الدور القادم. | | -+-+- | | -+-+- | | Turn for X. Move on which space? mid-M | | -+-+- |X| -+-+- | | --snip-- O|O|X -+-+- X|X|O -+-+- O| |X Turn for X. Move on which space? low-M O|O|X -+-+- X|X|O -+-+- O|X|X صحيحٌ أن البرنامج ليس لعبة إكس-أو كاملة، فلن يتحقق إن ربح أحد اللاعبين مثلًا؛ لكنه كافٍ لمعرفة كيفية استخدام بنى المعطيات في برامج حقيقية. القواميس والقوائم المتشعبة نمذجة لوحة إكس-أو هو أمر سهل: تحتاج اللوحة إلى قاموس فيه 9 مفاتيح تمثل خاناتها. لكن إن أردت نموذج أمور أكثر تعقيدًا فستجد أنك تحتاج إلى القواميس والقوائم التي تحتوي على قواميس وقوائم أخرى داخلها. تناسب القوائم تخزين سلسلة مرتبة من القيم، بينما تفيد القواميس بتخزين قواميس التي ترتبط فيها المفاتيح مع القيم. هذا مثال يستعمل قاموسًا يحتوي على قواميس داخله فيها الأغراض التي أتى بها الضيوف إلى الرحلة. يمكن أن تقرأ الدالة totalBrought()‎ بنية المعطيات وتحسب العدد الكلي للعناصر المجلوبة من كل الضيوف: allGuests = {'Alice': {'apples': 5, 'pretzels': 12}, 'Bob': {'steak sandwiches': 3, 'apples': 2}, 'Carol': {'cups': 3, 'apple pies': 1}} def totalBrought(guests, item): numBrought = 0 ➊ for k, v in guests.items(): ➋ numBrought = numBrought + v.get(item, 0) return numBrought print('Number of things being brought:') print(' - Apples ' + str(totalBrought(allGuests, 'apples'))) print(' - Cups ' + str(totalBrought(allGuests, 'cups'))) print(' - Cakes ' + str(totalBrought(allGuests, 'cakes'))) print(' - Steak Sandwiches ' + str(totalBrought(allGuests, 'steak sandwiches'))) print(' - Apple Pies ' + str(totalBrought(allGuests, 'apple pies'))) داخل الدالة totalBrought()‎ هنالك حلقة for تدور على أزواج مفتاح-قيمة في المتغير guests ➊، وسنسند داخل الحلقة اسم كل ضيف إلى المتغير k، وسنسند القاموس الذي يحتوي على قائمة الأغراض التي سيجلبها معه إلى الرحلة إلى المتغير v. إذا كان أحد المعامل item موجودة في القاموس، فستضاف قيمته (كمية الأغراض المجلوبة) إلى المتغير numBrought ➋، لكن إذا لم يكن المفتاح موجودًا فيسعيد التابع get()‎ القيمة 0 لإضافتها إلى numBrought. سيكون ناتج تنفيذ البرنامج كما يلي: Number of things being brought: - Apples 7 - Cups 3 - Cakes 0 - Steak Sandwiches 3 - Apple Pies 1 قد يبدو لك أن حالة الاستخدام السابقة بسيطة ولا حاجة إلى نمذجتها، لكن فكر أن الدالة totalBrought()‎ يمكن أن تتعامل بسهولة مع قاموس يحتوي على آلاف الضيوف، وكل ضيف يجلب آلاف العناصر، وتنظيم هذه المعلومات في بنية معطيات واضحة ووجود الدالة totalBrought()‎ سيوفر عليك وقتًا كثيرًا. يمكنك أن تنمذج العناصر في بنى المعطيات بالطريقة التي تراها مناسبة، لطالما كانت بقية الشيفرة في برنامج قادرةً على التعامل مع بنية المعطيات المنشأة. حينما تبدأ البرمجة فلا تقلق أن تنمذج البيانات بالطريقة «الصحيحة»، فكلما ازدادت خبرتك أصبحت تخطر ببالك طرائق أكثر فاعلية وكفاءة للنمذجة، لكن أهم ما في الأمر أن تعمل بنية المعطيات مع احتياجات برنامجك. الخلاصة يمكن أن تحتوي القوائم والقواميس على عدّة قيم، بما فيها قوائم وقواميس أخرى. القواميس مفيدة لأنك تستطيع ربط مفتاح معين مع قيمة، على عكس القوائم التي هي سلسلة من القيم المرتبة. يمكن الوصول إلى القيم داخل القاموس عبر استخدام الأقواس المربعة كما في القوائم، لكن بدلًا من استخدام فهرس رقمي فيمكن أن تكون المفاتيح في القواميس من مختلف القيم سواءً كانت أعدادًا صحيحةً أو أعدادًا عشريةً أو سلاسل نصية أو صفوف tuples. يمكنك تمثيل الكائنات الحقيقية بتنظيم قيم البرنامج في بنى المعطيات، ورأينا مثال ذلك عمليًا على لعبة إكس-أو. مشاريع تدريبية لكي تتدرب، اكتب برامج لتنفيذ المهام الآتية. مدقق لقواميس الشطرنج استعملنا في هذا المقال قيمةً مثل ‎{'1h': 'bking', '6c': 'wqueen', '2g': 'bbishop', '5h': 'bqueen', '3e': 'wking'}‎ لتمثيل رقعة الشطرنج. اكتب دالةً باسم isValidChessBoard()‎ التي تقبل وسيطًا هو قاموس وتعيد القيمة True أو False اعتمادًا إن كان القاموس صالحًا لتمثيل رقعة الشطرنج. تحتوي الرقعة السليمة على ملك أسود واحد وملك أبيض واحد. ويمكن لأيٍ من اللاعبين امتلاك 16 قطعة كحد أقصى، و 8 جنود كحد أقصى، ويجب أن تكون جميع القطع في المجال بين 1a و 8h، أي لا يمكن أن تكون القطعة في المكان 9z. يجب أن تبدأ أسماء القطع بحرف w أو b لتمثيل اللونين الأبيض أو الأسود، متبوعًا بإحدى الكلمات pawn أو knight أو bishop أو rook أو queen أو king. قائمة الأدوات في لعبة نحن نعمل على لعبة فيها قائمة أدوات يمكن أن يمتلكها اللاعب، والتي سننمذجها باستخدام بنية معطيات تتألف من قاموس تكون فيه مفاتيحه هي سلاسل نصية تصف القيمة الموجودة في قائمة الأدوات، وقيمتها هي عدد نصي يمثل عدد الأدوات التي يمتلكها اللاعب مثلًا القاموس ‎{'rope': 1, 'torch': 6, 'gold coin': 42, 'dagger': 1, 'arrow': 12}‎ تعني أن اللاعب يملك حبلًا واحدًا، و 6 شعلات، و 42 قطعة ذهبية …إلخ. اكتب دالةً باسم displayInventory()‎ التي تأخذ أي قاموس يمثل قائمة أدوات ويعرضه بالشكل: Inventory: 12 arrow 42 gold coin 1 rope 6 torch 1 dagger Total number of items: 62 تلميح: يمكنك استخدام حلقة for للمرور على جميع المفاتيح في القاموس: # inventory.py stuff = {'rope': 1, 'torch': 6, 'gold coin': 42, 'dagger': 1, 'arrow': 12} def displayInventory(inventory): print("Inventory:") item_total = 0 for k, v in inventory.items(): # أكمل الشيفرة هنا print("Total number of items: " + str(item_total)) displayInventory(stuff) دالة تحويل قائمة إلى قاموس في لعبة لنفترض أن لاعبنا في اللعبة السابقة قد حصل على غنيمة ممثلة في القائمة الآتية: playerLoot = ['gold coin', 'dagger', 'gold coin', 'gold coin', 'ruby'] اكتب دالةً باسم addToInventory(inventory, addedItems)‎ حيث أن المعامل inventory هو قاموس يمثل قائمة أدوات اللاعب (كما في المثال السابق) والمعامل addedItems يشبه المتغير playerLoot. يجب أن تعيد الدالة addToInventory()‎ قاموسًا يمثل قائمة الأدوات المحدثة. لاحظ أن القائمة الموجودة في addedItems قد تحتوي على عدة نسخ من نفس الأداة. يفترض أن تكون شيفرتك شبيهة بما يلي: def addToInventory(inventory, addedItems): # اكتب الدالة هنا inv = {'gold coin': 42, 'rope': 1} dragonLoot = ['gold coin', 'dagger', 'gold coin', 'gold coin', 'ruby'] inv = addToInventory(inv, dragonLoot) displayInventory(inv) يجب أن يظهر البرنامج السابق (مع الدالة displayInventory()‎ من المثال السابق) الناتج الآتي: Inventory: 45 gold coin 1 rope 1 ruby 1 dagger Total number of items: 48 ترجمة -بتصرف- للفصل Dictionaries And Structuring DATA من كتاب Automate the Boring Stuff with Python. اقرأ أيضًا المقال السابق: القوائم Lists في لغة بايثون أنواع البيانات والعمليات الأساسية في لغة بايثون تعلم لغة بايثون النسخة العربية الكاملة لكتاب البرمجة بلغة بايثون
  6. في البداية بدون دراية برمجية لن تتمكن من التعديل والتطوير، يجب الإلمام بلغة PHP لفعل ذلك بجانب لغات الويب HTML, CSS, JS. أولاً، عليك تثبيت Smarty: composer require smarty/smarty ثم قم بتهيئة Smarty في ملف PHP الرئيسي، ولنفترض أن ملفك الرئيسي هو index.php. <?php require_once 'vendor/autoload.php'; $smarty = new Smarty(); $smarty->setTemplateDir(__DIR__ . '/template'); $smarty->setCompileDir(__DIR__ . '/template_c'); $smarty->setCacheDir(__DIR__ . '/cache'); $smarty->setConfigDir(__DIR__ . '/configs'); ثم لنقم بكتابة كود PHP لقراءة القوالب المتاحة في مجلد القوالب. <?php // تابع للخطوة 2 $templateDir = __DIR__ . '/template'; $templateFolders = array_filter(glob($templateDir . '/*'), 'is_dir'); $templates = []; foreach ($templateFolders as $folder) { $templateName = basename($folder); $templates[] = $templateName; } $smarty->assign('templates', $templates); $smarty->display('admin.tpl'); بعد ذلك إنشاء ملف قالب Smarty يسمى admin.tpl لعرض القوالب في جدول. <!-- admin.tpl --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Admin Panel - Templates</title> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"> </head> <body> <div class="container"> <h1>Available Templates</h1> <table class="table table-bordered"> <thead> <tr> <th>Template Name</th> <th>Action</th> </tr> </thead> <tbody> {foreach from=$templates item=template} <tr> <td>{$template}</td> <td> <form method="post" action="install_template.php"> <input type="hidden" name="template_name" value="{$template}"> <button type="submit" class="btn btn-primary">Install Template</button> </form> </td> </tr> {/foreach} </tbody> </table> </div> </body> </html> ثم إنشاء ملف PHP يسمى install_template.php لتنفيذ عملية تركيب القالب. <?php if ($_SERVER['REQUEST_METHOD'] === 'POST') { $templateName = $_POST['template_name']; // نفذ عملية تركيب القالب هنا // تستطيع نسخ الملفات إلى مجلد آخر أو تحديث إعدادات قاعدة البيانات echo "Template '{$templateName}' has been installed!"; } else { echo "Invalid request."; } بذلك ستتمكن من عرض القوالب المتاحة في مجلد template على شكل جدول في صفحة لوحة التحكم، مع زر لتركيب كل قالب، وعند النقر على زر "تركيب القالب"، سيتم إرسال طلب POST إلى install_template.php لتنفيذ عملية التركيب، تستطيع تعديل عملية التركيب حسب متطلباتك.
  7. في البداية ستحتاج بالطبع إلى إنشاء حساب مطور على Google Cloud Vision API أو Amazon Rekognition. ثم ضبط إعدادات متغيرات البيئة في المشروع في ملف .env وأهما مفاتيح الـ API، عليك إضافة إعدادات Google Cloud Vision API أو Amazon Rekognition فقط، وإليك كلاهما: GOOGLE_CLOUD_VISION_API_KEY=google_api_key AWS_ACCESS_KEY_ID=aws_access_key AWS_SECRET_ACCESS_KEY=aws_secret_key AWS_REGION=aws_region ثم أنشئ نموذج وقاعدة بيانات لتخزين بيانات الصور: php artisan make:model Image -m في ملف التهجير: Schema::create('images', function (Blueprint $table) { $table->id(); $table->string('image_path'); $table->json('features'); // لتخزين بيانات الميزات (features) $table->timestamps(); }); أيضًا اعتمد على مكتبة مثل intervention/image لمعالجة الصور وضغط حجمها قبل إرسالها. ثم استخدام مكتبة google/cloud-vision أو aws/aws-sdk-php للتواصل مع المنصة. وإليك مثال لكلاهما اختر ما تريد: // ImageController.php use Illuminate\Http\Request; use Intervention\Image\Facades\Image; use Google\Cloud\Vision\V1\ImageAnnotatorClient; // or Aws\Rekognition\RekognitionClient; public function uploadImage(Request $request) { $image = $request->file('image'); // Process and resize the image $image->resize(300, 300); // Analyze the image using Google Cloud Vision API or Amazon Rekognition $visionClient = new ImageAnnotatorClient(); // or $rekognitionClient = new RekognitionClient(); $response = $visionClient->annotateImage(file_get_contents($image->getPathname()), ['LABEL_DETECTION']); // or $rekognitionClient->detectLabels(['Image' => ['Bytes' => file_get_contents($image->getPathname())]]); $features = $response->getLabelAnnotations(); // or $response->getLabels(); // Store the features in the database $imageFeatures = new ImageFeatures(); $imageFeatures->features = json_encode($features); $imageFeatures->save(); return response()->json(['message' => 'Image uploaded and analyzed successfully']); } بعد ذلك اعتمد على مكتبة doctrine/dbal للتفاعل مع قاعدة البيانات، حيث تتوفر cosine similarity أو Euclidean distance لقياس التشابه بين ميزات الصور. use App\ImageFeatures; use Illuminate\Http\Request; public function searchByImage(Request $request) { $searchImage = $request->file('image'); $searchFeatures = $this->analyzeImage($searchImage); // Query the database to find similar images $similarImages = ImageFeatures::all()->filter(function ($imageFeature) use ($searchFeatures) { $storedFeatures = json_decode($imageFeature->features, true); return $this->cosineSimilarity($searchFeatures, $storedFeatures) > 0.5; }); return response()->json(['similar_images' => $similarImages]); } private function analyzeImage($image) { // هنا يجب أن تضع الكود الخاص بتحليل الصورة والحصول على الميزات // سأضع مثال بسيط لتحليل الصورة return [/* array of features */]; } private function cosineSimilarity($features1, $features2) { $dotProduct = 0; $magnitude1 = 0; $magnitude2 = 0; foreach ($features1 as $index => $feature1) { $dotProduct += $feature1 * $features2[$index]; $magnitude1 += $feature1 * $feature1; $magnitude2 += $features2[$index] * $features2[$index]; } $magnitude1 = sqrt($magnitude1); $magnitude2 = sqrt($magnitude2); if ($magnitude1 * $magnitude2 == 0) { return 0; } return $dotProduct / ($magnitude1 * $magnitude2); } لتحسين الأداء، باستطاعتك استخدام تقنيات مثل الفهرسة المكانية أو مكتبات متخصصة في البحث عن الصور مثل Elasticsearch.
  8. افتحي مشروع Unity الذي تريدين تصديره كمكتبة، وانتقلي إلى File > Build Settings، وفي نافذة Build Settings، اختاري المنصة المستهدفة (Android أو iOS). وفي حال تستهدفي Android، اختاري Android من القائمة. ثم اضغطي على Player Settings في الزاوية السفلية اليسرى من نافذة Build Settings، وضبط الإعدادات اللازمة مثل اسم الحزمة (Package Name) وغيرها. وفي نافذة Build Settings، تأكدي من اختيار المنصة الصحيحة ثم اضغطي على Export في الزاوية اليمنى السفلى. وعندما تظهر نافذة التصدير، عليك تحديد خيار Export as a Library (أو خيار مشابه يعبر عن تصدير كمكتبة)، ثم اختيار مكان حفظ المجلد الذي سيحتوي على مكتبة Unity والضغط على Export لبدء عملية التصدير. بعد اكتمال التصدير، ستجدي مجلد يحتوي على مكتبة Unity في المسار الذي اخترتيه، باستطاعتك الآن دمج تلك المكتبة في مشروع Flutter كما هو موضح في الخطوات التي شرحها سابقًا.
  9. يمكنك ربط Python بلغة JavaScript .و هناك عدة طرق لتحقيق ذلك، ولكن الطريقة الأكثر شيوعًا هي استخدام واجهات برمجة التطبيقات (APIs). يمكنك إنشاء واجهة برمجة تطبيقات (API) باستخدام إطار عمل مثل Flask أو Django في Python كما بالعليق السابق . حيث ستقوم هذه الواجهة بتعريف نقاط نهاية تتيح لك الوصول إلى الدوال التي كتبتها في Python. بعد ذلك، يمكنك استدعاء هذه النقاط النهائية من جانب العميل (Client-side) باستخدام JavaScript وإجراء طلبات HTTP مثل GET أو POST لتمرير البيانات والحصول على النتائج. على سبيل المثال، إذا كتبت دالة في Python لحساب المتوسط الحسابي لمجموعة من الأرقام، يمكنك إنشاء نقطة نهائية في واجهة برمجة التطبيقات تسمح لك بإرسال الأرقام كبيانات JSON وتستدعي دالة Python لحساب المتوسط وإرجاع النتيجة. هناك أيضًا طرق أخرى مثل استخدام WebAssembly أو إطارات عمل مثل Transcrypt أو Brython التي تتيح لك كتابة كود Python يمكن تشغيله مباشرة في المتصفح. ومع ذلك، فإن استخدام واجهات برمجة التطبيقات هو الأكثر شيوعًا وفعالية.
  10. ملحوظة: يرجى كتابة عنوان واضح حتى تعم الفائدة على الجميع. بخصوص استخدام لغة البايثون من خلال الجافاسكريبت، فيمكن ذلك عن طريق إنشاء واجهات برمجية التطبيقات APIs ثم استدعاؤها من خلال الجافاسكريبت، وفي الحقيقة هذا يتم مع كل لغات البرمجة وليس مع البايثون فقط. ووجهة برمجة التطيبقات API هي عبارة عن وظائف يتم كتابتها بلغة برمجة معينة على الخادم، ثم تهيئتها لتكون متاحة لاستدعائها بواسطة أي لغة أخرى. بالنسبة لبايثون، فإن أطر عمل مثل الفلاسك والجانجو Flask & Django يوفران بيئة تطوير جاهزة تُمكن المبرمج من البدء في كتابة الواجهات البرمجية بكل سهولة ويسر.
  11. هل استطيع ربط بايثون بلغة الجافا سكريبت بحيث استخدم ال Functions التي اكتبها بالبايثون في الجافا سكريبت واضيفهم الى موقعي؟
  12. وعليكم السلام ورحمة الله وبركاته . مرحبا محمد . نعتذر عن المشكلة التي تواجاهك حاليا . من المفترض حين الإشتراك فإن قسم دوراتى يظهر فوريا بعد الإشتراك ويمكنك الدخول ومشاهدة الدورة دون أى مشاكل . ولكن يبدوا أنه حدثت مشكلة لديك تمنع ظهور القسم لديك.لذلك الحل هو التواصل مع الدعم كما فعلت وسيتم الرد عليك في أسرع وقت إن شاء الله . لا تقلق من التأخير في الرد فسوف يتم حل المشكلة لديك إن شاء الله . إذا لم يتم الرد عليك بحلول نهاية اليوم الحالي من فضلك أخبرنى . شكرا لتفهمك وبالتوفيق إن شاء الله
  13. السلام عليكم أود الاستفسار عن الوقت اللازم حتى أتمكن من الوصول لمحتوى الدورة التي اشتركت بها؟؟ حيث أني قمت بالاشتراك بدورة الذكاء الاصطناعي منذ صباح الأمس و تمت عملية الدفع بنجاح وإلى الآن لم أتمكن من الوصول إلى محتوى الدورة ولم يظهر لي خيار (دوراتي) على شريط المهام الرئيسي على صفحة الحساب الخاص بي!!! مع العلم اني قمت بالتواصل مع المساعدة الخاصة بموقع الأكاديمية عبر المحادثة المباشرة و عبر البريد الالكتروني ولم يتم الرد حتى الآن!!! هل هناك أي طريقة أخرى للتواصل لحل المشكلة؟ مع الشكر
  14. وعليكم السلام، إذا لم تكن باحثا في الذكاء الاصطناعي، فهذا ليس اختصاصك، لأن تطوير المعادلات شيء معقد، ومعظم ما سيأتي في بالك من تطويرات ستجد أن هناك من سبقك وقام بها على أغلب الظن. لذلك، فالنماذج الموجودة في مكتبات تعلم الآلة هي أكثر النماذج التي تمت مراجعتها ويمكنك أن تضمن أنها تعمل بشكل صحيح ويمكنك الاعتماد عليها. بينما لو طورت نموذجا خاصا بك فهناك احتمالية كبيرة في أن تقع في خطأ ما ويتسبب في إعطائك نتائج خاطئة يصعب إصلاحها. عندما تفهم كيف تعمل نماذج الآلة على الأمثلة التي تدرسها، يمكنك بعدها تطبيقها على مواضيع مختلفة لم يتم تدريسها، وقد يكون بعضها جديدا كليا لم يقم به أحد من قبلك. وهذا ممكن جدا، لأن المواضيع تختلف باختلاف الأماكن والأزمنة. فقد يكون هناك مشكل محدد في بلادك لا يوجد في البلدان الأخرى، وهنا يمكنك البحث عن كيفية الحصول على المعلومات اللازمة من بلادك، ثم معالجتها واستخدامها في نموذج تعلم آلة لحل ذلك المشكل. وربما تتمكن بعدها من نشر هذا النموذج كخدمة مدفوعة، أو شيء مشابه. هذا يعتبر إبداعا في المجال.
  15. وعليكم السلام لنبدأ أولا بالتكلم عن فهم المعادلات المقصود هنا هو أنك تفهم كيف تعمل المعادلات المستخدمة في النماذج، مثل معادلات الانحدار الخطي أو الشبكات العصبية. هذا الفهم يساعدك على استخدام هذه النماذج بفعالية وتحديد متى يكون النموذج مناسبا للمشكلة التي تعمل عليها. فهمك للمعادلات يساعدك أيضًا في تحسين النماذج واكتشاف الأخطاء. ثم تطوير المعادلات هذا يتطلب مستوى أعلى من الفهم والابتكار. يعني أنك تقوم بابتكار وتطوير معادلات جديدة أو تحسين المعادلات الحالية بشكل مبتكر. هذا يتطلب معرفة عميقة بالرياضيات والخوارزميات. فالخطوة الأولى هي فهم كيف تعمل الأشياء الموجودة، ثم بعد ذلك يمكنك محاولة تطوير أشياء جديدة.
  16. السلام عليكم هل المقصود ان اطور المعادالات الموجود في النماذج ؟ والا المقصود ان افهم المعادالات وسعاتها اقدر ان ابداع في المجال ؟ انا كا مطور في المجال ده اقصد كده
  17. وعليكم السلام، في مجال تعلم الآلة سوف تجد أن معظم الوقت يذهب في الجزء النظري والتفكير في الحل، وليس في كتابة الحل. عندما تكون عندك بيانات، فسوف تحتاج إلى معرفة ما هي المدخلات وما هي المخرجات، وكيف تقوم بتحويل المدخلات لتناسب النموذج، وما هو النموذج المختار وما هي إعداداه وكيف يعمل، وكيف تقوم بتقسيم البينات وما الذي تستخدمه للتدريب وما الذي تبقيه للاختبار، وكيف تحسب جودة النموذج. كل هذا عندما يتم تحويله إلى كود بالشكل الصحيح وعند استخدام مكتبة جيدة مثل Scikit-learn سوف ترى أنك قد تستطيع كتابته أحيانا في 10 سطور! مما يجعل قارئ تلك السطور يظن أن البرنامج بسيط. لكن الوصول إلى تلك 10 سطور يتطلب فهما عميقا يحتاج إلى ساعات أو أيام من العمل.
  18. البارحة
  19. أجل كما قلت لي أولا شكرا على كلامك ففي بعض المشاكل أجلس بالساعات وأسأل هنا وهناك وأبحث وحتى أستخدم الذكاء الاصطناعي ولكن لا يساعدني دائما بل يزيد المشاكل فعندما أستسلم أسأل أحد المدربين وأحيانا أخرى أتكاسل قليلا عن البحث وأسأل المدربين شكرا على كلامك الطيب وأيضا لا أكذب ولكن أصبحت أكثر للأحطاء التي تأتي إلي وأفهم أين المشكلة فهذا شيئ جيد ويساعدني كثيرا وفي أحد المشاكل التي واجهتني من البحث استطعت حل مشكلة في جهازي مما أدت إلى مساعدتي في لغات البرمجة وهي مشكلة الpath موضوع يطال شرحه ولكن عرفت ما هو الحل أيها المدربون شكرا لكم جدا على مساعدتكم لي ومن الدربين الذين يعرفونني مشاكل القوية والثقيلة والصعبة جدا فأشكركم وبشدة على جهدكم لمساعدتي 😅
  20. بالطبع لا، قد أديت ما عليك، هناك أخطاء بحاجة إلى خبرة مكتسبة تتأتى من التعلم من الأخطاء بمرور الوقت وتطور المستوى من خلال تنفيذ المشاريع والممارسة العملية والتعرض لأفكار وأخطاء مختلفة، لذا في البداية أنت بحاجة إلى من يرشدك لتوفير الوقت عليك وتوجيهك للطريق الصحيح. ليس المطلوب منك حل جميع المشاكل، بل المطلوب هو بذل جهدك ومحاولة اكتشاف ما المشكلة وفهم السبب، وإن تعذر ذلك عليك بالسؤال فمن لا يسأل لا يصل وتلك خصلة ليست جيدة فالسؤال بعد أن إتمام جانبك من المجهود هو الصحيح. المقال التالي سيفيدك:
  21. وعليكم السلام ورحمة الله وبركاته . نعم صحيح إن الأخطاء هى ما ستعلمك أكثر من أى شئ . حيث في بداية مسيرتى وبداية التعلم حينما كنت أقع في خطأ ما فإنى أقوم بالبحث عنه وحينما أجد الإجابة فإنها تظل في ذاكرتى ومن الصعب أن أخطا نفس الخطأ مرة أخرى أو إذا تكرر معى فسأستطيع حله بسهوله . ولكن توجد بعض الأخطاءالتي من المستحيل أن تستطيع حلها بنفسك فبعد المحاولات الكثيرة التى من الممكن أن تستمر لأيام ستجد أنك لم تستطع حلها فهنا لا بئس في سؤال أى شخص أو حتى نحن المدربون هنا سعداء بحل أى مشكلة تواجه أى طالب. لذلك فإن هذا ليس تقصيرا منك بل هذا هو المسار الصحيح للتعلم حاول حل الأخطاء نفسك أولا وإذا وجدت صعوبة فلا تتردد في السؤال وحاول فهم لما حدثت المشكلة وفهم الحل الخاص بها . حيث إذا لم تقم بالمحاولة بنفسك فستستهل الأمور و إذا واجهتك أى مشكلة فلن تستطيع حلها حيث أول شئ ستقوم به هو سؤال أى شخص ولن تحاول حتى فهم الخطأ و الحل .
  22. السلام عليكم! سمعت كثيرا عن موضوع تعلم البرمجة والأخطاء وسمعت عن أنه إذا واجهتك مشكلة قم بالبحث بنفسك وما إلى ذلك فهل بأنني أسأل المدربين بارك الله فيهم هل هذا تقصير مني وفي نفس الوقت بعض المشاكل أبحث عنها بكقرة وعنما أستسلم أسأل المدرب
  23. تعلم الآلة يعد مجالًا واسعًا ومعقدًا، ولكنه بالطبع أكثر سهولة عند البدء بالمفاهيم الأساسية وتتقدم بشكل تدريجي، وبما أنك تدرس حاليًا الـ Regression الانحدار، فتلك نقطة جيدة للبدء. وتتوفر العديد من المكتبات والأدوات مثل Scikit-Learn، TensorFlow، و PyTorch التي تسهل عملية بناء نماذج تعلم الآلة، فتلك المكتبات توفر واجهات برمجية سهلة الاستخدام وأدوات جاهزة للانحدار، التصنيف، التجميع، وغيرها. المجال الذي اخترته ليس بالسهل فهو بحاجة إلى وقت أطول وصبر كذلك، لذا عليك البدء بالأساسيات ثم التدرج ولا تنتظر نتائج سريعة، لذا مفتاح النجاح هو الاستمرار والصبر. فلا تنظر للأمر بصورته الكلية فتحبط، بل حاول تخطي جزء جزء لتصل، فمع تقدمك في تعلم الآلة ومحاولتك لحل مشكلات أكثر تعقيدًا، فإن تعقيد الكود يزيد بشكل كبير، وسيتطلب منك ذلك القيام بالعديد من المهام الإضافية، مثل: معالجة البيانات Preprocessing وتشمل تنظيف البيانات، معالجة القيم الناقصة، تحويل البيانات النصية إلى رقمية، وغيرها، وذلك أمرًا مرهقًا ويستغرق وقتًا طويلًا. هندسة الميزات Feature Engineering من خلال إنشاء ميزات جديدة من البيانات الحالية بشكل إبداعي لزيادة دقة النموذج، وتلك العملية تتطلب تفكيرًا إبداعيًا وفهمًا عميقًا للبيانات.
  24. السلام عليكم انا هنا بتكلم فقط علي مجال تعلم الاله مش تعلم العميق الان انا بتدرس مجال تعلم الاله لسه في اول حاجه وهي الRegression فا هل مجال تعليم الاله سهل من حيث بناء الكود ؟
  25. هل إصدار Gradle المستخدم في مشروعك متوافق مع إصدار Android Gradle Plugin (AGP) المذكور في الخطأ (7.3.0)، عليك العثور على ملف gradle-wrapper.properties وتحققي من تعيين إصدار Gradle بشكل صحيح. distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.0-all.zip أيضًا هل مشروع Unity تم تصديره بشكل صحيح كـ Android Library، عليك القيام بذلك من خلال الذهاب إلى File > Build Settings وتحديد Android كمنصة الهدف، ثم التأكد من تمكين خيار Export Project إذا كان موجودًا. وفي مشروع Flutter، تأكدي من أن لديك إعدادات Gradle الصحيحة لاستيراد مشروع Unity، من خلال فتح ملف settings.gradle في مشروع Flutter وتأكد من إضافة مشروع Unity بشكل صحيح. include ':app' include ':unityLibrary' project(':unityLibrary').projectDir = new File('../UnityExport/unityLibrary') ثم افتحي ملف build.gradle لمشروع Flutter وإضافة تبعية مشروع Unity بشكل صحيح. dependencies { implementation project(':unityLibrary') } ثم تفقد هل إعدادات Kotlin متوافقة مع إصدارات الأدوات الأخرى، توجهي إلى ملف build.gradle وتفقدي هل تم إضافة الإعدادات الصحيحة. buildscript { ext.kotlin_version = '1.5.31' repositories { google() mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:7.3.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } بعد تطبيق جميع التعديلات، قومي بإعادة مزامنة المشروع من خلال Android Studio أو استخدام أمر flutter pub get لإعادة جلب الحزم والتبعيات.
  1. عرض المزيد
×
×
  • أضف...