###### tags: `project`
## 숫자 야구
## Hoon, Hemg
안녕하세요. 앤드류 커리어 9기 hoon, hemg 입니다.
첫 PR 보냅니다. 한 주간 잘 부탁드립니다.
감사합니다.
## 고민되었던 점
### `Set`과 `Array` 타입 활용
- 순서도에서 생성한 사용자 랜덤 번호와 컴퓨터 랜덤 번호를 비교하기 위해 어떠한 자료구조를 사용할지에 대해 고민하였습니다. 볼은 순서(위치)와 관계없이 서로 같은 숫자가 있는지를 확인함으로 `Set`을 사용하였고 스트라이크는 순서(위치)와 관련하여 같은 위치에 같은 값이 있는지를 판단하기 때문에 `Array`를 사용하였습니다.
### 함수의 재활용성
- 사용자의 랜덤 숫자 생성과 컴퓨터의 랜덤 숫자 생성 기능을 처음에는 개별 함수로 선언하였습니다. 하지만 결괏값을 받는 변수 즉, 사용자인지 컴퓨터인지의 차이만 존재할 뿐 같은 기능을 수행하므로 하나의 함수로 변경하였습니다.
### 중복 숫자 확인에 대한 활용
- 랜덤 숫자를 포함시킬때 inserted의 대한 부분에 대해서 처음에는 `Set`을 사용하여 `insert`와 `contains`를해서 숫자를 포함시켰는데 `Array` 로 변경함에 있어서 중복된 숫자를 포함 시키지 않기 위해서 어떤 방법을 선택해야 할지 고민하다가 inserted을 사용하여 진행했습니다.
## 조언을 얻고 싶은 부분
### `drawRandomNumbers` 함수의 다른 구현 방법에 대해서 알고 싶습니다.
- 함수의 기능을 더 세분화하여 나누는 것이 좋을지 와 현재는 `Set`을 사용하여 중복 값에 대해 판단하고 이를 기준으로 랜덤 숫자 `Array`에 값을 추가하고 있습니다. 다른 방법으로는 어떻게 구현이 가능할지 궁금합니다.
### 매개변수 네이밍과 일반화 관련하여 조언을 받고 싶습니다.
- `checkMatchingCount`와 같은 함수에서 두 개의 매개변수를 받아 내부에서 교집합 연산을 수행합니다. 이때 실제로 함수 내에서 사용하는 매개변수는 SetA, SetB와 같이 일반화를 시킬 수도 있다고 생각하였습니다. 비교를 하는 두 매개변수의 네이밍을 `lhs`, `rhs` 말고 어떤 식으로 일반화하여 사용할 수 있을지 궁금합니다? 또한 함수의 재활용성을 높여보고자 일반화하여 매개변수의 네이밍을 한다면 다른 개발자가 읽었을 때 함수의 의미를 정확하게 파악하기 어렵다는 생각도 들었습니다. 함수의 네이밍에서 일반화를 사용하는 것은 좋지 않은 방향일까요?
### 더 나은 방향의 코드에 대해서 알고 싶습니다.
- 반복문에서 상수 사용과 함수 안에서의 변수 사용
```swift
let computerRandomNumbers: [Int] = drawRandomNumbers()
var remainCount: Int = 9
while remainCount > 0 {
let userRandomNumbers = drawRandomNumbers()
let matchCount = checkMatchingCount(with: computerRandomNumbers, userRandomNumbers)
let strikeCount = checkStrikeCount(with: computerRandomNumbers, userRandomNumbers)
let ballCount = checkBallCount(matching: matchCount, strike: strikeCount)
```
지금 현재 위와 같은 코드를 사용했습니다. 처음에는 밑에 코드를 사용하여 진행했는데 위와 아래의 코드의 차이점에 대해 생각해 봤을 때 반복문 안에서 상수를 선언하며 매번 반복문이 다시 실행할 때 상수가 새로 초기화되는 과정이 발생한다고 생각합니다. 이런 부분에서 메모리에 새로 할당하고 해제하는 것과 변수로 선언하여 계속 값을 변경하는 것 중에 어떤 방식이 더 효율적이라고 볼 수 있을까요?
```swift
let computerRandomNumbers: [Int] = drawRandomNumbers()
var userRandomNumbers: [Int] = drawRandomNumbers()
var remainCount: Int = 9
var matchCount: Int = 0
var strikeCount: Int = 0
var ballCount: Int = 0
while remainCount > 0 {
userRandomNumbers = drawRandomNumbers()
matchCount = checkMatchingCount(with: computerRandomNumbers, userRandomNumbers)
strikeCount = checkStrikeCount(with: computerRandomNumbers, userRandomNumbers)
ballCount = checkBallCount(matching: matchCount, strike: strikeCount)
```
### 함수 나열에 있어 가독성
- 순서도에 맞게 함수를 나열 하는것이 좋을까요?? ex) `generateRandomNumber` 함수가 순서도 상으로는 먼저이지만 `drawRandomNumbers` 함수를 먼저 호출한 후에 `generateRandomNumber`함수를 호출할 경우 가독성 부분에서 좋을꺼 같은 생각을 해봤습니다.
## 코멘트 궁금한점
함수 기능 분리에 overhead가 발생, 어떤식으로 나눠야할지?
객체지향관점으로 나눈다? 함수나열과 연관
일반화에 대한 설명이 좀 더 필요해보여요
함수에 대한 문서화란 주석?
더하여 let 대해 이런 생각을 해봤는데요.. 불필요한 메모리(변수라면 종료시점이 명확하지 않기때문에 대기하고있을꺼같은데 let이라면 변경되지않으니깐 종료시점이 명확할꺼 같습니다.) 사용을 줄일 수 있습니다. (이건 어떻게 생각하시는지 궁금합니다!)
(지금 drawRandomNumbers 함수에 있는 randomNumber를 Int로 초기화만 해준다음에
while문 안에다가 randomNumber를 랜덤한 값을 할당해 주는것은 어떨까요?)
혹시 여기 부분 코멘트를 앤드류가 어떠한 생각을 하면서 진행하셨는지 알고 싶어요~
파운데이션을 import를 하고 안하고의 차이가 기능을 사용하는거 외에 어떤게 있을지 답변을 찾지못했습니다. 어떤 의미가 있을까요?
#### STEP1 PR
1. Foundation
대표적인 파운데이션 관련 기능은 다음과 같았습니다.
* 텍스트 처리 기능: 문자열 처리, 인코딩, 정규 표현식, 로케일 등의 기능을 제공합니다.
* 데이터 저장 및 관리 기능: 파일 시스템, 미디어 데이터, 네트워크 통신, plist 파일 등 데이터 저장과 관리에 필요한 기능을 제공합니다.
* 날짜와 시간 처리 기능: 시간대, 날짜 계산, 캘린더, 타이머 등의 기능을 제공합니다.
--------------------------------------------------------------------------------------------
## 고민되었던 점
### `split` 함수
- `split`을 이용하여 `readLine` 사용시에 인덱스로 나눌 수 있다고 생각했습니다. `readLine`의 타입이 `String` 값이라 `if let` 바인딩을 하여 `Int` 값으로 변경하여 사용 하려고 했습니다. 처음 생각이 `["1, 2, 3"]` 이라고 생각되어 인트로 감싸서 `Int([1, 2, 3])` 해서 출력이 될꺼라고 생각했었는데 이점에서 잘못 알고 있어 배열 `Int`로 감쌀수없다는 것을 알게 되었고 그래서 `for`문을 통해 인덱스값 하나하나를 `Int`로 변경후 사용하게 되었습니다.
### else 이후 코드 컨벤션
```swift
guard let userGuessingNumbers = readLine() else {
print("입력이 잘못되었습니다.")
continue
}
```
- 결과적으로는 이 코드를 진행했습니다. else { reture } `else` 이후 코드 컨벤션에 있어 어떻게 해야 가독성이 조금더 좋을지 고민을 하게 되었고 한줄로 길게 뻣은것보다는? 입력 한줄 `continue` 한줄 이렇게 진행 하면서 {} 처리까지 하게 되었습니다.
## 조언을 얻고 싶은 부분
### 재귀 함수 vs 반복문
- 같은 동작을 반복하여 실행하고 싶은 경우에 재귀 함수와 반복문을 사용할 수 있었습니다. 두 방법론에 대해 찾아보고 고민한 결과 재귀 함수는 스택 오버플로우를 발생시켜 런타임 오류가 발생할 수 있다는 문제가 있었습니다. 반면 반복문은 동작이 오래 걸릴 수는 있으나 런타임에 에러가 나는 경우는 드물다고 확인하였고, 이러한 차이점을 바탕으로 반복문을 사용하여 문제를 해결하였습니다. 그렇다면 어떤 경우에 재귀 함수를 사용하는지가 궁금합니다. 재귀 함수의 대표적인 장점으로 본 내용은 코드의 크기(길이)가 작다는 장점뿐인 것 같았습니다.
### `if` 와 `guard`
- `guard` 문의 경우 early exit의 성격을 가지고 있다고 알고 있습니다. 따라서 이번 문제를 해결하면서도 함수에서 조건을 판별하여 종료시킬 때 이러한 성격을 가진 `guard` 문을 활용하였습니다. 일반적으로 `if` 문은 `if-else` 문과같이 다른 조건을 연속적으로 비교하여 사용하는 경우가 아니면 swift에서는 사용성이 조금 낮은 것 같다는 생각이 들었습니다. 앤드류의 경우에는 어떤 식으로 판별하시는지 의견을 들어보고 싶습니다.
### 페어프로그래밍을 하실때 어떤식으로 처음 문제에 대한 코드 계획을 세우시나요?
```swift
1-1 사용자 메뉴를 출력하고 메뉴를 입력받는 함수 (게임시작,게임종료)
사용자 메뉴 입력 함수 (inputUserMenu)
리드라인
1,2 인 경우
게임 시작
1,2 아닐경우
재입력
1-2 게임 숫자를 입력받는 함수
inputUserGuessingNumbers 함수 생성
리드라인
검증 함수 호출(숫자 검증)
1-3 입력 받은 값이 숫자 3개인지 검증 함수 생성
검증함수(verifyUserGuessingNumbers)
인풋값을 파라미터로 받겠다.
인풋값을 스플릿으로 나눈다
스플릿으로 나눈값이 갯수가 3개인지 확인한다
아닐경우 1-2 재실행을 한다.
스플릿으로 나눈값이 숫자인지 확인한다.
아닐경우 1-2 재실행을 한다.
```
- 이번에 처음 페어프로그래밍을 진행하면서 첫날에는 순서도를 함께 작성하며 이를 기반으로 step 1 미션을 해결하였습니다. step 2에서는 순서도보다는 좀 더 자세하게 의사(pseudo) 코드를 작성하여 아이디어를 공유하고 이를 바탕으로 네비게이터와 드라이버의 역할을 진행하였습니다. 여러 번 페어프로그래밍을 진행하시면서 어떤 방법들을 사용하셨고 추천하는 방법이 있는지 궁금해요.
## step 2 피드백
```swift
68 ~ 70 프린트 문 수정
""" 이런식일려나요..
숫자 3개를 띄어쓰기로 구분하여 입력해주세요.
중복 숫자는 허용하지 않습니다.
print("입력 :", terminator: " ")
"""
```
```swift
if 문 수정하기 이런식으로 수정이 맞을까요?
switch Int(userMenu) {
case 1:
playBaseballGame()
case 2:
print("게임종료")
isRun = false
default:
print("입력이 잘못되었습니다")
}
```
- for문 안에 가드 정규식?? 여기서? 어떤 의미인지 캐치를 못하겠어요.. 시도라니깐 굳이 안해도 될려나요??
-  별 수정은 아닌데 이렇게 겟씽넘버를 사이에 줘서 보기 편하게 하는방법? 도 있다고 하네요.
- start함수 말고 기존껄 호출해서 사용하는건 어떻냐? 흠 그럴수 있긴하지만 먼가 구분이 있었으면 좋을꺼같아서 진행 하는걸로하는게 좋을꺼같은데요..
크게 수정할 부분은 정말 없는거같아요..
정말 30분?도 안걸릴거라고 생각됩니다..
# ⚾️ 숫자야구
## 📖 목차
1. [소개](#-소개)
2. [팀원](#-팀원)
3. [타임라인](#-타임라인)
4. [시각화된 프로젝트 구조](#-시각화된-프로젝트-구조)
5. [실행 화면](#-실행-화면)
6. [트러블 슈팅](#-트러블-슈팅)
7. [참고 링크](#-참고-링크)
8. [팀 회고](#-팀-회고)
</br>
## 🍀 소개
`Hemg`와 `hoon` 팀이 만든 3개의 숫자로 하는 숫자 야구 게임입니다. 컴퓨터와의 승부에서 승리해야 합니다.
* 주요 개념: `Collection Types`, `Optionals`, `Naming`
</br>
## 👨💻 팀원
| Hemg | hoon |
| :--------: | :--------: |
| <Img src = "https://user-images.githubusercontent.com/101572902/235090676-acefc28d-a358-486b-b9a6-f9c84c52ae9c.jpeg" width="200" /> |<Img src="https://i.imgur.com/zXoi5OC.jpg" width="200">
|[Github Profile](https://github.com/hemg2) |[Github Profile](https://github.com/Hoon94)
</br>
## ⏰ 타임라인
|날짜|내용|
|:--:|--|
|2023.04.24.| 순서도 고민|
|2023.04.25.| 랜덤한 정수를 생성하는 함수 및 숫자비교 함수 구현|
|2023.04.26.| PR에 대한 피드백 반영|
|2023.04.27.| 사용자에게 숫자 3개 입력받는 함수 및 메뉴 선택하는 함수 구현|
|2023.04.28.| 프로젝트 회고 및 README 작성|
</br>
## 👀 시각화된 프로젝트 구조
<p align="center">
<img width="800" src="https://i.imgur.com/ARX3ccH.png">
</p>
</br>
## 💻 실행 화면
| 사용자 승리 | 컴퓨터 승리 | 잘못된 입력 |
|:--:|:--:|:--:|
|<img src="https://i.imgur.com/vKv9yJ5.gif" width="300">|<img src="https://i.imgur.com/Y6H8ar2.gif" width="300">|<img src="https://i.imgur.com/a0uzuGW.gif" width="300">|
</br>
## 🛠️ 트러블 슈팅
- #### `Set`과 `Array` 타입 활용
순서도에서 생성한 사용자 랜덤 번호와 컴퓨터 랜덤 번호를 비교하기 위해 어떠한 자료구조를 사용할지에 대해 고민하였습니다. 볼은 순서(위치)와 관계없이 서로 같은 숫자가 있는지를 확인함으로 `Set`을 사용하였고 스트라이크는 순서(위치)와 관련하여 같은 위치에 같은 값이 있는지를 판단하기 때문에 `Array`를 사용하였습니다.
- #### 함수의 재활용성
사용자의 랜덤 숫자 생성과 컴퓨터의 랜덤 숫자 생성 기능을 처음에는 개별 함수로 선언하였습니다. 하지만 결괏값을 받는 변수 즉, 사용자인지 컴퓨터인지의 차이만 존재할 뿐 같은 기능을 수행하므로 하나의 함수로 변경하였습니다.
- `generateRandomNumber()`의 함수에서 랜덤 숫자을 만들고 `randomNumber` 에 할당을 진행했었습니다.
```swift
func generateRandomNumber() -> Int {
return Int.random(in: 1...9)
}
while randomNumbers.count < 3 {
randomNumber = generateRandomNumber()
```
`randomNumber`에서 `generateRandomNumber()` 함수를 호출하는 방식에서 `Int.random(in: 1...9)`로 랜덤 숫자를 가져오는 방식으로 변경하였습니다.
```swift
while randomNumbers.count < 3 {
randomNumber = Int.random(in: 1...9)
```
- #### 중복 숫자 확인에 대한 활용
랜덤 숫자를 포함시킬때 inserted의 대한 부분에 대해서 처음에는 `Set`을 사용하여 `insert`와 `contains`를해서 숫자를 포함시켰는데 `Array` 로 변경함에 있어서 중복된 숫자를 포함 시키지 않기 위해서 어떤 방법을 선택해야 할지 고민하다가 inserted을 사용하여 진행했습니다.
- #### `split` 함수
`split`을 이용하여 `readLine` 사용시에 인덱스로 나눌 수 있다고 생각했습니다. `readLine`의 타입이 `String` 값이라 `if let` 바인딩을 하여 `Int` 값으로 변경하여 사용 하려고 했습니다. 처음 생각이 `["1, 2, 3"]` 이라고 생각되어 인트로 감싸서 `Int([1, 2, 3])` 해서 출력이 될꺼라고 생각했었는데 이점에서 잘못 알고 있어 배열 `Int`로 감쌀수없다는 것을 알게 되었고 그래서 `for`문을 통해 인덱스값 하나하나를 `Int`로 변경후 사용하게 되었습니다.
```swift
let components = userGuessingNumbers.split(separator: " ")
for component in components {
guard let guessingNumber = Int(component),
0 < guessingNumber,
guessingNumber < 10
```
- #### 코드의 길이
```swift
guard printMessages(userRandomNumbers: userRandomNumbers, strikeCount: strikeCount, ballCount: ballCount, remainCount: remainCount) else { return }
```
최대 길이 100자 제한을 지키기 위해 `printMessages` 함수에 전달인자 레이블로 wildcard 패턴을 사용하였습니다.
```swift
guard printMessages(with: userRandomNumbers, strikeCount, ballCount, remainCount) else { return }
```
</br>
## 📚 참고 링크
- [🍎Apple Docs: API Design Guidelines](https://www.swift.org/documentation/api-design-guidelines/)
- [🍎Apple Docs: Optionals](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/thebasics/#Optionals)
- [🍎Apple Docs: nil](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/thebasics/#nil)
- [🍎Apple Docs: Optional Binding](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/thebasics/#Optional-Binding)
- [🍎Apple Docs: Collection Types](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/collectiontypes/)
</br>
## 👥 팀 회고
- [팀 회고 링크](https://github.com/hemg2/ios-number-baseball/blob/main/%ED%8C%80%ED%9A%8C%EA%B3%A0.md)
# 팀 회고
## 우리팀이 잘한 점
- 그라운드 룰을 통해 함께 페어프로그래밍하는 고정된 시간을 설정하였습니다.
- 하루마다 목표치를 설정하고 이에 맞춰 프로젝트를 진행하였습니다.
- 함께 서로의 의견을 합친 순서도를 기준으로 프로그래밍을 진행하여 드라이버의 입장에서도 네비게이터의 의도를 쉽게 이해할 수 있었습니다.
- 코드 한줄한줄에도 소통하며 작성을 진행하였으며 그에 맞는 의미를 부여 하고 최선의 코드를 작성했다.
## 우리팀 개선할 점
- 하나의 방법만이 아닌 여러 다른 방법으로도 시도해봤으면 좋았을 것 같습니다.
- branch를 사용해봤으면 좋았을 것 같습니다.
## 서로에게 좋았던 점 피드백
- hoon to Hemg
- 함수를 네이밍하고 기능적으로 분리하는 부분에 대해 많은 시간을 사용하며 스스로의 기준을 가지고 작성하는 부분에서 많이 배웠습니다.
- 의견이 갈리는 부분에서 상대를 이해시키려고 본인의 의견에 대한 명분을 토대로 대처하는 모습을 배웠습니다.
- Hemg to hoon
- 전달인자 레이블, 네이밍등 고민하는데에 있어 시간을 아까워했지만 그에 대한 시간투자하는 이유를 얻게 되었습니다.
- 성격이 급한 나에게 있어 놓치는 부분을 체크하며 생각할 시간, 다시한번 했던 코드를 읽어보는 경험을 얻게 되었습니다.
## 서로에게 아쉬웠던 점 피드백
- hoon: 헤어지기가 너무 아쉽습니다.😭😭😭
- Hemg: 우리 왜이렇게 빨리 만났어요ㅠㅠㅠㅠㅠㅠㅠ