본문 바로가기
기타개발/Flutter

Dart #2 : Functions

by 궝테스트 2020. 8. 27.

Dart : https://dart.dev/guides/language/language-tour

  • Dart 는 객체 지향 언어이다
  • 함수도 객체이며 함수조차도 객체이고 모든 함수 타입의 기본 클래스인 Function 이 있다
  • 이는 함수를 변수에 할당하거나 다른 함수에 인수로 전달할 수 있음을 의미한다
  • Effective Dart는 public API 에 대한 타입 표시를 권장하지만 생략 가능하다
bool isNoble(int atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}

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

// 표현식이 하나인 경우 아래처럼 줄일 수 있다
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;

 

0. The main() function

  • 모든 앱에는 앱의 진입점 역할을하는 최상위 main() 함수가 있어야한다
  • main() 함수는 void 를 반환하고 인수에 대한 선택적 List<String> 매개 변수를 갖는다
void main() {
  querySelector('#sample_text_id')
    ..text = 'Click me!'
    ..onClick.listen(reverseText);
}

// List 파라미터가 있는 main 함수
void main(List<String> arguments) {
  print(arguments);

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

여기서 잠깐 Cascade notation (..)

// 원래 코드
var button = querySelector('#confirm');
button.text = 'Confirm';
button.classes.add('important');
button.onClick.listen((e) => window.alert('Confirmed!'));

// cascade .. 를 적용한 코드
querySelector('#confirm') // Get an object.
  ..text = 'Confirm' // Use its members.
  ..classes.add('important')
  ..onClick.listen((e) => window.alert('Confirmed!'));

 

1. Parameters

  • 함수는 필수 및 선택의 두 가지 타입의 매개 변수를 가질 수 있다
  • 필수 매개 변수가 먼저 나열되고 그 뒤에 선택적 매개 변수가 나열된다

1-1. Optional parameters

  • 선택적 매개 변수는 이름이 지정되거나 위치가 지정 될 수 있다

Named parameters

  • 함수를 호출 할 때 paramName : value 를 사용하여 매개 변수를 이름을 지정할 수 있다
  • 이름이 지정된 매개 변수는 선택적 매개 변수이지만 매개 변수가 필수임을 나타 내기 위해 @required 주석을 달 수 있다
    • @required 사용을 위해 package:meta meta.dart import 해야한다
/// 이름이 [bold] and [hidden] 로 지정된 매개변수를 갖는 함수
void enableFlags({bool bold, bool hidden}) {
	...
}

// 함수 호출
enableFlags(bold: true, hidden: false);

/// 사용자는 child 매개 변수에 대한 값을 제공해야한다
const Scrollbar({Key key, @required Widget child})


Positional parameters

  • 매개 변수를 [] 로 감싸면 선택적 positional 매개 변수로 표시된다
String say(String from, String msg, [String device]) {
  var result = '$from says $msg';
  if (device != null) {
    result = '$result with a $device';
  }
  return result;
}

// positional 파라미터 없이 함수 호출 가능
assert(say('Bob', 'Howdy') == 'Bob says Howdy');

// 파라미터 값을 넣어서 함수 호출 시에도 가능
assert(say('Bob', 'Howdy', 'smoke signal') ==
    'Bob says Howdy with a smoke signal');


Default parameter values

  • 함수는 파라미터에 = 을 사용하여 named 파라미터와 positional 파라미터에 기본값을 정의할 수 있다
  • 기본값은 컴파일 타임 상수여야하며 지정하지 않으면 null 이다
// Named 파라미터의 경우

/// 파라미터 [bold] 와 [hidden] 의 기본값을 각각 false 로 설정한다
void enableFlags({bool bold = false, bool hidden = false}) {...}

// 함수 호출 시 bold 에 true 를 넣으면 bold 는 true, hidden 은 false 일 것이다
enableFlags(bold: true);
// Positional 파라미터의 경우

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

assert(say('Bob', 'Howdy') ==
    'Bob says Howdy with a carrier pigeon');
  • List 또는 Map 을 파라미터로 전달할 수 있다
void doStuff(
    {List<int> list = const [1, 2, 3],
    Map<String, String> gifts = const {
      'first': 'paper',
      'second': 'cotton',
      'third': 'leather'
    }}) {
  print('list:  $list');
  print('gifts: $gifts');
}

 

2. Functions as first-class objects

  • 함수를 다른 함수의 파라미터로 넘겨줄 수 있다
void printElement(int element) {
  print(element);
}

var list = [1, 2, 3];

// printElement 함수를 파라미터로 넘긴다
list.forEach(printElement);

 

3. Anonymous functions

  • ([Type] paramName) { ... } 형태로 익명 함수를 만들 수 있다
  • 아래 예제는 forEach 내부에 익명 함수를 넘겼다
var list = ['apples', 'bananas', 'oranges'];
list.forEach((item) {
  print('${list.indexOf(item)}: $item');
});

// result
0: apples
1: bananas
2: oranges
  • 위 함수를 람다식으로 아래 처럼 만들 수도 있다
list.forEach(
    (item) => print('${list.indexOf(item)}: $item'));

 

4. 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);
    }
  }
}

 

5. Lexical closures

  • 클로저는 함수가 원래 범위 밖에서 사용되는 경우에도 Lexical scope 의 변수에 액세스 할 수있는 함수 객체이다
  • 함수는 주변 범위에 정의 된 변수를 통해 닫힐 수 있다
/// 함수의 인자로 [addBy] 를 넘기면 아래 함수를 반환한다
Function makeAdder(int addBy) {
  return (int i) => addBy + i;
}

void main() {
  // addBy 로 2 를 넘기면 add2 에 반환된 함수가 캡쳐된다
  var add2 = makeAdder(2);

  // addBy 로 4 를 넘기면 add2 에 반환된 함수가 캡쳐된다
  var add4 = makeAdder(4);

  // 2 + 3 = 5
  print(add2(3));
  
  // 4 + 3 = 7
  print(add4(3));
}

 

6. Testing functions for equality

  • 최상위 함수, 정적 메서드, 인스턴스 메서드가 같은지 테스트하는 예제이다
void foo() {} // 최상위 함수

class A {
  static void bar() {} // static 메소드
  void baz() {} // 인스턴스 메소드
}

void main() {
  var x;

  // 최상위 함수와 비교 -> true
  x = foo;
  assert(foo == x);

  // static 메소드와 비교 -> true
  x = A.bar;
  assert(A.bar == x);

  // 인스턴스 메소드와 비교 -> true
  var v = A(); // A 의 인스턴스 #1
  var w = A(); // A 의 인스턴스 #2
  var y = w;
  x = w.baz;

  // 이 클로저는 동일 인스턴스를 참조한다 (#2) -> true
  assert(y.baz == x);

  // 이 클로저는 다른 인스턴스를 참조한다 -> 같지 않으니 true
  assert(v.baz != w.baz);
}

 

7. Return values

  • 모든 함수는 값을 반환하며 반환값을 지정하지 않으면 null 을 반환한다
foo() {}

assert(foo() == null);

 

댓글