Recentemente, deparei-me com um problema que exigia que eu definisse o operador lógico "OR" programaticamente, mas sem usar o próprio operador.
O que eu inventei é o seguinte:
OR(arg1, arg2)
if arg1 = True and arg2 = True
return True
else if arg1 = True and arg2 = False
return True
else if arg1 = False and arg2 = True
return True
else:
return False
Essa lógica está correta ou eu perdi alguma coisa?
programming-logic
logicNoob
fonte
fonte
return arg1 ? arg1 : arg2;
or
operador.Respostas:
Eu diria que está correto, mas você não poderia condensar isso em algo como isso?
Como você está fazendo uma comparação ou não, acho que não precisa verificar a combinação. É importante apenas se um deles é verdadeiro para retornar verdadeiro. Caso contrário, queremos retornar false.
Se você estiver procurando por uma versão mais curta e menos detalhada, isso também funcionará:
fonte
if arg2 == true
).or(a, b): a ? a : b
Aqui está uma solução sem ou, e, não, comparações e literais booleanos:
Provavelmente não é muito mais fundamental que isso;)
fonte
true
oufalse
.||
Operador JavaScript em poucas palavras (quando implementado em um idioma digitado dinamicamente).Uma linha de código:
Sem ramificação, sem OR.
Em uma linguagem baseada em C, seria:
Isso é simplesmente uma aplicação das leis de De Morgan :
(A || B) == !(!A && !B)
fonte
if/else
construto é o mesmo que usar OR, apenas com um nome diferente.if
é equivalente a igualdade. Normalmente, no código de máquina, umif
é implementado como aritmético, seguido de uma comparação com zero com um salto.and
, proporcionando assim consistência entre os operadores.if (a) return true; else if (b) return true;
parece mais ou menos moralmente equivalenteif (a OR b) return true;
, mas essa visão pode muito bem estar aberta a disputas.Se você tiver apenas
and
enot
, poderá usar a lei de DeMorgan para mudarand
:... ou (ainda mais simples)
...
E como todos nós aparentemente nos concentramos em otimizar algo que quase sempre está disponível como uma instrução de máquina, isso se resume a:
etc etc etc etc.
Como a maioria dos idiomas fornece um condicional e, as chances são de que o operador "and" implica uma ramificação de qualquer maneira.
...
Se tudo o que você tem é
nand
(consulte a Wikipedia ):retornar nand (nand (arg1, arg1), nand (arg2, arg2))
fonte
return not (not arg1 and not arg2)
nand
solução pura .Funções (ECMAScript)
Tudo o que você precisa são definições de funções e chamadas de função. Você não precisa de nenhuma ramificação, condicionais, operadores ou funções internas. Vou demonstrar uma implementação usando ECMAScript.
Primeiro, vamos definir duas funções chamadas
true
efalse
. Poderíamos defini-los da maneira que quisermos, são completamente arbitrários, mas os definiremos de uma maneira muito especial, com algumas vantagens, como veremos mais adiante:tru
é uma função com dois parâmetros que simplesmente ignora seu segundo argumento e retorna o primeiro.fls
também é uma função com dois parâmetros que simplesmente ignora seu primeiro argumento e retorna o segundo.Por que codificamos
tru
efls
dessa maneira? Bem, assim, as duas funções não apenas representam os dois conceitos detrue
efalse
, não, ao mesmo tempo, também representam o conceito de "escolha", ou seja, são também uma expressãoif
/then
/else
! Avaliamos aif
condição e passamos othen
bloco e oelse
bloco como argumentos. Se a condição for avaliada comotru
, ela retornará othen
bloco; se avaliarfls
, retornará oelse
bloco. Aqui está um exemplo:Isso retorna
23
, e isso:retorna
42
, exatamente como você esperaria.Há uma ruga, no entanto:
Isso imprime ambos
then branch
eelse branch
! Por quê?Bem, ele retorna o valor de retorno do primeiro argumento, mas avalia os dois argumentos, já que ECMAScript é rigoroso e sempre avalia todos os argumentos de uma função antes de chamar a função. IOW: avalia o primeiro argumento que é
console.log("then branch")
, que simplesmente retornaundefined
e tem o efeito colateral de impressãothen branch
no console, e avalia o segundo argumento, que também retornaundefined
e imprime no console como efeito colateral. Então, ele retorna o primeiroundefined
.No cálculo λ, onde essa codificação foi inventada, isso não é um problema: o cálculo λ é puro , o que significa que não tem efeitos colaterais; portanto, você nunca notaria que o segundo argumento também é avaliado. Além disso, o cálculo λ é preguiçoso (ou pelo menos, é frequentemente avaliado em ordem normal), ou seja, na verdade, ele não avalia argumentos que não são necessários. Então, IOW: no cálculo λ, o segundo argumento nunca seria avaliado e, se fosse, não perceberíamos.
O ECMAScript, no entanto, é rigoroso , ou seja, sempre avalia todos os argumentos. Bem, na verdade, nem sempre: o
if
/then
/else
, por exemplo, avalia apenas athen
ramificação se a condição fortrue
e só avalia aelse
ramificação se a condição forfalse
. E queremos replicar esse comportamento com nossosiff
. Felizmente, mesmo que o ECMAScript não seja preguiçoso, ele tem uma maneira de atrasar a avaliação de um pedaço de código, da mesma forma que quase todas as outras línguas: envolva-o em uma função e, se você nunca chamar essa função, o código será nunca seja executado.Então, envolvemos os dois blocos em uma função e, no final, chamamos a função que é retornada:
impressões
then branch
eimpressões
else branch
.Poderíamos implementar o tradicional
if
/then
/else
desta maneira:Novamente, precisamos de um agrupamento extra de funções ao chamar a
iff
função e os parênteses da chamada de função extra na definição deiff
, pelo mesmo motivo que acima:Agora que temos essas duas definições, podemos implementar
or
. Primeiro, olhamos para a tabela verdadeor
: se o primeiro operando é verdadeiro, então o resultado da expressão é o mesmo que o primeiro operando. Caso contrário, o resultado da expressão é o resultado do segundo operando. Resumindo: se o primeiro operando fortrue
, retornamos o primeiro operando, caso contrário, retornamos o segundo operando:Vamos verificar se funciona:
Ótimo! No entanto, essa definição parece um pouco feia. Lembre-se,
tru
efls
já agem como condicionais sozinhos; portanto, realmente não há necessidadeiff
e, portanto, toda essa função de empacotamento:Aí está:
or
(além de outros operadores booleanos) definidos com nada além de definições de funções e chamadas de funções em apenas algumas linhas:Infelizmente, essa implementação é bastante inútil: não há funções ou operadores no ECMAScript que retornem
tru
oufls
, todos retornamtrue
oufalse
, portanto, não podemos usá-los com nossas funções. Mas ainda há muito que podemos fazer. Por exemplo, esta é uma implementação de uma lista vinculada individualmente:Objetos (Scala)
Você deve ter notado algo peculiar:
tru
efls
desempenha um papel duplo, eles agem como valores dos dadostrue
efalse
, ao mesmo tempo, também atuam como expressão condicional. São dados e comportamento , agrupados em um ... uhm ... "coisa" ... ou (ouso dizer) objeto !De fato,
tru
efls
são objetos. E, se você já usou Smalltalk, Self, Newspeak ou outras linguagens orientadas a objetos, notou que elas implementam booleanos exatamente da mesma maneira. Vou demonstrar essa implementação aqui em Scala:É por isso que a Substituição da refatoração condicional por polimorfismo sempre funciona: você sempre pode substituir todo e qualquer condicional em seu programa pelo envio polimórfico de mensagens, porque, como mostramos, o envio polimórfico de mensagens pode substituir condicionais simplesmente implementando-os. Idiomas como Smalltalk, Self e Newspeak são a prova da existência disso, porque esses idiomas nem têm condicionais. (Eles também não têm loops, BTW ou realmente qualquer tipo de estrutura de controle interna de linguagem, exceto para envio de mensagens polimórficas, também chamadas de métodos virtuais.)
Correspondência de padrões (Haskell)
Você também pode definir o
or
uso de correspondência de padrões ou algo como as definições de funções parciais de Haskell:Obviamente, a correspondência de padrões é uma forma de execução condicional, mas também o envio de mensagens orientadas a objetos.
fonte
False ||| False = False
e em_ ||| _ = True
vez disso? :)True ||| undefined
se em ghci para ver!Aqui está outra maneira de definir OU, ou mesmo qualquer operador lógico, usando a maneira mais tradicional de defini-lo: use uma tabela verdade.
Obviamente, isso é bastante trivial em linguagens de nível superior, como Javascript ou Perl, mas estou escrevendo este exemplo em C para mostrar que a técnica não depende de recursos de linguagem de alto nível:
Você pode fazer o mesmo com AND, NOR, NAND, NOT e XOR. O código é limpo o suficiente para parecer com sintaxe, de modo que você pode fazer coisas assim:
fonte
BinaryOperator or = new TruthTableBasedBinaryOperator(new TruthTable(false, true, true, true));
Outra maneira de expressar os operadores lógicos como expressões aritméticas inteiras (sempre que possível). Dessa forma, pode-se evitar muitas ramificações para uma expressão maior de muitos predicados.
Let True seja 1 Let False seja 0
se o somatório de ambos for maior que 1, será verdadeiro ou falso ser retornado.
fonte
booleanExpression ? true : false
é trivialmente igual abooleanExpression
.return (arga+argb)>0
return (((arg1 ? 1 : 0)+(arg2 ? 1 : 0)) > 0);
:)arg1 ? 1 : 0;
. Essas são expressões confiáveis para transformar um booleano em um número. É apenas a declaração de retorno que pode ser trivialmente refatorada.As duas formas:
OU
Tenha a vantagem do código do golfe de ser um pouco menor do que as outras sugestões até agora, um ramo a menos. Nem é tão tolo optar por reduzir o número de ramificações, se estamos considerando a criação de uma primitiva que, portanto, seria muito usada.
A definição de Javascript
||
é semelhante a isso, que combinada com sua digitação solta significa que a expressãofalse || "abc"
tem o valor"abc"
e42 || "abc"
o valor42
.Embora se você já tiver outros operadores lógicos, os gostos
nand(not(arg1), not(arg2))
poderão ter a vantagem de não ramificar.fonte
Além de todas as soluções programadas usando a construção if, é possível construir uma porta OR combinando três portas NAND. Se você quiser ver como isso é feito na wikipedia, clique aqui .
A partir disso, a expressão
que usa NOT e AND dá a mesma resposta que OR. Observe que o uso de NOT e AND é apenas uma maneira obscura de expressar NAND.
fonte
Todas as boas respostas já foram dadas. Mas eu não vou deixar isso me parar.
Alternativamente:
Espero que ninguém realmente use abordagens como essas. Eles estão aqui apenas para promover a conscientização de alternativas.
Atualizar:
Como os números negativos podem quebrar as duas abordagens acima, aqui está outra sugestão terrível:
Isso simplesmente usa as leis de DeMorgan e abusa do fato de que
*
é semelhante a&&
quandotrue
efalse
é tratado como1
e0
respectivamente. (Espere, você está dizendo que isso não é código de golfe?)Aqui está uma resposta decente:
Mas isso é essencialmente idêntico a outras respostas já dadas.
fonte
arg1+arg2
, -1 e 0 paramax(arg1,arg2)
etc.Uma maneira de definir
or
é através de uma tabela de pesquisa. Podemos tornar isso explícito:criamos uma matriz com os valores que o valor de retorno deve ter, dependendo do que
a
é. Então fazemos uma pesquisa. Em linguagens semelhantes a C ++,bool
promove um valor que pode ser usado como um índice de matriz, comtrue
ser1
efalse
ser0
.Podemos então estender isso para outras operações lógicas:
Agora, uma desvantagem de tudo isso é que ela requer notação de prefixo.
e agora você pode digitar
true *OR* false
e funciona.A técnica acima requer uma linguagem que suporte a pesquisa e modelos dependentes de argumento. Você provavelmente poderia fazer isso em um idioma com genéricos e ADL.
Como um aparte, você pode estender o
*OR*
acima para trabalhar com conjuntos. Basta criar uma função livreinvoke
no mesmo espaço para nome queor_tag
:e agora
set *OR* set
retorna a união dos dois.fonte
Este me lembra as funções charasteristic:
Isso se aplica apenas a idiomas que podem tratar booleanos como (1, 0). Não se aplica a Smalltalk ou Python, pois boolean é uma classe. No smalltalk, eles vão ainda mais longe (isso será escrito em uma espécie de pseudocódigo):
E os métodos duplos existem para e:
Portanto, a "lógica" é perfeitamente válida na instrução OP, embora seja detalhada. Cuidado, não é ruim. É perfeito se você precisar de uma função que atue como um operador matemático baseado em, digamos, um tipo de matriz. Outros implementariam um cubo real (como uma instrução Quine-McCluskey):
E você avaliará ou [a] [b]
Portanto, sim, todas as lógicas aqui são válidas (exceto aquela postada como usando o operador OR no idioma xDDDDDDDD).
Mas o meu favorito é a lei de DeMorgan:
!(!a && !b)
fonte
Veja a biblioteca padrão do Swift e confira a implementação das operações de atalho OR e atalho AND, que não avaliam os segundos operandos se não forem necessários / permitidos.
fonte
A lógica está perfeitamente correta, mas pode ser simplificada:
E, presumivelmente, seu idioma possui um operador OR, portanto, a menos que seja contra o espírito da pergunta, por que não
fonte
if arg1 = True or arg2 = True { return true } else { return false }
Melhor aindareturn arg1 = True or arg2 = True
.if condition then true else false
é redundante.