Swift 알고리즘 및 언어공부
우선 처음에 최적으로 m 만큼씩 상자에 나눠 담을 코드를 적었다.
만약에 총값이 상자에 딱 맞게 나누어 떨어지면 이런 방식으로 하고, 안 나누어 떨어지면 나머지 수만큼 가장 작은 수들을 버린 후에 이런 방식을 적용해 주기로 생각했는데, 자꾸 에러가 떴음.. 그래서 chat gpt에 왜 오류가 나는지 물어봤는데
-
주어진 코드에서 발생하는 주요 이슈는 arr이 빈 배열로 초기화되어 있어서, 배열 요소에 접근할 때 인덱스 오류가 발생한다는 점입니다. 코드에서 arr을 빈 배열로 선언한 후에는 이 배열에 요소를 추가하거나 요소에 접근하기 전에 요소를 추가해야 합니다. 현재 코드에서는 빈 배열 arr에 요소에 직접 접근하고 값을 할당하려고 하기 때문에 인덱스 오류가 발생합니다. 해결하기 위해서는 arr을 적절히 초기화해야 합니다. 예를 들어, arr을 2차원 배열로 초기화하고 각 요소를 빈 배열로 설정할 수 있습니다. 또한 요소를 추가할 때에는 append() 메서드를 사용하여 요소를 추가해야 합니다.
import Foundation
let k = 4
let m = 3
let score: [Int] = [4, 1, 2, 2, 4, 4, 4, 4, 1, 2, 4, 2]
var scoreValue: [Int] = score.sorted()
let rowCount = score.count / m
var arr: [[Int]] = []
for _ in 0..<rowCount {
var rowArray: [Int] = []
for _ in 0..<m {
if let firstValue = scoreValue.first {
rowArray.append(firstValue)
scoreValue.removeFirst()
}
}
arr.append(rowArray)
}
print(arr)
오오.. 이렇게하니까 배열이 내가 원하는 대로 잘 나왔음.. 역시 2차원 배열은 너무 헷갈린다...ㅠ
rowArray 변수를 추가해서 배열 안에 배열 설정해 주고, 2차원 배열이니까 배열에 배열을 append 하는 방식을 사용
import Foundation
func solution(_ k:Int, _ m:Int, _ score:[Int]) -> Int {
var scoreValue: [Int] = score.sorted()
let rowCount = score.count / m
var arr: [[Int]] = []
for _ in 0..<rowCount {
if scoreValue.count % m != 0 {
for n in 0 ... scoreValue.count % m {
scoreValue.removeFirst()
}
}
var rowArray: [Int] = []
for _ in 0..<m {
if let firstValue = scoreValue.first {
rowArray.append(firstValue)
scoreValue.removeFirst()
}
}
arr.append(rowArray)
}
var result:Int = 0
for a in 0..<arr.count {
result += arr[a].min()! * m
}
return result
}
위에 if문으로 나머지 상자가 있는 경우도 추가해 주고, 마지막 return 값까지 구했다
그냥 코드 실행하면 통과하긴 하는데,, 제출 후 체점하면 실패함 ㅋㅋㅋ 역시 좋은 코드 같아 보이진 않음..
시간초과 나서 변수를 사용해서 반복되는 계산을 줄이고, 조건문을 최소화해 줌
import Foundation
func solution(_ k: Int, _ m: Int, _ score: [Int]) -> Int {
var scoreValue: [Int] = score.sorted()
let rowCount = score.count / m
var arr: [[Int]] = []
let excessCount = scoreValue.count % m
if excessCount != 0 {
scoreValue.removeFirst(excessCount)
}
var result: Int = 0
var startIndex = 0
for _ in 0..<rowCount {
var minValue = Int.max //Int의 최댓값( chatGPT한테 배움 )
for j in 0..<m {
let value = scoreValue[startIndex + j]
if value < minValue {
minValue = value
}
}
result += minValue * m
startIndex += m
}
return result
}
겨우 풀었다..
//다른사람의 풀이
import Foundation
func solution(_ k:Int, _ m:Int, _ score:[Int]) -> Int {
let s = score.sorted(by: >)
return stride(from: m-1, to: score.count, by: m)
.reduce(0) { $0 + s[$1] * m }
}
ㅎㅎㅎ 살짝 자괴감 든다!ㅋㅋㅋㅋ 역시 함수를 잘 써주면 간단하게 구할 수 있구나....
난 reduce 함수가 어렵다.....ㅜ
이 코드는 stride로 상자에 들어가는 사과 중 가장 낮은 점수를 갖고 있는 사과에 인덱스 값들을 먼저 구하고,
큰 수부터 내림차순 정렬한 s에 [$1]을 해줘서(현재 인덱스를 나타냄) 각 상자에 가장 낮은 점수를 가진 사과의 점수를 골라냄
그 값에 m을 곱하고,
$0으로 이 곱한 값들을 누적해서 연산하도록 한 거 같음
2차원 배열 해줄 필요도 없이.. 이렇게 간단하게 할 수 있군 ^^!
그리고 새롭게 알게 된 stride 함수 정리
stride( from: to: by: )
: 시작 값부터 끝 값까지(포함하지 않음)의 시퀀스를 지정된 양만큼 단계별로 반환 (참고사이트)
from: 시퀀스에 사용할 시작 값입니다. 시퀀스에 값이 포함되어 있으면 첫 번째 값
to / through: 시퀀스를 제한하는 종료 값 ( to를 쓰면 마지막 값 포함 x, through는 포함 o )
by: 각 반복마다 단계별로 수행할 양
기존에 사용하던 for문과 비교해서 예시를 들어보자면,
//기존에 사용하던 for문
var sum:[Int] = []
for i in 0...5 {
sum.append(i)
}
print(sum) // 0 1 2 3 4 5 (개행 생략)
//stride 함수를 사용한 for문
for b in stride(from: 0, through: 5, by: 1) {
sum.append(b)
}
print(sum) // 0 1 2 3 4 5 (개행 생략)
강의 정리 (스파르타 코딩 클럽)
내부저장소
: 앱 종료와 함께 사라지지 않고, 앱이 기억하고 있어야할 비휘발성 데이터를 저장
( * 디바이스에 저장되기 때문에 많은 양에 데이터를 저장하기엔 적합하지 않음 )
1. UserDefaults
: 간단한 Key-Value ( 설정값, 사용자 기본설정과 같은 작은 정보 저장 )
// 데이터 추가 및 업데이트 ( 저장할 value값과 매칭 될 key값(String타입)을 넣어준다 )
UserDefaults.standard.set(value, forKey: key)
// 데이터 조회
// value메소드로 가져오면 Any?(옵셔널 Any)타입이여서 as를 사용한 타입 변환 후 사용
// 해당 타입과 동일한 메소드 네이밍을 사용하면 타입 변환없이 사용할 수 있다
UserDefaults.standard.value(forKey: key)
// 데이터 삭제
UserDefaults.standard.removeObject(forKey: key)
2. KeyChain
: 보안 정보를 안전하게 저장하고, 관리 ( 비밀번호, 토큰, 인증서와 같은 민감한 데이터, 보안 관련 정보 저장 )
ㄴ 암호화 되어있어서 외부에서 쉽게 접근할 수 없으며, 데이터의 안정성 보장
//Keychain에 저장할 데이터를 추가하는 쿼리 생성
let addQuery: [String:Any] = [
kSecClass as String: kSecClassGenericPassword, //Keychain내에서 다루고자 하는 데이터의 유형 지정
kSecAttrService as String: "service" //데이터를 저장하는 서비스나 앱을 식별하는데 사용
kSecAttrAccount as String: "key" // Keychain에서 데이터를 찾거나 식별하는데 사용
kSecValueData as String: "data" //실제로 Keychain에 저장될 데이터
]
//데이터 Keychain에 추가
let addStatus = SecItemAdd(addQuery as CFDictionary, nil)
kSecClass : 값이 항목의 클래스인 사전 키
kSecAttrService : 항목의 서비스를 나타내는 문자열 값을 갖는 키
ㄴ 해당 값은 CFString과 관련된 서비스를 나타내는 문자열 유형. 클래스의 항목에는 kSecClassGenericPassword 속성이 있다.
kSecAttrAccount : 항목의 계정 이름을 나타내는 문자열 값을 갖는 키
ㄴ CFString 유형, 계정 이름 포함. 클래스의 항목이고, kSecClassGenericPassword, kSecClassInternetPassword 속성을 가짐
kSecValueData : 값이 항목의 데이터인 키
ㄴ CFData 유형. 키 및 비밀번호 항목의 경우 데이터는 비밀(암호화)이므로 사용자가 액세스 하려면 비밀번호를 입력해야 할 수도 있다.
CFDictionary : Key-Value 쌍의 연결을 관리
ㄴ Core Foundation 프레임워크에서 제공하는 딕셔너리 데이터 구조
ㄴ Swift의 Dictionary를 as CFDictionary로 처리하여 바로 사용 가능
//Keychain 데이터 조회
let getQuery: [String: Any] = [
kSecClass as String: kSecClassGenericPassword, //Keychain 내에서 다루고자 하는 데이터의 유형을 지정
kSecAttrService as String: "service" //데이터를 저장하는 서비스나 앱을 식별하는데 사용
kSecAttrAccount as String: "key" //Keychain에서 데이터를 찾거나 식별하는데 사용
kSecReturnData as String: kCFBooleanTrue, //Keychain에서 데이터를 가져올 때, 반환할 데이터 형태 지정
kSecMatchLimit as String: kSecMatchLimitOne //일치하는 항목 수를 제한
]
//데이터를 Keychain에서 조회
var item: CFTypeRef?
let getStatus = SecItemCopyMatching(getQuery as CFDictionary, &item)
kSecReturnData : 항목 데이터를 반환할지 여부를 나타내는 부울 값
ㄴ CFBoolean유형. kCFBooleanTrue 값은 함수가 항목의 데이터를 CFData객체로 반환해야 함을 나타냅니다.
kSecMatchLimit : 일치 제한 값을 나타내는 키 (이 값을 제공하면 반환하거나 다른 작업을 수행할 최대 결과 수를 지정 )
ㄴ CFNumber유형. 단일 항목의 경우, kSecMatchLimitOne 지정. 일치하는 모든 항목을 지정하려면 kSecMatchLimitAll 지정.
(기본 동작은 함수에 따라 달라짐)
CFTypeRef? : 객체에 대한 유형이 지정되지 않은 "일반" 참조
ㄴ Core Foundation에 정의된 기본 유형, 여러 다형성 함수에서 유형 및 반환 값으로 사용됩니다. 이는 다른 실제 Core Foundation 개체에 대한 자리 표시자 역할을 하는 일반 개체 참조
//Keychain에서 데이터 업데이트
let updateQuery:[String:Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: "service",
kSecAttrAccount as String: "key",
]
// 업데이트할 데이터로 속성 설정
let attributes:[String:Any] = [
kSecValueData as String: "newData"
]
// 데이터를 Keychain에서 업데이트
let updateStatus = SecItemUpdate(updateQuery as CFDictionary, attributes as CFDictionary)
SecItemUpdate(_:_:) : 검색어와 일치하는 항목을 수정
//Keychain에서 데이터 삭제
let deleteQuery:[String:Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: "service",
kSecAttrAccount as String: "key",
]
//데이터를 Keychain에서 삭제
let deleteStatus = SecItemDelete(deleteQuery as CFDictionary)
SecItemDelete(_:) : 검색어와 일치하는 항목을 삭제
3. 파일 시스템
: 앱 내부 또는 외부 파일 시스템에 데이터를 저장 (주로 텍스트 파일, 이미지, 동영상 등의 데이터를 저장할 때 사용)
let fileName = "example.txt"
let content = "이것은 파일에 쓰여진 내용"
//FileManager로 저장할 위치 지정
if let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
let fileURL = dir.appendingPathComponent(fileName)
do {
try content.write(to:fileURL, atomically: false, encoding: .utf8)
print("파일이 성공적으로 생성되었습니다.")
} catch {
print("파일 생성 중 에러가 발생했습니다: \(error.localizedDescription)")
}
}
(중요!)
4. Core Data
: 데이터를 관리하고, 영구적으로 저장하는 데 사용되는 프레임 워크( 별도 외부 라이브러리를 사용하지 않는 경우 Core Data를 주로 사용)
ㄴ 데이터 모델을 정의하고, 이를 기반으로 데이터를 읽고 쓸 수 있음
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistenContainer(name:/*생성한 Data Model명*/)
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
func saveContext () {
let context = persistenContainer.viewContext
if context.hasChanges {
do {
try context.save()
catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
내일 Core Data 이어서 더 자세하게 공부해야겠다..
물론 이해가 잘 안 되지만.. 그래도 한 번 보고 적어서 익숙해지면 나중에 이해할 때 도움이 되지 않을까... ㅠ