Functional Programming 의 기본은 형변환이다.
List, Map, Set 같은 타입으로 진행을 한다.
[]
: 리스트 타입
()
: List-"[]" 와 비슷한 타입의 형태이다. 주로 toList()를 사용해 리스트 타입으로 반환한다.
Dart에서는 iterable이라는 개념이 있다. iterable은 연속적으로 접근할 수 있는 elements의 collection이다. 즉, 반복이 가능한 그룹을 뜻하고 list나 array 등을 의미한다.
Map은 순서가 없기 때문에 iterable이 아니다. 하지만 linked Map은 순서가 있으니 iterable이다.
void main() {
List<String> blackPink = ['로제', '지수', '리사', '제니', '제니'];
print(blackPink);
// [로제, 지수, 리사, 제니, 제니]
print(blackPink.asMap());
// {0: 로제, 1: 지수, 2: 리사, 3: 제니, 4: 제니}
print(blackPink.toSet());
// {로제, 지수, 리사, 제니}
Map blackPinkMap = blackPink.asMap();
print(blackPinkMap.keys);
// (0, 1, 2, 3, 4)
// "()" -> Iterable<dynamic> 형태는 List-"[]" 와 비슷한 형태이다.
// 다른형태로 바꾸기 쉽다. 실질적으로 사용하진 않고 리스트 형태로 바꿔준다.
print(blackPinkMap.keys.toList());
// [0, 1, 2, 3, 4]
print(blackPinkMap.values.toList());
// [로제, 지수, 리사, 제니, 제니]
Set blackPinkSet = Set.from(blackPink);
print(blackPinkSet.toList());
// [로제, 지수, 리사, 제니]
}
map
메소드는 iterable(배열그룹)를 대상으로 foreach돌린다.
void main() {
List<String> blackPink = ['로제', '지수', '리사', '제니', '제니'];
// List의 map method
// 함수에다 함수를 넣어준다.
// blackPink의 리스트들이 x parameter에 들어간다.
final newBlackPink = blackPink.map((x) {
return '블랙핑크 $x';
});
print(blackPink);
// [로제, 지수, 리사, 제니, 제니]
print(newBlackPink.toList());
// [블랙핑크 로제, 블랙핑크 지수, 블랙핑크 리사, 블랙핑크 제니, 블랙핑크 제니]
// 아래와 같이 Arrow함수로 간단하게 표현 가능
final newBlackPink2 = blackPink.map((x) => '블랙핑크 $x');
print(newBlackPink2.toList());
// [블랙핑크 로제, 블랙핑크 지수, 블랙핑크 리사, 블랙핑크 제니, 블랙핑크 제니]
print(blackPink == blackPink); // true
print(blackPink == newBlackPink); // false
print(newBlackPink == newBlackPink2); // false
}
void main() {
// [1.jpg, 3.jpg, 5.jpg, 7.jpg, 9.jpg]
String number = '13579';
final parsed = number.split('').map((x) => '$x.jpg').toList();
print(parsed);
// [1.jpg, 3.jpg, 5.jpg, 7.jpg, 9.jpg]
}
void main() {
Map<String, String> harryPotter = {
'Harry Potter': '해리 포터',
'Ron Wesley': '론 위즐리',
'Hermione Granger': '허마이오니 그레인저'
};
// 1. map 을 mapping 하여 새로은 map을 만들 때
// 이해를 돕기위한 예제,
// map을 map으로 바꾸는 일은 거의 없다.
final result2 = harryPotter.map(
(key, value) => MapEntry(
'영어로 $key',
'한글로 $value',
),
);
print(result2);
// {영어로 Harry Potter: 한글로 해리 포터, 영어로 Ron Wesley: 한글로 론 위즐리, 영어로 Hermione Granger: 한글로 허마이오니 그레인저}
// 2. key or value 값들을 리스트로 변경하고 싶을 때
// 자주 사용하는 예제
final keys = harryPotter.keys.map((x) => '한글로 $x').toList();
final values = harryPotter.values.map((x) => '영어로 $x').toList();
print(keys);
// [한글로 Harry Potter, 한글로 Ron Wesley, 한글로 Hermione Granger]
print(values);
// [영어로 해리 포터, 영어로 론 위즐리, 영어로 허마이오니 그레인저]
}
void main() {
Set blackPinkSet = {
'로제',
'지수',
'제니',
'리사',
};
final newSet = blackPinkSet.map((x) => '블랙핑크 $x').toSet();
print(newSet);
}
배열요소를 필터링 한다.
void main() {
List<String> fruits = ["banana", "apple", "strawberry"];
final result1 = fruits.where((x) => x == "banana");
final result2 = fruits.where((x) => x != "banana");
print(result1);
// (banana)
print(result2);
// (apple, strawberry)
}
void main() {
List<Map<String, String>> people = [
{
'name': '로제',
'group': '블랙핑크'
},
{
'name': '지수',
'group': '블랙핑크'
},
{
'name': 'RM',
'group': 'BTS'
},
{
'name': '뷔',
'group': 'BTS'
},
];
print(people);
// [{name: 로제, group: 블랙핑크}, {name: 지수, group: 블랙핑크}, {name: RM, group: BTS}, {name: 뷔, group: BTS}]
final blackPink = people.where((x) => x['group'] == '블랙핑크').toList();
print(blackPink);
// [{name: 로제, group: 블랙핑크}, {name: 지수, group: 블랙핑크}]
}
배열을 누적시켜 반환한다.
void main() {
List<int> numbers = [1, 3, 5, 7, 9];
// reduce()
// parameter prev의 초기값은 0 이고
// 다음 prev 부터는 이전 return 값을 받는다.
final result = numbers.reduce((prev, next) {
print('-----------------');
print('previous : $prev');
print('next : $next');
print('total : ${prev + next}');
return prev + next;
});
/*
-----------------
previous : 1
next : 3
total : 4
-----------------
previous : 4
next : 5
total : 9
-----------------
previous : 9
next : 7
total : 16
-----------------
previous : 16
next : 9
total : 25
*/
print(result);
// 25
}
void main() {
List<String> words = [
'오늘은 ',
'토요일 ',
'입니다.'
];
final result = words.reduce((prev, next) => prev + next);
print(result);
// 오늘은 토요일 입니다.
}
리듀스가 실행할 수 없는 경우의 수가 있다.
리듀스로 반환되는 타입이 리스트 리턴타입과 같아야 한다.
void main() {
List<String> words = [
'오늘은 ',
'토요일 ',
'입니다.'
];
final sentence1 = words.reduce((prev, next) => prev + next);
print(sentence1);
// 오늘은 토요일 입니다.
final sentence2 = words.reduce((prev, next) => prev.length + next.length);
// error - String 타입으로 받아야하는데 length가 int값
}
reduce
의 단점(무조건 같은 타입을 리턴해줘야하는)을 보안(?)한 메소드.
기능적으로 fold
함수로 reduce
함수를 만들수도 있다.
즉, 아무형태나 리턴가능한게 장점이다.
void main() {
// 기능적으로, fold함수로 reduce함수 만들기
List<int> numbers = [1, 3, 5, 7, 9];
// fold의 리턴값을 정해줘야 한다. 제너릭으로 리턴값 제공.
// 첫번째 parameter는 시작값. 즉, prev에 들어간다.
final sum = numbers.fold<int>(0, (prev, next) {
print('-------------');
print('prev : $prev');
print('next : $next');
print('total : ${prev + next}');
return prev + next;
});
/*
-------------
prev : 0
next : 1
total : 1
-------------
prev : 1
next : 3
total : 4
-------------
prev : 4
next : 5
total : 9
-------------
prev : 9
next : 7
total : 16
-------------
prev : 16
next : 9
total : 25
*/
print(sum);
}
reduce와 다르게 리턴값을 아래 예제와 같이 지정할 수 있다.
void main() {
List<String> words = [
'오늘은 ',
'토요일 ',
'입니다.'
];
final result = words.fold<String>('', (prev, next){
return prev+next;
});
print(result);
// 오늘은 토요일 입니다.
final count = words.fold<int>(0, (prev, next){
return prev + next.length;
});
print(count);
// 12
}
여러개의 리스트를 합칠때 많이 사용한다.
void main() {
List<int> even = [2, 4, 6, 8];
List<int> odd = [1, 3, 5, 7];
// cascading operator
// ...
print([even, odd]);
// [[2, 4, 6, 8], [1, 3, 5, 7]]
print([...even, ...odd]);
// [2, 4, 6, 8, 1, 3, 5, 7]
print(even == [...even]);
// false
}
void main() {
// json 형태를 클래스로 변환작업 하는 이유
// 1.map은 구조화가 안되어있고 자유도가 너무 높아서
// 구조화를 하기 위해
// list map의 데이터 형태를 클래스로 변환하는 작업.
// 2. json 형태를 클래스로 변환. 즉, 구조화를 하여 신뢰할 수 있는 상태로 만든다.
// 1. json 형태의 자료
final List<Map<String, String>> people = [
{'name': '지수', 'group': '블랙핑크'},
{'name': '로제', 'group': '블랙핑크'},
{'name': 'RM', 'group': 'BTS'},
{'name': '뷔', 'group': 'BTS'},
];
print(people);
// [{name: 지수, group: 블랙핑크}, {name: 로제, group: 블랙핑크}, {name: RM, group: BTS}, {name: 뷔, group: BTS}]
// 3. json형태 -> 구조화 클래스 mapping 작업.
// 이렇게 구조화를 해놓으면 다양하게 사용 할 수 있다.
// Person 파싱.
final parsedPeople = people.map((x) {
return Person(
name: x['name']!,
group: x['group']!,
);
}).toList();
print(parsedPeople);
// [Person(name:지수, group:블랙핑크), Person(name:로제, group:블랙핑크), Person(name:RM, group:BTS), Person(name:뷔, group:BTS)]
// 4-1. 유용한 예제
// "print." 하면 어떤 값들이 있는지
// suggestion(추천단어)가 떠서 직관적으로 볼 수 있다.
// 에러를 줄일 수 있다.
for(Person person in parsedPeople){
print(person.name);
print(person.group);
}
// 4-2. 유용한 예제
// 특정값 출력
final bts = parsedPeople.where(
(x) => x.group == 'BTS',
);
print(bts);
// (Person(name:RM, group:BTS), Person(name:뷔, group:BTS))
// 4-2를 아래와 같이 표현 가능.
// 이렇게 이어붙이기 가능(계속 가능).
final result = people.map(
(x) => Person(
name: x['name']!,
group: x['group']!,
),
).where((x) => x.group == 'BTS');
print(result);
// (Person(name:RM, group:BTS), Person(name:뷔, group:BTS))
}
// 2. 구조화 클래스 제작
class Person {
final String name;
final String group;
Person({required this.name, required this.group});
// toString - 부모클래스 Object에 제공되는 기본함수
String toString(){
return 'Person(name:$name, group:$group)';
}
}
functional Programming 의 기본은
OOP와 Functional Programming 둘 중 뭐가 좋다가 아닌 두개의 장단점을 활용하여 사용하자.
[Reference]
Dart #3 Functional Programming 함수형 프로그래밍
[Flutter/Drat]다트 유용한 메소드 Method 함수 Function 정리~
[Dart/Flutter] 유용한 메서드, 함수 정리
[Dart] iterable 의미와 Collection 의 종류