다트의 함수에 대해서 알아보자 !
1. Parameters
Dart도 객체 지향 언어이기 때문에 함수도 객체이다. JS처럼 함수를 변수에 할당할 수 있고, parameter로 전달할 수 있다. Dart 클래스의 인스턴스를 함수처럼 호출할 수도 있다.
함수는 required 파라미터를 원하는 수만큼 가질 수 있다.
- Named parameters
Named parameters는 required 키워드가 표시되지 않는 한 선택 사항. 함수를 정의할 때 {param1, param2, …}를 사용하여 Named parameters를 지정.
/// Sets the [bold] and [hidden] flags ...
void enableFlags({bool? bold, bool? hidden}) {...}
함수를 호출할 때 paramName: value를 사용하여 Named parameters를 지정할 수 있다.
enableFlags(bold: true, hidden: false);
명명된 매개변수에 null 이외의 기본값을 정의하려면, =을 사용하여 기본값을 지정하면 된다. 지정된 값은 컴파일 타임 상수이어야 한다.
/// Sets the [bold] and [hidden] flags ...
void enableFlags({bool bold = false, bool hidden = false}) {...}
// bold will be true; hidden will be false.
enableFlags(bold: true);
명명된 매개변수는 일종의 선택적 매개변수이지만 매개변수가 필수임을 나타내기 위해 required 키워드을 달 수 있다. 이렇게 되면, 사용자는 매개변수 값을 제공해야 한다.
const Scrollbar({super.key, required Widget child});
만약 child를 지정하지 않은 채로 Scrollbar를 생성하려하면 컴파일 타임에 에러가 발생. (필수로 표시된 매개변수도 null 가능.)
위치 인수(positional arguments)를 먼저 배치하는 것이 일반적이지만 Dart가 그러한 규칙을 요구하지는 않는다. Dart는 매개변수 목록에서 명명된 매개변수를 어디에 놓아도 허용한다.
repeat(times: 2, () {
...
});
- Optional positional parameters
대괄호 []로 함수 매개변수를 묶으면 선택적 위치 매개변수로 표시된다. 기본값을 제공하지 않으면, 해당 매개변수의 타입은 null이 될 것이므로 nullable이어야 한다.
String tttFunc(String from, String msg, [String? device]) {
var result = '$from and $msg';
if (device != null) {
result = '$result with a $device';
}
return result;
}
선택적 매개변수를 사용하지 않고 이 함수를 호출하는 예시.
// Optional positional parameter인 device 생략
assert(tttFunc('qqq', 'www') == 'qqq and www');
세 번째 매개변수를 사용하여 이 함수를 호출하는 예시.
assert(tttFunc('qqq', 'www', 'eee') == 'qqq and www with a eee');
null 이외의 선택적 위치 매개변수에 기본값을 정의하려면, =을 사용하여 기본값을 지정해야 한다. 지정된 값은 컴파일 타임 상수이어야 한다.
String qqqFunc(String from, String msg, [String device = 'tete']) {
var result = '$from and $msg with a $device';
return result;
}
assert(say('qqq', 'www') == 'qqq and www with a tete');
2. The main() function
모든 앱은 최상위 main() 함수를 가져야한다. 이 함수는 앱의 진입점(entrypoint) 역할을 한다. main() 함수는 void를 반환하며, 인자로 선택적으로 List<String>을 받을 수 있다.
void main() {
print('Hello, World!');
}
인자를 받는 command-line 앱의 main() 함수의 예시.
// Run the app like this: 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');
}
3. Functions as first-class objects
함수를 다른 함수의 매개변수로 전달할 수 있다.
void printElement(int element) {
print(element);
}
var list = [1, 2, 3];
// Pass printElement as a parameter.
list.forEach(printElement);
함수를 변수에 할당할 수도 있다.
var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');
4. Anonymous functions
대부분의 함수는 main()이나 printElement()와 같이 이름이 지정된 함수이다. 그러나 익명 함수(anonymous function) 또는 람다(lambda) 또는 클로저(closure)라고 불리는 이름 없는 함수도 만들 수 있다. 예를들어, 익명 함수를 변수에 할당하여 컬렉션에서 추가하거나 제거할 수 있다.
const list = ['apples', 'bananas', 'oranges'];
list.map((item) {
return item.toUpperCase();
}).forEach((item) {
print('$item: ${item.length}');
});
타입이 지정되지 않은 매개변수 item을 가지는 익명 함수를 정의하고, 이를 map 함수에 전달한다. 리스트의 각 항목에 대해 호출된 함수는 각 문자열을 대문자로 변환한다. 그런 다음 forEach에 전달된 익명 함수에서 각 변환된 문자열과 해당 길이를 출력한다.
list
.map((item) => item.toUpperCase())
.forEach((item) => print('$item: ${item.length}'));
만약 함수가 단일 표현식 또는 반환문만을 포함한다면, 화살표 표기법(arrow notation)을 사용하여 축약할 수 있다.
5. Lexical scope
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);
}
}
}
6. Lexical closures
클로저는 원래의 범위 밖에서 함수가 사용되더라도 그 어휘적 범위 내의 변수에 접근할 수 있는 함수 객체다.
함수는 주변 범위에서 정의된 변수를 캡처할 수 있다.
아래 예시에서 makeAdder()는 변수 addBy를 캡처한다. 반환된 함수가 어디로 가든, 그것은 addBy를 기억한다.
/// Returns a function that adds [addBy] to the
/// function's argument.
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);
assert(add2(3) == 5);
assert(add4(3) == 7);
}
7. Return values
모든 함수는 값을 반환한다. 반환 값이 지정되어 있지 않으면, return null; 문이 함수 본문에 암묵적으로 추가된다.
foo() {}
assert(foo() == null);
8. Generators
값의 시퀀스를 게으르게(lazily) 생성해야 하는 경우, 제너레이터(generator) 함수를 사용하는 것이 좋다. Dart는 두 종류의 제너레이터 함수를 내장하고 있다:
동기(generator): Iterable 객체를 반환합니다.
비동기(generator): Stream 객체를 반환합니다.
동기(generator) 함수를 구현하려면, 함수 본문을 sync*로 표시하고 yield 문을 사용하여 값을 전달하면 된다.
Iterable<int> naturalsTo(int n) sync* {
int k = 0;
while (k < n) yield k++;
}
비동기(generator) 함수를 구현하려면, 함수 본문을 async*로 표시하고 yield 문을 사용하여 값을 전달하면 된다.
Stream<int> asynchronousNaturalsTo(int n) async* {
int k = 0;
while (k < n) yield k++;
}
제너레이터(generator)가 재귀적(recursive)인 경우, yield*을 사용하여 성능을 향상시킬 수 있다.
Iterable<int> naturalsDownFrom(int n) sync* {
if (n > 0) {
yield n;
yield* naturalsDownFrom(n - 1);
}
}
끝.
- 참고
'개발 > Dart & Flutter' 카테고리의 다른 글
[Dart] Built in Types (30) | 2025.01.28 |
---|---|
[Dart] Dart 언어 가이드 살펴보기 (26) | 2025.01.24 |
[Flutter] 플러터 웹뷰 환경 파일 다운로드 구현하기 (130) | 2024.09.30 |
[Flutter] 플러터 웹뷰 사용하기 (2) (185) | 2024.07.09 |
[Flutter] 플러터 웹뷰 사용하기 (201) | 2024.07.03 |