Dart 기본 문법 03

GreenBean·2023년 4월 3일
0
post-thumbnail

Dart

Dart 공식문서
다트 ( Dart ) 함수
다트 ( Dart ) 연산자

Dart 의 함수

  • 다트는 완전한 객체 지향 언어
    • 다트에서는 모든 것이 객체이기 때문에 함수도 객체
  • 함수가 객체이기 때문에 갖는 특징 존재
    • 변수가 함수를 참조할 수 있음
    • 함수의 인자로 함수를 전달할 수도 있음
  • 선택 매개변수 ( Option Parameter ) 존재
    • 선택 매개변수는 또 두가지로 나누어짐
      • 이름 있는 선택 매개변수 ( Named Optional Parameter )
      • 위치적 선택 매개변수 ( Positional Optional Parameter )
  • 익명 함수 ( Anonymous Function ) 지원
    • 람다식 ( Lambda Expression ) 지원
  • 다트 함수의 특징 요약
    • 변수가 함수 참조 가능
    • 다른 함수의 인자로 함수 전달 가능
    • 이름 있는 선택 매개변수
    • 위치적 선택 매개변수
    • 익명 함수 및 람다

변수가 함수 참조 가능

// 타입 변수명 = 함수() {}
var name = getName() {}
  • var 타입 name 변수가 문자열을 리턴해주는 getName() 함수를 참조
    • 그러면 name 은 문자열을 값으로 가지기 때문에 String 타입이 됨
    • 흔하게 사용하는 함수 호출의 형태
# main.dart

main() {
  int a = 10;
  int b = 5;
  var name = getName();
  
  print("Name is $name");
  print("$a + $b = ${add(a, b)}");
  print("$a - $b = ${sub(a, b)}");
}

getName() {
  retern "Kim";
}

int add(int a, int b) {
  return a + b;
}

int sub(a, b) {
  return a - b;
}

------------------------------------------------------------------------------------------

# 결과

Name is Kim
10 + 5 = 5
10 - 5 = 5
  • 위 코드의 add() 함수를 보면 함수 리턴 타입과 매개변수 타입이 지정되어 있음
    • 하지만 sub() 함수에서는 타입이 지정되지 않음
    • 생략 가능하긴 하지만 가급적 명시하는 것이 좋음

다른 함수의 인자로 함수 전달 가능

// 함수A(함수B(), 함수C()) {}
getName(getFirstName(), getLastName()) {}
  • 아래 코드를 보면 multi() 함수의 첫번째 매개변수에 add(a, b)를 두번째 매개변수에 sub(a, b)를 넘겨주었음
    • 그러면 multi() 함수의 첫번째 인자는 add(a, b) 리턴값인 15가 되고 두번째 인자는 sub(a, b) 의 리턴값인 5가 됨
    • 그리고 두 인자를 곱한 값이 multi() 함수의 리턴값이 됨
# main.dart

int add(int a, int b) {
  return a + b;
}

int sub(a, b) {
  return a - b;
}

int multi(int a, int b) {
  return a * b;
}

main() {
  int a = 10;
  int b = 5;
  
  print("${a + b} & ${a - b} = ${multi(add(a, b), sub(a, b)}");
}

------------------------------------------------------------------------------------------

# 결과

15 * 5 = 75

이름 있는 선택 매개변수

// 함수(타입 매개변수명A, {타입 매개변수명B, 타입 매개변수명C=초기값}) {}
getAdress(String city, {String district, String zipCode="012345"})
  • 함수 호출 시 매개변수에 인자값을 넘겨줄 때 매개변수명을 이용하여 인자값을 넘겨줄 수 있음
    • 매개변수명으로 인자값을 넘겨줄 매개변수는 {} 로 감싸줘야 함
  • 아래 코드는 기본 형태에서 약간 변형을 주었음
    • district ∙ zipCode 를 선택 매개변수로 지정했고 zipCode 는 초기값을 가지도록 함
# main.dart

String getAddress(String city, {String district, String zipCode="012345"}) {
  return "$city시 $district구 $zipCode";
}

main() {
  print("${getAddress('서울', district: '강남')}");
  print("${getAddress('서울', district: '강남', zipCode: "543210")}");
  // print("${getAddress(district: '강남', zipCode: "543210")}");
}

------------------------------------------------------------------------------------------

# 결과

서울시 강남구 012345
서울시 강남구 543210
  • 첫번째 print() 함수처럼 zipCode 에 대한 인자값이 없으면 초기값을 사용
    • 두번째 print() 함수처럼 새로운 인자값을 넘겨주면 그 값으로 할당
    • 마지막 주석 처리된 세번째 print() 함수처럼 필수 매개변수와 혼용할 때는 기본 매개변수에 대한 인자값을 누락하면 안됨

위치적 선택 매개변수

// 함수(타입 매개변수명A, [타입 매개변수명B=초기값, 타입 매개변수명C=초기값]) {}
getAdress(String city, [String district="강남", String zipCode="012345"]) {}
  • 이름 있는 선택 매개변수 에서 zipCode 의 초기값을 지정한 형태를 살펴봤는데 그와 비슷한 개념
    • 위치적 선택 매개변수는 미리 초기값을 정해놓고 함수 호출 시 해당 매개변수에 인자값을 넘겨주지 않으면 초기값을 사용
    • 선언 방법은 선택 매개변수 지정을 {} 대신 [] 로 하는 것이 차이점
# main.dart

String getAddress(String city, [String district="강남", String zipCode="012345"]) {
  return "$city시 $district구 $zipCode";
}

main() {
  print("${getAddress('서울')}");
  print("${getAddress('서울', '서초')}");
}

------------------------------------------------------------------------------------------

# 결과

서울시 강남구 012345
서울시 서초구 012345
  • 초기값을 변경하고 싶으면 새로운 인자값을 넘겨주면 됨
    • 주의할 점은 이름 있는 선택 매개변수와 마찬가지로 필수 매개변수는 꼭 인자값을 줘야하고 매개변수 위치를 꼭 고려해야 함
    • 예를 들어 위 코드의 getAddress('서울', '54321') 와 같이 호출하면 zipCode 가 변경되는 것이 아니라 district 가 변경됨

익명 함수 및 람다식

# 익명 함수

// (매개변수명) { 표현식; } ;
(a, b) { a + b; } ;
# 람다식

// (매개변수명) => 표현식;
(a, b) => a - b;
  • 익명 함수와 람다식의 차이점을 보면 결국 {};=> 로 변경한 것
# main.dart

int add(int a, int b) {
  return a + b;
}

var multi = (a, b) {
  return a + b;
};

sub(a, b) => a - b;

main() {
  int a = 10;
  int b = 5;
  
  print("$a + $b = ${add(a, b)}");
  print("$a * $b = ${multi(a, b)}");
  print("$a - $b = ${sub(a, b)}");
}

------------------------------------------------------------------------------------------

# 결과

10 + 5 = 15
10 * 5 = 15
10 - 5 = 5

Dart 의 연산자

  • 다트는 다수의 전통적인 프로그래밍 언어에서 사용하는 기본 연산자를 동일하게 제공
    • 추가적으로 모던 언어에서 차용한 연산자도 제공하고 있음

산술 연산자

  • 거의 모든 프로그래밍에서 제공하는 기본적인 연산자
    • +-*/~/%++--
      • ~/ : 정수를 결과값으로 가짐
      • % 연산자가 나머지를 구한다면 ~/ 연산자는 몫을 구하는 연산자
# 예시

double divide(a, b) {
  return a / b;
}

int divideQuotient(a, b) {
  return a ~/ b;
}

int divideRemainder(a, b) {
  return a % b;
}

main() {
  int a = 11;
  int b = 5;
  
  print("$a / $b = ${divide(a, b)}");
  print("$a ~/ $b = ${divideQuotient(a, b)}");
  print("$a % $b = ${divideRemainder(a, b)}");
}

------------------------------------------------------------------------------------------

# 결과

11 / 5 = 2.2
11 ~/ 5 = 2
11 % 5 = 1

할당 연산자

  • =+=-=*=/=~/=
    • += 는 좌항 자신의 값에 우항의 값을 더한다는 의미
# 예시

main() {
  int a = 10;
  int b = 10;
  a = a+ 5;
  b += 5;
  
  print("a = $a");
  print("b = $b");
}

------------------------------------------------------------------------------------------

# 결과

a = 15
b = 15

관계 연산자 ( 비교 연산자 )

  • 관계 연산자는 비교 연산자라고도 함
    • 조건문에서 사용
    • ==!=><>=<=
# 예시

main() {
  int a = 10;
  int b = 20;
  
  if (a == b) {
    print("a == b");
  } else {
    print("a != b");
  }
}

------------------------------------------------------------------------------------------

# 결과

a != b

비트 & 시프트 연산자

  • 특정 로직을 수행할 때 더 빠른 속도를 낼 수 있으며 로우 레벨에서 레지스트리를 다룰 때 유용
    • & ( AND ) ∙ | ( OR ) ∙ ^ ( XOR ) ∙ ~ ( NOT ) ∙ <<>>
# 예시

main() {
  int a = 0x03; // 0011
  int b = 0x01; // 0001
  
  print("a = $a");
  print("a << 1 = ${a << 1}");
  print("a >> 1 = ${a >> 1}");
  print("a & 1 = ${a & 1}");
}

------------------------------------------------------------------------------------------

# 결과

a = 3
a << 1 = 6
a >> 1 = 1
a & 1 = 1
  • 위의 코드는 비트 시프트와 AND 게이트를 다룸
    • a 는 3 이므로 이진수로 0011 인데 이것을 왼쪽으로 1칸 비트 시프트 하면 0100 이 됨
      • 따라서 십진수로 6 이 됨
    • 반대로 0011 에서 오른쪽으로 1칸 시프트 하면 0001 이 되어 1 이 출력됨
  • $ ( AND ) 연산은 두 게이트가 1 일 때만 1
    • 0011 과 0001 에서는 마지막 비트만 동일하여 0001 이 됨

타입 검사 연산자

  • 타입 검사 연산자는 2종류가 존재
    • as : 형 변환
      • as 는 다른 타입으로 변환은 되지 않고 상위 타입으로 변환할 수 있음
    • is : 객체가 특정 타입이면 true
      • is 는 특정 객체가 특정 타입이면 true
    • is! : 객체가 특정 타입이면 false (=특정 타입이 아니면 true)
      • is! 는 특정 타입이면 false 이며 특정 타입이 아니면 true
  • 아래 코드는 Person 클래스를 상속한 Employee 클래스와 Student 클래스를 통해 형 변환 및 타입 검사를 하는 예제
# 예시

main() {
  Employee employee = Employee();
  Student student = Student();
  
  Person person1 = employee as Person;
  Person person2 = student as Person;
  // Person person = employee; as Person 생략 가능
  
  print("person1.name = ${person1.name}");
  print("person2.name = ${person2.name}");

  print("(employee as Person).name = ${(employee as Person).name}");
  // print("(employee as Person).name = ${(employee as Student).name}"); // error
  
  if (employee is Employee) {
    print("employee is Employee");
  } else {
    print("employee is not Employee");
  }
  
  if (employee is Student) {
    print("employee is Student");
  } else {
    print("employee is not Student");
  }
  
  if (employee is Person) {
    print("employee is Person");
  } else {
    print("employee is not Person");
  }
}

class Person {
  var name = "person";
}

class Employee extends Person {
  var name = "employee";
}

class Student extends Person {
  var name = "student";
}

------------------------------------------------------------------------------------------

# 결과

person1.name = employee
person2.name = student
(employee as Person).name = employee
employee is Employee
employee is not Student
employee is Person
  • employee 객체는 상위 타입인 Person 에도 속하기 때문에 true
    • employ is Person 이 출력됨

조건 표현식

  • 조건 표현식은 크게 3가지
    • 삼항 연산자
    • 조건적 멤버 접근 ( Conditional Member Access ) 연산자
    • ?? 연산자
  • 삼항 연산자
// 조건 ? 표현식1 : 표현식 2;
(a > 0) ? '양수' : '음수';

if ( a > 0 ) {
  return "양수";
} else {
  return "음수";
}
  • 조건적 멤버 접근 ( Conditional Member Access ) 연산자
    • null 체크를 편하게 해줌
    • 이 연산자는 좌항이 null 이면 null 을 리턴하고 아니면 우항의 값을 리턴
    • 코틀린의 안전 호출 연산자와 형태와 의미가 동일
// 좌항?.우항
employee?.name

if ( employee == null ) {
  return null;
} else {
  return employee.name;
}
  • ?? 연산자
    • 이 연산자는 null 체크 뿐만 아니라 null 일 경우에 대한 처리까지 가능
    • 좌항이 null 이 아니면 좌항 값을 리턴하고 null 이면 우항 값을 리턴
    • 코틀린의 ?: ( 앨비스 연산자 ) 와 같음
// 좌항 ?? 우항
employee.name ?? 'new name'

if ( employee.name != null ) {
  return employee.name;
} else {
  return 'new name';
}
# 예시

main() {
  var employee;
  print("employee name = ${(employee == null) ? null : employee.name}");
  print("employee.name = ${employee?.name}");
  
  employee = Employee();
  print("employee name = ${(employee == null) ? null : employee.name}");
  print("employee.name = ${employee?.name}");
  
  print("employee.name = ${employee.name ?? 'assign employee name'}");
  
  employee.name = null;
  print("employee.name = ${employee.name ?? 'assign employee name'}");
}

class Employee {
  var name = 'employee';
}

------------------------------------------------------------------------------------------

# 결과

employee name = null
employee.name = null
employee name = employee
employee.name = employee
employee.name = employee
employee.name = assign employee name

캐스케이드 표기법

  • 캐스케이드 표기법 ( .. ) 은 한 객체로 해당 객체의 속성이나 멤버 함수를 연속적으로 호출할 때 유용
    • 매번 객체를 표기하고 호출하는 불필요한 과정을 줄여주기 때문
# 예시

main() {
  Employee employee = Employee()
    ..name = "Kim"
    ..setAge(25)
    ..showInfo();
  
  employee.name = "Park";
  employee.setAge(30);
  employee.showInfo();
}

class Employee {
  var name = "employee";
  int age = 20;
  
  setAge(int age) {
    this.age = age;
  }
  
  showInfo() {
    print("$name is $age");
  }
}

------------------------------------------------------------------------------------------

# 결과

Kim is 25
Park is 30
  • 위 코드를 보면 employee 객체를 생성한 후 캐스케이드 표기법을 이용하여 해당 객체의 name ∙ setAge() ∙ showInfo() 에 연속적으로 접근하고 있음
profile
🌱 Backend-Dev | hwaya2828@gmail.com

0개의 댓글