메뉴

문서정보

목차

Function

다트는 순수 객체지향 언어(True object-oriented language)이므로 함수도 유형이 Function인 객체로 다룬다. 즉 함수를 변수에 할당하거나 다른 함수에 인자로 전달 할 수 있다. Dart 클래스의 인스턴스를 함수처럼 호출 할 수도 있다.

아래는 함수 구현 예제다.
main() {
  print("Factorial ${factorial(10)}");
}

int factorial(int number) {
  if (number <=0 ) {
    return 1;
  } else {
    return (number + factorial(number-1));
  }
}

Dart는 타입을 정확히 지정 할 것을 권장하지만, 생략해도 작동하긴 한다. 선택사항이긴 한데, 그냥 타입을 지정하는 걸로가자.
factorial(number) {
  if (number <=0 ) {
    return 1;
  } else {
    return (number + factorial(number-1));
  }
}

표현식이 하나 뿐인 함수의 경우 Arrow(=>)를 이용해서 약식으로 사용 할 수도 있다.
main() => print("Factorial ${factorial(10)}");
=> expr{ return expr; }의 약식표현이다.

다른 예제다. 아래 두 함수는 동일한 일을 함 수다.
int findArea(int length, int breath) {
  return length * breath;
}

int shortFindArea(int length, int breath) => length * breath;

매개변수(parameters)

함수는 필수 매개 변수(required parameters)와 선택적 매개 변수(optional parameters), 두 개의 타입을 가질 수 있다.

Optional positional parameters

대괄호([])는 선택적 위치 매개변수를 지정하기 위해서 사용 할 수 있다. 대괄호안에 위치하고 있는 매개변수를 지정할지는 개발자 선택이다. 매개변수의 위치가 중요하다.
main() {
  test(1);        // 1, null, null
  test(1,2);      // 1, 2, null
  test(1,2,3);    // 1, 2, 3
}

test(int x, [int y, int z]) {
  print("${x}, ${y}, ${z}");
}
설정하지 않은 매개변수의 값은 null이다. 아래와 같이 기본 값을 설정 할 수 있다.
test(int x, [int y=0, int z=0]) {
  print("${x}, ${y}, ${z}");
}

왠지 현실에 있을 법한 함수를 만들어보자.
void makeUrl(String domain,
        [String protocol = 'https', String path = "/dart", int port = 443]) =>
    print('$protocol://$domain:$port$path');

main(List<String> args) {
  print('calling positionalOptionalParameters:');
  makeUrl('nmrony.info', 'https', '/about', 8000);  // https://nmrony.info:8000/about
  makeUrl('nmrony.info', 'https', '/');             // https://nmrony.info:443/
  makeUrl('nmrony.info', 'https');                  // https://nmrony.info:443/dart
  makeUrl('nmrony.info');                           // https://nmrony.info:443/dart
}
protocol, path, port를 설정하지 않을 경우 기본 값이 설정되는 걸 확인 할 수 있다.

Optional named parameters

선택적 네임드 매개변수는 {}로 정의 할 수 있다. 이름이 있으니 매개변수의 위치와 상관없이 사용 할 수 있다. 네임드 매개변수를 가진 함수를 호출 할 때는 반드시 "매개변수이름: 값"형식을 사용해야 한다.
void makeUrl(String domain,
        {String protocol = 'https', String path = "/dart", int port = 443}) =>
    print('$protocol://$domain:$port$path');

main(List<String> args) {
  print('calling positionalOptionalParameters:');
  makeUrl('nmrony.info', port: 8888, protocol: "https");
  makeUrl('nmrony.info', protocol:'https', path:'/');
  makeUrl('nmrony.info', protocol:'https');
  makeUrl('nmrony.info');
}

main() 함수

main() 함수는 애플리케이션 진입점 역할을하는 top-level 함수다. 모든 애플리케이션은 반드시 main() 함수를 가지고 있어야 한다. main 함수는 void를 반환하며, 선택적으로 List<String>를 매개변수로 가질 수 있다.
main(List<String> args) {
  print(args);
}
args 라이브러리를 이용하면 명령행 인자(command-line arguments)들을 처리 할 수 있다.

1 급 객체(Fist-class objects)서의 함수

Dart 함수는 1급 객체로서 아래의 조건을 충족한다.
  1. 변수나 데이터에 할당 할 수 있어야 한다.
  2. 객체의 인자로 넘길 수 있어야 한다.
  3. 객체의 리턴값으로 리턴할 수 있어야 한다.
먼저 객체의 인자로 넘길 수 있는지 확인했다.

main() {
  var list = [1,2,3];
  list.forEach(printElement);
}

void printElement(int element) {
  print(element);
}

함수를 변수에 할당 해 보자.
var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');
이 예제는 익명 함수(anonymous function)을 사용하고 있다. 익명 함수는 밑에서 자세히 다루겠다.

익명 함수

대부분의 함수는 main(), printElement()와 같이 이름을 가지고 있다. Dart를 비롯해 많은 언어들이 익명 함수, 람다(lambda), 클로저(closure)라고 부르는 이름없는 함수를 만들 수 있다. 익명함수는 다른 함수처럼 변수에 할당하여 컬렉션에서 추가하거나 제거 할 수 있다. 익명함수는 아래와 같이 정의한다.
([[Type] param1[, …]]) {
  codeBlock;
}; 

사용예
main() {
  var myFunc = (String name) {
    print("My name is $name");
  };
  myFunc('yundream');
}

아래 예제는 익명함수를 이용해서 리스트의 아이템을 조작한다.
main() {
  var list = ['apple', 'bananas', 'oranges'];
  list.forEach((item) {
    print("${list.indexOf(item)}: $item");
  });
}

Lexical scope

Dart에서 변수의 유효범위는 코드의 레이아웃에 의해서 정적으로 결정된다. 변수의 유효범위는 중괄호를 따라서 내부로 확장된다.

int topLevel = 0;
main() {
  int mainLevel = 1;
  void myFunction(){
    int oneLevel = 2;
    void nestedFunction() {
      int twoLevel = 3;
      print(twoLevel);
      print(oneLevel);
      print(mainLevel);
      print(topLevel);
    } 
    nestedFunction();
  }

  myFunction();
}
// 3, 2, 1, 0 이 출력된다.

아래 코드를 보자.
Function makeAdder(int addBy) {
  return (int i) => addBy + i;
}

void main() {
  // Create a function that adds 2.
  var add2 = makeAdder(2);

  // Create a function that adds 4.
  var add4 = makeAdder(4);
  print(add2(3));   // 5
  print(add4(3));   // 7
}
함수는 닫기전에 정의된 변수를 저장 할 수 있다. 위 예제에서 makeAdder는 변수 addBy를 저장한다. 이제 이 함수는 어디를 가진 addBy를 기억하게 된다.

Lexcial closures

Lexcial closure는 closure 혹은 fcuntion closure 라고 부른다. 프로그래밍 언어에서 클로저는 일급 객체 함수의 개념을 이용하여 유효 범위에 묶인 변수를 바인딩 하기 위해서 사용하는 기술이다. 아래 코드를 보자.
Function makeAddr(num addBy) {
    return (num i) => addBy +i;    
}

void main() {
    var add2 = makeAddr(2);    
    var add4 = makeAddr(4);    
    assert(add2(3) == 5);    
    assert(add4(3) == 7);    
}
makeAddr 함수내부에 설정된 익명함수는 addBy정보를 저장 할 수 있다. 반환된 함수는 어디에서든지 addBy를 기억한다.

다른 예제다.
var talk = (s) => print(s);
talk("Hi there"); // prints "Hi there"

개발자가 처음 설정한 값을 출력하도록 함수를 수정했다.
Function say(String something) { 
  return () => print(something);
}

void main() {
  var talk = say("How are you?");
  talk(); // "How are you?"
}

함수 동일성 테스트

최상위 함수, 정적 메서드, 메서드의 인스턴스가 동일한지 테스트하는 예제 코드다.

		

리턴 값

모든 함수는 리턴을 가진다. 리턴 값을 지정하지 않으면 null을 반환한다.
foo() {}
assert(foo() == null);

« Dart Tutorial - Dart 설치 및 소개