【问题标题】:How to replace a complicated C-style for loop in Swift 2.2如何在 Swift 2.2 中替换复杂的 C 风格 for 循环
【发布时间】:2016-05-01 18:40:26
【问题描述】:

对于我开发的无符号整数类型库,我有一个专门的 C 风格 for 循环,用于计算存储数值中的有效位。我一直在为如何将其转换为 Swift 2.2+ 样式的 for 循环而苦苦挣扎。这是有问题的代码:

/// Counts up the significant bits in stored data.
public var significantBits: UInt128 {
    // Will turn into final result.
    var significantBitCount: UInt128 = 0
    // The bits to crawl in loop.
    var bitsToWalk: UInt64 = 0
    if self.value.upperBits > 0 {
        bitsToWalk = self.value.upperBits
        // When upperBits > 0, lowerBits are all significant.
        significantBitCount += 64
    } else if self.value.lowerBits > 0 {
        bitsToWalk = self.value.lowerBits
    }
    if bitsToWalk > 0 {
        // Walk significant bits by shifting right until all bits are equal to 0.
        for var bitsLeft = bitsToWalk; bitsLeft > 0; bitsLeft >>= 1 {
            significantBitCount += 1
        }
    }
    return significantBitCount
}

我确信有多种方法可以处理这个问题,我可以想出一些更详细的方法来处理这个问题,但我有兴趣找到一种简洁的方法来处理这种情况,我可以重新应用于类似的情况。我发现我很少使用 C 风格的 for 循环,但是当我这样做时,它是用于像这样的奇怪场景,它是处理问题的最简洁的方法。

【问题讨论】:

  • 你不能用“while”循环替换它吗?
  • 您可以简单地使用while 循环,或this cool code 来自@MartinR
  • @nhgrif 你可以通过加载我的 UInt128 项目在 GitHub 上查看我的全部源代码。
  • 你没有链接到它。但实际上,如果您将问题归结为实际的相关部分(只是循环)并包含确切的最少代码量,如果我将其复制并粘贴到操场上,它可以正常工作,这将是最理想的。加上一些测试用例。
  • 有什么原因不能使用log2() 来计算有效位数吗?

标签: swift


【解决方案1】:

最简单的解决方案是只使用while 循环:

替换此代码:

if bitsToWalk > 0 {
    // Walk significant bits by shifting right until all bits are equal to 0.
    for var bitsLeft = bitsToWalk; bitsLeft > 0; bitsLeft >>= 1 {
        significantBitCount += 1
    }
}

使用以下while 循环:

while bitsToWalk > 0 {
    significantBitCount += 1
    bitsToWalk >>= 1
}

【讨论】:

  • 感谢您的回答。这也是我的想法。就我而言,这可能是 Swift 改变之前的最佳答案。从我所有的研究来看,现在 Swift 似乎不再包含支持更复杂步进的语法,因为 C 风格的 for 循环已被弃用。
【解决方案2】:

一种选择是使用内置处理器功能:

放:

#import <x86intrin.h>

到您的 Obj-C 桥接头,然后在 Swift 中:

let number: UInt64 = 111
let mostSignificantBit = _lzcnt_u64(number)

print(mostSignificantBit)

(当然,您必须在正确的架构上,此功能仅在 x86 上定义。此解决方案不完全可移植)。

【讨论】:

  • 这是 OS X 还是 iOS 解决方案?
  • 不过,原始代码并未计算设置位的数量。它正在寻找最高设置位。也许你的意思是_lzcnt_u64
  • 说明如何?如果我将bitsToWalk 设置为2,则significantBitCount 为2。如果我将bitsToWalk 设置为3,则significantBitCount 也是2。
【解决方案3】:

此函数应计算UInt64 值中的有效位数:

import Foundation

func significantBits(n: UInt64) -> Int {
    return Int(ceil(log2(Double(n))))
}

let n: UInt64 = 0xFFFFFFFFFFFFFFFF  //  64 significant bits
let m: UInt64 = 0b11011             //  5 significant bits

print("n has \(significantBits(n)) significant bits.")
print("m has \(significantBits(m)) significant bits.")

和输出:

n has 64 significant bits.
m has 5 significant bits.

您可以将您的代码替换为以下内容:

private func calcSigBits(n: UInt64) -> Int {
    return Int(ceil(log2(Double(n))))
}

public var significantBits: Int {
    if self.value.upperBits > 0 {
        return calcSigBits(self.value.upperBits) + 64
    }
    else {
        return calcSigBits(self.value.lowerBits)
    }
}

如果您不想使用log2,您可以使用 nhgrif 的答案中的循环,但重构它仍然很好,因为它在概念上是一个独立的操作,并使您自己的代码更简单。你甚至可以将它作为扩展添加到UInt64

extension UInt64 {
    public var significantBits: Int {
        var sb = 0
        var value = self
        while value > 0 {
            sb += 1
            value >>= 1
        }
        return sb
    }
}

//  Rest of your class definition...

public var significantBits: Int {
    if self.value.upperBits > 0 {
        return self.value.upperBits.significantBits + 64
    }
    else {
        return self.value.lowerBits.significantBits
    }
}

【讨论】:

    猜你喜欢
    • 2016-07-10
    • 1970-01-01
    • 2020-10-04
    • 2017-07-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-22
    • 2020-11-05
    相关资源
    最近更新 更多