java similarities أسباب فنية وراء التنسيق عند زيادة بمقدار 1 في حلقة "for"؟




if statement in c (24)

لتلخيص إيجابيات وسلبيات كلا الخيارين

الايجابيات!

  • عندما يتم استبدال int مع بعض المكرر أو نوع تم تمريره بواسطة حجة القالب ، فهناك فرصة أفضل للعمل ، وسوف يفعل ما هو متوقع ، وسوف يكون أكثر كفاءة.
  • سوف "حلقة إلى الأبد" إذا حدث شيء غير متوقع إلى متغير ط السماح للكشف عن الأخطاء

الايجابيات من

  • كما يقول الآخرون بنفس الكفاءة مثل الآخر مع أنواع بسيطة
  • فإنه لن يتم تشغيل "للأبد" إذا تم زيادة i في الحلقة أو يتم استبدال 5 مع بعض التعبير الذي يحصل على تعديل أثناء تشغيل الحلقة
  • سوف تعمل مع نوع تعويم
  • أكثر قابلية للقراءة - مسألة التعود على

استنتاجاتي:

  1. ربما ينبغي استخدام ! = version في معظم الحالات ، عندما يكون i منفصلاً ، وكذلك الجانب الآخر من المقارنة لا يقصد به التلاعب داخل الحلقة.

  2. في حين أن وجود < سيكون علامة واضحة على أن i هو من نوع بسيط (أو يتم تقييمه إلى نوع بسيط) وأن الشرط ليس بسيطًا: يتم أيضًا تعديل الحالة i أو الشرط داخل المعالجة المتوازية و / أو التوازي.

في جميع أنحاء الويب ، تحتوي نماذج التعليمات البرمجية for حلقات تبدو كالتالي:

for(int i = 0; i < 5; i++)

بينما كنت أستخدم التنسيق التالي:

for(int i = 0; i != 5; ++i)

أفعل هذا لأنني أعتقد أنه أكثر فعالية ، لكن هل هذا مهم حقا في معظم الحالات؟


Answer #1

إذا لسبب ما i يقفز إلى 50 في الحلقة ، سوف تكرار حلقة الإصدار الخاص بك إلى الأبد. و i < 5 هو فحص سلامة العقل.


Answer #2

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


Answer #3

للتسجيل ، فإن مكافئ كوبول للحلقة "for" هو:

    PERFORM VARYING VAR1 
            FROM +1 BY +1
            UNTIL VAR1 > +100
  *      SOME VERBOSE COBOL STATEMENTS HERE
    END-PERFORM.

أو

PERFORM ANOTHER-PARAGRAPH
        VARYING VAR2 BY +1 
        UNTIL TERMINATING-CONDITION
        WITH TEST AFTER.

هناك العديد من الاختلافات في هذا. أهم مسحة للشعوب التي لم تتعرض عقولها للتلف بسبب التعرض الطويل لـ COBOL هو ، افتراضيا ، UNTIL تعني في الواقع WHILE أي أن الاختبار يتم إجراؤه في أعلى الحلقة ، قبل أن يتزايد متغير الحلقة وقبل جسم حلقة تتم معالجتها. أنت في حاجة إلى " WITH TEST AFTER " لجعلها مناسبة UNTIL .


Answer #4

فيما يتعلق بالقراءة. كونه مبرمج C # الذي يحب روبي ، كتبت مؤخرا طريقة تمديد ل int التي تسمح للبناء التالي (كما هو الحال في روبي):

4.Times(x => MyAction(x));

Answer #5

When I first started programming in C, I used the ++i form in for loops simply because the C compiler I was using at the time did not do much optimization and would generate slightly more efficient code in that case.

Now I use the ++i form because it reads as "increment i", whereas i++ reads as "i is incremented" and any English teacher will tell you to avoid the passive voice.

The bottom line is do whatever seems more readable to you.


Answer #6

القيم المعدنية الرقمية رشها في التعليمات البرمجية الخاصة بك؟ للعار ...

العودة إلى مسارها ، قال دونالد كنوث ذات مرة

يجب أن ننسى الكفاءات الصغيرة ، نقول حوالي 97 ٪ من الوقت: التحسين المبكر هو أصل كل الشرور.

لذلك ، فإنه يتلخص حقا إلى ما هو أسهل للتحليل

لذلك ... مع الأخذ في الاعتبار كل من المذكور أعلاه ، أي مما يلي أسهل لمبرمج تحليل؟

for (int i = 0; i < myArray.Length; ++i)

for (int i = 0; i != myArray.Length; ++i)

تحرير: أنا على دراية بأن المصفوفات في C # تقوم بتطبيق واجهة System.Collections.ilist ، ولكن هذا ليس صحيحًا بالضرورة في اللغات الأخرى.


Answer #7

تحولت إلى استخدام != قبل أكثر من 20 سنة بعد قراءة كتاب Dijkstra بعنوان "A Program of Programming" . في كتابه Dijkstra لاحظ أن ظروف استمرار أضعف يؤدي إلى أقوى بعد الظروف في بنيات حلقة.

على سبيل المثال ، إذا قمنا بتعديل البنية الخاصة بك لفضح i بعد الحلقة ، فإن حالة ما بعد الشروع في حلقة القبضة ستكون i >= 5 ، في حين أن الحالة اللاحقة للحلقة الثانية تكون أقوى بكثير i == 5 . هذا أفضل للتفكير حول البرنامج من الناحية الشكلية من الثوابت الحلقيّة ، الشروط اللاحقة ، وأضعف الشروط المسبقة.


Answer #8

I remember one code segment where the i was getting incremented by 2 instead of 1 due to some mistake and it was causing it to go in infinite loop. So it is better to have this loop as shown in the first option. This is more readable also. Because i != 5 and i < 5 conveys two different meaning to the reader. Also if you are increasing the loop variable then i<5 is suppose to end some point of time while i != 5 may never end because of some mistake.


Answer #9

FORTRAN's DO loop and BASIC's FOR loop implemented < (actually <= ) for positive increments. Not sure what COBOL did, but I suspect it was similar. So this approach was "natural" to the designers and users of "new" languages like C.

Additionally, < is more likely than != to terminate in erroneous situations, and is equally valid for integer and floating point values.

The first point above is the probable reason the style got started, the second is the main reason it continues.


Answer #10

حسناً ... هذا جيد طالما أنك لا تعدلني داخل الحلقة الخاصة بك. تعتمد صيغة "BEST" الحقيقية على هذا الأمر تمامًا على النتيجة المرجوة.


Answer #11

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

لـ (int i = 0؛ i <5؛ i ++):

    movl $0, -12(%ebp)
    jmp L2
L3:
    leal    -12(%ebp), %eax
    incl    (%eax)
L2:
    cmpl    $4, -12(%ebp)
    jle L3

لـ (int i = 0؛ i! = 5؛ ++ i)

    movl    $0, -12(%ebp)
    jmp L7
L8:
    leal    -12(%ebp), %eax
    incl    (%eax)
L7:
    cmpl    $5, -12(%ebp)
    jne L8

أعتقد أن "jle" و "jne" يجب أن تترجم إلى تعليمات سريعة بنفس القدر في معظم المعماريات. لذلك يجب عليك ألا تميز بين الأداءين. بشكل عام ، أوافق على أن الأول هو أكثر أمانًا قليلاً وأعتقد أيضًا أنه أكثر شيوعًا.

EDIT (بعد عامين): بما أن هذا الموضوع قد حظي مؤخراً باهتمام كبير ، أود أن أضيف أنه سيكون من الصعب الإجابة على هذا السؤال بشكل عام. ما هي إصدارات الأكواد الأكثر كفاءة غير محددة بشكل محدد من قبل C-Standard [PDF] (والشيء نفسه ينطبق على C ++ وربما أيضًا على C #).

القسم 5.1.2.3 تنفيذ البرنامج

تصف الأوصاف الدلالية في هذه المواصفة القياسية الدولية سلوك آلة مجردة حيث تكون قضايا التحسين غير ذات صلة.

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

أما بالنسبة للطعم ، فأكتب for(int i = 0; i < 5; ++i) .


Answer #12

It is not good approach to use as != 5. But

for (int i =0; i<index; ++i)

is more efficient than

for(int i=0; i<index; i++)

Because i++ first perform copy operation. For detailed information you can look operator overloading in C++.


Answer #13

I see plenty of answers using the specific code that was posted, and integer. However the question was specific to 'for loops', not the specific one mentioned in the original post.

I prefer to use the prefix increment/decrement operator because it is pretty much guaranteed to be as fast as the postfix operator, but has the possibility to be faster when used with non-primitive types. For types like integers it will never matter with any modern compiler, but if you get in the habit of using the prefix operator, then in the cases where it will provide a speed boost, you'll benefit from it.

I recently ran a static analysis tool on a large project (probably around 1-2 million lines of code), and it found around 80 cases where a postfix was being used in a case where a prefix would provide a speed benefit. In most of these cases the benefit was small because the size of the container or number of loops would usually be small, but in other cases it could potentially iterate over 500+ items.

Depending on the type of object being incremented/decremented, when a postfix occurs a copy can also occur. I would be curious to find out how many compilers will spot the case when a postfix is being used when its value isn't referenced, and thus the copy could not be used. Would it generate code in that case for a prefix instead? Even the static analysis tool mentioned that some of those 80 cases it had found might be optimized out anyway, but why take the chance and let the compiler decide? I don't find the prefix operator to be at all confusing when used alone, it only becomes a burden to read when it starts getting used, inline, as part of a logic statement:

int i = 5;
i = ++i * 3;

Having to think about operator precedence shouldn't be necessary with simple logic.

int i = 5;
i++;
i *= 3;

Sure the code above takes an extra line, but it reads more clearly. But with a for loop the variable being altered is its own statement, so you don't have to worry about whether it's prefix or postfix, just like in the code block above, the i++ is alone, so little thought is required as to what will happen with it, so this code block below is probably just as readable:

int i = 5;
++i;
i *= 3;

As I've said, it doesn't matter all that much, but using the prefix when the variable is not being used otherwise in the same statement is just a good habit in my opinion, because at some point you'll be using it on a non-primitive class and you might save yourself a copy operation.

Just my two cents.


Answer #14

Ultimately, the deciding factor as to what is more efficient is neither the language nor the compiler, but rather, the underlying hardware . If you're writing code for an embedded microcontroller like an 8051, counting up vs. counting down, greater or less than vs. not equals, and incrementing vs. decrementing, can make a difference to performance, within the very limited time scale of your loops.

While sufficient language and compiler support can (and often do) mitigate the absence of the instructions required to implement the specified code in an optimal but conceptually equivalent way, coding for the hardware itself guarantees performance, rather than merely hoping adequate optimizations exist at compile time.

And all this means, there is no one universal answer to your question, since there are so many different low-end microcontrollers out there.

Of much greater importance, however, than optimizing how your for loop iterates, loops, and breaks, is modifying what it does on each iteration . If causing the for loop one extra instruction saves two or more instructions within each iteration, do it ! You will get a net gain of one or more cycles! For truly optimal code, you have to weigh the consequences of fully optimizing how the for loop iterates over what happens on each iteration.

All that being said, a good rule of thumb is, if you would find it a challenge to memorize all the assembly instructions for your particular target hardware, the optimal assembly instructions for all variations of a “for” loop have probably been fully accounted for. You can always check if you REALLY care.


Answer #15

I think in the end it boils down to personal preference.
I like the idea of

for(int i = 0; i < 5; i++)

over

for(int i = 0; i != 5; ++i)

due to there being a chance of the value of i jumping past 5 for some reason. I know most times the chances on that happening are slim, but I think in the end its good practice.


Answer #16

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

لقد عملت في شركة تنتج برامج لأنظمة السلامة الحرجة ، وكان أحد القواعد هو أنه ينبغي أن تنتهي الحلقة بـ "<" بدلاً من! =. هناك عدة أسباب وجيهة لذلك:

  1. قد يقفز متغير التحكم الخاص بك إلى قيمة أعلى من خلال بعض مشكلة الأب أو بعض غزو الذاكرة ؛

  2. في الصيانة ، يمكن للمرء زيادة قيمة المكرر الخاص بك داخل الحلقة ، أو القيام بشيء مثل "i + = 2" ، وهذا من شأنه أن يجعل دورتك تدحرج للأبد.

  3. إذا تغير نوع التكرار الخاص بك من "int" إلى "float" (لسبب ما ، لا أعرف لماذا يقوم شخص ما بذلك ، ولكن على أي حال ...) فإن المقارنة الدقيقة لنقاط التعويم هي ممارسة سيئة.

(معيار MISRA C ++ Coding (للأنظمة الحساسة للسلامة) يخبرك أيضًا بأنك تفضل "<" بدلاً من "! =" في القاعدة 6-5-2. لا أعرف إذا كان بإمكاني نشر تعريف القاعدة هنا لأن MISRA هي وثيقة مدفوعة.)

حول ++ أنا أو i ++ ، أود أن preffer لاستخدام ++ أنا. لا يوجد اختلاف لذلك عند التعامل مع أنواع أساسية ، ولكن عندما تستخدم أداة إعادة تسمية STL ، يكون التفضيل المسبق أكثر كفاءة. لذلك أنا دائما استخدام preincrement لتعتاد عليها.


Answer #17

هناك بالفعل أربعة تباديل على ما تعطيه. إلى اثنين:

for(int i = 0; i < 5; i++)
for(int i = 0; i != 5; ++i)

يمكننا أن نضيف:

for(int i = 0; i < 5; ++i)
for(int i = 0; i != 5; i++)

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

قد يكون في بعض الحالات أكثر منطقية لعقل معين مع حالة معينة للتفكير في "أقل من" أو "غير متساوٍ" اعتمادًا على السبب الذي جعلنا نختار 0 و 5 ، ولكن حتى في هذه الحالة ، ما الذي يجعل المرء يبدو واضحًا لأحد المبرمج قد لا مع آخر.

بشكل أكثر تجريدًا ، هذه الأشكال:

for(someType i = start; i < end; i++)
for(someType i = start; i != end; ++i)
for(someType i = start; i < end; ++i)
for(someType i = start; i != end; i++)

من الاختلافات الواضحة هنا أنه في حالتين ، يجب أن يكون someType نوع" معنى لـ < وبالنسبة للباقي يجب أن يكون له معنى != الأنواع التي تم تعريفها != و < ليست شائعة تمامًا ، بما في ذلك عدد قليل من كائنات المكررة في C ++ (ومن المحتمل أن يكون ذلك في C # حيث يكون النهج نفسه مثل تكرارات STL ممكنًا ومفيدًا في بعض الأحيان ، ولكن ليس كإعلاني ، يدعمه مباشرةً والمكتبات الشائعة ولا تكون مفيدة في كثير من الأحيان لأن هناك تعابير منافسة مع المزيد من الدعم المباشر). تجدر الإشارة إلى أن منهج STL مصمم خصيصًا ليشمل مؤشرات داخل مجموعة أنواع المحاكاة الصحيحة. إذا كنت معتادًا على استخدام STL ، فستفكر في الاستمارات != حتى أكثر من ذلك بكثير عند تطبيقها على الأعداد الصحيحة. شخصياً ، كان قدر ضئيل جداً من التعرض له كافياً لجعله غريزيتي.

من ناحية أخرى ، في حين أن تحديد < و ليس != سيكون أكثر ندرة ، فإنه ينطبق على الحالات التي إما أننا نستبدل الزيادة بزيادة مختلفة في قيمة i ، أو حيث يمكن تعديلها داخل الحلقة.

لذلك ، هناك حالات محددة على كلا الجانبين حيث واحد أو آخر هو النهج الوحيد.

الآن لـ ++i مقابل i++ . مرة أخرى مع الأعداد الصحيحة وعندما يتم استدعاؤها مباشرة وليس من خلال وظيفة تقوم بإرجاع النتيجة (والفرص حتى ذلك الحين) تكون النتيجة العملية هي نفسها تمامًا.

في بعض اللغات ذات النمط C (تلك التي لا يوجد بها تحميل زائد للمشغل) ، تكون الأعداد الصحيحة والمؤشرات حول الحالات الوحيدة الموجودة. يمكننا فقط أن نخترع بشكل اصطناعي حالة يتم فيها استدعاء الزيادة من خلال وظيفة فقط لتغيير الطريقة التي تسير بها ، والفرص هي أن المترجم سيحولها إلى الشيء نفسه على أي حال.

تسمح لنا C ++ و C # بتجاوزها. عموما ، تعمل البادئة ++ كوظيفة تقوم بما يلي:

val = OneMoreThan(val);//whatever OneMoreThan means in the context.
//note that we assigned something back to val here.
return val;

ويعمل postfix ++ كوظيفة تعمل:

SomeType copy = Clone(val);
val = OneMoreThan(val);
return copy;

لا تطابق C ++ أو C # ما سبق تماما (أنا جعلت عمدا تطابق نظري pseudo-code) ، ولكن في كلتا الحالتين قد يكون هناك نسخة أو ربما اثنين من صنع. قد يكون هذا أو لا يكون مكلفًا. قد يكون أو لا يمكن تجنبه (في لغة C ++ ، يمكننا في كثير من الأحيان تجنبه تمامًا لنموذج البادئة عن طريق إرجاع this وفي postfix عن طريق إرجاع الفراغ). قد يكون أو لا يكون الأمثل إلى شيء بعيد ، ولكن يبقى أنه يمكن أن يكون أكثر كفاءة للقيام ++i i++ في بعض الحالات.

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

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


Answer #18

والثاني هو أقل قابلية للقراءة ، وأعتقد (إذا كان فقط لأن الممارسة "القياسية" يبدو أن السابق).


Answer #19

فيما يتعلق باستخدام ++ i بدلاً من i ++ ، لا يحدث فرق مع معظم compilers ، ومع ذلك ++ يمكنني أن أكون أكثر فعالية من i ++ عند استخدام كمكرر.


Answer #20

We can use one more trick for this.

for (i = 5; i > 0; i--)

I suppose most of the compilers optimize the loops like this. I am not sure. Someone please verify.


Answer #21

ذلك يعتمد على اللغة.

غالبًا ما تقترح النصوص C ++ التنسيق الثاني الذي سيعمل مع المتكررات التي يمكن مقارنتها (! =) مباشرة ولكن ليس مع شرط أكبر أو أقل من الشرط. كما أن الزيادة السابقة يمكن أن تكون أسرع من الزيادة اللاحقة ، حيث لا توجد حاجة إلى نسخة من المتغير للمقارنة - على الرغم من أنه يمكن للمتخصصين التعامل مع هذا.

للأعداد الصحيحة إما تشكيل الأعمال. المصطلح الشائع لـ C هو الأول في C ++ وهو الثاني.

بالنسبة إلى C # واستخدام Java ، كنت أريد foreach للحلقة فوق كل الأشياء.

في C ++ هناك أيضًا الدالة std :: for_each التي تتطلب استخدام أحد المختبرات التي قد تكون أكثر تعقيدًا من المثالين هنا و Boost FOR_EACH والتي يمكن أن تبدو مثل C # foreach ولكنها معقدة داخلها.


Answer #22

On many architectures, it is far easier to check whether something is zero that whether it is some other arbitrary integer, therefore if you truly want to optimize the heck out of something, whenever possible count down , not up (here's an example on ARM chips).

In general, it really depends on how you think about numbers and counting. I'm doing lots of DSP and mathematics, so counting from 0 to N-1 is more natural to me, you may be different in this respect.


Answer #23

لقد قررت أن أجد الإجابات الأكثر إفادة حيث أن هذا السؤال يزداد قليلاً.

DenverCoder8 مقعد DenverCoder8 بوضوح بعض الاعتراف بالإضافة إلى الإصدارات المجمعة من الحلقات بواسطة Lucas . وقد أظهر تيم جي الاختلافات بين زيادة ما قبل وبعد المشاركة بينما User377178 بعض إيجابيات وسلبيات <and! =. وقد كتب Tenacious Techhunter حول تحسينات حلقة بشكل عام وجدير بالذكر.

هناك لديك أعلى 5 إجابات.

  1. DenverCoder8
  2. Lucas
  3. تيم جي
  4. User377178
  5. تانسي تشونتر






for-loop