(続 ((Rで) 書く (Lisp) インタプリタ))

@igjit

前回のTokyo.Rでこんな話をしました。

https://igjit.github.io/slides/2018/01/tiny_pipe/

言いたかったのは

Rはすごく不思議な言語

演算子

中置演算子

1 + 2

関数呼び出し

sum(1, 2)

実は

R内で起きることすべては関数呼び出しである。

これは

1 + 2

これと等価

`+`(1, 2)

これは

1:10

これと等価

`:`(1, 10)

代入も

x <- 2

関数呼び出し

`<-`(x, 10)

制御構文も

if (x < 0) -x else x

関数呼び出し

`if`(x < 0, -x, x)

!?

遅延評価

Rの関数では

引数は遅延評価される

つまり

引数が使われた場合に初めて評価される

f <- function(a, b) {
    if (a > 0) b
}
f(1, stop("This is an error!"))
# f(1, stop("This is an error!")) でエラー (#1 から) : This is an error!

f(0, stop("This is an error!"))  # 何も起きない

メタプログラミング

quote() は表現式を返す

quote(1 + 2)
# 1 + 2

表現式は

木構造

abstract syntax tree (AST) とも呼ばれる

pryr::ast() で木構造を見ることができる

ast(1 + 2 * 3)
# \- ()
#   \- `+
#   \-  1
#   \- ()
#     \- `*
#     \-  2
#     \-  3 

Lispっぽい

表現式はlistのように扱える

expr <- quote(1 + 2)

as.list(expr)
# [[1]]
# `+`
# 
# [[2]]
# [1] 1
# 
# [[3]]
# [1] 2

expr[[1]]
# `+`

表現式は修正できる

expr <- quote(1 + 2)
expr[[1]] <-  quote(`*`)

expr
# 1 * 2

構文木を読み書きする

メタプログラミング

ところでだいぶ前のTokyo.Rでこんな話をしました。

メタプログラミングを使えばもっと簡単に書けるのでは?

やってみた。

lisprr

https://github.com/igjit/lisprr

インストール

devtools::install_github("igjit/lisprr")

あそびかた

S式をRの表現式に変換する

lisprr::translate("(+ 1 2)")
#> 1 + 2
lisprr::translate("(lambda (x) (+ x 2))")
#> function(x = NULL) {
#>     x + 2
#> }

S式を実行

lisprr::evaluate("(+ 1 2)")
#> [1] 3

Lispで書いた関数を

lisprr::evaluate("(define add2 (lambda (x) (+ x 2)))",
                 parent.frame())

Rで使える

add2(40)
#> [1] 42

REPLを起動すると

> lisprr::repl()

Rコンソール上でLispが動く

lisprr> (+ 1 2)
3
lisprr> (define add2 (lambda (x) (+ x 2)))
#<closure>
lisprr> (add2 40)
42

Rの関数も使える

lisprr> (: 1 10)
1 2 3 4 5 6 7 8 9 10
lisprr> (plot iris)

demo

作ってみた感想

楽しい!

lisprr

https://github.com/igjit/lisprr

Enjoy!

// reveal.js plugins