📜 ⬆️ ⬇️

We master new programming languages, based on the already studied

Hello colleagues.



Snapshot of Jenny Marvin from Unsplash
')
Today we have prepared for you a translation of an article about the fundamental similarities of many programming languages ​​using the example of Ruby and C #. We hope that the ideas of the distinguished Severin Peres will help many of you to start learning a new programming language as soon as possible, and things will be done with sense and pleasure.

What does not take away from the programmer - he never stops learning. You may have a favorite language, or a framework, or a library, but you can be sure that you cannot do it with them alone. You may like JavaScript, but on a project where you are currently working, Python may be required. You may be skilled in Perl, but the code base in your company may be written in C ++. For a rookie developer, the idea of ​​learning a new language may seem daunting, especially if deadlines are coming. This is bad news. However, there is a good one: it is usually not so difficult to learn a new language. If you take the already existing mental models as a basis, then you will see that learning a new language is basically an extension of the existing knowledge, and not work from scratch.

What they have similar


Most programming languages ​​are based on the same set of key principles. The implementation is different, but there are hardly two such dissimilar languages ​​that it is impossible to draw parallels between them. In order to learn and understand a new language, the most important thing is to reveal how it looks like you already know, and then acquire new knowledge, expanding your ideas about it when / if necessary. Consider, for example, data types and variables. In every language there is a way to define and store data - uniformly throughout the program. Therefore, when learning a new language, you first need to understand how variables are defined and used here. Take for example two different languages: interpreted Ruby with dynamic typing and compiled C # with static typing.

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}" 

Similar example:

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

Suppose you are an experienced Ruby developer and want to learn C #. Below are the code snippets, in one of which you easily recognize Ruby. There just need to define a few variables and display them in the console. Now pay attention to the second fragment. Do you know anything? The syntax is different, but there is no doubt that the second code works much like the first. The = operator is encountered several times, which is most certainly striking as the symbol of assignment operations. Then a certain Console.WriteLine() is called, implying that the values ​​will be output to the console. There are also several lines in which interpolation appears to be used to compose messages. Conceptually, there is nothing particularly surprising here - assignment, interpolation, output to the console, all these operations are already known to you for working with Ruby.

Perhaps you will understand the second code fragment without any knowledge of C #, but there is definitely room for expanding your mental model. For example, why is everything wrapped in a Main() method? What are these keywords int , double and string ? This is where the training begins. You already understand in general what is happening here (assignment, interpolation, output), now is the time to move on to the details:


The above example is very simple, but even a number of important conclusions can be drawn from it. You have already learned that the C # program requires a well-defined entry point, that this language is statically typed (unlike Ruby) and object-oriented (like Ruby). You figured it out because you already have an idea of ​​what variables and methods are, and then expanded these mental models, enriching them with the phenomenon of typification.

Search for differences


Starting to try on how to read and write code in a new language, first of all you need to find out what things are already known and can serve as a basis for learning. Next, go to the differences. Let's return to our transition from Ruby to C # and look at something more complicated.

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

In this fragment in Ruby, we define an array called particles , which will have several lines, and then use Array#push to add a few more lines to it, and Array#each to iterate through the array and output each individual line to the console. But how to do the same in C #? A little googling, we learn that there are typed arrays in C # (typing should no longer surprise you, considering what you learned earlier), and also there is the SetValue method, which slightly resembles push , but takes the value and position in the index as parameters. In this case, the first attempt to rewrite Ruby code in C # might look like this:

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

Unfortunately, this code will give a Run-time exception runtime Run-time exception when you try to use SetValue to add a new value to the array. We look at the documentation again and find out that the arrays in C # are not dynamic, and must be initialized either with all the values ​​at once, or with an indication of the length. Again, trying to reproduce the Ruby code, we take this into account and get the following option:

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

This fragment really reproduces all the functionality of the Ruby source code, but with a big stretch: it just outputs the same values ​​to the console. If you take a closer look at both fragments, then the problem is quickly discovered: there can be no more than 5 values ​​in the particles array in the particles array in the particles array, whereas in the fragments in Ruby they are allowed as many as you want. Then it becomes clear that arrays in Ruby and C # are fundamentally different: the first one has a dynamic size, and the second one does not. In order to properly reproduce the Ruby fragment fragment in C #, we need the following code:

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

It uses a List data structure that allows you to dynamically collect values. In this case, we actually reproduce the original Ruby code in C #, but, more importantly, here we can appreciate the key difference between the two languages. Although, in both languages ​​the term “array” is used and it may seem that these arrays are one and the same, in practice they are quite different. Here you have one more thing that helps to expand the mental model, to more fully understand what an “array” is and how it works. In C #, an array as a data structure may or may not be suitable in situations where you would use arrays in Ruby; Speech about situations where dynamic resizing of an array is critical. Now you have to take care of this in advance and think through your code accordingly.

Returning to key principles


It is very convenient to start learning new languages, exploring their similarities and differences compared to already known languages; however, in some cases it is better to start with universal principles. Above, we logically concluded that C # is an object-oriented language when we worked with the built-in class and one of its methods, System.Console.WriteLine() , with which we performed the action. It is logical to assume that C #, like other object-oriented languages, has a mechanism for defining a class and instantiating objects from it. This is a basic principle of object-oriented programming, so there is little doubt that our assumption is correct. First, let's look at how this operation might look like in the Ruby language we know.

 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 

Here we have a simple Element class, which has a constructor method to accept values ​​and assign them to instantiated objects, a set of access methods for setting and retrieving values, and an instance method for outputting these values. In this case, the key concepts are the idea of ​​a class, the idea of ​​a constructor method, the idea of ​​getters / setters, and the idea of ​​an instance method. Returning to our ideas about what can be done in object-oriented languages, consider how to do the same in 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 ); } } } 

Having studied this fragment in C #, we see that, in fact, it is not so different from the Ruby version. We define a class, use the constructor to specify how the class will instantiate objects, define getters / setters, and determine the method of the instance that we will call in the created objects. Naturally, the two fragments are quite different in appearance, but not in a most unexpected way. In the C # version, we refer to the object being instantiated with this , whereas in Ruby we use self for this. The C # version is typed both at the method level and at the parameter level, but not in Ruby. However, at the level of key principles, both fragments are almost identical.

Developing this topic, we can consider the idea of ​​inheritance. It is known that subclass inheritance and education are key points of object-oriented programming, so it is easy to understand that in C # this is done with the same success as in 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 

In the Ruby version, we define a NobleGas subclass that inherits from our Element class; its constructor uses the super keyword, which extends the constructor of the parent class and then overrides the instance method describe , to define a new behavior. The same can be done in C #, but with a different syntax:

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

At first glance, when we still did not know anything about C #, this last listing might have seemed intimidating. The syntax is unfamiliar, some strange keywords and code are not organized as we are used to. However, if we consider this code from the point of view of basic principles, the difference is not so significant: here we just have to define a class, a set of methods and variables, and a set of rules for instantiating and using objects.

TL; DR


Learning a new language can be hellishly difficult if you do it from scratch. However, most programming languages ​​are based on the same basic principles, between which it is easy to draw parallels, notice important differences and apply in many languages. Trying on a new language on already established mental models, one can find where it does not differ from the languages ​​already studied, and where something really needs to be clarified. Studying with time more and more new languages, you expand your mental models, and such refinements require less and less - you recognize various implementations in various languages ​​from the sheet.

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


All Articles