Last active
April 7, 2019 11:38
-
-
Save sunghyun-k/59a80b46fbbdd81f80d535d17471bfdf to your computer and use it in GitHub Desktop.
단위 변환기 개선버전
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import Foundation | |
| struct Unit { | |
| var notations: [String] | |
| /// 이 수를 곱하면 기준 단위로 변환됩니다. 기준 단위는 1을 가집니다. | |
| let scaleInfo: Double | |
| var isDefaultOutput = false | |
| init(notations: [String], scaleInfo: Double) { | |
| self.notations = notations | |
| self.scaleInfo = scaleInfo | |
| if scaleInfo == 1 { | |
| isDefaultOutput = true | |
| } | |
| } | |
| init() { | |
| notations = [] | |
| scaleInfo = 0 | |
| } | |
| } | |
| /// 측정 형식입니다. | |
| enum Type { | |
| case length | |
| case mass | |
| case volume | |
| case unknown | |
| func description() -> String { | |
| switch self { | |
| case .length: | |
| return "길이" | |
| case .mass: | |
| return "질량" | |
| case .volume: | |
| return "부피" | |
| case .unknown: | |
| return "알 수 없음" | |
| } | |
| } | |
| } | |
| class UnitConverter { | |
| /// 단위 정보를 저장합니다. 첫번째 원소는 기본적으로 표시되는 단위입니다. | |
| var types = [ | |
| Type.length: [ | |
| Unit.init(notations: ["m", "meter"], scaleInfo: 1), | |
| Unit.init(notations: ["cm", "centimeter"], scaleInfo: 0.01), | |
| Unit.init(notations: ["inch"], scaleInfo: 0.0254), | |
| Unit.init(notations: ["yard"], scaleInfo: 0.0277778) | |
| ], | |
| Type.mass: [ | |
| Unit.init(notations: ["g", "gram"], scaleInfo: 1), | |
| Unit.init(notations: ["kg", "kilogram"], scaleInfo: 1000), | |
| Unit.init(notations: ["lbs", "pound", "lb"], scaleInfo: 453.592), | |
| Unit.init(notations: ["oz", "ounce"], scaleInfo: 28.3495) | |
| ], | |
| Type.volume: [ | |
| Unit.init(notations: ["L", "liter", "l"], scaleInfo: 1), | |
| Unit.init(notations: ["pt", "pint"], scaleInfo: 0.473176), | |
| Unit.init(notations: ["qt", "quart"], scaleInfo: 0.946353), | |
| Unit.init(notations: ["gal", "galon"], scaleInfo: 4.546) | |
| ] | |
| ] | |
| /// UnitConverter에 사용자 지정 단위를 추가합니다. | |
| func add(type: Type, unit: Unit) { | |
| types[type]?.append(unit) | |
| } | |
| func convert(_ input: String) -> String { | |
| /// 입력 문자열에서 숫자와 입력 단위, [출력 단위]를 반환합니다. 숫자나 입력 단위가 없으면 nil을 반환합니다. | |
| func convertFormat(_ input: String) -> (inputValue: Double, inputUnit: String, outputUnits: [String])? { | |
| // 첫번째 숫자 및 `.`를 제외한 문자가 나타나면 반복을 멈춥니다. | |
| var number = "" | |
| var unformatedUnits = input | |
| for character in input { | |
| if ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "."].contains(String(character)) { | |
| number += String(character) | |
| unformatedUnits.removeFirst() | |
| } else { | |
| break | |
| } | |
| } | |
| // 숫자가 없으면 nil을 반환합니다. | |
| guard let inputValue = Double(number) else { | |
| return nil | |
| } | |
| // 단위를 inputUnit과 outputUnit으로 나눕니다. inputUnit이 없으면 nil을 반환합니다. | |
| let units = unformatedUnits.components(separatedBy: " ") | |
| var inputUnit = "" | |
| var outputUnits: [String] = [] | |
| if units[0] != "" { | |
| inputUnit = units[0] | |
| } else { | |
| return nil | |
| } | |
| // 출력 단위들을 outputUnit에 저장합니다. | |
| if units.indices.contains(1) { | |
| outputUnits = units[1].components(separatedBy: ",") | |
| } | |
| return (inputValue, inputUnit, outputUnits) | |
| } // convertFormat의 끝 | |
| func convert(_ inputValue: Double, from inputUnit: String, to outputUnits: [String]) -> [(value: Double, unit: Unit)]? { | |
| /// 기준 단위로 변환한 값과 측정 형식을 반환합니다. 호환되는 단위를 찾지 못하면 nil을 반환합니다. | |
| func convertToWaypoint(_ inputValue: Double, from inputUnit: String) -> (type: Type, value: Double)? { | |
| for type in types { | |
| for unit in type.value { | |
| if unit.notations.contains(inputUnit) { | |
| return (type.key, inputValue * unit.scaleInfo) | |
| } | |
| } | |
| } | |
| return nil | |
| } | |
| /// 출력 단위로 변환한 값들을 반환합니다. 비어있는 경우 기준 단위를 반환합니다. | |
| func convertFromWaypoint(_ type: Type, _ inputValue: Double, to outputUnits: [String]) -> [(value: Double, unit: Unit)] { | |
| if outputUnits.isEmpty { | |
| for unit in types[type]! { | |
| if unit.isDefaultOutput { | |
| return [(inputValue, unit)] | |
| } | |
| } | |
| } | |
| var outputs: [(Double, Unit)] = [] | |
| for outputUnit in outputUnits { | |
| for unit in types[type]! { | |
| if unit.notations.contains(outputUnit) { | |
| outputs.append((inputValue / unit.scaleInfo, unit)) | |
| } | |
| } | |
| } | |
| return outputs | |
| } | |
| guard let waypoint = convertToWaypoint(inputValue, from: inputUnit) else { | |
| return nil | |
| } | |
| return convertFromWaypoint(waypoint.type, waypoint.value, to: outputUnits) | |
| } // convert의 끝 | |
| guard let info = convertFormat(input) else { | |
| return "올바른 형식이 아닙니다." | |
| } | |
| guard let outputs = convert(info.inputValue, from: info.inputUnit, to: info.outputUnits) else { | |
| return "호환되는 단위가 없습니다." | |
| } | |
| var message = "" | |
| for output in outputs { | |
| if output.value == Double(Int(output.value)) { | |
| message += "결과: \(Int(output.value))\(output.unit.notations[0])\n" | |
| } else { | |
| message += "결과: \((output.value * 1000).rounded() / 1000)\(output.unit.notations[0])\n" | |
| } | |
| } | |
| return message | |
| } | |
| } // UnitConverter Class의 끝 | |
| // 사용자 조작 방식 설정 | |
| let unitConverter = UnitConverter() | |
| let startMessage = """ | |
| 변환할 단위를 입력하십시오. | |
| 예) 10cm m,yard -> 10cm를 미터와 야드로 변환합니다. | |
| 지원되는 단위 목록: list, l | |
| 단위 추가: add, a | |
| 종료: quit, q | |
| """ | |
| func runUnitConverter() { | |
| print(startMessage) | |
| while true { | |
| let input = readLine()! | |
| switch input { | |
| case "list", "l": | |
| supportedUnits() | |
| case "add", "a": | |
| addUnit() | |
| print(startMessage) | |
| case "quit", "q": | |
| return | |
| default: | |
| print(unitConverter.convert(input)) | |
| } | |
| } | |
| } | |
| func supportedUnits() { | |
| for type in unitConverter.types { | |
| print("\(type.key.description()):") | |
| for unit in type.value { | |
| print(unit.notations[0]) | |
| } | |
| print() | |
| } | |
| } | |
| func addUnit() { | |
| print(""" | |
| 형식을 지정하십시오. | |
| 길이: 1 | |
| 질량: 2 | |
| 부피: 3 | |
| """) | |
| var type = Type.unknown | |
| while type == Type.unknown { | |
| let selectedType = readLine()! | |
| switch selectedType { | |
| case "1": | |
| type = Type.length | |
| case "2": | |
| type = Type.mass | |
| case "3": | |
| type = Type.volume | |
| default: | |
| print("오류: 형식의 숫자를 입력하세요.") | |
| } | |
| } | |
| print("이 단위의 기본 표기법을 입력하십시오.") | |
| var notations: [String] = [] | |
| while true { | |
| let defaultNotation = readLine()! | |
| if defaultNotation == "" { | |
| print("빈 문자열을 사용할 수 없습니다. 다시 입력하십시오.") | |
| } else { | |
| notations.append(defaultNotation) | |
| break | |
| } | |
| } | |
| print("인식할 수 있도록 다른 표기 방법들을 입력해주십시오. 입력이 끝나면 0 입력.") | |
| while true { | |
| let notation = readLine()! | |
| if notation == "" { | |
| print("빈 문자열을 사용할 수 없습니다. 다시 입력하십시오.") | |
| } else if notation == "0" { | |
| break | |
| } else { | |
| notations.append(notation) | |
| } | |
| } | |
| // 기본 단위 찾고 저장하기 | |
| var waypointUnit = Unit.init() | |
| for unit in unitConverter.types[type]! { | |
| if unit.isDefaultOutput == true { | |
| waypointUnit = unit | |
| break | |
| } | |
| } | |
| print("이 단위를 기준 단위(\(waypointUnit.notations[0]))로 변환하려면 어떤 수를 곱해야 합니까?") | |
| var scaleInfo = Double() | |
| while true { | |
| if let number = Double(readLine()!) { | |
| if number == 0 { | |
| print("0으로 지정할 수 없습니다. 다시 입력하십시오.") | |
| } else { | |
| scaleInfo = number | |
| break | |
| } | |
| } else { | |
| print("숫자가 아닙니다. 다시 입력하십시오.") | |
| } | |
| } | |
| unitConverter.add(type: type, unit: Unit.init(notations: notations, scaleInfo: scaleInfo)) | |
| print(""" | |
| 아래 내용을 저장했습니다. | |
| - 유형: \(type.description()) | |
| - 표기: \(notations) | |
| - 기준 단위(\(waypointUnit.notations[0]))로 변환 시에 곱해야 하는 수: \(scaleInfo) | |
| """) | |
| } // addUnit의 끝 | |
| runUnitConverter() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment