[iOS 4주차] Algorithm: 시저 암호 - 유니코드 스칼라 메서드

DoyleHWorks·2024년 11월 14일
0

시저 암호

문제 링크 / Github 링크
func solution(_ s:String, _ n:Int) -> String {
    return ''
}

처음 짠 코드

func solution(_ str: String, _ num: Int) -> String {
    let shiftedChars = str.map { char -> Character in
        if let scalar = char.unicodeScalars.first {
            if (65...90).contains(scalar.value) || (97...122).contains(scalar.value) {
                let shiftedValue = scalar.value + UInt32(num)
                if (65...90).contains(shiftedValue) || (97...122).contains(shiftedValue) {
                    return Character(Unicode.Scalar(shiftedValue)!)
                } else {
                    return Character(Unicode.Scalar(shiftedValue - 26)!)
                }
            }
            return char
        }
        return char
    }
    return String(shiftedChars)
}

테스트

print(solution("a  Z", 25))
// 출력값 "z  s" / 기댓값 "z  Y"

-> 65...9097...122의 범위가 7밖에 차이가 나지 않는다는 것을 간과함

수정한 코드

func solution(_ str: String, _ num: Int) -> String {
    let shiftedChars = str.map { char -> Character in
        if char.isLetter {
            if let scalar = char.unicodeScalars.first {
                let shiftedValue = scalar.value + UInt32(num)
                if (65...90).contains(scalar.value) {
                    if shiftedValue > 90 { return Character(Unicode.Scalar(shiftedValue - 26)!) }
                } else if (97...122).contains(scalar.value) {
                    if shiftedValue > 122 { return Character(Unicode.Scalar(shiftedValue - 26)!) }
                }
                return Character(Unicode.Scalar(shiftedValue)!)
            }
        }
        return char
    }
    
    return String(shiftedChars)
}

개선할 수 있는 점

  1. 반복된 범위 검사 코드:
    대문자와 소문자의 범위 (65...90) 및 (97...122)에 대해 중복된 코드

  2. 음수 num 처리 부족:
    문제의 제한 조건에는 부합하지만, num이 음수일 때 제대로 동작하지 않음

  3. unicodeScalars 접근 방식:
    문제의 제한 조건에는 부합하지만, 각 문자가 단일 유니코드 스칼라만 가진다는 가정이 암묵적임
    (알파벳은 맞지만, 다중 스칼라 문자를 고려하면 잠재적 문제가 될 수 있음)

개선한 코드

func solution(_ str: String, _ num: Int) -> String {
    let shiftedChars = str.map { char -> Character in
        guard char.isLetter, let scalar = char.unicodeScalars.first else { 
            return char 
        }
        
        let isUppercase = (65...90).contains(scalar.value)
        let isLowercase = (97...122).contains(scalar.value)
        
        let base = isUppercase ? 65 : (isLowercase ? 97 : 0)
        let alphabetCount = 26
        
        if base > 0 {
            let shiftedValue = Int(scalar.value) - base + num
            let wrappedValue = (shiftedValue % alphabetCount + alphabetCount) % alphabetCount + base
            return Character(Unicode.Scalar(wrappedValue)!)
        }
        
        return char
    }
    
    return String(shiftedChars)
}

유니코드 스칼라 주요 메서드

1. unicodeScalars (프로퍼티)

문자열의 유니코드 스칼라 뷰(UnicodeScalarView)를 반환한다.

용도: 문자열의 각 문자의 유니코드 스칼라 값에 접근.

let char: Character = "A"
if let scalar = char.unicodeScalars.first {
    print(scalar.value)  // 65
}

2. Unicode.Scalar.init(_:)

주어진 UInt32 값으로 유니코드 스칼라를 생성한다.

용도: 특정 값으로 새로운 유니코드 스칼라 생성.

if let scalar = Unicode.Scalar(66) {  // 10진수 66
    print(scalar)  // B
}

3. Character.init(_:)

유니코드 스칼라 값을 사용해 새로운 Character를 생성한다.

용도: 유니코드 스칼라 값을 기반으로 문자 생성.

if let scalar = Unicode.Scalar(97) {
    let char = Character(scalar)
    print(char)  // a
}

4. map(_:)

컬렉션의 각 요소를 변환하여 새로운 컬렉션을 만든다.

용도: 유니코드 스칼라 값을 변환하여 새로운 스칼라 또는 문자를 생성.

let text = "ABC"
let shiftedText = String(text.unicodeScalars.map { scalar in
    Unicode.Scalar(scalar.value + 1).map(Character.init) ?? Character(scalar)
})
print(shiftedText)  // BCD

5. filter(_:)

컬렉션의 요소를 조건에 따라 필터링한다.

용도: 특정 범위의 유니코드 스칼라 값만 필터링.

let text = "Hello, World!"
let filtered = text.unicodeScalars.filter { $0.value >= 65 && $0.value <= 90 }  // 대문자 필터링
print(String(filtered.map(Character.init)))  // HW

6. compactMap(_:)

nil 값을 제거하며 변환 작업을 수행한다.

용도: 유니코드 스칼라 값이 유효한 경우만 사용.

let scalars: [UInt32] = [72, 101, 108, 108, 111]  // "Hello"의 유니코드 값
let text = String(scalars.compactMap(Unicode.Scalar.init).map(Character.init))
print(text)  // Hello

7. reduce(_:_:)

컬렉션의 모든 요소를 누적하여 단일 값으로 만든다.

용도: 모든 유니코드 스칼라 값을 누적 연산.

let text = "ABC"
let sum = text.unicodeScalars.reduce(0) { $0 + Int($1.value) }
print(sum)  // 198 (65 + 66 + 67)

8. String.init(_:)

유니코드 스칼라 배열을 문자열로 변환한다.

용도: 유니코드 스칼라 뷰를 문자열로 재구성.

let scalars: [Unicode.Scalar] = ["H", "i", "!"].compactMap { $0 }
let text = String(String.UnicodeScalarView(scalars))
print(text)  // Hi!

요약

메서드/프로퍼티설명결과
unicodeScalars문자열의 유니코드 스칼라 값 뷰 제공String.UnicodeScalarView
Unicode.Scalar.init(_:)주어진 값으로 유니코드 스칼라 생성Unicode.Scalar?
Character.init(_:)유니코드 스칼라 값을 사용해 문자 생성Character
map(_:)각 유니코드 스칼라에 변환 작업 적용변환된 값 배열
filter(_:)조건에 맞는 유니코드 스칼라 필터링조건에 맞는 값만 포함
compactMap(_:)변환 후 nil 값 제거변환된 값 배열
reduce(_:_:)누적 연산 수행누적 값
String.init(_: UnicodeScalarView)유니코드 스칼라 배열을 문자열로 변환String
profile
Reciprocity lies in knowing enough

0개의 댓글