Higher Order Function

2022. 9. 14. 00:00CSharp/Functional Programming

반응형

Higher Order Function (HOF) 는 하나 이상의 함수를 인수로 취하거나, 함수를 반환하거나, 둘 모두를 수행하는 함수다. 우리는 정수나 문자열과 같은 단순한 객체 또는 컬렉션 및 사용자 정의 유형과 같은 더 복잡한 객체를 함수 매개변수로 전달하는 데 익숙하다. 그러나 C#은 HOF도 잘 지원한다. 이것은 delegate 와 람다 식을 사용하여 수행된다.

 

일단 delegate 와 lambda 에 대해 간단히 알아보자

x 와 y 의 값을 입력 받아 둘을 더해 그 결과값을 return 하는 method 를 만든다고 생각해 보자

 

delegate 를 통해 이 method 를 위임받아 처리 하려면 다음과 같이 한다. 

private delegate int Add(int x, int y); //delegate 선언
public void Test()
{
    Add add = AddMethod; // Add delegate 에 AddMethod 할당
    var result = add(1, 2); // delegate 를 통해 실행
    Console.WriteLine(result);
}

public int AddMethod(int x, int y)
{
    return x + y;
}

똑같은 내용을 lambda 식을 사용하여 처리 해보자 . lambda 식을 사용할때는 Func<> 을 이용한다. 

public void Test()
{
    Func<int,int,int> add = ( x, y) => x + y; //lambda 식을 Func 에 할당
    //var add = (int x, int y) => x + y;  // var 을 통해 할당 받을때는 타입을 지정한다.
    var result = add(1, 2); // 실행
    Console.WriteLine(result);
}

delegate 에 대한 자세한 내용은 다음 강좌를 참고하자

https://yogingang.tistory.com/24

 

Delegate (대리자)

특정 매개 변수 목록 및 반환 형식이 있는 메서드에 대한 참조를 나타내는 형식. 접근제어자 + delegate + 반환형 + 대리자명 + (전달인수) public delegate int PerformCalculation(int x, int y); 대리자는 C++..

yogingang.tistory.com

lambda 에 대한 자세한 내용은 다음 강좌를 참고하자

https://yogingang.tistory.com/26

 

Action, Func, Lambda

Action 매개 변수가 없고 값을 반환하지 않는 메서드를 캡슐화 한 대리자 (delegate) Action 매개 변수가 하나이고 값을 반환하지 않는 메서드를 캡슐화 한 대리자 (delegate) Action   매개 변수가 n 개이고

yogingang.tistory.com

https://yogingang.tistory.com/27

 

Lambda advanced

C# 10 에서 lambda 개선 사항 Attribute 속성이 있는 람다 허용 속성은 람다 식 및 람다 매개 변수에 추가할 수 있다. 메서드 특성과 매개변수 특성 간의 모호성을 피하기 위해 특성이 있는 람다 표현식

yogingang.tistory.com

 

이제 기본적인 개념을 알았다고 가정하고 HOF 에 대해 알아 보겠다.

그전에 LINQ 를 사용해 본적이 있다면  HOF 를 사용해 본적이 있다고 말할 수 있겠다.

var numbers = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var even = numbers.Where(x => x % 2 == 0);
// => [2, 4, 6, 8]
Func<int, bool> isOdd = x => x % 2 != 0; // assign lambda expression to Func
var odd = numbers.Where(isOdd);
// => [1, 3, 5, 7, 9]

여기서 HOF 의 좋은 예는 바로 Where 이다.  Where 는 다음과 같이 선언되어 있다.

public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)

Where 는 함수를 매개변수를 받게 되어있다. (predicate)

이를 이용해서 호출하는 쪽에서는 결과에 포함하거나 제외해야 하는 항목을 function 을 통해 전달 할 수 있다.

 

이번에는 함수를 반환 하는 경우를 예로 들어보자

Func<int, bool> IsDivisible(int n)
{
    return x => x % n == 0;
}
public void ReturnFunc()
{
    var numbers = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    var num3 = numbers.Where(IsDivisible(3));
    // => [3, 6, 9]
}

이미 정의된 Function 의 Interface 를 변경 할 수도 있다. 

(처음 Functional Progrmming 을 접하게되면 지금까지  많은 편견들을 가지고 있었다는 것에 놀라움을 느끼게 된다.

명령형 programming 에서

static type 형태의 language 를 익히고

compile 과 build 가 필요한 환경에서

OOP 위주의 교육을 받았던 programmer 들이라면 더욱 공감할 것이다.

이제 아래 내용을 확인하면 자신이 아주 좁은 경계에서 프로그래밍을 익혔다는 것을 알게 될것이다.

세상은 빠르게 변해가고 programming 도 변하고 있다.....아니 이미 변하였다.)

 

다음과 같은 Function 이 있다고 하자 이 Function 은 2개의 입력값을 받아서 x 를 y 로 나눈 몫을 return 한다.

Func<int, int, int> divide = (x, y) => x / y;
divide(6, 2); // => 3

이제 divide 라는 함수 자체를 변경하지 않고 x 와 y 의 순서를 바꾸어 실행되도록 변경해 보자

public static class SwapExtensions
{
    public static Func<T2, T1, R> Swap<T1, T2, R>(this Func<T1, T2, R> f)
    => (t2, t1) => f(t1, t2);
}
var divideBy = divide.Swap();
divideBy(2, 6); // => 3

Swap 는 Function 을 인수로 받아서 T2,T1 으로 들어온 값을 T1,T2 로 변경하여 인수로 받은 Function 을 실행 시킨다.

Functional Programming 에 익숙한 사람들은 그냥 일상적인 내용이겠지만

처음 익히는 경우는 놀라운 광경이다....

(Functional Programming 만세 삼창을 한 후 오늘 하루를 마무리 짓자!!)

 

관련 영상

https://youtu.be/dK2j5jeWg78

 

반응형