1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

shellとevalとlambda

Last updated at Posted at 2019-09-08

ふと思いついたので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
1
0
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
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?