🎉 berenickt 블로그에 온 걸 환영합니다. 🎉
Lang
Dart
03-함수형 프로그래밍

1. 함수형 프로그래밍

  • Functional Programming
  • 함수형 프로그래밍은 프로그래밍 패러다임 중 하나로, 함수를 값으로 다루는 프로그래밍을 말한다.
1
void main() {
2
List<String> blackPink = ['로제', '제니', '리사', '지수'];
3
4
print(blackPink); // [로제, 제니, 리사, 지수]
5
print(blackPink.asMap()); // {0: 로제, 1: 제니, 2: 리사, 3: 지수}
6
print(blackPink.toSet()); // {로제, 제니, 리사, 지수}
7
8
// Map
9
Map blackPinkMap = blackPink.asMap();
10
print(blackPinkMap.keys); // (0, 1, 2, 3)
11
print(blackPinkMap.values); // (로제, 제니, 리사, 지수)
12
13
// Set
14
Set blackPinkSet = Set.from(blackPink);
15
16
// Set to List
17
print(blackPinkSet.toList()); // [로제, 제니, 리사, 지수]
18
}

2. list 관련 함수

2.1 map

1
void main() {
2
List<String> blackPink = ['로제', '제니', '리사', '지수'];
3
4
final newBlackPink = blackPink.map((element) {
5
return '블랙핑크 $element';
6
});
7
8
print(blackPink); // ['로제', '제니', '리사', '지수']
9
print(newBlackPink.toList()); // ['블랙핑크 로제', '블랙핑크 제니', '블랙핑크 리사', '블랙핑크 지수']
10
11
final newBlackPink2 = blackPink.map((element) => '블랙핑크 $element');
12
print(newBlackPink2.toList()); // ['블랙핑크 로제', '블랙핑크 제니', '블랙핑크 리사', '블랙핑크 지수']
13
14
// map을 쓰면 새로운 Iterable을 반환하기 때문에 == 연산자로 비교하면 항상 false가 나온다.
15
print(blackPink == blackPink); // true
16
print(blackPink == newBlackPink); // false
17
print(newBlackPink == newBlackPink2); // false
18
}

2.2 split

1
void main() {
2
String number = '13579';
3
4
final parsed = number.split('').map((element) => '$element.jpg').toList();
5
6
print(parsed); // [1.jpg, 3.jpg, 5.jpg, 7.jpg, 9.jpg]
7
}

2.3 Map

1
void main() {
2
Map<String, String> haryyPotter = {
3
'Harry Potter': '해리 포터',
4
'Ron Weasley': '론 위즐리',
5
'Hermione Granger': '헤르미온느 그레인저',
6
};
7
8
// MapEntry : Map의 key와 value를 담는 클래스
9
final result = haryyPotter.map(
10
(key, value) => MapEntry(
11
'Hearry Potter Character $key',
12
'해리포터 캐릭터 $value'
13
),
14
);
15
16
print(haryyPotter);
17
print(result);
18
19
final keys = haryyPotter.keys.map((e) => 'HPC $e').toList();
20
final values = haryyPotter.values.map((e) => '해리포터 $e').toList();
21
print(keys);
22
print(values);
23
}

2.4 Set

1
void main() {
2
Set blackPinkSet = {'로제', '제니', '리사', '지수'};
3
4
final newSet = blackPinkSet.map((e) => '블랙핑크 $e').toSet();
5
6
print(newSet);
7
}

2.5 where

1
void main() {
2
List<Map<String, String>> people = [
3
{'name': '로제', 'group': '블랙핑크'},
4
{'name': '지수', 'group': '블랙핑크'},
5
{'name': '사나', 'group': '트와이스'},
6
{'name': '다현', 'group': '트와이스'},
7
];
8
9
print(people);
10
11
// where : 조건에 맞는 요소만 추출
12
final blackPink = people.where((el) => el['group'] == '블랙핑크').toList();
13
final twice = people.where((el) => el['group'] == '트와이스').toList();
14
15
print(blackPink);
16
print(twice);
17
}

2.6 reduce

1
void main() {
2
List<int> numbers = [1, 3, 5, 7, 9];
3
4
// prev : 이전 함수를 실행한 결과 값, 맨 처음은 리스트의 첫번째 값
5
// next : 다음 요소 값
6
final result = numbers.reduce((prev, next) {
7
print('-------------------------------');
8
print('previous: $prev');
9
print('next: $next');
10
print('total: ${prev + next}');
11
12
return prev + next;
13
});
14
15
print(result);
16
}

위 코드를 축약하고, 추가적인 예제를 보자.

1
void main() {
2
List<int> numbers = [1, 3, 5, 7, 9];
3
4
final result = numbers.reduce((prev, next) => prev + next);
5
6
print(result);
7
8
List<String> words = [
9
'안녕하세요',
10
'반갑습니다',
11
'다트 언어',
12
];
13
14
final sentence = words.reduce((prev, next) => prev + next);
15
16
print(sentence);
17
18
// 📌 reduce는 멤버들의 타입과 같은 타입을 return해줘야지만 실행 가능하다.
19
// words.reduce((prev, next) => prev.length + next.length); // Error
20
}

2.7 fold

  • reduce의 멤버들의 타입과 같은 타입을 return해줘야지만 실행 가능한 부분을 제거한 것이 fold다.
  • fold는 초기값을 지정할 수 있다.
1
void main() {
2
List<int> numbers = [1, 3, 5, 7, 9];
3
4
// fold : 맨 처음 시작시 초기값을 설정할 수 있음
5
// 여기서는 0을 맨 처음 초기값으로 설정함
6
final sum = numbers.fold<int>(0, (prev, next) {
7
print('-------------------------------');
8
print('previous: $prev');
9
print('next: $next');
10
print('total: ${prev + next}');
11
12
return prev + next;
13
});
14
15
print(sum); // 25
16
}

위 코드를 축약하고, 추가적인 예제를 보자.

1
void main() {
2
List<int> numbers = [1, 3, 5, 7, 9];
3
4
// fold : 맨 처음 시작시 초기값을 설정할 수 있음
5
// 여기서는 0을 맨 처음 초기값으로 설정함
6
final sum = numbers.fold<int>(0, (prev, next) => prev + next);
7
8
print(sum); // 25
9
10
List<String> words = ['hello', 'world', 'dart'];
11
12
final sentence = words.fold<String>('', (prev, next) => prev + next);
13
print(sentence); // hello world dart
14
15
final count = words.fold<int>(0, (prev, next) => prev + next.length);
16
print(count); // 15
17
}

2.8 cascading operator

  • cascade operator(연쇄 연산자) : 펼침 연산자로도 불린다.
  • 완전히 새로운 리스트에다가 값을 풀어서 추가할 수 있다.
1
void main() {
2
List<int> even = [2, 4, 6, 8];
3
List<int> odd = [1, 3, 5, 7];
4
5
print([...even, ...odd]); // [2, 4, 6, 8, 1, 3, 5, 7]
6
print(even); // [2, 4, 6, 8]
7
print([...even]); // [2, 4, 6, 8]
8
print(even == [...even]); // false
9
}

3. 함수형 프로그래밍 예제

1
void main() {
2
List<Map<String, String>> people = [
3
{'name': '로제', 'group': '블랙핑크'},
4
{'name': '지수', 'group': '블랙핑크'},
5
{'name': '사나', 'group': '트와이스'},
6
{'name': '다현', 'group': '트와이스'},
7
];
8
9
print(people);
10
11
final parsedPeople = people.map(
12
(e) => Person(
13
name: e['name']!, // !는 null 이 아님을 보장
14
group: e['group']!,
15
),
16
).toList();
17
18
print(parsedPeople);
19
20
for(Person p in parsedPeople) {
21
print(p.name);
22
print(p.group);
23
}
24
25
final twices = parsedPeople.where((p) => p.group == '트와이스').toList();
26
print(twices);
27
28
final result = people.map(
29
(e) => Person(
30
name: e['name']!,
31
group: e['group']!,
32
),
33
).where((p) => p.group == '트와이스').toList();
34
print(result);
35
}
36
37
class Person {
38
final String name;
39
final String group;
40
41
Person({
42
required this.name,
43
required this.group,
44
});
45
46
@override
47
String toString() {
48
return 'Person(name: $name, group: $group)';
49
}
50
}
  • 어떤 데이터든 데이터를 다루게 되면, 꼭 구조화를 해가지고 클래스 형태로 다루게 된다.

3.1 함수형 프로그래밍 특징

  • 실행하는 그 대상 리스트나 세트나 맵과 완전히 다른 새로운 값을 생성해준다.
  • 체이닝을 할 수가 있다.
    • 체이닝 : 여러 개의 함수들을 연결해서 사용하는 것
    • 여러 개의 함수들을 체이닝 해가지고 새로운 값을 만들어내면서 매번 새로운 값을 만들 수 있음
  • 함수형 프로그래밍을 하는 굉장히 큰 이유 중 하나가 코드가 간결해지기 때문이다.
    • 그런데 여기에 너무 심취해 가지고 너무 많이 연동을 해버리면,
    • 다른 사람들과 협업을 할 때에는 코드를 이해하기가 어려워진다.
    • 그렇기 때문에 진짜 필요한 기능들만 따로따로 작성을 하고서 코멘트를 달아 준다던가
    • 또는 조금 잘라가지고 잘라가지고 작성을 한다던가 라는 습관을 갖는 것이 좋다.