Sub test()
Dim dic As Scripting.Dictionary
Set dic = CreateObject("Scripting.Dictionary")
Dim c As Long
Dim cMx As Long
Dim st As String
cMx = Range("A" & Rows.Count).End(xlUp).Row
For c = 1 To cMx
st = Range("A" & c).Value
If dic.Exists(st) Then
dic.Item(st) = dic.Item(st) + 1
Else
dic.Add (st), 1
End If
Next
'★疑問1 vKeysとvItmsを動的配列で宣言しても同じ結果になった
'★疑問2 配列のサイズがわからなかったのでredimしていないがエラーが出なかった
'★疑問3 vKeysの型をstring,vItmsの型をlongにしたら『型が一致しません』エラーになる
Dim vKeys() As Variant
Dim vItms() As Variant
Debug.Print TypeName(dic.Keys) '[1]
Debug.Print TypeName(dic.Items) '[2]
vKeys() = dic.Keys
vItms() = dic.Items
For c = LBound(vKeys) To UBound(vKeys)
Range("C1").Offset(c).Value = vKeys(c)
Range("D1").Offset(c).Value = vItms(c)
Next
End Sub
mokoさんの投稿
(投稿ID: 4054)
ヒントのコード、ありがとうございます。
変数にも配列を入れられるとは思っていなかったので、とても驚きました。
配列は静的配列か動的配列かで宣言するものだと思いこんでました。
今回のようにdic.Keysやdic.Itemsで作った配列をそのままいれるのであれば、配列よりも変数で宣言したほうがシンプルでわかりやすいと思いました。
・・・が、疑問1と疑問2の動的配列で宣言してもエラーがでないまま動いたことについては未だに理解できていません。((+_+))
疑問3は理解しました。
dic.Keysもdic.ItemsもTypeNameで型を調べてみたらどちらもVariantでした。だから他の型で宣言するとエラーがでたんですね。
型が違うというエラーがでたらTypeNameで調べて修正していきます。
小川 慶一さんのコメント
(コメントID: 5615)
> dic.Keysもdic.ItemsもTypeNameで型を調べてみたらどちらもVariantでした。
戻り値は、「Variant()」でしたよね。
つまり、戻り値は、「Variant型」でなく、「Variant型の配列」です。こういうところ、しっかり詰めてください。
☆ポイント:
[1]
DictionaryオブジェクトのKeysメソッド、Itemsメソッドの戻り値はVariant型の配列です。
たとえ内部データがInterger型、String型だったとしても、受取人たる左辺の変数はVariant型の配列でなければなりません。
[2]
配列でなく通常のVariant型で受取人を宣言しても動作します。
それは、Variant型は何にでも使える型なので(発展編1第1章参照)、Variant型の配列としても使えるからです。
> 疑問1と疑問2の動的配列で宣言してもエラーがでないまま動いたことについては未だに理解できていません。((+_+))
動的配列がRedimで事前にサイズ指定することなしにいきなり配列を受け取れるほうがプログラマーにとっては取扱いしやすいでしょう。
「ほうが」というのは、「配列を変数が受け取るときは、事前に受け取ろうとする配列のサイズを調べ、そのサイズに合うようにRedimしなくてはならない場合と比べて」という意味です。
という説明でどうでしょうか。
要は、「そういう言語の仕様だ」ということです。(*)
以下では、[1], [2]に注目してください。
(*) 似たようなクレーム(?)でよくあるのが、以下のような関数の挙動についてのものです。
基礎編で対面講座をやると2割くらいの方が疑問に思うのは、「なぜ、Instr関数の戻り値は、常に、調査対象の文字が『左から数えて○文字目』という情報なのだろう。6文字目からさがして9文字目で見つかったのだから、こういう関数は、戻り値は『9』ではなく『3』であるべきではないか?」とか、そういうものです。
でも、Instr関数の戻り値が常に「左から数えて」であってくれたほうが、プログラマーにとっては取扱いが簡単です。
mokoさんも、ある程度経験を積まれたあとなのでそのことは今となってはすぐにピンと来るかと思います。
ニュアンスはそれと同じです。
要は、「そういう言語の仕様だ」ということです。よりはっきり言ってしまえば、「この仕様がしっくりこないのは、理解力ではなく、演習と経験の量の問題だ」ということです。
僕としては、「配列を使った実装をある程度経験して、それでもまだ同じことを疑問に思うようならまた聞いてください。Instr関数の場合と同様、たぶん、ある程度実装経験を積んだら、この質問自体バカバカしいと感じられるようになるでしょう」とお返事したいところ。
> 小川先生
> ヒントのコード、ありがとうございます。
>
> 変数にも配列を入れられるとは思っていなかったので、とても驚きました。
> 配列は静的配列か動的配列かで宣言するものだと思いこんでました。
> 今回のようにdic.Keysやdic.Itemsで作った配列をそのままいれるのであれば、配列よりも変数で宣言したほうがシンプルでわかりやすいと思いました。
> ・・・が、疑問1と疑問2の動的配列で宣言してもエラーがでないまま動いたことについては未だに理解できていません。((+_+))
>
> 疑問3は理解しました。
> dic.Keysもdic.ItemsもTypeNameで型を調べてみたらどちらもVariantでした。だから他の型で宣言するとエラーがでたんですね。
> 型が違うというエラーがでたらTypeNameで調べて修正していきます。