Nós dominamos novas linguagens de programação, contando com o já aprendido

Olá colegas.



Instantâneo de Jenny Marvin de Unsplash

Hoje preparamos para você uma tradução de um artigo sobre as semelhanças fundamentais de muitas linguagens de programação usando Ruby e C # como exemplo. Esperamos que as idéias do respeitado Severin Peres ajudem muitos de vocês a começarem rapidamente a aprender uma nova linguagem de programação, e as coisas sigam com verdadeiro sentido e prazer.

O que não tira o programador - ele nunca para de aprender. Você pode ter um idioma favorito, uma estrutura ou uma biblioteca, mas não há dúvida de que não pode fazer isso exclusivamente com eles. Você pode gostar de JavaScript, mas o projeto em que está trabalhando no momento pode exigir Python. Você pode ser adepto do Perl, mas a base de código da sua empresa pode ser escrita em C ++. Para um desenvolvedor iniciante, a idéia de aprender um novo idioma pode parecer assustadora, especialmente se os prazos estiverem chegando. Esta é uma má notícia. No entanto, existe uma boa: aprender um novo idioma geralmente não é tão difícil. Se tomarmos como base os modelos mentais existentes, você verá que aprender um novo idioma é basicamente uma extensão do conhecimento existente e não funciona do zero.

O que eles têm em comum


A maioria das linguagens de programação é basicamente baseada no mesmo conjunto de princípios-chave. A implementação é diferente, mas dificilmente existem duas linguagens tão diferentes que não será possível traçar paralelos entre elas. Para aprender e entender um novo idioma, o mais importante é identificar como parece que você já conhece e, em seguida, adquirir novos conhecimentos, expandindo sua compreensão quando / se necessário. Considere, por exemplo, tipos de dados e variáveis. Em todos os idiomas, há uma maneira de definir e armazenar dados - de maneira uniforme ao longo do programa. Portanto, ao aprender um novo idioma, primeiro você precisa entender como as variáveis ​​são definidas e usadas aqui. Tome dois idiomas diferentes como exemplo: Ruby interpretado com digitação dinâmica e C # compilado com digitação estática.

my_int = 8 my_decimal = 8.5 my_string = "electron" puts "My int is: #{my_int}" puts "My float is: #{my_decimal}" puts "My string is: #{my_string}" 

Um exemplo semelhante:

 using System; public class Program { public static void Main() { int myInt = 8; double myDecimal = 8.5; string myString = "electron"; Console.WriteLine("My int is: {0}", myInt); Console.WriteLine("My float is: {0}", myDecimal); Console.WriteLine("My string is: {0}", myString); } } 

Digamos que você seja um desenvolvedor experiente em Ruby e queira aprender C #. A seguir, trechos de código, em um dos quais você pode reconhecer facilmente o Ruby. Lá você só precisa definir várias variáveis ​​e exibi-las no console. Agora preste atenção no segundo fragmento. Você aprende alguma coisa? A sintaxe é diferente, mas não há dúvida de que o segundo código funciona como o primeiro. O operador = é encontrado várias vezes, o que provavelmente parece um símbolo das operações de atribuição. Em seguida, um determinado Console.WriteLine() é chamado, implicando que os valores serão exibidos no console. Existem também algumas linhas aqui que parecem usar interpolação para compor mensagens. Conceitualmente, não há nada particularmente surpreendente aqui - atribuição, interpolação, saída para o console, todas essas operações já são conhecidas por você ao trabalhar com Ruby.

Você pode entender o segundo fragmento de código sem nenhum conhecimento de C #, mas definitivamente há espaço para expandir seu modelo mental. Por exemplo, por que todos se envolvem no método Main() ? Quais são essas palavras-chave int , double e string ? Com isso, o treinamento começa. Você já entende em termos gerais o que está acontecendo aqui (atribuição, interpolação, saída), agora é hora de passar aos detalhes:

  • Main() : Tendo um pouco mais de profundidade da situação, descobrimos que o método Main() é o ponto de entrada a partir do qual o programa é iniciado. Agora sabemos que em todos os programas C # precisamos do método Main ().
  • Variáveis: na primeira parte do nosso fragmento em C #, algum tipo de atribuição definitivamente ocorre. Dada a nomenclatura, você provavelmente acha que a palavra-chave int significa uma variável inteira, double é um número de ponto flutuante de precisão dupla e string é uma variável de string. Quase imediatamente, você percebe que em C #, diferentemente do Ruby, é necessária a digitação estática de variáveis, pois as variáveis ​​são declaradas de maneira diferente para diferentes tipos de dados. Depois de ler a documentação, você entenderá como é diferente.
  • Console.WriteLine() : Por fim, executando o programa, você verá que Console.WriteLine() exibe os valores no console. No Ruby, você sabe que puts é um método do objeto global $stdout e, se você consultar a documentação de Console.WriteLine() , verá que Console é uma classe do namespace System e WriteLine() é o método definido neste sala de aula. Isso não apenas se parece muito com puts , mas também sugere que o C #, como Ruby, é uma linguagem orientada a objetos. Aqui você tem outro modelo mental que o ajudará a traçar novos paralelos.

O exemplo acima é muito simples, mas até várias conclusões importantes podem ser tiradas dele. Você já aprendeu que um programa C # requer um ponto de entrada bem definido, que esse idioma é digitado estaticamente (ao contrário do Ruby) e orientado a objetos (como Ruby). Você descobriu, porque já imagina o que são variáveis ​​e métodos e depois expandiu esses modelos mentais, enriquecendo-os com o fenômeno da tipificação.

Procure diferenças


Começando a tentar ler e escrever código em um novo idioma, a primeira coisa que você precisa descobrir é que as coisas já são conhecidas e podem servir como base para o aprendizado. Em seguida, vá para as diferenças. Vamos voltar à nossa transição de Ruby para C # e ver algo mais complicado.

 particles = ["electron", "proton", "neturon"] particles.push("muon") particles.push("photon") particles.each do |particle| puts particle end 

Neste fragmento Ruby, definimos uma matriz chamada particles , que conterá várias linhas e, em seguida, usamos Array#push para adicionar mais algumas linhas a ela, e Array#each para iterar sobre a matriz e gerar cada linha individual no console. Mas como fazer o mesmo em c #? Pesquisando um pouco no Google, aprendemos que o C # digitou matrizes (a digitação não deve mais surpreendê-lo, considerando o que você aprendeu anteriormente), e também há um método SetValue que lembra um pouco o push , mas usa o valor e a posição no índice como parâmetros. Nesse caso, a primeira tentativa de reescrever o código Ruby em C # pode resultar nisso:

 using System; using System.Collections.Generic; public class Program { public static void Main() { string[] particles = new string[] { "electron", "proton", "neturon" }; particles.SetValue("muon", 3); //    ( 11):      particles.SetValue("photon", 4); foreach (string particle in particles) { Console.WriteLine(particle); } } } 

Infelizmente, esse código lançará uma Run-time exception ao tentar usar o SetValue para adicionar um novo valor à matriz. Novamente, examinamos a documentação e descobrimos que as matrizes em C # não são dinâmicas e devem ser inicializadas imediatamente com todos os valores ou com uma indicação do comprimento. Mais uma vez tentando reproduzir o código Ruby, considere isso e obtenha a seguinte opção:

 using System; using System.Collections.Generic; public class Program { public static void Main() { string[] particles = new string[] { "electron", "proton", "neturon", null, null }; particles.SetValue("muon", 3); particles.SetValue("photon", 4); foreach (string particle in particles) { Console.WriteLine(particle); } } } 

Esse trecho realmente reproduz toda a funcionalidade do código-fonte do Ruby, mas com uma grande extensão: ele apenas exibe os mesmos valores para o console. Se você examinar cuidadosamente os dois fragmentos, um problema será rapidamente descoberto: em um fragmento em C # na matriz de partículas não pode haver mais que 5 valores, enquanto em um fragmento em Ruby eles são permitidos quantos você quiser. Então fica claro que as matrizes em Ruby e C # são fundamentalmente diferentes: a primeira possui tamanho dinâmico, enquanto a segunda não. Para reproduzir adequadamente a funcionalidade do snippet Ruby em C #, você precisa de mais deste código:

 using System; using System.Collections.Generic; public class Program { public static void Main() { List<String> particles = new List<String>(); particles.Add("electron"); particles.Add("proton"); particles.Add("neutron"); particles.Add("muon"); particles.Add("photon"); foreach (string particle in particles) { Console.WriteLine(particle); } } } 

Isso usa a estrutura de dados da List para coletar valores dinamicamente. Nesse caso, na verdade, reproduzimos o código Ruby original em C #, mas, mais importante, aqui podemos apreciar a principal diferença entre os dois idiomas. Embora em ambos os idiomas o termo “matriz” seja usado e possa parecer que essas matrizes são uma e a mesma, na prática elas são bem diferentes. Aqui está mais uma coisa que ajuda a expandir o modelo mental, para entender melhor o que é uma "matriz" e como ela é organizada. Em C #, uma matriz como estrutura de dados pode ou não ser adequada em situações em que no Ruby você recorreria a matrizes; estamos falando de situações em que o redimensionamento dinâmico de uma matriz é crítico. Agora você precisa cuidar disso com antecedência e pensar no seu código adequadamente.

Retornando aos princípios fundamentais


É muito conveniente começar a aprender novos idiomas, explorando suas semelhanças e diferenças em comparação com os idiomas já conhecidos; no entanto, em alguns casos, é mais aconselhável começar com princípios universais. Acima, concluímos logicamente que o C # era uma linguagem orientada a objetos quando trabalhamos com uma classe interna e um de seus métodos, System.Console.WriteLine() , com o qual executamos uma ação. É lógico supor que em C #, como em outras linguagens orientadas a objetos, existe um mecanismo para definir uma classe e instanciar objetos a partir dela. Este é um princípio básico da programação orientada a objetos, para que você tenha poucas dúvidas sobre a exatidão de nossa suposição. Primeiro, vamos ver como essa operação pode parecer na linguagem Ruby familiar.

 class Element attr_accessor :name, :symbol, :number def initialize(name, symbol, number) self.name = name self.symbol = symbol self.number = number end def describe puts "#{self.name} (#{self.symbol}) has atomic number #{self.number}." end end hydrogen = Element.new("Hydrogen", "H", 1) hydrogen.describe 

Aqui temos uma classe Element simples, na qual existe um método construtor para aceitar valores e atribuí-los a objetos instanciados, um conjunto de métodos de acesso para definir e receber valores e também um método de instância para gerar esses valores. Nesse caso, os principais conceitos são a idéia de uma classe, a idéia de um método construtor, a idéia de getters / setters e a idéia de um método de instância. Voltando às nossas idéias sobre o que pode ser feito em linguagens orientadas a objetos, veremos como fazer o mesmo em C #.

 using System; public class Program { public static void Main() { Element hydrogen = new Element("Hydrogen", "H", 1); hydrogen.Describe(); } public class Element { public string Name { get; set; } public string Symbol { get; set; } public int Number { get; set; } public Element(string name, string symbol, int number) { this.Name = name; this.Symbol = symbol; this.Number = number; } public void Describe() { Console.WriteLine ( "{0} ({1}) has atomic number {2}.", this.Name, this.Symbol, this.Number ); } } } 

Tendo estudado esse fragmento em C #, vemos que, de fato, não é tão diferente da versão Ruby. Definimos a classe, usando o construtor, especifique como a classe instanciará os objetos, definirá getters / setters e determinará o método de instância que chamaremos nos objetos criados. Naturalmente, os dois fragmentos são bastante diferentes na aparência, mas não da maneira mais inesperada. Na versão C #, usamos this para se referir ao objeto instanciado, enquanto no Ruby usamos self para isso. A versão C # é digitada no nível do método e no nível do parâmetro, enquanto no Ruby não é. No entanto, no nível dos princípios-chave, ambos os fragmentos são quase idênticos.

Desenvolvendo este tópico, podemos considerar a idéia de herança. Sabe-se que herança e subclasse são os pontos principais da programação orientada a objetos, portanto é fácil entender que em C # isso é feito com o mesmo sucesso que em Ruby.

 class Element attr_accessor :name, :symbol, :number def initialize(name, symbol, number) self.name = name self.symbol = symbol self.number = number end def describe puts "#{self.name} (#{self.symbol}) has atomic number #{self.number}." end end class NobleGas < Element attr_accessor :category, :type, :reactivity def initialize(name, symbol, number) super(name, symbol, number) self.category = "gas" self.type = "noble gas" self.reactivity = "low" end def describe puts "#{self.name} (#{self.symbol}; #{self.number}) is a #{self.category} " + "of type #{self.type}. It has #{self.reactivity} reactivity." end end argon = NobleGas.new("Argon", "Ar", 18) argon.describe 

Na versão Ruby, definimos uma subclasse de NobleGas que herda de nossa classe Element ; seu construtor usa a super palavra-chave, que estende o construtor da classe pai e substitui o método da instância de describe para definir um novo comportamento. O mesmo pode ser feito em C #, mas com uma sintaxe diferente:

 using System; public class Program { public static void Main() { NobleGas argon = new NobleGas("Argon", "Ar", 18); argon.Describe(); } public class Element { public string Name { get; set; } public string Symbol { get; set; } public int Number { get; set; } public Element(string name, string symbol, int number) { this.Name = name; this.Symbol = symbol; this.Number = number; } public virtual void Describe() { Console.WriteLine ( "{0} ({1}) has atomic number {2}.", this.Name, this.Symbol, this.Number ); } } public class NobleGas : Element { public string Category { get; set; } public string Type { get; set; } public string Reactivity { get; set; } public NobleGas(string name, string symbol, int number) : base(name, symbol, number) { this.Category = "gas"; this.Type = "noble gas"; this.Reactivity = "low"; } public override void Describe() { Console.WriteLine ( "{0} ({1}; {2}) is a {3} of type {4}. It has {5} reactivity.", this.Name, this.Symbol, this.Number, this.Category, this.Type, this.Reactivity ); } } } 

À primeira vista, quando ainda não sabíamos nada sobre C #, essa última listagem pode parecer intimidadora. A sintaxe não é familiar, algumas palavras-chave e códigos estranhos não estão organizados como costumávamos. No entanto, se considerarmos esse código do ponto de vista dos princípios básicos, a diferença não é tão significativa: aqui apenas temos uma definição de classe, um conjunto de métodos e variáveis ​​e uma série de regras para instanciar e usar objetos.

TL; DR


Aprender um novo idioma pode ser terrivelmente difícil se você o fizer do zero. No entanto, a maioria das linguagens de programação é baseada nos mesmos princípios básicos, entre os quais é fácil traçar paralelos, observar diferenças importantes e aplicar em muitas linguagens. Tentando uma nova linguagem nos modelos mentais existentes, você pode descobrir onde ela não difere das línguas já estudadas e onde você realmente precisa esclarecer alguma coisa. Estudando mais e mais idiomas ao longo do tempo, você expande seus modelos mentais e esses refinamentos são cada vez menos necessários - você reconhecerá várias implementações em vários idiomas da planilha.

Source: https://habr.com/ru/post/pt422869/


All Articles