Skip to content

Instantly share code, notes, and snippets.

@tarsisazevedo
Created January 26, 2018 20:04
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tarsisazevedo/57d3bfc7513705b13fb5a6a2301a5ff2 to your computer and use it in GitHub Desktop.
Save tarsisazevedo/57d3bfc7513705b13fb5a6a2301a5ff2 to your computer and use it in GitHub Desktop.

essas batalhas sobre linguagens de programação sao frequententes, mas muitos ignoram que tudo vira instruções que a máquina é capaz de executar então, no inicio, a programação era complicada, com fios (isso falando de computadores modernos, da época da segunda guerra depois, com o desenvolvimento das memórias, viu-se a vantagem de tornar a programação flexivel e armazena-la na memoria (agora abundante) os computadores e os programas começaram a crescer entao eles programavam apenas em código de máquina, instruções que eram codificadas para cada processador depois veio a ideia de agrupar instrucoes semelhantes em menemonicos aparece o assembly uma instrucao em assembly pode gerar varias instrucoes de maquina diferente mov Registrador, memoria mov memoria, Registrador são a mesma instrucao em assembly, mas sao diferentes em linguagem de maquina isso comecou a facilitar as coisas, mas com mais memoria e mais poder de processamento, precisavamos de formas mais avançadas de programação

surgem as primeiras linguagens de programação de alto nivel: Fortran e depois Algol que influenciaram linguagens que usamos até hoje em fortran eles resolveram passar para a maquina a tarefa de traduzir o codigo que escrevemos a ideia era escrever os calculos de uma forma que um matematico poderia ser treinado

C AREA OF A TRIANGLE - HERON'S FORMULA
C INPUT - CARD READER UNIT 5, INTEGER INPUT
C OUTPUT -
C INTEGER VARIABLES START WITH I,J,K,L,M OR N
     READ(5,501) IA,IB,IC
 501 FORMAT(3I5)
     IF(IA.EQ.0 .OR. IB.EQ.0 .OR. IC.EQ.0) STOP 1
     S = (IA + IB + IC) / 2.0
     AREA = SQRT( S * (S - IA) * (S - IB) * (S - IC) )
     WRITE(6,601) IA,IB,IC,AREA
 601 FORMAT(4H A= ,I5,5H  B= ,I5,5H  C= ,I5,8H  AREA= ,F10.2,
    $13H SQUARE UNITS)
     STOP
     END

so como exemplo ai estavamos em 1957 avançando rapidamente, passando pelo basic de 64, chegamos no algol 68


 Rosetta Code "Count the coins"
 This is a direct translation of a Haskell version, using an array rather than
 a list. LWB, UPB, and array slicing makes the mapping very simple:

 LWB > UPB     <=> []
 LWB = UPB     <=> [x]
 a[LWB a]      <=> head xs
 a[LWB a + 1:] <=> tail xs
#

BEGIN
 PROC ways to make change = ([] INT denoms, INT amount) INT :
 BEGIN
   IF amount = 0 THEN
     1
   ELIF LWB denoms > UPB denoms THEN
     0
   ELIF LWB denoms = UPB denoms THEN
     (amount MOD denoms[LWB denoms] = 0 | 1 | 0)
   ELSE
     INT sum := 0;
     FOR i FROM 0 BY denoms[LWB denoms] TO amount DO
       sum +:= ways to make change(denoms[LWB denoms + 1:], amount - i)
     OD;
     sum
   FI
 END;
 [] INT denoms = (25, 10, 5, 1);
 print((ways to make change(denoms, 100), newline))
END

ja bem mais proximo do que conhecemos hoje absurdamente proximo O Algol 68 influenciou várias linguagens, como o Pascal, C e por conseguencia o Python mas sempre, o objetivo era convertar o programa em código de máquina e continua assim até hoje Existem outros ramos, completamente diferentes Vejamos list, também dos anos 60/70:

(defun count-change (amount coins
                   &optional
                   (length (1- (length coins)))
                   (cache  (make-array (list (1+ amount) (length coins))
                                       :initial-element nil)))
 (cond ((< length 0) 0)
       ((< amount 0) 0)
       ((= amount 0) 1)
       (t (or (aref cache amount length)
              (setf (aref cache amount length)
                    (+ (count-change (- amount (first coins)) coins length cache)
                       (count-change amount (rest coins) (1- length) cache)))))))

; (compile 'count-change) ; for CLISP

(print (count-change 100 '(25 10 5 1)))       ; = 242
(print (count-change 100000 '(100 50 25 10 5 1)))  ; = 13398445413854501
(terpri)

Aqui a ideia de deixar mais próximo da matemática é mais forte Conceitos como nao poder alterar a variavel depois da primeira atribuicao recursividade e outros bichos mas o trabalho do compilador era o mesmo: converter o programa para codigo de maquina que por natureza é desestruturado tudo isso é equivalente, mas com apresentacoes diferentes o que aconteceu nos anos 80 e 90 as pessoas passaram a ter mais poder de computacao que poderiam usar claro, nunca é demais entao o pensamento de super performance na traducao do programa para codigo de maquina deu lugar para linguagens que aumentem a produtividade do programador nos anos 80/90, o software passou a ser mais caro que o hardware e é assim até hoje por isso linguagens como Perl, PHP, Python entre outras ganharam tanto mercado elas resolveram problemas de produtividade quem programa em C++ como eu e passou para Perl viu um mundo novo... po, relatorio que antes era umas 1000 linhas para ainda sair feio virar 20 a 50 linhas em perl ok, ninguem consegui ler depois, mas eram apenas 20 ou 50 linhas no pior dos casos, escrevia de novo Isso da uma ideia pq a alta performance foi deixando de ser uma prioridade durante essa época, todos ja estavam acostumados a ter computadores mais rapidos todo ano e por mais rapidos, era muito mais rapido que os anteriores entao, nao valia mais a pena escovar bits o que o escovador de bits levava um mes para fazer, o script kid fazia em um ou dois dias com menos bugs esse foi uma das escolhas que a comunidade que programa vez nos anos 2000, a coisa começou a mudar chegou-se num teto na velocidade em Ghz 4/5 Ghz virou uma barreira fisica antes mesmo disso, os processadores passaram a ter varios cores para compensar de alguma forma a falta de aumento escalar na velocidade um dos problemas é que as linguagens de programacao nao acompanharam esse movimento hoje, temos um ouro momento pagamos a computacao por segundo (como nos anos 60...) no caso de hospedagem nas nuvens entao, performance passa a ser interessante novamente é por isso que as linguagens mais modernas tentam resolver o problema: como gerar código eficiente para o processador, mas sem matar o programador? em lugares onde essa falta de performance nao é critica, voce usa Python de olhos fechados quando performance passa a ser um problema, voce precisa combinar python com outras linguagens Eu queria voltar no ponto de OO nos anos 70, partiu-se da idea de programacao desestruturada (tradicional) para estruturada com blocos de repeticao, sem linhas de programa sem goto e essa foi a máxima resolveu varios problemas, mas nao funcionava bem para grandes programas com grandes problemas, um problema recorrente era tratar conjuntos de dados semelhantes pelas mesmas funcoes pq na programacao estruturada, funcoes sao muito importantes uma nova forma de organizar os programas foi ganhando o mercado Em 67, aparece Simula:

Begin
 Ref(TwinProcess) firstProc, secondProc;
 Class TwinProcess(Name); 
 Text Name;
 Begin
       ! Initial coroutine entry (creation)
    Ref(TwinProcess) Twin;
    OutText(Name); OutText(": Creation"); OutImage;
       ! First coroutine exit
    Detach;
       ! Second coroutine entry
    OutText(Name); OutText(": Second coroutine entry"); OutImage;
        ! Second coroutine exit: switch to the twin's coroutine
    Resume(Twin);
        ! Last coroutine entry
    OutText(Name); OutText(": Last coroutine entry"); OutImage;
    Resume(Twin);
 End;
 Begin
    firstProc :- New TwinProcess ("1st Proc");
    secondProc :- New TwinProcess ("2nd Proc");
    firstProc.Twin :- secondProc;
    secondProc.Twin :- firstProc;
    OutText("Starting"); OutImage;
    Resume(firstProc);
    OutText("End");
 End;
End;

Familiar? Classes, referências (ponteiros) liberaram os programadores da pilha pq funcoes sao limitadas em escopo pela fila

nos anos 70 e 80 ja se usava muito o heap para nao sobrecarregar a pilha

Esse estilo, consisitia em passar uma estrutura (struct ou record) como parametro da funcao e uma relacao entre a estrutura e o codigo ficou clara e muitos defenderam que os programas deveriam organizar os dados e codigo em blocos codigo + dados -> classes

Na realidade, o C++ comecou sendo um mero tradutor para C voce escrevia em C++ e ele traduzia as classes em estruturas e os metodos em funcoes que recebiam a estrutura como primeiro parametro é assim que se implementam objetos até hoje mas a teoria nao parou ai alguem teve a ideia que alem dos dados, poderiamos passar uma tabela de metodos e trocar esses metodos para implementar novos comportamentos é dai que vem conceitos como herança, poliformismo, etc O exemplo classico de OO é um programa que precisa trabalhar com formas geometricas, mas pode ser qualquer codigo com comportamento comum entao, imagina que voce so tem funcoes e nao pode usar objetos teu programa tem que calcular a area de um triango vc cria area(a, h) e a funcao é uma beleza depois alguém pede para calcular a area de um retangulo a interface é parecida: area_ret(a, b) mas as formulas sao diferentes outro programador faz entao uma descoberta se eu usar uma terceira variavel, para indicar se é um retangulo ou um triangulo posso ter uma funcao area(tipo, a, b) que calcula a area, mudando a formula de acordo com o tipo

area(tipo, a, b):
    if tipo == "retangulo":
        return area(a,b)
    if tipo == "retangulo":
       return area_ret(a,b)
   ...

Ok, problema resolvido uma semana depois, o programa tem que calcular áreas de outras formas geométricas, algumas com 2, outras com 3 ou 4 parametros ai ocorre um grande problema a interface da funcao area nao serve mais pois nao suporta os novos parametros alguem entao pensa em organizar isso de forma diferente (OO) eu vou criar uma estrutura, onde o tipo da forma é um campo e a funcao usada para o calculo faz parte dessa estrutura e sabe como calcular a area agora voce tem um mecanismo, com interface unica que recebe a tal estrutura e sabe calcular sua area um objeto Isso deixa o programa muito flexivel, pois o mesmo programa, funcionando uma interface unica pode fazer mais coisas mais formas e se novas formas precisarem ser adicionadas, basta reespecificar um nova estrutura e modificar a forma de calcular E esse foi um dos saltos que se deu escrevendo programas voce so ve esse tipo de programa quando tem que alterar teu programa com o tempo quando o programa é mantido por algum tempo e precisa ser constantemente modificado é esse tipo de problema que OO resolve Outro exemplo, voce pode criar uma estrutura que associa várias formas: circulos, triangulos e retangulos internamente ela calcula a area dessas formulas somando a area de cada uma como ter tal flexibilidade com as funcoes simples? complica né entao a OO é um sintax sugar que permite o que temos hoje apenas uma forma mais simples de escrever programas complexos Agora, se voce nao entende de onde veio a necessidade de OO, nao vai entender muita coisa voce tem que entender como as classes sao implementadas para ver alguns detalhes bem interessantes Uma classe pode ser vista como uma estrutura que tem uma lista de métodos Quando voce cria um objeto, outra estrutura é criada ela contém a classe e uma estrutura para os dados os dois juntos sao o objeto entao os dados sao utilizados pelos metodos listados na classe e é assim que funciona quando voce tem heranca, voce esta manipulando de formas diferentes essa lista de metodos alguns serao da superclasse, outros da classe corrente é bem interessante ver como é feita a implementacao, realmente recomendo darem uma olhado pode debaixo dos panos Voltando pro Python no Python, as classes são dicionários com os métodos, associados ao objetos isso é uma das razoes do python ser lento quando voce compila um programa em C++, o metodo que um objeto chama é praticamente escrito em pedra ou vem de uma lista dinamica limitada em python, voce pode trocar os metodos de cada objeto pq o objeto é uma copia da lista de metodos (dicionário) com os seus dados é por isso que temos o self embram da estrutura que era o primeiro parametro na conversao de C++ para C? é o nosso self é por isso que fazemos self.A = 1 criamos tudo em self e nao na classe em si meu ponto é que entendo um pouco de historia e mesmo como compiladores sao construidos, podemos ter uma melhor ideia de como a coisa funciona pq é lento, pq tal problema nao foi resolvido e por ai vai linguagens continuam sendo formas de escrever um programa num formato que um compilador ou interpretador possam executar Tanto a linguagem A ou B, uma vez traduzida, vai executar em código de máquina o compilador faz a tradução diretamente e o interpretador faz em tempo de execucao ah mas python ao é assim nao o interpretador nao chama codigo de maquina na realidade, ele executa nosso programa linha a linha chamando funcoes em C que implementam cada comando, funcao, etc e é capaz de chamar codigo python tambem como isso é lento, eles criaram o pytho bytecode e a vm python que nao é codigo de maquina, com instrucoes ainda muito altas para serem executadas diretamente entao em Python, o programa é traduzido para bytecode e o programa em C, no caso do C Python executa esse bytecode ora, seu programa é na realidade executado por outro programa escrito em C, mas agora falamos de um programa que executa outro por isso é mais lento por isso nao pode comparar com a performance de rust ou C ou de outras linguagens compiladas o java faz algo legal com esse bytecode eles criaram o bytecode do java em baixo nivel é como um processador normal, mas com definicao unica e todo o mundo java foi construido em volta dele logo depois eles começaram a criar JIT Just in Time Compilation que nada mais é que pegar o bytecode Java (escrito em codigo de maquina virtual, mas bem proximo de um processador real) e traduzilo em código da máquina real é por isso que a VM "esquenta" ou seja, o codigo vai ficando mais rapido na realidade com o tempo, parte do bytecode vai sendo convertido em codigo nativo voltando pro python, pq isso nao é feito? pq python é uma linguagem dinamica para ter as caracteristicas que python tem, como poder mudar os metodos, tudo objeto, etc voce perde a facilidade de traduzir de forma deterministica esses programas varios ja tentaram resolver esse problema o melhor até agora é o Cython que resolveu limitando o que se podia fazer ou criando um subset da linguagem mais facil de ser traduzido em C e depois em codigo de maquina é por isso que linguagens como go parecem, mas nao sao python eles fizeram um otimo trabalho mantendo parte da sintaxe do python mas num conjunto que pode ser compilado umas das diferenças é sacrificar as classes! eles voltaram um nivel pra tras, ficando nas estruturas mas ao inves de ter codigo + dados em um objeto eles tem os dados (structs) com métodos que podem ser adicionados a interface deixa de ser definida pelo tipo da classe e passa a ser definida pela capacidade ou pela implementacao de tal struct ter uma interface que implemente um metodo Como em Python, om objeto com um médoto A, seria chamado como o.A() sendo o objeto de uma classe C para que A exista, C ou uma de suas super classes implemente A a interface entre classes é mantida de os objetos vem da mesmo origem no caso, hierarquia de classes isso seria simples, mas temos o duck typing do python Se a classe C implementa A e a classe D também o pode ser tanto de C quanto de D o.A() é válido tanto para o objeto de C quanto o objeto de D ou seja, vele o objeto ter a implementacao do metodo A, pouco importa se C e D tem alguma relacao entre elas flexibilidade em Go e Rust C seria uma estrutura D outra nao ha heranca Ai podemos criar um método A que trabalha com estruras C e outro método A que trabalha com estruturas D lembra que o primeiro parametro era o objeto? em Rust e Go, o primeiro parametro é o tipo da estrutura! é sutil mas ja enchi o saco de voces

@luzfcb
Copy link

luzfcb commented Jan 26, 2018

@villares
Copy link

Opa, era legal corrigir um errinho de digitação, onde está "Vejamos list, também dos anos 60/70" deveria ser "Vejamos LISP, também dos anos 60/70"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment