Compartir a través de


Refactorizar en funciones puras

Actualización: November 2007

Un aspecto importante de las transformaciones funcionales puras es aprender cómo refactorizar código usando funciones puras.

Nota:

La nomenclatura común en la programación funcional consiste en refactorizar programas usando funciones puras. En Visual Basic y C++, esto se alinea con el uso de funciones en los lenguajes respectivos. No obstante, en C#, las funciones son métodos llamados. Con fines de análisis, una función pura se implementa como un método en C#.

Tal como se indicó previamente en esta sección, una función pura tiene dos características útiles:

  • No tiene efectos secundarios. La función no cambia variables o datos de ningún tipo fuera de la función.

  • Es coherente. Con el mismo conjunto de datos de entrada, siempre devolverá el mismo valor de salida.

Una forma de realizar una transición a la programación funcional es refactorizar código existente para eliminar efectos secundarios innecesarios y dependencias externas. De esta forma puede crear versiones de función pura del código existente.

En este tema se trata qué es una función pura y qué no es. El tutorial Manipular información en un documento WordprocessingML muestra cómo manipular un documento WordprocessingML e incluye dos ejemplos de cómo refactorizar usando una función pura.

Eliminar efectos secundarios y dependencias externas

Los siguientes ejemplos contraponen dos funciones no puras y una función pura.

Función no pura que cambia un miembro de clase

En el siguiente código, la función HypenatedConcat no es una función pura porque modifica el miembro de datos aMember en la clase:

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

Este código genera el siguiente resultado:

StringOne-StringTwo

Tenga en cuenta que es irrelevante si los datos que se están modificando tienen acceso public o private o si son un miembro static (shared) o un miembro de instancia. Una función pura no cambia datos fuera de la función.

Función no pura que cambia un argumento

Asimismo, la siguiente versión de esta misma función no es pura porque modifica el contenido de su parámetro, 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

Esta versión del programa crea el mismo resultado que la primera versión porque la función HypenatedConcat ha cambiado el valor (estado) de su primer parámetro invocando la función de miembro Append. Tenga en cuenta que esta modificación se produce a pesar del hecho que HypenatedConcat usar el paso de parámetros llamada por valor.

Nota importante:

Para los tipos de referencia, si pasa un parámetro por valor, tiene como resultado una copia de la referencia a un objeto que se está pasando. Esta copia sigue asociada con los mismos datos de la instancia que la referencia original (hasta que la variable de referencia se asigna a un objeto nuevo). La llamada por referencia no es necesariamente requerida para que una función modifique un parámetro.

Función pura

La versión siguiente del programa muestra cómo implementar la función HypenatedConcat como una función pura.

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

De nuevo, esta versión crea la misma línea de salida: StringOne-StringTwo. Tenga en cuenta que para conservar un valor concatenado, se almacena en la variable intermedia s2.

Un enfoque que puede ser muy útil para escribir funciones que son localmente impuras (es decir, declaran y modifican variables locales) pero que son globalmente puras. Tales funciones tienen muchas características deseables de facilidad de creación, pero evitan algunas de las expresiones de programación funcional más complicadas, como tener que usar una recursión cuando un simple bucle lograría lo mismo.

Operadores de consulta estándar

Una característica importante de los operadores de consulta estándar es que se implementan como funciones puras.

Para obtener más información, vea Información general sobre operadores de consulta estándar.

Vea también

Conceptos

Introducción a las transformaciones funcionales puras

Diferencias entre programación funcional y programación imperativa