Swift – 함수, 콜렉션 타입

오늘의 주제

  • 함수
  • 콜렉션 타입 - 배열, 딕셔너리, 세트

안녕하세요, 야곰입니다. 지난 포스팅에서는 변수와 상수 선언방법 그리고 기초적인 데이터 타입까지 알아봤습니다.

2017/01/23 - [Swift] - Swift란 어떤 언어인가?
2017/01/25 - [Swift] - Swift 기초문법 - 변수, 상수, 기초 데이터 타입

이번에는 스위프트의 함수와 함께 스위프트에서 제공하는 컬렉션 타입에 대해 알아보겠습니다 🙂

함수

대부분 함수는 작업의 가장 작은 단위이자 하나의 작은 프로그램이기도 합니다.
“하나의 프로 그램은 하나의 큰 함수다”라는 말이 있듯이, 함수는 프로그램을 이루는 주된 요소 중 하나입니다.

스위프트에서 함수는 일급 객체이기 때문에 하나의 값으로도 사용할 수 있습니다. 스위프트에서 함수는 다른 언어보다 훨씬 다양한 모습으로 존재하며, 코딩 스타일도 여러 가지입니다. 따라서 개인이나 협업자끼리 코딩 규칙을 만들고 함수를 사용하기를 권합니다. 

함수와 메서드
함수와 메서드는 기본적으로 같습니다. 다만 상황이나 위치에 따라 다른 용어로 부르는 것뿐입니다. 구조체, 클래스, 열거형 등 특정 타입에 연관되어 사용하는 함수를 메서드, 모듈 전체에서 전역적으로 사용할 수 있는 함수를 그냥 함수라고 부릅니다. 즉, 함수가 위치하거나 사용되는 범위 등에 따라 호칭이 달라질 뿐, 함수라는 것 자체에는 변함이 없습니다. 

함수의 정의와 호출 

스위프트의 함수는 재정의(오버라이드)와 중복 정의(오버로드)를 모두 지원합니다. 따라서 매개변수의 타입이 다르면 같은 이름의 함수를 여러 개 만들 수 있고, 매개변수의 개수가 달라도 같은이름의 함수를 만들 수 있습니다. 그렇기 때문에 예제 중간중간 이름이 같은 함수를 구현해도 오류가 발생하지 않습니다. 

기본적인 함수의 정의와 호출

스위프트의 함수는 자유도가 굉장히 높은 문법 중 하나입니다. 기본으로 함수의 이름과 매개변수(Parameter), 반환 타입(Return Type) 등을 사용하여 함수를 정의합니다. 

함수를 정의하는 키워드는 func입니다. 함수 이름을 지정해준 후 매개변수는 소괄호( ( ) )로 감싸줍니다. 반환 타입을 명시하기 전에 -> 를 사용하여 어떤 타입이 반환될 것인지 명시해줍니다.
반환을 위한 키워드는 다른 언어처럼 return입니다. 

함수의 기본 형태는 다음과 같습니다. 

func 함수이름(매개변수...) -> 반환타입 { 
    // 실행 구문 
    return 반환 값 
} 

간단한 함수를 구현해 봅니다. 함수 이름은 hello이며, name이라는 이름의 String 타입 매개변수를 가지며, String 타입의 값을 반환하는 함수입니다. 또, greeting이라는 문자열에 hello 함수의 반환 값을 저장하고 출력합니다.

func hello(name: String) -> String {
    return "Hello \(name)!"
}

let greeting: String = hello(name: "yagom")
print(greeting)   // Hello yagom!

매개변수가 없는 함수

함수에 매개변수가 필요 없다면 매개변수 위치를 공란으로 비워둡니다. 

func helloWorld() -> String {
    return "Hello, world!"
}

print(helloWorld()) // Hello, world!

매개변수가 여러 개인 함수 

매개변수가 여러 개 필요한 함수를 정의할 때는 쉼표(,)로 매개변수를 구분합니다.
주의할 점은 함수를 호출할 때, 매개변수 이름을 붙여주고 콜론(:)을 적어준 후 전달인자를 보내준다는 점입니다. 

func sayHello(myName: String, yourName: String) -> String {
    return "Hello \(yourName)! I'm \(myName)"
}

print(sayHello(myName: "yagom", yourName: "eric"))
// Hello eric! I'm yagom

컬렉션 타입

스위프트는 많은 수의 데이터를 묶어서 저장하고 관리할 수 있는 컬렉션 타입을 제공합니다. 컬렉션 타입에는 배열(Array), 딕셔너리(Dictionary), 세트(Set) 등이 있습니다.

이번 포스팅에서는 배열과 딕셔너리에 대해 알아봅니다.

배열

배열은 같은 타입의 데이터를 일렬로 순서대로 저장하는 형태의 컬렉션 타입입니다. 각기 다른 위치에 같은 값이 들어갈 수도 있음을 알아두세요.

let 키워드를 사용하여 상수로 선언하면 변경할 수 없는 배열이 되고, var 키워드를 사용하여 변수로 선언해주면 변경 가능한 배열이 됩니다.
실제로 배열을 사용할 때는 Array라는 키워드와 타입 이름의 조합으로 사용합니다.
또, 대괄호로 값을 묶어 Array 타입임을 표현할 수도 있습니다. 이처럼 배열 타입을 선언해줄 수 있는 방법은 다양합니다.

빈 배열은 이니셜라이저 또는 리터럴 문법을 통해 생성해줄 수 있는 데 isEmpty 프로퍼티를 통해 비어있는 배열인지 확인해볼 수 있습니다. 그리고 배열에 몇 개 의 요소가 존재하는지 알고 싶으면 count 프로퍼티를 확인하면 됩니다. 

스위프트의 Array
스위프트의 Array는 C 언어 또는 자바의 배열과는 다른 형태의 타입입니다. 스위프트의 Array는 자료구조 과목에서 배우는 연결리스트Linked-List의 형태를 띄고 있습니다. 스위프트에서 연결리스트의 형태를 띠고 있는 이 데이터 타입을 Array, 즉 배열이라고 표현하고 있기 때문에 여기서도 Array를 배열이라고 표현 하겠습니다. 

var names: Array<String> = ["yagom", "chulsoo", "younghee", "yagom"]
// 대괄호를 사용하여 배열임을 표현합니다. 

var names: [String] = ["yagom", "chulsoo", "younghee", "yagom"]
// 위의 선언과 정확히 동일한 표현으로, [String]은 Array<String>의 축약 표현입니다.

var emptyArray: [String] = [String]() 
// String 데이터를 요소로 갖는 빈 배열을 생성합니다. 

var emptyArray: [String] = Array<String>() 
// 위의 선언과 정확히 같은 동작을 하는 코드입니다. 

var emptyArray: [String] = []
// 배열의 타입을 정확히 명시해줬다면 []만으로도 빈 배열을 생성할 수 있습니다.

print(emptyArray.isEmpty)   // true
print(names.count)          // 4 

배열은 각 요소에 인덱스를 통해 접근할 수 있습니다.
인덱스는 0부터 시작합니다. 잘못된 인덱스로 접근하려고 하면 오류가 발생합니다. 

맨 처음과 맨 마지막 요소는 firstlast 프로퍼티를 통해 가져올 수 있습니다. 
index(of:) 메서드를 사용하면 해당 요소의 인덱스를 알아낼 수도 있습니다. 만약, 중복된 요소가 있다면 제일 먼저 발견된 요소의 인덱스를 반환합니다. 
맨 뒤에 요소를 추가하고 싶다면 append(_:) 메서드를 사용합니다. 
중간에 요소를 삽입하고 싶다면 insert(_:at:) 메서드를 사용하면 됩니다. 
요소를 삭제하고 싶다면 remove(_:) 메서드를 사용하게 되는데, 메서드를 사용하면 해당 요소가 삭제된 후 반환됩니다.

print(names[2])     // younghee

names[2] = "jenny"

print(names[2])     // jenny
print(names[4])     // 인덱스의 범위를 벗어났기 때문에 오류가 발생합니다.

names[4] = "elsa"   // 인덱스의 범위를 벗어났기 때문에 오류가 발생합니다.
names.append("elsa")    // 마지막에 elsa가 추가됩니다.

names.append(contentsOf: ["john", "max"]) // 맨 마지막에 john과 max가 추가됩니다. 
names.insert("happy", at: 2) // 인덱스 2에 삽입됩니다. 
names.insert(contentsOf: ["jinhee", "minsoo"], at: 5)
// 인덱스 5의 위치에 jinhee와 minsoo가 삽입됩니다. 

print(names[4]) // yagom
print(names.index(of: "yagom"))     // 0
print(names.index(of: "christal"))  // nil
print(names.first)      // yagom
print(names.last)       // max

let firstItem: String = names.removeFirst()
let lastItem: String = names.removeLast()
let indexZeroItem: String = names.remove(at: 0)

print(firstItem)        // yagom
print(lastItem)         // max
print(indexZeroItem)    // chulsoo
print(names[1 ... 3])   // ["jenny", "yagom", "jinhee"]
  • 중간중간 몇몇 print 함수를 실행하려고 하면 경고를 띄워주는 이유는 해당 메서드의 반환값이 옵셔널이기 때문입니다. 옵셔널에 대해서는 다음 포스팅에서 다룹니다.
  • nil은 ‘없음’을 의미하는 표현입니다.

위의 코드 맨 아래 줄의 names[1 ... 3] 표현은 범위 연산자를 사용하여 names 배열의 일부만 가져온 것입니다.
코드처럼 읽기만 가능한 것이 아니라 names[1 ... 3] = ["A", "B", "C"]와 같이 범위에 맞게 요소를 바꾸는 것도 가능합니다.

스위프트의 배열을 비롯한 컬렉션 타입을 활용할 때 서브스크립트(Subscript) 기능을 많이 사용합니다. 

딕셔너리

딕셔너리는 요소들이 순서 없이 키와 값의 쌍으로 구성되는 컬렉션 타입입니다. 
딕셔너리에 저장되는값은 항상 키와 쌍을 이루게 되는데, 딕셔너리 안에는 키가 하나이거나 여러 개일 수 있습니다. 단, 하나의 딕셔너리 안의 키는 그 안에서는 유일해야 합니다.

쉽게 말해서 아래의 코드에서 “yagom”이라는 키가 두 번 쓰일 수 없다는 뜻입니다.
즉, 딕셔너리에서 키는 값을 대변 하는 유일한 식별자가 되는 것입니다.

let 키워드를 사용하여 상수로 선언하면 변경 불가능한 딕셔너리가 되고, var 키워드를 사용하여 변수로 선언해주면 변경 가능한 딕셔너리가 됩니다. 

딕셔너리는 Dictionary라는 키워드와 키의 타입과 값의 타입 이름의 조합으로 써줍니다.
대괄호로 키와 값의 타입 이름의 쌍을 묶어 딕셔너리 타입임을 표현합니다. 
빈 딕셔너리는 이니셜라이저 또는 리터럴 문법을 통해 생성할 수 있습니다. isEmpty 프로퍼티를 통해 비어있는 딕셔너리인지 확인할 수 있습니다. 그리고 count 프로퍼티로 딕셔너리의 요소 개수를 확인할 수 있습니다.

// 키는 String, 값은 Int 타입인 빈 딕셔너리를 생성합니다.
var numberForName: Dictionary<String,int> = Dictionary<String,int>()

// 위의 선언과 정확히 동일한 표현입니다.
var numberForName: [String: Int] = [String: Int]()
// [String: Int]는 Dictionary<String, int>의 축약 표현입니다.

// 딕셔너리의 키와 값 타입을 정확히 명시해줬다면 [:]만으로도 빈 딕셔너리를 생성할 수 있습니다. 
var numberForName: [String: Int] = [:]

var numberForName: [String:Int] = ["yagom":100, "chulsoo":200, "jenny":300]
// 초깃값을 주어 생성해줄 수도 있습니다.

print(numberForName.isEmpty) // false 
print(numberForName.count) // 3

딕셔너리는 각 값에 키를 통해 접근할 수 있습니다.
딕셔너리 내부에서 키는 유일해야 하며, 값은 유일하지 않습니다.

딕셔너리는 배열과는 다르게 딕셔너리 내부에 없는 키로 접근해도 오류가 발생하지 않습니다. 다만 nil을 반환할 뿐이죠. 

특정 키에 해당하는 값을 제거하려면 removeValue(forKey:) 메서드를 사용하면 됩니다.키에 해당하는 값이 제거된 후 반환됩니다.

print(numberForName["chulsoo"]) // 200
print(numberForName["minji"])   // nil

numberForName["chulsoo"] = 150
print(numberForName["chulsoo"]) // 150

numberForName["max"] = 999      // max라는 키로 999라는 값을 추가해줍니다.
print(numberForName["max"])     // 999

print(numberForName.removeValue(forKey: "yagom"))   // 100
print(numberForName.removeValue(forKey: "yagom"))   // nil
// 위에서 yagom 키에 해당하는 값이 이미 삭제되었으므로 nil이 반환됩니다.

이번에 스위프트의 기초적인 형태의 함수와 컬렉션 데이터 타입에 대해 알아봤습니다.

다음 포스팅에서는 구조체와 클래스에 대해 알아보겠습니다. 
다음 번에 또 뵈어요~ 고맙습니다 😀

본 글의 일부내용은 필자의 저서 [스위프트 프로그래밍](2017, 한빛미디어)(http://www.hanbit.co.kr/store/books/look.php?p_code=B5682208459)에서 요약, 발췌하였음을 알립니다.

스위프트의 문법에 대해 더 알아보고 싶다면 애플의 Swift Language Guide[https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html]를 참고해도 많은 도움이 됩니다.

by yagom


facebook : https://www.facebook.com/yagompage
facebook group : https://www.facebook.com/groups/yagom/

p.s 제 포스팅을 RSS 피드로 받아보실 수 있습니다.
RSS Feed 받기   

댓글 남기기

Close