함수형 프로그래밍
💡 Dart언어에서는 객체 지향뿐만 아니라 함수형 프로그래밍도 지원한다. 또한 java의 iterator와 비슷한 iterable이라는 개념이 있다. iterable은 연속적으로 접근할 수 있는 elements의 collection이다. Map은 순서가 없기 때문에 iterable이 아니다. 하지만 기본 구현체가 linkedHashMap으로 순서가 있으므로 iterable로 변경이 된다.
1. 일반적으로 Collection끼리 변경하기
void main() {
List<String> members = ['a','b','c','d','e'];
print(members); //[a, b, c, d, e]
print(members.asMap()); //{0: a, 1: b, 2: c, 3: d, 4: e}
print(members.toSet()); //{a, b, c, d, e}
Map<int,String> map = members.asMap();
Map map2 = members.asMap();
print(map.keys.toList());//[0, 1, 2, 3, 4]
print(map.values.toList());//[a, b, c, d, e]
Set membersSet = members.toSet();
Set<String> membersSet2 = members.toSet();
print(membersSet.toList()); //[a, b, c, d, e]
}
2. map 사용하기(List);
void main() {
List<String> members = ['a','b','c','d','e'];
//iterable 형태로 나옴
final funcMembers = members.map((x){
return '멤버 $x';
});
print(funcMembers); //(멤버 a, 멤버 b, 멤버 c, 멤버 d, 멤버 e)
//arrow사용
final funcMembers2 = members.map((x) => '멤버 $x');
print(funcMembers2);
//문자열을 리스트로 나눠서 만들기
String number = '12345';
final list = number.split('').map((x) => '숫자$x').toList();
print(list);//[숫자1, 숫자2, 숫자3, 숫자4, 숫자5]
}
3. map 사용하기(Map, Set);
Dart에서 map의 구현체는 기본적으로 LinkedHashMap이다. Dart에서 Set의 구현체는 기본적으로 LinkedHashSet이다.
void main() {
Map<String, String> menu = {
'pizza' : '10000',
'hamburger' : '5000',
'coke' : '2000'
};
final Map result = menu.map((key, value) => MapEntry(
'menu:$key',
'price:$value'
));
print(result);//{menu:pizza: price:10000, menu:hamburger: price:5000, menu:coke: price:2000}
final keys = menu.keys.map((x) => '키 = $x');
final values = menu.values.map((x) => '밸류 = $x');
print(values.runtimeType); // EfficientLengthMappedIterable<String, String>
print(keys); //(키 = pizza, 키 = hamburger, 키 = coke)
print(values); //(키 = pizza, 키 = hamburger, 키 = coke)
Set menuSet = {
'pizza','hamburger','coke'
};
final result2 = menuSet.map((x) => 'menu=$x').toSet();
print(result2); //{menu=pizza, menu=hamburger, menu=coke}
}
4. where 사용하기
필터링하는 함수
void main() {
List<Map<String,int>> menu = [
{
'pizza' : 10000,
'hamburger': 5000,
'coke' : 2000
},
{
'ramen': 3000,
'rice' : 1000,
'coke' : 2000
},
{'coke' : 3000}
];
print(menu); //[{pizza: 10000, hamburger: 5000, coke: 2000}, {ramen: 3000, rice: 1000, coke: 2000}, {coke: 3000}]
print(menu.where((x) => x['coke'] == 2000)); //({pizza: 10000, hamburger: 5000, coke: 2000}, {ramen: 3000, rice: 1000, coke: 2000}, {coke: 2000})
}
5. reduce 사용하기
reduce사용시엔 변수의 타입과 return값의 타입이 일치해야한다.
void main() {
List<int> numbers = [1,3,5,7,9];
//return 값이 있고 prev에 이전 return 값을 저장한다.
//next에는 다음 idx의 값을 의미한다.
final result = numbers.reduce((prev, next){
print('=========');
print('previus= $prev');
print('next= $next');
print('total = ${prev+next}');
return prev + next;
});
print(result);
/*=========
previus= 1
next= 3
total = 4
=========
previus= 4
next= 5
total = 9
=========
previus= 9
next= 7
total = 16
=========
previus= 16
next= 9
total = 25
25*/
print(numbers.reduce((prev,next)=> prev+next));//25
List<String> words = ['hello' , 'dart', 'world!'];
print(words.reduce((prev,next) => '$prev $next')); //hello dart world!
}
6. fold 사용하기
fold는 reduce와 달리 변수의 타입과 return값의 타입이 같지 않아도 된다. 또한 fold의 시작점은 초기값부터다.
reduce의 출력값과 비교해보면 reduce는 4번 출력
fold는 5번 출력된것을 알 수 있다.
void main() {
List<int> numbers = [1,3,5,7,9];
final result = numbers.fold(0,(prev, next){
print('=========');
print('previus= $prev');
print('next= $next');
print('total = ${prev+next}');
return prev + next;
});
print(result);
/*
* =========
previus= 0
next= 1
total = 1
=========
previus= 1
next= 3
total = 4
=========
previus= 4
next= 5
total = 9
=========
previus= 9
next= 7
total = 16
=========
previus= 16
next= 9
total = 25
25
* */
List<String> words = [
'hello', 'Dart', 'World!'
];
final sentence = words.fold<String> ('',(prev,next) => '$prev $next'); // hello Dart World! 앞에 공백이 먼저 들어옴
print(sentence);
final length = words.fold<int>(0, (prev,next) => prev+next.length);
print(length);
}
7. cascading operator
… 를 이용하여 값을 풀어 넣을 수 있다.
void main() {
List<int> even = [2,4,6,8];
List<int> odd = [1,3,5,7];
print([even,odd]);
// [[2, 4, 6, 8], [1, 3, 5, 7]]
print([...even,...odd]);
// [2, 4, 6, 8, 1, 3, 5, 7]
print([...even]);
// [2, 4, 6, 8]
print(even == [...even]);
// false
}
8. 객체사용해서 활용하기
void main() {
List<Map<String,String>> menu = [
{
'food' : 'pizza',
'price': '10000',
},
{
'food' : 'hamburger',
'price' : '5000',
},
{
'food' : 'coke',
'price' : '2000',
}
];
print(menu);
// [{food: pizza, price: 10000}, {food: hamburger, price: 5000}, {food: coke, price: 2000}]
final parsedMenu = menu.map(
(x) => Menu(
food : x['food'],// 객체 속성에 null 할당시엔 ! 안붙여도됨
price : x['price']!) //객체 속성에 null불가시엔 ! 붙여야함.
).toList();
print(parsedMenu);
// [Menu판:pizza 10000), Menu판:hamburger 5000), Menu판:coke 2000)]
// for(Menu menu in parsedMenu) {
// print(menu.food);
// print(menu.price);
// }
// 5000원 이상인 음식 출력하기
print(parsedMenu.where((x) => int.parse(x.price) >= 5000).toList());
// [Menu판:pizza 10000), Menu판:hamburger 5000)]
}
class Menu{
final String? food;
final String price;
Menu({required this.food, required this.price});
@override
String toString() {
return 'Menu판:$food $price)';
}
}
비동기 프로그래밍
다트언어는 비동기 프로그래밍을 지원한다.
1. Future
비동기를 하는동안 값을 받을 때까지 기다지 않고 다음 작업을 진행한다.
void main() {
//Future - 미래에 받아올 값, 비동기로 받아올 값
Future<String> name = Future.value('홍길동');
Future<int> number = Future.value(1);
Future<bool> flag = Future.value(true);
// 첫번째 파라미터, 지연할 시간
// 두번째 파라미터, 지연시간이 지난 후 실행할 함수
Future.delayed(Duration(seconds: 3),(){
print('First Delay 끝');
});
addNumbers(10,20);
addNumbers(30,40);
}
void addNumbers(int number1, int number2) {
print('Second 계산 전 시작');
//case : 서버에 요청하는 상황
Future.delayed(Duration(seconds: 1), (){
print('Third 계산 중: $number1 + $number2 = ${number1+number2}');
});
print('Fourth 계산 완료');
}
// Second 계산 전 시작
// Fourth 계산 완료
// Second 계산 전 시작
// Fourth 계산 완료
// Third 계산 중: 10 + 20 = 30
// Third 계산 중: 30 + 40 = 70
// First Delay 끝
2. async , await
비동기를 하는동안 값을 받을 때까지 기다릴 수가 있다. 그러나 CPU가 놀고 있는게 아니라 다른 작업을 진행하며 기다린다.
void main() {
//Future - 미래에 받아올 값, 비동기로 받아올 값
Future<String> name = Future.value('홍길동');
Future<int> number = Future.value(1);
Future<bool> flag = Future.value(true);
// 첫번째 파라미터, 지연할 시간
// 두번째 파라미터, 지연시간이 지난 후 실행할 함수
Future.delayed(Duration(seconds: 3),(){
print('First Delay 끝');
});
addNumbers(10,20);
addNumbers(30,40);
}
void addNumbers(int number1, int number2) async{
print('Second 계산 전 시작');
//case : 서버에 요청하는 상황
await Future.delayed(Duration(seconds: 1), (){
print('Third 계산 중: $number1 + $number2 = ${number1+number2}');
});
print('Fourth 계산 완료');
}
// Second 계산 전 시작
// Second 계산 전 시작
// Third 계산 중: 10 + 20 = 30
// Fourth 계산 완료
// Third 계산 중: 30 + 40 = 70
// Fourth 계산 완료
// First Delay 끝
3. void async,await
Future를 리턴해주는 함수만 await가 가능하다.
함수가 완료될 때까지 기다렸다가 다음 함수를 진행한다.
await가 없을 시엔 완료여부와 상관없이 다음 함수를 진행한다.
void main() async{
//Future - 미래에 받아올 값, 비동기로 받아올 값
Future<String> name = Future.value('홍길동');
Future<int> number = Future.value(1);
Future<bool> flag = Future.value(true);
// 첫번째 파라미터, 지연할 시간
// 두번째 파라미터, 지연시간이 지난 후 실행할 함수
Future.delayed(Duration(seconds: 3),(){
print('First Delay 끝');
});
await addNumbers(10,20);
await addNumbers(30,40);
}
//void => Future<void>
Future<void> addNumbers(int number1, int number2) async{
print('Second 계산 전 시작');
//case : 서버에 요청하는 상황
await Future.delayed(Duration(seconds: 1), (){
print('Third 계산 중: $number1 + $number2 = ${number1+number2}');
});
print('Fourth 계산 완료');
}
// Second 계산 전 시작
// Third 계산 중: 10 + 20 = 30
// Fourth 계산 완료
// Second 계산 전 시작
// Third 계산 중: 30 + 40 = 70
// Fourth 계산 완료
// First Delay 끝
4. return 값이 있을 때
void main() async{
//Future - 미래에 받아올 값, 비동기로 받아올 값
Future<String> name = Future.value('홍길동');
Future<int> number = Future.value(1);
Future<bool> flag = Future.value(true);
// 첫번째 파라미터, 지연할 시간
// 두번째 파라미터, 지연시간이 지난 후 실행할 함수
final result1 = await addNumbers(10,20);
final result2 = await addNumbers(20,40);
print('main첫번쨰 $result1');
//addNumebers(10,20);
//await를 안붙이고 함수 실행시엔 Instance of '_Future<int>'로 값을 받기전에 출력해버림
print('main두번째 $result2');
}
//void => Future<void>
Future<int> addNumbers(int number1, int number2) async{
print('Second 계산 전 시작');
//case : 서버에 요청하는 상황
await Future.delayed(Duration(seconds: 2), (){
print('Third 계산 중: $number1 + $number2 = ${number1+number2}');
});
print('Fourth 계산 완료');
return number1+number2;
}
// Second 계산 전 시작
// Third 계산 중: 10 + 20 = 30
// Fourth 계산 완료
// Second 계산 전 시작
// Third 계산 중: 30 + 40 = 70
// Fourth 계산 완료
// First Delay 끝
5. Stream
💡 Future represents a one-time value: the app performs an operation and comes back with some data. A Stream represents a sequence of data. 출처: https://www.kodeco.com/32851541-dart-futures-and-streams
그래도 잘 이해가 안되서 스택오버플로우를 보니 Future는 현재 사용할 수 없는 값이지만 나중에 사용할 수 있고, 변경되지 않는 값에 사용된다. stream은 시간이 지남에 따라 일부 값이 변경 될 때 사용한다. 즉 Future는 한번만 리턴하고, stream은 리턴이 계속해서 바뀔 수 있다.
import 'dart:async';
void main() {
final controller = StreamController();
// final stream = controller.stream;
//한번만 리스닝할 수 있음(리스닝 하나만)
final stream = controller.stream.asBroadcastStream();
final streamListener1 = stream.listen((val){
print('listener : $val');
});
//값들을 listening
controller.sink.add(1);
controller.sink.add(2);
controller.sink.add(3);
controller.sink.add(4);
controller.sink.add(5);
final streamListener2 = stream.listen((val){
print('Listener 2 : $val');
});
}
/*
*
* listener : 1
Listener 2 : 1
listener : 2
Listener 2 : 2
listener : 3
Listener 2 : 3
listener : 4
Listener 2 : 4
listener : 5
Listener 2 : 5
* */
6. stream과 함수형 프로그래밍
import 'dart:async';
void main() {
final controller = StreamController();
//case : 짝수 값만 리스닝하라
//where 사용하기
final stream = controller.stream.asBroadcastStream();
final streamListener1 = stream.where((val)=> val%2 == 0).listen((val){
print('짝수만 : $val');
});
controller.sink.add(1);
controller.sink.add(2);
controller.sink.add(3);
controller.sink.add(4);
controller.sink.add(5);
final streamListener2 = stream.where((val) => val%2 ==1).listen((val){
print('홀수만 : $val');
});
}
/*
* 홀수만 : 1
짝수만 : 2
홀수만 : 3
짝수만 : 4
홀수만 : 5
*/
7. 함수로 stream 사용하기
import 'dart:async';
void main() {
calculate(2).listen((val){
print('calculate(1) : $val');
});
}
//1초마다 loop를 돌면서 값을 받는다.
Stream<int> calculate(int number) async* {
for(int i = 0; i < 5; i++) {
yield number * i;
await Future.delayed(Duration(seconds: 1));
}
}
/*
calculate(1) : 0
calculate(1) : 2
calculate(1) : 4
calculate(1) : 6
calculate(1) : 8
*/
8. yield*
import 'dart:async';
void main() {
playAllStream().listen((val){
print(val);
});
}
//1초마다 loop를 돌면서 값을 받는다.
Stream<int> calculate(int number) async* {
for(int i = 0; i < 5; i++) {
yield number * i;
await Future.delayed(Duration(seconds: 1));
}
}
//yield* 은 Future의 await처럼
//함수의 모든 값을 다받고 나서 실행을 한다.
Stream<int> playAllStream() async* {
yield* calculate(1);
yield* calculate(10);
}
/*
0
1
2
3
4
0
10
20
30
40
*/
출처 : https://velog.io/@keemeesuu/Flutter-Dart.-Functional-Programming함수형-프로그래밍 ,
인프런_[코드팩토리][입문]Dart 언어 4시간만에 완전정복
'일기' 카테고리의 다른 글
Flutter 설치하기 (0) | 2023.01.10 |
---|---|
Dart 공부하기_ 기본문법,OOP (0) | 2023.01.09 |
"AI와 이야기하기: ChatGPT로 새로운 인공지능을 체험해보자" (3) | 2022.12.17 |
@Builder패턴 사용시에 nullpointException 고치기 (0) | 2022.08.01 |