영어에선 a, e, i, o, u 다섯 가지 알파벳을 모음으로 분류합니다.
문자열 my_string이 매개변수로 주어질 때
모음을 제거한 문자열을 return하도록 solution 함수를 완성해주세요.
func solution(_ my_string:String) -> String {
let gather: [String] = ["a", "e", "i", "o", "u"]
var array_my_string: [String] = my_string.map { String($0) }
print(array_my_string)
for i in 0..<array_my_string.count {
print(array_my_string[i])
if gather.contains(array_my_string[i]) {
array_my_string.remove(at: i)
}
}
return ""
}
- 결과: 에러 발생
Thread 1: Fatal error: Index out of range
Fatal error: Index out of range 오류는 배열에 인덱스 값이 초과하면 발생한다.i이 초과한다. for i in 0..<array_my_string.count {
print(array_my_string[i])
if gather.contains(array_my_string[i]) {
array_my_string.remove(at: i) // 배열 요소 제거 -> 인덱스가 줄어듦
}
}func solution(_ my_string:String) -> String {
let gather: [String] = ["a", "e", "i", "o", "u"]
let array_my_string: [String] = my_string.map { String($0) }
var result: [String] = []
for i in array_my_string {
if !gather.contains(i) {
result.append(i)
}
}
return result.joined()
}
print(solution("bus")) // bs
print(solution("nice to meet you")) // nc t mt y
result 배열에 추가하는 방식으로 변경했다.
과연 지피티는 또 얼마나 코드를 줄일까...
func solution(_ my_string:String) -> String {
let gather: Set<Character> = ["a", "e", "i", "o", "u"]
return my_string.filter { !gather.contains($0) }
}
단 두줄...😳
filter를 사용해서 조건에 맞는 문자로 반환했다.✅ 알고리즘에서 Set<Character>를 사용하는 이유
gather를 배열에서 Set으로 변경했다.탐색 속도가 더 빠름
Set.contains(_:)의 평균 시간 복잡도는 O(1) (해시 기반 탐색)Array.contains(_:)의 평균 시간 복잡도는 O(n) (앞에서부터 하나씩 탐색)Set을 쓰면 모음인지 확인하는 연산이 훨씬 빨라짐!불필요한 반복을 줄임
contains를 호출할 때마다 배열 전체를 탐색할 수도 있음.Set을 사용하면 한 번에 빠르게 확인 가능.🔥 결론
Set은 contains 연산이 배열보다 훨씬 빠름(평균 O(1) vs O(n)) → 성능 개선!filter 함수는 컬렉션(Array, Set 등)의 요소를 특정 조건에 맞게 필터링하는 기능을 한다. 클로저를 인자로 받아 true를 반환하는 요소만 남겨 새로운 컬렉션을 반환한다.
동작 과정
filter의 내부 클로저서 $0은 Character 타입이 됨filter는 조건을 만족하는 Character 배열을 반환([Character])String([Character]) 형태로 변환되면서 최종적으로 String이 반환됨.따라서 알고리즘 문제에서 Set<String> 이 아니고 Set<Character> 타입을 사용
만약 Set<String> 으로 사용했다면 $0를 String($0)로 변경해줘야된다.
🔥 결론
Set<Character>를 사용해야 깔끔하게 동작함.IBAction 에서 했던 버튼 클릭 이벤트 코드베이스에서 추가하기
import UIKit
import SnapKit
class ViewController: UIViewController {
let button = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
configureUI()
}
private func configureUI() {
view.backgroundColor = .white
button.setTitle("Click", for: .normal)
button.setTitleColor(.white, for: .normal)
button.backgroundColor = .red
button.layer.cornerRadius = 10
//버튼 액션 역확 코드
button.addTarget(self, action: #selector(buttonClicked), for: .touchDown)
view.addSubview(button)
button.snp.makeConstraints {
$0.width.equalTo(120)
$0.height.equalTo(60)
$0.center.equalToSuperview()
}
}
// #selector() 안에 넣으려면 @objc 해줘야된다.
@objc
private func buttonClicked() {
print("버튼이 클릭됨")
}
}
import UIKit
import SnapKit
class ViewController: UIViewController {
let imageView = UIImageView()
let label = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
configureUI()
}
private func configureUI() {
view.backgroundColor = .white
imageView.image = UIImage(named: "cat")
imageView.backgroundColor = .black
imageView.contentMode = .scaleAspectFit
label.text = "고양이"
label.textColor = .black
label.font = UIFont.boldSystemFont(ofSize: 30)
// 이게 뭐야
[imageView, label]
.forEach { view.addSubview($0) }
imageView.snp.makeConstraints {
$0.width.height.equalTo(300)
$0.center.equalToSuperview()
}
label.snp.makeConstraints {
$0.centerX.equalToSuperview()
$0.top.equalTo(imageView.snp.bottom).offset(10)
}
}
}
import UIKit
import SnapKit
class ViewController: UIViewController {
let imageView = UIImageView()
let label = UILabel()
let imageView2 = UIImageView()
let label2 = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
configureUI()
}
private func configureUI() {
view.backgroundColor = .white
imageView.image = UIImage(named: "cat")
imageView.backgroundColor = .black
imageView.contentMode = .scaleAspectFit
label.text = "고양이"
label.textColor = .black
label.font = UIFont.boldSystemFont(ofSize: 30)
imageView2.image = UIImage(named: "cat2")
imageView2.backgroundColor = .black
imageView2.contentMode = .scaleAspectFit
label2.text = "고양이2"
label2.textColor = .black
label2.font = UIFont.boldSystemFont(ofSize: 30)
// 이게 뭐야
[imageView, label, imageView2, label2]
.forEach { view.addSubview($0) }
imageView.snp.makeConstraints {
$0.width.height.equalTo(160)
$0.leading.equalToSuperview().offset(16)
$0.centerY.equalToSuperview()
}
label.snp.makeConstraints {
$0.centerX.equalTo(imageView.snp.centerX)
$0.top.equalTo(imageView.snp.bottom).offset(16)
}
imageView2.snp.makeConstraints {
$0.width.height.equalTo(160)
//offset inset 차이가 뭐야?
// offset은 ➡️⬆️ 방햘 양수 값
// inset은 ⬅️⬇️ 방향 양수 값
$0.trailing.equalToSuperview().inset(16)
$0.centerY.equalToSuperview()
}
label2.snp.makeConstraints {
$0.centerX.equalTo(imageView2.snp.centerX)
$0.top.equalTo(imageView2.snp.bottom).offset(16)
}
}
}
1️⃣ offset
2️⃣ inset
UIButton을 생성하고, addTarget을 사용하여 클릭 이벤트를 처리하는 방법을 배웠다.SnapKit 라이브러리를 사용하여 이미지와 텍스트를 레이아웃에 맞게 배치할 수 있었다. 특히, forEach를 사용해 여러 뷰를 한 번에 추가하는 방식이 인상 깊었다offset과 inset의 차이를 이해할 수 있었는데, offset은 뷰가 이동하는 방향을 나타내고, inset은 뷰의 외부 여백을 설정하는 방식이라는 점에서 차이를 알게 되었다.forEach문을 활용하여 [imageView, label]과 같은 배열을 사용하여 여러 뷰를 한 번에 view.addSubview로 추가하는 방법을 알게 되었다.UIKit을 다룰 때, 코드를 어떻게 간결하게 작성할 수 있는지 고민하면서 실습을 했는데, SnapKit이 정말 유용한 도구임을 다시 한 번 실감했다. 뷰 간의 관계를 제약조건으로 설정하는 과정이 직관적이고, 코드도 간결해져서 뷰 레이아웃 작업이 훨씬 쉬워다.addTarget을 사용해 버튼 클릭 이벤트를 처리하는 방식에서 UI와 로직을 분리하는 중요성을 느꼈다. 앞으로 버튼 클릭 이벤트나 다른 액션을 처리할 때도 이 방식을 적극적으로 사용할 것 같다.