Gente isso é um gancho pra explicar TCO e recursão. Eu falo com todas as letras que não é pra parar de usar FOR. Tudo depende, Golang só tem FOR, você PRECISA escrever FOR quase todo dia em Go. Já Elixir, Haskell, Erlang, e outras FPs não tem NENHUM loop. Por isso é interessante conhecer os diferentes paradigmas.
"Eu falo em todas as letras" o nome do vídeo: dev senior nao escreve FOR loop ¬¬' Você pode ser o melhor do mundo, se ficar inventando click bait, isso vai ofuscar sua habilidade .. acorda
Cara acompanho seu conteúdo e acho ele muito bem explicado e super importante, estou aprendendo demais com você. Muito obrigado por compartilhar seu conhecimento!
Mano eu nao te conheço e é claro que é tua estratégia de clickbait e tal, mas isso é muito errado. Não engane os juniors cara. Voce pode ta prejudicando a carreira de alguém. Pra quem tiver lendo: nao façam isso por favor. Nao se troca a memoria local por stack sem motivo. Nao se faz recursao sem motivo ! Alem de deixar o código mais ilegível, voce provavelmente nao vai saber fazer. Nem o autor do video vai saber. Acredite. Recursão é foda. Tirando esses métodos ridículos que o pessoal usa de exemplo no RU-vid, na vida real vc so usa quando é necessário Ex: coisas que funcionam com linguagem de contexto, como compiladores e interpretadores. Voce provavelmente nao vai saber usar. Aprenda, mas evite.
Eu já usei recursão em parsers e varredura de árvores / estruturas aninhadas, fora disso a recursão é um problema. Em embarcados, então, pior ainda. Já precisei simular recursão com uso de pilha em array, a pilha nativa não aguentava kkkk!
@@IronNidow A recursividade tem uma utilidade muito limitada porque a explosão dos números inteiros é muito rápida e o uso da pilha é extremamente intensivo
Programo desde 1985! Já passei por diversas linguagens (basic, clipper, c, delphi, vo, java, php, etc.). Até hoje uso "for" e funciona muito bem. Não sei porque essa mania de reinventar a roda!!!
Ele não está reinventando a roda. Apenas fazendo o programador pensar como as coisas funcionam por baixo dos panos. Você saberia resolver esse problema com recursividade? Outra coisa, no exemplo em que ele deu, que ia decrescendo os números em vez de somando, ao contrário do que ele disse, existe uma diferença sim com os exemplos anteriores. Principalmente em relação à memória. Você saberia dizer que diferença é essa? É bem legal saber essas coisas.
@@felipe.raposo na verdade, ele tá tentando resolver problemas de python, uma linguagem tão mal otimizada que um for loop é uma tremenda fonte de lentidão
Ainda não tenho a experiência do mestre mas concordo. No C-Sharp por exemplo, o compilador converge diversas formas de código em um mesmo código de IL. Ou seja, se usar for ou do ou oque seja, no fim roda da mesma maneira. Faltou ele especificar que ele está falando de python.
@@felipe.raposorecursividade é uma das piores formas de resolver um problema. É raro o algoritmo que vai funcionar bem usando recursividade. Meter uma recursividade pra esconder o for não é coisa de sênior, mas de junior.
@@felipe.raposoO sujeito acha que não tá fazendo um looping, mas ta. A única diferença é que ao invés de usar memória no contexto local, tá usando a stack. A stack é limitada pelo sistema operacional e carrega-la demais da zebra (tem até site com esse nome: stack overflow). Ou seja, é coisa de noobzao
Não necessariamente. Tem algoritmos que são bem mais simples de implementar recursivamente, como DFS por exemplo. E performance vai depender da lang e da TCO, tem vezes que o compilador consegue fazer ficar até mais performático do que loop, mas aqui depende MUITO. Sem saber a lang e a runtime usada, não tem como determinar. Eu preciso fazer mais um vídeo indo mais a fundo, pq esses temas são pouco explorados, complexos, e cheios de nuance que a gente raramente aprende e/ou conversa sobre, mas é bem legal!
@@GutoGalego Tem algorítmos que são mais fáceis de implementar, mas a ideia que tu passou é que Senior não usa for, não funciona assim, ou por acaso você usa funções recursivas para fazer um for each? Tem situações que o código fica mais simples, é bem raro.
Um exemplo prático no dia a dia, é quando falamos de ADT (Abstract Data Types) mais especificamente quando tratamos por exemplo listas ligadas ou dicionários de tipos complexos (json por exemplo) . Isso sim é um exemplo de recursão que você pode usar no dia a dia.
Eu já sabia o básico de recursão que todo programador normalmente conhece, mas achei muito produtivo entender que loops são "apenas" gotos "disfarçados" e todo a conversa encima de Tail Call Optimization, que não conhecia. inclusive o post do Guido exemplifica o famoso: "não existe bala de prata" conteúdo nota 10.
Achei isso muito interessante, porque já fui criticado por utilizar um GoTo para sair de um loop aninhado. Entendo que realmente não é uma boa prática e deveria ser feito em métodos separados, mas não dispunha desse argumento de que internamente um for e um goto são a mesma coisa.
@@imperiaonlinebr Na prática é muito melhor usar um break/continue do que uma variável de controle Goto puro realmente é meio... gambiarra, mas dependendo de como é feito é a solução mais elegante...
De fato, loops são apenas uma forma de codificar um bloco inicialização-condição-pulo, evitando as más práticas de gotos para lugares não convencionais Mas além disso são uma forma de dar previsibilidade para seu código, o que ajuda tanto uma CPU a fazer o branch prediction e puxar para o cache código que vai ser usado, ou mesmo ajudando o compilador/runtime a entregar operações paralelas para processadores/threads/unidades de execução diferentes.
Geralmente quando atuo como entrevistador de engenheiros seniores e staff, eu aplico um problema simples envolvendo recursão e separo alguns cenários passiveis de TCO justamente para inferir alguns conhecimentos mais avançados do candidato. Não é ponto deterministico na entrevista, mas sem dúvidas o candidato ganha bons pontos se souber do que se trata. Obrigado pelo conteúdo!
Mais uma daquelas exigências que não servem de nada pq na hora do trabalho real ninguém precisa 🤣🤣 Tipo 'inglês nativo" e "capacidade de se vender" Na hora de programar o que conta é sua capacidade de resolver o máximo de problemas no menor tempo possível e trazer o menor esforço possível para a equipe fazer manutenção no seu código
@@AlexeiDimitri eu concordo. No contexto que a maioria dos profissionais é inserido, que é resolver problemas de negócio, o que acaba sendo mais valioso é investir em desacoplamento, organização e produtividade (mas se aparecer uma função quadrática numa PE a gente manda um request change hahaha).
Incrível! Estou aprendendo em minhas aulas na faculdade a recursão com Racket (lisp) e realmente é muito bom para o aprendizado, o for e os tipos de loops são mais fáceis de aplicar, mas a lógica por trás do recursion facilita ajuda a desenvolver uma visão melhor sobre os loops. Ou seja, vale a pena dar uma olhada mesmo que tu não vá aplicar! Vlw, excelente vídeo!
Eu acredito que a forma que o paradigma funcional trata os dados é muito elegante e intuitiva, infelizmente em linguagens que não são puramente funcionais isso vem com um custo. Por exemplo, como explicado no vídeo em python não tem tail call optimisation, em js/ts as funções como map, filter e afins criam uma cópia do array e isso pode causar problemas de performance/memória quando usadas em arrays muito grandes.
JS é uma linguagem funcional O estilo de programação funcional por si só já é um conceito pouco eficiente e motivo pelo qual foi abandonado há muito tempo no mercado Só recentemente voltou a ter alguma importância pq a área de pesquisa em IA é mantida pela academia, que costuma usar soluções porcas e acadêmicas como R, Perl, etc. Até o Python por exemplo era uma coisas só usada em faculdade Creio que em algumas décadas, quando todas as outras linguagens ganharem a maturidade do Python nesse segmento o povo vai finalmente migrar para suas linguagens nativas e abandonar essa desgraça que basta um tab no lugar errado para tirar sua noite de sono
Muito bom ver conteúdo sobre baixo nível em pt-br. É uma coisa que deveria ser fundamental pra qualquer desenvolvedor, tá cheio de dev “senior” por aí que não sabe nem o que é Big O e muito menos tem um conhecimento sobre o funcionamento das ADTs que são implementadas por padrão na linguagem que ele usa no dia a dia.
uma dúvida genuína. a recursividade por padrão não seria um O(n) enquanto utilizar loops vai da implementação ou existe uma recursividade "ruim" (no caso, com mais complexidade)?
Uma coisa importante sobre o porque de Python não ter TCO (que não me lembro do Guido ter comentado no post dele) é justamente por conta de Python ser totalmente objetos (sim, você pode alterar a própria stack, memória e código em execução, talvez você conheça algo como pytest e unittest, essas bibliotecas fazem isso :D), pois todas operações que fazemos (como raise, try, finally, with, yield, function call, etc.) na linguagem mexem ou dependem da stack, então esse tipo de otimização seria extremamente complexa e difícil de ser feita, mas ainda assim existem otimizações que decorreram dela, um exemplo é a função `partial` da bilbioteca `functools` que não cria um frame na stack
Augusto, você trabalha com qual framework python no seu dia a dia? Eu vi que o fastapi passou o flask em star's no github e tá quase alcançado o Django, se acha que ele (fastapi) vai ser o padrão para Back end com python?
Trabalho com Django, são ferramentas diferentes com filosofias diferentes. Django é um batteries included, faz muito do trabalho pra você. FastAPI é mais conciso, menos boilerplate, depende mais de você mesmo fazer as coisas. Vejo os dois coexistindo, mas eu gosto bastante de FastAPI
Rapaz bom vídeo. Desenvolvo software há 24 anos. Do C,C++,Python,Erlang, Haskell, Typescript, Java e agora Rust. E eu vejo essa turma senior mais novinha "cagando regra" na internet apenas para aparecer. Sinceramente, empresa alguma deveria dar posição de senior para gente tão imatura quanto esses devs xiitas. Falo isso porwue já vi em discussão técnica umas pessoas defendendo justamente o titulo do click bait sabe? É tipo flamewar de linguagem(algo mais bizatro que isso não há).
recursividade vai muita além desse exemplo. Recursividade serve para quando você quer fazer exatamente a MESMA coisa inúmeras vezes mas passando valores diferentes até uma certa condição ser atendida. Navegar por uma árvore é um exemplo, você não sabe onde está o que você precisa, mas você tem uma função de busca que chama ela mesma passando um nó, depois o próximo e assim por diante. Se você quer ensinar conceitos mais complexos, utilize CONCEITOS COMPLEXOS. Recursão nesse caso não serve pra nada e é deixar o código inseguro.
Linguagens de paradigma funcional não tem loop, recursão serve tanto para casos simples e complexos nesse contexto. Ele contempla isso no vídeo mais ao final, dando exemplo com Elixir. Se você quer ensinar conceitos complexos, você também pode abrir um canal e ensinar esses conceitos.
@@allainclair Na prática ninguém usa linguagens funcionais O que o povo faz é usar linguagens imperativas e procedurais com recursos de programação funcional (como Java) Na prática, não tem nenhum ganho com map-reduce e operações funcionais, o compilador e nem o runtime vão dividir cada chamada em processamento paralelo, ao contrário do que dá para fazer com for de forma praticamente transparente Além é claro dos nossos processadores terem sido projetados para usar branch-prediction baseados em loops for compactos
Recursao é interessante mas ta mais pra uma solução própria que criamos usando a logica da linguagem do que uma solução oficial da própria linguagem. Geralmente é mais complexo e tende a gerar mais casos de excessão se feitas se maneira errada...
Dá para alterar o Heap e a Stack Mas isso é programação porca Daqui a pouco vem o gerente da cloud perguntar pq uma simples rotina de folha de pagamento tá usando 256GB de memória RAM
Mas esse aí é o nível extremo que o cara simplesmente esqueceu que memória não é infinita, mas eu tava falando de situação genérica, sem contexto, só da possibilidade de poder alterar
@@douglasmb787 Sim mano mas o que eu mais vejo é desenvolvedor comprando RAM e pedindo para colocar RAM no serviço mas o cara simplesmente não sabe gerenciar o próprio ambiente Por exemplo o Docker quando instala aloca metade da RAM para a VM dele Velho, isso é 8GB num PC de 16GB Quando a gente tá trabalhando com microcontainers, que não deveria ter mais que algumas centenas de megas de RAM, 4GB deveria ser mais que suficiente para uns 10 containers Aí o cara me abre um monte de coisa que come RAM e fica só aumentando a RAM do ambiente, sem entender que ele tá com programa mal feito, mal otimizado, usando recursos ineficientes, etc.
Kkk eu ouvi um ditado que era assim "se voce precisou usar for possivelmente ta errado" kkkkk e aprendendo algoritmo mais a fundo usar for nao e o problema kk o problema e nao saber quando usar e a criar uma boa logicar pra você ter a maxima eficiência do codigo, pesquisem ai "algoritmo de ordenação por inserção" e dissequem a lógica e mt satisfatório quando vc entende
Na minha época a frase era "se vc tá usando recursão, tem alguma coisa errada" E a gente aprendia a versão for do código, que era pouca coisa diferente e rodava muito melhor e mais rápido
Sem saber qual a linguagem, qual a implementação, e qual a runtime não tem como saber. Em linhas bem gerais, se vc usa uma linguagem mais "normal", python, javascript, loop é melhor. Mas tem casos que recursão é uma boa, ou as vezes a unica alternativa (FP)
Recursão consume tanta RAM que dependendo do valor passado estoura a pilha Loop for por sua vez é otimizado pelo próprio processador na execução (branch-prediction) e até permite o compiltador/runtime utilizar processamento paralelo (cores/threads/procesasdores, etc)
Adoro estes conteudos mais tecnicos, e acho meio dfcl de achar algum de qualidade, ganhou mais um inscrito, mas senti falta apenas de um áudio mais alto, achei baixo dms
Bela explicação, mas senti falta da continuação, como fazer a recursividade desse problema funcionar otimizada no Python. Pensei em variáveis com estado permanente, que não existe no Python, então precisaríamos usar variável global ou fazer uma classe que manteasse esse estado
If you value code reliability and readability you never use recursion as it can lead to runaway code with, for example, infinite loops and is harder to read from an outside perspective.
A criação de threads é um recurso extremamente pesado, só perdendo para a criação de processos Se for para ficar prejudicando o código, melhor usar for
Mas isso nao resolveu o problema do "empilhar processos e quando acaba o loop um a gente pode remover pq tem o loop 2"... a funcao 1 ainda ta ativa e na memoria... e vc ta chaamando a funcao 2... 3... 4... e criando uma pilha de processos nao? bom talvez se for assyncrono funcione
Muito bom o video no geral. So ficou um "buraco" nessa questao do TCO em Elixir, poderia dar uma passada por cima em como e feito na linguagem, ja que mostrou ela, e como evitar o TCO no elixir (Mostrar um exemplo que se comporta "igual" no Python :)
Não. Se vc tiver como jogar a execução para milhares de unidades de processamento paralelo, teria alguma vantagem Mas tem o OpenMP que basicamente usa loops for. Na prática não se faz mais isso: computação paralela em larga escala vc vai usar GPUs e ASICs dedicados e para isso vc vai usar frameworks dedicados como TensorFlow, CUDA, etc.
Eu dei uma pesquisada aqui, vi que no C# essa otimização só é feita pelo JIT caso a compilação seja release x64, caso contrário StackOverflowException na cara meu camarada, 'kkkkk Mas legal saber disso.
No melhor dos casos o compilador/interpretador vai ser mais esperto que vc e transformar sua recursão num loop sem te contar. É basicamente isso que TCO faz. No pior dos casos vc confia no TCO que não acontece e agora sua recursão vai dar um estouro de pilha (igual aconteceu ai no video) e vc quebra seu programa. Recursão só faz sentido ser usada num caso que vc não consiga fazer uma boa abstração em loop, como por exemplo varrer um grafo.
Assisti o video esperando como ele ia fazer pra transformar uma função recursiva sem usar loop, e fiquei so na espera mesmo Ate porque nao tem como vc simular recursividade sem usar pilha ou fila e sem usar loop
me surpreende a quantidade de desenvolvedores nos comentarios que nao conseguem entender a mensagem do video, parece q so leem o titulo e ignoram o conteudo do video… pelo amor de deus
Se for para levar em conta a mensagem do vídeo, ninguém nem passaria dos 20 segundos de vídeo cara Ninguém usa recursividade na prática, é um negócio muito específico e que quase sempre tem um equivalente iterativo
Não necessariamente, isso é detalhe de implementação. Pega um Elixir e tenta estourar stack overflow fazendo recursão com TCO. Não vai estourar, pode chamar 100 bilhões de vezes (como eu mostrei no vídeo)
Não sei dizer sobre o Elixir, pois não conheço a tecnologia, mas de modo geral, na maioria das linguagens, por baixo dos panos, ocupa mais memória, porque cada empilhamento na stack você precisa de uma cópia da função, isso naturalmente faz com que seja maior o uso de memória, independentemente da implementação. E o que pega um pouco é dizer que: DEV SENIOR NÃO USA FOR, se você não usar for na grande maioria esmagadora dos casos, você explode a aplicação rapidinho, o título que deu uma pegada..., mas o conteúdo é ótimo, parabéns, por mais vídeos como esse.@@GutoGalego
@@BrunoBafilli1Na verdade isso não ocorre se a única chamada recursiva for a ultima instrução da função , chamam isso de "Tail Recursion" que é quando você consegue se livrar da ultima chamada da call stack antes de chamar a função seguinte Ainda assim, uma chamada de função é muito mais pesada do que um simples jump em assembly, porém, algumas linguagens podem ser capazes de tornar essa chamada "inline", de forma que na prática ela vire um "loop" ao ser compilada, eu mesmo testei isso em C usando compiler explorer e observei que o assembly do loop e da função recursiva eram bastante similares e nenhum deles tinha chamadas a funções (exceto a função de print) Mas concordo com vc, é ridiculo não usar "For", até pq ele é a keyword mais flexivel de loop na maioria das linguagens, inclusive acho q o código fica bem menos legível usando recursão
@@Digimaloko Sim, eu entendo o que você quer dizer quando falamos sobre Tail Recursion, mas nem todas linguagens dão suporte a isso, quando você usa uma linguagem que não tem essa capacidade, trocar for por recursão é ridiculamente mais custoso. E um outro importante é que, não é por que esse comportamento ocorreu em C que vai ocorrer em todas as linguagens, esse tipo de coisa é sempre bom ser fundamentada na teoria do comportamento geral. Por isso mencionei que, o problema é o titulo do vídeo, isso pode gerar uma confusão monstruosa, principalmente nos mais iniciantes. Existe um estudo de Turing entre outros matemáticos provando que todo problema que recursão resolve, um loop também resolve, o problema é que, nem sempre é viável: (en.wikipedia.org/wiki/Computability_theory) o que pega é realmente o título do vídeo. Mas sua visão sobre é bem fundamentada, e o conteúdo do vídeo é ótimo, precisamos de mais vídeos como esse na comunidade, mas sem clickbait kkkkk
@@BrunoBafilli1 Cara concordo totalmente contigo, o titulo ai é 100% bait e mesmo no C cara eu tive que colocar a função como "static" pra ele saber que não ia ter linkagem externa e conseguir fazer essa otimização (sem isso ele n fez e é muito fácil fazer esse descuido pq raramente se olha o "assembly" do que foi gerado pra melhorar performance em programas comuns) Acho muito zoado depender desse tipo de otimização e mesmo eu n faria isso em qualquer projeto sério
Também acho isso meio desnecessário e bastante "flex" ou Overengineering for, forEach etc são recursos criado pela própria linguagem com mais suporte compiladas de maneira correta etc, isso só é nescessário em linguagens puramente funcionais como lisp, Clojure e Haskel
@@_Acrk629lak0sUg1s Exatamente! Eu também uso forEach pra alguns casos, mas prefiro de longe utilizar instruções/ estruturas de repetição nativas (até porque são mais eficientes kk), .. E sim, seria um overengineering xgh for 🤣🤣
Eu acho que o ponto deste vídeo não é do que eu irei falar a seguida, mas é muito bom saber recursão, porém, nem de perto você irá ser um sênior por causa disso, estou falando somente da parte técnica em sí. Um sênior geralmente iria se preocupar com a otimização do código e isso você só saberá estudando análise de algorítmos e o famoso BigO para saber que com base na entrada de sua função aumentar o tempo de execução desta função também aumenta fazendo com que você troque muitas vezes a lógica de uma função só para deixá-la mais otimizada. Concluindo, uma recursão se não me engano é O(n^2) sendo então muito lenta a execução com base no aumento da entrada do parâmetro da função❤
A técnica de recursão per-se não diz sobre a complexidade Big O. Você pode ter uma DFS recursiva numa arvore binária O(N) por exemplo. A recursividade em si não altera a complexidade do algoritmo.
A tese do título vídeo é muito burra Recursão tem um limite natural que explode muito rapidamente, é preciso conhecer a natureza dos valores operados e computar se haverá estouro de pilha ou de inteiros E o uso de funções do tipo map-reduce parece bonita a princípio mas torna de difícil depuração o código e na prática não existe otimização na execução Loops for por outro lado já são otimizados há décadas e inclusive permitem ao compilador e ao runtime otimizar a execução de várias formas, incluindo a paralelização do processamento E... vc reduziu propositalmente a quantidade de parâmetros em Elixir em relação a versão Python? CARA, não é a primeira vez que vejo vc usando falsas equivalências de código para justificar alguma coisa No caso concreto, vc tá deixando de passar um número para cada chamada de pilha, o que simplesmente permite o dobro de chamadas recursivas e isso faz uma diferença MONSTRA Sem contar que vc malandramente inverteu a lógica de bottom-up para top-bottom a fim de colocar a condição de parada na assertiva da função E convenhamos, na prática ninguém usa linguagens funcionais no mercado. Apenas algumas aplicações muito específicas ocasionalmente as usam, mas o pessoa prefere usar R, Julia e até MatLab. Vc tá com o VSCode e não tem .NET na máquina? Tudo isso é preguiça de ativar a extensão e deixar ele baixar os tools?
Em sistemas críticos o recomendado é usar for loops com um plus, garantia que o loop terá um fim. Recursividade é legal, mas em sistema crítico não é recomendado. O que seria um sistema crítico? Um sistema para uma sonda espacial que vai chegar muito longe e qualquer falha de programação será uma catástrofe.
Depende, kernel do linux usa arvores, e o melhor jeito de percorer uma arvore eh um algoritmo recursivo. Na computacao tudo depende, sempre vai variar de caso pra caso.
@@marlonoliveira175 Eh usado sim. O Perseverance e Ingenuity sao exemplos de sistemas criticos que usam linux, e por consequencia usam tambem algoritmos recursivos. Kernel do linux por si soh eh um sistema critico. E ignorando que o Kernel usa algoritmos recursivos, robotica usa recursividade em um monte de algoritmos, desde navegacao ate a controle de atuadores.
Exatamente. Tem um sujeito americano que fica fazendo shorts de python. O cara escreve um caminhão de b**ta quase todo vídeo. E um monte de maluco seguindo e aplaudindo. É nessas horas que eu temo pelo futuro da minha profissão e acredito que sim as LLMs vão substituir uns 80% dessa galera aí
@@GutoGalegoerrado. Performa infinitamente pior. Para chamada de método você tem que: Salvar a posição atual do stack pointer como return pointer na pilha Por na pilha os parâmetros Por o endereço do destino no instruction pointer Chamar a interrupção de jump Depois tem que desfazer: Desempilhar o stack pointer de retorno, empilhar o return e atualizar o instruction pointer. Loop local: basta um jump. Olha a diferença. Isso que dá achar que faculdade não é necessária...
@@cristianoo2 Evite a soberba em relação ao seu conhecimento. Com otimizações realizadas pelo compilador, o mesmo stack frame é utilizado (no caso do TCO), eliminando a necessidade de empilhar e desempilhar a cada chamada de função. Isso resulta em desempenho prático equivalente a um loop. E existem outras tecnicas que podem otimizar um algoritmo recursivo, como memoization.
@@cristianoo2 Ate mesmo um algoritmo recursivo para calcular fibonnaci pode perfomar quase igual ao mesmo algoritmo so que implementado com um loop, se feito da forma correta, e olha que a quantidade de folhas na arvore de fibonnaci vai subindo de maneira esponencial quanto mais niveis a arvore tem.
@anoo2 vê o vídeo de novo, pesquisa no google "TCO compiler optimization", e ai descobre onde no seu comentário você presumiu uma coisa que não é verdade