23. Expert #1
Operators
Short-circuit Evaluation, Side Effect
논리 연산자가 논리식을 평가하는 방법에 대해 공부
- 논리식에서 결과를 도출하는데 필요한 최소한의 코드만 실행하는 것을 [달락평가] 라고 한다.
- 표현식을 평가 했을 때 값이 변경되거나 상태가 변경되는 것을 사이드 이펙트라고 한다.
- 논리식에 사이드 이펙트를 발생시킬 수 있는 있는 코드가 포함되어 있으면 논리적인 오류가 발생할 가능성이 높아지기 때문에 조심해야한다.
//flase &&
//true ||
// 달락 평가
var a = 1
var b = 1
func updateLeft() -> Bool {
a += 1
return false
}
func updateRight() -> Bool {
b += 1
return true
}
let resultA = updateLeft()
let resultB = updateRight()
if resultA && resultB {
}
a
b
Operator Methods
기존 연산자가 새로운 형식을 지원하도록 확장 하는 방법에 대해 공부
static func operator(parameters) -> ReturnType { statements }
// 연산자 메소드
"a" == "a"
struct Point {
var x = 0.0
var y = 0.0
}
// 개별 속성 비교
extension Point: Equatable {
static func == (lhs: Point, rhs: Point) -> Bool {
return (lhs.x == rhs.x) && (lhs.y == rhs.y)
}
}
let p1 = Point(x: 12, y: 34)
let p2 = Point(x: 67, y: 89)
p1 == p2
p1 != p2
// prefix 키워드로 선언하면 피연산자 앞에 오는 전치연산자로 선언된다
extension Point {
static prefix func -(pt: Point) -> Point {
return Point(x: -pt.x, y: -pt.y)
}
}
let p3 = -p1
p3.x
p3.y
// 증감연산자
// postfix 키워드를 사용하면 피연산자 뒤에오는 후치 연산자로 사용됨
// 후치 증가 연산자 = 현재 값을 리턴한 다음 현재 값을 1씩 증가 시킨다
extension Point {
static postfix func ++(pt: inout Point) -> Point {
let ret = pt
pt.x += 1
pt.y += 1
return ret
}
}
var p4 = Point(x: 1.0, y: 2.0)
let p5 = p4++
p5.x
p5.y
p4.x
p4.y
Custom Operators
Swift가 제공하지 않는 새로운 연산자를 직접 구현하는 방법에 대해 공부
prefix operator operator postfix operator operator infix operator operator
Reserved Tokens
(, ), {, }, [, ], ., ,, :, ;, =, @, #, &(prefix operator), ->, `, ?, !(postfix operator), /, /
First Character
/, =, -, +, !, *, %, <, >, &, |, ^, ?, ~
static prefix func operator(parameters) -> ReturnType { statements } static postfix func operator(parameters) -> ReturnType { statements } static func operator(parameters) -> ReturnType { statements }
Conditional Statements
Value Binding Pattern
switch 문에서 활용할 수 있는 Value Binding Pattern에 대해 공부
case let name: case var name:
벨류 바인딩 패턴은 매칭시킬 대상을 상수나 변수로 바인딩한 다음에 케이스 블록에서 활용하는 패턴
let a = 1
switch a {
case var x where x > 100: // case의 값을 바꾸려면 상수가 아니라 변수로 지정해야된다.
x = 200
print(x)
default:
break
}
// 벨류 바인딩은 튜플에서도 자주 사용
let pt = (1, 2)
switch pt {
case let(x, y): // 상수로 바인딩 할 때 이 코드가 간단
print(x, y)
case (let x, let y):
print(x,y)
case (let x, var y): // 상수로 바인딩하고 변수로 바인딩 할 때 활용
print(x,y)
case let(x, _): // 첫 번째 값만 바인딩(와일드 패턴을 활용)
print(x)
}
Expression Pattern
직접 구현한 형식에 대해 패턴 매칭을 적용하는 방법에 대해 공부
let a = 1
switch a {
case 0...10:
print("0 ~ 10")
default:
break
}
Pattern Matching Operator
a ~= b
struct Size {
var width = 0.0
var height = 0.0
static func ~=(left: Range<Int>, right: Size) -> Bool {
return left.contains(Int(right.width))
// 첫 번째 파라미터로 전달 된 범위에 두 번째 파라미터로 전달 된 사이즈에 width의 값이 포함되어 있다면 True가 리턴
}
}
let s = Size(width: 5, height: 20)
switch s {
case 1..<9:
print("1 ~ 9")
case 10..<99:
print("10 ~ 99")
default:
break
}
Optionals
Optional Chaining
하나의 표현식 내에서 다수의 옵셔널 형식 멤버에 접근하는 방법에 대해 공부
기억
1. 옵셔널 체이닝의 결과는 항상 옵셔널이다
2. 옵셔널 체이닝의 표현식 중에서 하나라도 nil을 리턴한다면 이어지는 표현식을 평가하지 않고 nil을 리턴한다
var p = Person(name: "Ben", email: "[email protected]")
let a = p.contacts?.address
var optionalP: Person? = Person(name: "Ben", email: "[email protected]")
let b = optionalP?.contacts?.address
b
optionalP = nil
let c = optionalP?.contacts?.address
c
p.contacts?.address?.count
p.getContacts()?.address
// let f: (() -> Contacts?)? = p.getContacts
// 함수나 메서드에 옵셔널 값이 접근할 떄는 괄호 뒤에 ?를 붙인다.
// f?()?.address
let d = p.getContacts()?.printAddress()
if p.getContacts()?.printAddress() != nil {
}
if let _ = p.getContacts()?.printAddress() {
}
let e = p.contacts?.email?["home"]
p.contacts?.email?["home"]?.count
// 딕셔너리가 옵셔널로 선언되어 있고 키를 통해 값을 얻을 때는 [] 앞에 ?를 붙인다.
// [] 뒤에 ?를 붙이는 경우는 서브 스크립트에서 속성에 접근하거나 메소드를 호출 할 때
p.contacts?.address = "Daegu"
p.contacts?.address
optionalP?.contacts?.address = "Deagu"
optionalP?.contacts?.address
Optional Pattern
옵셔널 패턴을 활용해서 옵셔널 매칭 코드를 더욱 효율적으로 작성하는 방법에 대해 공부
let a: Int? = 0
let b: Optional<Int> = 0
if let x = a {
print(x)
}
if case .some(let x) = a {
print(x)
}
if case let x? = a {
print(x)
}
let list: [Int?] = [1, nil, nil, 3, nil, 5]
for item in list {
guard let x = item else { continue }
print(x)
}
for case let x? in list {
print(x)
}
// ---------------
//if a == nil {
//
//}
//
//if a == .none {
//
//}
// nil == .none
// 위 아래 둘 다 같은 코드
// ===============
//if a == 1 {
//
//}
//
//if a == .some(1) {
//
//}
// 1 == .some(1)
// 위 아래 같은 코드
Functions
Variadic Parameters
하나의 파라미터를 통해 두 개 이상의 값을 전달하는 가변 파라미터에 대해 공부
(name: Type...)
- 가변 파라미터(...) ...을 붙이면 가변 파라미터로 선언된다
- 하나의 파라미터로 두 개 이상의 아그먼트를 전달 할 수 있다.
- 아그먼트는 배열 형태로 전달 된다.
- 가변 파라미터는 함수당 하나만 사용 가능하다
- 기본값 선언이 불가능하다.
print("Hello")
print("Hello", "Swift")
func printSum(of nums: Int...) {
var sum = 0
for num in nums {
sum += num
}
print(sum)
}
printSum(of: 1, 2, 3) // 6
printSum(of: 1, 2, 3, 4, 5) // 15
Function Types
(ParameterTypes) -> ReturnType
func sayHello() {
print("Hello, Swift")
}
let f1 = sayHello
f1()
func printHello(with name: String) {
print("hello, \(name)")
}
let f2: (String) -> () = printHello(with:)
let f3 = printHello(with: )
// 함수에 저장할 때는 아그먼트 레이블을 사용하지 않는다
f3("World")
func add(a: Int, b: Int) -> Int{
return a + b
}
var f4: (Int, Int) -> Int = add(a: b: )
f4(1,2)
func add(_ a: Int, with b: Int) -> Int {
return a + b
}
f4 = add(_:with:)
// 입출력
func swapNumbers(_ a: inout Int, _ b: inout Int) {
}
let f5 = swapNumbers(_:_:)
f5
// 가변 파라미터
func sum(of numbers: Int...) {
}
let f6 = sum(of: )
f6
// 현실적인 코드
func add(_ a: Int, _ b: Int) -> Int {
return a + b
}
func subtract(_ a: Int, _ b: Int) -> Int {
return a - b
}
func multiply(_ a: Int, _ b: Int) -> Int {
return a * b
}
func divide(_ a: Int, _ b: Int) -> Int {
return a / b
}
typealias ArithmeticFunction = (Int, Int) -> Int
func selectFunction(from op: String) -> ArithmeticFunction? {
switch op {
case "+":
return add(_:_:)
case "-":
return subtract(_:_:)
case "*":
return multiply(_:_:)
case "/":
return divide(_:_:)
default:
return nil // nil을 리턴하려면 반환타입을 옵셔널로 추가해야 됨
}
}
let af = selectFunction(from: "+")
af?(1, 2) // 옵셔널 체이닝 활용
selectFunction(from: "*")?(12, 34)
Nested Functions
함수 내부에 새로운 함수를 구현하는 방법에 대해 공부
//다른 함수에 포함되어 있는 함수
func outer() -> () -> () {
func inner() { // Nested Functions
print("inner")
}
print("outer")
return inner
}
let f = outer()
f()
//func inner() {
// print("inner")
//}
outer()
Nonreturning Function
호출하면 코드를 종료하거나 예외를 던지는 특별한 함수를 공부
Nonreturning Function을 호출하면 결과는 함수 Body에서 프로그래밍을 종료하거나 에러를 전달한다
func returnSomething() -> Int {
return 0
}
let result = returnSomething()
print(result)
func returnNothing() {
return
}
returnNothing()
print("done")
func doSomethingAndTerminate() -> Never { // Never = 아무것도 리턴하지 않는다
fatalError("msg") // 프로그램 실행을 종료하는 함수
}
// doSomethingAndTerminate()
print("agter terminate")
// 에러를 던지도록 구현
enum MyError: Error {
case error
}
func doSomethingAndAlwaysThrow() throws -> Never {
throw MyError.error // 에러를 던짐
}
//do {
// try doSomethingAndTerminate()
// print("after try")
//} catch {
// print(error)
//}
//
//
//func terminate() -> Never {
// fatalError("positive only")
//}
//
//func doSomething(with value: Int) -> Int {
// guard value >= 0 else {
// terminate()
// }
// return 0
//}
// doSomething(with: -1)
Closures
Escaping Closure
Escaping과 Non escaptin 방식으로 클로저를 실행했는데 어떤 차이가 있는지 비교
클로저 파라미터는 기본적으로 non-escaping 클로저이다
func performNonEscapint(closure: () -> ()) {
print("start")
closure()
print("end")
}
// 함수 호출
performNonEscapint {
print("closure")
}
func performEscaping(closure: @escaping () -> ()) { // 파라미터가 escaping 클로저로 선언 됨
print("start")
var a = 12
DispatchQueue.main.asyncAfter(deadline: .now() + 3) { // 지연시간
closure()
print(a)
}
print("end")
}
performEscaping {
print("closure")
}
Autoclosure
아규먼트로 전달된 표현식을 클로저로 래핑하는 @autoclosure 특성에 대해 공부
// 파라미터로 전달 되는 표현을 클로저로 래핑해줌
// 랜덤 넘버를 리턴하는 단순한 함수
func random() -> Int {
return Int.random(in: 0...100)
}
// 정수를 파라미터로 받아서 print 함수로 출력하는 함수
func takeResult(param: Int) {
print(#function)
print(param)
}
takeResult(param: random())
print("-------------------------------")
func takeClosure(param: () -> Int) {
print(#function)
print(param())
}
takeClosure(param: { Int.random(in: 0...100) })
print("-------------------------------")
func takeAutoclosure(param: @autoclosure @escaping () -> Int) { // @autoclosure // 비동기로 호출하려면 @escaping를 추가해야됨
// 오토클로저를 호출하면 파라미터를 호출할 수 없음 -> 파라미터를 항상 비워놔야됨
// 반면 리턴 타입은 원하는 타입으로 선언 할 수 있음
print(#function)
DispatchQueue.main.asyncAfter(deadline: .now() + 1 ) { // 비동기
print(param())
}
}
takeAutoclosure(param: Int.random(in: 0...100)) // @autoclosure로 선언되면 클로저로 사용할 수 없음 -> 자동으로 클로저로 래핑되기 때문에
// 오토클로저를 많이 활용하는 것은 어썰트
let rnd = random()
assert(rnd > 30)
String and Character
String Options
메소드를 활용해서 문자열을 편집하는 방법에 대해 공부
Numeric Option
"A" < "B"
"a" < "B"
// 아스키코드 때매 그럼
let file9 = "file9.txt"
let file10 = "file10.txt"
file9 < file10
file9.compare(file10) == .orderedAscending
file9.compare(file10, options: [.numeric]) == .orderedAscending
// [.numeric] 옵션을 사용하면 문자열에 추가된 숫자를 숫자열로 판단한다
Diacritic Insensitive
let a = "Cafe"
let b = "Cafè"
a == b
a.compare(b) == .orderedSame
a.compare(b, options: [.diacriticInsensitive]) == .orderedSame
// [.diacriticInsensitive] = 발음 기호를 무시함
Width Insensitive Option
let a = "\u{30A1}"
let b = "\u{ff67}"
a == b
a.compare(b) == .orderedSame
// 정각 문자와 반각? 문자를 비교하고 싶지 않으면 Width Insensitive Option 추가
a.compare(b, options: [.widthInsensitive]) == .orderedSame
Forced Ordering Option
// 강제로 정렬
let upper = "STRING".lowercased()
let lower = "string"
upper == lower
upper.compare(lower, options: [.caseInsensitive]) == .orderedSame
upper.compare(lower, options: [.caseInsensitive, .forcedOrdering]) == .orderedSame
// [.forcedOrdering] 옵션은 전체 옵션을 적용했을 때 같은 문자열로 판단된다면 일부 옵션을 무시하고 최대한 문자열의 순서를 파악할 수 있는 값을 리턴해줌
Regular Expression
// 정규식 옵션 = 복잡한 패턴의 문자를 쉽게 검색할 수 있음
// 많이 사용함
let emailPattern = "([0-9a-zA-Z_-]+)@([0-9a-zA-Z_-]+)(\\.[0-9a-zA-Z_-]+){1,2}"
let emailAddress = "[email protected]"
if let _ = emailAddress.range(of: emailPattern) {
print("found")
} else {
print("not found")
}
// Regular Expression 옵션 추가
if let range = emailAddress.range(of: emailPattern, options: [.regularExpression]), (range.lowerBound, range.upperBound) == (emailAddress.startIndex, emailAddress.endIndex) {
print("found")
} else {
print("not found")
}
Character Set
문자 집합
문자열 검색이나 잘못된 문자를 삭제할 때 사용
let a = CharacterSet.uppercaseLetters
let b = a.inverted
var str = "loRem Ipsum"
var charSet = CharacterSet.uppercaseLetters
if let range = str.rangeOfCharacter(from: charSet) { // 대문자가 검색된다면 첫번째 결과의 범위를 리턴해준다
print(str.distance(from: str.startIndex, to: range.lowerBound))
}
if let range = str.rangeOfCharacter(from: charSet, options: [.backwards]) { // [.backwards] 옵션 추가
print(str.distance(from: str.startIndex, to: range.lowerBound))
}
str = " A p p l e "
charSet = .whitespaces
// .trimmingCharacters 파라미터로 전달 된 캐릭터셋의 포함되어 있는 문자를 문자열에서 삭제한다 그리고 새로운 문자열로 리턴한다
let trimmed = str.trimmingCharacters(in: charSet)
print(trimmed)
// 양끝 공백 삭제됨
var editTarget = CharacterSet.uppercaseLetters
editTarget.insert("#") // 문자 하나 추가
editTarget.insert(charactersIn: "~!@") // 문자 여러개 추가
editTarget.remove(charactersIn: "BCD")
// 커스텀 캐릭터셋
let customCharSet = CharacterSet(charactersIn: "@.") // 두 개의 문자열로 저장된 캐릭터셋 생성
let email = "[email protected]"
let components = email.components(separatedBy: customCharSet) // 문자열을 분리해주고 리턴함. // .components(separatedBy: )
Author And Source
이 문제에 관하여(23. Expert #1), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@jkang4531/23.-Expert-1저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)