منذ وقت ليس ببعيد ، درست إخراج محلل ثابت IntelliJ IDEA لرمز جافا وصادفت حالة مثيرة للاهتمام. نظرًا لأن جزء الكود المقابل ليس مفتوح المصدر ، فقد قمت بتحديد هويته وإلغاء ربطه من التبعيات الخارجية. نحن نفترض أنه يشبه هذا:
private static List<Integer> process(Map<String, Integer> options, List<String> inputs) { List<Integer> res = new ArrayList<>(); int cur = -1; for (String str : inputs) { if (str.startsWith("-")) if (options.containsKey(str)) { if (cur == -1) cur = options.get(str); } else if (options.containsKey("+" + str)) { if (cur == -1) cur = res.isEmpty() ? -1 : res.remove(res.size() - 1); if (cur != -1) res.add(cur + str.length()); } } return res; }
الرمز يشبه الكود ، يتم تحويل شيء ما ، شيء ما يجري ، لكن المحلل الثابت لم يعجبه. هنا نرى تحذيرين:

في تعبير res.isEmpty()
يقول IDE أن الشرط صحيح دائمًا ، cur
أن المهمة لا معنى لها ، لأن نفس القيمة موجودة بالفعل في هذا المتغير. من السهل أن نرى أن مشكلة المهمة هي نتيجة مباشرة للمشكلة الأولى. إذا كان res.isEmpty()
صحيحًا دائمًا ، فسيتم تقليل السلسلة إلى
if (cur == -1) cur = -1;
هذا هو في الواقع لا لزوم لها. لكن لماذا التعبير دائمًا صحيح؟ بعد كل شيء ، res
عبارة عن قائمة ، يتم ملؤها في نفس الدورة. يعتمد عدد التكرارات للحلقة والفروع التي ندخلها على معلمات الإدخال التي يتعذر على IDE معرفتها. يمكننا إضافة عنصر res
عند التكرار السابق ، ثم لن تكون القائمة فارغة.
رأيت هذا الرمز لأول مرة وقضيت الكثير من الوقت للتعامل مع هذه الحالة. في البداية ، كنت مقتنعًا تقريبًا بأنني واجهت خللًا في المحلل ، وسيتعين علي إصلاحه. دعونا نرى ما اذا كان هذا هو الحال.
أولاً ، سنضع علامة على جميع الأسطر التي تتغير فيها حالة الأسلوب. هذا تغيير في متغير cur
أو تغيير إلى قائمة res
:
private static List<Integer> process(Map<String, Integer> options, List<String> inputs) { List<Integer> res = new ArrayList<>(); int cur = -1; for (String str : inputs) { if (str.startsWith("-")) if (options.containsKey(str)) { if (cur == -1) cur = options.get(str);
الأسطر 'A'
و 'B'
( 'B'
هي الفرع الأول من البيان الشرطي) ، قم بتغيير متغير cur
، و 'D'
يغير القائمة ، و 'C'
(الفرع الثاني من البيان الشرطي) يغير كل من القائمة cur
المتغيرة. من المهم بالنسبة لنا ما إذا كان cur
1 يكمن وما إذا كانت القائمة فارغة. وهذا هو ، تحتاج إلى مراقبة أربع حالات:

سلسلة 'A'
التغييرات cur
إذا كان هناك -1
قبل ذلك. ونحن لا نعرف ما إذا كانت النتيجة ستكون -1
أم لا. لذلك ، هناك خياران ممكنان:

تعمل السلسلة 'B'
أيضًا فقط إذا كانت قيمة cur
هي -1
. في الوقت نفسه ، كما لاحظنا بالفعل ، من حيث المبدأ ، فهي لا تفعل شيئًا. لكننا نلاحظ مع ذلك هذا الضلع لاستكمال الصورة:

تعمل السلسلة 'C'
، مثلها في السابق ، مع cur == -1
بشكل تعسفي (مثل 'A'
). ولكن في الوقت نفسه ، لا يزال بإمكانه تحويل القائمة غير الفارغة إلى قائمة فارغة ، أو ترك غير فارغة إذا كان هناك أكثر من عنصر واحد.

أخيرًا ، تزيد السلسلة 'D'
من حجم القائمة: يمكن أن تتحول فارغة إلى غير فارغة أو تزيد غير فارغة. لا يمكن أن يتحول غير فارغ إلى فارغ:

ماذا يعطينا هذا؟ لا شيء على الاطلاق. من غير المفهوم تمامًا سبب كون الشرط res.isEmpty()
صحيحًا دائمًا.
في الواقع ، لقد بدأنا خطأ. في هذه الحالة ، لا يكفي مراقبة حالة كل متغير على حدة. تلعب الدول المرتبطة هنا دورًا مهمًا. لحسن الحظ ، نظرًا لحقيقة أن 2+2 = 2*2
، لدينا أيضًا أربعة منهم فقط:

مع حد مزدوج ، قمت بتمييز الحالة الأولية التي لدينا عند إدخال الأسلوب. حسنا ، حاول مرة أخرى. 'A'
يتغير أو يحفظ cur
لأي res
، res
لا يتغير:

'B'
يعمل فقط مع cur == -1 && res.isEmpty()
ولا يفعل شيئًا. مضيفا:

'C'
يعمل فقط مع cur == -1 && !res.isEmpty()
. في الوقت نفسه ، يتغير كل من cur
و res
بشكل تعسفي: بعد 'C'
ينتهي بنا الأمر في أي ولاية:

أخيرًا ، يمكن أن تبدأ 'D'
في cur != -1 && res.isEmpty()
وجعل القائمة غير فارغة ، أو تبدأ في cur != -1 && !res.isEmpty()
هناك:

للوهلة الأولى ، يبدو أنها ازدادت سوءًا: أصبح الرسم البياني أكثر تعقيدًا ، وليس من الواضح كيفية استخدامه. ولكن في الواقع ، نحن قريبون من الحل. تُظهر الأسهم الآن التدفق الكامل لتنفيذ طريقة عملنا. نظرًا لأننا نعرف الحالة التي بدأنا منها ، دعنا نسير على طول الأسهم:

وهنا يتم الكشف عن شيء مثير للاهتمام للغاية. لا يمكننا الوصول إلى الزاوية اليسرى السفلى. وبما أننا لا نستطيع الدخول فيه ، فهذا يعني أنه لا يمكننا السير على أي من الأسهم 'C'
. وهذا يعني أن الخط 'C'
قابل للتحقيق حقًا ، ويمكن تنفيذ 'B'
. هذا ممكن فقط إذا كانت الحالة res.isEmpty()
صحيحة دائمًا! IntelliJ IDEA هو الصحيح تماما. آسف ، محلل ، عبثا اعتقدت أنك كنت عربات التي تجرها الدواب. أنت ذكي جدًا لدرجة أنه يصعب عليّ ، أي شخص عادي ، اللحاق بك.
لا يحتوي محللنا على أي تقنيات "ضجيج" للذكاء الاصطناعي ، ولكنه يستخدم أساليب تحليل تدفق التحكم وتحليل تدفق البيانات ، والتي لا يقل عمرها عن نصف قرن. ومع ذلك ، فإنه في بعض الأحيان يستخلص استنتاجات غير تافهة للغاية. ومع ذلك ، فإن هذا أمر مفهوم: فمن الأفضل لفترة طويلة إنشاء الرسوم البيانية والمشي عليها باستخدام الآلات بدلاً من الأشخاص. هناك مشكلة مهمة لم يتم حلها: لا يكفي مجرد إخبار شخص ما بأنه لديه خطأ في البرنامج. يجب على الدماغ السيليكوني أن يشرح للبيولوجي السبب وراء ذلك ، بحيث يفهم الدماغ البيولوجي. إذا كان لدى شخص ما أفكار رائعة حول كيفية القيام بذلك ، فسوف يسعدني أن أسمع منك. إذا كنت على استعداد لإدراك أفكارك بنفسك ، فلن يرفض فريقنا التعاون معك!
أمامك أحد اختبارات القبول: على سبيل المثال ، يجب إنشاء تفسير تلقائيًا. يمكن أن يكون نصًا أو رسمًا بيانيًا أو شجرة أو صورة ذات أختام - أي شيء ، إذا كان هناك شخص واحد فقط يمكن أن يفهم.
يبقى السؤال مفتوحًا ، ماذا يعني مؤلف الطريقة ، وكيف يجب أن يبدو الكود فعليًا. أبلغني المسؤولون عن النظام الفرعي أن هذا الجزء قد تم التخلي عنه إلى حد ما ، وأنهم أنفسهم لا يعرفون كيفية إصلاحه أو أنه من الأفضل إزالته تمامًا.