Functional Programming: Difference between revisions
No edit summary |
No edit summary |
||
| Line 7: | Line 7: | ||
=== Action Delegate in C#: === | === Action Delegate in C#: === | ||
The `Action` delegate is a predefined delegate type in C# that represents a method which performs an action and does not return a value. It can take parameters, but it does not return any value (it returns `void`). | The `Action` delegate is a predefined delegate type in C# that represents a method which performs an action and does not return a value. It can take parameters, but it does not return any value (it returns `void`). | ||
{{CodeSnippet | |||
| language = python | |||
| code = print("Hello, world!") | |||
}} | |||
<syntaxhighlight lang="csharp"> | |||
<abc> Hello World = a; | |||
</syntaxhighlight> | |||
<source lang="php"> | <source lang="php"> | ||
<?php | <?php | ||
Revision as of 07:17, 17 January 2025
Functional Programming
Functional programming in C# can complement both imperative and object-oriented programming paradigms. Let's get started with functional programming in C#:
Delegates
C# provides delegates and events, which are critical for functional programming - these include `Action` and `Func`.
Action Delegate in C#:
The `Action` delegate is a predefined delegate type in C# that represents a method which performs an action and does not return a value. It can take parameters, but it does not return any value (it returns `void`).
Template:CodeSnippet
<syntaxhighlight lang="csharp">
<abc> Hello World = a;
</syntaxhighlight>
<source lang="php">
<?php
v = "string"; // sample initialization
?> html text <?php
echo v; // end of php code
?> </source> <source lang="csharp"> Action<string> greet = name => Console.WriteLine($"Hello, {name}!"); greet("World"); // Output: Hello, World! </source>
Func Delegate in C#
The `Func` delegate type represents a method that takes zero or more input parameters and returns a value. The last type parameter specifies the return type. ```csharp Func<int, int, int> sum = (x, y) => x + y; int result = sum(2, 3); // result = 5 Console.WriteLine(result); // Output: 5 ```
Basics
Lambda Expressions
C# provides lambda expressions and closures, which are essential for functional programming. These allow you to define and pass functions as arguments to other functions.
```csharp Func<int, int> square = x => x * x; int result = square(5); // result will be 25 ```
Immutable Data
Immutable data structures ensure that once a data structure is created, it cannot be modified.
```csharp
public class ImmutablePerson
{
public readonly string Name;
public readonly int Age;
public ImmutablePerson(string name, int age)
{
Name = name;
Age = age;
}
}
```
In functional programming, immutability is a core concept that helps to avoid side effects and maintain a clear flow of data through functions.
LINQ
LINQ is a powerful feature in C# that enables functional-style querying of collections.
Higher-Order Functions
Higher-order functions are functions that can take other functions as parameters or return functions as results.
```csharp
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
List<int> squareNumbers = numbers.Select(x => x * x).ToList(); // squareNumbers contains [1, 4, 9, 16, 25]
```
Pure Functions
Pure functions have no side effects and always return the same output for the same input.
```csharp
int Add(int a, int b)
{
return a + b;
}
```
Functional Composition
Functional composition involves combining smaller functions to create more complex ones.
```csharp Func<int, int> doubleNumber = x => x * 2; Func<int, int> squareNumber = x => x * x;
Func<int, int> compose = x => doubleNumber(squareNumber(x));
int result = compose(3); // Result: 18 (first square, then double) ```
Pattern Matching
Pattern matching in C# allows you to write more expressive and functional code when dealing with complex data structures. It's a valuable feature in functional programming.
```csharp public record Circle(double Radius); public record Rectangle(double Width, double Height); public record Triangle(double Base, double Height);
static double CalculateArea(object shape) => shape switch {
Circle circle => Math.PI * Math.Pow(circle.Radius, 2),
Rectangle rectangle => rectangle.Width * rectangle.Height,
Triangle triangle => 0.5 * triangle.Base * triangle.Height,
_ => throw new ArgumentException("Unknown shape")
};
static void Main() {
Circle circle = new Circle(5.0);
Rectangle rectangle = new Rectangle(4.0, 6.0);
Triangle triangle = new Triangle(3.0, 4.0);
Console.WriteLine($"Circle area: {CalculateArea(circle)}"); // Circle area: 78.53981633974483
Console.WriteLine($"Rectangle area: {CalculateArea(rectangle)}"); // Rectangle area: 24
Console.WriteLine($"Triangle area: {CalculateArea(triangle)}"); // Triangle area: 6
}
```
Monads
Monads encapsulates and abstracts values to enable consistent and controlled manipulation, composition and transformation of those value.
```csharp
int? number = 42;
int? result = number
.Select(x => x * 2)
.Where(x => x > 50)
.FirstOrDefault();
Console.WriteLine(result); // Result: 84 ```
Option Types (Optional Values)
An option type represents values that can be either present or absent (null), providing a safer and more expressive way to handle potentially missing data. Option Types (Handling Optional Values):
```csharp int? someValue = 42; // A value exists. int result = someValue ?? 0; // If someValue is null, use a default value (0). ```
There are libraries and frameworks available in C# that can assist you in functional programming, such as LanguageExt and F#.
Concepts
Currying
Currying is the process of converting a function that takes multiple arguments into a series of functions, each taking a single argument. In C#, you can implement currying using lambda expressions: ```csharp Func<int, Func<int, int>> curriedAdd = a => b => a + b; int addTwo = curriedAdd(2); int result = addTwo(3); // result will be 5 ``` In this example, `curriedAdd` is a curried function that takes two integers and returns a new function that adds them. This concept can help with partial function application and creating more reusable functions. 
- Partial Function Application
Partial function application is a technique where you fix a certain number of arguments of a function, creating a new function with fewer parameters. In C#, you can achieve this using lambda expressions as well: ```csharp Func<int, int, int> add = (a, b) => a + b; Func<int, int> addTwo = a => add(a, 2); int result = addTwo(3); // result will be 5 ``` Here, `addTwo` partially applies the `add` function by fixing one of its arguments to 2, resulting in a new function that only requires one input.
Memoization
Memoization is a caching technique where the results of expensive function calls are stored and reused when the same inputs occur again. You can implement memoization in C# to optimize recursive or computationally expensive functions, making them more efficient. This demonstrates memoization for Fibonacci numbers using a Dictionary for caching: ```csharp using System; using System.Collections.Generic;
static class MemoizationExample {
static Dictionary<int, long> memo = new Dictionary<int, long>();
static long Fibonacci(int n) =>
n <= 1 ? n : (memo.ContainsKey(n) ? memo[n] : (memo[n] = Fibonacci(n - 1) + Fibonacci(n - 2)));
static void Main() {
Console.WriteLine($"Fibonacci(40) = {Fibonacci(40)}");
}
}
```
Monads
Monads are a fundamental concept in functional programming that provide a way to handle sequences of operations, error handling and side effects in a clean and composable manner. In C#, you can work with monads for tasks like asynchronous programming using Task<T>, error handling using Option or Either types, or working with sequences using LINQ. This Task monad for asynchronous programming uses await with Task.Delay to simulate asynchronous operations and processing. ```csharp using System; using System.Threading.Tasks;
static class MonadExample {
static async Task Main() {
int data = await ReadDataAsync();
int result = await ProcessDataAsync(data);
Console.WriteLine($"Result: {result}");
}
static async Task<int> ReadDataAsync() => await Task.Delay(1000).ContinueWith(_ => 42); static async Task<int> ProcessDataAsync(int data) => await Task.Delay(500).ContinueWith(_ => data * 2);
} ```
Recursion
Functional programming encourages the use of recursion for solving problems. Recursion is the process of a function calling itself to solve a problem. Make sure to learn about tail recursion and how to optimize recursive functions in C#. Using recursion to calculate the factorial of a number in C#, without additional functions or set-up: ```csharp using System;
static class RecursionExample {
static int Factorial(int n) => n == 0 ? 1 : n * Factorial(n - 1);
static void Main() {
int n = 5;
Console.WriteLine($"Factorial({n}) = {Factorial(n)}");
}
} ```