Использование аргументов функции в качестве локальных переменных - PullRequest
17 голосов
/ 02 мая 2010

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

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

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

Ответы [ 9 ]

17 голосов
/ 02 мая 2010
  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;
    }
    

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.

9 голосов
/ 02 мая 2010

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

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

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

Одна из ситуаций, в которой этот может прийти и быть вполне разумным, - это когда у вас есть какое-то значение, означающее «использовать значение по умолчанию» (обычно нулевая ссылка вязык как 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
}
4 голосов
/ 26 февраля 2011

О C и C ++ :

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

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

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

1 голос
/ 02 мая 2010

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

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

0 голосов
/ 02 мая 2010

Я думаю, что лучшие практики этого зависят от языка. Например, в 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() во встроенном Сохраняемом модуле.
** В Perl см. lock_hash() или lock_hash_ref() во встроенном модуле Hash :: Util ).

0 голосов
/ 02 мая 2010

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

0 голосов
/ 02 мая 2010

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

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

0 голосов
/ 02 мая 2010

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

0 голосов
/ 02 мая 2010

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...