在 Swift 中将整数转换为罗马数字字符串

问题描述 投票:0回答:7

我想在 Swift 中使用

Integer
并将其转换为罗马数字
String
。有什么想法吗?

swift nsnumber swift-extensions
7个回答
4
投票

可以在

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 对改进代码的一些建议。


3
投票

这是我的 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
    }
}

1
投票

罗马数字可以被认为是一种密码。您可以为复合案例编写规则,但它们不是那么一致,因此最好将它们作为实际案例来处理。

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 { }

0
投票

还有一个很好的措施:

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
            }
    }
}

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
    }
}

0
投票
    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
    }
}

-1
投票

在 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
}
© www.soinside.com 2019 - 2024. All rights reserved.