none
Bitフラグの扱い RRS feed

  • 質問

  • お世話になっております。

    早速ですが、質問させてください。

     

    現在、VB.netで開発しておりまして下記のような処理があります。

     

    if(a=a) then f_a = True
    if(b=b) then f_b = True
    ・・・・
    if(g=g) then f_g = True
    

     

    この後、全てのフラグを見て、

    一つでも立っているか、一つも立っていないか等を検出したいのです。

    Cで開発していた時は共用体を用い、その1byteにOrしてあげたりと

    していましたが、VBとなると調べた結果、BitArrayというのがあるみたいで

    使ってみようにも、

    検出する際、わざわざByteにコピーしてあげて...

    みたいになってしまうのか美しくありません。

     

    見た目ににも、美しく書きやすい

    『複数フラグの簡潔判断方法』

    が、ありましたらご教示ください。

    2010年12月15日 0:47

回答

  • フラグを管理するクラスを作成して、好きなプロパティ、例えば「全てOFFかどうかをBooleanで返す」などを

    実装する方法はどうでしょうか。

    以下簡単にサンプルを作ってみました。必要な機能はプロパティやメソッドとして追加実装できます。

    ■使い方

    ' フラグ数を9個でインスタンスを作成
    Dim fa As New FlgArray(9)
    ' フラグ数を13個で全てONの状態でインスタンスを作成
    Dim fa2 As New FlgArray(13, True)
    
    ' フラグの0番目と7番目を立てる
    fa(0) = True
    fa(7) = True
    ' フラグの6番目の状態を取得する
    Dim flg6 As Boolean = fa(6)
    ' フラグ全てをリセットする
    fa.AllTo = False
    ' フラグが全てONかどうか
    If fa.IsAllOn Then
      Console.WriteLine("全てON")
    End If
    ' フラグが全てOFFかどうか
    If fa.IsAllOff Then
      Console.WriteLine("全てOFF")
    End If
    ' フラグがひとつでもONかどうか
    If fa.IsAnyOn Then
      Console.WriteLine("ひとつでもON")
    End If
    
    

    ■クラス本体

    ''' <summary>
    ''' フラグ管理クラス
    ''' </summary>
    ''' <remarks></remarks>
    Public Class FlgArray
      ' メンバ
      Private _flgs As ULong
      Private _AllOn As ULong = 0UL
      ' コンストラクタ
      Public Sub New(ByVal length As Integer)
        If length > 64 Then
          Throw New Exception("length over.")
        End If
        For i As Integer = 0 To length - 1
          _AllOn = _AllOn Or 1UL << i
        Next
      End Sub
      ' コンストラクタ(初期値指定あり)
      Public Sub New(ByVal length As Integer, ByVal value As Boolean)
        If length > 64 Then
          Throw New Exception("length over.")
        End If
        For i As Integer = 0 To length - 1
          _AllOn = _AllOn Or 1UL << i
        Next
        AllTo = value
      End Sub
      ' フラグの状態を取得・設定する
      Default Public Property Flg(ByVal i As Integer) As Boolean
        Get
          If (_flgs And 1UL << i) = 1UL << i Then
            Return True
          End If
          Return False
        End Get
        Set(ByVal value As Boolean)
          If value Then
            _flgs = _flgs Or 1UL << i
          Else
            _flgs = _flgs And (_AllOn Xor 1UL << i)
          End If
        End Set
      End Property
      ' フラグ全ての値を設定する
      Public WriteOnly Property AllTo() As Boolean
        Set(ByVal value As Boolean)
          If value Then
            _flgs = _AllOn
          Else
            _flgs = 0UL
          End If
        End Set
      End Property
      ' 全てONかどうか
      Public ReadOnly Property IsAllOn() As Boolean
        Get
          If _flgs = _AllOn Then
            Return True
          End If
          Return False
        End Get
      End Property
      ' 全てOFFかどうか
      Public ReadOnly Property IsAllOff() As Boolean
        Get
          If _flgs = 0UL Then
            Return True
          End If
          Return False
        End Get
      End Property
      ' ひとつでもONかどうか
      Public ReadOnly Property IsAnyOn() As Boolean
        Get
          If _flgs > 0UL Then
            Return True
          End If
          Return False
        End Get
      End Property
    End Class
    
    

    • 回答としてマーク segment 2010年12月15日 6:22
    2010年12月15日 5:18

すべての返信

  • 思い付きですが、Linqを使ってwhere句でフラグが立っているものを抽出するというのはいかがでしょうか?

     


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    2010年12月15日 1:13
    モデレータ
  • 複数という時点で、まずフラグの管理はコレクションなり配列で持つ方法がよいかなと思いました。

    そして.NETにはLINQというものがありますので、これを利用して書いた場合、以下のように書くことができます。

    ' Boolean型のフラグデータの配列(BitArrayを利用する場合)
    Dim ba As New BitArray(New Boolean() {False, False, False, False})
    
    ' 1つ以上フラグがたっているか
    If (From r In ba Where r Select r).Count() > 0 Then
     Console.WriteLine("1件以上True")
    End If
    ' 全てフラグがたっていないか
    If (From r In ba Where r Select r).Count() = 0 Then
     Console.WriteLine("全てFalse")
    End If
    ' 全てフラグがたっているか
    If (From r In ba Where r Select r).Count() = ba.Count Then
     Console.WriteLine("全てTrue")
    End If
    
    

     

    追記:trapemiyaさんの返信内容と同様です

    • 編集済み honefai 2010年12月15日 1:23 送信後気づく(汗
    2010年12月15日 1:21
  • ご回答、ありがとうございます!

    なるほど、便利そうですが使用するにはFrameworkのバージョンが3.5以上と

    なっているようです。

    残念ながら、今回の開発には2までで開発しなければならず他の方法があれば

    うれしいです><

     

    理想の形:

    ' マッチングフラグの定義
    <StructLayout(LayoutKind.Explicit)> _
    Structure DoubleWord
      <FieldOffset(0)> Public Value As System.UInt32
    
      <FieldOffset(0)> Public f_a As System.Byte
      <FieldOffset(1)> Public f_b As System.Byte
      <FieldOffset(2)> Public f_c As System.Byte
      <FieldOffset(3)> Public f_d As System.Byte
    End Structure
    
    Dim MatchFlag As New DoubleWord()
    
    MatchFlag.f_a = 0
    MatchFlag.f_b = 1
    MatchFlag.f_c = 0
    MatchFlag.f_d = 1
    
    ' 1つ以上
    If (MatchFlag.Value > 0) Then
        ’ 処理
    End If
    
    ' フラグ初期化
    MatchFlag.Value = 0
    

     

    これは、共用体を使った例ですがメモリ範囲をByteでしか設定できまぜん。

     

    というか、こういう思想・設計じたいが.Net的ではないのかもしれませんね。。

    2010年12月15日 1:54
  • いつもご回答ありがとうございます!

     

    LINQを使った事がなかったので、こうやってサンプルソースを提示

    して頂けると、とても解りやすくてありがたいです。

    お気遣いありがとうございます!

    2010年12月15日 1:55
  • 単純なビット演算ではフラグの数が足りないのですか?

     列挙体をビット・フィールドとして取り扱うには?[C#、VB] - @IT
     http://www.atmarkit.co.jp/fdotnet/dotnettips/1052enumflags/enumflags.html

    # .NET Framework 4 の HasFlags は便利そうですね。(余談ですが)
    2010年12月15日 4:36
  • フラグを管理するクラスを作成して、好きなプロパティ、例えば「全てOFFかどうかをBooleanで返す」などを

    実装する方法はどうでしょうか。

    以下簡単にサンプルを作ってみました。必要な機能はプロパティやメソッドとして追加実装できます。

    ■使い方

    ' フラグ数を9個でインスタンスを作成
    Dim fa As New FlgArray(9)
    ' フラグ数を13個で全てONの状態でインスタンスを作成
    Dim fa2 As New FlgArray(13, True)
    
    ' フラグの0番目と7番目を立てる
    fa(0) = True
    fa(7) = True
    ' フラグの6番目の状態を取得する
    Dim flg6 As Boolean = fa(6)
    ' フラグ全てをリセットする
    fa.AllTo = False
    ' フラグが全てONかどうか
    If fa.IsAllOn Then
      Console.WriteLine("全てON")
    End If
    ' フラグが全てOFFかどうか
    If fa.IsAllOff Then
      Console.WriteLine("全てOFF")
    End If
    ' フラグがひとつでもONかどうか
    If fa.IsAnyOn Then
      Console.WriteLine("ひとつでもON")
    End If
    
    

    ■クラス本体

    ''' <summary>
    ''' フラグ管理クラス
    ''' </summary>
    ''' <remarks></remarks>
    Public Class FlgArray
      ' メンバ
      Private _flgs As ULong
      Private _AllOn As ULong = 0UL
      ' コンストラクタ
      Public Sub New(ByVal length As Integer)
        If length > 64 Then
          Throw New Exception("length over.")
        End If
        For i As Integer = 0 To length - 1
          _AllOn = _AllOn Or 1UL << i
        Next
      End Sub
      ' コンストラクタ(初期値指定あり)
      Public Sub New(ByVal length As Integer, ByVal value As Boolean)
        If length > 64 Then
          Throw New Exception("length over.")
        End If
        For i As Integer = 0 To length - 1
          _AllOn = _AllOn Or 1UL << i
        Next
        AllTo = value
      End Sub
      ' フラグの状態を取得・設定する
      Default Public Property Flg(ByVal i As Integer) As Boolean
        Get
          If (_flgs And 1UL << i) = 1UL << i Then
            Return True
          End If
          Return False
        End Get
        Set(ByVal value As Boolean)
          If value Then
            _flgs = _flgs Or 1UL << i
          Else
            _flgs = _flgs And (_AllOn Xor 1UL << i)
          End If
        End Set
      End Property
      ' フラグ全ての値を設定する
      Public WriteOnly Property AllTo() As Boolean
        Set(ByVal value As Boolean)
          If value Then
            _flgs = _AllOn
          Else
            _flgs = 0UL
          End If
        End Set
      End Property
      ' 全てONかどうか
      Public ReadOnly Property IsAllOn() As Boolean
        Get
          If _flgs = _AllOn Then
            Return True
          End If
          Return False
        End Get
      End Property
      ' 全てOFFかどうか
      Public ReadOnly Property IsAllOff() As Boolean
        Get
          If _flgs = 0UL Then
            Return True
          End If
          Return False
        End Get
      End Property
      ' ひとつでもONかどうか
      Public ReadOnly Property IsAnyOn() As Boolean
        Get
          If _flgs > 0UL Then
            Return True
          End If
          Return False
        End Get
      End Property
    End Class
    
    

    • 回答としてマーク segment 2010年12月15日 6:22
    2010年12月15日 5:18
  • ご回答、ありがとうございます!

     

    すごいですね、こんな使い方があったのですね。

    しかし、今回のフラグ数から考えると

    おそらくソースが煩雑になるのでこの方法は見送りたいと思います。

     

    ですが、勉強になりました、ありがとうございます!

    2010年12月15日 6:21
  • ありがとうございます!

    ビットシフトが使えるなんてことを、すっかり見失っていました。。。

     

    今回は、提示して頂いたクラスを拡張して、使わせて頂きたいと思います。

     

    ありがとうございました!

     

    というか、みなさん複数フラグの扱いはどのようにされているのでしょうね。

    まぁ、普通にシコシコ書いていけばいいのでしょうが。。

    if(a=a and b=b and c=c and........)

    うーん、フラグクリアを考えると頭が痛くなります

    2010年12月15日 6:26
  • こんなのもどうでしょうか(^^;
    (細かいところは自由に作るって事で)

    Module Module1

        Sub Main()

            Dim FL As New Flags(9)

            'OFFがあるかを探す
            If IsNothing(FL.SearchList(False)) Then
                Console.WriteLine("OFFなし")
            Else
                Console.WriteLine("OFFあり")
            End If

            FL(5).Value = False

            'OFFがあるかを探す
            If IsNothing(FL.SearchList(False)) Then
                Console.WriteLine("OFFなし")
            Else
                Console.WriteLine("OFFあり")
            End If


            Console.ReadLine()

        End Sub

    End Module

    Public Class Flag

        Property Value As Boolean
            Set(ByVal value As Boolean)
                _Value = value
            End Set
            Get
                Return _Value
            End Get
        End Property
        Dim _Value As Boolean

        Public Sub New(ByVal value As Boolean)
            _Value = value
        End Sub
    End Class

    Public Class Flags
        Inherits List(Of Flag)

        Const _ON As Boolean = True
        Const _OFF As Boolean = False

        Public Sub New(ByVal NumberOfFLag As Integer)
            For i As Integer = 0 To NumberOfFLag
                Add(New Flag(_ON))
            Next
        End Sub

        Public Function SearchList(ByVal value As Boolean) As Flag
            Return Me.Find(Function(o) o.Value = value)
        End Function

        Public Sub SetAllValue(ByVal value As Boolean)
            For Each x As Flag In Me
                x.Value = value
            Next
        End Sub

    End Class


    K.Oumi
    2010年12月16日 2:53