challenge 呼んだのは誰?

自身を呼び出した関数(メソッド)の名前を返す関数(メソッド)を書いてください。

Ruby で表現すると、 以下のような「fooという関数を呼び出す関数」#bar、#bazがあるとき

def bar; foo end
def baz; foo end
このbar, bazの返り値が以下のようになるような関数fooを定義してください。
bar  #=> "bar"
baz  #=> "baz"

このお題は匿名の「Smalltalk からの挑戦状」を元に作成しました。 確かにこの手のリフレクションの機能が言語によってどう違うのかは興味深いところです。 リフレクションを使う問題をいくつか考えてみたいと思います。 ご投稿ありがとうございました。

Posted feedbacks - Flatten

Nested Hidden
とりあえず。
1
2
3
4
5
6
7
8
9
import sys
def bar():
  return foo()

def baz():
  return foo()

def foo():
  return sys._getframe(1).f_code.co_name

callerの使い方をよくわかってませんが。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
def foo
  /.*`(.+)'/ =~ caller(1)[0]
  $1
end

def bar; foo; end
def baz; foo; end

p bar
p baz

引数の数がつくのでbar$0になりますが。
1
2
3
4
5
6
7
def bar = foo
def baz = foo

def foo = {
  val trace = (new Throwable).getStackTrace
  trace(1).getMethodName
}

あまり自信なし。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import inspect

def foo():
    print inspect.stack()[1][3]

def bar():
    foo()

def baz():
    foo()

def main():
    bar()
    baz()

if __name__ == '__main__':
    main()


	
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public class Test {
	public static void main(String[] args) throws Exception {
		System.out.println("bar() = " + bar());
		System.out.println("bar() = " + baz());
	}
	
	public static String bar() {
		return foo();
	}
	
	public static String baz() {
		return foo();
	}
	
	public static String foo() {
		StackTraceElement[] stackTraceElementArray = new Throwable().getStackTrace();
		return stackTraceElementArray[1].getMethodName();
	}
}

ごめんなさいアップしてから気づきましたが、
System.out.println("bar() = " + baz());
ではなく
System.out.println("baz() = " + baz());
でした。


	
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<?php
function getfuncname()
{
	$a=debug_backtrace();
	return $a[1]['function'];
}

function func_a()
{
	echo getfuncname(),"\n";
}

function func_b()
{
	echo getfuncname(),"\n";
}

func_a();
func_b();
?>

Schemeの場合、そもそも関数という概念が抽象的で、gotoの飛び先ラベル程度の
意味しか持たないため、「呼び出した関数」の意味を厳密に決めるのは困難です。
(ナイーブな処理系でも、少なくとも末尾呼び出しについては「ソース上のcaller」の
情報は出せません---出せるようにすると言語仕様を満たせないから)

なので、ここではGaucheのMOPを使って通常のSchemeとは違う呼び出し
セマンティクスを実現してみます。

実行例:
gosh> (define-generic bar :class <tracer>)
bar
gosh> (define-method bar () (foo))
#<<tracer> 0x8e26940>
gosh> (define-generic baz :class <tracer>)
baz
gosh> (define-method baz () (foo))
#<<tracer> 0x8e26900>
gosh> (bar)
bar
gosh> (baz)
baz
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
(use gauche.parameter)

(define call-trace (make-parameter '()))

(define-class <tracer> (<generic>) ())

(define-method apply-generic ((gf <tracer>) args)
  (parameterize ((call-trace (cons (ref gf'name) (call-trace))))
    (next-method)))

(define (foo) ((every-pred pair? car) (call-trace)))

ちなみに下のマクロも仕込んでおけば見かけ上は普通の手続き定義のようになります:

gosh> (define (bar) (foo))
#<<tracer> 0x8cc0900>
gosh> (define (baz) (foo))
#<<tracer> 0x8cc0880>
gosh> (bar)
bar
gosh> (baz)
baz

ただ、マクロを使ったら何でもありになってしまいますね。
(マクロ内で手続きのトレース用のラッパーを仕込んでしまう、とか。)
1
2
3
4
5
6
7
8
9
(use util.match)
(define-macro (define . args)
  (match args
    [((fn . args) . body)
     `(begin
        ((with-module gauche define) ,fn (make <tracer> :name ',fn))
        (define-method ,fn ,args . ,body))]
    [(var expr)
     `((with-module gauche define) ,var ,expr)]))

処理系依存、ですよ。
write('')みたいな副作用でもしないと、最適化で「呼び出し」が無かったことになっちゃうので、そうしています。
実行例
 $ pl -qs stack.pl
aaa(**)
bbb(**)
?-
1
2
3
4
5
6
aaa(X):-whoami(X),write('').
bbb(X):-whoami(X),write('').

whoami(X):-prolog_current_frame(F),prolog_frame_attribute(F,parent,X0),prolog_frame_attribute(X0,goal,X).

:-aaa(Xa),writeln(Xa),bbb(Xb),writeln(Xb).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
using System;
using System.Diagnostics;
class Program
{
  static void Main()
  {
    Console.WriteLine("bar  #=> \"{0}\"", bar());
    Console.WriteLine("baz  #=> \"{0}\"", baz());
  }
  static string bar() { return foo(); }
  static string baz() { return foo(); }
  static string foo()
  {
    return new StackTrace().GetFrame(1).GetMethod().Name;
  }
}

アカウントが作成できなかったので匿名投稿します。

var bar = function(){...}

という形式では実現できませんでした。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
var foo = function(){
    return arguments.callee.caller.toString().match(/function\s*([^(]+)/)[1];
}
function bar(){
    return foo();
}
function baz(){
    return foo();
}

alert(bar());
alert(baz());

GNU拡張を使用しています。

$ gcc -finstrument-functions -rdynamic caller.c -ldl

のような形でコンパイルしてください。

参考 : http://alohakun.blog7.fc2.com/blog-entry-760.html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <dlfcn.h>

char *caller_func_name;

__attribute__((no_instrument_function))
void __cyg_profile_func_enter(void *func_address, void *call_site)
{
    Dl_info func_info;
    Dl_info call_info;

    if (dladdr(func_address, &func_info) == 0) {
        return;
    }
    if (strcmp(func_info.dli_sname, "foo") != 0) {
        return;
    }

    if (dladdr(call_site, &call_info) == 0) {
        return;
    }
    caller_func_name = (char *)call_info.dli_sname;
}

char *foo()
{
    return caller_func_name;
}

char *bar()
{
    return foo();
}

char *baz()
{
    return foo();
}

int main()
{
    printf("%s\n", bar());
    printf("%s\n", baz());
    return 0;
}

> foo()
[1] "foo"
> bar()
[1] "bar"
> baz()
[1] "baz"
1
2
3
4
5
6
7
8
9
foo <- function(){
    as.character(sys.calls()[[1]])
}
bar <- function(){
    foo()
}
baz <- function(){
    foo()
}

アカウント作れないですねぇ
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
use strict;

sub bar {
  foo();
}

sub baz {
  foo();
}

sub foo {
  my $p = (caller(1))[0];
  (caller(1))[3] =~ m|^${p}::(.*)|;
}

print bar(),"\n";
print baz(),"\n";

処理系依存だな。
1
2
3
4
5
(defun foo () (caadr (caddr (sb-debug:backtrace-as-list))))
(defun bar () (foo))
(defun baz () (foo))
(bar)                                   ; => BAR
(baz)                                   ; => BAZ

1
2
3
4
5
6
7
8
def foo
  caller(1)[0][ /in `(.+)'$/, 1 ]
end

def bar() foo end
def baz() foo end
bar                             # => "bar"
baz                             # => "baz"

インタプリタ版。 当然だがこういうのはバイトコンパイルすると使えない。他のコードになる。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
(defun foo ()
  (with-temp-buffer
    (let ((standard-output (current-buffer)))
      (backtrace)
      (goto-char (point-min))
      (forward-line 8)
      (forward-sexp 1)
      (buffer-substring-no-properties (+ 2 (point-at-bol)) (point)))))
(defun bar () (foo))
(defun baz () (foo))
(bar)                                   ; => "bar"
(baz)                                   ; => "baz"

tail call optimizationをやるのでわざとstring.formatを呼んでみる。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
function foo()
  return string.gsub(debug.traceback(), "^.+\n.+\n.+`(.+)'.+$", "%1")
end
function bar()
  return string.format("%s",foo())
end
function baz()
  return string.format("%s",foo())
end

print(bar())
print(baz())

すみません。問題の投稿時にログインを忘れていました(^_^;)。

遅まきながら Squeak Smalltalk です。
1
2
Object >> foo
   ^thisContext sender selector

どうして匿名で投稿したのかなと疑問に思っていたのですが
そういうことでしたか(^^;

inspectはまだ使いこなせていませんが。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
def caller():
    import inspect
    return inspect.getouterframes(inspect.currentframe())[1][3]

def foo():
    print caller()

def bar():
    print caller()

foo(), bar()
# foo
# bar

__builtin_return_address() と libbfd の bfd_find_nearest_line() を使ってみました。 % gcc -g caller.c -lbfd でコンパイルできます。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <stdio.h>
#include <stdlib.h>
#include <bfd.h>

static bfd *abfd;
static int size;
static asymbol **symbols;
static asection *section;

const char *foo()
{
    int ret;
    void *addr;
    const char *file;
    const char *func;
    unsigned int line;

    addr = __builtin_return_address(0);
    ret = bfd_find_nearest_line(abfd, section, symbols, (long)addr,
                                &file, &func, &line);
    return func;
}

const char *bar()
{
    return foo();
}

const char *baz()
{
    return foo();
}

int main()
{
    abfd = bfd_openr("/proc/self/exe", NULL);
    bfd_check_format(abfd, bfd_object);
    size = bfd_get_symtab_upper_bound(abfd);
    symbols = (asymbol**)malloc(size);
    section = bfd_get_section_by_name(abfd, ".debug_info");
    printf("%s\n", bar());
    printf("%s\n", baz());
    free(symbols);
    return 0;
}

ちゃんとテーブルの形で情報もらえるので文字列から抽出する必要はないかと
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
function foo() return debug.getinfo(2).name end

function bar()
  local ret = foo()
  return ret
end

function baz()
  local ret = foo()
  return ret
end

print(bar())
print(baz())

関数の概念がない言語だと、カバレッジに影響しませんか?

新し目のbashじゃないと無理だと思います。
1
2
3
4
5
6
7
8
9
foo () {
  echo "${FUNCNAME[${#FUNCNAME[@]}-1]}"
}
bar () {
  foo
}
baz () {
  foo
}

Haskellでは名前(識別子)はfirst-classの対象じゃないので式の中では通常の方法では不可能でしょうね。正規順簡約ではなにをcaller、calleeというのかは自明ではないでしょうねぇ。せいぜいコマンドラインから起動したプログラム名くらいかなかのうなのは。。。
1
2
import System.Environment
main = getProgName >>= putStrLn

dbstackでファイル名やそのファイル名の中の何行目で呼ばれたかも取得可能。デバッグ目的の関数なので。
1
2
3
function foo
st = dbstack;
st(2).name

Perl6::Callerを使う例

PS C:\> bar
bar
PS C:\> buz
buz
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
function caller
{
     if ((get-variable -scope 1 MyInvocation).Value.PositionMessage -match "\+ (\S+) <<<<")
     {
          $matches[1]
     }
}

function bar { caller }
function buz { caller }


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
foo := method(
    call coroutine callStack at(2) message name
)

bar := method(
    foo
)

baz := method(
    foo
)

bar println
baz println

実行結果
--------------
Called from :bar
Called from :baz

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
println bar()
println baz()

def bar() { foo() }
def baz() { foo() }

def foo() {
    def className = getClass().getName()
    def thisObject = new Throwable().getStackTrace().findAll {
            stackTraceElement ->
            className.equals(stackTraceElement.getClassName())
        }
    return "Called from :"+ thisObject[1].getProperties().methodName
}

Index

Feed

Other

Link

Pathtraq

loading...