Подтвердить что ты не робот

Использование аргументов функции в качестве локальных переменных

Что-то вроде этого (да, это не касается некоторых случаев краев - это не точка):

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

Каково ваше мнение об этом? То есть, используя аргументы функции в качестве локальных переменных.
Оба они помещены в стек, и в значительной степени идентичные производительности, я задаюсь вопросом о лучших практических аспектах этого.
Я чувствую себя идиотом, когда добавляю дополнительную и довольно избыточную линию к этой функции, состоящей из int numCopy = num, но это меня беспокоит.
 Как вы думаете? Следует ли это избегать?

4b9b3361

Ответ 1

  • Как правило, я бы не использовал параметр функции в качестве локальной переменной обработки, то есть обрабатывал параметры функции только для чтения.

    На мой взгляд, интуитивно понятный код имеет первостепенное значение для ремонтопригодности, а изменение параметра функции для использования в качестве локальной переменной обработки имеет тенденцию противодействовать этой цели. Я пришел к выводу, что параметр будет иметь то же значение в середине и снизу метода, что и в верхней части. Кроме того, подходящая локальная переменная обработки может улучшить понятность.

    Тем не менее, как говорит @Stewart, это правило более или менее важно в зависимости от длины и сложности функции. Для коротких простых функций, таких как тот, который вы показываете, просто использование самого параметра может быть легче понять, чем ввести новую локальную переменную (очень субъективную).

    Тем не менее, если бы я написал что-то простое, как countDigits(), я бы предпочел использовать локальную переменную remainingBalance вместо изменения параметра num как часть локальной обработки - просто кажется яснее я.

  • Иногда я изменяю локальный параметр в начале метода для нормализации параметра:

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

    Я рационализирую, что это нормально, потому что:

    а. в верхней части метода легко видеть,

    б. параметр сохраняет свое первоначальное концептуальное намерение, а

    с. параметр стабилен для остальной части метода

    И снова, в половине случаев, я так же склонен использовать локальную переменную в любом случае, просто чтобы получить там пару дополнительных final (хорошо, это плохая причина, но мне нравится final).

    void saveName(final String name) {
      final String normalizedName = (name != null ? name.trim() : "");
      ...
    }
    
  • Если в 99% случаев код оставляет неизмененным функциональные параметры (т.е. параметры мутации являются неинтуитивными или неожиданными для этой базы кода), то в течение этого другого 1% времени снижается быстрый комментарий о параметре mutating в верхней части длинной/сложной функции может стать большим благом для понимания:

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

P.S.:-)
параметры против аргументов 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.

Ответ 2

Мне всегда бывает немного смешно, когда я это делаю, но это не очень хорошая причина, чтобы избежать этого.

Одна из причин, по которой вы, возможно, захотите избежать этого, - это отладка целей. Возможность рассказать о различиях между переменными "scratchpad" и входом функции может быть очень полезна, когда вы на полпути через отладку.

Я не могу сказать, что это очень часто появляется в моем опыте - и часто вы можете обнаружить, что стоит ввести другую переменную только ради другого имени, но если код, который в противном случае чист, заканчивается изменяя значение переменной, то так и должно быть.

Одна из ситуаций, когда это может возникнуть и быть вполне разумным, - это то, где вы получили значение, означающее "использовать значение по умолчанию" (обычно это пустая ссылка на языке Java или С#). В этом случае я считаю совершенно разумным изменить значение параметра на "реальное" значение по умолчанию. Это особенно полезно в С# 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
}

Ответ 3

О C и С++:

Мое мнение состоит в том, что использование параметра в качестве локальной переменной функции отлично, потому что это уже локальная переменная. Почему тогда не использовать его как таковое?

Я тоже чувствую себя глупо, когда копирую параметр в новую локальную переменную, чтобы иметь изменяемую переменную, с которой можно работать.

Но я думаю, что это личное мнение. Сделайте это как хотите. Если вы чувствуете, что копия копирует параметр только из-за этого, это означает, что ваша личность не нравится, и тогда вы не должны этого делать.

Ответ 4

Если мне не нужна копия исходного значения, я не объявляю новую переменную.

IMO Я не думаю, что мутация значений параметров является плохой практикой в ​​целом,
это зависит от того, как вы собираетесь использовать его в своем коде.

Ответ 5

Мой стандарт кодирования команд рекомендует против этого, потому что он может выйти из-под контроля. На мой взгляд, для функции, подобной той, которую вы показываете, это не больно, потому что каждый может видеть, что происходит. Проблема в том, что с функциями времени больше, и они получают исправления ошибок в них. Как только функция содержит более одного экрана с кодом, это начинает запутываться, поэтому наш стандарт кодирования запрещает его.

Компилятор должен быть в состоянии избавиться от избыточной переменной довольно легко, поэтому он не оказывает никакого влияния на эффективность. Вероятно, именно между вами и вашим обозревателем кода будет ли это нормально или нет.

Ответ 6

Я бы вообще не изменил значение параметра внутри функции. Если в какой-то момент в функции вам нужно обратиться к исходному значению, у вас все еще есть. в вашем простом случае нет проблем, но если вы добавите больше кода позже, вы можете обратиться к "num", не понимая, что он был изменен.

Ответ 7

Код должен быть как можно более самодостаточным. Я имею в виду, что теперь у вас есть зависимость от того, что передается как часть вашего алгоритма. Если другой член вашей команды решит изменить это на пропуск по ссылке, у вас могут возникнуть большие проблемы.

Лучшая практика - это, безусловно, скопировать входящие параметры, если вы ожидаете, что они будут неизменными.

Ответ 8

Обычно я не изменяю параметры функции, если они не являются указателями, и в этом случае я могу изменить значение, на которое указывалось.

Ответ 9

Я думаю, что лучшие практики этого варьируются в зависимости от языка. Например, в Perl вы можете локализовать любую переменную или даже часть переменной в локальной области, так что ее изменение в этой области не будет имеют какое-либо влияние за его пределами:

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

}

Иногда меня укусили, забыв локализовать структуру данных, которая была передана ссылкой на функцию, которую я изменил внутри функции. Напротив, я также вернул структуру данных в виде результата функции, который был разделен между несколькими системами, и вызывающий затем приступил к изменению данных по ошибке, воздействуя на эти другие системы в трудноотслеживаемой проблеме, обычно называемой действием на расстоянии. Лучше всего сделать это, чтобы сделать клон данных перед его возвратом * или сделать его доступным только для чтения **.

* В Perl см. функцию dclone() во встроенном модуле Storable.
** В Perl см. lock_hash() или lock_hash_ref() во встроенном модуле Hash::Util).