【问题标题】:Break A Number Up To An Array of Individual Digits将一个数字分解为一组单独的数字
【发布时间】:2015-09-09 18:07:04
【问题描述】:

如果我有整数 123,并且我想将数字分解为数组 [1,2,3],那么最好的方法是什么?我已经搞砸了很多,我有以下工作:

var number = 123    
var digits = Array(String(number)).map{Int(strtoul((String($0)),nil,16))}

我看了看,觉得可能有更好/更简单的方法来做到这一点。如果没有,那么它可能会出现在网络搜索中。任何替代想法?

【问题讨论】:

  • 在哪些方面更好?更短,更快,...?
  • 我经常使用地图,这适用于我尝试过的所有数字。我想知道其他方法。
  • 我刚刚意识到我的问题与单个答案并不匹配。但是当我查看其他问题的答案时,我并不总是接受公认的答案。我喜欢另类的想法!

标签: swift


【解决方案1】:

处理数字字符串的 UTF-8 表示形式更容易 因为十进制数字的 UTF-8 编码单元可以很容易地转换为 通过减去一个常数得到相应的整数:

let asciiZero = UInt8(ascii: "0")
let digits = map(String(number).utf8) { Int($0 - asciiZero) }

事实证明这也明显更快。

如果性能是主要目标,那么你应该限制 不使用字符串的简单整数运算方法 或字符:

var digits : [Int] = []
while number > 0 {
    digits.insert(number % 10, atIndex: 0)
    number /= 10
}

为了您的方便,这是我的完整测试代码(编译 Xcode 6.4 在 MacBook Pro 上处于发布模式)。

func digits1(number : Int) -> [Int] {
    let digits = Array(String(number)).map{Int(strtoul((String($0)), nil, 16))}
    return digits
}

func digits2(number : Int) -> [Int] {
    // Use a static property so that the constant is initialized only once.
    struct Statics {
        static let asciiZero = UInt8(ascii: "0")
    }

    let digits = map(String(number).utf8) { Int($0 - Statics.asciiZero) }
    return digits
}

func digits3(var number : Int) -> [Int] {
    var digits : [Int] = []
    while number > 0 {
        digits.insert(number % 10, atIndex: 0)
        number /= 10
    }
    return digits
}

func measure(converter: (Int)-> [Int]) {
    let start = NSDate()
    for n in 1 ... 1_000_000 {
        let digits = converter(n)
    }
    let end = NSDate()
    println(end.timeIntervalSinceDate(start))
}

measure(digits1) // 10.5 s
measure(digits2) // 1.5 s
measure(digits3) // 0.9 s

Swift 3 更新:

func digits(_ number: Int) -> [Int] {
    var number = number
    var digits: [Int] = []
    while number > 0 {
        digits.insert(number % 10, at: 0)
        number /= 10
    }
    return digits
}

print(digits(12345678)) // [1, 2, 3, 4, 5, 6, 7, 8]

这也证明比附加数字要快一些 到一个数组并在最后反转它。

【讨论】:

  • 使用 UTF-8 代码单元似乎可以提供速度优势。加上它读起来很好!
  • @pn1dude:我添加了另一种可能的解决方案(更快但更多代码)。
  • 很好地使用模数 10 和 /= 作为小数移位器弹出器!
  • 适用于严格正整数
  • @ielyamani:是的。没有指定整数是否可以为零或负数,或者在这种情况下预期的结果是什么。需要它的人处理这些案件应该没有问题。
【解决方案2】:

我对 Swift 2 的看法:

var x = 123
var digits = String(x).characters.map { Int(String($0))! } // [1,2,3]

对字符比较明确,所以我认为它的可读性很好。

【讨论】:

  • 是的,没有注意到“Swift 2”的东西……还是回到了 6.4 的操场上。我肯定喜欢它的阅读方式,一旦我们移动到 7,我会尝试它!
【解决方案3】:

另一个 Swift 3 替代方案是使用全局 sequence(state:next:) method

斯威夫特 3.1

let number = 123456
let array = Array(sequence(state: number,
    next: { return $0 > 0 ? ($0 % 10, $0 = $0/10).0 : nil }
    ).reversed())

print(array) // [1, 2, 3, 4, 5, 6]

Swift 3.0

let number = 123456
let array = Array(sequence(state: number,
    next: { (num: inout Int) -> Int? in
        return num > 0 ? (num % 10, num /= 10).0 : nil
    }).reversed())

print(array) // [1, 2, 3, 4, 5, 6]

上面的方法假设一个非负数,而且如果number0,将返回一个空数组([])。涵盖自然数的全部范围如下:

// -123 -> [1, 2, 3]
// 0    -> [0]
// 123  -> [1, 2, 3]

我们可以将上面的修改为:

// for some number ...
let number = ...

// Swift 3.1
let array: [Int]
if number == 0 { array = [0] }
else {
    array =  Array(sequence(state: abs(number),
    next: { return $0 > 0 ? ($0 % 10, $0 = $0/10).0 : nil }
    ).reversed())
}

// Swift 3.0
let array: [Int]
if number == 0 { array = [0] }
else {
    array = Array(sequence(state: number,
    next: { (num: inout Int) -> Int? in
        return num > 0 ? (num % 10, num /= 10).0 : nil
    }).reversed())
}

有关上述元组返回的一些细节

在上面的单行 return 中,我们使用了整洁的 "()-return 操作作为 ()" 类型的元组成员内联,这是我第一次看到使用的方法@MartinR 在他的改进建议中更新the following answer。我们使用(Int, ()) 元组的最后一个成员来改变state 属性num;元组的第一个成员将在“计算”第二个元组成员中执行()-return 操作之前计算。

我们可以在这个元组方法和使用单个deferreturn 语句执行闭包的方法之间进行类比。即return 声明:

return num > 0 ? (num % 10, num /= 10).0 : nil

也可以通过执行这样的闭包来完成(在此上下文中为“长格式”)

return num > 0 ? { defer { num /= 10 }; return num % 10 }() : nil  

我没有对这两种方法进行相互基准测试,但我感觉前者在重复调用时会更快,如上述sequence(state:next:) 的上下文中。


Swift 3.0 与 3.1:上述 next 闭包中的匿名参数

由于 SR-1976 中报告的现已关闭的(Swift 3.1 及更高版本)错误(Swift 3 中的闭包签名需要 inout 参数),因此 Swift 对 inout 的类型推断存在限制闭包的参数。参见例如详情如下问答:

这就是为什么我们必须在上面的 Swift 3.0 解决方案的 next 闭包中显式注释 state 的类型,而我们可以在 Swift 的 next 闭包中使用匿名参数3.1解决方案。

【讨论】:

  • 嘿,我正要添加那个解决方案 :)
  • @MartinR 啊,我希望我没有像你更新你的那样发布它!太糟糕了,我们不能为 next 闭包使用匿名参数(由于inout 参数错误SR-1976,我认为),或者它可以更整洁:)
  • 基于序列的解决方案比插入或附加到数组的显式循环快一点。稍后我会添加一些基准。
  • @MartinR 我没想到它会与显式循环相提并论,这很好。谢谢!
  • extension BinaryInteger { var digits: [Self] { var source = self return sequence(state: source) { _ in source > 0 ? (source % 10, source /= 10).0 : nil }.reversed() } }
【解决方案4】:

我不知道您是否是 swift 新手,但请明确一点,您使用 map 的方法最适合您想要做的事情:) 我不推荐另一种方法,因为对代码结构进行良好的可视化非常重要。

import Foundation

var number2 = 123
var number3 : String = "\(number2)"
var array : [String] = []
var str2 = ""
for i in number3.characters
{
    str2.append(i)
    var string = NSString(string: "str")
    string.doubleValue
    array.append(str2)
    str2 = ""
}

干杯

【讨论】:

  • 使用 swift 已经有一段时间了,但是每次发布新版本时,我有时都会质疑我知道多少……我经常使用地图。在 for 循环中,“i”默认为 Character 类型。
  • map 和 join 是 swift 的两个很棒的功能,因为您实际上避免了执行 for 循环等的所有麻烦。保持代码清洁和代码更快、更智能的真正好方法。我建议你看看闭包。我认为这些东西是斯威夫特最好的东西之一。真正简化事情
  • 同意 -> 地图连接关闭!天哪!
【解决方案5】:

我会说如果它没有坏就不要修理它。我可以换一种方式,但它不会更短或任何东西:

var number = 123
var digits = map(String(number)) { String($0).toInt() ?? 0 }

【讨论】:

  • 你可能是对的,但这读起来更好看!在 6.4 操场上运行良好。
  • 仅仅因为某些东西(还)没有损坏是不是不修复它的理由。代码优化(美学事件,也就是可读性)总是有序的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-03-18
  • 2013-08-08
  • 2012-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多