ホーム

OFF-SOFT.net

OFF-SOFT.net

ウェブやソフトウェアに関するサポート&情報サイトです。サイト構築からソフトウェアの作成、利用まであなたの助けになるかも・・・・しれません。たぶん・・。

Qt (B1) QListのソート

公開日| 2009年06月15日 | 1つのコメントがあります。
概要 :
 Qt(1)-(10)までで、Qtの特徴を一通り説明してきました。(まだ、脱稿分があるかもしれませんが、随時、投稿されますのでご了承ください。) これから、Qt B シリーズでは、基本的なクラスの使い方を記載していきます。これらの記事は、Qtの初心者向けの記事です。

 今回のテーマは、QListのソートについてです。QtAlgorithmsのqSortを使って簡単にソートを行う例を簡単に説明してます。
Qtでは、複数の画面情報を取り出すとき、ほとんどQListのテンプレートを用いていると思います。
そのQListの中が、使う側の意図に沿って、並んでいるとはいえません。そんな時、QListの中を検索したり、順番を並べ替えたりしなければならないことがあります。
今回は、昇順、降順で、QListを並べ替えてみます。
ここで使用したサンプルソースコード:

簡単なint情報のソート
最も単純なQList<int>を使ってソートをしてみます。

[sample.cpp]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <QtGui>
#include <iostream>
 
using namespace std; 
 
//	dump list
void outputList(const char *title,QList<int> &plist )
{
	int ni;
	std::cout <<  title << std::endl;
	for(ni=0;ni<plist.count();ni++){
		std::cout << "[" <<  ni  << "]="<<  plist.at(ni) << std::endl;
	}
}
 
//	main
int main(int /* argc */, char* /*argv[]*/ )
{
	QList<int> olist;
	olist.append(1);
	olist.append(3);
	olist.append(2);
 
	outputList("Not Sort",olist);
 
	//	sort
	qSort(olist);
	outputList("Sorted",olist);
 
	return 0;
}

6行目から14行目までのoutputListは、コンソールへQList情報を出力する関数です。
実際にソートを実行しているのは、27行目です。
qSortへ、QListを渡しているだけです。

上記をコンパイルして、実行してみましょう。
上記は、コンソールアプリなので、以下のようにコンパイルします。
関連記事 : Qt(1)コンソールアプリでQtのパス制御を調べる

> qmake -project
> qmake "CONFIG+=console"
> nmake

実行結果は、以下のとおりです。

> debug\sort1.exe
Not Sort
[0]=1
[1]=3
[2]=2
Sorted
[0]=1
[1]=2
[2]=3

きれいに昇順にソートされたことがわかります。

さて、個別のクラスを使ったQListのソートはどのようにすれば良いでしょうか?

独自クラスのQListのソート
例えば、以下のような名前情報を持つクラスを作ったとしましょう。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//	class for test
class MyName
{
public :
 
	MyName(const QString name) { m_sname=name; };
	MyName(const MyName &other) { *this=other; };
 
	QString WhatYourName() const  { return m_sname; };
 
	MyName & operator =(const MyName &other) { m_sname=other.m_sname;return *this; };
 
private:
	QString m_sname;
};

メソッドは、名前を問い合わせる"WhatYourName"ぐらいです。簡単なクラスです。
(QListを使用するためのコピーコンストラクタが必要なので作っています。)

このクラスでQListを作成して、ソートを実施してみましょう。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//	main
int main(int /* argc */, char* /*argv[]*/ )
{
	QList<MyName> olist;
	MyName my1("test");
	MyName my2("taro");
	MyName my3("hanako");
	olist.append(my1);
	olist.append(my2);
	olist.append(my3);
 
	outputList("Not Sort",olist);
 
	//	sort
	qSort(olist);
	outputList("Sorted",olist);
 
	return 0;
}

こんな感じで、"test","taro","hanako"の順番でQListに入っているものをソートして、出力しています。

これをコンパイルするとエラーが出力されます。
   qalgorithms.h(178) : error C2678:

これは、qSortで必要としているメソッドが、MyNameに存在しないエラーです。
qSortを上記のような使い方をする場合、MyNameに operator < をオーバーロードしなければなりません。
つまり、qSortは、各クラスの operator < を起動して、ソートを行っているということです。

では、早速、 operator < を追加してみましょう。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//	class for test
class MyName
{
public :
 
	MyName(const QString name) { m_sname=name; };
	MyName(const MyName &other) { *this=other; };
 
	QString WhatYourName() const  { return m_sname; };
 
	MyName & operator =(const MyName &other) { m_sname=other.m_sname;return *this; };
	bool   operator <(const MyName &other) const { return (m_sname<other.WhatYourName()); };
 
private:
	QString m_sname;
};

12行目に operator < が追加されました。
これをコンパイルすると正常にコンパイルできますので、続けて実行してみましょう。

> debug\sort2.exe
Not Sort
[0]=test
[1]=taro
[2]=hanako
Sorted
[0]=hanako
[1]=taro
[2]=test

うまく、名前の昇順でソートできました。

次は、降順でソートしてみましょう。

独自クラスのQListのソート(降順)
降順でソートさせるには、qSortに降順でソートするようにパラメータで指定します。
いままで、以下のようにqSortへのパラメータを指定していました。

1
	qSort(olist);

降順にするために、以下のようにqSortへのパラメータを指定します。

1
	qSort(olist.begin(),olist.end(),qGreater<MyName>());

第1,第2引数は、olistのソート範囲です。第3引数が、降順で実施するように指定したものです。
qGreaterというテンプレート関数が用意されていて、そこにクラスを指定するだけです。

もともと、昇順の場合も、以下のように指定するとことろをパラメータを略したにすぎません。

1
	qSort(olist.begin(),olist.end(),qLess<MyName>());

qGreaterが降順、qLessが昇順というわけです。
では、以下のようにmainルーチンを変更して、実行してみましょう。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//	main
int main(int /* argc */, char* /*argv[]*/ )
{
	QList<MyName> olist;
	MyName my1("test");
	MyName my2("taro");
	MyName my3("hanako");
	olist.append(my1);
	olist.append(my2);
	olist.append(my3);
 
	outputList("Not Sort",olist);
 
	//	sort - qLess
	qSort(olist.begin(),olist.end(),qLess<MyName>());
	outputList("Sorted(qLess)",olist);
 
 
	//	sort - qGreater
	qSort(olist.begin(),olist.end(),qGreater<MyName>());
	outputList("Sorted(qGreater)",olist);
 
	return 0;
}

実行結果は、以下のとおりです。

> debug\sort4.exe
Not Sort
[0]=test
[1]=taro
[2]=hanako
Sorted(qLess)
[0]=hanako
[1]=taro
[2]=test
Sorted(qGreater)
[0]=test
[1]=taro
[2]=hanako

次は、QListにオブジェクトのアドレスを設定する場合のソートは、どうすれば良いでしょうか?

オブジェクトアドレスのQListのソート
以下のようにアドレスをQListで管理している場合、ソートはどうなるでしょう。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//	main
int main(int /* argc */, char* /*argv[]*/ )
{
	QList<MyName*> olist;
	MyName *my1=new MyName("test");
	MyName *my2=new MyName("taro");
	MyName *my3=new MyName("hanako");
	olist.append(my1);
	olist.append(my2);
	olist.append(my3);
 
	outputList("Not Sort",olist);
 
	//	sort - origin method
	qSort(olist);
	outputList("Sorted",olist);
 
	qDeleteAll(olist);
	olist.clear();
 
	return 0;
}

MyNameというクラスは、以前のサンプルと全く同じです。QListがアドレス管理になっただけです。

18行目は、QListでアドレス管理をする場合、自力でdeleteしなければなりません。それを実施してくれる関数が、qDeleteAllです。
自力でdeleteしても、数stepではありますが、知っておくと便利です。

これをコンパイルして実行してみると以下のような結果になります。

> debug\sort3.exe
Not Sort
[0]=test
[1]=taro
[2]=hanako
Sorted
[0]=test
[1]=taro
[2]=hanako

何も変化していません。オブジェクトのアドレスを比較しているに過ぎないので、当然な結果です。
求めているオブジェクトの内容の比較を行うには、その比較する関数を用意してあげなければなりません。

以下は、その例として、MyNameクラスに比較するstaticなメソッドを用意しました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//	class for test
class MyName
{
public :
 
	MyName(const QString name) { m_sname=name; };
	MyName(const MyName &other) { *this=other; };
 
	QString WhatYourName() const  { return m_sname; };
 
	MyName & operator =(const MyName &other) { m_sname=other.m_sname;return *this; };
	bool   operator<(const MyName &other) const { return (m_sname<other.WhatYourName()); };
 
	static bool   compare(const MyName *d1,const MyName *d2){ return (d1->WhatYourName()<d2->WhatYourName()); };
 
private:
	QString m_sname;
};

14行目が、そのstaticなメソッドです。
このメソッドの引数は、MyNameの比較対象となるアドレス2つです。その比較結果をboolで出力しています。

このあたりは、Cの標準関数qsortの扱い方と同じですね。

また、このメソッドを使って比較するようにqSortへパラメータを指定します。

1
	qSort(olist.begin(),olist.end(),MyName::compare);

この指定の仕方は、先の降順の指定と同じように指定します。最後の降順でした関数でなく、MyNameで用意したcompareを指定します。

これを、コンパイルして、実行してみましょう。

> debug\sort3_2.exe
Not Sort
[0]=test
[1]=taro
[2]=hanako
Sorted
[0]=hanako
[1]=taro
[2]=test

うまくソートできました。
ここでは、MyNameで、staticなメソッドを用意しましたが、同一の引数で、通常のCの関数を用意してあげても同じようにソートできます。

さて、最後にqUpperBound,qLowerBoundを少しやってみましょう。

QListの中で、位置を見つける
qUpperBound,qLowerBoundは、使い方によっては、非常に便利な関数ですので、簡単に説明してみます。
qUpperBound,qLowerBoundは、それぞれ、ソートされたQListの中で、指定された情報の最後、最初のiteratorを出力してくれるものです。

簡単な例で説明します。以下のような名前を持つQList<MyName>があるとしましょう。
そこで、"test"という名前を持つ全てのアイテムを取り出したいときなどに、qUpperBound,qLowerBoundは、威力を発揮します。

QList[0]=hanako
QList[1]=taro
QList[2]=test
QList[3]=test
QList[4]=test
QList[5]=test1

1
2
3
4
	MyName my_check("test");
 
	QList<MyName>::iterator ipos1=qUpperBound(olist.begin(),olist.end(),my_check);
	QList<MyName>::iterator ipos2=qLowerBound(olist.begin(),olist.end(),my_check);

このように、qUpperBound,qLowerBoundを使用します。my_checkは、比較のためのオブジェクトです。
ipos1には、QList[5]=test1
ipos2には、QList[2]=test
が設定されます。

つまり、qLowerBoundは、"test"という名前をもつ先頭のオブジェクトQList[2]を捕らえます。
また、qUpperBoundは、"test"という名前をもつ最後尾+1のオブジェクトQList[5]を捕らえます。
最後尾は、QList[4]ですが、+1の位置を取り出すことに注意します。

以下は、ソートの有無で、どのように検索されるかをテストしたものです。
当然、昇順にソートされていない場合の動作は、保障されません。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
//	main
int main(int /* argc */, char* /*argv[]*/ )
{
	QList<MyName>::iterator ipos;
	QList<MyName> olist;
	MyName my1("test",1);
	MyName my12("test",2);
	MyName my13("test",3);
	MyName my2("taro",4);
	MyName my3("hanako",5);
	MyName my14("test1",6);
	olist.append(my1);
	olist.append(my12);
	olist.append(my13);
	olist.append(my2);
	olist.append(my3);
	olist.append(my14);
 
 
	MyName my_check("test",-1);
 
	outputList("Not Sort",olist);
 
	ipos=qUpperBound(olist.begin(),olist.end(),my_check);
	outputItem("[Not Sort(Upper)]",ipos,olist.end());
 
	ipos=qLowerBound(olist.begin(),olist.end(),my_check);
	outputItem("[Not Sort(Lower)]",ipos,olist.end());
 
	//	sort - qLess
	qSort(olist.begin(),olist.end(),qLess<MyName>());
	outputList("Sorted(qLess)",olist);
 
	ipos=qUpperBound(olist.begin(),olist.end(),my_check);
	outputItem("[Sorted(qLess)(Upper)]",ipos,olist.end());
 
	ipos=qLowerBound(olist.begin(),olist.end(),my_check);
	outputItem("[Sorted(qLess)(Lower)]",ipos,olist.end());
 
	//	sort - qGreater
	qSort(olist.begin(),olist.end(),qGreater<MyName>());
 
	outputList("Sorted(qGreater)",olist);
 
	ipos=qUpperBound(olist.begin(),olist.end(),my_check);
	outputItem("[Sorted(qGreater)(Upper)]",ipos,olist.end());
 
	ipos=qLowerBound(olist.begin(),olist.end(),my_check);
	outputItem("[Sorted(qGreater)(Lower)]",ipos,olist.end());
 
	return 0;
}

上記をコンパイルして、実行した結果は、以下のとおりです。

> debug\sort5.exe
Not Sort
[0]=test(1)
[1]=test(2)
[2]=test(3)
[3]=taro(4)
[4]=hanako(5)
[5]=test1(6)
[Not Sort(Upper)]test1(6)
[Not Sort(Lower)]test1(6)
Sorted(qLess)
[0]=hanako(5)
[1]=taro(4)
[2]=test(1)
[3]=test(3)
[4]=test(2)
[5]=test1(6)
[Sorted(qLess)(Upper)]test1(6)
[Sorted(qLess)(Lower)]test(1)
Sorted(qGreater)
[0]=test1(6)
[1]=test(2)
[2]=test(3)
[3]=test(1)
[4]=taro(4)
[5]=hanako(5)
[Sorted(qGreater)(Upper)]** last ** previous item --> hanako(5)
[Sorted(qGreater)(Lower)]test1(6)

今回は、基本的なソート処理を行いました。 QtAlgorithmsでは、その他にも検索するqFindや、コピーするqCopyなど便利なものがあります。
QtAlgorithmsは、基本的にiteratorで管理しているテンプレートクラスであれば動作します。
(QVectorから、QListへのコピーなどもできます。)

自分で作成することもできますが、知っておくと便利です。

何かお気づきの点があれば、コメントいただければ幸いです。

もっと、Qt関連について詳しく知りたい方は、以下の本なども良いと思います。
Qtに関する日本語の本が少ないですね。「入門書」は、さすがに、このページを読まれるくらいの方は不要だと思います。
やっぱり、本+ネット+試してみる!!の3本柱でやっていく以外にないように思います。


コメント

1つのコメントがあります。 “Qt (B1) QListのソート”


  1. oscar
    2014年02月03日 @ 11:26:06

    buena informacion
    me ayudo en mucho

コメントをどうぞ







  • はてなブックマークへ追加する
  • Facebookでシェアする
  • twitter でつぶやく
  • Google Plusでシェアする
  • Pocketでシェアする
ページトップへ