오늘은 여러가지 알고리즘 문제를 풀면서 몰랐던 메서드나 사고방식을 많이 경험할 수 있었다. 각 알고리즘에서 어떤 메서드나 방법을 활용했는지 간략하게 기록해두었고, 오늘 배운 것 중에 정리해두면 좋겠다고 생각한 것은 ChatGPT를 이용해 정리해놓았다 :)
_
) 활용-
를 바로 붙여도 되는구나 / 방법에 따라 속도차 컸음// ChatGPT 4o prompts:
메서드 체이닝에 대해서 설명해볼래?
메서드 체이닝(Method Chaining)은 여러 메서드를 연속해서 호출하여 하나의 구문으로 연결하는 프로그래밍 기법이다. Swift뿐만 아니라 JavaScript, Python, Kotlin 같은 언어에서도 자주 사용되며, 코드의 가독성과 간결성을 높여준다.
메서드 체이닝은 각 메서드가 특정 값을 반환하고, 이 반환된 값에 대해 다시 메서드를 호출할 수 있는 경우에 가능해진다. 예를 들어, Swift의 배열이나 문자열 메서드는 변환된 새로운 배열이나 문자열을 반환하므로, 반환된 값에 대해 추가 메서드를 호출할 수 있다.
let numbers = [1, 2, 3, 4, 5, 6]
let result = numbers
.filter { $0 % 2 == 0 } // 짝수만 필터링
.map { $0 * $0 } // 제곱으로 변환
.reduce(0, +) // 모든 요소의 합을 계산
print(result) // 4 + 16 + 36 = 56
let phrase = " Hello, World! "
let result = phrase
.trimmingCharacters(in: .whitespaces) // 문자열 앞뒤 공백 제거
.replacingOccurrences(of: "World", with: "Swift") // "World"를 "Swift"로 대체
.uppercased() // 모든 문자를 대문자로 변환
print(result) // "HELLO, SWIFT!"
메서드 체이닝은 가독성을 높이지만, 너무 많은 체이닝이 있거나, 각 체인 구문이 복잡할 경우 오히려 가독성이 떨어질 수 있다. 이런 경우 중간에 변수를 사용해 각 단계를 명확히 구분하는 것이 좋다.
// ChatGPT 4o prompts:
- 비트 연산과 swift의 비트 연산 방법을 설명해줘
- 비트 시프트는 2배 단위로 숫자가 변화되는거야?
- 2배로 변화되는 건 예외적인 경우 없이 안심하고 써도 되는거야?
비트 연산(bitwise operations)은 정수형 데이터의 이진 표현을 기준으로 비트 단위로 수행되는 연산이다. 이러한 연산은 일반적으로 컴퓨터가 데이터를 매우 빠르게 조작할 수 있도록 하며, 효율적인 알고리즘을 작성하는 데 중요한 역할을 한다. Swift에서는 아래의 연산자들을 사용하여 비트 단위로 데이터를 조작할 수 있으며, 높은 성능의 저수준 연산을 구현할 때 유용하다. 예를 들어, 특정 비트를 설정하거나 해제할 때 또는 비트 마스킹 기법을 사용할 때 자주 활용된다.
AND (&
): 두 비트가 모두 1일 때만 결과가 1이 된다.
let result = 5 & 3
(0101 & 0011
→ 0001
, 결과는 1
)OR (|
): 두 비트 중 하나라도 1이면 결과가 1이 된다.
let result = 5 | 3
(0101 | 0011
→ 0111
, 결과는 7
)XOR (^
): 두 비트가 다를 때만 결과가 1이 됩니다.
let result = 5 ^ 3
(0101 ^ 0011
→ 0110
, 결과는 6
)NOT (~
): 각 비트를 반전합니다(1은 0으로, 0은 1로).
let result = ~5
(0101
→ 1010
, 결과는 -6
, 2의 보수 표현에 의해 음수로 나타남)비트 시프트 (<<
, >>
):
<<
): 지정된 수만큼 비트를 왼쪽으로 이동시킵니다. 오른쪽은 0으로 채워집니다.let result = 5 << 1
(0101 << 1
→ 1010
, 결과는 10
)>>
): 지정된 수만큼 비트를 오른쪽으로 이동시킵니다. 왼쪽은 부호 비트로 채워집니다(부호 있는 정수의 경우).let result = 5 >> 1
(0101 >> 1
→ 0010
, 결과는 2
)※ 비트 시프트는 숫자를 2배 단위로 변환하는 특성이 있다. 이는 정수형 연산에서 곱셈이나 나눗셈보다 더 빠르게 수행된다!
비트 시프트 연산이 숫자를 2배 단위로 변화시키는 것은 일반적으로 안전하게 사용할 수 있지만 몇 가지 예외 상황을 주의해야 한다.
Int
)의 경우:<<
)는 양수의 경우 문제가 없지만, 음수일 때는 예상하지 못한 결과를 초래할 수 있다. 음수를 왼쪽으로 시프트하면 부호 비트가 변할 수 있어 원치 않는 값이 될 수 있다.>>
)는 음수의 경우 부호 비트를 유지하면서 시프트되기 때문에, 음수일 때 결과는 항상 2로 나누는 결과와 동일하지 않을 수 있다. 음수는 부호를 유지하기 위해 최상위 비트에 1
이 채워진다.Int
타입에서 가장 큰 값인 Int.max
를 왼쪽으로 시프트하면 오버플로우가 발생할 수 있다.<< 32
는 원래 값을 유지하거나 비트가 사라질 수 있다. Swift에서는 시프트할 비트 수가 음수이거나 타입의 비트 수 이상인 경우 런타임 에러가 발생하지는 않지만, 안전한 범위 내에서만 시프트를 사용하는 것이 좋다.Swift의 filter
메서드는 컬렉션(배열, 집합 등)의 요소들을 특정 조건에 따라 걸러내어 새로운 배열이나 컬렉션을 반환하는 메서드이다. 주어진 클로저의 조건을 만족하는 요소만 포함된 새로운 배열을 만든다.
let filteredArray = array.filter { 조건식 }
array
: 필터를 적용할 배열 또는 컬렉션.{ 조건식 }
: 각 요소에 대해 실행될 클로저. 클로저는 요소를 받아 true
또는 false
를 반환해야 하며, true
인 경우 해당 요소가 결과 배열에 포함된다.조건을 만족하는 요소들만 담긴 새로운 배열을 반환한다. 조건을 만족하는 요소가 없다면 빈 배열을 반환한다.
let numbers = [1, 2, 3, 4, 5, 6]
let evenNumbers = numbers.filter { $0 % 2 == 0 } // 배열에서 짝수인 요소만 필터링합니다.
print(evenNumbers) // 출력: [2, 4, 6]
let words = ["apple", "banana", "kiwi", "peach"]
let longWords = words.filter { $0.count > 4 } // 문자열의 길이가 4보다 큰 요소만 필터링합니다.
print(longWords) // 출력: ["apple", "banana"]
filter
는 Swift에서 조건에 맞는 요소만 빠르게 추출할 때 매우 유용한 메서드이다.
// ChatGPT 4o prompts:
- enumerated() 를 설명해줘
- 튜플 안에서 T는 뭘 의미하는거야?
enumerated()
는 Swift의 시퀀스 메서드 중 하나로, 컬렉션의 각 요소에 대해 해당 요소와 그에 대한 인덱스를 튜플로 반환하는 메서드이다. 이 메서드를 사용하면 컬렉션의 요소와 인덱스를 동시에 순회할 수 있다. enumerated()
는 배열이나 다른 컬렉션을 순회할 때 인덱스와 요소를 모두 필요로 하는 경우 유용하게 사용할 수 있는 메서드이다.
enumerated()
메서드는 (offset: Int, element: T)
형태의 튜플 시퀀스를 반환한다.
offset
: 해당 요소의 인덱스.element
: 해당 인덱스에 위치한 요소.Swift에서 튜플 안의 T
는 일반적으로 타입을 의미하는 제네릭 타입 매개변수의 표기이다.
enumerated()
메서드의 반환 타입은 (offset: Int, element: T)
형태의 튜플이다. 여기서 T
는 원래 컬렉션의 요소 타입을 나타낸다. 예를 들어, 만약 배열이 [String]
타입이라면 T
는 String
이고, [Int]
타입이라면 T
는 Int
이다.
let fruits = ["apple", "banana", "cherry"]
// enumerated()를 사용하여 요소와 인덱스를 함께 순회
for (index, fruit) in fruits.enumerated() {
print("Index \(index): \(fruit)")
}
// 출력:
// Index 0: apple
// Index 1: banana
// Index 2: cherry
// map과 조합하여 인덱스를 활용한 새 배열 생성
let newArray = fruits.enumerated().map { (index, fruit) in
"\(index + 1). \(fruit)" // 인덱스에 1을 더해 순번으로 표시
}
print(newArray) // ["1. apple", "2. banana", "3. cherry"]
allSatisfy
는 컬렉션의 모든 요소가 특정 조건을 만족하는지 확인하는 함수이다. 조건을 정의하는 클로저를 받아, 모든 요소가 해당 조건을 만족하면 true
, 하나라도 만족하지 않으면 false
를 반환한다.
let numbers = [1, 2, 3, 4, 5]
print(numbers.allSatisfy { $0 > 0 }) // true, 모든 요소가 0보다 큼
let mixedNumbers = [1, -2, 3, 4, 5]
print(mixedNumbers.allSatisfy { $0 > 0 }) // false, 음수가 있음
let word = "hello"
print(word.allSatisfy { $0.isLowercase }) // true, 모든 문자가 소문자임
let validCharacters = Set("0123456789+-")
let input = "+12345"
print(input.allSatisfy { validCharacters.contains($0) }) // true, 특정 문자 집합에만 속함
Swift의 zip()
함수는 두 개의 시퀀스를 결합하여 각 시퀀스의 대응하는 요소를 튜플로 묶어주는 함수이다. 두 시퀀스 중 더 짧은 시퀀스의 길이에 맞춰 결합되며, 두 시퀀스의 요소들을 병렬로 처리할 때 유용하다.
let zippedSequence = zip(sequence1, sequence2)
sequence1
과 sequence2
는 결합할 시퀀스들이다.zippedSequence
는 두 시퀀스의 요소가 (element1, element2)
형태의 튜플로 묶인 시퀀스를 반환한다.let array1 = [1, 2, 3, 4]
let array2 = ["a", "b", "c"]
let zipped = zip(array1, array2)
for (num, letter) in zipped {
print("\(num): \(letter)")
}
// 출력:
// 1: a
// 2: b
// 3: c
zip()
은 두 시퀀스 중 더 짧은 시퀀스의 길이에 맞춰 동작한다. 예시에서 array1
의 길이는 4이고, array2
의 길이는 3이므로 결과는 3개의 튜플이 반환된다.zip()
은 원래의 시퀀스를 변경하지 않고 새로운 시퀀스를 반환한다.Swift의 pow
함수는 거듭제곱을 계산하는 함수로, 두 개의 Double
값을 매개변수로 받아 첫 번째 값을 두 번째 값으로 거듭제곱한 결과를 반환한다. Foundation
프레임워크에 포함되어 있으며, 소수나 음수 지수도 처리할 수 있다.
import Foundation
// 기본 문법: pow(base, exponent)
// 예시: 양수, 소수, 음수 지수 처리
let positiveExponent = pow(2.0, 3.0) // 8.0
let fractionalExponent = pow(9.0, 0.5) // 3.0 (제곱근)
let negativeExponent = pow(2.0, -2.0) // 0.25
// 정수 거듭제곱 예시
let intResult = Int(pow(Double(5), Double(2))) // 25
오… 메소드 체이닝을 저렇게 하니까 상당히 깰꼼하군요 내일 써봐야징