LoginSignup
1
2

More than 3 years have passed since last update.

ラムダ式とMakefileマクロ

Last updated at Posted at 2020-05-14

Makefile のマクロでラムダ式っぽいことができることに気付いたのでメモしておきます。

ループ

Node.js でカレントディレクトリのファイルを列挙する例です。

test1.js
const fs = require("fs");
for (let f of fs.readdirSync(".")) console.log(f);

これと同じことを Makefile で書きます。

test1.mk
all:
    for f in ./*; do echo $$f; done

※ Makefile の変数と区別するため、$$$ にエスケープする必要があります。

ラムダ式とマクロ

Node.js を forEach で書き直します。

test2.js
const fs = require("fs");
fs.readdirSync(".").forEach(f => console.log(f));

これと同じようなことを Makefile のマクロで書きます。

test2.mk
define forEach
for $1 in $2; do $3; done
endef

all:
    $(call forEach,f,./*,echo $$f)
実行結果
$ make -f test2.mk
for f in ./*; do echo $f; done
./test1.js
./test1.mk
(略)

@ で抑制していないためマクロの展開結果が確認できます。

ただのループなのであまり嬉しさはありませんが、もう少し複雑な処理に応用すると便利かもしれません。

なお、JavaScript の forEach はコールバックに引数を3つ渡す仕様のため、ポイントフリースタイルで書くと実行結果が変わります。

test2a.js
const fs = require("fs");
fs.readdirSync(".").forEach(console.log);
実行結果
test1.js 0 [ 'test1.js', 'test1.mk', (略) ]
test1.mk 1 [ 'test1.js', 'test1.mk', (略) ]
(略)

フィルター

Node.js の API ではワイルドカードが使えないため、フィルターを挟む必要があります。

test3.js
const fs = require("fs");
fs.readdirSync(".").filter(f => f.endsWith(".js")).forEach(f => console.log(f));

Makefile は中に書いているのはシェルのコマンドなのでワイルドカードが使えますが、敢えてフィルターを書いてみます。

test3.mk
define forEach
for $1 in $2; do $3; done
endef
define filter_
if $1; then $2; fi
endef
define endsWith
[ "`echo $1 | sed 's/.*$2$$/ok/'`" = "ok" ]
endef

all:
    $(call forEach,f,./*,$(call filter_,$(call endsWith,$$f,\.js),echo $$f))

※ 組み込み関数 filter と名前が被るため、マクロは filter_ にしています。

実行結果
$ make -f test3.mk
for f in ./*; do if [ "`echo $f | sed 's/.*\.js$/ok/'`" = "ok" ]; then echo $f; fi; done
./test1.js
(略)

実用性はともかく、ちょっと面白いと思いました。

参考

マクロの使い方は以下の記事を参考にしました。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2