Поделиться через


Оптимизация кода для получения чистых функций

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

Примечание

Общим подходом к функциональному программированию является оптимизация кода программ для получения чистых функций.В Visual Basic и C++ это равносильно использованию функций.Но в C# функции называются методами.В данном случае чистая функция в C# реализуется как метод.

Как отмечалось ранее в этом разделе, чистые функции имеют две полезные характеристики.

  • У них отсутствуют побочные эффекты. Функции не изменяют значения или данные любого типа, не входящие в область их определения.

  • Функции действуют единообразно. После получения того же набора входных данных они всегда возвращают одно и то же выходное значение.

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

В этом разделе объясняется, что представляет собой чистая функция и чем она не является. В учебнике Управление данными в документе WordprocessingML показано управление документом WordprocessingML, содержится два примера оптимизации кода с помощью чистых функций.

Устранение побочных эффектов и внешних зависимостей

В следующих примерах сравниваются обычные и чистые функции.

Обычная функция, изменяющая член класса

В следующем примере кода функция HypenatedConcat представляет собой обычную функцию, поскольку изменяет элемент данных aMember в классе.

public class Program
{
    private static string aMember = "StringOne";

    public static void HypenatedConcat(string appendStr)
    {
        aMember += '-' + appendStr;
    }

    public static void Main()
    {
        HypenatedConcat("StringTwo");
        Console.WriteLine(aMember);
    }
}
Module Module1
    Dim aMember As String = "StringOne"

    Public Sub HypenatedConcat(ByVal appendStr As String)
        aMember = aMember & "-" & appendStr
    End Sub

    Sub Main()
        HypenatedConcat("StringTwo")
        Console.WriteLine(aMember)
    End Sub
End Module

Этот код выводит следующие результаты.

StringOne-StringTwo

Обратите внимание, что неважно, какой доступ имеют изменяемые данные, public или private, и являются ли они членом класса static (shared) или элементом экземпляра. Чистая функция не изменяет не относящиеся к ней данные.

Обычная функция, изменяющая аргумент

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

public class Program
{
    public static void HypenatedConcat(StringBuilder sb, String appendStr)
    {
        sb.Append('-' + appendStr);
    }

    public static void Main()
    {
        StringBuilder sb1 = new StringBuilder("StringOne");
        HypenatedConcat(sb1, "StringTwo");
        Console.WriteLine(sb1);
    }
}
Module Module1
    Public Sub HypenatedConcat(ByVal sb As StringBuilder, ByVal appendStr As String)
        sb.Append("-" & appendStr)
    End Sub

    Sub Main()
        Dim sb1 As StringBuilder = New StringBuilder("StringOne")
        HypenatedConcat(sb1, "StringTwo")
        Console.WriteLine(sb1)
    End Sub
End Module

Эта версия программы дает те же выходные данные, что и первая версия, поскольку функция HypenatedConcat изменила значение (состояние) своего первого параметра, вызвав функцию-член Append. Обратите внимание, что это изменение происходит несмотря на то, что функция HypenatedConcat использует передачу параметра по значению.

Важно!

Передача параметров по значению для ссылочных типов приводит к созданию копии ссылки на передаваемый объект.Эта копия все еще связана с теми же данными экземпляра, что и первоначальная ссылка (пока ссылочная переменная не будет присвоена новому объекту).Вызов по значению необязательно требуется функции для изменения параметра.

Чистая функция

Следующая версия этой программы реализует функцию HypenatedConcat в виде чистой функции.

class Program
{
    public static string HyphenatedConcat(string s, string appendStr)
    {
        return (s + '-' + appendStr);
    }

    public static void Main(string[] args)
    {
        string s1 = "StringOne";
        string s2 = HyphenatedConcat(s1, "StringTwo");
        Console.WriteLine(s2);
    }
}
Module Module1
    Public Function HyphenatedConcat(ByVal s As String, ByVal appendStr As String) As String
        Return (s & "-" & appendStr)
    End Function

    Sub Main()
        Dim s1 As String = "StringOne"
        Dim s2 As String = HyphenatedConcat(s1, "StringTwo")
        Console.WriteLine(s2)
    End Sub
End Module

И снова она дает ту же строку вывода: StringOne-StringTwo. Обратите внимание, что для сохранения соединенного значения оно сохраняется в промежуточной переменной s2.

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

Стандартные операторы запроса

Важной характеристикой стандартных операторов запросов является то, что они реализуются как чистые функции.

Дополнительные сведения см. в разделе Общие сведения о стандартных операторах запроса.

См. также

Основные понятия

Введение в чисто функциональные преобразования

Сравнение функционального и императивного программирования