Skip to main content

Dart의 함수

Dart2.19.6About 2 min

원문 : https://dart.dev/language/functionsopen in new window

Dart는 진정한 객체지향 언어이기에 함수 또한 객체이며 Funtionopen in new window이라는 타입을 가집니다.
이 의미는 함수가 변수에 할당이 되거나 다른 함수에 인자로 전달될 수 있습니다.
또한 Dart 클래스의 인스턴스를 함수처럼 호출할 수 있습니다.
자세한 정보는 호출가능한 클래스를 참고하세요.

함수 구현체에 대한 예제입니다.

bool isNoble(int atomicNumber) {
    return _nobleGases[atomicNumber] != null;
}

효율적인 Dart를 위해 공개 API의 타입지정open in new window을 추천하지만, 함수에서 타입을 생략해도 동작합니다.

isNoble(atomicNumber) {
    return _nobleGases[atomicNumber] != null;
}

만약 함수가 한가지 표현식만 있다면 간소화된 문법을 사용할 수 있습니다.

bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;

=> expr 문법은 { return expr; }의 축약형입니다.
=> 표기법은 화살표 문법이라고도 합니다.

Note

표현식만(명령문은 아님) 화살표(=>)와 세미콜론(;) 사이에 위치할 수 있습니다.
예로 들어, if 명령문은 위치할 수 없으나 조건 표현식은 가능합니다.

매개변수

함수는 몇개던 필수적 위치의 매개변수를 가질 수 있습니다.
그 뒤로 명명된 매개변수나 선택적 매개변수를 가질 수 있습니다. (동시는 안됨)

Note

몇몇 API - 특히 Flutteropen in new window의 위젯 생성자 - 는 필수 매개변수임에도 명명된 매개변수만 사용합니다.
다음 절에서 자세하게 알아보겠습니다.

함수에 인자를 전달하거나 함수의 매개변수를 정의할 때 마지막 콤마를 사용할 수 있습니다.

명명된 매개변수

명명된 매개변수는 명시적으로 required가 마킹되기전까지는 선택적입니다.

함수를 정의할 때 명명된 매개변수를 지정하려면 {param1, param2, ...}를 사용하면 됩니다.

// [bold]와 [hidden] 플래그를 설정
void enableFlags({bool? bold, bool? hidden}) {...}

함수를 호출할 때 명명된 인자를 paramName: value를 사용해서 지정할 수 있습니다.
예로 들면 다음과 같습니다.

enableFlags(bold: ture, hidden: false);

null 외에 명명된 매개변수의 기본값을 정의하려면 =를 사용해 기본값을 지정할 수 있습니다.
지정된 값은 컴파일타임 상수여야 합니다.
예로 들면 다음과 같습니다.

/// [bold]와 [hidden] 플래그 설정하기
void enableFlags({bool bold = false, bool hidden = false}) {...}

// bold는 true, hidden은 false
enableFlags(bold: true);

명명된 매개변수가 선택적 매개변수 중 하나이지만, required를 어노테이션으로 추가해서 매개변수가 필수값으로 만들 수 있으며 사용자는 매개변수의 값을 제공해야 합니다.
예제는 다음과 같습니다.

const Scrollbar({super.key, required Widget child});

누군가 child 매개변수 없이 Scrollbar를 만들려고 시도한다면 분석기는 이슈를 보고할 것 입니다.

Note

required가 마킹된 매개변수는 null이 가능합니다.

const Scrollbar({super.key, required Widget? child});

위치적 인자를 먼저 배치하고 싶을 수 있지만 Dart는 요구하지는 않습니다.
Dart는 API에 적합하다면 명명된 인자를 인자 목록의 아무 곳에나 배치할 수 있도록 허용합니다.

repeat(times: 2, () {
  ...
});

선택적 위치 매개변수

함수의 매개변수 집합을 []로 감싸면 선택적 위치 매개변수로 지정됩니다.
기본값을 제공하지 않으면 해당 타입의 기본값이 null이 될 수 있도록 null 가능이어야 합니다.

String say(String from, String msg, [String? device]) {
    var result = '$from says $msg';
    if (device != null) {
        result = '$result with a $device';
    }
    return result;
}

선택적 매개변수없이 함수를 호출하는 예제입니다.

assert(say('Bob', 'Howdy') == 'Bob says Howdy');

3번째 매개변수를 추가하여 함수를 호출하는 예제입니다.

assert(say('Bob', 'Howdy', 'smoke signal') == 'Bob says Howdy with a smoke signal');

null 외에 선택적 위치 매개변수의 기본값을 정의하려면 =를 사용하여 기본값을 지정합니다.
지정된 기본값은 컴파일타임 상수여야 합니다.
예로 들면 다음과 같습니다.

String say(String from, String msg, [String device = 'carrier pigeon']) {
  var result = '$from says $msg with a $device';
  return result;
}

assert(say('Bob', 'Howdy') == 'Bob says Howdy with a carrier pigeon');

main() 함수

모든 앱은 앱의 진입점을 제공하는 최상위 main() 함수를 가지고 있어야 합니다.
main() 함수는 void를 반환하고 선택적으로 List<String> 매개변수를 인자로 받습니다.

간단한 main() 함수 예제입니다.

void main() {
    print('Hello, World!');
}

명령줄에서 인자를 받는 앱의 main() 함수 예제입니다.

// 앱을 다음과 같이 실행하면 됩니다 : dart args.dart 1 test
void main(List<String> arguments) {
    print(arguments);

    assert(arguments.length == 2);
    assert(int.parse(arguments[0]) == 1);
    assert(arguments[1] == 'test');
}

args 라이브러리open in new window를 사용해서 명령줄 인자를 정의/파싱할 수 있습니다.

1급 클래스객체인 함수

함수를 다른 함수의 인자로 전달할 수 있습니다.
예로 들면 다음과 같습니다.

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

var list = [1, 2, 3];

// printElement를 인자로 전달하기
list.forEach(printElement)

또한 함수를 변수에 할당할 수 있습니다.

var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');

이 예제는 익명 함수를 사용했습니다.
더 자세한 내용은 다음 절을 참고하세요.

익명 함수

대부분의 함수는 main()이나 printElement()와 같이 이름을 가집니다.
익명 함수(람다 또는 클로저)라고 불리는 이름이 없는 함수를 만들 수도 있습니다.
익명 함수를 변수에 할당할수 있어서 콜렉션에 추가하거나 삭제할수 도 있습니다.

익명 함수는 일반적인 함수와 비슷하게 콤마로 구분하고 선택적 타입 어노테이션이 있는 0개 이상의 인자가 괄호 사이에 있습니다.

아래의 코드 블럭은 함수의 본문을 담고 있습니다.

([[Type] param1[, ...]]) {
    codeBlock;
};

아래 예제는 타입이 없는 item 이라는 인자를 가진 익명 함수를 정의합니다.
목록의 각 아이템에 실행되는 함수는 문자열을 대문자로 변경합니다.
다음으로 forEach에 전달된 익명함수에서 각 변경된 문자열을 길이와 함께 출력합니다.

const list = ['apples', 'bananas', 'oranges'];
list.map((item) {
  return item.toUpperCase();
}).forEach((item) {
  print('$item: ${item.length}');
});

Run 을 클릭하여 코드를 실행합니다.

    
void main() {
  const list = ['apples', 'bananas', 'oranges'];
  list.map((item) {
    return item.toUpperCase();
  }).forEach((item) {
    print('$item: ${item.length}');
  });
}
    

함수가 하나의 표현식 또는 반환 명령문을 가지고 있으면 화살표 표기법을 사용하여 간결하게 할 수 있습니다.
아래의 코드를 복사하여 DartPad에 붙여넣고 함수가 동일한지 Run을 클릭하여 실행해보세요.

list
    .map((item) => item.toUpperCase())
    .forEach((item) => print('$item: ${item.length}'));

어휘적 범위

Dart는 어휘적 범위를 가지는 언어이며, 변수의 범위를 정적으로 결정하여 코드의 레이아웃을 간결하게 합니다.
변수가 범위안에 있는지 보고싶으면 "중괄호 밖으로 따라가기"를 하면 됩니다.

각 범위 수준을 가지는 변수와 중첩 함수에 대한 예제입니다.

bool topLevel = true;

void main() {
  var insideMain = true;

  void myFunction() {
    var insideFunction = true;

    void nestedFunction() {
      var insideNestedFunction = true;

      assert(topLevel);
      assert(insideMain);
      assert(insideFunction);
      assert(insideNestedFunction);
    }
  }
}

nestedFunction() 에서 최상위 수준까지 모든 수준의 변수를 어떻게 사용할 수 있는지 확인해보세요.

어휘적 클로저

클로저는 함수가 본래의 범위 밖에서 사용되더라도 어휘적 범위에서 변수에 접근이 가능한 함수 객체입니다.

함수는 주변 범위에 정의된 변수를 포함시킬 수 있습니다.
아래 예제에서 makeAdder()는 변수 addBy를 캡쳐합니다.
반환된 함수가 어디로 가든, addBy를 기억합니다.

/// 함수가 [addBy]에 함수 인자를 더하여 반환합니다.
Function makeAdder(int addBy) {
    return (int i) => addBy + i;
}

void main() {
    // 2를 더하도록 함수를 만듦
    var add2 = makeAdder(2);

    // 4를 더하도록 함수를 만듦
    var add4 = makeAdder(4);

    assert(add2(3) == 5);
    assert(add2(3) == 7);
}

함수 동일여부 테스트

최상위 함수, 정적 함수, 인스턴스 함수에 대한 동일여부를 테스트하는 예제입니다.

void foo() {} // 최상위 함수

class A {
    static void bar() {} // 정적 함수
    void baz() {} // 인스턴스 함수
}

void main() {
    Function x;

    // 최상위 함수 비교
    x = foo;
    assert(foo == x);

    // 정적 함수 비교
    x = A.bar;
    assert(A.bar == x);

    // 인스턴스 함수 비교
    var v = A(); // A의 #1 인스턴스
    var w = A(); // A의 #2 인스턴스
    var y = w;
    x = w.baz;

    // 이 클로저는 동일한 인스턴스 (#2)를 참조합니다.
    // 따라서 동일합니다.
    assert(y.baz == x);

    // 이 클로저는 다른 인스턴스를 참조합니다.
    // 따라서 다릅니다.
    assert(v.baz != w.baz);
}

반환 값

모든 함수는 값을 반환합니다.
반환되는 값이 명시되지 않으면, 암무적으로 함수 본문에 return null;을 추가합니다.

foo() {}

assert(foo() == null);

생성기

값의 목록을 지연되게 생성이 필요한 경우 생성기 함수 사용을 고려해보세요.
Dart는 내장된 두가지 종류의 생성기 함수를 제공합니다.

동기식 생성기 함수를 구현하려면 함수본문에 sync*를 마킹하고 값을 전달하기 위해 yield 표현식을 사용합니다.

Iterable<int> naturalsTo(int n) sync* {
  int k = 0;
  while (k < n) yield k++;
}

비동기 생성기 함수를 구현하려면 함수본문에 async*를 마킹하고 값을 전달하기 위해 yield 표현식을 사용합니다.

Stream<int> asynchronousNaturalsTo(int n) async* {
  int k = 0;
  while (k < n) yield k++;
}

생성기가 재귀되면 성능을 향상시키기 위해 yield*를 사용합니다.

Iterable<int> naturalsDownFrom(int n) sync* {
  if (n > 0) {
    yield n;
    yield* naturalsDownFrom(n - 1);
  }
}