조건문 ( if, else, else if, switch)
if 조건문에 대해 알아보자
if는 if 명령어만 단독으로 사용 가능하고, else, else if 와 조합해서 사용 가능하다. Swift에 조건을 지정하고 해당조건이 true이면 실행한다.
→ Swift에서 if조건문을 사용하는 방법은 위 주석 코드와 같다. 조건이 true가 되면 위 코드블록처럼 if조건식이 실행될 것이다.
→ 나이가 미성년자인지 알 수 있는 if조건문을 만들어 보자. 우선 age라는 상수를 먼저 선언하고, 12살로 가정을 하였다. 그리고, if 조건문을 통해 age < 19 나이가 19살보다 작으면 미성년자입니다. 라는 조건을 만들었고, print를 통하여 "미성년자 입니다." 라고 출력을 하게 만들었다. 상수로 선언한 나이가 12살이므로, 출력시 "미성년자 입니다."라는 조건이 true가 되어 출력되어 진다.
if else 조건문에 대해 알아보자.
→ if else 조건문은 위 코드처럼 선언하면 된다.
→ if else를 통해 나이가 미성년자인지 성년자인지 구분하는 코드를 작성해보자. 우선 if조건문으로 age < 19 나이가 19살보다 작으면 print를 통해 "미성년자"가 호출되게 만들었고, else 그게 아니라면 "성년자" 가 출력되게 만들었다. 만일에 age를 let age = 20 으로 가정을 하여 20살이라고 했을때, if의 첫번째 조건을 지난 else구문으로 넘어가 실행되어 "성년자"라고 출력이 되어질 것이다.
만약 비교할 조건이 많을시 if else를 활용해 구현한다면 코드가 상당히 복잡해질 수 있다. 이럴때는 if와 else if 조건문을 활용하여 사용하면 된다.
→ if, else if 조건문은 위 주석의 코드처럼 생각하면 된다.
그러면 if, else if에 관련된 조건문 코드를 하나의 예로 만들어보자.
→ 우선 animal이라는 상수를 선언후 "cat"이라는 문자열로 초기화 시켜주었다. 이제 if조건문을 활용할건데 if animal이 "dog" 강아지라면 print를 통하여 ("강아지 사료주기") 가 출력이 될 것이고, else if animal = "cat" 첫번째 if 조건구문이 false 일시 else if 구문이 실행되어 밑의 else 구문을 실행하지 않고 ("고양이 사료주기")가 출력이 되어 if문을 탈출할 것이다. 이제 끝의 구문인 else 구문은 if와 else if 둘다아닐시 실행되어 "해당하는 동물 사료가 없음"으로 출력이 될 것이다.
Switch 조건문에 대해 알아보자. (switch 조건문은 if 조건문과 달리 패턴기반으로 실행이 된다.)
Switch 구문의 특징
- 각각의 case내부에는 실행가능한 코드가 반드시 위치해야 한다.
- enum의 case등 매우 한정적인 값이 비교값이 아니라면 default 구문은 반드시 작성해야 한다.
- break를 추가 하지 않아도 자동으로 case마다 break가 된다.
- fallthrough 키워드를 사용해서 break를 무시할 수 있고, 원하는만큼 여러번 사용 가능하다.
- 쉼표를 사용하여 하나의 case에 여러패턴을 명시 할 수 있다.
- 원하는만큼 많은 case를 작성할 수 있다.
→ 위 코드를 보면 switch 조건문은 다음과 같이 사용이 된다. if 조건문과는 달리 패턴이 일치하면 case아래에 있는 구문들이 실행이 되어 각 패턴 키워드는 case를 통해 나타날수 있고, case패턴 끝은 :(콜론)으로 패턴을 종료한다. 하나의 패턴이 일치하면 switch문은 종료된다.
→ 그러면 case에 해당하는 색상을 출력하는 swtich문을 작성해보자. color라는 상수를 선언후 green이라는 문자열을 넣었다. 그리고 switch키워드에 비교대상인 color를 입력한후 조건에 맞는 색을 print함수를 통해 출력하였다.
- case가 "blue" 라면 "파란색 입니다." 가 출력
- case가 "green" 이라면 "초록색 입니다." 가 출력
- case가 "yellow" 라면 "노랑색 입니다." 가 출력
- 위 3가지 패턴에 맞는 색상이 없을시 "찾는 색상이 없습니다."가 출력
상수로 선언한 색상의 "green"이 그대로 실행된다면 "초록색 입니다." 조건에 해당되어 그대로 switch문이 종료된다. 만일 여기에 있지 않은 다른색상인 "red"라고 가정했을때 패턴에 맞는 색상에 충족되지 않아 default구문이 실행되면서 "찾는 색상이 없습니다." 구문이 실행되면서 "찾는 색상이 없습니다." 라고 출력될 것 이다.
또한 비교패턴에는 특정값 뿐만 아니라 범위연산자를 통해서도 비교가 가능하다.
범위 연산자를 사용하여 온도에 따라 계절을 알려주는 코드를 작성해보자.
→ 먼저 temperature 라는 상수를 먼저 선언후 30이라는 값을 넣어 초기화 시켜주었다. switch 비교대상자인 temperature라는 상수를 넣어준 후 비교패턴에 case -20...9도 사이면 겨울입니다.가 출력되게 하고, 10...14:는 가을, 15...25:는 봄, 26...35:는 여름, 이 4가지 패턴이 다 일치하지 않으면 default구문이 실행되어 "이상 기후입니다."가 출력되게 하였다.
상수 선언시 30이라고 선언하였기 때문에 온도가 30이므로 26...35: 사이의 패턴을 만족하여 "여름 입니다"가 출력되며, 이렇게 비교패턴안에 범위연산자를 사용하여 switch문을 구현할 수 있다.
switch 입력값 {
case 비교값
// 실행 구문
case 비교값
// 실행 구문
// case를 마치고 switch구문 탈출하지 않고 계속 진행
fallthrough
case 비교값, 비교값, 비교값 // 한번에 여러값과 비교 가능
break // 종료
defalut : // 한정된 범위가 명확하지 않다면 사용 필수
// 실행 구문
}
→ 위 코드처럼 작성하게 된다면 "에어컨이 필요하다" 문자열을 반환받게 된다. 각 case에는 break가 자동설정되어 true값을 얻게 되면 구문에서 빠져나와 switch문이 종료된다.
하지만, fallthrough을 사용하여 break를 무시하고 계속 실행해보자.
→ fallthrough를 사용하여서 계속 실행을 하였고, 보다시피 실행을 하면 "에어컨이 필요하다"를 반환을 하고 default값도 반환되어 출력되는 모습을 확인할 수 있다.
튜플 : 어떠한 값들의 묶음
참조 : https://babbab2.tistory.com/31
let zoonee = ("zoonee", 20, 180.9)
이렇게 괄호 안에 원하는 타입을 나열해서 쓰는것을 튜플이라고 한다. String, Int, Double 자료형등을 마음대로 섞어서 튜플로 나타낼수 있다. 하지만 튜플에도 몇가지 규칙이라는게 있다.
→ 튜플에 저장된 값에 접근하려면 .(dot) 문법을 사용한다. 위 코드를 보면 튜플을 선언시 나열된 순서대로 배열처럼 index를 가진다. 이 튜플의 값에 접근하는 방법을 알아보자.
→ 위 코드의 사진처럼 .(dot)을 이용해서 index로 접근 가능하다.
let으로 선언하면 Immutable Tuple, var로 선언하면 Mutable Tuple.
→ 당연히 에러가 나는거라고 볼 수 있지만 상수로 선언된 zoonee이기에 당연히 튜플 값도 상수로 저장된다.(Immutable Tuple). 그러면 var로 선언하면 어떨지 확인해보자.
→ var은 변할수 있는 변수이기에 당연히 수정 가능하다.(Mutable Tuple)
그리고, 튜플을 선언한 후엔 자료형 및 멤버의 갯수는 수정할 수 없다.
→ 위 코드를 보면 이 튜플의 자료형은 type(of: zoonee) 이 구문을 실행해서 출력된걸 보면 되는데 이 구문을 보면 타입을 확인 할 수 있다. (String, Int, Double).Type. 여기에 1번째 인덱스에 Int값이 아닌 String 값을 대입하려고 한다.
→ zoonee 상수에 첫번째 인덱스값에 접근하여, 인트타입을 스트링타입으로 바꾸려 해보았다. 그럼 Int타입인데 String타입을 넣었다는 에러가 발생한다. 튜플로 선언한것도 하나의 자료형으로 선언이후 자료형 및 멤버의 갯수를 바꿀수 없다.
그리고 튜플은 멤버들의 이름을 붙여 줄 수 있다.
→ 이름을 붙여주게 되면 접근할때도 index가 아닌 이름으로 접근이 가능해 가독성이 좋아진다.
그럼 이젠 분해 하는 법에 대해 알아보자.
→ 위 코드를 보면 let 상수구문에 차례대로 (name, age, height)를 생성하고 튜플 멤버 값을 순서대로 저장하는 것이다.
보면 너무나 간단하게 선언하는 상수인만큼 주의할점이 있다. 튜플의 갯수와 지정할 상수의 갯수는 동일해야 한다는 것. 튜플의 zoonee의 값이 3개면 무조건 상수도 3개가 와야한다. 만약 3개보다 많거나 적다면 오류가 발생한다.
그런데 만일 선언된 상수에서 name과 age의 상수만 선언을 하고 싶다면 오류가 발생하는데 이 오류를 발생하지 않기 위해서 와일드패턴이라는 걸 이용하면된다. 상수를 생략하고 싶다면 wildcard pattern을 이용하면 된다.
→ 와일드카드 패턴은 어떤값과도 일치하고 무시하며, "밑줄( _ )"로 구성한다. 맞춰볼 값에 신경을 쓰고 싶지 않을때 와일드카드 패턴 을 사용하고, 위의 코드를 보면 반복문의 각 회차에 대한 현재의 범위 값을 무시하면서 1...3이라는 닫힌범위를 반복한다. 그래서 사용하고 싶다면 필요없는 멤버를 생략하고 바인딩 할 수 있고, 상수와 튜플갯수가 맞지않더라도 와일드카드패턴을 통해서 사용하면 오류가 발생하지않고 실행이 된다.
그리고 Tuple Matching이라는 것도 확인해보자.
→ 위 코드와 같이 resolution이라는 상수를 선언후 값을 주었다. 그리고 switch 문에 조건을 주었다. 보면 case 조건을 위 코드와 같이 Tuple자료형으로 설정하였고, 따라서 resolution은 본인과 같은 튜플조건문을 가진 case문을 실행하게 된다.
→ wildcard pattern을 이용해 원하는 멤버 값에 대한 조건도 걸 수 있다. ( _ )를 이용해 다음과 같이 원하는 튜플의 멤버값에 대한 조건을 걸 수 있다.
→ Decomposition 문법과 같이 튜플 resolution의 멤버값은 상수 w, h에 바인딩 되고, 이 바인딩 된 상수 w, h값을 이용해 조건문을 만들게 된다.
where 키워드 사용
where키워드를 이용하여 case조건을 확장할 수 있다. 쉽게 말해 조건을 그냥 더 추가해야겠다 싶으면 사용하는거고, 특정타입에 대한 제한을 두고 싶을때 등등 다양한 용도로 사용이 가능한데 이런이유로 많이 사용된다고 보면 될 것 같다. where문은 크게 2개로 쓰인다.
- for문 while문 switch문 등에서 조건을 추가하여 나타낼 떄
- 프로토콜의 extension같은 타입에도 조건을 추가할 때
→ 위 코드를 보면 for문에서 where가 없다면 1...10 까지 출력을 하게 될 것이다. 하지만 where키워드가 추가 됨으로 1%2 == 1 ( 홀수일때 true가 되는 조건) 추가가 되어 10까지의 홀수만 출력을 하게 된다.
→ print를 이용하여 홀수만 출력된 모습이다. 이렇게 코드를 단축시킬 수 있다.
그러면 switch문의 case에도 where문을 넣을 수 있다.
→ 위 코드를 보자. 맨위의 case는 wildcard를 쓰며 where 절의 num % 2 == 0 짝수일때 true가 되는 조건을 추가하여 짝수의 case만 출력할 수 있다. 이렇게 조건을 추가하거나 특정타입에 제한을 두겠다 할때 사용할 수 있다.
→ switch 구문의 값바인딩 패턴을 이용한 case에서 바인딩 된 값들로도 where문을 구성할 수 있다.
그리고 식별자 패턴은
→ 위 코드를 예로 들자면 someValue라는 상수를 선언하였다. let상수를 선언한거고 someValue에 Int데이터타입인 42값을 일치하는 식별자 패턴이다. 어렵게 생각할 것 없이 var 변수나 let 상수에 이름에 맞게 값을 바인딩(할당) 시키는 패턴으로 생각하면 된다. 42는 someValue라는 상수에 바인드(할당) 된 것이다.
→ 여기서 값바인딩패턴은 기존 식별자 패턴으로 바인딩 된 값들을 새로운 변수에 바인딩 하는것. 이라고 생각하면 될 것 같다. 튜플의 값을 식별자 패턴에 맞는 값을 매칭시켜 바인딩해주고, 튜플의 요소를 분해하고 각 요소의 값을 해당 식별자 패턴에 바인드 할 수도 있다.
위 코드에서 let point 상수구문 은 식별자 패턴이라고 보면 될 것이다.
let에서 선언한 상수값을 새로운 변수 switch문의 case let 새로운 변수에 바인딩(할당)하여 사용하는것을 값바인딩패턴이라고 생각하면 된다. 그리고, 이 동작으로 case let(x, y): 이 구문이나 case (let x, let y): 이 구문이나 둘 다 동일하게 봐도 된다.
열거형 사용
참조 : https://babbab2.tistory.com/116
참조 : https://seons-dev.tistory.com/103
열거형은 같은 주제로 연관된 데이터들을 멤버로 구성하여 나타내는 자료형 이라고 보면 된다. 열거형과 같이 한정된 범위의 값을 입력값으로 받게 될 때 값에 대응하는 각 case를 구현한다면 default를 구현하지 않아도 된다. 만약에 값에 대응하는 각 case를 구현하지 않는다면 default는 필수이다.
열거형을 정의한 예제를 보자.
원시값이 없는 열거형
→ case를 일일히 다 써서 나열한 것과
→ case에 콤마를 이용해서 나열한 방식이다.
이렇게 작성하는 방식이 원시값이 없는 열거형으로 보면된다.
실제 사용시는 우리가 선언한 이 열거형이 하나의 자료형이 된다고 보면된다. 그러면 직접 String으로 선언한 코드를 열거형으로 고쳐보자
→ 열거형을 타입처럼 사용하고 열거형으로 타입이 지정된 경우 . (점문법)을 이용해 내가 선언한 case에 한해서만 접근할 수 있다.
원시값이 있는 열거형
case에 원시값을 지정해줄수 있는걸 Raw Value라고 한다. 이때 Raw Value가 될 수 있는 자료형은 3가지 이다.
Number Type을 가지는 열거형
→ 위 코드처럼 Int타입을 enum선언시 이름 옆에 명시해주면 가장 먼저 선언된 case부터 0부터 1씩 증가된 값이 들어간다. 만일 RawValue를 직접 지정하고 싶다면 위 코드처럼 값을 지정해주면 된다. 하지만 Int형이 아닌 자료형 Double이나 Float타입으로 했을 경우 모든 case에 대해 값을 지정해주는것이 아니라면 오류가 발생한다.
오류가 발생하는 이유는 Number Type의 Raw Value는 만약 값이 없으면, 바로 이전 case의 Raw Value의 값에서 1이란 정수값을 더한 값을 가진다. sup의 Raw Value를 컴파일러가 지정해야 하는데, 바로 이전 case인 adc의 Raw Value가 정수값이 아닌 실수값이기 때문에 에러가 발생한다.
그래서 Int형이 아닌 Number 자료형을 사용할 경우, Raw Value를 생략하고 싶다면 바로 이전 case의 Raw Value를 정수값으로 해주면 된다. 정수값으로 해도 Double형으로 형반환 되어 들어간다.
Character Type을 가지는 열거형
→ Character는 이렇게 사용하면 된다. 다만 Character Type으로 열거형을 선언할 경우 모든 case에 대한 Raw Value를 직접선언 해주어야 한다. 만일 하나라도 직접선언을 하지 않고 비어있다면 에러가 발생하니 조심해야 한다.
String Type을 가지는 열거형
→ String은 Character와 달리 Raw Value를 지정하지 않으면, case이름과 동일한 Raw Value가 자동으로 만들어 진다.
→ 원시값이 있는 열거형의 경우 Raw Value에 접근하려면 위 코드와 같이 rawValue라는 속성을 이용하여 접근하면 된다. 단 Raw Value가 있는 열거형의 경우, Raw Value를 통해서도 열거형을 생성할 수 있는데 이때는 다음과 같은 생성자를 이용하면 된다.
+ 추가
Associated Values(연관값)
→ 일엏게 Raw Value를 통해 case별로 원하는 값을 지정해주어도 되지만 이처럼 Raw Value를 사용할 경우,
모든 case가 동일한 형식(위에서 String)으로 Raw Value를 가져야 하고, case 별 값은 미리 지정된 한 가지 값만 가질 수 있는 단점을 지니고 있다.
따라서 이것을 보완해서 사용하는것이 associated value 즉, 연관값이다.
①-㉮. 연관값을 가지는 열거형 선언 방법
→ 이렇게 case옆에 튜플 형태로 원하는 Type을 명시하면 된다. 이때 Tuple은 Named Tuple도 되고, Unnamed Tuple도 가능하다. 따라서 Raw Value로 한계가 있던 예제를 연관 값으로 바꾸면
→ 이런식으로 튜플을 활용해서, 내가 원하는 연관값을 받을 수 있게 선언해줄수 있다.
①-㉯. 연관값을 가지는 열거형 선언 방법
→ 이렇게 열거형 생성시 내가 만들어준 case가 연관값이 함께 뜨고, 써게스트로 직접 값을 지정해서 사용하면 된다.
①-㉰. 연관값을 가지는 열거형 선언 방법
switch product {
case .iPad("5s"): break // 연관값이 5s만 매칭
case .iPad: break // 연관값 무시
case .iPhone("X", _): break // 연관값 생략 가능
case .iPhone(let model, var storage): break // 연관값 상수(변수) 바인딩
case let .macbook(model, storage, size): break // 모든연관값을 let으로 바인딩시 let을 맨앞으로 뺄 수 있음
}
→ switch 매칭 시킬 때, 이런식으로 다양하게 연관값을 매칭시켜서 사용할 수 있다.
①-㉱. 연관값을 가지는 열거형의 if 사용법
if case let .iPhone("8", storage) = product { // product의 첫 번째 연관값이 "8"이면 매칭
print(storage)
}
if case .iPhone(_, 64) = product { // Product의 첫 번째 연관 값은 상관 없고, 두번째 연관값이 64면 매칭
print("iPhone 64GB")
}
→ 이런 식으로, if를 통해서도 연관값을 매칭시킬 수 있음
프로토콜을 채택하는 열거형
Class, Struct와 마찬가지로 Enum에서도 프로토콜을 채택할 수 있다.
enum AppleProduct: String, CaseIterable {
case iPad = "5, 128GB"
case iPhone = "6, 64GB"
case macbook = "Pro, 256GB"
}
AppleProduct.allCases.randomElement()
→ 이렇게 CaseIterable처럼 내가 원하는 프로토콜을 채택할 수 있고, 해당 프로토콜에 지정된 allCases 또한 당연히 사용할 수 있다.
→ Menu라는 열거형에 나중에 case를 추가할수도 있으니 해당 열거형의 값을 처리하는 switch 구무 마지막 case로 와일드카드 case _: 미리 추가하였다. 그러면 나중에 Menu 열거형에 case를 추가해도 switch 구문에서 컴파일러 오류가 발생하지 않는다.
언제 if를 대신해서 Switch문을 사용해야할까?
switch 구문은 변수를 입력받아 미리 정해놓은 여러값들과의 일치 여부를 판단하여 switch문 내의 control flow를 결정한다.
if else 구문은 Boolean의 결과 값을 내놓는 조건문에 따라 true, false 에 해당하는 각각 두 개의 흐름으로 갈라진다.
if else문을 중첩되게 배치하면, 두 개의 흐름뿐만 아니라 세 개, 네 개 등등.. 그 이상의 control flow을 가질 수 있게 된다.
if else 구문을 쓸 수 있는 모든 상황에 switch문을 쓸 수 있는건 아니지만, 반대로 모든 switch 구문은 if else문으로 대체될 수 있다.
즉 하나의 변수를 입력받아 그 변수의 값에 따라 다른 흐름으로 이동할 수 있는 코드를 짜야 할때에 switch문과 if else 구문이 둘 다 사용될 수 있음을 알 수 있다.
'Swift 기초문법' 카테고리의 다른 글
ios 패스트캠퍼스 10 (0) | 2022.03.16 |
---|---|
Swift: 조건문2 (guard) (0) | 2022.03.14 |
Swift: 함수 사용법(func, 피라미터네임, 매개변수, 전달인자 레이블, 와일드카드식별자, 가변매개변수) (0) | 2022.03.12 |
Swift: 컬렉션 타입(Array, Dicionary, Set) (0) | 2022.03.12 |
Swift: 기본 데이터 타입(Int, UInt, Float, Double, Bool, Character, String, Any) (0) | 2022.03.12 |