About C # lambda expressions and Linq

Summarize what you learned about C # lambdas. The case of Java 8 is also described.

1. History of C # to Lambda

The following example describes the history of lambda.

Example: Extract data by the length of a character string from an array of String.

No lambda


        private void button2_Click(object sender, EventArgs e)
        {
            var values = new string[] { "a", "bb", "ccc", "dddd", "eeeee" };
            var result = GetData(values);
            Console.WriteLine(string.Join(",", result));
        }

        private string[] GetData(string[] values)
        {
            var result = new List<string>();
            foreach (var val in values)
            {
                if (val.Length > 2)
                {
                    result.Add(val);
                }
            }
            return result.ToArray();
        }

Execution result


ccc,dddd,eeeee

In the above example, the ones with a size larger than 2 are extracted, but when you want to extract the cases with a size of 3 or less, it cannot be handled. You can take 2 of "if (val.Length> 2)" as an argument, but you cannot change the equal sign such as above, below, and so on.

1-1. Delegate

delegate



        private void button2_Click(object sender, EventArgs e)
        {
            var values = new string[] { "a", "bb", "ccc", "dddd", "eeeee" };
            var result = GetData(values, Check1);
            Console.WriteLine(string.Join(",", result));
        }

        private string[] GetData(string[] values, LengthCheck check)
        {
            var result = new List<string>();
            foreach (var val in values)
            {
                if (check(val))
                {
                    result.Add(val);
                }
            }
            return result.ToArray();
        }

        delegate bool LengthCheck(string value);

        bool Check1(String value)
        {
            return value.Length > 2;
        }

Expressions can now be passed by using delegate. Therefore, if you want to extract 3 or less, create a Check2 method for the following and pass it.

delegate2


       private void button2_Click(object sender, EventArgs e)
        {
            var values = new string[] { "a", "bb", "ccc", "dddd", "eeeee" };
            var result = GetData(values, Check2);
            Console.WriteLine(string.Join(",", result));
        }

        private string[] GetData(string[] values, LengthCheck check)
        {
            var result = new List<string>();
            foreach (var val in values)
            {
                if (check(val))
                {
                    result.Add(val);
                }
            }
            return result.ToArray();
        }

        delegate bool LengthCheck(string value);

        bool Check1(String value)
        {
            return value.Length > 2;
        }

        bool Check2(String value)
        {
            return value.Length <= 3;
        }

1-2. Anonymous method

In the above example, we need to create a method and think about the method name. Also, since the actual declaration is different from the calling "GetData (values, Check2)", there is a problem that it is difficult to understand what is being done.

Anonymous method


        private void button2_Click(object sender, EventArgs e)
        {
            var values = new string[] { "a", "bb", "ccc", "dddd", "eeeee" };
            var result = GetData(values, 
                delegate(string s) { return s.Length <= 3; }) ;
            Console.WriteLine(string.Join(",", result));
        }

        private string[] GetData(string[] values, LengthCheck check)
        {
            var result = new List<string>();
            foreach (var val in values)
            {
                if (check(val))
                {
                    result.Add(val);
                }
            }
            return result.ToArray();
        }

        delegate bool LengthCheck(string value);

Anonymous methods have solved that problem. Anonymous methods were introduced in .NET 2.0.

1-3. Predicate

Generics were introduced in .NET 2.0, and as a result, using bool Predicate \ eliminates the need to declare delegates.

Predicate


        private void button2_Click(object sender, EventArgs e)
        {
            var values = new string[] { "a", "bb", "ccc", "dddd", "eeeee" };
            var result = GetData(values, 
                delegate(string s) { return s.Length <= 3; }) ;
            Console.WriteLine(string.Join(",", result));
        }

        private string[] GetData(string[] values, Predicate<string> check)
        {
            var result = new List<string>();
            foreach (var val in values)
            {
                if (check(val))
                {
                    result.Add(val);
                }
            }
            return result.ToArray();
        }

1-4. Lambda expression

The lambda operator "=>] was written as "delegate (string s) {return s.Length <= 3;}" in the anonymous method. If you write in, it will be as follows.

Lambda expression


        private void button2_Click(object sender, EventArgs e)
        {
            var values = new string[] { "a", "bb", "ccc", "dddd", "eeeee" };
            var result = GetData(values, s => s.Length <= 3) ;
            Console.WriteLine(string.Join(",", result));
        }

        private string[] GetData(string[] values, Predicate<string> check)
        {
            var result = new List<string>();
            foreach (var val in values)
            {
                if (check(val))
                {
                    result.Add(val);
                }
            }
            return result.ToArray();
        }

Lambda expressions were introduced in .NET 3.0. The lambda operator is read as "Goes to". The left side is the parameter and the right side is the expression or statement

1-5. Bonus

For this sample, the following code gives the same result

bonus


        private void button2_Click(object sender, EventArgs e)
        {
            var values = new string[] { "a", "bb", "ccc", "dddd", "eeeee" };
            var result = values.Where(s => s.Length <= 3);
            Console.WriteLine(string.Join(",", result));
        }

2. Lambda-style grammar

2-1. Parameters (on the left side of "=>")

You do not have to write the parameter type.

I used to write "delegate" and "string" like "delegate (string s) {return s.Length <= 3;})", but it is no longer necessary. Because the second parameter of "GetData (values, s => s.Length <= 3)" is "private string [] GetData (string [] values, Predicate check)" and it can be seen that it is a string type.

It should be noted that writing for the following does not cause an error.

It is OK to specify the type


GetData(values, (string s) => s.Length <= 3) ;

The format differs depending on the number of parameters.

() Is required when there are 0 parameters.

0 parameters


() => hoge

() Is not required when there is one parameter. Even if there is, it is OK.

1 parameter


s => hoge

If there are multiple parameters, enclose them in () and separate them with commas.

Multiple parameters


(a,b) => hoge

2-2. Expression or statement (on the right side of "=>")

No return required for expressions only

no return required


GetData(values, s => s.Length <= 3) ;

In the case of a statement, write it in {} and need a return

With return


GetData(values, s => { return s.Length <= 3; }) ;

3. Func and Action

Func has a return value, Action has no return value.

In Func \ <T, T, Result>, the end of the angle bracket is the return type. The maximum number of parameters is 16. Now that we have Func, we don't use Predicate anymore.

3-1.Action

A sample that outputs start and end logs for heavy methods and runs them asynchronously.

Action sample


        private void button2_Click(object sender, EventArgs e)
        {
            Wrap(Execute);
        }

        private void Execute()
        {
            Console.WriteLine("Heavy processing execution");
            System.Threading.Thread.Sleep(5000);
        }

        private async void Wrap(Action action)
        {
            Console.WriteLine("START");
            await Task.Run(() => action());
            Console.WriteLine("END");
        }

4. Processing for int type list

4-1. Extraction and sorting

C#


        private void button2_Click(object sender, EventArgs e)
        {
            int[] values = new int[] { 2, 5, 3, 1, 8, 4, 2, 6 };
            //Extract values ​​of 5 and above in ascending order
            var result = values.Where(v => v >= 5).OrderBy(v => v);
            Console.WriteLine(string.Join(",", result));
        }

Java


import java.util.Arrays;
import java.util.stream.IntStream;

public class Sample {

	public static void main(String[] args) {
		int[] values = new int[] { 2, 5, 3, 1, 8, 4, 2, 6 };
		//Extract values ​​of 5 and above in ascending order
		IntStream stream = Arrays.stream(values);
		stream.filter(v -> v >= 5).sorted().forEach(v -> System.out.print(v + " "));
	}
}

4-2.Max,Min,Average

C#


        private void button2_Click(object sender, EventArgs e)
        {
            int[] values = new int[] { 2, 5, 3, 1, 8, 4, 2 };
            var max = values.Max();
            var min = values.Min();
            var avg = values.Average();
            Console.WriteLine($"Max={max}, Min={min}, Average={avg}");
        }

Java


import java.util.Arrays;

public class Sample {

	public static void main(String[] args) {
		int[] values = new int[] { 2, 5, 3, 1, 8, 4, 2, 6 };
		int max = Arrays.stream(values).max().getAsInt();
		int min = Arrays.stream(values).min().getAsInt();
		double avg = Arrays.stream(values).average().getAsDouble();
		System.out.println(String.format("max=%d, min=%d, average=%f", max, min, avg));
	}
}

5. Delayed execution

Linq is a lazy execution, but it's a bit confusing and can lead to bugs, so I'll sort it out.

As a sample, a program was created in which the numerical values ​​from 1 to 10 were multiplied by 5, only even numbers were extracted, and the total was calculated.

5-1. Delayed execution

C#


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    class Class1
    {
        internal static void Main(string[] args)
        {
            int[] numbers = Enumerable.Range(1, 10).ToArray();

            IEnumerable<int> number5 = numbers.Select(n =>
            {
                Console.WriteLine($"{n}Is multiplied by 5.");
                return n * 5;
            });

            Console.WriteLine("5 times processing finished?");

            IEnumerable<int> even = number5.Where(n =>
            {
                Console.WriteLine($"{n}Is even.");
                return n % 2 == 0;
            });

            Console.WriteLine("Is it even number?");

            int sum = even.SumNumber();
            Console.WriteLine($"The total is{sum}");
        }
    }

    public static class SumSample
    {
        public static int SumNumber(this IEnumerable<int> numbers)
        {
            Console.WriteLine("Sum processing started");
            int sum = 0;
            foreach(var n in numbers)
            {
                sum += n;
                Console.WriteLine($"{n}Add.(sum:{sum})");
            }
            Console.WriteLine("Sum processing finished");
            return sum;
        }
    }
}

result


5 times processing finished?
Is it even number?
Sum processing started
Multiply 1 by 5.
Determine if 5 is even.
Multiply 2 by 5.
Determine if 10 is an even number.
Add 10.(sum:10)
Multiply 3 by 5.
Determine if 15 is an even number.
Multiply 4 by 5.
Determine if 20 is an even number.
Add 20.(sum:30)
Multiply 5 by 5.
Determine if 25 is an even number.
Multiply 6 by 5.
Determine if 30 is an even number.
Add 30.(sum:60)
Multiply 7 by 5.
Determine if 35 is an even number.
Multiply 8 by 5.
Determine if 40 is an even number.
Add 40.(sum:100)
Multiply 9 by 5.
Determine if 45 is an even number.
Multiply 10 by 5.
Determine if 50 is an even number.
Add 50.(sum:150)
Sum processing finished
150 in total

I try to output to the Console in the loop, but it doesn't move like "First perform the process of multiplying the value by 5 and then perform the process of extracting after that ...". The process has been executed since it was needed for SumNumber processing. (Delayed execution)

5-2. Immediate execution

C#


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    class Class1
    {
        internal static void Main(string[] args)
        {
            int[] numbers = Enumerable.Range(1, 10).ToArray();

            IEnumerable<int> number5 = numbers.Select(n =>
            {
                Console.WriteLine($"{n}Is multiplied by 5.");
                return n * 5;
            }).ToArray();   //Immediate execution

            Console.WriteLine("5 times processing finished?");

            IEnumerable<int> even = number5.Where(n =>
            {
                Console.WriteLine($"{n}Is even.");
                return n % 2 == 0;
            }).ToArray();   //Immediate execution

            Console.WriteLine("Is it even number?");

            int sum = even.SumNumber();
            
            Console.WriteLine($"The total is{sum}");
        }
    }

    public static class SumSample
    {
        public static int SumNumber(this IEnumerable<int> numbers)
        {
            Console.WriteLine("Sum processing started");
            int sum = 0;
            foreach(var n in numbers)
            {
                sum += n;
                Console.WriteLine($"{n}Add.(sum:{sum})");
            }
            Console.WriteLine("Sum processing finished");
            return sum;
        }
    }
}

result


Multiply 1 by 5.
Multiply 2 by 5.
Multiply 3 by 5.
Multiply 4 by 5.
Multiply 5 by 5.
Multiply 6 by 5.
Multiply 7 by 5.
Multiply 8 by 5.
Multiply 9 by 5.
Multiply 10 by 5.
5 times processing finished?
Determine if 5 is even.
Determine if 10 is an even number.
Determine if 15 is an even number.
Determine if 20 is an even number.
Determine if 25 is an even number.
Determine if 30 is an even number.
Determine if 35 is an even number.
Determine if 40 is an even number.
Determine if 45 is an even number.
Determine if 50 is an even number.
Is it even number?
Sum processing started
Add 10.(sum:10)
Add 20.(sum:30)
Add 30.(sum:60)
Add 40.(sum:100)
Add 50.(sum:150)
Sum processing finished
150 in total

To execute immediately, add ToArray or ToList.

5-3. Precautions for delayed execution

Intuitively difficult to understand. In the example below, 5 or more are extracted from the list containing the values ​​2,4,6,8. Note that not only 6 and 8 are extracted, but 7 added after that is also extracted.

C#


        private void button1_Click(object sender, EventArgs e)
        {
            List<int> list = new List<int> { 2, 4, 6, 8 };
            var result = list.Where(x => x > 5);
            list.Add(3);
            list.Add(7);
            Console.WriteLine(string.Join(",", result));
        }

result


6,8,7

5-4. Benefits of delayed execution

5-4-1. Memory usage is low

Number 5 and even in 5-1 have no substance. Just call it when you need it and return the value.

Recommended Posts

About C # lambda expressions and Linq
About Java lambda expressions
[For beginners] About lambda expressions and Stream API
[Introduction to Java] About lambda expressions
Nowadays Java lambda expressions and Stream API
[Java] Sort the list using streams and lambda expressions
About Bean and DI
About classes and instances
Understand Java 8 lambda expressions
About gets and gets.chomp
About redirect and forward
About encapsulation and inheritance
Explain Java 8 lambda expressions
colorize and regular expressions
About Serializable and serialVersionUID
Have fun programming with lambda expressions and a fluid interface
Handle exceptions coolly with Java 8 lambda expressions and Stream API
About for statement and if statement
About synchronized and Reentrant Lock
About regular expressions in Ruby
[Java] Introduction to lambda expressions
About Ruby hashes and symbols
[Java] About String and StringBuilder
About the same and equivalent
Ruby C extension and volatile
About classes and instances (evolution)
About pluck and ids methods
Consideration about classes and instances
About Java Packages and imports
About Ruby and object model
About Ruby classes and instances
About instance variables and attr_ *
About self-introduction and common errors
C # and Java Overrides Story