DOBON.NET DOBON.NETプログラミング掲示板過去ログ

System.Collectionの使い方について。

環境/言語:[.NET Frameworkのバージョン(1.1)]
分類:[.NET]

はじめまして。たいちうと申します。
VB .NET 2003のCollectionについての質問です。

単語の出現頻度を求めるためのサンプルを作ってみたのですが、
SortedListやHashtableの使い方にかなり無駄が多いと思います。


Private Structure MyData
    Public key As String
    Public value As Integer
End Structure

Sub Main()
    Dim words As String() = Split("This is a pen. That is a book.")

    Dim hash As New Hashtable
    For i As Integer = 0 To words.Length - 1
        hash(words(i)) = CInt(hash(words(i))) + 1
    Next

    Dim list As New SortedList
    Dim seq As Integer = 0
    For Each key As String In hash.Keys
        Dim data As New MyData
        data.key = key
        data.value = hash(key)
        list.Add(hash(key) & ": " & seq, data)
        seq = seq + 1
    Next

    For i As Integer = list.Count - 1 To 0 Step -1
        Dim data As MyData = list.GetByIndex(i)
        Console.WriteLine(data.value & " : " & data.key)
    Next i

    ' quit
    Console.WriteLine("End.")
    Console.Read()
End Sub

要するに、

"This is a pen. That is a book."
↓
2 : a
2 : is
1 : book.
1 : That
1 : pen.
1 : This

という変換ができれば良いのですが、これしか思いつきませんでした。
もっと良い方法を教えていただけないでしょうか。

HashtableやSortedListの使用には拘りませんが、
シンプルな方法がありがたいです。効率面も特に重視はしていません。
■No26079に返信(たいちうさんの記事)
> Dim words As String() = Split("This is a pen. That is a book.")
> 
> Dim hash As New Hashtable
> For i As Integer = 0 To words.Length - 1
>       hash(words(i)) = CInt(hash(words(i))) + 1
> Next

これは、それぞれの単語の出現回数を数えているのですよね。
処理方法としては、単純で良いと思います。ただし、この手法だと、

 ・空白が2つ以上連続した場合、空文字列が単語としてカウントされてしまう。

 ・"government of the people, by the people, for the people." の場合、
  "people"×3ではなく、"people,"×2 と"people."×1 としてカウントされる。

といった問題はあります。


> Dim list As New SortedList
SortedList を使っているのは、並び替えのためでしょうか?

この場合、元の Hashtable に
 hash("the") = 20
 hash("It") = 15
が追加されると、並び順は
 20 : the
 15 : It
 2 : is
 2 : a
 1 : This
 (以下略)
ではなく、
 20 : the
 2 : is
 2 : a
 15 : It
 1 : This
 (以下略)
となってしまいます。


> HashtableやSortedListの使用には拘りませんが、
DataTable/DataView を使うのは如何でしょうか?
これならば、並び順を自由に変更できます。


Dim words() As String = Split("This is a pen. That is a book.")

Dim dt As New DataTable()
dt.CaseSensitive = True   '単語の大文字小文字を区別するなら True
dt.PrimaryKey = New DataColumn() {dt.Columns.Add("word", GetType(String))}
dt.Columns.Add("count", GetType(Integer))

For Each word As String In words
    Dim row As DataRow = dt.Rows.Find(word)
    If row Is Nothing Then
        dt.Rows.Add(New Object() {word, 1})
    Else
        row("count") += 1
    End If
Next

Dim view As New DataView(dt)
view.Sort = "count DESC, word ASC"
Console.WriteLine("出現回数の降順")
For Each row As DataRowView In view
    Console.WriteLine(String.Format("{0} : {1}", row("count"), row("word")))
Next

view.RowFilter = "count>=2"
Console.WriteLine("複数回出現した単語")
For Each row As DataRowView In view
    Console.WriteLine(String.Format("{0} : {1}", row("count"), row("word")))
Next

' quit
Console.WriteLine("End.")
Console.Read()
魔界の仮面弁士 さま、ご回答ありがとうございます。

今回の質問の意図ですが、「単語を出現頻度順に表示する」という程度の
単純な事をするのに、HashtableとSortedListを2段階で使うことに無駄を感じ、
もっと簡単な方法を求めている、ということでした。

ちょっと詳しい人ならば、口を揃えて○○を使えば一発、
というような方法があるのでは、と思っていました。

ご教示いただいたDataTableとDataViewを使う方法は、
今回の目的に対しては、わざわざここまでする必要はないと思い、
手間としては私の方法と大差ないように感じました。

しかし、いろいろと強力な使い道のありそうな方法ですね。
近いうちに使う機会があるでしょう。ありがとうございました。


なお私に「単語の出現頻度」のような課題があるわけではなく、
別のプログラムのデバッグ中にとあるコードの出現頻度を
調べようと、ちょっとしたプログラムを埋め込んだのですが、
それが先ほどのプログラムに近いものです。

ですので、ご指摘いただいたピリオドの問題は存在しないのですが、
並び順が文字列順になることには投稿後に気づきましたので、
PadLeftで対処しました。
解決済み!
■No26082に返信(たいちうさんの記事)
> 今回の質問の意図ですが、「単語を出現頻度順に表示する」という程度の
> 単純な事をするのに、HashtableとSortedListを2段階で使うことに無駄を感じ、
> もっと簡単な方法を求めている、ということでした。

2003 が相手となると、私もそのぐらいしか思いつきませんでした。
2008 だと簡単なのですけれどね…。

Dim Words() As String = Split("This is a pen. That is a book.")

Dim q = From Word In Words _
        Group By Word Into Count() _
        Order By Count Descending, Word Ascending

For Each x In q
    Console.WriteLine(x.Count & " : " & x.Word)
Next
解決済み!
> 2008 だと簡単なのですけれどね…。

たまにしか .NET を使う機会のなかった私には簡単ではないですね。
私が使ったとしても、周りの人は読んでくれないでしょう。
このようにして、先進的な機能拡張から取り残されていくわけでした。

ご教示いただいたサンプルは、2008を使う機会が来るまで
しっかりと寝かしておきます。ありがとうございました。
この掲示板の仕様では解決済みが消えてしまうのですね。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板