ふと思いついたのでdashのevalを使って無名関数的なものを作る。
無名関数の生成
レキシカルスコープは無理なのでただの文字列にする。
$ foge='echo $(($1+$2+$3))'
生成した関数の呼び出し
fcallで呼び出す。
fcall() {
local __LAMBDA_FUNC=$1;
shift
eval "$__LAMBDA_FUNC"
}
$ fcall "$foge" 1 2 3
6
部分適用
shは引数を無制限に渡せるため、部分適用用の関数(papp)と
呼び出し用の関数(fcall)を分けることにする。
これは少し難しいが、無名関数の文字列と引数を受け取って、
以下のような関数に変換する。
- fcall時に与えられた引数の前にpappで渡された引数を割り込ませてsetする。
- pappで渡された引数がevalの際にshellによりいじられないようにエスケープする。
# エスケープ h"og'e -> 'h"og'\''e'
esc() {
local h= v="$1"
while :; do
case "$v"
in *\'*)
eval 'h="$h${v%%'\\\''*}"'
h="$h"\'\\\'\'
eval 'v="${v#*'\\\''}"'
;; *)
RET="'$h$v'"
break
esac
done
}
# 部分適用
papp() {
local RET __FUNC="$1" args= arg
shift
for arg in "$@"; do
esc "$arg"
args="$args $RET"
done
echo 'set -- '"$args"' "$@";'"$__FUNC"
}
$ x="`papp "$foge" 1`"
$ y="`papp "$x" 2`"
$ echo "$x"
set -- '1' "$@";echo $(($1+$2+$3))
$ echo "$y"
set -- '2' "$@";set -- '1' "$@";echo $(($1+$2+$3))
$ fcall "$y" 3
6
付録 : スコープ
レキシカルスコープがなくても例によってカウンタとか作ろうとしなければ
だいたいなんとかなる。
$ a=hello
$ fuga="local a=$a;"'echo "$a" "$1"'
$ hoge='local a=bye; fcall "$1" "$2"'
$ fcall "$hoge" "$fuga" world
hello world