몸이 너무 안좋은 날
오히려 집중은 잘 됐다.
사실, 지금까지 들어왔던 강의, 참고했던 Swift 공식 문서를 통해 공부했던 내용들과 크게 다르지 않기에 새로 보게된 용어 및 개념, 반복되고있지만 중요한 것들 몇개만 짚고 넘어가보자.
- Swift는 nil(값이 없음)을 허용하지 않기에 필요시에 사용하기 위함.
- Optional Type의 값에 접근하면 Optional로 감싸진 값이 나옴. 이를 Optional로 래핑된 값이라고 부름.
- 기본적으로 Optional로 래핑된 값은 기본 타입과 연산이 되지 않음.
- Optional 타입을 포함하는 데이터 구조에서 옵셔널 값이 nil인지를 체크할 수 있는 방법
- 쉽게 말해 규약, class, struct, enum에서 공통으로 구현해야 하는 메소드와 프로퍼티를 정의하는 기능
- class, struct, enum에서 채택할 수 있으며, 정의한 모든 프로퍼티와 메소드를 구현해야 함.
- class, struct, enum, protocol 타입에 새로운 기능을 추가할 수 있는 키워드
- 기존 타입을 확장
- 하나 이상의 프로토콜을 extension 가능
- 연산 프로퍼티, 메소드, 새로운 초기화, 중첩된 타입에는 확장 가능
- 저장 프로퍼티에는 불가능
문제 3,4,5 완료 및 1,2 수정
/* 문제 3.
- [ ] Int 배열의 짝수번째 요소를 제거해서 반환하는 함수 `a` 를 작성해주세요.
- 테스트 입력: [1, 2, 3, 4, 5]
- 테스트 출력: [2, 4]
- [ ] String 배열의 짝수번째 요소를 제거해서 반환하는 함수 `b` 를 작성해주세요.
- 테스트 입력: [”가”, “나”, “다”, “라”, “마”]
- 테스트 출력: [”나”, “라”]
- [ ] 위 두 함수를 하나의 함수로 대체할 수 있는 방법을 고민해보고, 함수 `c` 로 작성해주세요.
- [ ] 테스트 입력들을 넣고 호출하여 출력이 제대로 나오는지 작성해주세요.
- 테스트 입력: [1, 2, 3, 4, 5], [”가”, “나”, “다”, “라”, “마”]
- 테스트 출력: [2, 4], [”나”, “라”]
- [ ] 함수 `c` 를 기반으로 수정하여 함수 `d` 를 작성해주세요.
- 파라미터의 타입을 << 'Numeric 프로토콜'을 준수하는 타입의 요소를 가진 배열 >> 로 변경합니다.
*/
/* 1. `a` 함수 작성 */
func a(arr: [Int])->[Int]{
var iArr: [Int] = []
for idx in 0..<arr.count{
if idx % 2 != 0{
iArr.append(arr[idx])
}
}
return iArr
}
let iArray = [1,2,3,4,5]
print(a(arr: iArray))
/* 2. `b` 함수 작성 */
func b(arr: [String])->[String]{
var sArr: [String] = []
for idx in 0..<arr.count{
if idx % 2 != 0{
sArr.append(arr[idx])
}
}
return sArr
}
let sArray: [String] = ["가","나","다","라","마"]
print(b(arr: sArray))
/* 3. `c` 함수 작성 */
func c<T>(arr: [T])->[T]{
var cArr: [T] = []
for idx in 0..<arr.count{
if idx % 2 != 0{
cArr.append(arr[idx])
}
}
return cArr
}
print(c(arr: iArray))
print(c(arr: sArray))
/* 4. `d` 함수 작성 */
func d<T: Numeric>(arr: [T])->[T]{
var dArr: [T] = []
for idx in 0..<arr.count{
if idx % 2 != 0{
dArr.append(arr[idx])
}
}
return dArr
}
print(d(arr: iArray))
print(d(arr: [1.0,2.0,3.0,4.0,5.0]))
/* 문제 4.
우리는 여러 타입에 “자기소개” 기능을 부여하고자 합니다.
- [ ] Introducible 프로토콜을 정의하세요.
- name: String 프로퍼티를 요구사항으로 포함합니다.
- introduce() -> String 메서드를 요구사항으로 포함합니다.
- 동작 예시: print("안녕하세요, 저는 \(name)입니다.")
- [ ] Robot, Cat, Dog 타입을 정의하고 Introducible 프로토콜을 채택해주세요.
- [ ] Robot 타입의 경우, name 값이 변경될 때마다 변경 이전값과 이후 값을 출력하도록 구현해주세요.
- 만약 변경 이전값과 이후값이 같다면 출력하지 않아야합니다.
- 출력 예시
```swift
name 변경 알림
변경 이전 값: 피규어
변경 이후 값: 옵티머스
```
- [ ] Introducible 프로토콜 에 정의되지 않은 각 타입 고유의 메서드들도 하나씩 추가 정의해주세요.
- 예를 들어, Robot 은 충전하기(batteryCharge) 라는 메서드를 추가 할 수 있습니다.
- [ ] `[Introducible]` 타입 배열을 정의하고, Robot, Cat, Dog 인스턴스 1개씩을 append 해주세요.
- 배열을 순회하며 각 타입 고유의 메서드들을 호출하는 코드를 작성해주세요.
*/
/* Introducible 프로토콜 정의 */
protocol Intoroducible {
var name: String {get set}
func introduce()->String
}
/* Robot 정의 */
struct Robot: Intoroducible {
var name: String{
didSet {
print("name 변경 알림")
print("변경 이전 값: \(oldValue)")
print("변경 이후 값: \(name)")
}
}
var batterySts: Int = 50
func introduce() -> String {
return "안녕하세요, 저는 \(name)입니다."
}
/* Robot 고유 메서드 생성 */
mutating func batteryCharge(){
if batterySts == 100{
print("배터리 충전이 완료된 상태입니다.")
}else{
self.batterySts += 10
print("배터리가 충전되었습니다.")
}
print("현재 배터리 잔량은 \(batterySts)입니다.")
}
}
/* Cat 정의 */
struct Cat: Intoroducible {
var name: String
func introduce() -> String {
return "안녕하세요, 저는 \(name)입니다."
}
/* Cat 고유 메서드 생성 */
func meow(){
print("고양이가 울고있습니다.")
}
}
/* Dog 정의 */
struct Dog: Intoroducible{
var name: String
func introduce() -> String {
return "안녕하세요, 저는 \(name)입니다."
}
/* Dog 고유 메서드 생성 */
func bark(){
print("강아지가 짖고있습니다.")
}
}
/* robot,cat,dog 인스턴스 생성 -> append */
var robot = Robot(name: "피규어")
var cat = Cat(name: "길고양이")
var dog = Dog(name: "길강아지")
var arr: [Intoroducible] = []
arr.append(robot)
arr.append(cat)
arr.append(dog)
/* robot name 변경 */
robot.name = "옵티머스"
/* 배열 순회하며 고유 메서드 출력 */
for idx in arr {
print(idx.introduce())
if let r = idx as? Robot {
robot.batteryCharge()
} else if let c = idx as? Cat {
cat.meow()
} else if let d = idx as? Dog {
dog.bark()
}
}
/* 문제 5.
우리는 간단한 **택배 도착 예측 시스템**을 만들고 있다고 가정합니다.
사용자에게 예상 도착일을 알려주려 하지만, 다음과 같은 여러 상황에서 문제가 발생할 수 있습니다:
- 주소가 잘못된 경우
- 배송이 아직 시작되지 않은 경우
- 시스템 서버 에러로 예측이 불가능한 경우
- [ ] 배송 상태를 표현하는 DeliveryStatus 열거형을 구현하고, 아래 3가지 상태를 포함하도록 합니다.
- notStarted
- inTransit(daysRemaining: Int)
- error
- [ ] 사용자 정의 에러 타입 DeliveryError를 Error 프로토콜을 따르도록 정의합니다.
- invalidAddress
- notStarted
- systemError(reason: String)
- [ ] 아래 시그니처를 가진 throwing function 을 구현해봅니다.
```swift
func predictDeliveryDay(for address: String, status: DeliveryStatus) throws -> String
```
- 주소가 빈 문자열이면 DeliveryError.invalidAddress를 던져야 합니다.
- 배송이 아직 시작되지 않은 경우 DeliveryError.notStarted를 던져야 합니다.
- 시스템 에러 상태면 DeliveryError.systemError(reason:)을 던져야 합니다.
- 나머지 경우에는 "배송까지 X일 남았습니다." 형태의 문자열을 반환합니다.
- [ ] 위 함수를 do-catch 로 호출하고, 각 에러 상황에 따라 사용자에게 다른 메시지를 출력하세요.
*/
/* 1.DeliveryStatus Enum 구현*/
enum DeliveryStatus {
case notStarted
case inTransit(daysRemaining: Int)
case error
}
/* 2. DeliveryError 구현(Error Protocol)*/
enum DeliveryError: Error{
case invalidAddress
case notStarted
case systemError(reason: String)
}
/* 3. throwing function 구현 */
func predictDeliveryDay(for address: String, status: DeliveryStatus) throws -> String{
if address.isEmpty{
throw DeliveryError.invalidAddress
}
switch status{
case .notStarted:
throw DeliveryError.notStarted
case .error:
throw DeliveryError.systemError(reason: "시스템에 오류가 있습니다.")
case .inTransit(daysRemaining: let days):
return "배송까지 \(days)일 남았습니다."
}
}
/* 4. do-catch로 호출 */
do{
let result = try predictDeliveryDay(for:"" ,status: .inTransit(daysRemaining: 3))
print(result)
} catch DeliveryError.invalidAddress{
print("주소가 입력되지 않았습니다.")
} catch DeliveryError.notStarted{
print("배송이 아직 시작되지 않았습니다.")
} catch DeliveryError.systemError(reason: let reason){
print("오류: \(reason)")
}
let sum: (Int, Int) -> String = {
val1, val2 in
"두 수의 합은 \(val1 + val2)입니다."
}
//let sum: (Int, Int) -> String = {"두 수의 합은 \($0 + $1)입니다."}
위 내용을 아래와 같이 변경(요구사항에 더 적합하다 생각함)
let sum: (Int, Int) -> String = closure
func calculate(calClosure: (Int, Int) -> String){
let result = calClosure(10,20)
print(result)
}
마찬가지로 위 내용을 아래와 같이 변경함
func calculate(calClosure: (Int, Int) -> String){
let result = calClosure(10,20)
print(result)
}
코드에는 정답은 없다. 그러나 오답은 존재한다. 내일은 문제의 요구사항에 적합하게 문제를 풀어냈는가, 출제자가 의도한 바를 충족하는가에 관한 생각으로 다시 한번 문제를 풀어보며 수정이 필요한 부분은 수정을 진행할 것이다. 또한, 본인은 고차함수 및 심화 내용들은 공부하지 않고 문제를 풀어나갔기에 최적화되어있지않을 것이라 생각되는 부분이 많다. 앞으로 심화주차를 공부하면서 알게된 개념들을 통해 다시 문제에 적용하여 코드를 보다 최적화되도록 할 것이다. 필수 문제에 관한 풀이는 끝이지만, 도전 문제는 바로 시도하지 않는다. 심화주차에 대한 공부를 진행한 후 그에 대한 이해를 바탕으로 풀이할 생각이다. 물론, 다 풀어야겠지만
ref. https://bbiguduk.gitbook.io/swift/language-guide-1/generics#extending-a-generic-type
제네릭 참고 자료
ref. https://developer.apple.com/documentation/swift/numeric
Numetric