본문 바로가기
공부/스위프트

[스위프트] 복잡한 함수의 사용

by 초코팅촉 2023. 12. 19.
728x90

스위프트에서 함수를 선언하고 쓰는 데에 정말 복잡한 경우가 있다.

바로 함수를 일급객체로 쓰는 경우다.

함수를 일급객체로 쓰면 변수 및 상수에 저장해 쓸 수 있다.

 

// 이 함수의 타입은 (String, String) -> String 이다
func areYouKorean(name: String, food: String) -> String {
    return "\(name), 당신은 \(food)를 좋아하는 한국인입니다."
}

// 이 함수의 타입도 (String, String) -> String 이다
func areYouAmerican(name: String, food: String) -> String {
    return "\(name), 당신은 \(food)를 좋아하는 미국인입니다."
}

일단 name과 region 파라미터가 있다.

이 둘은 각각 String, Bool을 받아서 하나의 String으로 조합해 반환한다.

// 위 두 함수가 올바르게 작동하는지 실행해보자
var name: String = areYouKorean(name: "김철수", food: "김치")
print("\(name)") // 김철수, 당신은 김치를 좋아하는 한국인입니다.

name = areYouAmerican(name: "조 바이든", food: "타코")
print("\(name)") // 조 바이든, 당신은 타코를 좋아하는 미국인입니다.

// 통합 함수를 만들자
// 이 함수의 타입은 (String, String, String) -> String 이다.
func areYouFrom(name: String, food: String, region: String = "en") -> String {
    if region == "ko" {
        return areYouKorean(name: name, food: food)
    } else {
        return areYouAmerican(name: name, food: food)
    }
}

name = areYouFrom(name: "밀튼 제임스", food: "피자")
print("\(name)") // 밀튼 제임스, 당신은 피자를 좋아하는 미국인입니다.
// en이 기본값으로 들어갔기 때문에 기본적으로 미국인으로 넘어간다.

name = areYouFrom(name: "밀튼 제임스", food: "피자", region: "en")
print("\(name)") // 밀튼 제임스, 당신은 피자를 좋아하는 미국인입니다.

name = areYouFrom(name: "홍길동", food: "김치", region: "ko")
print("\(name)") // 홍길동, 당신은 김치를 좋아하는 한국인입니다.

이 두함수를 각각 name과 food에 할당해 실행했다.

첫 번째로 "김철수, 당신은 김치를 좋아하는 한국인입니다." 가 실행됐고

두 번째도 비슷하게 "조 바이든, 당신은 타코를 좋아하는 미국인입니다."가 반환되어 출력됐다.

그 후 이 함수 두 개를 "areYouFrom"이라는 이름의 함수로 묶어 단순히 안에서 반환할 때 바로 함수를 써서 출력하도록 했다.

 

region 파라미터가 en이 기본값이므로 18번째 줄은 "밀튼 제임스, 당신은 피자를 좋아하는 미국인입니다."로 출력되고
나머지 두 실행에서는 각각 en, ko이므로 밀튼 제임스 ~~홍길동 ~~로 으로 출력된다.

 

그리고 이어지는 추가적인 예시가 있다.

// 함수를 던져서 일을 시켜보자
// 앞서 만든 areYouKorean, areYouAmerican 같은 타입의 함수를 받아 처리하는 함수가 된다
func printNameWithMethod(name: String, food: String, method: (String, String) -> String) {
    let name = method(name, food)
    print("\(name)")
}

printNameWithMethod(name: "김민수", food: "불고기", method: areYouKorean)
// 김민수, 당신은 불고기를 좋아하는 한국인입니다.
printNameWithMethod(name: "일론 머스크", food: "랍스터", method: areYouAmerican)
// 일론 머스크, 당신은 랍스터를 좋아하는 미국인입니다.

여기 printNameWithMethod에서 특이점이 있다면 바로 method 파라미터이다.
이는 (String, String)을 입력받아 String을 출력해주는 함수 자체를 받아준다.
그렇기 때문에 내용을 보면 name에 해당 함수를 name과 food를 넣어주며 바로 할당해 주고 바로 다음줄에서 출력한다.

처음엔 김민수, 불고기를 areYouKorean이랑 같이 넣었기 때문에 "김민수, 당신은 불고기를 좋아하는 한국인입니다."가 출력되고
두 번째엔 각각 일론 머스크, 랍스터가 areYouAmerican과 들어갔기 때문에 "일론 머스크, 당신은 랍스터를 좋아하는 미국인입니다."이 출력되었다.

 

 

마지막 예시를 보자.

// 함수를 만들어 돌려주는 더 큰 함수를 만들어보자
func methodBuilder(region: String) -> (String, String) -> String {
    switch region {
    case "ko":
        return areYouKorean
    default:
        return areYouAmerican
    }
}

var methods: (String, String) -> String = methodBuilder(region: "ko")
name = methods("대장금", "꿀")
print("\(name)") // 대장금, 당신은 꿀을 좋아하는 한국인입니다.

name = methods("영희", "설탕")
print("\(name)") // 영희, 당신은 설탕을 좋아하는 한국인입니다.

methods = methodBuilder(region: "en")
name = methods("스티브 잡스", "사과")
print("\(name)") // 스티브 잡스, 당신은 사과를 좋아하는 미국인입니다.

name = methodBuilder(region: "ko")("둘리", "호의")
print("\(name)") // "둘리, 당신은 호의를 좋아하는 한국인입니다.

methodBuilder 함수는 String 하나를 받아 (String, String) -> String의 형태를 갖는 함수에 전달해 준다.
(String, String) -> String의 형태를 갖는 함수는 String 두 개를 받고 하나의 String을 반환하는 함수를 의미한다.
여기서는 앞서 나온 areYouKorean과 areYouAmerican함수 두개를 의미한다.

그래서 함수의 내부를 보면 우선 어떤 언어를 쓰는지 region로 받는다.
그 후 이를 switch문으로 분기를 나눠 각각 areYouKorean과 areYouAmerican함수로 반환한다.
하지만 이 둘은 String 파라미터가 두 개씩 필요하니 당연히 이를 넣어줘야 한다.
밑에서 어떻게 쓰는지 보자.

 

methodBuilder함수를 methods에 할당할 때 미리 region을 ko로 정해서 넘겨줬다.
그래서 다음의 두 개 예시에서는 따로 region을 지정하지 않고 바로 "대장금, 꿀"과 "영희, 설탕"를 넣어 값을 출력했다.
하지만 밑의 "스티브잡스, 사과"예시에서는 en을 할당해 주었다. 그렇기에  "미국인"으로 출력되었다.

 

또한 마지막 예시에서는 바로 사용한다면 어떻게 되는지를 보여주는 아주 좋은 경우라고 생각한다.

region을 지정해줘야 하니 일단 (region: "ko")를 써주고

그다음에 ("둘리", "호의")을 바로 입력해 줘 값을 넘겨주었다.
해서 값이 정상적으로 홍길동으로 출력됨을 알 수 있었다.