R の if は文なのか? ~R の内部処理の観点から~

おもしろいネタを見つけたのでダメ出しにダメ出ししてみようと思います。

if() 関数 → if 文
 R では if も関数ではあるが,if を 関数として使うときは x <- 3; “if”(x %% 2, print(“odd”), print(“even”)) のような使い方になる。すなわち,例に挙げているのは if 文である。

ダメ出し:R で学ぶデータ・プログラミング入門 ―RStudioを活用する― その1 - 裏 RjpWiki

個人的に対象読者を考えると「if() 関数」よりも「if 文」の方が混乱を招かないので適切かと思いますが、この主張は的外れだと思います。
関数と文の厳密な定義はない気もしますが、例えば英語版 Wikipedia では if 文 (if statements) と if 関数 (if expressions) を値を返すかどうかで分けています。
この観点だと R の if は使い方に関係なく if 関数と呼ぶべきでしょう。

> (v <- if (TRUE) 1)
[1] 1
> (v <- `if`(TRUE, 1))
[1] 1

それはそうと、値を返すかどうかに関係なく R の if は厳密な意味で関数だと思います。というのも、if はどのような使い方をしようと内部的には special functions (SPECIALSXP) と呼ばれるデータになるからです。
パースの段階だと if の使い方によって違う処理が走りますが、パースが完了すると全く同じデータになります。

試しに if を処理する関数(do_if)にブレークポイントを仕込んでそれぞれの場合にコールスタックを表示してみます。

$ R --debugger=gdb
GNU gdb 6.3.50-20050815 (Apple version gdb-1708) (Mon Aug 15 16:03:10 UTC 2011)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "x86_64-apple-darwin"...Reading symbols for shared libraries .... done

(gdb) run
Starting program: /path/to/R.framework/Versions/2.15/Resources/bin/exec/R 
Reading symbols for shared libraries .+++.............. done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done

R version 2.15.2 (2012-10-26) -- "Trick or Treat"
Copyright (C) 2012 The R Foundation for Statistical Computing
ISBN 3-900051-07-0
Platform: x86_64-apple-darwin10.8.0 (64-bit)

R is free software and comes with ABSOLUTELY NO WARRANTY.
You are welcome to redistribute it under certain conditions.
Type 'license()' or 'licence()' for distribution details.

  Natural language support but running in an English locale

R is a collaborative project with many contributors.
Type 'contributors()' for more information and
'citation()' on how to cite R or R packages in publications.

Type 'demo()' for some demos, 'help()' for on-line help, or
'help.start()' for an HTML browser interface to help.
Type 'q()' to quit R.

[Previously saved workspace restored]

Reading symbols for shared libraries .................................................................................. done
Reading symbols for shared libraries .. done
> 
Program received signal SIGINT, Interrupt.
0x00007fff84db9932 in select$DARWIN_EXTSN ()
(gdb) b do_if
Breakpoint 1 at 0x1000b8634: file eval.c, line 1170.
(gdb) signal 0
Continuing with no signal.
(v <- if (TRUE) 1)

Breakpoint 1, do_if (call=0x1038e7ad8, op=0x101847c70, args=0x1038e7b10, rho=0x101873358) at eval.c:1170
1170	SEXP attribute_hidden do_if(SEXP call, SEXP op, SEXP args, SEXP rho)
(gdb) bt
#0  do_if (call=0x1038e7ad8, op=0x101847c70, args=0x1038e7b10, rho=0x101873358) at eval.c:1170
#1  0x00000001000aa0bf in Rf_eval (e=0x1038e7ad8, rho=0x1002c7680) at eval.c:468
#2  0x00000001000b8222 in do_set (call=0x1038e7a30, op=0x101849ed8, args=0x1038e7a68, rho=0x101873358) at eval.c:1717
#3  0x00000001000aa0bf in Rf_eval (e=0x1038e7a30, rho=0x1002c7680) at eval.c:468
#4  0x00000001000b5738 in Rf_evalList (el=0x1038e7ad8, rho=0x7fff5fbfdb50, call=0x7fff5fbfdb50, n=1) at eval.c:1831
#5  0x00000001000aa170 in Rf_eval (e=0x1038e79c0, rho=0x101873358) at eval.c:487
#6  0x00000001000ed5bf in Rf_ReplIteration (rho=0x101873358, savestack=1606409504, browselevel=59668928, state=0x1826b7800000000) at main.c:256
#7  0x00000001000edc43 in R_ReplConsole [inlined] () at /Users/arabiki/Downloads/R-2.15.2/src/main/main.c:305
#8  0x00000001000edc43 in run_Rmainloop () at main.c:987
#9  0x0000000100000e5b in main (ac=59669208, av=0x3723c000000000) at Rmain.c:32
(gdb) c
Continuing.
[1] 1
> (v <- `if`(TRUE, 1))

Breakpoint 1, do_if (call=0x1038e7608, op=0x101847c70, args=0x1038e76e8, rho=0x101873358) at eval.c:1170
1170	SEXP attribute_hidden do_if(SEXP call, SEXP op, SEXP args, SEXP rho)
(gdb) bt
#0  do_if (call=0x1038e7608, op=0x101847c70, args=0x1038e76e8, rho=0x101873358) at eval.c:1170
#1  0x00000001000aa0bf in Rf_eval (e=0x1038e7608, rho=0x1002c7680) at eval.c:468
#2  0x00000001000b8222 in do_set (call=0x1038e8508, op=0x101849ed8, args=0x1038e8540, rho=0x101873358) at eval.c:1717
#3  0x00000001000aa0bf in Rf_eval (e=0x1038e8508, rho=0x1002c7680) at eval.c:468
#4  0x00000001000b5738 in Rf_evalList (el=0x1038e7608, rho=0x7fff5fbfdb50, call=0x7fff5fbfdb50, n=1) at eval.c:1831
#5  0x00000001000aa170 in Rf_eval (e=0x1038e8498, rho=0x101873358) at eval.c:487
#6  0x00000001000ed5bf in Rf_ReplIteration (rho=0x101873358, savestack=1606409504, browselevel=59671704, state=0x1826b7800000000) at main.c:256
#7  0x00000001000edc43 in R_ReplConsole [inlined] () at /Users/arabiki/Downloads/R-2.15.2/src/main/main.c:305
#8  0x00000001000edc43 in run_Rmainloop () at main.c:987
#9  0x0000000100000e5b in main (ac=59667976, av=0x3723c000000000) at Rmain.c:32
(gdb) c
Continuing.
[1] 1

もちろんアドレスは変わりますが、基本的に全く同じですね。細かく call, op, args, rho の中身を確認しても同じであることがわかります。

以上、Wikipedia の定義の観点と R の内部処理の観点では R の if は使い方に関係なく関数と結論付けたいと思います。
もちろん他の観点だと文という結論になるかもしれませんが、少なくとも人様の書籍に対して誤りと強く主張できるようなことではない気がします。便宜上こちらの使い方を if 文、こちらの使い方を if 関数とする、とかは良いでしょう。

広告
R のデータ構造とメモリ管理 R のコアチームにバグレポートを送ってみた
※このエントリーははてなダイアリーから移行したものです。過去のコメントなどはそちらを参照してください