Função / macro que retorna true se um de seus argumentos contiver uma chamada para si mesmo

7

Escreva uma função (ou macro) que retorne true se e somente se pelo menos um de seus argumentos contiver uma chamada para a própria função e false caso contrário .

Por exemplo:

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: A função / macro pode chamar outras funções / macros auxiliares.

EDIT 2: A função deve levar pelo menos um argumento, a menos que a linguagem usada se comporte como C, onde uma função que não aceita argumentos ainda pode ser chamada com argumentos.


fonte
Ele precisa funcionar em um contexto como print(func(), func(func())), ou apenas haverá uma chamada de nível superior para a função logo após sua definição?
algorithmshark
Deve funcionar nessa situação.
10
Por que aceitar tão rapidamente?
Kyle Kanos
1
Não gosto dessa pergunta, porque ela só pode ser solucionada em linguagens que permitem examinar a árvore de expressões em tempo de execução.
FUZxxl
1
@FUZxxl: eu discordo; Acabei de postar uma resposta que funciona muito bem, mesmo que as chamadas passem indiretamente por um FFI (e, portanto, a árvore de expressão das funções interiores não pode ser visualizada).

Respostas:

11

Mathematica ...

... foi feito para isso.

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

Tudo é uma expressão, e uma expressão tem uma Cabeça e vários elementos. Então 1+2é realmente Plus[1,2], e {1,2}é realmente List[1,2]. Isso significa que podemos corresponder a qualquer expressão para a cabeça em que estamos interessados ​​- nesse caso, a fprópria função .

Tudo o que precisamos fazer é encontrar uma maneira de impedir que o Mathematica avalie os argumentos da função antes que a função seja chamada, para que possamos analisar a árvore de expressão na função. O que é o que Holde HoldAllpara que serve. O próprio Mathematica usa isso em todo o lugar para fornecer casos especiais para determinadas implementações. Por exemplo, se você chamar, Length[Permutations[list]]ele nunca criará todas essas permutações e desperdiçará muita memória, mas, em vez disso, perceberá que ele pode simplesmente calcular isso como Length[list]!.

Vejamos o código acima em detalhes, usando a última chamada f[True || f[1]]como exemplo. Normalmente, o Mathematica avaliará primeiro os argumentos das funções, então isso simplesmente causaria um curto-circuito e a chamada f[True]. No entanto, definimos

SetAttributes[f, HoldAll]

Isso instrui o Mathematica a não avaliar os argumentos; portanto, FullForma chamada (ou seja, a árvore de expressão interna, sem nenhum açúcar sintático) é

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

E o argumento será realmente recebido por fneste formulário. A próxima questão é que, dentro de f, assim que usarmos o argumento, o Mathematica tentará novamente avaliá-lo. Podemos suprimir isso localmente com Hold@x(açúcar sintático para Hold[x]). Neste ponto, temos um identificador na árvore de expressão original e fazemos com ela o que quisermos.

Para procurar um padrão em uma árvore de expressão, podemos usar FreeQ. Ele verifica se um determinado padrão não foi encontrado na árvore de expressões. Usamos o padrão _f, que corresponde a qualquer subexpressão com head f(exatamente o que estamos procurando). Obviamente, FreeQretorna o oposto do que queremos, então negamos o resultado.

Mais uma sutileza: especifiquei o argumento como x___, que é uma sequência de 0 ou mais elementos. Isso garante que ffuncione com qualquer número de argumentos. Na primeira chamada, f[]isso significa que Hold@xsimplesmente se tornará Hold[]. Se houvesse vários argumentos, como f[0,f[1]], então Hold@xseria Hold[0,f[1]].

Isso é realmente tudo o que existe.

Martin Ender
fonte
6

C ++ 11

Semelhante aos modelos de expressão, podemos propagar o fato de termos chamado a função dentro de uma lista de parâmetros de funções em seu tipo de retorno.

Inicialmente, precisamos de algumas classes e funções 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!

Agora, podemos usá-lo para exatamente o mesmo código da pergunta:

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

Saída do ideone:

0//a==false
0//b==false
1//c==true
1//d==true (weakly-typed language)
1//e==true (strongly-typed language)
Ninguém
fonte
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 (no LINQPad ):

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

O truque aqui é tornar fmais ciente dos parâmetros, passando um tipo personalizado ( PcgBool) que é como um booleano.

PS Espero que o retorno de um tipo personalizado que seja implicitamente convertível de e para um bool não seja considerado trapaça. Tecnicamente, você pode usar o valor de retorno como se fosse do tipo booleano, e a pergunta solicitada "retornará true se e somente se" etc. etc., mas nunca afirmou que o tipo de retorno deve ser booleano.

Jacob
fonte
Que tal f(new PcgBool())?
26515 Qwertiy
Hmm ... Você está certo sobre isso. Parece que freia minha resposta. Infelizmente, eu sou muito preguiçoso para corrigi-lo por agora (já faz um tempo ...)
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
fonte
E eu estava pensando em uma maneira de comparar tabelas recursivamente. Bem feito, não achei que a depuração pudesse ser tão poderosa!
yyny
3

C ++ 11 TMP

Este é um pouco mais longo. Por causa de algumas limitações nos modelos C ++, tive que fazer tudo com os tipos. Assim, "True", assim como os números, foram transformados em bool e int. Também as operações +, - e || foram transformados em add, mul e or_.

Espero que isso ainda se qualifique como uma resposta 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
fonte
Muito bom! Qual regra faz com que a segunda definição is_given_fooseja preferida à primeira?
feersum
Talvez alguém possa me ajudar, porque não consegui encontrar o lugar certo no padrão para citar. De qualquer forma, como o segundo is_given_foo é uma especialização de modelo do primeiro, é sempre preferível quando o (s) parâmetro (s) do modelo correspondem ao padrão fornecido, nesse caso, quando o parâmetro é o próprio modelo.
Felix Bytow 26/02
2

C

Eu não acho que isso possa ser feito com procedimentos, mas sempre 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)))));
}

Isso gera:

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

Nossa macro frastreia a profundidade atual e a profundidade máxima alcançada desde que foi invocada. Se o último for maior que o anterior, será chamado recursivamente.

James_pic
fonte
Heh. Acabamos com mais ou menos a mesma solução, mais ou menos ao mesmo tempo.
Art
@Art LOL. Além disso, agora me sinto muito boba, pois não estava ciente do operador de vírgula (já que C está em algum lugar em torno da minha 4ª ou 5ª linguagem de escolha), que usei &&e ||. Eu posso tentar resgatar minha resposta.
James_pic
Bem, o operador de vírgula é apenas para uma camada extra de ofuscação. Eu nunca o usaria no código normal. Mas, neste caso, me confundi ao tentar usar operadores lógicos.
Art
2

C, 13

#define F(X)0

O argumento nunca é expandido, portanto, não pode chamar macros. Não passa de um monte de texto simples. Assim, a resposta é sempre falsa.

feersum
fonte
1
Esta é realmente uma resposta brilhante.
FUZxxl
Me deparei com isso enquanto olhava para as postagens antigas do pré-processador ... na verdade, isso está errado. F(F(0))felizmente primeiro avaliará o argumento de F F(0),. Esse argumento se expande para 0. Em seguida, ele avalia F sem tinta azul em seu argumento de 0, resultando em 0. A restrição não recursiva não se aplica; é quando, por exemplo, eu tenho #define F(X) Ge #define G F(Y)está em jogo; no presente caso, durante a expansão F (0) a G, e, em seguida, a F (Y), o token Faparece. Como atualmente estou expandindo F, F tem tinta azul nesse caso e, portanto, a expansão para em F (Y).
H Walters
@HWalters Uau, você está certo. O que você acha da minha nova explicação?
feersum
Parece que agora você está dizendo que, uma vez que Xnão está na lista de substituição de argumentos, as macros associadas a ela nunca são expandidas. Se interpretarmos isso como uma função sendo chamada, isso significa que as funções do argumento nunca são chamadas. Sim, acho que está correto.
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 a profundidade da recursão na macro. Em seguida, substituímos o valor de retorno da macro externa na macro interna. !!bé normalizar o valor de retorno para um booleano. O código pré-processado acaba assim:

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
fonte
E o caso (reconhecidamente muito bobo) de printf("%d\n", foo(printf("%d\n", foo(1)))). A chamada interna para fooretorna 1, mas não chama 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;
}

A macro compara se o argumento começa com "BLAH (".

kwokkie
fonte
1
Não funciona BLAH(blub(BLAH(0))).
FUZxxl
1

Algol 60

Aqui está um boolean procedureque faz o que a pergunta pede (observação: o Algol 60 é definido em termos de uma lista de tokens sem fixar sintaxe para eles; o abaixo usa a sintaxe Marst para representar os tokens individuais que compõem o 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;

Verificação

Aqui está o código de teste que eu usei:

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 esperado, a saída é:

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

Explicação

O Algol 60 tem uma ordem de avaliação diferente da maioria dos idiomas, que possui uma lógica própria, e é realmente muito mais poderosa e geral do que a ordem de avaliação típica, mas é bastante difícil para os humanos entenderem (e também bastante difícil para computadores para implementar eficientemente, e foi por isso que foi alterado para o Algol 68). Isso permite uma solução sem nenhum tipo de trapaça (o programa não precisa olhar para a árvore de análise ou algo parecido e, ao contrário de quase todas as outras soluções aqui, isso funcionaria perfeitamente se a chamada aninhada fosse feita por meio de um FFI).

Também decidi mostrar algumas outras peculiaridades do idioma. (Notavelmente, nomes de variáveis ​​podem conter espaços em branco; isso é bastante útil para facilitar a leitura, porque não podem conter sublinhados. Também adoro o fato de o indicador de comentário ser a palavra literal commentna maioria das codificações de sintaxe. Algol 68 achou isso bastante estranho comentários e introduzidos ¢como uma alternativa. Normalmente, as citações no corpo do comentário não são necessárias, apenas as adiciono para maior clareza e para impedir que o comentário termine acidentalmente quando eu digito um ponto-e-vírgula.) Na verdade, eu realmente gosto dos conceitos gerais do idioma (se não os detalhes), mas é tão detalhado que raramente uso no PPCG.

A principal maneira pela qual o Algol 60 difere das linguagens que ele inspirou (como o Algol 68 e indiretamente C, Java, etc; pessoas que conhecem o K&R C provavelmente reconhecerão essa sintaxe para funções) é que um argumento de função é tratado um pouco como uma pequena lambda própria; por exemplo, se você der o argumento 5para uma função que é apenas o número 5, mas se você der o argumento, x+1obterá exatamente o que especificou, o conceito de " xmais 1", em vez do resultado de xmais 1. A diferença aqui é que, se houver xalterações, as tentativas de avaliar o argumento da função em questão verão o novo valor dex. Se um argumento da função não for avaliado dentro da função, também não será avaliado fora da função; da mesma forma, se for avaliado várias vezes dentro da função, será avaliado separadamente a cada vez (assumindo que isso não pode ser otimizado). Isso significa que é possível fazer coisas como capturar a funcionalidade de, digamos, ifou whileem uma função.

Neste programa, estamos explorando o fato de que, se uma chamada para uma função aparecer em um argumento para essa função, isso significa que a função será executada recursivamente (porque o argumento é avaliado exatamente no ponto ou pontos em que a função o avalia) , não antes ou depois, e isso deve necessariamente estar dentro do corpo da função). Isso reduz o problema de detectar se a função está sendo executada recursivamente, o que é muito mais fácil; tudo o que você precisa é de uma variável local do encadeamento que detecte se há uma chamada recursiva (além disso, nesse caso, outra para comunicar informações de outra maneira). Podemos usar uma variável estática (ou seja,own) para esse fim, porque o Algol 60 é de thread único. Tudo o que precisamos fazer depois é colocar tudo de volta do jeito que estava, para que a função funcione corretamente se for chamada várias vezes (conforme exigido pelas regras do PPCG).

A função não retorna o valor desejado das chamadas internas no momento (pelo menos se você assumir que elas devem procurar chamadas próprias apenas em seus argumentos, em vez de se contar); tornar esse trabalho bastante fácil, usando os mesmos princípios gerais, mas mais complexo e obscureceria o funcionamento da função. Se for considerado necessário cumprir a pergunta, não deve ser muito difícil mudar.


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

pode reset()ser contado como auxiliar?

Resultado:

First
0
Second
0
Third
1
Fourth
1
Fifth
1

EDITAR

Aqui está outra versão que não usa o reset()método, mas muita loucura. Ele cria e compila em tempo de execução o código acima com a chamada para a função passada como argumento em stdin. Eu queria uma solução mais elegante, mas infelizmente não tenho muito tempo para isso :(

Para executá-lo, basta chamar, por exemplo 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
fonte
Não acredito que você possa forçar o uso a invocar reset()após cada functioninvocação. A idéia das funções auxiliares é permitir que você invoque outros métodos privados do functioncorpo de alguém ... Mas essa é apenas a minha interpretação da questão, vamos deixar que o solicitante decida.
24414 Jacob
Tenho o mesmo sentimento ... Vamos esperar o OP esclarecer esse ponto. Enquanto isso, estou trabalhando para uma reset()versão menos. Ainda assim, o código acima funciona se houver apenas uma chamada no principal (sem a contagem variável e a função de reset)
Narmer
1
Desculpe a falta de clareza, mas eu quis dizer o que Jacob disse por função auxiliar. Além disso, o primeiro código que você escreveu retornará "true" se algum método, não apenas function (), for chamado de function inside (...), não é?
Sua edição deve ser um dos piores códigos que eu já vi. Tenha um +1.
Cruncher
Ahaha! Obrigado! É o que acontece ao tentar ser criativo sem criatividade e com muito pouco tempo ... Essa é provavelmente a pior coisa que já fiz em java. Ainda compila!
Narmer
0

Pitão

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)

Salve-o como selfarg.pye execute-o ou from selfarg import functionem outro script. Não funciona no repl.

O uso dos quadros de pilha atuais e externos functionobtém seu nome e local de chamada (número de arquivo e linha). Em seguida, ele abre o arquivo obtido e o analisa em uma árvore de sintaxe abstrata. Ele pula para a chamada de função identificada pelo número da linha e verifica se há outra chamada de função com o mesmo nome em seus argumentos.

edit : Tudo bem com python2. Python3 alterado para python no título.

pgy
fonte