Swift for Ruby Devs:基础知识pt。 2

在上一篇文章中,Swift for Ruby Devs:The Basics pt。 参见图1,我们研究了最基本的数据类型在Swift和Ruby之间的语法差异。 我们介绍了变量和常量,字符串,整数和浮点数,运算符以及集合类型,例如数组和哈希(在Swift中称为字典)。 在了解了这些基本数据类型之后,我们将研究如何在Swift中编写控制流结构,以及它们在Ruby中的不同之处。 我们还将学习Swift中的方法,结构和类。

控制流程结构

循环和迭代器

在Swift中,有很多不同的机制可以遍历数组,字典,字符串,范围和其他序列。 尽管语法可能有些不同,但它们在大多数编程语言中都很常见。 如果您是Ruby / Rails开发人员,则应该已经熟悉这些内容,尤其是如果您之前编写过一些Javascript。

在大多数编程语言中,“ for-in”循环很常见。 即使Ruby也会实现它,尽管我们更喜欢其他迭代方法来编写更多惯用的Ruby。

  #红宝石 
  #数组 
 生物= [“尼斯湖怪兽”,“萨斯喀彻奇”,“唐纳德·特朗普”] 
 对于生物中的生物 
输入“您好,我叫#{creature}。”
结束


#哈希
 生物= {狗:“汪汪”,猫:“喵”,donald_trump:“拥抱!”} 
 对于钥匙,生物的价值 
放置“#{key}和#{value}”
结束

如果您以前编写过Javascript,那么您已经熟悉使用花括号打开和关闭函数,类,控制流语句等。 在Swift中也是如此,这是将Swift的语法与Ruby的语法进行比较时会注意到的主要区别之一。

  //迅捷 
  //数组 
 让生物= [“尼斯湖怪兽”,“萨斯喀彻奇”,“唐纳德·特朗普”] 
 对于生物中的生物{ 
打印(“你好,我叫\(生物)。”)
}


//字典
 让动物= [“狗”:“ woof”,“猫”:“喵”,“唐纳德·特朗普”:“呵呵!”] 
 为生物中的(键,值){ 
打印(“ \(键)和\(值)”)
}

值得一提的是,Swift中的字典是按照相反的顺序进行迭代的,而Ruby中的哈希是从哈希中定义的第一个键值对进行迭代的。 如果您使用的是哈希/字典,那么您可能不在乎事物的顺序。

但是,在编写惯用的Ruby时,在遍历数组或哈希执行某些任务时,我们更喜欢使用“ each”,“ map”,“ collect”和“ reduce”等方法代替“ for-in”循环。 幸运的是,Swift的标准库为我们提供了完成相同任务的相似方法。

  //迅捷 
 让生物= [“尼斯湖怪兽”,“萨斯喀彻奇”,“唐纳德·特朗普”] 
 生物.forEach {(生物)在 
打印(“你好,我叫\(生物)。”)
}
  //迅捷 
 设数字= [1,2,3] 
  var newArray = numbers.map {$ 0 + 1} 
  newArray = Numbers.map {(number in 
//做跨多行的工作
数+ 1
}
  listOfStrings = numbers.map {(number)->字符串输入 
返回“这是数字\(数字)。”
}

在上面的示例中,我们仅查看了“ forEach”和“ map”,但是Swift的标准库提供了许多遍历和转换数组和字典的方法。 这样的方法包括flatMap,reduce和filter,它们可以完成与Ruby的select和reject方法相同的任务。

在上面的示例中,您可能已经注意到了“ $ 0”,并且想知道这意味着什么。 Swift为从集合传递到闭包中的每个元素提供了一个简写。 它可以减少代码的冗长性(有时以可读性为代价),从而避免编写冗长的参数列表。 以下是Apple的Swift语言指南中有关闭包的摘录:

Swift会自动为内联闭包提供速记参数名称,可使用$ 0,$ 1,$ 2等名称来引用闭包参数的值。

如果在闭包表达式中使用这些速记参数名称,则可以从其定义中省略闭锁的参数列表,而速记参数名称的数量和类型将从所需的函数类型中推断出来。 in关键字也可以省略,因为闭包表达式完全由其主体组成:

reversedNames = names.sorted(by:{$ 0> $ 1})

在这里,$ 0和$ 1引用闭包的第一个和第二个String参数。

专业提示 :要了解Swift标准库中所有可用的方法和属性,请在Xcode的文档和API参考指南中搜索“ Array”,“ Dictionary”等。 在Xcode中,输入Shift + Command + 0打开文档。

条件语句

Swift提供了两种向代码中添加条件分支的方法。 这些是`if`和`switch`语句。

如果陈述

  //迅捷 
 如果isWeekday &&(isRaining || isSnowing){ 
打印(“勉强准备穿衣服”)
}
否则,如果isWeekend &&(isRaining || isSnowing){
打印(“留在床上”)
}
其他{
打印(“穿好衣服”)
}
  #红宝石 
 如果工作日?  &&(正在下雨吗?||正在下雪吗?) 
放置“不情愿准备穿衣服”
elsif周末? &&(正在下雨吗?||正在下雪吗?)
放置“卧床休息”
其他
放置“很高兴穿衣服”
结束

比较这些等效的“ if”语句,我们看到两个主要区别。 首先,在Swift中,我们必须使用大括号“ {}”打开和关闭语句。 第二个区别是我们编写“ else if”语句的方式。 在Ruby中,我们仅使用`elsif`关键字。 在Swift中,我们写“ else if”。 如果您已经编写了Javascript,那么Swift的语法应该看起来很熟悉。

切换语句

  //迅捷 
 让食物=“西瓜” 
 换食物{ 
案例“草莓”:印刷(“浸入巧克力”)
案例“西瓜”:
打印(“切成碎片”)
打印(“吃”)
默认:
打印(“扔掉”)
}
  #红宝石 
 食物=“西瓜” 
 案例食物 
当“草莓”
放入“蘸巧克力”
当“西瓜”
把“切成碎片”
其他
把“扔掉”
结束

在编写switch / case语句时,Swift和Ruby之间有一些区别。 Swift在检查一个值时使用`switch`关键字,在将该值与可能的匹配模式进行比较时使用`case`关键字。 另一方面,Ruby在检查值时使用`case`关键字,在将值与几种可能的匹配模式进行比较时使用`when`关键字。

另一个区别是,如果我们要打开的值可能与任何case语句都不匹配,则Swift使用并要求使用default关键字来执行某些工作。 这是因为Swift要求switch语句是详尽无遗的。 意味着必须考虑所有与我们匹配的值的情况。 如果不是,那么我们必须在语句末尾提供一个“默认值”。 在Ruby中,“ else”完成与“ default”相同的任务。 唯一的区别是Ruby不需要`else`语句,并且如果该值从未匹配,则只会返回`nil`。

功能

Swift中的函数(或方法)的编写方式与Ruby中的编写方式截然不同,并通过示例来更好地理解。 让我们检查两种方法。 一个将两个数字相加,另一个输出结果。

  #红宝石 
  def add_two_numbers(a,b) 
a + b
结束
  def print_the_sum(first_num,second_num) 
sum = add_two_numbers(first_num,second_num)
放置“#{first_num}和#{second_num}的总和为#{sum}”
结束
  print_the_sum(3,3) 
  //迅捷 
  func addTwoNumbers(a:Int,b:Int)-> Int { 
返回a + b
}
  func printTheSum(firstNum:Int,secondNum:Int){ 
让sum = addTwoNumbers(a:firstNum,b:secondNum)
print(“ \(firstNum)和\(secondNum)的和为\(sum)”)
}
  printTheSum(firstNum:3,secondNum:3) 

Swift中的每个函数都有一个类型 。 函数的类型由其参数的类型和函数的返回类型组成。 这意味着我们必须指定每个参数的类型以及方法返回值的返回类型。 例如,如果某个函数只是在控制台上打印一些内容,则无需指定返回类型。 在这种情况下,Swift会推断出返回类型为“无效”。

我们通过提供参数名称,后跟冒号“:”和类型(“ a:Int”)来指定参数类型。 指定参数(如果有的话)后,我们使用`-> Type`语法指定返回类型。 在上面的示例中,`addTwoNumbers`指定了Int的返回类型,而`printTheSum`根本没有指定返回类型。

Swift中的参数语法与Ruby中的关键字参数相似。 尽管我们没有在Ruby中指定类型,但是我们使用关键字参数调用方法的方式大致相同:

  #红宝石 
  def add_two_numbers(a :, b :) 
a + b
结束
  add_two_numbers(a:2,b:3) 
=> 5

Swift函数的一项独特功能就是所谓的参数标签。 在定义函数将接受的参数时,可以为每个参数提供参数标签。 参数标签在调用函数时使用,其目的是使代码在调用现场更具可读性。 与参数标签关联的参数名称在函数的实现中使用。 参数标签紧接在函数定义中的参数名称之前。 这是一个“ sayHello”方法的示例,该方法使您可以对两个人说“ Hello”。

  //迅捷 
  func sayHello(发送给人:字符串,另一个人:字符串)->字符串{ 
返回“你好\(人)和\(另一个人)”
}
  sayHello(收件人:“ Danny”和“ Fred”) 

在上面的示例中,“ person”和“ anotherPerson”是函数实现中使用的参数名称。 参数标签为“ to”和“ and”。 现在,调用该方法更像是完整的句子。 然而,在该方法的实现中,“至”和“与”的含义要比“人”和“另一个人”的含义小。

Swift中函数的另一个独特功能是您无法在Ruby中找到的,它能够在类,结构等中定义具有相同名称的函数。由于Swift中的每个函数都有一个类型签名 ,因此,就参数和返回类型而言,就编译器而言,具有相同名称但具有不同类型的函数被视为两个完全不同的函数。 再说说sayHello函数,假设我们还想编写一个只向一个人说“ Hello”的函数。

  //迅捷 
  func sayHello(发送给人:字符串,另一个人:字符串)->字符串{ 
返回“你好\(人)和\(另一个人)”
}
  sayHello(收件人:“ Danny”和“ Fred”) 
  func sayHello(to person:String)-> String { 
返回“你好\(人)”
}
  sayHello(致:“比利”) 

Swift中的功能具有许多强大的功能。 有关深入的概述,请参见Swift 3语言指南。

结构

尽管Swift中的结构与Ruby中的结构具有相同的目的,但它们具有该语言独特的一些功能。 让我们看一下具有“名称”和“地址”属性的“客户”结构。

  #红宝石 
 客户= Struct.new(:name,:address) 
  a_customer = Customer.new(“ Danny”,“ 123 Main St.”) 
  //迅捷 
 构造客户{ 
变量名称:字符串
var地址:字符串
}
 让aCustomer = Customer(名称:“ Danny”,地址:“ 123 Main St.”) 

在Ruby中,结构是通过初始化`Struct`类的实例并为其提供成员值以及`Struct`的可选名称来创建的。 然后,我们可以根据需要创建任意数量的该结构实例,并为`:name`和`:address`提供值。

为了在Swift中创建一个结构,我们使用struct关键字,后跟一个大写的结构名称。 在闭包内,我们通过变量或常量提供属性。 在上面的Swift示例中,我们通过为每个属性提供值的方式与为方法提供参数的方式相同,从而创建了Customer结构的新实例。

在Ruby和Swift中,如果需要某种行为,我们都可以为结构提供方法。 Swift的独特之处在于,每次创建新结构时,我们都可以覆盖初始化方法来添加自定义行为。

  #红宝石 
 客户= Struct.new(:name,:address)做 
def问候
“您好,#{name}。 您的地址是#{address}。”
结束
结束
  a_customer = Customer.new(“ Danny”,“ 123 Main St.”) 
  a_customer.greeting 
=>“你好,丹尼。 您的地址是123 MainSt。”
  //迅捷 
 构造客户{ 
变量名称:字符串
变量地址:字符串
  init(名称:字符串,地址:字符串){ 
self.name =名称
self.address =地址
//在初始化struct时还要做一些其他的事情。
}
  func greeting()->字符串{ 
返回“你好,\(名称)。 您的地址是\(地址)。”
}
}
 让aCustomer = Customer(名称:“ Danny”,地址:“ 123 Main St.”) 
  aCustomer.greeting() 
=>“你好,丹尼。 您的地址是123 MainSt。”

班级

当比较在Swift和Ruby中创建类之间的语法差异时,对我来说最突出的是初始化方法和从基类继承。 这是创建一个继承自Automobile基类的Lamborghini子类的示例。

  #红宝石 
 汽车类 
attr_reader:make,:model
  def initialize(make,model) 
@make =制造
@model =模型
结束
  def turn_on 
将“插入钥匙点火”
结束
结束
 类别兰博基尼<汽车 
attr_reader:所有者
  def initialize(模型,所有者) 
@owner =所有者
超级(“兰博基尼”,型号)
结束
  def turn_on 
把“推开始”
结束
结束
  lambo = Lamborghini.new(“ Aventador”,“ Danny”) 
  //迅捷 
 汽车类{ 
让做:字符串
让模型:字符串
  init(make:String,model:String){ 
self.make =制造
self.model =模型
}
  func turnOn(){ 
打印(“插入点火钥匙”)
}
}
 兰博基尼课程:汽车{ 
让所有者:字符串
  init(model:String,owner:String){ 
self.owner =所有者
super.init(制造:“ Lamborghini”,型号:model)
}
 覆盖func turnOn(){ 
打印(“一键启动”)
}
}
 让兰博=兰博基尼(型号:“ Aventador”,所有者:“丹尼”) 

在此示例中,“汽车”包含所有汽车共有的两个属性; 一个`make`和`model`。 它还包括一种打开汽车的方法。 Lamborghini类从Automobile继承,并包含一个附加的owner属性。 它还覆盖了打开汽车的方法。

在Ruby中,我们定义了用于在“ initialize”方法中初始化类实例的逻辑。 Swift通过包含`init`方法提供了类似的行为。 定义“ init”方法时,不需要像其他所有函数中那样使用“ func”关键字。 “ init”方法不会返回值,它的唯一目的是确保在使用类之前正确初始化该类的实例。

当从Ruby中的基类继承时,我们使用`<`语法并提供基类的名称。

  #红宝石 
 类别兰博基尼<汽车 
结束

在Swift中,我们改为使用`:`语法。

  //迅捷 
 兰博基尼课程:汽车{ 
}

您可能已经注意到的另一件事是,如何在子类中重写基类的方法。 在Ruby和Swift中,我们只需在子类中重写函数即可。 唯一的区别是,在Swift中,我们还必须在函数声明之前包含`override`关键字。

  #红宝石 
 类别兰博基尼<汽车 
def turn_on
把“推开始”
结束
结束
  //迅捷 
 兰博基尼课程:汽车{ 
覆盖func turnOn(){
打印(“一键启动”)
}
}

您可能已经注意到的最后一件事是我们如何从子类中调用基类中的方法。 在Ruby中,我们只需从方法中调用“ super”以及任何参数即可。 在Swift中,我们必须明确要在超类上调用的方法。

  #红宝石 
  def初始化 
超级(param1,param2)
结束
  //迅捷 
 在里面() { 
super.init(param1:param,param2:param)
}

有关Swift中的结构和类的深入说明,请参见Swift 3语言指南。

摘要

现在,我们已经掌握了Swift的基础知识,我们可以开始编写简单的程序,并为开始学习Swift特有的更复杂的概念做好了更好的准备。 作为Ruby开发人员,您可能不熟悉的概念,例如协议,枚举(不要与Ruby中的枚举混淆),可选,元组和类型转换。 要真正巩固您在Swift基础上的技能,上面链接的Swift 3语言指南是一个不错的起点。 此外,exercism.io还有很多代码挑战,这些挑战使您可以练习基本的Swift技能并免费获得反馈!