Función / macro que devuelve verdadero si uno de sus argumentos contiene una llamada a sí mismo

7

Escriba una función (o macro) que devuelva verdadero si y solo si al menos uno de sus argumentos contiene una llamada a la función misma y falso en caso contrario .

Por ejemplo:

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) 

EDITAR: La función / macro puede llamar a otras funciones / macros auxiliares.

EDIT 2: La función debe tomar al menos un argumento, a menos que el lenguaje utilizado se comporte como C, donde una función que no toma argumentos aún puede ser invocada con argumentos.


fuente
¿Tiene que funcionar en un contexto como print(func(), func(func())), o solo habrá una llamada de alto nivel a la función justo después de que se haya definido?
algorithmshark
Debería funcionar en esa situación.
10
¿Por qué aceptar tan rápido?
Kyle Kanos
1
No me gusta esta pregunta porque solo se puede resolver en lenguajes que le permiten examinar el árbol de expresiones en tiempo de ejecución.
FUZxxl
1
@FUZxxl: no estoy de acuerdo; Acabo de publicar una respuesta que funciona bien incluso si las llamadas pasan indirectamente a través de un FFI (y, por lo tanto, no se puede ver el árbol de expresión de las funciones interiores).

Respuestas:

11

Mathematica ...

... fue hecho para esto.

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 *)

Todo es una expresión, y una expresión tiene una Cabeza y varios elementos. Así 1+2es en realidad Plus[1,2], y {1,2}es en realidad List[1,2]. Esto significa que podemos hacer coincidir cualquier expresión para la cabeza que nos interesa, en este caso, la función en fsí.

Todo lo que necesitamos hacer es encontrar una manera de evitar que Mathematica evalúe los argumentos de la función antes de que se llame a la función, de modo que podamos analizar el árbol de expresión dentro de la función. Para qué es Holdy HoldAllpara qué sirve. Mathematica lo usa en todas partes para proporcionar casos especiales para ciertas implementaciones. Por ejemplo, si lo llama Length[Permutations[list]], nunca creará todas esas permutaciones y desperdiciará un montón de memoria, sino que se dará cuenta de que simplemente puede calcular esto como Length[list]!.

Veamos el código anterior en detalle, usando la última llamada f[True || f[1]]como ejemplo. Normalmente, Mathematica evaluará primero los argumentos de la función, por lo que esto simplemente provocaría un cortocircuito y una llamada f[True]. Sin embargo hemos establecido

SetAttributes[f, HoldAll]

Esto le indica a Mathematica que no evalúe los argumentos, por lo que FullFormla llamada (es decir, el árbol de expresión interna, sin ningún azúcar sintáctico) es

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

Y el argumento será realmente recibido de festa forma. El siguiente problema es que, dentro f, tan pronto como usemos el argumento, Mathematica intentará nuevamente evaluarlo. Podemos suprimir esto localmente con Hold@x(azúcar sintáctico para Hold[x]). En este punto, tenemos un control sobre el árbol de expresión original y hacemos con él lo que queramos.

Para buscar un patrón en un árbol de expresión podemos usar FreeQ. Comprueba que un patrón dado no se encuentra en el árbol de expresión. Usamos el patrón _f, que coincide con cualquier subexpresión con head f(exactamente lo que estamos buscando). Por supuesto, FreeQdevuelve lo contrario de lo que queremos, por lo que negamos el resultado.

Una sutileza más: he especificado el argumento como x___, que es una secuencia de 0 o más elementos. Esto asegura que ffuncione con cualquier número de argumentos. En la primera llamada, f[]esto significa que Hold@xsimplemente se convertirá Hold[]. Si hubiera múltiples argumentos, como f[0,f[1]], entonces Hold@xsería Hold[0,f[1]].

Eso es realmente todo lo que hay que hacer.

Martin Ender
fuente
6

C ++ 11

Similar a las plantillas de expresión, podemos propagar el hecho de que llamamos a la función dentro de una lista de parámetros de funciones en su tipo de retorno.

Al principio necesitamos algunas clases y funciones auxiliares:

#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!

Ahora, podemos usarlo exactamente para el mismo código que en la pregunta:

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";
}

Salida de ideone:

0//a==false
0//b==false
1//c==true
1//d==true (weakly-typed language)
1//e==true (strongly-typed language)
Nadie
fuente
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);
    }
}

Uso (en LINQPad ):

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

El truco aquí es hacer fmás conscientes de los parámetros, pasando un tipo personalizado ( PcgBool) que es como un booleano.

PD: Espero que devolver un tipo personalizado que se pueda convertir implícitamente en bool no se considere trampa. Técnicamente, puede usar el valor de retorno como si fuera del tipo bool, y la pregunta se hizo para "devolver verdadero si y solo si", etc., pero nunca dijo que el tipo de retorno debe ser bool.

Jacob
fuente
¿Qué hay de f(new PcgBool())?
Qwertiy
Hmm ... Tienes razón en eso. Parece que frena mi respuesta. Desafortunadamente, soy demasiado flojo para arreglarlo por ahora (ha pasado un tiempo ...)
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
fuente
Y estaba pensando en una forma de comparar tablas de forma recursiva. Bien hecho, ¡no pensé que la depuración pudiera ser tan poderosa!
yyny
3

C ++ 11 TMP

Este es un poco más largo. Debido a algunas limitaciones en las plantillas de C ++, tuve que hacer todo con tipos. Así, "True" y los números se convirtieron en bool e int. También las operaciones +, - y || se convirtieron en add, mul y or_.

Espero que esto todavía califique como una respuesta válida.

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
fuente
¡Muy agradable! ¿Qué regla hace is_given_fooque se prefiera la segunda definición de la primera?
feersum
Quizás alguien pueda ayudarme, porque no pude encontrar el lugar correcto en el estándar para citar. De todos modos, como el segundo is_given_foo es una especialización de plantilla del primero, siempre se prefiere cuando los parámetros de la plantilla coinciden con el patrón dado, en ese caso cuando el parámetro es una plantilla en sí.
Felix Bytow
2

C

No creo que se pueda hacer con procedimientos, pero siempre podemos (ab) usar macros.

#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)))));
}

Esto produce:

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

Nuestra macro frealiza un seguimiento de la profundidad actual y la profundidad máxima alcanzada desde que se invocó. Si el último es mayor que el primero, entonces se ha llamado recursivamente.

James_pic
fuente
Je Terminamos con más o menos la misma solución más o menos al mismo tiempo.
Arte
@Art LOL. Además, me siento muy tonto ahora, ya que no estaba al tanto del operador de coma (ya que C está en algún lugar alrededor de mi 4to o 5to idioma de elección), que pirateé usando &&y ||. Puedo intentar canjear mi respuesta.
James_pic
Bueno, el operador de coma es solo para una capa adicional de ofuscación. Nunca lo usaría en código normal. Pero en este caso me confundí al tratar de usar operadores lógicos.
Arte el
2

C, 13

#define F(X)0

El argumento nunca se expande, por lo que no puede llamar a ninguna macro. No es más que un montón de texto sin formato. Por lo tanto, la respuesta siempre es falsa.

Feersum
fuente
1
Esta es realmente una respuesta brillante.
FUZxxl
Encontré esto mientras miraba las viejas publicaciones del preprocesador ... en realidad, esto está mal. F(F(0))felizmente evaluará el primer argumento de M, F(0). Ese argumento se expande a 0. Luego evalúa F sin pintura azul en su argumento de 0, dando como resultado 0. La restricción no recursiva no se aplica; es entonces cuando, por ejemplo, tengo #define F(X) Gy #define G F(Y)está en juego; en este caso, al expandir F (0) a G y luego a F (Y), Faparece el token . Como actualmente estoy expandiendo F, F tiene pintura azul en este caso y, por lo tanto, la expansión se detiene en F (Y).
H Walters
@HWalters Wow, tienes razón. ¿Qué opinas de mi nueva explicación?
Feersum
Entonces, parece que ahora estás diciendo que, dado que Xno está en la lista de reemplazo de argumentos, las macros vinculadas a ella nunca se expanden. Si interpretamos eso como una función que se llama, eso significa que las funciones del argumento nunca se llaman. Sí, creo que eso es correcto.
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;
}

Podemos contar la profundidad de recursión en la macro. Luego sobrescribimos el valor de retorno de la macro externa en la macro interna. !!bes normalizar el valor de retorno a un valor booleano. El código preprocesado termina así:

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;
}
Arte
fuente
¿Qué pasa con el caso (ciertamente muy tonto) de printf("%d\n", foo(printf("%d\n", foo(1)))). La llamada interna a foodevuelve 1, pero no llama 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;
}

La macro compara si su argumento comienza con "BLAH (".

kwokkie
fuente
1
No funciona en BLAH(blub(BLAH(0))).
FUZxxl
1

Algol 60

Aquí hay una boolean procedureque hace lo que pide la pregunta (nota: Algol 60 se define en términos de una lista de tokens sin corregir la sintaxis para ellos, el siguiente usa la sintaxis de Marst para representar los tokens individuales que componen el programa):

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;

Verificación

Aquí está el código de prueba que utilicé:

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

Como se esperaba, el resultado es:

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

Explicación

Algol 60 tiene un orden de evaluación diferente al de la mayoría de los idiomas, que tiene una lógica propia, y en realidad es mucho más poderoso y general que el orden de evaluación típico, pero es bastante difícil de entender para los humanos (y también bastante difícil de entender). computadoras para implementar de manera eficiente, por lo que se cambió para Algol 68). Esto permite una solución sin ningún tipo de trampa (el programa no necesita mirar el árbol de análisis ni nada de eso, y a diferencia de casi todas las otras soluciones aquí, esto funcionaría bien si la llamada anidada se realizara a través de un FFI).

También decidí mostrar algunas otras peculiaridades del lenguaje. (Notablemente, los nombres de variables pueden contener espacios en blanco; esto es bastante útil para facilitar la lectura, ya que no pueden contener guiones bajos. También me encanta el hecho de que el indicador de comentario es la palabra literal commenten la mayoría de las codificaciones de sintaxis. Algol 68 encontró esto bastante incómodo para abreviar) comentarios e introducidos ¢como una alternativa. Las citas alrededor del cuerpo del comentario normalmente no son necesarias, solo las agrego para mayor claridad y para evitar que el comentario finalice accidentalmente cuando escribo un punto y coma.) Realmente me gustan los conceptos generales del lenguaje (si no los detalles), pero es tan detallado que rara vez lo uso en PPCG.

La forma principal en que Algol 60 difiere de los lenguajes que inspiró (como Algol 68 e indirectamente C, Java, etc.; las personas que conocen K&R C probablemente reconocerán esta sintaxis para las funciones) es que un argumento de función se trata un poco como una pequeña lambda propia; por ejemplo, si le das el argumento 5a una función que es solo el número 5, pero si le das el argumento x+1obtienes exactamente lo que especificaste, el concepto de " xmás 1", en lugar del resultado de xmás 1. La diferencia aquí es que si xcambia, entonces los intentos de evaluar el argumento de la función en cuestión verán el nuevo valor dex. Si un argumento de función no se evalúa dentro de la función, tampoco se evaluará fuera de la función; Del mismo modo, si se evalúa varias veces dentro de la función, se evaluará por separado cada vez (suponiendo que esto no se pueda optimizar). Esto significa que es posible hacer cosas como capturar la funcionalidad de, por ejemplo, ifo whileen una función.

En este programa, estamos explotando el hecho de que si una llamada a una función aparece en un argumento de esa función, eso significa que la función se ejecutará de forma recursiva (porque el argumento se evalúa exactamente en el punto o puntos en los que la función lo evalúa). , no antes ni después, y esto necesariamente debe estar dentro del cuerpo de la función). Esto reduce el problema de detectar si la función se ejecuta de forma recursiva, lo cual es mucho más fácil; todo lo que necesita es una variable local de subprocesos que detecte si hay una llamada recursiva (más, en este caso, otra para comunicar información de otra manera). Podemos usar una variable estática (es decirown) para este propósito, porque Algol 60 es de un solo subproceso. Todo lo que tenemos que hacer después de eso es volver a poner todo como estaba, para que la función funcione correctamente si se llama varias veces (como lo requieren las reglas PPCG).

La función no devuelve el valor deseado de las llamadas internas en este momento (al menos si asume que deberían buscar auto-llamadas solo en sus argumentos, en lugar de contarlas); hacer que el trabajo sea bastante fácil usando los mismos principios generales, pero más complejo y oscurecería el funcionamiento de la función. Si se considera necesario cumplir con la pregunta, no debería ser demasiado difícil de cambiar.


fuente
0

Java

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;
}
}

se reset()puede contar como auxiliar?

Salida:

First
0
Second
0
Third
1
Fourth
1
Fifth
1

EDITAR

Aquí hay otra versión que no usa el reset()método, pero mucha locura. Crea y compila en tiempo de ejecución el código anterior con la llamada a la función pasada como argumento en stdin. Quería una solución más elegante, pero lamentablemente no tengo mucho tiempo para esto :(

Para ejecutarlo simplemente llame por ejemplo 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
fuente
No creo que pueda forzar el uso para invocar reset()después de cada functioninvocación. La idea de las funciones auxiliares es permitirle invocar otros métodos privados desde el interior functiondel cuerpo ... Pero esa es solo mi interpretación de la pregunta, dejemos que el autor de la decisión decida.
Jacob
Tengo el mismo sentimiento ... Esperemos a que el OP aclare este punto. Mientras tanto, estoy trabajando para una reset()versión menos. Aún así, el código anterior funciona si solo hay una llamada en el principal (sin el recuento variable y la función de reinicio)
Narmer
1
Perdón por la falta de claridad, pero quise decir lo que Jacob dijo por función auxiliar. Además, el primer código que escribió devolverá "verdadero" si algún método, no solo la función (), se llama dentro de la función (...), ¿no?
Tu edición tiene que ser uno de los peores códigos que he visto. Tener un +1.
Cruncher
Jajaja ¡Gracias! Eso es lo que sucede cuando intento ser creativo sin creatividad y con muy poco tiempo ... Eso es probablemente lo peor que he hecho en Java. Sin embargo, todavía compila!
Narmer
0

Pitón

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)

Guárdelo como selfarg.pyy ejecútelo o from selfarg import functionen otro script. No funciona en la respuesta.

El uso de los marcos de pila actuales y externos functionobtiene su nombre y lugar de llamada (número de archivo y línea). Luego abre el archivo obtenido y lo analiza en un árbol de sintaxis abstracta. Salta a la llamada de función identificada por el número de línea e inspecciona si tiene otra llamada de función con el mismo nombre en sus argumentos.

editar : también está bien con python2. Cambió python3 a python en el título.

pgy
fuente