swift 将数字分解为单个数字的数组

6qftjkof  于 2023-01-12  发布在  Swift
关注(0)|答案(6)|浏览(158)

如果我有一个整数1,2,3,我想把这些数字拆分成一个数组[1,2,3],最好的方法是什么?我已经做了很多次了,我有下面的工作:

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

我看了看,觉得可能有更好/更容易的方法来做这件事。如果没有,那么也许它会出现在网络搜索。有什么替代的想法吗?

ldioqlga

ldioqlga1#

处理数字字符串的UTF-8表示更容易,因为十进制数字的UTF-8代码单位可以通过减去一个常数轻松地转换为相应的整数:

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

这也被证明是明显更快。
如果 performance 是主要目标,那么您应该将该方法限制为简单的整数运算,而不使用字符串或字符:

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

以下是我完整的测试代码,方便您使用(在MacBook Pro的发布模式下使用Xcode 6.4编译)。

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]

这也证明比将数字附加到数组并在末尾反转它要稍微快一些。

v1uwarro

v1uwarro2#

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]

雨燕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操作inlined as tuple member of type (),这是我第一次看到@MartinR在他的改进建议中使用的方法,用于更新the following answer。我们使用(Int, ())元组的最后一个成员来改变state属性num;在“计算”第二元组成员时,将在执行()-返回操作之前计算元组的第一成员。
我们可以在这个元组方法和用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及更高版本)bug(* Swift 3中inout参数需要闭包签名 *),Swift对inout参数的类型推断存在限制。

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

dzjeubhm

dzjeubhm3#

我对斯威夫特2的看法:

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

它对人物的描述更明确,所以我认为它相当具有可读性。

7rfyedvj

7rfyedvj4#

其他的答案没有考虑基数--这个例子要求16,而不是10。

public extension UnsignedInteger {
  /// The digits that make up this number.
  /// - Parameter radix: The base the result will use.
  /// - Note: Leading zeros are not taken into account. Zero itself will yield an empty array.
  @inlinable func digits(radix: Self = 10) -> [Self] {
    sequence(state: self) { quotient in
      guard quotient > 0 else { return nil }

      let division = quotient.quotientAndRemainder(dividingBy: radix)
      quotient = division.quotient
      return division.remainder
    }
    .reversed()
  }
}
(867_5309 as UInt32).digits()
[8,6,7, 5,3,0,9]

(0xF0 as UInt8).digits(radix: 0b10)
[1,1,1,1, 0,0,0,0]

(0xA0_B1_C2_D3_E4_F5 as UInt).digits(radix: 0x10)
[10,0, 11,1, 12,2, 13,3, 14,4, 15,5]

(0o707 as UInt16).digits(radix: 0o10)
[0b111, 0, 0b111]
0ve6wy6x

0ve6wy6x5#

我不知道你是否是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 = ""
}

干杯

fdbelqdn

fdbelqdn6#

我会说,如果它没有坏,就不要修理它。我可以想到另一种办法,但它不会短一点或什么的:

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

相关问题