Refaktoryzacja do czystych funkcji (LINQ to XML)
Ważnym aspektem czystych przekształceń funkcjonalnych jest uczenie się, jak refaktoryzować kod przy użyciu czystych funkcji.
Uwaga
Powszechną nomenklaturą w programowaniu funkcjonalnym jest refaktoryzacja programów przy użyciu czystych funkcji. W języku Visual Basic i C++jest to zgodne z użyciem funkcji w odpowiednich językach. Jednak w języku C#funkcje są nazywane metodami. Na potrzeby tej dyskusji czysta funkcja jest implementowana jako metoda w języku C#.
Jak wspomniano wcześniej w tej sekcji, czysta funkcja ma dwie przydatne cechy:
- Nie ma skutków ubocznych. Funkcja nie zmienia żadnych zmiennych ani danych dowolnego typu poza funkcją.
- Jest spójny. Biorąc pod uwagę ten sam zestaw danych wejściowych, zawsze zwraca tę samą wartość wyjściową.
Jednym ze sposobów przejścia do programowania funkcjonalnego jest refaktoryzacja istniejącego kodu w celu wyeliminowania niepotrzebnych skutków ubocznych i zależności zewnętrznych. W ten sposób można tworzyć czyste wersje funkcji istniejącego kodu.
W tym artykule omówiono, czym jest czysta funkcja i czym nie jest. Samouczek: manipulowanie zawartością w dokumencie WordprocessingML pokazuje, jak manipulować dokumentem WordprocessingML i zawiera dwa przykłady refaktoryzacji przy użyciu czystej funkcji.
W poniższych przykładach porównane są dwie nieczyste funkcje i czysta funkcja.
Przykład: Implementowanie funkcji nieczystej, która zmienia składową klasy statycznej
W poniższym kodzie funkcja nie jest czystą funkcją, HyphenatedConcat
ponieważ modyfikuje statyczną aMember
składową klasy:
public class Program
{
private static string aMember = "StringOne";
public static void HyphenatedConcat(string appendStr)
{
aMember += '-' + appendStr;
}
public static void Main()
{
HyphenatedConcat("StringTwo");
Console.WriteLine(aMember);
}
}
Module Module1
Dim aMember As String = "StringOne"
Public Sub HyphenatedConcat(ByVal appendStr As String)
aMember = aMember & "-" & appendStr
End Sub
Sub Main()
HyphenatedConcat("StringTwo")
Console.WriteLine(aMember)
End Sub
End Module
Ten przykład generuje następujące wyniki:
StringOne-StringTwo
Należy pamiętać, że nie ma znaczenia, czy modyfikowane dane mają public
dostęp private
, czy też są static
członkami, shared
członkiem lub członkiem wystąpienia. Czysta funkcja nie zmienia żadnych danych poza funkcją.
Przykład: Implementowanie funkcji nieczystej, która zmienia parametr
Ponadto następująca wersja tej samej funkcji nie jest czysta, ponieważ modyfikuje zawartość parametru sb
.
public class Program
{
public static void HyphenatedConcat(StringBuilder sb, String appendStr)
{
sb.Append('-' + appendStr);
}
public static void Main()
{
StringBuilder sb1 = new StringBuilder("StringOne");
HyphenatedConcat(sb1, "StringTwo");
Console.WriteLine(sb1);
}
}
Module Module1
Public Sub HyphenatedConcat(ByVal sb As StringBuilder, ByVal appendStr As String)
sb.Append("-" & appendStr)
End Sub
Sub Main()
Dim sb1 As StringBuilder = New StringBuilder("StringOne")
HyphenatedConcat(sb1, "StringTwo")
Console.WriteLine(sb1)
End Sub
End Module
Ta wersja programu generuje te same dane wyjściowe co pierwsza wersja, ponieważ HyphenatedConcat
funkcja zmieniła wartość (stan) pierwszego parametru, wywołując funkcję składową Append . Należy pamiętać, że ta zmiana występuje pomimo faktu, że HyphenatedConcat
używa przekazywania parametru call-by-value.
Ważne
W przypadku typów odwołań, jeśli przekażesz parametr według wartości, spowoduje to przekazanie kopii odwołania do przekazanego obiektu. Ta kopia jest nadal skojarzona z tymi samymi danymi wystąpienia co oryginalne odwołanie (dopóki zmienna referencyjna nie zostanie przypisana do nowego obiektu). Wywołanie po odwołaniu nie musi być wymagane do zmodyfikowania parametru przez funkcję.
Przykład: Implementowanie czystej funkcji
W następnej wersji programu pokazano, jak zaimplementować HyphenatedConcat
funkcję jako czystą funkcję.
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
Ponownie ta wersja generuje ten sam wiersz danych wyjściowych: StringOne-StringTwo
. Należy pamiętać, że aby zachować połączoną wartość, jest ona przechowywana w zmiennej s2
pośredniej .
Jednym z podejść, które może być bardzo przydatne, jest pisanie funkcji, które są lokalnie nieczyste (czyli deklarują i modyfikują zmienne lokalne), ale są globalnie czyste. Takie funkcje mają wiele pożądanych cech komponowania, ale unikaj niektórych bardziej zawiłych idiomów programowania funkcjonalnego, takich jak konieczność używania rekursji, gdy prosta pętla zrobiłaby to samo.
Standardowe operatory zapytań
Ważną cechą standardowych operatorów zapytań jest to, że są one implementowane jako czyste funkcje.
Aby uzyskać więcej informacji, zobacz Omówienie standardowych operatorów zapytań (C#) i Standardowe operatory zapytań — omówienie (Visual Basic).