모닝스터디 , 동기 프로그래밍 , 비동기 프로그래밍 , 과제 풀이
// 2단 부터 9단까지
for (int j = 1; j <= 9; j++) {
mulTable(j);
}
mulTable(3);
mulTable(7);
}
/// 특정 단수의 구구단을 출력하는 함수!
///
/// [number] : 출력할 단 번호
///
void mulTable(int number) {
// ===== 함수 시작 =====
for (int i = 1; i <= 9; i++) {
print('$number * $i = ${number * i}');
}
// ===== 함수 끝 =====
/// 회문 판별
///
/// 회문 : 글자를 앞에서 읽으나 뒤에서 읽으나 같은 글자
///
/// ex) level, eye, kayak, noon, reviver
///
void main() {
// print(isPalindrome("level"));
print(isPalindrome("reviver"));
// print(isPalindrome("test"));
}
/// 문자열을 입력받아서
///
/// 회문(Palinderome)이면 true
///
/// 아니면 false
///
/// 반환하는 함수
bool isPalindrome(String str) {
// ====== 함수 시작 =====
// "level"
// 1. 문자열을 한글자씩 새로운 리스트에 뒤집어서 담기 -> 뒤집은 문자의 리스트와 기존 리스트 비교
// => "level"이라는 문자열의 경우 5 + 5 번의 반복이 이루어짐
// => 한글자씩 담긴 리스트 (길이 5) + 뒤집은 문자열 담은 리스트 (길이 5) => 길이 10정도 필요
// 2. 문자열 한글자씩 새로운 리스트에 담기 -> 리스트 반복하면서 첫글자와 끝글자 비교
// => 반복문에서 i 번째 글자와 (리스트 길이 - i - 1) 번째 글짜 비교
// => "level"이라는 문자열의 경우 = 2번의 반복이 이루어짐
// => 한글자씩 담긴 리스트 (길이 5) => 길이 5정도 필요
// 공간복잡도 : 메모리 공간을 얼마나 차지하는지 (RAM과 관련)
// 시간복잡도 : 얼마나 걸렸는지 (CPU와 관련)
// 2번 방법으로 진행
List<String> charList = str.split(''); // 한 글자씩 분리하고 싶을때는 빈 문자열 "split"
int halfIndex = charList.length ~/ 2; // 2로 나눴을 때 나머지
for (var i = 0; i < halfIndex; i++) {
// i => 0
// 첫글자, 끝글자
// reviver
String char1 = charList[i];
int endIndex = charList.length - 1 - i;
String char2 = charList[endIndex];
if (char1 != char2) {
return false;
}
}
return true; // 끝까지 비교하기 위해 if문 밖에 true 씀
// ===== 함수 끝 =====
}
void introduce(String name) {
print('자기소개 시작 !');
print('안녕 ? 나는 $name ~');
print('자기소개 완료 !');
}
void main() {
introduce('강미래');
introduce('강현재');
}
/*
자기소개 시작 !
안녕 ? 나는 강미래 ~
자기소개 완료 !
자기소개 시작 !
안녕 ? 나는 강현재 ~
자기소개 완료 !
*/
무조건 introduce(’강미래’); → introduce(’강현재’); → introduce(’강과거’);
순서로 실행된답니다 !
작업이 완료될 때까지 기다리지 않고, 미래의 특정 시점에 값을 반환
결과값이 나올 때까지 멈춰 있지 않고, 수행할 수 있는 다른 작업을 찾아서 수행하기 때문에 작업 진행 중 다른 작업이 가능하다.
시간이 오래 걸리는 작업을 처리할 때 사용
Dart로도 라이브러리를 사용해서 비동기 할 수 있음
dart:async에 있는 Future,Stream 같은 클래스를 통해 비동기 프로그래밍 지원
dart:async에 포함되어 있음)Future 클래스를 사용하려면 impor 'dart:async'; 를 해야 하지만, dart:core 가 Future 클래스를 사용할 수 있도록 해주기 때문에 따로 dart:async 를 import 하지 않아도 됨Future<int> number = Future.value(1);
Future<String> name = Future.value('강미래');
Future<bool> isOddNumber = Future.value(1.isOdd);
Future<int> 는 우리가 알고 있는 int,
Future<String> 은 우리가 알고 있는 String,
Future<bool> 은 우리가 알고 있는 bool 이랑 같은데
단지 Future 가 감싸고 있을 뿐이라고 생각하면 돼요
void main() {
int seconds = 2;
print('실행 시작 !');
Future.delayed(Duration(seconds: seconds));
print('실행 끝 !');
}
/*
실행 시작 !
실행 끝 !
*/
void main() {
int seconds = 2;
print('실행 시작 !');
Future.delayed(Duration(seconds: seconds), () {
print('$seconds초 다 기다림 !');
});
print('실행 끝 !');
}
/*
실행 시작 !
2초 다 기다림 !
실행 끝 !
*/
void introduce(String name) {
print('$name의 자기소개 시작 !');
Future.delayed(Duration(seconds: 2), () {
print('안녕 ? 나는 $name ~');
});
print('$name의 자기소개 끝 !');
}
void main() {
introduce('강미래');
}
/*
강미래의 자기소개 시작 !
강미래의 자기소개 끝 !
안녕 ? 나는 강미래 ~
*/
2초 기다리는 동안 강미래의 자기소개 끝 ! 이 출력되는 것을 통해
2초 기다리는 동안 멈춰 있지 않고 다른 연산이 실행되는 것을 알 수 있어요 👀
만약 이 코드가 동기적으로 동작했다면 2초동안 멈춰 있었겠죠 ?
void introduce(String name) {
print('$name의 자기소개 시작 !');
Future.delayed(Duration(seconds: 2), () {
print('안녕 ? 나는 $name ~');
});
print('$name의 자기소개 끝 !');
}
void main() {
introduce('강미래');
introduce('강현재');
}
/*
강미래의 자기소개 시작 !
강미래의 자기소개 끝 !
강현재의 자기소개 시작 !
강현재의 자기소개 끝 !
안녕 ? 나는 강미래 ~
안녕 ? 나는 강현재 ~
*/
2초 기다리는 동안 수행할 수 있는 다른 작업을 찾아서 수행하는군요 🤭 (제법 부지런해 ~)
void introduce(String name) async {
print('$name의 자기소개 시작 !');
await Future.delayed(Duration(seconds: 2), () {
print('안녕 ? 나는 $name ~');
});
print('$name의 자기소개 끝 !');
}
void main() {
introduce('강미래');
}
/*
강미래의 자기소개 시작 !
안녕 ? 나는 강미래 ~
강미래의 자기소개 끝 !
*/
가능해요 !!
비동기적인 코드를 동기적인 코드처럼 순서대로 실행되도록 하고 싶은 경우에는
비동기적인 코드 앞에 await 를 붙이고,
await 를 붙인 코드를 포함하는 함수에 async 를 붙이면 돼요 🙂
참고로, await 는 Future 류의 코드 앞에만 붙일 수 있어요.
async 랑 await 는 비동기적인 코드를 동기적인 코드처럼 보이게 만든답니다.
void introduce(String name) async {
print('$name의 자기소개 시작 !');
await Future.delayed(Duration(seconds: 2), () {
print('안녕 ? 나는 $name ~');
});
print('$name의 자기소개 끝 !');
}
void main() {
introduce('강미래');
introduce('강현재');
}
/*
강미래의 자기소개 시작 !
강현재의 자기소개 시작 !
안녕 ? 나는 강미래 ~
강미래의 자기소개 끝 !
안녕 ? 나는 강현재 ~
강현재의 자기소개 끝 !
*/
아뇨 !! 그러면 비동기 프로그래밍 이 아니라 동기 프로그래밍 이었겠죠 !!
이 코드의 실행 결과를 보면 2초 기다리는 동안 멈춰 있지 않다는 것을 알 수 있어요 🤭
2초 기다리는 동안 다음 작업인 introduce('강현재'); 가 실행되고 있거든요 ~~
Future<void> introduce(String name) async {
print('$name의 자기소개 시작 !');
await Future.delayed(Duration(seconds: 2), () {
print('안녕 ? 나는 $name ~');
});
print('$name의 자기소개 끝 !');
}
void main() async {
await introduce('강미래');
await introduce('강현재');
}
/*
강미래의 자기소개 시작 !
안녕 ? 나는 강미래 ~
강미래의 자기소개 끝 !
강현재의 자기소개 시작 !
안녕 ? 나는 강현재 ~
강현재의 자기소개 끝 !
*/
이렇게 하면 작성한 순서대로 실행된답니다 !
마찬가지로, 2초 기다리는 동안 멈춰 있지 않고 수행할 수 있는 다른 작업이 있다면
그 작업을 수행해요. 왜냐 ? 비동기적으로 동작하니까 ~~
await 는 Future 류의 코드 앞에만 사용할 수 있다고 했기 때문에
introduce() 의 반환 타입을 Future<void> 로 바꿔 주어야 하고,
await 를 포함하는 함수에는 반드시 async 를 붙여야 한다고 했기 때문에
main() 에 async 를 붙여 주어야 해요.
introduce() 는 반환값이 없기 때문에 (return 구문이 없기 때문에)
Future<void> 타입으로 해주는 것이겠죠 🙂
async 함수 안에 await 가 여러 번 있어도 돼요.
Future<String> introduce(String name) async {
print('$name의 자기소개 시작 !');
await Future.delayed(Duration(seconds: 2), () {
print('안녕 ? 나는 $name ~');
});
print('$name의 자기소개 끝 !');
return '이름은 $name ~';
}
void main() async {
var riverfuture = await introduce('강미래');
var riverpresent = await introduce('강현재');
print('나의 $riverfuture 너의 $riverpresent');
}
/*
강미래의 자기소개 시작 !
안녕 ? 나는 강미래 ~
강미래의 자기소개 끝 !
강현재의 자기소개 시작 !
안녕 ? 나는 강현재 ~
강현재의 자기소개 끝 !
나의 이름은 강미래 ~ 너의 이름은 강현재 ~
*/
그럼요 !!
Future<타입> 으로 반환 타입을 바꾸고, 그 반환 타입에 맞는 return 구문만 써주면 끝 !!
dart:async에 포함된 클래스Steam<int> number = Steam.value(1);
Steam<String> name = Steam.value('강미래');
Steam<bool> isOddNumber = Steam.value(1.isOdd);
Steam<int> 는 우리가 알고 있는 int,
Steam<String> 은 우리가 알고 있는 String,
Steam<bool> 은 우리가 알고 있는 bool 이랑 같은데
단지 Steam 이 감싸고 있을 뿐이라고 생각하면 돼요 🙂
-하나의 작업에 대해 값이나 이벤트가 여러번 발생하는 경우
사용자가 버튼 누를때마다 그 이벤트를 받아오는 경우
Stream<String> emitNames() async* {
yield '강미래';
yield '강현재';
yield '강과거';
}
Future 를 반환 타입으로 갖는 함수는 async 를 사용했지만,
Stream 을 반환 타입으로 갖는 함수는 async* 을 사용해요.
이때 yield 옆에 있는 값의 타입이랑 Stream<타입> 에 있는 타입 이 같아야겠죠 ?
참고로, Stream 에서는 반환 대신 방출 이라는 단어를 사용한답니다 !
Stream<String> emitNames(List<String> names) async* {
for (var i = 0; i < names.length; i++) {
yield '${i + 1}번째는 ${names[i]} ~';
}
}
매개변수나 반복문을 사용해서 방출할 수도 있어요 🤩
return과 같은 개념yald를 통해 방출되는 값을 받기 위해 사용하는 메서드Stream<String> emitNames() async* {
yield '강미래';
yield '강현재';
yield '강과거';
}
void main() {
int number = 1;
emitNames().listen((name) {
print('$number번째는 $name ~');
number += 1;
});
}
/*
1번째는 강미래 ~
2번째는 강현재 ~
3번째는 강과거 ~
*/
emitName() 에서 yield 를 통해 방출되는 값들이
순서대로 하나씩 listen 의 매개변수인 name 에 들어가요.
Stream<String> emitNames(List<String> names) async* {
for (var i = 0; i < names.length; i++) {
yield '${i + 1}번째는 ${names[i]} ~';
}
}
void main() {
List<String> names = ['강미래', '강현재', '강과거'];
emitNames(names).listen((element) {
print(element);
});
}
/*
1번째는 강미래 ~
2번째는 강현재 ~
3번째는 강과거 ~
*/
Stream<int> emitNumbers(int first) async* {
for (var i = first; i >= 0; i--) {
yield i;
}
}
void main() {
emitNumbers(10).listen((number) {
print(number);
});
}
/*
10
9
8
7
6
5
4
3
2
1
0
*/
main() 에 await 가 포함되어 있지 않으니까 async 를 써줄 필요가 없겠죠 ?
지금까지는 for 문의 증감식에 i++ 만 썼지만 .. 응용해 보았습니다 ! 🧐
Stream<int> emitNumbers(int first) async* {
for (var i = first; i >= 0; i--) {
yield i;
}
}
void main() {
print('숫자 놀이 시작 !');
emitNumbers(10).listen((number) {
print(number);
});
print('숫자 놀이 끝 !');
}
/*
숫자 놀이 시작 !
숫자 놀이 끝 !
10
9
8
7
6
5
4
3
2
1
0
*/
No !! 비동기적으로 동작한다구요 ~~
emitNumbers() 가 실행되기 전에 숫자 놀이 끝 ! 이 출력되는 것을 보면
그렇지 않다는 것을 알 수 있죠 😅
Stream<int> emitNumbers(int first) async* {
for (var i = first; i >= 0; i--) {
yield i;
await Future.delayed(Duration(seconds: 1));
}
}
void main() {
emitNumbers(10).listen((number) {
print(number);
});
}
/*
10
9
8
7
6
5
4
3
2
1
0
*/
물론이죠 ~~ 아까 배웠던 Future.delayed() 를 사용하면 된답니다 👍🏼
yield i; 를 통해 i 를 방출하고 나서 1초를 기다리겠네요 !
emitNumbers() 옆에 async* 이 있기 때문에 함수 안에 await 가 있을 수 있겠네요 !
아 ! 여러분은 방금 타이머를 만드셨습니다 ㅋㅋ 1초에 숫자가 1씩 감소하고 있으니까 ~~
Stream<int> emitNumbers(int first) async* {
for (var i = first; i >= 0; i--) {
yield i;
await Future.delayed(Duration(seconds: 1));
}
}
void main() {
emitNumbers(10).listen((number) {
print('[타이머 1] $number');
});
emitNumbers(10).listen((number) {
print('[타이머 2] $number');
});
}
/*
[타이머 1] 10
[타이머 2] 10
[타이머 1] 9
[타이머 2] 9
[타이머 1] 8
[타이머 2] 8
[타이머 1] 7
[타이머 2] 7
[타이머 1] 6
[타이머 2] 6
[타이머 1] 5
[타이머 2] 5
[타이머 1] 4
[타이머 2] 4
[타이머 1] 3
[타이머 2] 3
[타이머 1] 2
[타이머 2] 2
[타이머 1] 1
[타이머 2] 1
[타이머 1] 0
[타이머 2] 0
*/
실행시키면 타이머 1 이랑 타이머 2 가 번갈아 가면서 출력돼요 !
이것을 통해 비동기적으로 동작하고 있다는 것을 알 수 있어요 👀
Stream<int> emitNumbers(int first) async* {
for (var i = first; i >= 0; i--) {
yield i;
await Future.delayed(Duration(seconds: 1));
}
}
void main() {
emitNumbers(10).where((number) => number.isOdd).listen((number) {
print('[홀수 타이머] $number');
});
emitNumbers(10).where((number) => number.isEven).listen((number) {
print('[짝수 타이머] $number');
});
}
/*
[짝수 타이머] 10
[홀수 타이머] 9
[짝수 타이머] 8
[홀수 타이머] 7
[짝수 타이머] 6
[홀수 타이머] 5
[짝수 타이머] 4
[홀수 타이머] 3
[짝수 타이머] 2
[홀수 타이머] 1
[짝수 타이머] 0
*/
방출되는 값에 고차 함수 를 적용할 수도 있답니다 !
