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.
print(func(), func(func()))
, ou apenas haverá uma chamada de nível superior para a função logo após sua definição?Respostas:
Mathematica ...
... foi feito para isso.
Tudo é uma expressão, e uma expressão tem uma Cabeça e vários elementos. Então
1+2
é realmentePlus[1,2]
, e{1,2}
é realmenteList[1,2]
. Isso significa que podemos corresponder a qualquer expressão para a cabeça em que estamos interessados - nesse caso, af
pró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
Hold
eHoldAll
para 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 comoLength[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 chamadaf[True]
. No entanto, definimosIsso instrui o Mathematica a não avaliar os argumentos; portanto,
FullForm
a chamada (ou seja, a árvore de expressão interna, sem nenhum açúcar sintático) éE o argumento será realmente recebido por
f
neste formulário. A próxima questão é que, dentro def
, assim que usarmos o argumento, o Mathematica tentará novamente avaliá-lo. Podemos suprimir isso localmente comHold@x
(açúcar sintático paraHold[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 headf
(exatamente o que estamos procurando). Obviamente,FreeQ
retorna 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 quef
funcione com qualquer número de argumentos. Na primeira chamada,f[]
isso significa queHold@x
simplesmente se tornaráHold[]
. Se houvesse vários argumentos, comof[0,f[1]]
, entãoHold@x
seriaHold[0,f[1]]
.Isso é realmente tudo o que existe.
fonte
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:
Agora, podemos usá-lo para exatamente o mesmo código da pergunta:
Saída do ideone:
fonte
C #
Uso (no LINQPad ):
O truque aqui é tornar
f
mais 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.
fonte
f(new PcgBool())
?Lua
fonte
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.
fonte
is_given_foo
seja preferida à primeira?C
Eu não acho que isso possa ser feito com procedimentos, mas sempre podemos (ab) usar macros.
Isso gera:
Nossa macro
f
rastreia 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.fonte
&&
e||
. Eu posso tentar resgatar minha resposta.C, 13
O argumento nunca é expandido, portanto, não pode chamar macros. Não passa de um monte de texto simples. Assim, a resposta é sempre falsa.
fonte
F(F(0))
felizmente primeiro avaliará o argumento de FF(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) G
e#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 tokenF
aparece. Como atualmente estou expandindo F, F tem tinta azul nesse caso e, portanto, a expansão para em F (Y).X
nã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.C
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:fonte
printf("%d\n", foo(printf("%d\n", foo(1))))
. A chamada interna parafoo
retorna 1, mas não chamafoo
.C
A macro compara se o argumento começa com "BLAH (".
fonte
BLAH(blub(BLAH(0)))
.Algol 60
Aqui está um
boolean procedure
que 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):Verificação
Aqui está o código de teste que eu usei:
Como esperado, a saída é:
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
comment
na 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
5
para uma função que é apenas o número 5, mas se você der o argumento,x+1
obterá exatamente o que especificou, o conceito de "x
mais 1", em vez do resultado dex
mais 1. A diferença aqui é que, se houverx
alteraçõ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,if
ouwhile
em 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
Java
pode
reset()
ser contado como auxiliar?Resultado:
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)
.fonte
reset()
após cadafunction
invocação. A idéia das funções auxiliares é permitir que você invoque outros métodos privados dofunction
corpo de alguém ... Mas essa é apenas a minha interpretação da questão, vamos deixar que o solicitante decida.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)Pitão
Salve-o como
selfarg.py
e execute-o oufrom selfarg import function
em outro script. Não funciona no repl.O uso dos quadros de pilha atuais e externos
function
obté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.
fonte