我想在 Swift 中使用
Integer
并将其转换为罗马数字 String
。有什么想法吗?
可以在
Int
上写一个扩展,类似于下面看到的那个。
请注意:对于小于一的数字,此代码将返回“”。虽然就罗马数字而言这可能没问题(零不存在),但您可能希望在自己的实现中以不同方式处理它。
extension Int {
var romanNumeral: String {
var integerValue = self
// Roman numerals cannot be represented in integers greater than 3999
if self >= 4000 {
return self
}
var numeralString = ""
let mappingList: [(Int, String)] = [(1000, "M"), (900, "CM"), (500, "D"), (400, "CD"), (100, "C"), (90, "XC"), (50, "L"), (40, "XL"), (10, "X"), (9, "IX"), (5, "V"), (4, "IV"), (1, "I")]
for i in mappingList {
while (integerValue >= i.0) {
integerValue -= i.0
numeralString += i.1
}
}
return numeralString
}
}
感谢 Kenneth Bruno 对改进代码的一些建议。
这是我的 int 到 roman 转换器的版本(没有嵌套循环):
extension Int {
func toRoman() -> String {
let conversionTable: [(intNumber: Int, romanNumber: String)] =
[(1000, "M"),
(900, "CM"),
(500, "D"),
(400, "CD"),
(100, "C"),
(90, "XC"),
(50, "L"),
(40, "XL"),
(10, "X"),
(9, "IX"),
(5, "V"),
(4, "IV"),
(1, "I")]
var roman = ""
var remainder = 0
for entry in conversionTable {
let quotient = (self - remainder) / entry.intNumber
remainder += quotient * entry.intNumber
roman += String(repeating: entry.romanNumber, count: quotient)
}
return roman
}
}
罗马数字可以被认为是一种密码。您可以为复合案例编写规则,但它们不是那么一致,因此最好将它们作为实际案例来处理。
String([RomanNumeral](3456)) // MMMCDLVI
import Algorithms
/// A cipher between numbers and strings.
/// - Precondition: `allCases` is sorted.
public protocol NumericCipher: RawRepresentable & CaseIterable
where RawValue: BinaryInteger, AllCases: BidirectionalCollection { }
public extension Array where Element: NumericCipher {
init(_ number: Element.RawValue) {
self = .init(
sequence(
state: (remainder: number, index: Element.allCases.indices.last!)
) { state in
guard let (index, element) = Element.allCases.indexed()
.prefix(through: state.index)
.last(where: { $0.element.rawValue <= state.remainder })
else { return nil }
state.remainder -= element.rawValue
state.index = index
return element
}
)
}
}
public extension String {
init(_ cipher: some Sequence<some NumericCipher>) {
self = cipher.map { "\($0)" }.joined()
}
}
public enum RomanNumeral: Int {
case i = 1
case iv = 4
case v = 5
case x = 10
case xl = 40
case l = 50
case xc = 90
case c = 100
case cd = 400
case d = 500
case cm = 900
case m = 1000
}
extension RomanNumeral: CustomStringConvertible {
public var description: String {
switch self {
case .i: return "I"
case .iv: return "\(Self.i)\(Self.v)"
case .v: return "V"
case .x: return "X"
case .xl: return "\(Self.x)\(Self.l)"
case .l: return "L"
case .xc: return "\(Self.x)\(Self.c)"
case .c: return "C"
case .cd: return "\(Self.c)\(Self.d)"
case .d: return "D"
case .cm: return "\(Self.c)\(Self.m)"
case .m: return "M"
}
}
}
extension RomanNumeral: NumericCipher { }
还有一个很好的措施:
fileprivate let romanNumerals: [String] = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"]
fileprivate let arabicNumerals: [Int] = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]
extension Int {
var romanRepresentation: String {
guard self > 0 && self < 4000 else {
return "Invalid Number"
}
var control: Int = self
return zip(arabicNumerals, romanNumerals)
.reduce(into: "") { partialResult, ar in
partialResult += String(repeating: ar.1, count: control/ar.0)
control = control % ar.0
}
}
}
Brian Sachetta 版本的补充。如果你想超越 4999,你可以使用一组增强的罗马数字。此集合中最大的数字是 8,999,999,999,999,即 OZZZQZUQBUGBTGRTHREHMECMXCIX。 Set 使用拉丁字母表中的所有字母。
extension Int {
var romanNumeral: String {
var integerValue = self
var numeralString = ""
let mappingList: [(Int, String)] = [(5000000000000, "O"), (4000000000000, "ZO"), (1000000000000, "Z"),
(900000000000, "QZ"), (500000000000, "Y"), (400000000000, "QY"), (100000000000, "Q"),
(90000000000, "UQ"), (50000000000, "W"), (40000000000, "UW"), (10000000000, "U"),
(9000000000, "BU"), (5000000000, "A"), (4000000000, "BA"), (1000000000, "B"),
(900000000, "GB"), (500000000, "J"), (400000000, "JG"), (100000000, "G"),
(90000000, "TG"), (50000000, "S"), (40000000, "TS"), (10000000, "T"),
(9000000, "RT"), (5000000, "P"), (4000000, "RP"), (1000000, "R"),
(900000, "HR"), (500000, "K"), (400000, "HK"), (100000, "H"),
(90000, "EH"), (50000, "F"), (40000, "EF"), (10000, "E"),
(9000, "ME"), (5000, "N"), (4000, "MN"), (1000, "M"),
(900, "CM"), (500, "D"), (400, "CD"), (100, "C"),
(90, "XC"), (50, "L"), (40, "XL"), (10, "X"),
(9, "IX"), (5, "V"), (4, "IV"), (1, "I")]
for i in mappingList {
while (integerValue >= i.0) {
integerValue -= i.0
numeralString += i.1
}
}
return numeralString
}
}
extension Int {
func convertToOrdinal() -> String {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .ordinal
guard let ordinalString = numberFormatter.string(from: NSNumber(value: self)) else {
return "\(self)"
}
return ordinalString
}
}
在 Swift 中将整数转换为罗马数字字符串
func romanToInt(_ s: String) -> Int {
let dict = ["M": 1000, "D": 500, "C": 100,"L": 50, "X": 10, "V": 5, "I": 1]
var ans = 0
for i in s.reversed() {
guard let num = dict[String(i)] else {
return 0
}
if (4 * num < ans){
ans -= num
} else {
ans += num
}
}
return ans
}