πŸ’» Programming Language/C#

38. λžŒλ‹€μ‹(Lambda Expression)

S.Honey 2022. 4. 8. 09:14
  • λžŒλ‹€μ‹ : 읡λͺ… λ©”μ†Œλ“œλ₯Ό λ§Œλ“€κΈ° μœ„ν•΄ μ‚¬μš©ν•˜λŠ” 식
    • λžŒλ‹€μ‹μœΌλ‘œ λ§Œλ“€μ–΄μ§„ 읡λͺ… λ©”μ†Œλ“œλ₯Ό 무λͺ… ν•¨μˆ˜(Anonymous Function) 라고 ν•œλ‹€.

λžŒλ‹€μ‹ μ„ μ–Έ ν˜•μ‹

λ§€κ°œλ³€μˆ˜ λͺ©λ‘ => 식 
  • => μ—°μ‚°μžλ₯Ό μž…λ ₯ μ—°μ‚°μžλΌκ³  ν•œλ‹€.
delegate int AAA(int x, int y);
static void Main(string[] args){
    AAA aaa = (int x,int y) => x + y;
    }
  • (int x, int y) => x + y λΌλŠ” 읡λͺ… λ©”μ†Œλ“œκ°€ delegate 인 aaa에 μ „λ‹¬λ˜κ³  μžˆλŠ” λͺ¨μŠ΅

  • μœ„μ˜ μ½”λ“œλŠ” λ‹€μŒκ³Ό 같이 λ°”κΏ”μ„œ μ‚¬μš© κ°€λŠ₯

    AAA aaa = (x, y) => x + y; 
  • delegate의 μΈμžμ— νƒ€μž…μ΄ μ„ μ–Έλ˜μ–΄ μžˆκΈ°μ— κ°€λŠ₯

  • μœ„μ˜ 읡λͺ… λ©”μ†Œλ“œ(무λͺ…ν•¨μˆ˜)λŠ” 기쑴에 읡λͺ… λ©”μ†Œλ“œλ₯Ό μ‚¬μš©ν•˜λ˜ 것 보닀 κ°„μ†Œν™” λ˜μ—ˆλ‹€.


κΈ°μ‘΄ 델리게이트λ₯Ό μ‚¬μš©ν•˜λ˜ 방식
    delegate int AAA(int x, int y);

    static void Main(string[] args){
        AAA aaa = delegate(int x, int y)
                  {
                    return x+y;
                  }
    }

μ½”λ“œ μ‚¬μš© 예제1

using System.Collections;

namespace LambdaEx
{       
    class Program
    {
        delegate int Sum(int x, int y);

        static void Main(string[] args)
        {
            Sum sum = (x, y) => x + y;

            Console.WriteLine($"{sum(10, 20)}");
        }
    }
}

λžŒλ‹€μ‹μ˜ 또 λ‹€λ₯Έ ν‘œν˜„

  • μ„ μ–Έν˜•μ‹λ“€ .. ? => λ‹€μ–‘ν•˜κ²Œ μ‚¬μš©λ  λ“―

λ§€κ°œλ³€μˆ˜λͺ©λ‘ => {
    μ‹€ν–‰μ½”λ“œ ... 
}

λŒ€λ¦¬μž μΈμŠ€ν„΄μŠ€ = () => { ... μ‹€ν–‰μ½”λ“œ ... } 
// λ§€κ°œλ³€μˆ˜κ°€ μ—†λŠ” 무λͺ…ν•¨μˆ˜

λŒ€λ¦¬μž μΈμŠ€ν„΄μŠ€ = (λ§€κ°œλ³€μˆ˜) => { ... μ‹€ν–‰μ½”λ“œ ... } 
// λ§€κ°œλ³€μˆ˜κ°€ μžˆλŠ” 무λͺ…ν•¨μˆ˜

μ‚¬μš©μ˜ˆμ œ μ½”λ“œ
using System.Collections;

namespace LambdaEx
{       
    class Program
    {
        delegate string SumString(string[] args);
        static void Main(string[] args)
        {
            SumString ss = (str) =>
            {
                string result = "";
                foreach (string s in str)
                {
                    result += s;
                }
                return result;
            };

            Console.WriteLine(ss(args));
        }
    }
}
  • .exeνŒŒμΌμ„ μ‹€ν–‰μ‹œμΌœ μž…λ ₯을 μ£Όλ©΄ ν•΄λ‹Ή μž…λ ₯을 이어뢙인 string κ²°κ³Όλ₯Ό 좜λ ₯.

λžŒλ‹€μ‹ μ‚¬μš©μ˜ˆμ œ μ½”λ“œ μΆ”κ°€
class Program
{
    delegate int Calculate(int a , int b);
    delegate string Concatenate(string[] strs);

    static void Main(string[] args)
    {
        // 식 ν˜•μ‹μ˜ λžŒλ‹€μ‹
        Calculate cal1 = (a, b) => a + b;

        Console.WriteLine("{0} + {1} = {2}",3,4,cal1(3,4));

        // λ¬Έ ν˜•μ‹μ˜ λžŒλ‹€μ‹
        Concatenate concat = (arr) =>
        {
            string result = "";

            foreach (string str in arr)
            {
                result += str;
            }

            return result;
        };

        string[] txt = {"아버지가","방에", "λ“€μ–΄κ°€μ‹ λ‹€" };

        Console.WriteLine("아버지가 방에 λ“€μ–΄κ°€μ‹ λ‹€ => {0}", concat(txt));
    }
}
Output

3 + 4 = 7
아버지가 방에 λ“€μ–΄κ°€μ‹ λ‹€ => 아버지가방에듀어가신닀

Func 와 Action 을 μ‚¬μš©ν•œ 무λͺ…ν•¨μˆ˜ μ„ μ–Έ

  • κΈ°μ‘΄ 읡λͺ…ν•¨μˆ˜ ν˜Ήμ€ 무λͺ…ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜λ €λ©΄ 맀번 λ³„κ°œμ˜ 델리게이트λ₯Ό μ„ μ–Έν•΄μ•Όν•˜λŠ” λ²ˆκ±°λ‘œμ›€μ΄ μžˆλ‹€.
    • MSλŠ” 이λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ .Netν”„λ ˆμž„μ›Œν¬μ— Func 와 Action 델리게이트λ₯Ό 미리 μ„ μ–Έ ν•΄λ‘μ—ˆλ‹€.
    • Func 델리게이트 => κ²°κ³Όλ₯Ό λ°˜ν™˜ν•˜λŠ” λ©”μ†Œλ“œλ₯Ό μ°Έμ‘°ν•˜κΈ° μœ„ν•΄
    • Action 델리게이트 => κ²°κ³Όλ₯Ό λ°˜ν™˜ν•˜μ§€ μ•ŠλŠ” λ©”μ†Œλ“œλ₯Ό μ°Έμ‘°ν•˜κΈ° μœ„ν•΄

Func 델리게이트

  • μ„ μ–Έν˜•μ‹
public delegate TResult Func<out TResult>()
public delegate TResult Func<in T1, out TResult>(T arg)
public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2)

...

16개 인자λ₯Ό κ°€μ§„ κ²ƒκΉŒμ§€..
  • λͺ¨λ“  Func 델리게이트의 ν˜•μ‹ λ§€κ°œλ³€μˆ˜ 쀑 κ°€μž₯ λ§ˆμ§€λ§‰μ— μžˆλŠ” 것이 λ°˜ν™˜ν˜•μ‹μ΄λ‹€.

  • κ°„λ‹¨ν•œ μ‚¬μš©μ˜ˆμ‹œ
Func<int> func1 = () => 10; 
// μž…λ ₯ λ§€κ°œλ³€μˆ˜λŠ” μ—†μœΌλ©° 무쑰건 intν˜•κ°’μΈ 10을 λ°˜ν™˜ 
Console.WriteLine(func1()); // 10 좜λ ₯

Func<int, int> func2 = (x) => x * 2;
//μž…λ ₯ λ§€κ°œλ³€μˆ˜λŠ” int ν˜•μ‹ ν•˜λ‚˜, λ°˜ν™˜ ν˜•μ‹λ„ int
Console.WriteLine(func2(3)); // 6 좜λ ₯

Func<int,int,int> func3 = (x,y) => x + y;
//μž…λ ₯ λ§€κ°œλ³€μˆ˜λŠ” int ν˜•μ‹ λ‘˜, λ°˜ν™˜ ν˜•μ‹λ„ int
Console.WriteLine(func3(2,3)); // 5 좜λ ₯

Func 델리게이트 μ‚¬μš© 예제 μ½”λ“œ
using System;

namespace FuncTest
{
    class MainApp
    {
        static void Main(string[] args)
        {
            Func<int> func1 = () => 10;
            // μž…λ ₯ λ§€κ°œλ³€μˆ˜λŠ” μ—†μœΌλ©° 무쑰건 intν˜•κ°’μΈ 10을 λ°˜ν™˜ 
            Console.WriteLine(func1()); // 10 좜λ ₯

            Func<int, int> func2 = (x) => x * 2;
            //μž…λ ₯ λ§€κ°œλ³€μˆ˜λŠ” int ν˜•μ‹ ν•˜λ‚˜, λ°˜ν™˜ ν˜•μ‹λ„ int
            Console.WriteLine(func2(3)); // 6 좜λ ₯

            Func<int, int, int> func3 = (x, y) => x + y;
            //μž…λ ₯ λ§€κ°œλ³€μˆ˜λŠ” int ν˜•μ‹ λ‘˜, λ°˜ν™˜ ν˜•μ‹λ„ int
            Console.WriteLine(func3(2, 3)); // 5 좜λ ₯
        }
    }
}
Output

10
6
5

Action 델리게이트

  • Action λΈλ¦¬κ²Œμ΄νŠΈλŠ” Func와 거의 λΉ„μŠ·ν•˜λ‹€.
  • 차이점이라 ν•˜λ©΄ Action 델리게이트의 κ²½μš°μ—λŠ” λ°˜ν™˜ ν˜•μ‹μ΄ μ—†λ‹€.

  • μ„ μ–Έν˜•μ‹
public delegate void Action<>()
public delegate void Action<in T>(T1 arg)
public delegate void Action<in T1, in T2>(T1 arg1, T2 arg2)

...

16개 인자λ₯Ό κ°€μ§„ κ²ƒκΉŒμ§€..

Action 델리게이트 μ‚¬μš© 예제 μ½”λ“œ
using System;

namespace ActionTest
{
    class MainApp
    {
        static void Main(string[] args)
        {
            Action act1 = () => Console.WriteLine("Action()");
            act1();

            int result = 0;
            Action<int> act2 = (x) => result = x * x;
            act2(3);


            Console.WriteLine("result : {0}", result);

            Action<double, double> act3 = (x, y) =>
             {
                 double pi = x / y;
                 Console.WriteLine("Action<T1, T2>({0}, {1}) : {2}", x, y, pi);
             };
            act3(22.0, 7.0);
        }
    }
}
Output

Action()
result : 9
Action<T1, T2>(22, 7) : 3.142857142857143

Func & Action 톡합 예제 μ½”λ“œ

using System;

class MainApp
{
    static void Main(string[] args)
    {

        Func<int> func1 = () => 10;
        // μž…λ ₯ λ§€κ°œλ³€μˆ˜λŠ” μ—†μœΌλ©° 무쑰건 intν˜•κ°’μΈ 10을 λ°˜ν™˜ 
        Console.WriteLine(func1()); // 10 좜λ ₯

        Func<int, int> func2 = (x) => x * 2;
        //μž…λ ₯ λ§€κ°œλ³€μˆ˜λŠ” int ν˜•μ‹ ν•˜λ‚˜, λ°˜ν™˜ ν˜•μ‹λ„ int
        Console.WriteLine(func2(3)); // 6 좜λ ₯

        Func<int, int, int> func3 = (x, y) => x + y;
        //μž…λ ₯ λ§€κ°œλ³€μˆ˜λŠ” int ν˜•μ‹ λ‘˜, λ°˜ν™˜ ν˜•μ‹λ„ int
        Console.WriteLine(func3(2, 3)); // 5 좜λ ₯

        Action act1 = () => Console.WriteLine("Action()");
        act1();

        int result = 0;
        Action<int> act2 = (x) => result = x * x;
        act2(3);

        Console.WriteLine("result : {0}", result);

        Action<double, double> act3 = (x, y) =>
        {
            double pi = x / y;
            Console.WriteLine("Action<T1, T2>({0}, {1}) : {2}", x, y, pi);
        };
        act3(22.0, 7.0);
    }
}
Output

10
6
5
Action()
result : 9
Action<T1, T2>(22, 7) : 3.142857142857143

식 트리 (Expression Tree)

  • 식을 트리둜 ν‘œν˜„ν•œ 자료ꡬ쑰
  • Expression 클래슀의 νŒŒμƒν΄λž˜μŠ€λ“€μ„ μ΄μš©ν•΄ μ‹νŠΈλ¦¬λ₯Ό κ΅¬μ„±ν•œλ‹€.

νŒ©ν† λ¦¬ λ©”μ†Œλ“œ(Factory Method)

νŒ©ν† λ¦¬ λ©”μ†Œλ“œλŠ” 클래슀의 μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•˜λŠ” 일을 λ‹΄λ‹Ήν•˜λŠ” λ©”μ†Œλ“œλ₯Ό κ°€λ¦¬ν‚€λŠ” μš©μ–΄μ΄λ‹€.
C#μ—λŠ” 객체λ₯Ό μƒμ„±ν•˜λŠ” μƒμ„±μž λ©”μ†Œλ“œκ°€ μžˆκΈ΄ν•˜μ§€λ§Œ 가끔은 μ΄κ²ƒλ§ŒμœΌλ‘œ μΆ©λΆ„ν•˜μ§€ μ•Šμ„ λ•Œκ°€ μžˆλ‹€. 
객체의 생성에 λ³΅μž‘ν•œ 논리가 ν•„μš”ν•œ 경우, 객체 생성 과정을 λ³„λ„μ˜ λ©”μ†Œλ“œμ— κ΅¬ν˜„ν•΄ λ†“μœΌλ©΄ μ½”λ“œμ˜ λ³΅μž‘λ„λ₯Ό μƒλ‹Ήνžˆ 쀄일 수 μžˆλ‹€.

Expression 클래슀의 정적 νŒ©ν† λ¦¬ λ©”μ†Œλ“œλ“€μ€ Expression 클래슀의 νŒŒμƒ 클래슀인 ConstantExpression,
BinaryExpression 클래슀 λ“±μ˜ μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•˜λŠ” κΈ°λŠ₯을 μ œκ³΅ν•¨μœΌλ‘œμ¨ 수고λ₯Ό 쀄여쀀닀.

1*2+(x-y) 식을 트리둜 λ§Œλ“  ν›„ 컴파일 ν•˜λŠ” 예제 μ½”λ“œ

using System;
using System.Linq.Expressions;

namespace UsingExpressionTree
{
    class MainApp
    {
        static void Main(string[] args)
        {
            // 1 * 2 + (x - y)
            Expression const1 = Expression.Constant(1);
            Expression const2 = Expression.Constant(2);

            Expression leftExp = Expression.Multiply(const1, const2);

            Expression param1 = Expression.Parameter(typeof(int)); // x λ₯Ό μœ„ν•œ λ³€μˆ˜
            Expression param2 = Expression.Parameter(typeof(int)); // y λ₯Ό μœ„ν•œ λ³€μˆ˜

            Expression rightExp = Expression.Subtract(param1, param2);

            Expression exp = Expression.Add(leftExp, rightExp);

            Expression<Func<int,int,int>> expression = Expression<Func<int,int,int>>.Lambda<Func<int,int,int>>(exp, new ParameterExpression[]
            {
                (ParameterExpression)param1,
                (ParameterExpression)param2,
            });

            Func<int,int,int> func = expression.Compile();

            //x = 7, y = 8
            Console.WriteLine("1 * 2 + ({0} - {1}) = {2}", 7, 8, func(7,8));
        }
    }
}
Output

1 * 2 + (7 - 8) = 1

λžŒλ‹€μ‹μ„ μ΄μš©ν•΄μ„œ 식 트리λ₯Ό λ§Œλ“œλŠ” 예제 μ½”λ“œ

using System;
using System.Linq.Expressions;

namespace UsingExpressionTree
{
    class MainApp
    {
        static void Main(string[] args)
        {
            Expression<Func<int,int,int>> expression = (a,b) => 1 * 2 + (a - b);
            Func<int,int,int> func = expression.Compile();

            //x = 7, y = 8
            Console.WriteLine("1 * 2 + ({0} - {1}) = {2}", 7, 8, func(7, 8));
        }
    }
}
Output

1 * 2 + (7 - 8) = 1
  • μ•žμ„œ λ§Œλ“  μ½”λ“œμ— λΉ„ν•΄μ„œ 훨씬 κ°„λ‹¨ν•˜κ²Œ 같은 κ²°κ³Όλ₯Ό λ„μΆœν•  수 μžˆλ‹€.
  • ν•˜μ§€λ§Œ 이 κ²½μš°μ—λŠ” λ™μ μœΌλ‘œ 식 트리λ₯Ό λ§Œλ“€κΈ°λŠ” μ–΄λ ΅λ‹€.
  • Expression ν˜•μ‹μ€ λΆˆλ³€(Immutable)이기 λ•Œλ¬Έμ— ν•œ 번 μΈμŠ€ν„΄μŠ€κ°€ λ§Œλ“€μ–΄μ§€κ³  λ‚œ ν›„μ—λŠ” λ³€κ²½ν•  수 μ—†κΈ° λ•Œλ¬Έμ΄λ‹€.