Como definir "ou" logicamente


36

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?


10
@gnat: Para ser justo, uma tabela verdade lista as saídas para cada combinação das entradas e o artigo da Wikipedia fornece uma descrição da função. Eu acho que o OP realmente está perguntando é como definir um OR lógico programaticamente sem o uso do próprio operador.
Blrfl

6
@ user3687688 Você pode esclarecer as primitivas que podemos usar?
Fredoverflow

4
esta questão começou um espasmo coletivo de micro-otimização;)
Rob

8
Você pode usar o operador ternárioreturn arg1 ? arg1 : arg2;
Matthew

4
Eu tenho que saber por que você precisava redefinir o oroperador.
Kyle Strand

Respostas:


102

Eu diria que está correto, mas você não poderia condensar isso em algo como isso?

or(arg1, arg2)
    if arg1 == true
        return true
    if arg2 == true
        return true

    return false

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á:

or(arg1, arg2)
    if arg1
        return arg1
    return arg2

6
Você também pode remover o "else" na linha 4 (deixando apenas if arg2 == true).
Dawson Toth

11
@DawsonToth Há muitas maneiras diferentes de girar, depende se você quer ser detalhado ou condensado. Eu ficaria feliz com o resto se, mas parece que essa é uma questão de pseudo-código, então provavelmente deixaria assim para maior clareza. Muito verdade!
Elliot Blackburn

@BlueHat Parece um pouco inconsistente usar um else if, mas não outro no final.
SBoss

11
@Mehrdad Thanks! Incluí a resposta antiga apenas porque acho que é um pouco mais detalhada e explica a solução um pouco mais clara. Mas sua solução é muito menor e faz o mesmo trabalho.
Elliot Blackburn

11
ainda melhor (pior):or(a, b): a ? a : b
sara

149

Aqui está uma solução sem ou, e, não, comparações e literais booleanos:

or(arg1, arg2)
  if arg1
    return arg1
  else
    return arg2

Provavelmente não é muito mais fundamental que isso;)


32
+1 para uma resposta um pouco mais curta que a minha. No entanto, eu ficaria tentado a largar o "outro" também por elegância.
Elliot Blackburn

10
@BlueHat Mas então os dois retornos seria recuado de forma diferente;)
fredoverflow

5
Gostaria de receber um euro cada vez que alguém compara algo contra trueou false.
perfil completo de JensG

11
@JensG Bem, de onde você acha que a renda de Bill Gates vem?
Kroltan

11
||Operador JavaScript em poucas palavras (quando implementado em um idioma digitado dinamicamente).
Rhino

108

Uma linha de código:

return not (not arg1 and not arg2)

Sem ramificação, sem OR.

Em uma linguagem baseada em C, seria:

return !(!arg1 && !arg2);

Isso é simplesmente uma aplicação das leis de De Morgan :(A || B) == !(!A && !B)


6
Eu acho que essa abordagem é a melhor solução, já que (na minha opinião) um if/elseconstruto é o mesmo que usar OR, apenas com um nome diferente.
Nick

2
@Nick usando ifé equivalente a igualdade. Normalmente, no código de máquina, um ifé implementado como aritmético, seguido de uma comparação com zero com um salto.


11
Eu gosto dessa abordagem porque ela provoca um curto-circuito no IFF and, proporcionando assim consistência entre os operadores.
Kyle Strand

11
@ Snowman Isso é verdade. Eu quis dizer que if (a) return true; else if (b) return true;parece mais ou menos moralmente equivalente if (a OR b) return true;, mas essa visão pode muito bem estar aberta a disputas.
21414 Nick

13

Se você tiver apenas ande not, poderá usar a lei de DeMorgan para mudar and:

if not (arg1 = False and arg2 = False)
  return True
else
  return False

... ou (ainda mais simples)

if arg1 = False and arg2 = False
  return false
else
  return true

...

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:

return not(not arg1 and not arg2)

return arg1 ? true : arg2

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


7
Simplifique:return not (not arg1 and not arg2)

@ Boneco de neve, você realmente deve fazer uma resposta para que eu possa aprovar. Você é (atualmente) o único aqui que não participou da ramificação.
precisa saber é o seguinte

4
Ia adicionar a solução NAND, mas você me venceu. Tudo deve ser implementado em termos de NAND.
Andy

2
@ Andy: Na verdade, tudo deve ser definido em termos de NOR. ;-)
Pieter Geerkens

11
Bom trabalho com a nandsolução pura .
AAT,

13

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 truee false. 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:

const tru = (thn, _  ) => thn,
      fls = (_  , els) => els;

trué uma função com dois parâmetros que simplesmente ignora seu segundo argumento e retorna o primeiro. flstambém é uma função com dois parâmetros que simplesmente ignora seu primeiro argumento e retorna o segundo.

Por que codificamos true flsdessa maneira? Bem, assim, as duas funções não apenas representam os dois conceitos de truee false, não, ao mesmo tempo, também representam o conceito de "escolha", ou seja, são também uma expressão if/ then/ else! Avaliamos a ifcondição e passamos o thenbloco e o elsebloco como argumentos. Se a condição for avaliada como tru, ela retornará o thenbloco; se avaliar fls, retornará o elsebloco. Aqui está um exemplo:

tru(23, 42);
// => 23

Isso retorna 23, e isso:

fls(23, 42);
// => 42

retorna 42, exatamente como você esperaria.

Há uma ruga, no entanto:

tru(console.log("then branch"), console.log("else branch"));
// then branch
// else branch

Isso imprime ambos then branch e else 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 retorna undefinede tem o efeito colateral de impressão then branchno console, e avalia o segundo argumento, que também retorna undefinede 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 a thenramificação se a condição for truee só avalia a elseramificação se a condição for false. 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:

tru(() => console.log("then branch"), () => console.log("else branch"))();
// then branch

impressões then branche

fls(() => console.log("then branch"), () => console.log("else branch"))();
// else branch

impressões else branch.

Poderíamos implementar o tradicional if/ then/ elsedesta maneira:

const iff = (cnd, thn, els) => cnd(thn, els);

iff(tru, 23, 42);
// => 23

iff(fls, 23, 42);
// => 42

Novamente, precisamos de um agrupamento extra de funções ao chamar a ifffunção e os parênteses da chamada de função extra na definição de iff, pelo mesmo motivo que acima:

const iff = (cnd, thn, els) => cnd(thn, els)();

iff(tru, () => console.log("then branch"), () => console.log("else branch"));
// then branch

iff(fls, () => console.log("then branch"), () => console.log("else branch"));
// else branch

Agora que temos essas duas definições, podemos implementar or. Primeiro, olhamos para a tabela verdade or: 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 for true, retornamos o primeiro operando, caso contrário, retornamos o segundo operando:

const orr = (a, b) => iff(a, () => a, () => b);

Vamos verificar se funciona:

orr(tru,tru);
// => tru(thn, _) {}

orr(tru,fls);
// => tru(thn, _) {}

orr(fls,tru);
// => tru(thn, _) {}

orr(fls,fls);
// => fls(_, els) {}

Ótimo! No entanto, essa definição parece um pouco feia. Lembre-se, true flsjá agem como condicionais sozinhos; portanto, realmente não há necessidade iffe, portanto, toda essa função de empacotamento:

const orr = (a, b) => a(a, b);

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:

const tru = (thn, _  ) => thn,
      fls = (_  , els) => els,
      orr = (a  , b  ) => a(a, b),
      nnd = (a  , b  ) => a(b, a),
      ntt = a          => a(fls, tru),
      xor = (a  , b  ) => a(ntt(b), b),
      iff = (cnd, thn, els) => cnd(thn, els)();

Infelizmente, essa implementação é bastante inútil: não há funções ou operadores no ECMAScript que retornem truou fls, todos retornam trueou false, 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:

const cons = (hd, tl) => which => which(hd, tl),
      car  = l => l(tru),
      cdr  = l => l(fls);

Objetos (Scala)

Você deve ter notado algo peculiar: true flsdesempenha um papel duplo, eles agem como valores dos dados truee false, 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, true flssã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:

sealed abstract trait Buul {
  def apply[T, U <: T, V <: T](thn: ⇒ U)(els: ⇒ V): T
  def &&&(other:Buul): Buul
  def |||(other:Buul): Buul
  def ntt: Buul
}

case object Tru extends Buul {
  override def apply[T, U <: T, V <: T](thn: ⇒ U)(els: ⇒ V): U = thn
  override def &&&(other:Buul) = other
  override def |||(other:Buul): this.type = this
  override def ntt = Fls
}

case object Fls extends Buul {
  override def apply[T, U <: T, V <: T](thn: ⇒ U)(els: ⇒ V): V = els
  override def &&&(other:Buul): this.type = this
  override def |||(other:Buul) = other
  override def ntt = Tru
}

object BuulExtension {
  import scala.language.implicitConversions
  implicit def boolean2Buul(b:Boolean) = if (b) Tru else Fls
}

import BuulExtension._

(2 < 3) { println("2 is less than 3") } { println("2 is greater than 3") }
// 2 is less than 3

É 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 oruso de correspondência de padrões ou algo como as definições de funções parciais de Haskell:

True ||| _ = True
_    ||| b = b

Obviamente, a correspondência de padrões é uma forma de execução condicional, mas também o envio de mensagens orientadas a objetos.


2
Que tal False ||| False = Falsee em _ ||| _ = Truevez disso? :)
fredoverflow

3
@FredOverflow: Isso exigiria sempre avaliar o operando certo. Normalmente, espera-se que os operadores booleanos não sejam rigorosos no argumento correto, conhecido como "curto-circuito".
Jörg W Mittag

Ah, claro. Eu sabia que tinha que haver uma razão mais profunda :)
fredoverflow

A primeira parte me lembrou imediatamente da grande série de Eric Lippert sobre o estilo de passagem contínua . Mera coincidência, mas ainda diversão :)
Voo

11
@ JörgWMittag A definição de FredOverflow está apropriadamente em curto-circuito. Tente- True ||| undefinedse em ghci para ver!
Daniel Wagner

3

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:

#include <stdio.h>

int main (void) {
    // Define truth table for OR:
    int OR[2][2] = {
        {0,   // false, false
         1},  // false, true
        {1,   // true, false
         1}   // true, true
    }

    // Let's test the definition
    printf("false || false = %d\n",OR[1==2]['b'=='a']);
    printf("true || false = %d\n",OR[10==10]['b'=='a']);

    // Usage:
    if (OR[ 1==2 ][ 3==4 ]) {
        printf("at least one is true\n");
    }
    else {
        printf("both are false\n");
    }
}

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:

if (OR[ a ][ AND[ b ][ c ] ]) { /* ... */ }

Eu acho que essa é a abordagem "mais pura" em certo sentido matemático. O operador OR é uma função, afinal, e a tabela verdade é realmente a essência dessa função como uma relação e um conjunto. É claro que isso também pode ser escrito de uma maneira divertida OO:BinaryOperator or = new TruthTableBasedBinaryOperator(new TruthTable(false, true, true, true));
VEM DE

3

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.

boolean isOR(boolean arg1, boolean arg2){

   int L = arg1 ? 1 : 0;
   int R = arg2 ? 1 : 0;

   return (L+R) > 0;

}

6
booleanExpression ? true : falseé trivialmente igual a booleanExpression.
Keen

Gosto da sua metodologia, mas um erro simples é que a soma dos dois argumentos deve ser maior que ZERO para ser verdadeira, não maior que UM.
Grantly

11
return (arga+argb)>0
Grantly

11
Eu estava apenas corrigindo seu texto. Seu código é perfeito, mas pode estar em uma linha: return (((arg1 ? 1 : 0)+(arg2 ? 1 : 0)) > 0); :)
Grantly

11
@SenthuSivasambu Não tenho objeções ao seu uso 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.
Keen

1

As duas formas:

OR(arg1, arg2)
  if arg1
     return True
  else:
     return arg2

OU

OR(arg1, arg2)
  if arg1
     return arg1
  else:
     return arg2

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ão false || "abc"tem o valor "abc"e 42 || "abc"o valor 42.

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.


qual é o sentido de repetir a resposta anterior ( como você admitiu )?
Gnat

@gnat está perto o suficiente para que eu não tivesse me incomodado se tivesse visto essa resposta, mas ainda há algo que não foi encontrado em nenhuma delas, então estou deixando.
Jon Hanna

@gnat, na verdade considerando "Estamos procurando respostas longas que forneçam alguma explicação e contexto". Estou mais feliz com esta resposta agora.
Jon Hanna

1

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

NÃO [NÃO (A e A) e NÃO (B e B)]

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.


NÃO (A e A) == NÃO (A)?
22414 Charlie

Sim, exatamente. No mesmo artigo da wikipedia, você pode ver como eles reduzem um portão NOT aos portões NAND. O mesmo para um portão AND. Eu escolhi não editar a fórmula que eles apresentavam para o portão OR.
Walter Mitty

1

Todas as boas respostas já foram dadas. Mas eu não vou deixar isso me parar.

// This will break when the arguments are additive inverses.
// It is "cleverness" like this that's behind all the most amazing program errors.
or(arg1, arg2)
    return arg1 + arg2
    // Or if you need explicit conversions:
    // return (bool)((short)arg1 + (short)arg2)

Alternativamente:

// Since `0 > -1`, negative numbers will cause weirdness.
or(arg1, arg2)
    return max(arg1, arg2)

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:

or(arg1, arg2)
    return !(!arg1 * !arg2)

Isso simplesmente usa as leis de DeMorgan e abusa do fato de que *é semelhante a &&quando truee falseé tratado como 1e 0respectivamente. (Espere, você está dizendo que isso não é código de golfe?)

Aqui está uma resposta decente:

or(arg1, arg2)
    return arg1 ? arg1 : arg2

Mas isso é essencialmente idêntico a outras respostas já dadas.


3
Essas abordagens são fundamentalmente falhas. Considere -1 + 1 para arg1+arg2, -1 e 0 para max(arg1,arg2)etc.
fluffy

@fluffy Essa abordagem assume argumentos booleanos e funciona apenas corretamente com a maioria dos tipos de entrada de lixo. É bom lembrar que ainda há algum lixo que produz problemas. Esse tipo de coisa é exatamente por que devemos procurar modelar o domínio real do problema o mais diretamente possível (e evitar sermos empolgados por nossa própria inteligência) na prática.
Keen

Se você estiver fazendo valores de 1 bit boolean, pura, depois disso ainda não funcionar, uma vez que 1 + 1 = 0. :)
fofo

@fluffy É aí que entram as conversões explícitas. A necessidade ou não delas depende dos detalhes da implementação (e é exatamente por isso que essa é uma ideia boba).
Keen

0

Uma maneira de definir oré através de uma tabela de pesquisa. Podemos tornar isso explícito:

bool Or( bool a, bool b } {
  bool retval[] = {b,true}; // or {b,a};
  return retval[a];
}

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 ++, boolpromove um valor que pode ser usado como um índice de matriz, com trueser 1e falseser 0.

Podemos então estender isso para outras operações lógicas:

bool And( bool a, bool b } {
  bool retval[] = {false,b}; // or {a,b};
  return retval[a];
}
bool Xor( bool a, bool b } {
  bool retval[] = {b,!b};
  return retval[a];
}

Agora, uma desvantagem de tudo isso é que ela requer notação de prefixo.

namespace operators {
  namespace details {
    template<class T> struct is_operator {};
    template<class Lhs, Op> struct half_expression { Lhs&& lhs; };
    template<class Lhs, class Op>
    half_expression< Lhs, Op > operator*( Lhs&&lhs, is_operator<Op> ) {
      return {std::forward<Lhs>(lhs)};
    }
    template<class Lhs, class Op, class Rhs>
    auto operator*( half_expression<Lhs, Op>&& lhs, Rhs&& rhs ) {
    return invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) );
    }
  }
  using details::is_operator;
}

struct or_tag {};
static const operators::is_operator<or_tag> OR;

bool invoke( bool a, or_tag, bool b ) {
  bool retval[] = {b,true};
  return retval[a];
}

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 livre invokeno mesmo espaço para nome que or_tag:

template<class...Ts>
std::set<Ts...> invoke( std::set<Ts...> lhs, or_tag, std::set<Ts...> const& rhs ) {
  lhs.insert( rhs.begin(), rhs.end() );
  return lhs;
}

e agora set *OR* setretorna a união dos dois.


0

Este me lembra as funções charasteristic:

or(a, b)
    return a + b - a*b

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

False::or(a)
    return a

True::or(a)
    return self

E os métodos duplos existem para e:

False::and(a)
    return self

True::and(a)
    return a

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

or = array[2][2] {
    {0, 1},
    {1, 1}
}

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)


0

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.


-2

A lógica está perfeitamente correta, mas pode ser simplificada:

or(arg1, arg2)
  if arg1 = True
     return True
  else if arg2 = True
     return True
  else
     return False

E, presumivelmente, seu idioma possui um operador OR, portanto, a menos que seja contra o espírito da pergunta, por que não

or(arg1, arg2)
  if arg1 = True or arg2 = True
     return True
  else
     return False

if arg1 = True or arg2 = True { return true } else { return false }Melhor ainda return arg1 = True or arg2 = True. if condition then true else falseé redundante.
Doval

4
consulente apontou especificamente que sua exigência era "sem usar o próprio operador"
mosquito

2
Hum, eu não disse nada disso. Era o que eu queria dizer, mas a pergunta não dizia isso até que fosse editada, e ela a respondeu como tal, então foi minha culpa nessa questão.
logicNoob
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.