سؤال

شيء من هذا القبيل (نعم ، هذا لا يتعامل مع بعض حالات الحافة - هذا ليس هو النقطة):

int CountDigits(int num) {
    int count = 1;
    while (num >= 10) {
        count++;
        num /= 10;
    }
    return count;
}

ما رأيك في هذا؟ وهذا هو ، باستخدام وسيط الوظائف كمتغيرات محلية.
يتم وضع كلاهما على المكدس ، وأداء متطابق إلى حد كبير ، أنا أتساءل عن جوانب أفضل الممارسات في هذا.
أشعر أنني أحمق عندما أضيف خطًا إضافيًا ومتكررًا تمامًا لتلك الوظيفة التي تتكون من هذه الوظيفة int numCopy = num, ومع ذلك ، فإنه يخضع لي.
ماذا تعتقد؟ هل يجب تجنب هذا؟

هل كانت مفيدة؟

المحلول

  1. كقاعدة عامة ، لا أفعل استخدم معلمة دالة كمتغير معالجة محلي ، أي علاج معلمات الوظيفة على أنها قراءة فقط.

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

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

    ومع ذلك ، إذا كنت سأكتب شيئًا بسيطًا مثل countDigits(), ، أميل إلى استخدام أ remainingBalance متغير المعالجة المحلي بدلاً من تعديل num المعلمة كجزء من المعالجة المحلية - تبدو أكثر وضوحًا بالنسبة لي.

  2. في بعض الأحيان ، سأفعل تعديل معلمة محلية في بداية طريقة لتطبيع المعلمة:

    void saveName(String name) {
      name = (name != null ? name.trim() : "");
      ...
    }
    

    أبرر أن هذا جيد لأن:

    أ. من السهل رؤيته في الجزء العلوي من الطريقة ،

    ب. تحافظ المعلمة على نيتها المفاهيمية الأصلية ، و

    ج. المعلمة مستقرة لبقية الطريقة

    ثم مرة أخرى ، نصف الوقت ، أنا على ما يرام لاستخدام متغير محلي على أي حال ، فقط للحصول على زوجين إضافيين finalهناك (حسنًا ، هذا سبب سيء ، لكني أحب final):

    void saveName(final String name) {
      final String normalizedName = (name != null ? name.trim() : "");
      ...
    }
    
  3. إذا ، 99 ٪ من الوقت ، يترك الكود معلمات دالة غير معدلة (أي معلمات التحور غير بديهية أو غير متوقع بالنسبة لقاعدة الرمز) ، ثم ، خلال تلك الفترة الأخرى 1 ٪ من الوقت ، إسقاط تعليق سريع حول معلمة متحولة في الجزء العلوي من الوظيفة الطويلة/المعقدة ، يمكن أن تكون نعمة كبيرة للفهم:

    int CountDigits(int num) {
        // num is consumed
        int count = 1;
        while (num >= 10) {
            count++;
            num /= 10;
        }
        return count;
    }
    

ملاحظة :-)
المعلمات مقابل الوسائطhttp://en.wikipedia.org/wiki/Parameter_(computer_science)#parameters_and_arguments

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

لذا،

int foo(int bar)

bar هو معلمة.

int x = 5
int y = foo(x)

قيمة ال x هي الحجة ل bar معامل.

نصائح أخرى

إنه دائمًا ما يكون مضحكا بالنسبة لي عندما أفعل هذا ، لكن هذا ليس سببًا جيدًا لتجنب ذلك.

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

لا أستطيع أن أقول إنه شيء يأتي جداً في كثير من الأحيان في تجربتي - وغالبًا ما تجد أن الأمر يستحق تقديم متغير آخر لمجرد وجود اختلاف اسم, ، ولكن إذا كان الرمز الذي يكون أنظفه ينتهي تغيير قيمة المتغير ، فليكن ذلك.

موقف واحد حيث هذا يستطيع تعال وكن معقولًا تمامًا هو المكان الذي تحصل فيه على بعض القيمة "استخدم الافتراضي" (عادةً ما يكون مرجعًا فارغًا بلغة مثل Java أو C#). في هذه الحالة ، أعتقد أنه من المعقول تمامًا تعديل قيمة المعلمة إلى القيمة الافتراضية "الحقيقية". هذا مفيد بشكل خاص في C# 4 حيث أنت يستطيع لها معلمات اختيارية ، ولكن يجب أن تكون القيمة الافتراضية ثابتة:

فمثلا:

public static void WriteText(string file, string text, Encoding encoding = null)
{
    // Null means "use the default" which we would document to be UTF-8
    encoding = encoding ?? Encoding.UTF8;
    // Rest of code here
}

حول C و C ++:

رأيي هو أن استخدام المعلمة كمتغير محلي للوظيفة أمر جيد لأنه هو متغير محلي بالفعل. لماذا إذن لا تستخدمه على هذا النحو؟

أشعر بالسخافة أيضًا عند نسخ المعلمة إلى متغير محلي جديد لمجرد الحصول على متغير قابل للتعديل للعمل معه.

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

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

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

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

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

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

يجب أن يكون الكود كافيًا قدر الإمكان. ما أعنيه بذلك هو أن لديك الآن اعتماد على ما يتم تمريره كجزء من الخوارزمية الخاصة بك. إذا قرر عضو آخر في فريقك تغيير هذا إلى تمريرة بالرجوع إليه ، فقد تواجه مشاكل كبيرة.

أفضل الممارسات هي بالتأكيد نسخ المعلمات الواردة إذا كنت تتوقع أن تكون غير قابلة للتغيير.

عادةً لا أقوم بتعديل معلمات الوظائف ، إلا إذا كانت مؤشرات ، وفي هذه الحالة قد أغير القيمة التي يتم توجيهها إليها.

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

sub my_function
{
    my ($arg1, $arg2) = @_;    # get the local variables off the stack

    local $arg1;    # changing $arg1 here will not be visible outside this scope
    $arg1++;

    local $arg2->{key1};   # only the key1 portion of the hashref referenced by $arg2 is localized
    $arg2->{key1}->{key2} = 'foo';   # this change is not visible outside the function

}

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

* في بيرل ، انظر الوظيفة dclone() في المدمج قاطع وحدة.
** في بيرل ، انظر lock_hash() أو lock_hash_ref() في المدمج hash :: useil وحدة).

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top