31750


整数型のビットの並びを表示するジェネリックス関数を作成したいと思っています。  
最初に、UInt8型のみ対応を作成してみました。

ここで、1つの疑問があり、型に対するバイト数を知る方法がわかりません。  
ネット拾ったコードでサイズを取得するようにしました。  
sizeof()って使えますか？

[code]

    func BitStringUInt8(n:UInt8)->String{
    
        var bitString : String = ""
        for i in (0...(MemoryLayout<UInt8>.size)*8-1).reversed() {
            if ((n >> UInt8(i)) & 0x1) == 0x1{
                bitString += "1"    
            }else{
                bitString += "0"
            }
        }
        return bitString
    }
    
[/code]

で、これを元にジェネリックスにしてみました。

[code]

    func BitString<T>(n:T)->String{
    
        var bitString : String = ""
        for i in (0...(MemoryLayout<T>.size)*8-1).reversed() {
            print(i)
            if ((n >> T(i)) & 0x1) == 0x1{
                bitString += "1"    
            }else{
                bitString += "0"
            }
        }
        return bitString
    }
    
[/code]

if ((n >> T(i)) & 0x1) == 0x1{のT(i)でコンパイルエラーとなります。

型条件としてBitwiseOperationsとしてみたのですが、エラーが取れません。  
教えてください。イニシャライズが無いとのエラーです。

また、なぜ、シフト演算子は「型」が同じにしなければならないのに、＆演算子は、  
「型」を同じにする必要はないのでしょうか？

「0x1」はInt型となるはずなのに。。。


CC BY-SA 3.0
2017-01-10T09:57:03.460
31747
2017-01-10T21:21:32.687
2017-01-10T14:54:24.177
19110
11148
question
swift swift3
bit演算子とジェネリックス


先に疑問に思われている点から。

**_sizeof()って使えますか？_**

Swift2までは、`sizeof(UInt8)`と言う書き方が出来たのですが、Swift3では`sizeof`関数は削除されました。Swift3では、`MemoryLayout<T>.size`または`MemoryLayout<T>.stride`が`sizeof(T)`に相当すると思ってください。

**_なぜ、シフト演算子は「型」が同じにしなければならないのに、＆演算子は、  
「型」を同じにする必要はないのでしょうか？  
「0x1」はInt型となるはずなのに。。。_**

Swiftの`&`演算子もシフト演算子と同様に左項と右項が同じ型の場合にしか定義されていません。間違っているのは、 _「0x1」はInt型となるはず_
のところですね。

Swiftのリテラル(定数表記)には特定の決まった型はなく、コンテキスト(文脈)によって、そのデータ型が決定されます。`0x1`が`Int`型となるのは、コンテキストにデータ型を決定できるような情報がない場合のデフォルトであって、常に`Int`型になるわけではありません。(これは他のC言語系文法を持った言語と大きく異なる点です。)

例えば、

[code]

    let a: UInt8 = 3
    let b = 0x1 << a
    
[/code]

と言うコードは普通にコンパイルが通りますが、この場合`0x1`は`UInt8`型として解釈されるために _シフト演算子は「型」が同じにしなければならない_
にも関わらずコンパイル出来るわけです。

* * *

さて、本題ですが、あなたのジェネリック版のコードを動かすためには、次のような条件が必要です。

  1. `T(i)`と言うイニシャライザ構文で`Int`型の値`i`を`T`型に変換できること
  2. 2つの`T`型の値(`n`と`T(i)`)で`>>`演算が実行できること
  3. 整数リテラル`0x1`が`T`型として解釈できること
  4. 2つの`T`型の値(`(n >> T(i))`と`0x1`)で`&`演算が実行できること
  5. 2つの`T`型の値(`((n >> T(i)) & 0x1)`と`0x1`)で`==`演算が実行できること

残念ながら`BitwiseOperations`と言うプロトコルは4.の`&`演算が実行できるという条件しか現しませんので、あなたのコードはコンパイルできないと言うことになります。

上記5つの条件をすべて満たすようなプロトコルはSwiftの標準ライブラリには存在しないので、自分で宣言してやる必要があるでしょう。

[code]

    protocol BinaryInteger: ExpressibleByIntegerLiteral/*3.*/, BitwiseOperations/*4.*/, Equatable/*5.*/ {
        init(_ value: Int) //1.
        static func >> (lhs: Self, rhs: Self) -> Self //2.
    }
    extension Int: BinaryInteger {}
    extension UInt: BinaryInteger {}
    extension Int8: BinaryInteger {}
    extension UInt8: BinaryInteger {}
    extension Int16: BinaryInteger {}
    extension UInt16: BinaryInteger {}
    extension Int32: BinaryInteger {}
    extension UInt32: BinaryInteger {}
    extension Int64: BinaryInteger {}
    extension UInt64: BinaryInteger {}
    
    func bitString<T: BinaryInteger>(_ n: T) -> String {
        var result : String = ""
        for i in (0..<(MemoryLayout<T>.size)*8).reversed() {
            //print(i)
            if ((n >> T(i)) & 0x1) == 0x1{
                result += "1"
            }else{
                result += "0"
            }
        }
        return result
    }
    
    
    print(bitString(0x123 as UInt16)) //->0000000100100011
    print(bitString(-0x12 as Int8)) //->11101110
    
[/code]

(ちなみに`as データ型`と言うのが定数リテラルのデータ型を明示する場合のSwift流の記法です。)


CC BY-SA 3.0
2017-01-10T11:03:18.580
31750
2017-01-10T21:21:32.687
2017-01-10T21:21:32.687
13972
13972
31747
answer