如何将位掩码 Int 转换为一组 Int?
How do I convert a bitmask Int into a set of Ints?
我想要一个接受位掩码 Int
和 returns 其掩码值作为一组 Int
的函数。像这样:
func split(bitmask: Int) -> Set<Int> {
// Do magic
}
这样
split(bitmask: 0b01001110) == [0b1000000, 0b1000, 0b100, 0b10]
一种解决方案是检查每一位,如果该位已设置,则添加相应的掩码。
func split(bitmask: Int) -> Set<Int> {
var results = Set<Int>()
// Change 31 to 63 or some other appropriate number based on how big your numbers can be
for shift in 0...31 {
let mask = 1 << shift
if bitmask & mask != 0 {
results.insert(mask)
}
}
return results
}
print(split(bitmask: 0b01001110))
对于二进制数 0b01001110
,结果将是:
[64, 2, 4, 8]
这是你问题中结果的十进制等值。
对于十六进制数 0x01001110
(二进制为 1000100010000
),结果将是:
[16, 256, 4096, 16777216]
这是另一种解决方案,它不需要知道值的大小,而且对于较小的数字来说效率稍微高一些:
func split(bitmask: Int) -> Set<Int> {
var results = Set<Int>()
var value = bitmask
var mask = 1
while value > 0 {
if value % 2 == 1 {
results.insert(mask)
}
value /= 2
mask = mask &* 2
}
return results
}
请注意,位掩码最常见的用例包括将一组特定的、有意义的布尔标志打包成单个字大小的值,并针对这些标志执行测试。 Swift 在 OptionSet
类型中为此提供便利。
struct Bits: OptionSet {
let rawValue: UInt // unsigned is usually best for bitfield math
init(rawValue: UInt) { self.rawValue = rawValue }
static let one = Bits(rawValue: 0b1)
static let two = Bits(rawValue: 0b10)
static let four = Bits(rawValue: 0b100)
static let eight = Bits(rawValue: 0b1000)
}
let someBits = Bits(rawValue: 13)
// the following all return true:
someBits.contains(.four)
someBits.isDisjoint(with: .two)
someBits == [.one, .four, .eight]
someBits == [.four, .four, .eight, .one] // set algebra: order/duplicates moot
someBits == Bits(rawValue: 0b1011)
(在实际使用中,当然,您会在 OptionSet
中为每个 "element" 值提供一些对您的用例有意义的值。)
一个 OptionSet
实际上是一个单一的值(它本身支持集合代数,而不是元素类型),所以它不是一个集合——也就是说,它不提供一种枚举其元素的方法。但是如果您打算使用位掩码的方式只需要设置和测试特定标志(或标志组合),也许您不需要枚举元素的方法。
如果您确实需要枚举元素,但又想要 OptionSet
的所有集合代数功能,您可以将 OptionSet
与 [=18] 中的位拆分数学相结合=]:
extension OptionSet where RawValue == UInt { // try being more generic?
var discreteElements: [Self] {
var result = [Self]()
var bitmask = self.rawValue
var element = RawValue(1)
while bitmask > 0 && element < ~RawValue.allZeros {
if bitmask & 0b1 == 1 {
result.append(Self(rawValue: element))
}
bitmask >>= 1
element <<= 1
}
return result
}
}
someBits.discreteElements.map({[=11=].rawValue}) // => [1, 4, 8]
这是我的“1 行”版本:
let values = Set(Array(String(0x01001110, radix: 2).characters).reversed().enumerated().map { (offset, element) -> Int in
Int(String(element))! << offset
}.filter { [=10=] != 0 })
效率不高,但很有趣!
编辑: 封装在拆分函数中...
func split(bitmask: Int) -> Set<Int> {
return Set(Array(String(bitmask, radix: 2).characters).reversed().enumerated().map { (offset, element) -> Int in
Int(String(element))! << offset
}.filter { [=11=] != 0 })
}
编辑:短一点
let values = Set(String(0x01001110, radix: 2).utf8.reversed().enumerated().map { (offset, element) -> Int in
Int(element-48) << offset
}.filter { [=12=] != 0 })
我想要一个接受位掩码 Int
和 returns 其掩码值作为一组 Int
的函数。像这样:
func split(bitmask: Int) -> Set<Int> {
// Do magic
}
这样
split(bitmask: 0b01001110) == [0b1000000, 0b1000, 0b100, 0b10]
一种解决方案是检查每一位,如果该位已设置,则添加相应的掩码。
func split(bitmask: Int) -> Set<Int> {
var results = Set<Int>()
// Change 31 to 63 or some other appropriate number based on how big your numbers can be
for shift in 0...31 {
let mask = 1 << shift
if bitmask & mask != 0 {
results.insert(mask)
}
}
return results
}
print(split(bitmask: 0b01001110))
对于二进制数 0b01001110
,结果将是:
[64, 2, 4, 8]
这是你问题中结果的十进制等值。
对于十六进制数 0x01001110
(二进制为 1000100010000
),结果将是:
[16, 256, 4096, 16777216]
这是另一种解决方案,它不需要知道值的大小,而且对于较小的数字来说效率稍微高一些:
func split(bitmask: Int) -> Set<Int> {
var results = Set<Int>()
var value = bitmask
var mask = 1
while value > 0 {
if value % 2 == 1 {
results.insert(mask)
}
value /= 2
mask = mask &* 2
}
return results
}
请注意,位掩码最常见的用例包括将一组特定的、有意义的布尔标志打包成单个字大小的值,并针对这些标志执行测试。 Swift 在 OptionSet
类型中为此提供便利。
struct Bits: OptionSet {
let rawValue: UInt // unsigned is usually best for bitfield math
init(rawValue: UInt) { self.rawValue = rawValue }
static let one = Bits(rawValue: 0b1)
static let two = Bits(rawValue: 0b10)
static let four = Bits(rawValue: 0b100)
static let eight = Bits(rawValue: 0b1000)
}
let someBits = Bits(rawValue: 13)
// the following all return true:
someBits.contains(.four)
someBits.isDisjoint(with: .two)
someBits == [.one, .four, .eight]
someBits == [.four, .four, .eight, .one] // set algebra: order/duplicates moot
someBits == Bits(rawValue: 0b1011)
(在实际使用中,当然,您会在 OptionSet
中为每个 "element" 值提供一些对您的用例有意义的值。)
一个 OptionSet
实际上是一个单一的值(它本身支持集合代数,而不是元素类型),所以它不是一个集合——也就是说,它不提供一种枚举其元素的方法。但是如果您打算使用位掩码的方式只需要设置和测试特定标志(或标志组合),也许您不需要枚举元素的方法。
如果您确实需要枚举元素,但又想要 OptionSet
的所有集合代数功能,您可以将 OptionSet
与 [=18] 中的位拆分数学相结合=]:
extension OptionSet where RawValue == UInt { // try being more generic?
var discreteElements: [Self] {
var result = [Self]()
var bitmask = self.rawValue
var element = RawValue(1)
while bitmask > 0 && element < ~RawValue.allZeros {
if bitmask & 0b1 == 1 {
result.append(Self(rawValue: element))
}
bitmask >>= 1
element <<= 1
}
return result
}
}
someBits.discreteElements.map({[=11=].rawValue}) // => [1, 4, 8]
这是我的“1 行”版本:
let values = Set(Array(String(0x01001110, radix: 2).characters).reversed().enumerated().map { (offset, element) -> Int in
Int(String(element))! << offset
}.filter { [=10=] != 0 })
效率不高,但很有趣!
编辑: 封装在拆分函数中...
func split(bitmask: Int) -> Set<Int> {
return Set(Array(String(bitmask, radix: 2).characters).reversed().enumerated().map { (offset, element) -> Int in
Int(String(element))! << offset
}.filter { [=11=] != 0 })
}
编辑:短一点
let values = Set(String(0x01001110, radix: 2).utf8.reversed().enumerated().map { (offset, element) -> Int in
Int(element-48) << offset
}.filter { [=12=] != 0 })