develop/Flutter

Dart에서 Currying 사용하기 - 커링 함수사용하여 코드 질 높이기

방뎁 2024. 11. 18. 12:53

Currying 함수란?

Currying은 여러 개의 인자를 받는 함수(multi-argument function)를
하나의 인자를 받는 함수들의 연속으로 변환하는 프로그래밍 기법
즉, 하나의 함수가 한 번에 모든 인자를 받는 대신,
각각의 인자를 별도로 받아 새로운 함수를 반환하는 방식으로 동작한다.

이 방식은 복잡한 함수 호출을 간결하게 표현하고,
특정 인자 값을 고정(fixed)하여 재사용성을 높이는 데 유용
커링은 함수형 프로그래밍에서 자주 사용하는 기법으로,
Dart에서도 클로저와 함수 반환을 통해 구현할 수 있어 활용도가 높음


Currying의 기본 개념

// 일반적인 함수
int add(int a, int b) {
  return a + b;
}

// Currying 방식
Function addCurry(int a) {
  return (int b) => a + b;
}

void main() {

  var add7 = add(7, 10); // 인자 고정 안됨
  print(add7); // 17

  var add5 = addCurry(5); // 첫 번째 인자를 고정
  print(add5(10)); // 15
  print(add5(20)); // 25
}
  • 일반 함수: add(5, 10)과 같이 호출.
  • Curried 함수: addCurry(5)(10) 또는 첫 번째 인자를 고정한 add5(10) 형태로 호출.

Dart에서 Currying의 이점

  1. 재사용성 증가: 특정 인자를 고정하여 새 함수로 활용.
  2. 코드 가독성 향상: 복잡한 함수 호출을 간결한 형태로 분리 가능.
  3. 함수형 프로그래밍 스타일: 체이닝이나 조합(composition)에 유리.

Dart에서 Currying 구현

Dart에서 Currying은 클로저(closure)를 활용하여 구현됨

1. 기본적인 Currying 사용법

두 개의 인자를 받는 곱셈 함수를 커링 방식으로 변환 할 수 있음

Function multiply(int a) {
  return (int b) => a * b;
}

void main() {
  var doubleValue = multiply(2); // a를 2로 고정
  print(doubleValue(10)); // 20
  print(doubleValue(15)); // 30
}

위 코드에서 doubleValueb만 필요로 하는 새로운 함수가 됨


2. 여러 인자를 가진 함수에서의 Currying

다중 인자를 처리하는 경우,
여러 단계를 통해 인자를 하나씩 처리하도록 구현할 수 있음

Function addThreeNumbers(int a) {
  return (int b) {
    return (int c) => a + b + c;
  };
}

void main() {
  var add10And5 = addThreeNumbers(10)(5); // a=10, b=5를 고정
  print(add10And5(3)); // 18
}

여기서 addThreeNumbers(10)(5)ab를 고정한 새로운 함수로,
c만 입력받아 계산함

3. 특정 인자 고정 (Partial Application)

Currying은 특정 인자 값을 고정하는 데 유용
이를 Partial Application이라고도 부름

Function subtract(int a) {
  return (int b) => a - b;
}

void main() {
  var subtractFrom10 = subtract(10); // a를 10으로 고정
  print(subtractFrom10(3)); // 7
  print(subtractFrom10(7)); // 3
}

subtract(10)은 항상 10에서 빼는 함수를 생성

4. 커링을 활용한 체이닝

Currying은 체이닝 방식으로 복잡한 로직을 단순화할 때도 유용

Function compose(Function f, Function g) {
  return (x) => f(g(x));
}

void main() {
  var addTwo = (int x) => x + 2;
  var multiplyByThree = (int x) => x * 3;

  var addThenMultiply = compose(multiplyByThree, addTwo); // 3 * (x + 2)
  print(addThenMultiply(5)); // 21
}

위 코드에서 compose를 통해 함수 두 개를 결합하여 사용함

5. 실전 예제: 커링과 데이터 필터링

실제로 데이터를 필터링하거나 가공할 때 커링이 특히 유용함

Function filterBy(String field) {
  return (String value) {
    return (List<Map<String, String>> items) {
      return items.where((item) => item[field] == value).toList();
    };
  };
}

void main() {
  var data = [
    {'name': 'Alice', 'role': 'admin'},
    {'name': 'Bob', 'role': 'user'},
    {'name': 'Charlie', 'role': 'admin'}
  ];

  var filterByAdmin = filterBy('role')('admin'); // 'role''admin'인 데이터 필터링
  print(filterByAdmin(data)); // [{'name': 'Alice', 'role': 'admin'}, {'name': 'Charlie', 'role': 'admin'}]
}

filterBy 룰 당장 가져다 쓸 수 있음


커링 사용시 주의점!

  • 복잡성 증가: 너무 많은 단계로 나누면 가독성이 떨어질 수 있다.
  • 성능 고려: 불필요하게 많은 함수를 생성하면 메모리 사용이 증가할 수 있디.
  • Dart의 문법적 제약: Dart는 완전한 함수형 언어는 아니기 때문에 일부 함수형 프로그래밍 개념을 제한적으로만 구현할 수 있다.

재사용 가능한 코드와 가독성을 제공하지만, 단점이 있다.
클로저와 함께 사용하여 특정 값을 고정하거나 복잡한 로직을 간단한 단계로 나눌 수 있다.
남용하지 않도록 조심하여 사용하면 코드 리뷰때 칭찬 받을 수 있다

고차 함수 컬렉션 등등은 앞 포스팅에서 살펴볼 수 있다.

2024.11.17 - [develop/Flutter] - Dart의 고급 함수를 적절히 사용하여 코드 퀄리티 높이기

 

Dart의 고급 함수를 적절히 사용하여 코드 퀄리티 높이기

1. 고차 함수 (Higher-order Functions)고차 함수는 다른 함수를 인자로 받거나, 함수를 반환하는 함수임Dart에서는 고차 함수를 사용하여 코드를 더욱 유연하고 재사용 가능하게 만들 수 있음1.1 함수 전

devfart.tistory.com