[Swift] 함수2

한음·2022년 3월 9일
0

InOut 매개변수

함수 내의 인자값 수정이 외부 변수에 영향을 미칠 수 있는 방법. inout 키워드
인자를 '참조에 의한 전달방식'으로 전달한다.

var a = 10

func test(x: Int) -> Int {
    var a = a
    a += 1
    return a
}

test(x: a)
print(a) // 10

//////

func testInout(x: inout Int) -> Int {
    x += 1 // inout 때문에 따로 변수 선언 안해도 인자값 자체(= 외부 변수)로 그대로 실행
    return x
}

testInout(x: &a) // 값이 아니라 주소를 전달하는 것이기 때문에 주소 추출 연산자 & 를 붙인다.
print(a) // 11

예외적으로 구현된 인스턴스는 inout 없이도 참조에 의해 전달됨.

변수의 생존 범위와 생명 주기

전역변수 -> 프로그램의 최상위 레벨에 위치. 특별한 경우를 제외하고 프로그램 종료 전까지 삭제되지 않음.
지역변수 -> 실행 블록이 끝나면 제거 (변수의 생명 주기)

지역변수를 더 안쪽 범위에서 사용하려면 초기화가 된 상태여야함.
-> 초기화 전에는 메모리를 할당받지 못했으므로 주소값이 없음.

일급 객체로서의 함수

스위프트는 객체지향 언어이자 동시에 함수형 언어.

일급 객체
- 객체가 런타임에도 생성이 가능해야 한다.
- 인자값으로 객체를 전달할 수 있어야 한다.
- 반환값으로 객체를 사용할 수 있어야 한다.
- 변수나 데이터 구조 안에 저장할 수 있어야 한다.
- 할당에 사용된 이름과 관계없이 고유한 구별이 가능해야 한다.

함수가 위 조건을 만족하면 이를 일급 함수라 하고, 그 언어를 함수형 언어로 분류.

1. 변수나 상수에 함수를 대입 가능

func foo(base: Int) -> String {
    return "입력값 \(base)"
}

func foo(base: Int, base2: Int){
    print(base + base2)
}

let fn1 = foo(base:)
let fn2 = foo(base:base2:)
print(fn1(123)) // 입력값 123
fn2(3, 5) // 8 출력

이때 식별자 없이도 함수 구분이 가능하다면(같은 이름의 함수가 없다면), let fn1 = foo 와 같은 형태만으로도 함수를 변수에 대입할 수 있다.

이런 타입을 함수 타입이라고 하며, 입력 타입과 반환 타입으로 표시한다.

func bar(age: Int) -> String {
    return "\(age)"
}

let fn : (Int) -> String = bar // 함수 타입 어노테이션
func foo(x: Int) {
    print(x + 5)
}

func bar() {
    print("hello")
}

var x : (Int) -> () = foo // 반환값 없는 타입
var y : () -> () = bar // 입력, 반환 다 없는 타입

x(5) // 10
y() // hello

위처럼 반환값이 없는 경우, 빈 튜플 대신 Void 도 사용 가능.

func foo(x: Int) {
    print(x + 5)
}

func bar() {
    print("hello")
}

var f1 : (Int) -> Void = foo
var f2 : () -> Void = bar // 반환값에만 Void 키워드 사용 가능.

f1(10)
f2()

2. 함수의 반환 타입으로 함수를 사용할 수 있음.

func foo() -> String {
    return "foo() is excuted."
}

func pass() -> () -> String { // pass() 는 반환값으로 () -> String 함수 타입을 가짐
    return foo
}

let p = pass()

print(p()) // "foo() is excuted."

예시

func plus(x: Int, y:Int) -> Int {
    return x + y
}

func minus(x: Int, y:Int) -> Int{
    return x - y
}

func cal(_ operand: String) -> (Int, Int) -> Int {
    switch operand{
    case "+":
        return plus
    case "-":
        return minus
    default:
        return plus
    }
}

let ex1 = cal("-")(5, 3) // 어떤 함수인지 명시 되어있기 때문에 라벨링 필요 X
let ex2 = cal("+")(5, 3)
let ex3 = cal("-")

print(ex1) // 2
print(ex2) // 8
print(ex3(4,3)) // 1

3. 함수의 인자값으로 함수를 사용할 수 있음

JS 의 콜백 함수 개념과 유사.

func incrase(param: Int) -> Int{
    return param + 1
}

func pass(base: Int, function fn: (Int) -> Int) -> Int {
    return fn(base)
}

print(pass(base: 5, function: incrase)) // 6

예시

func successExcute() {
    print("SUCCESS")
}

func failExcute() {
    print("FAIL")
}

func divide(x: Int, success sCallback: () -> Void, fail fCallback: () -> Void) -> Int {
    guard (x != 0) else {
        fCallback()
        return 0
    }
    let answer = 100 / x
    defer {
        sCallback()
    }
    return answer
}

let ex1 = divide(x: 0, success: successExcute, fail: failExcute) // FAIL
let ex2 = divide(x: 100, success: successExcute, fail: failExcute) // SUCCESS
print(ex1) // 0
print(ex2) // 1

*위치와 관계없이 함수 종료 직전에 실행되는 구문이 defer 블럭 내에 들어간다.

- defer 블록을 읽기 전에 함수의 실행이 종료될 경우, defer 블록은 실행되지 않음.
- 하나의 함수나 메소드 내에서 defer 블록을 여러 번 사용할 수 있다. 이때는 **가장 마지막에 작성된 블록부터 역순으로 실행된다.
- 중첩해서 사용할 수 있다. 이때는 **바깥쪽 블록부터 실행되며 가장 안쪽에 있는 블록은 가장 마지막에 실행된다.

-> 함수가 연산을 처리하는 과정에 영향을 끼치지 않으면서 실행해야 할 다른 내용이 있을 때 사용하거나, 함수를 종료하기 직전에 정리해야 하는 변수나 상수값들을 처리하는 용도로 사용.

함수를 인자로 넘길 수 있다는 특성의 장점.

  • 함수 내부에서 함수나 메소드의 객체를 직접 전달하므로, 검색을 통해 찾지 않아도 되어 효율적으로 실행할 수 있다.
  • 함수를 주고받을 수 있으므로, 기존 함수를 데코레이션 하는 문법 구현이 가능하다. (파이썬 데코레이터)
profile
https://github.com/0hhanum

0개의 댓글