GNU Makeはテキストを「空白区切りの単語の集合」として扱うことが比較的容易だ。組み込み関数*1を使うと簡単な集合演算を簡単に実現できる。
集合を作る
単純に空白区切りで単語を並べれば、GNU Makeではそれが集合となる。ただ組み込み関数を使う都合上、集合の要素には英数文字のみの単語を使ったほうが無難だろう。
実験用に適当な集合を作りたい場合は seq(1) が便利だ。ここでは重複しない30以下の正の整数を集合の要素として扱う。
MAXVAL ?= 30 SET_A := $(shell seq 3 3 $(MAXVAL)) SET_B := $(shell seq 5 5 $(MAXVAL))
3の倍数と5の倍数。どこかで見たことのあるような……。
積集合、共通部分(論理積)
集合Aの中から集合Bにも含まれる要素を取り出したい場合、組み込み関数filterを使う。
$(filter $(SET_B),$(SET_A))
引数の順番に注意。上記の例からAとBを逆にすると「集合Bの中から集合Aにも含まれる要素を取り出す」という意味になる。もっともどちらも答えは変わらない。
和集合(論理和)
集合Aと集合Bの要素を取り出す(但し重複を取り除く)場合は組み込み関数sortを使う。
$(sort $(SET_A) $(SET_B))
引数のAとBの間がコンマではなく空白であることに注意。
組み込み関数sortは各要素に対してシェルスクリプトでいうところの、
sort | uniq
的な処理を行った結果を返す。ただ辞書順にソートするので、各要素が数値の場合に残念なことになる。でもまあ気にしない方向で。
差集合
集合Aから集合Bにも含まれる要素を取り除いた結果を取得したい場合、組み込み関数filter-outを使う。
$(filter-out $(SET_B),$(SET_A))
正確には「集合Aの中から、集合Bに含まれる要素以外を取り出す」という意味になる。
対称差(排他的論理和)
集合Aと集合Bの対称差を求める方法は2通り。
ひとつはAとBの和集合X及び積集合Yを求めた上で、XとYの差集合を求める(XからYを取り除く)。
$(filter-out $(filter $(SET_B),$(SET_A)),$(sort $(SET_A) $(SET_B)))
もうひとつは差集合同士の和集合を求める。
$(sort $(filter-out $(SET_B),$(SET_A)) $(filter-out $(SET_A),$(SET_B)))
どちらの方法も積集合、和集合、差集合の演算の組み合わせだ。もっとエレガントな方法は無いだろうか?
テスト
ここまでの方法が正しそうか、実際にMakefileを書いて確かめた。
# setop_proto.mak - sample of set operations # Ubuntu 8.04.4 LTS (server) GNU Make 3.81-3build1 MAXVAL ?= 30 SET_A := $(shell seq 3 3 $(MAXVAL)) SET_B := $(shell seq 5 5 $(MAXVAL)) .PHONY: all all: @echo 'set A : $(SET_A)' @echo 'set B : $(SET_B)' @echo 'A and B : $(filter $(SET_B),$(SET_A))' @echo 'A or B : $(sort $(SET_A) $(SET_B))' @echo 'A and !B : $(filter-out $(SET_B),$(SET_A))' @echo 'A xor B : $(filter-out $(filter $(SET_B),$(SET_A)),$(sort $(SET_A) $(SET_B)))' @echo 'A xor B : $(sort $(filter-out $(SET_B),$(SET_A)) $(filter-out $(SET_A),$(SET_B)))'
Ubuntu 8.04.4 LTS サーバ版のGNU Make 3.81で実行した結果。
$ make -f setop_proto.mak set A : 3 6 9 12 15 18 21 24 27 30 set B : 5 10 15 20 25 30 A and B : 15 30 A or B : 10 12 15 18 20 21 24 25 27 3 30 5 6 9 A and !B : 3 6 9 12 18 21 24 27 A xor B : 10 12 18 20 21 24 25 27 3 5 6 9 A xor B : 10 12 18 20 21 24 25 27 3 5 6 9 $ _
Windows XP上でMSYS付属のGNU Make 3.81で実行しても同じだった。
ライブラリ化
GNU Makeにはユーザ定義関数(マクロ)の機能があるので、ここまでの集合演算を関数化してライブラリっぽくしてみた。
# setop.mak - set operations library ver.1 # Ubuntu 8.04.4 LTS (server) GNU Make 3.81-3build1 # $(call setop-and,A,B) - A and B define setop-and $(filter $2,$1) endef # $(call setop-or,A,B) - A or B define setop-or $(sort $1 $2) endef # $(call setop-and-not,A,B) - A and !B (relative complement) define setop-and-not $(filter-out $2,$1) endef # $(call setop-xor,A,B) - A xor B define setop-xor1 $(filter-out $(call setop-and,$1,$2),$(call setop-or,$1,$2)) endef define setop-xor2 $(sort $(call setop-and-not,$1,$2) $(call setop-and-not,$2,$1)) endef
組み込み関数にandやorが存在する*2ので、名前がかぶらないように `setop-' というプレフィックスを付けている。また展開時に先頭に余分な空白が付かないように、関数定義の中身にわざとインデントを付けていない。
今回定義した関数はどれも1行で済むこともあり、再帰変数(再帰展開変数)を使ってこんな風に定義することもできる。
# setop.mak - set operations library ver.2 # Ubuntu 8.04.4 LTS (server) GNU Make 3.81-3build1 # $(call setop-and,A,B) - A and B setop-and = $(filter $2,$1) # $(call setop-or,A,B) - A or B setop-or = $(sort $1 $2) # $(call setop-and-not,A,B) - A and !B (relative complement) setop-and-not = $(filter-out $2,$1) # $(call setop-xor,A,B) - A xor B setop-xor1 = $(filter-out $(call setop-and,$1,$2),$(call setop-or,$1,$2)) setop-xor2 = $(sort $(call setop-and-not,$1,$2) $(call setop-and-not,$2,$1))
こちらの方がスッキリした見た目だ。
上記の関数群はsetop.makという独立したMakefileに記述している。使いたいときはsetop.makをインクルードすればよい。
# setop_sample.mak - sample of set operations library # Ubuntu 8.04.4 LTS (server) GNU Make 3.81-3build1 include setop.mak MAXVAL ?= 30 SET_A := $(shell seq 3 3 $(MAXVAL)) SET_B := $(shell seq 5 5 $(MAXVAL)) .PHONY: all all: @echo 'set A : $(SET_A)' @echo 'set B : $(SET_B)' @echo 'A and B : $(call setop-and,$(SET_A),$(SET_B))' @echo 'A or B : $(call setop-or,$(SET_A),$(SET_B))' @echo 'A and !B : $(call setop-and-not,$(SET_A),$(SET_B))' @echo 'A xor B : $(call setop-xor1,$(SET_A),$(SET_B))' @echo 'A xor B : $(call setop-xor2,$(SET_A),$(SET_B))'
多重インクルードとかはどうなっているのか不明。
実行結果はこんな感じ。
$ make -f setop_sample.mak set A : 3 6 9 12 15 18 21 24 27 30 set B : 5 10 15 20 25 30 A and B : 15 30 A or B : 10 12 15 18 20 21 24 25 27 3 30 5 6 9 A and !B : 3 6 9 12 18 21 24 27 A xor B : 10 12 18 20 21 24 25 27 3 5 6 9 A xor B : 10 12 18 20 21 24 25 27 3 5 6 9 $ _
まとめ
いやぁ、GNU Makeって本当に興味深いプログラミング言語ですねぇ。「オレのGNU Makeは宇宙だ」と言わんばかり。
想像がつくように、参考書はコレ。
- 作者: Robert Mecklenburg,矢吹道郎(監訳),菊池彰
- 出版社/メーカー: オライリージャパン
- 発売日: 2005/12/01
- メディア: 大型本
- 購入: 4人 クリック: 115回
- この商品を含むブログ (34件) を見る