我想要一個函數,它需要一個位掩碼Int
,並將其掩碼值作爲一組Int
返回。是這樣的:如何將位掩碼Int轉換爲一組Ints?
func split(bitmask: Int) -> Set<Int> {
// Do magic
}
使得
split(bitmask: 0b01001110) == [0b1000000, 0b1000, 0b100, 0b10]
我想要一個函數,它需要一個位掩碼Int
,並將其掩碼值作爲一組Int
返回。是這樣的:如何將位掩碼Int轉換爲一組Ints?
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
}
@MartinR我用'&*'修正了可能的溢出。 – rmaddy
請注意,最常見的用例s用於位掩碼包括將特定的有意義的布爾標誌的集合打包爲單個字大小的值,並對這些標誌執行測試。 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
類型的一些值,它是有意義的,以你的使用情況。)
的OptionSet
實際上是一個單一的值(它本身支持集合代數,而不是一個元素類型),所以它不是一個集合 - 也就是說,它沒有提供枚舉元素的方法。但是,如果你打算使用位掩碼的方式只需要設置和測試特定的標誌(或標誌的組合),也許你不需要枚舉元素的方法。
如果你確實需要列舉的元素,同時也希望所有的OptionSet
的集代數特性,可以如在@rmaddy's answer發現的位分割數學結合OptionSet
:
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({$0.rawValue}) // => [1, 4, 8]
或作爲一個序列:http://stackoverflow.com/a/32103136/1187415。 –
謝謝,但我應該提到,對於這個特殊用例,OptionSet不是一個選項:c –
這裏是我的「 1行「版本:
let values = Set(Array(String(0x01001110, radix: 2).characters).reversed().enumerated().map { (offset, element) -> Int in
Int(String(element))! << offset
}.filter { $0 != 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 { $0 != 0 })
}
編輯:有點短
let values = Set(String(0x01001110, radix: 2).utf8.reversed().enumerated().map { (offset, element) -> Int in
Int(element-48) << offset
}.filter { $0 != 0 })
你的意思'0b01001110'?十六進制值「0x01001110」與二進制數字「0b01001110」具有非常不同的位集。 – rmaddy
@rmaddy是的,謝謝!我想這是肌肉記憶:) –