Group Study (2023-2024)/Flutter

[Flutter] 1주차 스터디_Functional programming 과 Asychronous programming

k_____w___ 2023. 11. 6. 18:12

Functional programming

  • 프로그램을 함수들로 구성하는 프로그래밍 패러다임
  • 함수는 인자를 받아 결과를 반환하는 것으로 구성되며, 상태와 상태의 변화가 없는 함수를 사용하는 것을 권장

Iterable 데이터 타입에 활용할 수 있는 대표적인 function

1. Iterable 사이의 형 변환

  • List → Map : asMap()
    • .keys/.values → Iterable
  • List → Set : toSet() 또는 Set.from(listName)
    • 자동으로 중복 제거
  • Map/Set → List : toList()

2. map

  • 각 요소에 함수를 적용할 수 있으며, 각 요소를 새로운 값으로 대체해줌
    • 단, 원본이 변경되는 것이 아니고 새로운 리스트를 만들어냄
  • 함수의 파라미터로 함수를 전달 → map(() {}) 또는 map(() ⇒ ) 의 형태
//List에서 활용
void main() {
  List<String> blackPink = ['로제', '지수', '리사', '제니'];

  final newBlackPink = blackPink.map((x) {
    return '블랙핑크 $x';
  });

  final newBlackPink2 = blackPink.map((x) => '블랙핑크 $x');
//newBlackPink,newBlackPink2의 데이터 타입은 Iterable<String>

  print(blackPink == newBlackPink);
//false
  print(newBlackPink == newBlackPink2);
//false

//Map에서 활용
Map<String, String> blackPink = {
    'Rose': '로제',
    'Jisu': '지수',
    'Lisa': '리사',
    'Jennie': '제니'
  };

//key와 value 모두를 파라미터로 전달
//MapEntry: Map을 return 해주는 메소드
  final result = blackPink
      .map((key, value) => MapEntry('BlackPink member $key', '블랙핑크 멤버 $value'));

//key와 value을 따로 mapping 하고 싶을때
  final key = blackPink.keys.map((x) => 'BP $x').toList();
  final values = blackPink.values.map((x) => '블랙핑크 $x').toList();

//Set는 위의 예시와 동일하게 활용 가능
}

 

3. where

  • 일종의 필터링 함수
    • 특정 조건을 만족하는 요소를 찾을 수 있음
void main() {
  List<Map<String, String>> people = [
    {'name': '로제', 'group': '블랙핑크'},
    {'name': '지수', 'group': '블랙핑크'},
    {'name': 'RM', 'group': 'BTS'},
    {'name': 'V', 'group': 'BTS'},
  ];

  final blackPink = people.where((x) => x['group'] == '블랙핑크');
  final bts = people.where((x) => x['group'] == '블랙핑크');
}

 

4. reduce와 fold

  • 각각의 값들을 mapping 해가면서 그 값들의 total을 구할 때 활용
  • 원본 리스트와 리턴되는 값의 데이터 타입이 동일해야함
  • 데이터 타입이 다르다면 fold 사용
void main() {
  List<int> numbers = [1, 3, 5, 7, 9];

  //첫번째: list의 첫번째, 두번째 값이 각각 prev, next
  //두번째: 이전의 return 값이 prev, list의 세번쨰 값이 next
  final result = numbers.reduce((prev, next) => prev + next);

  List<String> words = ['안녕하세요', '플러터', '스터디'];

  final sentence = words.reduce((prev, next) => prev + next);

  //error 발생
  //words는 String, return 값은 int
  words.reduce((prev, next) => prev.length + next.length);

  //fold
  //return 값이 무엇인지 generic하게 명시
  //첫번째 파라미터가 첫번째 prev가 됨
  numbers.fold<int>(0, (prev, next) => prev + next);

  words.fold<int>(0, (prev, next) => prev + next.length);
}

 

5. spread 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 == [...even]); //false
}

 

 

Asynchronous programming

  • 프로그램이 요청한 작업이 완료되기를 기다리지 않고 또 다른 작업을 수행하는 것
    • 장점:
      •  전체 작업 처리 시간 단축시킬 수 있음 -> 성능, 응답성 향상
      • 시스템 자원을 효율적으로 사용할 수 있음
    • 예시:
      • 서버에 작업 요청
      • 네트워크를 통해 데이터 가져오기
      • 데이터베이스에 쓰기
      • 파일에서 데이터 읽기
    • 비동기 프로그래밍을 하지 않으면 이전의 작업이 완료될 때까지 기다려야 하므로, 시스템이 응답하지 않는 것 처럼 보일 수 있음

 

Future

  • 비동기 작업의 결과를 나타내며, Uncompleted와 Completed 라는 2가지 상태를 가짐

  • delayed
    • 1번 파라미터: 지연할 시간 Duration
    • 2번 파라미터: 지연 시간이 지난 후 실행할 함수
//Synchronos
void main() {
	addNumbers(1, 1);
	addNumbers(2, 2);
}

void addNumbers(int number1, int number2) {
	print('계산중: $number1 + $number2');
	print('계산 완료: ${number1 + number2}');
}



//Asynchoronous programming
void main() {
  addNumbers(1, 1);
  addNumbers(2, 2);
}

void addNumbers(int number1, int number2) {
  print('계산시작: $number1 + $number2');

  Future.delayed(Duration(seconds: 2), () {
    print('계산완료: $number1 + $number2 = ${number1 + number2}');
  });

  print('함수완료: $number1 + $number2');
}

///결과
///계산시작: 1 + 1
///함수완료: 1 + 1
///계산시작: 2 + 2
///함수완료: 2 + 2
///계산완료: 1 + 1 = 2
///계산완료: 2 + 2 = 4
  • Future function
    • 함수의 본문 앞에 async 키워드를 추가
    • 해당 작업이 끝나기까지 기다릴 코드에 await 키워드를 추가
      • await 키워드는 async 함수 내에서만 사용 가능
    • addNumber 함수 내에서, await 함수를 기다려서 계산 시작 → 계산 완료 → 함수 완료 순서로 출력
    • 그러나 전체 코드의 관점에서 보면 await를 기다리는 동안 수행할 수 있는 다음 코드를 수행하여
      addNumbers(1, 1)이 끝나기 전에 addNumbers(2,2)가 수행됨
void main() {
	addNumbers(1,1);
	addNumbers(2,2);
}

void addNumbers(int number1, int number2) async {
  print('계산시작: $number1 + $number2');
	await Future.delayed(Duration(seconds: 2), () {
		print('계산완료: $number1 + $number2 = ${number1 + number2}');	
  });
	print('함수완료: $number1 + $number2');
}
///결과
///계산 시작: 1 + 1
///계산 시작: 2 + 2
///계산완료: 1 + 1 = 2
///함수완료: 1 + 1
///계산완료: 2 + 2 = 4
///함수완료: 2 + 2
  • addNumber(1, 1)이 끝난 후, addNumbers(2,2)가 실행되도록 하기 위해서는 addNumbers를 Future를 return 하는 함수로 만들고, await addNumber로 작성
void main() async {
	await addNumbers(1,1);
	await addNumbers(2,2);
}

Future<void> addNumbers(int number1, int number2) async {
  print('계산시작: $number1 + $number2');
	await Future.delayed(Duration(seconds: 2), () {
		print('계산완료: $number1 + $number2 = ${number1 + number2}');	
  });
	print('함수완료: $number1 + $number2');
}

///계산시작: 1 + 1
///계산완료: 1 + 1 = 2
///함수완료: 1 + 1
///계산시작: 2 + 2
///계산완료: 2 + 2 = 4
///함수완료: 2 + 2

 

Stream

  • 일종의 비동기 Iterable
    • 연속된 데이터를 listen()을 통해 비동기적으로 처리
      • future: 사진에 적합/stream: 영상에 적합
    • 일련의 비동기 이벤트, 요청시 다음 이벤트를 가져오는 대신 이벤트가 준비되었을 때 스트림이 이벤트가 있다는 것을 알려줌
    • stream을 닫을 때까지 무한대로 결과값을 받아낼 수 있음

  • stream에서 이벤트를 받고싶을 때 listen()
import 'dart:async';

//단일 stream: single subscription
void main() {
	//StrramController: stream에 이벤트를 직접 지정
  final controller = StreamController();
  final stream = controller.stream;

  final streamListener1 = stream.listen((val) {
    print('Listener 1: $val');
  });

  controller.sink.add(1);
  controller.sink.add(2);
  controller.sink.add(3);
}

//stream을 여러개 만들기: broadcast
void main() {
  final controller = StreamController();
  final stream = controller.stream.asBroadcastStream();

  final streamListener1 = stream.listen((val) {
    print('Listener 1: $val');
  });

  final streamListener2 = stream.listen((val) {
    print('Listener 2: $val');
  });

  controller.sink.add(1);
  controller.sink.add(2);
  controller.sink.add(3);
}
  • Stream function
    • async → async*
    • await → yield
    • yield* : stream의 모든 결과값이 나올때까지 대기
import 'dart:async';

void main() {
  playAllStream().listen((val) {
    print(val);
  });
}

Stream<int> playAllStream() async* {
  yield* calculate(1);
  yield* calculate(1000);
}

Stream<int> calculate(int number) async* {
  for (int i = 0; i < 5; i++) {
    yield i * number;
    await Future.delayed(Duration(seconds: 1));
  }
}

///결과
///0
///1
///2
///3
///4
///0
///1000
///2000
///3000
///4000

 

 

참고자료