[[20210301234712]] 『配列が初期化済みかの判定関数』(中途B) ページの最後に飛ぶ

[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]

 

『配列が初期化済みかの判定関数』(中途B)

 半分くらい「趣味」みたいなものなんですが、

 配列が初期化済みかの判定を行う関数を作りたいと考えています。
 下記[Test]の
 各Debug.Printの第1引数にある[HasDim]が作ろうとしているFunction名で、
 各Debug.Printの第2引数が、期待する[HasDim]の戻り値です。

    Sub Test()
        Dim v() As Variant, v2 As Variant
        Debug.Print HasDim(v), False, "Dim v()"
        Debug.Print HasDim(v2), False, "Dim v2"
        ReDim v(0)
        ReDim v2(0)
        Debug.Print HasDim(v), True, "ReDim v(0)"
        Debug.Print HasDim(v2), True, "ReDim v2(0)"
        v2 = Split("1 2 3")
        Debug.Print HasDim(v2), True, "Split(""1 2 3"")"
        v2 = Split("123")
        Debug.Print HasDim(v2), True, "Split(""123"")"
        v2 = Split("")
        Debug.Print HasDim(v2), False, "Split("""")"
        v2 = Array()
        Debug.Print HasDim(v2), False, "Array()"
        v2 = Array(1)
        Debug.Print HasDim(v2), True, "Array(1)"
    End Sub

    Function HasDim(var As Variant) As Boolean
        If IsArray(var) Then
            If VarType(var) = vbArray + vbVariant Then
                Dim tmp() As Variant
                tmp = var
                HasDim = (Not tmp) <> -1
            Else
                HasDim = (LBound(var) <= UBound(var))
            End If
        End If
    End Function

 ↓実行結果

 False         False         Dim v()
 False         False         Dim v2
 True          True          ReDim v(0)
 True          True          ReDim v2(0)
 True          True          Split("1 2 3")
 True          True          Split("123")
 False         False         Split("")
 True          False         Array()         <--ココが惜しい
 True          True          Array(1)

 「Array()かどうか」を判定する場面なんて、実際にはあり得ないんですけど、
 これはもう気持ちの問題でして。

 どうにかしてこの部分にも対応出来ないものでしょうか?
 (あるいはもっとスマートな方法があればお知恵を拝借したく...)

 または「他に想定すべきパターンがあるでしょ」的なアドバイスもありがたいです。
 宜しくお願いします。

< 使用 Excel:Excel2010、使用 OS:Windows7 >


 初期化とこの変数は配列宣言しているだけを勘違いしてませんかね?
 isarray(配列)
 ??
 後は自分でいろいろ研究してください。
 尚、ReDim は、初期化です。
(BJ) 2021/03/02(火) 01:44

 感想です
 この関数使いみちありますか?
 こういう処理が必要無いようにコードを組むばいいだけだし、
 それはできないことではないし、それができてないコードはバグが潜んでいると思います。

 回答です。
 配列に代入してみて、エラーなら初期化されていない という考えで良くないですか?

    Function HasDim(ByVal var As Variant) As Boolean
        If Not ((VarType(var) And vbArray) = vbArray) Then Exit Function
        On Error Resume Next
           var(UBound(var)) = -1
           HasDim = Err = 0
        On Error GoTo 0
    End Function

    代入したときに、上書きされないように、ByValで引数を取る必要があります。
    実際上は、何を代入するかが問題になるので、
    数値型でない Object型の配列とかだとこの方法ではまずいですね

 余談です。
   HasDim = (Not tmp) <> -1
   って何をやってるのは私は理解できないのですが...
(´・ω・`) 2021/03/02(火) 10:04

 (カブっちゃいましたがそのまま投下します) 
 有効な配列として定義済みかどうか... の判定ってことですよね?

 Array()への対応だけ考えれば、
 ご自身でもVariant型配列以外の場合の対応で、添字順序の逆走判定を入れておられますし、
 これをVariant型配列の方にも流用しちゃえばいいんじゃないでしょうか?

 > HasDim = (Not tmp) <> -1
 に続けて、
 If HasDim Then HasDim = (LBound(tmp) <= UBound(tmp))
 という感じで。

 ただ、仰る通り、実務上は不要だと思います。
 Split("")による添字の逆走は実務でも出くわしそうですが、
 Array関数を引数なしで使用する事はあり得ないですからね。

 それよか、
 Variant型以外の配列でReDim前の場合、
 このままではLBound、UBoundがエラーになるので、そっちの対策の方が必要ではないかと。

    Function HasDim(var As Variant) As Boolean
        If IsArray(var) Then
            Dim tmp() As Variant
            If VarType(var) = vbArray + vbVariant Then
                tmp = var
                HasDim = (Not tmp) <> -1
                If HasDim Then HasDim = (LBound(tmp) <= UBound(tmp)) '←追加(不要とは思うけど)
            Else
                On Error Resume Next '←追加
                HasDim = (LBound(var) <= UBound(var))
                On Error GoTo 0 '←追加
            End If
        End If
    End Function

    Sub Test2()
        Dim v() As Variant, v2 As Variant
        Debug.Print HasDim(v), False, "Dim v()"
        Debug.Print HasDim(v2), False, "Dim v2"
        ReDim v(0)
        ReDim v2(0)
        Debug.Print HasDim(v), True, "ReDim v(0)"
        Debug.Print HasDim(v2), True, "ReDim v2(0)"
        v2 = Split("1 2 3")
        Debug.Print HasDim(v2), True, "Split(""1 2 3"")"
        v2 = Split("123")
        Debug.Print HasDim(v2), True, "Split(""123"")"
        v2 = Split("")
        Debug.Print HasDim(v2), False, "Split("""")"
        v2 = Array()
        Debug.Print HasDim(v2), False, "Array()"
        v2 = Array(1)
        Debug.Print HasDim(v2), True, "Array(1)"

        '追試
        Dim s() As Single
        Debug.Print HasDim(s), False, "Dim s()"
        ReDim s(0)
        Debug.Print HasDim(s), True, "ReDim s(0)"
    End Sub

 ここで On Error Resume Next に頼っちゃうのは
 「手抜き工事」と思われるかも知れませんが... (他にいい手が思い付かないデス^^;)

(白茶) 2021/03/02(火) 10:07


 お返事ありがとうございます。
 使い道のない関数にお付き合い頂き恐縮です。

 私もHasDim自体をどこかで使おうというつもりは無く、
 配列を操作する処理に入る前に、それがちゃんと配列になってるかを判定する方法は、
 その場面場面に応じた最適な方法が様々とあると思ってます。

 それをある程度パターン化した場合、何か見えてくるものがあるかしら?
 という、まあ言ってしまえば実験みたいなものでした。

 だからという訳でもないですが「エラーを発生させてみる」という手法が
 念頭から抜けてしまっていた様です。
 そして結果、白茶さんのご指摘にも気付けずという orz

 実のないことを安易に質問するものではありませんね。
 皆さん、アドバイスありがとうございました!

(中途B) 2021/03/02(火) 12:47


(何となくチョッピリ居たたまれない感じで終わってしまった感があって自分的にモヤモヤするので足しときます^^;)

 過去にも何度かお見掛けしたHNだと記憶にありましたもので、
 今回の質問に至った経緯も勝手ながら想像を膨らませて回答させて頂きました^^;
 それだけ私にとっては「面白い質問される方だなー」って印象に残ってたんでしょうね。
 相変わらず基礎研究的な事がお好きな様で^^

 >実のないこと
 とは仰いますけど、
 たぶんそういう部分に目が行く人の方が「気付き」が多くて、より楽しめるんじゃないかと思ってます。
 羨ましいくらいです。

 今思ってみれば、今回の発端って、
 > Dim v() As Variant, v2 As Variant
 恐らくココじゃないかと勝手に予想してます。

 配列用の変数を宣言する時に()付きVariantにするか()無しVariantにするか。
 「配列として使うつもりなんだから最初から配列にするでしょ」という考え方も出来ますし、
 「どうせ後からReDimするんだから無しでいいじゃん」という考え方も出来ます。

 あるいは、配列を返す関数を作る時に
 Function func(arg) As Variant
 と定義するか
 Function func(arg) As Variant()
 と定義するか

 とか、
 引数定義で()付き宣言の配列しか受け付けない様に制限するか、
 一旦受け入れちゃって内部で処理や分岐を考えるか。 とか。

 それによって後続の処理で、内容のある配列かどうかの判定方法も変わってくる場合がありますね。
 たいてい()無しVariantの方が融通が利くので便利な印象がありますが、
 それ故に後から面倒事が...みたいな場合もあったりなかったりして、
 私も、作った後になってから「どっちが良かったんだろ」って迷う時があります。

 結局は仰る通り、
 >その場面場面に応じた最適な方法
 を都度考えるしかないんですよね。

 ま、要は、共感できる部分のあるトピックスでしたよ。ってことです。

(白茶) 2021/03/02(火) 20:21


コメント返信:

[ 一覧(最新更新順) ]


YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki. Modified by kazu.