Uma coletânea sobre diversas formas de descobrir se um número é par ou não em Go

Definição de um número par

https://pt.wikipedia.org/wiki/Paridade

Um thread bem divertida no twitter

Em um thread, é apresentada uma situação absurda em que a função proposta para descobrir se um número é par era uma sequência de ifs para cada número retornando True ou False, e a autora, ironicamente pergunta se há um meio para fazer isso de maneira mais fácil:

https://twitter.com/ctrlshifti/status/1288745146759000064

Eu peguei a imagem e postei no Linkedin, perguntando “Como proceder se der de cara com isso em um PR? :)” e foi um baita clickbait. Dentro da minha bolha a galera reagiu com bom humor, mas fora teve respostas interessantes sobre como mentorizar ou orientar alguém que fizesse isso, e até mesmo rendendo uns comentários mais exaltados tipo “por que expor dessa forma alguém que está começando?”:

https://www.linkedin.com/posts/paulohrpinheiro_como-proceder-se-der-de-cara-com-isso-em-activity-6694579298629685248-eXyQ

No twitter também rolou alguns casos de haters com a mulher que postou isso. Alguns a ironizando por ser mulher. Outros desesperados apontando a solução óbvia em tom de lacração.

É nítido, em tudo isso, a exasperação ou incapacidade de muitos, para entender um texto. Reflexo desse triste momento em que o mundo vive de trevas econômicas, sociais, sanitárias e culturais.

O aprendizado

Mas a galera do bem sempre se sobressai. E as alternativas para esse trivial problema demonstram uma criatividade admirável. Eu escolhi algumas delas, pois a matemática como ciência já é um interesse especial para mim, e também tive a ideia de reescrever em Go os algoritmos, linguagem que estou aprendendo. Tem muito mais coisa lá, vale o passeio pela thread.

Também deixei no backlog outras sugestões para um próximo com ideias ainda mais surpreendentes.

https://los40.com.mx/los40/2017/03/30/moda/1490903640_358806.html

Vamos neeessa!

Isso é pra quem tem referências :)

Mais uma piada antes de começar

O @paullouth sugeriu a seguinte solução:

package main

import “fmt”

func isOdd(n int) bool { return !isEven(n) }

func isEven(n int) bool { return !isOdd(n) }

func main() { fmt.Println(isEven(10)) }

Que obviamente congelou minha modesta máquina por alguns minutos:

Execução com sigkill do programa proposto

Não resisto, mais uma

O @panondo argumentou que performance é o que interessa:

func isEven(n int) bool { return true }

Verificar o último dígito (@ctrlshifti)

package main

import ( “strconv”

“github.com/paulohrpinheiro/mytest” )

func isEven(n int) bool { s := strconv.Itoa(n) l := s[len(s)-1]

return l == ‘0’ || l == ‘2’ || l == ‘4’ || l == ‘6’ || l == ‘8’ }

func main() { mytest.Run(1000, isEven) }

Minha versão com expressão regular

Inspirado por essa versão, lembrei dessa grande bala de prata, geralmente usada pra fazer a coisa errada no lugar errado!

package main

import ( “regexp” “strconv”

     "github.com/paulohrpinheiro/mytest"

)

func isEven(n int) bool { var r = regexp.MustCompile([02468]$) s := strconv.Itoa(n)

     return r.MatchString(s)

}

func main() { mytest.Run(1000, isEven) }

Multiplicando por dez e dividindo por dois (@KaptenUppenbar)

Mais uma genial ideia, afinal, números pares nessa condição terão como último dígito o zero e números ímpares o cinco. Perceba-se que ele multiplicou por dez apenas adicionando um zero ao final do número.

A ideia original for apresentada em Python:

is_even = lambda x: str(int(int(str(x)+‘0’)/2)).endswith(‘0’)

Em Go pode-se ter:

package main

import ( “fmt” “strconv” )

func isEven(n int) bool { p := strconv.Itoa((n * 10) / 2)

     return p[len(p)-1] == '0'

}

func main() { fmt.Printf(“127 is even? %v\n”, isEven(127)) fmt.Printf(“388 is even? %v\n”, isEven(388)) fmt.Printf(" 0 is even? %v\n", isEven(0)) fmt.Printf("-10 is even? %v\n", isEven(-10)) fmt.Printf("-11 is even? %v\n", isEven(-11)) }

Não usei o pacote de teste, pois ao multiplicar por 10, estamos fora dos limites do tipo numérico.

Execução do programa em terminal

Inspirado no anterior, criei esse

Aqui, usando a divisão inteira, se o número for par, ao dividi-lo por dois, e então multiplicá=lo por dois, deve-se ter o mesmo número:

package main

import ( “github.com/paulohrpinheiro/mytest” )

func isEven(n int) bool { return (n/2)*2 == n }

func main() { mytest.Run(1000, isEven) }

Corre-se o risco de se o compilador otimizar a expressão, cairmos em uma tautologia.

Pegada funcional, com guardas e recursão (@arara_)

Esse aqui me deu uma vontade louca de voltar a estudar Haskell.

package main

import “fmt”

func isEven(n int8) bool { if n == 1 { return false } if n >= 2 { return isEven(n - 2) } if n <= -1 { return isEven(n + 2) }

// n == 0 return true }

func main() { fmt.Printf(“10 is even? %v\n”, isEven(10)) fmt.Printf(“15 is even? %v\n”, isEven(15)) }

Não pude usar o pacote de teste por conta dos extremos que ele usa.

Falando em Haskell, perdi um tempo desenferrujando minhas desabilidades nessa linguagem e não é que saiu uma função? Peguei o máximo de um inteiro de 64 bits e chamei a função e deixei calculando. Demorando muito pra ter um resultado, mas a máquina está usável (no tempo em que estou escrevendo esse texto tem um processador torrando aqui). Recursão ali é outra conversa.

isEven :: Int -> Bool isEven n | n == 1 = False | n >= 2 = isEven (n - 2) | n < 0 = isEven (n + 2) | otherwise = True – n==0

Testes

Como pode ser observado, há um package para testar as funções:

package mytest

import ( “fmt” “math/rand” “time” )

// https://stackoverflow.com/questions/6878590/the-maximum-value-for-an-int-type-in-go const maxUint = ^uint(0) const minUint = 0 const maxInt = int(maxUint » 1) const minInt = -maxInt - 1

func Run(randoms int, isEven func(n int) bool) bool { zeroTest := zero(isEven) maxTest := max(isEven) minTest := min(isEven) positiveTest := positive(randoms, isEven) negativeTest := negative(randoms, isEven)

     final := zeroTest && maxTest && minTest && positiveTest && negativeTest
     s := "PASS"

     if !final {
             s = "NO PASS"
     }

     fmt.Println("Final result: ", s)

     return final

}

func myIsEven(n int) bool { return n%2 == 0 }

func zero(f func(n int) bool) bool { if f(0) == true { return true }

     fmt.Printf("zero FAIL! 0 is even.\n")
     return false

}

func max(f func(n int) bool) bool { if f(maxInt) == myIsEven(maxInt) { return true }

     fmt.Printf("max FAIL! %v is even.\n", maxInt)
     return false

}

func min(f func(n int) bool) bool { if f(minInt) == myIsEven(minInt) { return true }

     fmt.Printf("min FAIL! %v is even.\n", minInt)
     return false

}

func positive(randoms int, f func(n int) bool) bool { rand.Seed(time.Now().UnixNano())

     for i := 0; i < randoms; i++ {
             r := rand.Int()

             if f(r) != myIsEven(r) {
                     fmt.Printf("random FAIL! %v is not even.\n", r)
                     return false
             }
     }

     return true

}

func negative(randoms int, f func(n int) bool) bool { rand.Seed(time.Now().UnixNano()) maxRandom := maxInt - 1

     for i := 0; i < randoms; i++ {
             r := rand.Intn(maxRandom) * (-1)

             if f(r) != myIsEven(r) {
                     fmt.Printf("random FAIL! %v is not even.\n", r)
                     return false
             }
     }

     return true

}

Espero em breve publicar o restante das soluções que separei.

Por hoje é isso, pratiquei Go me divertindo!

Agora temos a segunda parte:

https://paulohrpinheiro.xyz/texts/go/2020-08-30-como-saber-se-um-numero-e-par-de-forma-complicada-II.html