我们依靠已学到的知识来掌握新的编程语言

大家好



来自Unsplash的 Jenny Marvin快照

今天,我们以Ruby和C#为例为您准备了一篇有关许多编程语言的基本相似之处的文章的翻译。 我们希望受人尊敬的Severin Peres的想法将帮助你们中的许多人迅速开始学习一种新的编程语言,并且一切都会真正地有意义和愉快。

程序员没有收获的东西-他从未停止学习。 您可能拥有最喜欢的语言,框架或库,但是毫无疑问您不能只使用它们。 您可能喜欢JavaScript,但是当前正在处理的项目可能需要Python。 您可能精通Perl,但您公司的代码库可以用C ++编写。 对于新手开发人员而言,学习一种新语言的想法似乎令人生畏,尤其是在临近期限的情况下。 这是坏消息。 但是,有一个不错的选择:学习一种新语言通常并不难。 如果我们以现有的思维模型为基础,那么您将看到学习一种新语言基本上是现有知识的扩展,而不是从头开始。

他们有什么共同点


大多数编程语言基本上都基于相同的关键原则集。 实现方式不同,但是几乎没有两种语言如此不同,以致于无法在它们之间画出相似之处。 为了学习和理解一种新语言,最重要的是确定它看起来像您已经知道的样子,然后获得新知识,并在必要时扩大对它的理解。 例如,考虑数据类型和变量。 在每种语言中,都有一种方法来定义和存储数据-在整个程序中统一。 因此,在学习新语言时,您首先需要了解如何在此处定义和使用变量。 以两种不同的语言为例:具有动态类型的解释型Ruby和具有静态类型的已编译C#。

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

一个类似的例子:

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

假设您是一位经验丰富的Ruby开发人员,并且想学习C#。 以下是代码片段,您可以在其中轻松识别Ruby。 在那里,您只需要定义几个变量并将其显示在控制台中即可。 现在注意第二个片段。 你学到什么吗? 语法不同,但是毫无疑问,第二个代码的工作原理与第一个代码非常相似。 多次遇到=运算符,这可能是赋值操作的标志。 然后,将调用某个Console.WriteLine() ,这意味着这些值将显示在控制台中。 这里还有几行似乎使用插值来编写消息。 从概念上讲,这里没有什么特别令人惊讶的事情-赋值,内插,输出到控制台,使用Ruby已经知道了所有这些操作。

您可能在没有任何C#知识的情况下理解了第二个代码片段,但是肯定有扩展您的思维模型的空间。 例如,为什么每个人都用Main()方法包装自己? 这些intdoublestring关键字是什么? 这样,培训就开始了。 您已经大致了解了这里发生的事情(赋值,内插,输出),现在是时候进行详细说明了:

  • Main() :对情况有更深入的了解,我们发现Main()方法是程序启动的入口点。 现在我们知道,在所有C#程序中,我们都需要Main()方法。
  • 变量:在C#片段的第一部分中,确实发生了某种赋值。 给定命名法,您可能会猜测int关键字表示整数变量, double是双精度浮点数,而string是字符串变量。 几乎立即,您就会意识到,在C#中,与Ruby不同,由于需要为不同的数据类型声明不同的变量,因此需要静态键入变量。 阅读文档后,您将了解它的不同之处。
  • Console.WriteLine() :最后,运行程序,您将看到Console.WriteLine()在控制台中显示值。 在Ruby中,您知道puts是全局$stdout对象的方法,如果查看Console.WriteLine()的文档,您会发现ConsoleSystem命名空间中的类,而WriteLine()是在此方法中定义的方法。教室。 这不仅看起来像puts ,而且表明C#和Ruby一样,是一种面向对象的语言。 在这里,您还有另一个思维模型,可以帮助您追踪新的相似之处。

上面的例子非常简单,但是甚至可以从中得出许多重要的结论。 您已经了解到C#程序需要一个定义明确的入口点,该语言是静态类型的(不同于Ruby)和面向对象的(例如Ruby)。 您已经弄清楚了,因为您已经想象了什么是变量和方法,然后扩展了这些思维模型,使它们充满了典型现象。

寻找差异


开始尝试如何用一种新的语言读写代码,首先需要了解哪些是已知的,并且可以作为学习的基础。 接下来,继续进行区别。 让我们回到从Ruby到C#的过渡,然后看一些更复杂的东西。

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

在这个Ruby片段中,我们定义了一个名为particles的数组,该数组将包含几行,然后使用Array#push向其添加更多行,并使用Array#each行遍历该数组并将每行输出到控制台。 但是在C#中该怎么做? 稍作搜索,我们发现C#已经对数组进行了类型化(考虑到您先前学到的内容,键入不再会让您感到惊讶),并且还有一个SetValue方法,它稍微类似于push ,但是将索引中的值和位置作为参数。 在这种情况下,第一次尝试用C#重写Ruby代码可能会导致以下结果:

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

不幸的是,当您尝试使用SetValue向数组添加新值时,此代码将引发Run-time exception 。 再次,我们查看文档,发现C#中的数组不是动态的,应立即使用所有值或使用长度指示进行初始化。 再次尝试重现Ruby代码,请考虑一下并获得以下选项:

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

该代码段确实重现了Ruby源代码的所有功能,但涉及的范围很大:它只向控制台显示相同的值。 如果仔细检查两个片段,就会很快发现一个问题:粒子数组中C#中的一个片段中最多可以有5个值,而Ruby中的一个片段中则有尽可能多的值。 然后很明显,Ruby和C#中的数组本质上是不同的:前者具有动态大小,而后者则没有。 为了正确地重现C#中的Ruby片段功能,您需要更多以下代码:

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

这使用List数据结构来动态收集值。 在这种情况下,我们实际上是用C#复制原始的Ruby代码,但更重要的是,在这里我们可以体会两种语言之间的主要区别。 尽管在两种语言中都使用了术语“数组”,并且看起来这些数组是相同的,但实际上它们是完全不同的。 这是有助于扩展心理模型,更好地了解“数组”及其排列方式的另一件事。 在C#中,将数组作为数据结构可能不适合使用Ruby的情况; 我们正在谈论动态调整数组大小至关重要的情况。 现在,您必须事先做好准备,并仔细考虑代码。

返回关键原则


开始学习新语言,探索它们与已知语言的异同非常方便。 但是,在某些情况下,建议从通用原则开始。 上面,我们从逻辑上得出结论,当我们使用内置类及其方法之一System.Console.WriteLine()来执行操作时,C#是一种面向对象的语言。 逻辑上假设,与其他面向对象的语言一样,在C#中存在定义类并从中实例化对象的机制。 这是面向对象编程的基本原理,因此您对我们假设的正确性毫无疑问。 首先,让我们看一下在熟悉的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 hydrogen = Element.new("Hydrogen", "H", 1) hydrogen.describe 

在这里,我们有一个简单的Element类,其中有一个构造函数方法来接受值并将其分配给实例化的对象,还有一组用于设置和接收值的访问方法,以及用于输出这些值的实例方法。 在这种情况下,关键概念是类的思想,构造方法的思想,getter / setter的思想以及实例方法的思想。 回到关于可以在面向对象的语言中完成操作的想法,我们将研究如何在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 ); } } } 

在C#中研究了此片段后,我们发现,实际上它与Ruby版本没有太大区别。 我们定义一个类,使用构造函数指定该类将如何实例化对象,定义getters / setters并确定在创建的对象中将调用的实例方法。 当然,这两个片段的外观有很大不同,但并非以最意外的方式。 在C#版本中,我们使用this来引用实例化的对象,而在Ruby中,我们使用self来实现。 在方法级别和参数级别都键入C#版本,而在Ruby中则不是。 但是,在关键原则方面,两个片段几乎是相同的。

开发这个主题,我们可以考虑继承的概念。 众所周知,继承和子类化是面向对象编程的关键点,因此很容易理解,在C#中,这样做与在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 

在Ruby版本中,我们定义了NobleGas的子类,该子类继承自Element类; 它的构造函数使用super关键字,该关键字扩展了父类的构造函数,然后重写了describe实例方法以定义新行为。 在C#中可以完成相同的操作,但是语法不同:

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

乍一看,当我们仍然对C#一无所知时,最后的清单似乎令人生畏。 语法不熟悉,有些奇怪的关键字和代码没有像我们以前那样组织。 但是,如果从基本原理的角度考虑此代码,则差异并不是那么明显:这里我们只有一个类定义,一组方法和变量以及一系列实例化和使用对象的规则。

TL; DR


如果您从头开始学习新语言,可能会非常困难。 但是,大多数编程语言都基于相同的基本原理,在这两种基本原理之间很容易得出相似之处,注意到重要差异并适用于多种语言。 在现有的心理模型上尝试一种新的语言,您可以找到与已学习的语言没有什么不同的地方,以及您真正需要澄清的地方。 随着时间的推移,研究越来越多的语言,您会扩展自己的思维模式,而对这种改进的需求也越来越少-您将在工作表中认识到各种语言的各种实现。

Source: https://habr.com/ru/post/zh-CN422869/


All Articles