Fungsi / makro yang mengembalikan true jika salah satu argumennya berisi panggilan ke dirinya sendiri

7

Tulis fungsi (atau makro) yang mengembalikan true jika dan hanya jika setidaknya salah satu argumennya berisi panggilan ke fungsi itu sendiri dan false jika tidak .

Sebagai contoh:

int a=function(); //a==false
int b=function(0); //b==false
int c=function(function(0)); //c==true
int d=function(3*function(1)+2); //d==true (weakly-typed language)
bool e=function(true||function(1)); //e==true (strongly-typed language) 

EDIT: Fungsi / makro dapat memanggil fungsi / makro tambahan lainnya.

EDIT 2: Fungsi harus mengambil setidaknya satu argumen, kecuali bahasa yang digunakan berperilaku seperti C, di mana fungsi yang tidak menggunakan argumen masih dapat dipanggil dengan argumen.


sumber
Apakah itu harus berfungsi dalam konteks seperti print(func(), func(func())), atau apakah hanya akan ada panggilan tingkat atas ke fungsi segera setelah ditetapkan?
algorithmshark
Seharusnya berfungsi dalam situasi itu.
10
Mengapa menerima begitu cepat?
Kyle Kanos
1
Saya tidak menyukai pertanyaan ini karena hanya bisa dipecahkan dalam bahasa yang memungkinkan Anda untuk memeriksa pohon ekspresi saat runtime.
FUZxxl
1
@ FuZxxl: Saya tidak setuju; Saya baru saja mengirim jawaban yang berfungsi dengan baik bahkan jika panggilan tidak langsung melalui FFI (dan dengan demikian pohon ekspresi fungsi interior tidak dapat dilihat).

Jawaban:

11

Matematika ...

... dibuat untuk ini.

SetAttributes[f, HoldAll]
f[x___] := ! FreeQ[Hold@x, _f]

f[]             (* False *)
f[0]            (* False *)
f[f[0]]         (* True *)
f[3 f[1] + 2]   (* True *)
f[True || f[1]] (* True *)

Semuanya adalah ekspresi, dan ekspresi memiliki Kepala dan sejumlah elemen. Jadi 1+2sebenarnya Plus[1,2], dan {1,2}sebenarnya List[1,2]. Ini berarti kami dapat mencocokkan ekspresi apa pun untuk kepala yang kami minati - dalam hal ini fungsinya fsendiri.

Yang perlu kita lakukan adalah menemukan cara untuk mencegah Mathematica dari mengevaluasi argumen fungsi sebelum fungsi dipanggil, sehingga kita dapat menganalisis pohon ekspresi dalam fungsi. Untuk apa Holddan HoldAlluntuk apa. Mathematica sendiri menggunakan ini ke semua tempat untuk menyediakan kasus khusus untuk implementasi tertentu. Misalnya jika Anda menyebutnya Length[Permutations[list]]tidak akan benar-benar membuat semua permutasi dan membuang banyak memori, tetapi sebaliknya menyadari bahwa itu hanya dapat menghitung ini sebagai Length[list]!.

Mari kita lihat kode di atas secara terperinci, menggunakan panggilan terakhir f[True || f[1]]sebagai contoh. Biasanya, Mathematica akan mengevaluasi argumen fungsi terlebih dahulu, jadi ini hanya akan menyebabkan hubungan pendek dan panggilan f[True]. Namun kami telah menetapkan

SetAttributes[f, HoldAll]

Ini menginstruksikan Mathematica untuk tidak mengevaluasi argumen, sehingga FullFormpanggilan (yaitu pohon ekspresi internal, tanpa gula sintaksis) adalah

f[Or[True,f[1]]]

Dan argumen itu sebenarnya akan diterima fdalam bentuk ini. Masalah berikutnya adalah, di dalam f, segera setelah kami menggunakan argumen, Mathematica akan kembali mencoba untuk mengevaluasinya. Kami dapat menekan ini secara lokal dengan Hold@x(sintaksis gula untuk Hold[x]). Pada titik ini kita sudah memiliki pegangan pada pohon ekspresi asli dan melakukan apa pun yang kita inginkan.

Untuk mencari pola di pohon ekspresi dapat kita gunakan FreeQ. Ia memeriksa bahwa pola yang diberikan tidak ditemukan di pohon ekspresi. Kami menggunakan derai _f, yang cocok dengan subekspresi apa pun dengan kepala f(persis apa yang kami cari). Tentu saja, FreeQmengembalikan kebalikan dari apa yang kita inginkan, jadi kita meniadakan hasilnya.

Satu lagi kehalusan: Saya telah menetapkan argumen sebagai x___, yang merupakan urutan 0 atau lebih elemen. Ini memastikan bahwa fbekerja dengan sejumlah argumen. Dalam panggilan pertama, f[]ini artinya Hold@xakan menjadi begitu saja Hold[]. Jika ada beberapa argumen, seperti f[0,f[1]], maka Hold@xakan menjadi Hold[0,f[1]].

Hanya itu yang ada di sana.

Martin Ender
sumber
6

C ++ 11

Mirip dengan templat ekspresi kita dapat menyebarkan fakta bahwa kita memang memanggil fungsi di dalam daftar parameter fungsi dalam tipe pengembaliannya.

Pada awalnya kita membutuhkan beberapa kelas dan fungsi pembantu:

#include <iostream>

template <bool FunctionWasInParameters> struct FunctionMarker {
  operator bool() const { return FunctionWasInParameters; }

  operator int() const { return FunctionWasInParameters; }
};

template <bool... values> struct Or;

template <bool first, bool... values> struct Or<first, values...> {
  static constexpr bool value = first || Or<values...>::value;
};

template <> struct Or<> { static constexpr bool value = false; };

template <class T> struct is_FunctionMarker {
  static constexpr bool value = false;
};

template <bool B> struct is_FunctionMarker<FunctionMarker<B>> {
  static constexpr bool value = true;
};

#define OVERLOAD_OPERATOR_FOR_FUNCTION_MARKER(OPERATOR)                        \
  template <bool B, class T>                                                   \
  FunctionMarker<B> operator OPERATOR(FunctionMarker<B>, T) {                  \
    return {};                                                                 \
  }                                                                            \
  template <bool B, class T>                                                   \
  FunctionMarker<B> operator OPERATOR(T, FunctionMarker<B>) {                  \
    return {};                                                                 \
  }                                                                            \
  /* to break ambiguity by specialization */                                   \
  template <bool B, bool B2>                                                   \
  FunctionMarker<B || B2> operator OPERATOR(FunctionMarker<B>,                 \
                                            FunctionMarker<B2>) {              \
    return {};                                                                 \
  }

OVERLOAD_OPERATOR_FOR_FUNCTION_MARKER(|| )
OVERLOAD_OPERATOR_FOR_FUNCTION_MARKER(+)
OVERLOAD_OPERATOR_FOR_FUNCTION_MARKER(*)
// TODO: overload all other operators!

Sekarang, kita dapat menggunakannya untuk kode yang sama persis seperti pada pertanyaan:

template <class... Args>
auto function(Args... args)
    -> FunctionMarker<Or<is_FunctionMarker<Args>::value...>::value> {
  return {};
}

FunctionMarker<false> function() { return {}; }

int main() {
  int a = function();
  int b = function(0);
  int c = function(function(0));
  int d = function(3 * function(1) + 2);
  bool e = function(true || function(1));

  // clang-format off
  std::cout << a << "//a==false\n"
            << b << "//b==false\n"
            << c << "//c==true\n"
            << d << "//d==true (weakly-typed language)\n"
            << e << "//e==true (strongly-typed language)\n";
}

Output dari ideone:

0//a==false
0//b==false
1//c==true
1//d==true (weakly-typed language)
1//e==true (strongly-typed language)
Tak seorangpun
sumber
4

C #

public PcgBool f(params object[] args)
{
    return args.Any(p => p is PcgBool);
}

public class PcgBool
{
    public PcgBool() { }
    public PcgBool(bool value)
    {
        Value = value;
    }

    private bool Value;

    public static implicit operator bool(PcgBool pcgBool)
    {
        return pcgBool.Value;
    }    

    public static implicit operator PcgBool(bool value)
    {
        return new PcgBool(value);
    }
}

Penggunaan (dalam LINQPad ):

void Main()
{
    Console.WriteLine(f(1,2,f(3),4)); // True
    Console.WriteLine(f(1,2,3,"4")); // False
}

Kuncinya di sini adalah untuk membuat flebih sadar akan parameter, dengan melewati tipe custom ( PcgBool) yang agak mirip boolean.

PS Saya harap mengembalikan jenis kustom yang secara implisit dapat dicor dari dan ke bool tidak dianggap sebagai curang. Secara teknis Anda dapat menggunakan nilai kembali seolah-olah itu dari jenis bool, dan pertanyaan yang diajukan untuk "mengembalikan true jika dan hanya jika" dll, tetapi tidak pernah menyatakan jenis kembali harus bool.

Yakub
sumber
Bagaimana dengan f(new PcgBool())?
Qwertiy
Hmm ... Anda benar tentang itu. Tampaknya rem jawaban saya. Sayangnya, saya terlalu malas untuk memperbaikinya sekarang (sudah lama ...)
Jacob
3

Lua

local x
local function f()
    local y = x
    x = true
    return y
end
debug.sethook(function()
        if debug.getinfo(2).func ~= f then
            x = false
        end
    end, "l")
print(f())
print(f(0))
print(f(f()))
print(f(f(0)))
print(f("derp" .. tostring(f())))
mniip
sumber
Dan saya memikirkan cara untuk membandingkan tabel secara rekursif. Bagus, saya tidak berpikir debug bisa sekuat itu!
yyny
3

C ++ 11 TMP

Yang ini sedikit lebih lama. Karena beberapa keterbatasan dalam template C ++ saya harus melakukan semuanya dengan tipe. Jadi "Benar" serta angka-angka diubah menjadi bool dan int. Juga Operasi +, - dan || diubah menjadi tambah, mul dan or_.

Saya harap ini masih memenuhi syarat sebagai jawaban yang valid.

template <typename... Args>
struct foo;

template <>
struct foo<>
{
    static bool const value = false;
};

template <typename T>
struct is_foo
{
    static bool const value = false;
};

template <typename... Args>
struct is_foo<foo<Args...>>
{
    static bool const value = true;
};

template <typename T>
struct is_given_foo
{
    static bool const value = false;
};

template <template <typename...> class T, typename... Args>
struct is_given_foo<T<Args...>>
{
    static bool const value = foo<Args...>::value;
};

template <typename Head, typename... Tail>
struct foo<Head, Tail...>
{
    static bool const value = is_foo<Head>::value || is_given_foo<Head>::value || foo<Tail...>::value;
};

template <typename... Args>
struct add {};

template <typename... Args>
struct mul {};

template <typename... Args>
struct or_ {};

static_assert(foo<>::value == false, "int a=function(); //a==false");
static_assert(foo<int>::value == false, "int b=function(0); //b==false");
static_assert(foo<foo<int>>::value == true, "int c=function(function(0)); //c==true");
static_assert(foo<add<mul<int, foo<int>>, int>>::value == true, "int d=function(3*function(1)+2); //d==true (weakly-typed language)");
static_assert(foo<or_<bool,foo<int>>>::value == true, "bool e=function(true||function(1)); //e==true (strongly-typed language)");

// just for the sake of C++
int main()
{
    return 0;
}
Felix Bytow
sumber
Benar-benar bagus! Aturan apa yang menyebabkan definisi kedua is_given_foolebih disukai daripada yang pertama?
feersum
Mungkin seseorang dapat membantu saya, karena saya tidak dapat menemukan tempat yang tepat dalam standar untuk dikutip. Pokoknya karena is_given_foo yang kedua adalah spesialisasi templat dari yang pertama, maka selalu lebih disukai ketika parameter templat cocok dengan pola yang diberikan, dalam hal ini jika parameternya adalah templat itu sendiri.
Felix Bytow
2

C

Saya tidak berpikir itu bisa dilakukan dengan prosedur, tetapi kita selalu dapat (ab) menggunakan makro.

#include <stdio.h>
#define f(x) ((max_depth = ++depth), (x), (depth-- < max_depth))

int depth = 0;
int max_depth = 0;

char* bool(int x) { // Helper - not necessary for solution
  return x ? "true" : "false";
}

int main() {
  printf("f(1): %s\n", bool(f(1)));
  printf("f(f(1)): %s\n", bool(f(f(1))));
  printf("f(bool(f(1))): %s\n", bool(f(bool(f(1)))));
  printf("f(printf(\"f(1): %%s\\n\", bool(f(1)))): %s\n", bool(printf("f(1): %s\n", bool(f(1)))));
}

Output ini:

f(1): false
f(f(1)): true
f(bool(f(1))): true
f(1): false
f(printf("f(1): %s\n", bool(f(1)))): true

Makro kami fmelacak kedalaman saat ini, dan kedalaman maksimum yang dicapai sejak dipanggil. Jika yang terakhir lebih besar dari yang pertama, maka telah disebut secara rekursif.

James_pic
sumber
Heh. Kami berakhir dengan solusi yang kurang lebih sama pada waktu yang kurang lebih sama.
Seni
@ Art LOL. Juga, saya merasa sangat konyol sekarang, karena saya tidak mengetahui operator koma (karena C berada di sekitar bahasa pilihan 4 atau 5 saya), yang saya retas menggunakan &&dan ||. Saya dapat mencoba menebus jawaban saya.
James_pic
Nah, operator koma hanya untuk lapisan kebingungan tambahan. Saya tidak akan pernah menggunakannya dalam kode normal. Tetapi dalam hal ini saya bingung sendiri ketika mencoba menggunakan operator logis.
Seni
2

C, 13

#define F(X)0

Argumen tidak pernah diperluas, sehingga tidak bisa memanggil makro apa pun. Itu tidak lebih dari seikat teks biasa. Jadi, jawabannya selalu salah.

feersum
sumber
1
Ini benar-benar jawaban yang brilian.
FUZxxl
Datangi ini sambil melihat posting preprosesor lama ... sebenarnya, ini salah. F(F(0))akan dengan senang hati mengevaluasi argumen F F(0),. Argumen itu berkembang menjadi 0. Kemudian mengevaluasi F tanpa cat biru pada argumennya 0, menghasilkan 0. Pembatasan non-rekursif tidak berlaku; saat itulah misalnya saya miliki #define F(X) Gdan #define G F(Y)sedang bermain; dalam hal ini, saat memperluas F (0) ke G, dan kemudian ke F (Y), token Fmuncul. Karena aku sedang memperluas F, F memiliki cat biru dalam ini kasus, dan dengan demikian ekspansi berhenti di F (Y).
H Walters
@Halter Wow, Anda benar. Apa pendapat Anda tentang penjelasan baru saya?
feersum
Jadi sepertinya sekarang Anda mengatakan bahwa karena Xtidak ada dalam daftar penggantian argumen, makro apa pun yang terikat padanya tidak pernah diperluas. Jika kita menafsirkannya sebagai fungsi yang dipanggil, itu berarti fungsi argumen tidak pernah dipanggil. Ya, saya pikir itu benar.
H Walters
1

C

#include <stdio.h>

int a, b;
#define foo(x) (b=a++,(void)x,a--,!!b)

int
main(int argc, char **argv)
{
        printf("%d\n", foo(1));
        printf("%d\n", foo(0));
        printf("%d\n", foo(foo(1)));
        printf("%d\n", foo(foo(foo(1))));
        return 0;
}

Kita bisa menghitung kedalaman rekursi di makro. Kami kemudian menimpa nilai kembali makro luar di makro dalam. !!badalah menormalkan nilai kembali ke boolean. Kode preprocessed berakhir seperti ini:

int a, b;

int
main(int argc, char **argv)
{
 printf("%d\n", (b=a++,(void)1,a--,!!b));
 printf("%d\n", (b=a++,(void)0,a--,!!b));
 printf("%d\n", (b=a++,(void)(b=a++,(void)1,a--,!!b),a--,!!b));
 printf("%d\n", (b=a++,(void)(b=a++,(void)(b=a++,(void)1,a--,!!b),a--,!!b),a--,!!b));
 return 0;
}
Seni
sumber
Bagaimana dengan kasus (diakui sangat konyol) printf("%d\n", foo(printf("%d\n", foo(1)))). Panggilan batin untuk foomengembalikan 1, tetapi tidak menelepon foo.
James_pic
1

C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BLAH(x) (strncmp( "BLAH(", #x, strlen("BLAH(")) == 0)

int main(int argc, char** argv)
{
  printf("%d\n", BLAH(1));
  printf("%d\n", BLAH("1234"));
  printf("%d\n", BLAH("BLAH"));
  printf("%d\n", BLAH(BLAHA));
  printf("%d\n", BLAH(BLAHB()));
  printf("%d\n", BLAH(BLAH));
  printf("%d\n", BLAH(BLAH()));
  printf("%d\n", BLAH(BLAH("1234")));
  return 0;
}

Makro membandingkan jika argumennya dimulai dengan "BLAH (".

kwokkie
sumber
1
Tidak berfungsi BLAH(blub(BLAH(0))).
FUZxxl
1

Algol 60

Berikut adalah boolean procedureyang melakukan apa yang ditanyakan oleh pertanyaan (catatan: Algol 60 didefinisikan dalam hal daftar token tanpa memperbaiki sintaks untuk mereka, di bawah ini menggunakan sintaks Marst untuk mewakili token individu yang membentuk program):

boolean procedure recursion detector(n);
  boolean n;
  begin
    own boolean nested, seen nested;
    boolean was nested, retval;
    was nested := nested;
    begin if nested then seen nested := true end;
    nested := true;
    retval := n; comment "for the side effects, we ignore the result";
    nested := was nested;
    retval := seen nested;
    begin if ! nested then seen nested := false end;
    recursion detector := retval
  end;

Verifikasi

Berikut kode tes yang saya gunakan:

procedure outboolean(c, b);
  integer c;
  boolean b;
  begin
    if b then outstring(c, "true\n") else outstring(c, "false\n")
  end;

begin
  outboolean(1, recursion detector(false));
  outboolean(1, recursion detector(true));
  outboolean(1, recursion detector(recursion detector(false)));
  outboolean(1, recursion detector(false | recursion detector(true)));
  outboolean(1, recursion detector(false & recursion detector(true)));
  outboolean(1, recursion detector(recursion detector(recursion detector(false))))
end

Seperti yang diharapkan, outputnya adalah:

false
false
true
true
true             comment "because & does not short-circuit in Algol 60";
true

Penjelasan

Algol 60 memiliki urutan evaluasi yang berbeda dari kebanyakan bahasa, yang memiliki logika sendiri, dan sebenarnya jauh lebih kuat dan umum daripada urutan evaluasi yang khas, tetapi cukup sulit bagi manusia untuk mendapatkan kepala mereka sekitar (dan juga cukup sulit untuk komputer untuk diimplementasikan secara efisien, itulah sebabnya ia diubah untuk Algol 68). Ini memungkinkan untuk solusi tanpa kecurangan (program tidak perlu melihat pohon parse atau semacamnya, dan tidak seperti hampir semua solusi lain di sini, ini akan bekerja dengan baik jika panggilan bersarang dilakukan melalui FFI).

Saya juga memutuskan untuk memamerkan beberapa kebiasaan bahasa lainnya. (Khususnya, nama variabel dapat berisi spasi putih; ini cukup berguna untuk dibaca, karena mereka tidak dapat berisi garis bawah. Saya juga menyukai kenyataan bahwa indikator komentar adalah kata literal commentdalam sebagian besar penyandian sintaksis. Algol 68 menemukan ini cukup canggung untuk pendeknya komentar, dan diperkenalkan ¢sebagai alternatif. Kutipan di sekitar badan komentar biasanya tidak diperlukan, saya hanya menambahkannya untuk kejelasan dan untuk mencegah komentar secara tidak sengaja berakhir ketika saya mengetikkan titik koma.) Saya sebenarnya sangat menyukai konsep luas bahasa (jika bukan detailnya), tetapi sangat verbose sehingga saya jarang menggunakannya di PPCG.

Cara utama di mana Algol 60 berbeda dari bahasa yang diilhami (seperti Algol 68, dan secara tidak langsung C, Java, dll; orang yang tahu K&R C mungkin akan mengenali sintaks ini untuk fungsi) adalah bahwa argumen fungsi diperlakukan agak seperti lambda kecil sendiri; misalnya, jika Anda memberikan argumen 5ke fungsi yang hanya angka 5, tetapi jika Anda memberikan argumen x+1Anda mendapatkan apa yang Anda tentukan, konsep " xplus 1", bukan hasil dari xplus 1. Perbedaannya di sini adalah bahwa jika xperubahan, kemudian mencoba untuk mengevaluasi argumen fungsi yang bersangkutan akan melihat baru nilaix. Jika argumen fungsi tidak dievaluasi di dalam fungsi, itu tidak akan dievaluasi di luar fungsi juga; juga, jika dievaluasi beberapa kali di dalam fungsi, itu akan dievaluasi secara terpisah setiap kali (dengan asumsi bahwa ini tidak dapat dioptimalkan). Ini berarti bahwa dimungkinkan untuk melakukan hal-hal seperti menangkap fungsi, katakanlah, ifatau whiledalam suatu fungsi.

Dalam program ini, kami mengeksploitasi fakta bahwa jika panggilan ke suatu fungsi muncul dalam argumen ke fungsi itu, itu berarti bahwa fungsi tersebut akan berjalan secara rekursif (karena argumen tersebut dievaluasi tepat pada titik atau titik yang dievaluasi oleh fungsi fungsi itu. , tidak lebih awal atau lebih lambat, dan ini harus berada di dalam fungsi body). Ini mengurangi masalah untuk mendeteksi jika fungsi berjalan secara rekursif, yang jauh lebih mudah; yang Anda butuhkan adalah variabel thread-lokal yang merasakan jika ada panggilan rekursif (ditambah, dalam hal ini, yang lain untuk mengkomunikasikan informasi kembali dengan cara lain). Kita dapat menggunakan variabel statis (misown) untuk tujuan tersebut, karena Algol 60 adalah single-threaded. Yang harus kita lakukan setelah itu adalah mengembalikan semuanya seperti semula, sehingga fungsinya akan berfungsi dengan benar jika dipanggil beberapa kali (seperti yang disyaratkan oleh aturan PPCG).

Fungsi tidak mengembalikan nilai yang diinginkan dari panggilan dalam saat ini (setidaknya jika Anda menganggap mereka harus mencari panggilan sendiri hanya dalam argumen mereka , daripada menghitung sendiri); membuat pekerjaan itu cukup mudah menggunakan prinsip-prinsip umum yang sama, tetapi lebih kompleks dan akan mengaburkan cara kerja fungsi. Jika itu dianggap perlu untuk mematuhi pertanyaan, itu seharusnya tidak terlalu sulit untuk diubah.


sumber
0

Jawa

public class FuncOFunc{

private static int count = 0;

public static void main(String[] args){

    System.out.println("First\n" + function());
    reset();

    System.out.println("Second\n" + function(1));
    reset();

    System.out.println("Third\n" + function(function(1)));
    reset();

    System.out.println("Fourth\n" + function(3*function(1)+2));
    reset();

    System.out.println("Fifth\n" + function(function(1), function(), 4));
}

/**
 * @param args
 */
private static int function(Object...args) {
    StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
    count+=stackTraceElements.length;
    if(count>3) return 1;
    else return 0;
}

static void reset(){
    count = 0;
}
}

dapat reset()dihitung sebagai tambahan?

Keluaran:

First
0
Second
0
Third
1
Fourth
1
Fifth
1

EDIT

Ini adalah versi lain yang tidak menggunakan reset()metode ini, tetapi banyak kegilaan. Itu menciptakan dan mengkompilasi pada saat runtime kode di atas dengan panggilan ke fungsi yang dilewati sebagai argumen di stdin. Saya ingin solusi yang lebih elegan tetapi sayangnya saya tidak punya terlalu banyak waktu untuk ini :(

Untuk menjalankannya cukup panggil misalnya javac FuncOFunc.java function(function(1),function(),4).

import java.io.File;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLClassLoader;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

public class FuncOFunc {
    public static void main(String[] args) throws Exception {
        JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager sjfm = jc.getStandardFileManager(null, null, null);
        File jf = new File("FuncOFuncOFunc.java");
        PrintWriter pw = new PrintWriter(jf);
        pw.println("public class FuncOFuncOFunc{"
                + "public static void main(){ "
                + "     System.out.println("+ args[0] +");"
                + "}"
                + "     private static int function(Object...args)     {"
                + "         StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();"
                + "         if(stackTraceElements.length>3) return 1;"
                + "         else return 0;"
                + "     }"
                + "}");
    pw.close();
    Iterable<? extends JavaFileObject> fO = sjfm.getJavaFileObjects(jf);
    if(!jc.getTask(null,sjfm,null,null,null,fO).call()) {
        throw new Exception("compilation failed");
    }
    URL[] urls = new URL[]{new File("").toURI().toURL()};
    URLClassLoader ucl = new URLClassLoader(urls);
    Object o= ucl.loadClass("FuncOFuncOFunc").newInstance();
    o.getClass().getMethod("main").invoke(o);
    ucl.close();
}
}
Narmer
sumber
Saya tidak percaya Anda dapat memaksa penggunaan untuk meminta reset()setelah setiap functiondoa. Gagasan fungsi tambahan adalah untuk memungkinkan Anda menggunakan metode pribadi lainnya dari dalam functiontubuh ... Tapi itu hanya interpretasi saya dari pertanyaan, mari serahkan kepada penanya untuk memutuskan.
Yakub
Saya memiliki perasaan yang sama ... Mari kita tunggu OP untuk memperjelas poin ini. Sementara itu saya bekerja untuk reset()versi yang kurang. Masih kode di atas berfungsi jika hanya ada satu panggilan di main (tanpa jumlah variabel dan fungsi reset)
Narmer
1
Maaf tentang kurangnya kejelasan, tapi saya maksudkan apa yang dikatakan Yakub dengan fungsi tambahan. Juga, kode 1 yang Anda tulis akan mengembalikan "true" jika ada metode, bukan hanya function (), yang disebut inside function (...), bukan?
Hasil edit Anda harus merupakan kode terburuk yang pernah saya lihat. Punya +1.
Cruncher
Ahaha! Terima kasih! Itulah yang terjadi ketika mencoba menjadi kreatif tanpa kreativitas dan waktu yang sangat sedikit ... Itu mungkin hal terburuk yang pernah saya buat di java. Masih mengkompilasi!
Narmer
0

Python

import ast, inspect

def iscall(node, name, lineno=None):
    return isinstance(node, ast.Call) \
            and (node.lineno == lineno or lineno is None) \
            and hasattr(node.func, 'id') \
            and node.func.id == name

def find_call(node, name, lineno):
    for node in ast.walk(node):
        if iscall(node, name, lineno):
            return node

def is_call_in_args(call):
    descendants = ast.walk(call);
    name = next(descendants).func.id
    return any(map(lambda node: iscall(node, name), descendants))

def function(*args):
    this = inspect.currentframe()
    _, _, funcname, _, _ = inspect.getframeinfo(this)
    outer = inspect.getouterframes(this)[1]
    frame, filename, linenum, _, context, cindex = outer
    module = ast.parse(open(filename).read())
    call = find_call(module, funcname, linenum)
    return is_call_in_args(call)

if __name__ == '__main__':

    print("Works with these:")
    assert(function() == False)
    assert(function(3*function(1)+2) == True)
    assert(function(0) == False)
    assert(function(function(0)) == True)
    assert(function(True or function(1) == True))

    print("Does not work with multiple expressions on the same line:")
    assert(function(function()) == False); function()
    function(); assert(function(function()) == False)

Simpan sebagai selfarg.pydan jalankan atau from selfarg import functiondalam skrip lain. Itu tidak bekerja di repl.

Menggunakan frame stack saat ini dan luar functionmendapatkan nama dan tempat panggilan (file dan nomor saluran). Ini kemudian membuka file yang diperoleh dan mem-parsingnya menjadi pohon sintaksis abstrak. Itu melompati panggilan fungsi yang diidentifikasi oleh nomor baris dan memeriksa apakah ada panggilan fungsi lain dengan nama yang sama dalam argumennya.

sunting : Tidak apa-apa dengan python2 juga. Mengubah python3 menjadi python dalam judul.

pgy
sumber