Swift 初探:集 C++/Golang/Haskell/Rust 众多特性于一身
去年 WWDC 2014 苹果推出了全新的编程语言 Swift,不仅让全球 iOS 开发者为之兴奋,甚至让很多其他阵营的开发者都跃跃欲试。
Objective-C 确实很老了,感觉它就和 C++ 一样,历史包袱太重:本来就要兼容 C,还要不断添加新特性,支持各种编程范式,积重难返。
虽然作为一门新的语言 Swift 还需要逐步完善,一些 API 可能还有变动的可能,但毕竟大势所趋,未来它肯定是 iOS 开发者的必备技能。(目前已有不少相关文档、社区、开源项目)
最近从网上的反馈看,很多其他平台(语言)的开发者在尝试了 Swift 之后,都从中找到了自己原来熟悉的语言的影子。
我个人虽然主业是 Android,但定位一直是 Android 和 iOS 双修(外加 Golang),之前也系统学习过 Objective-C 和 iOS 开发,所以 Swift 自然是必学的。
况且除了 Java、Objective-C 和 Golang,之前还写过 C++、Haskell、Lua、Rust、C#、JavaScript 等不少语言的代码,怀着好奇,也迫切想看看 Swift 到底吸收了多少其他语言的精华。
一开始买了本《Swift权威指南》,看了一大半发现基本就是官方文档的中文翻译,而且部分 API 有变化也没更新。后来发现还是《Swift语言实战入门》这本书讲得比较深入。
书还未看完,故只对基本语法层面的进行总结。
TypeAlias 类型别名:类似 C++ 的 typedef 和 Golang 的 type
Swift 版本:
typealias status = Int8
C++ 版本:
typedef status int
Golang 版本:
type status int
Optional 可选类型: 类似 Haskell 中的 Maybe 类型
Optional
可选类型,表示变量有值时返回存储的值,没有值返回 nil
。它可以在变量未声明的情况下确保后面调用的安全性。
用法为 Optional<Type>
或者 Type?
。 例如:
var i:Optional<Int>
var str:String?
Haskell 中也有类似的东西: Maybe
类型,它是这样定义的:
data Maybe a = Nothing | Just a deriving (Eq, Ord, Read, Show)
用法也类似:
i :: Maybe Int
枚举支持元组:类似 Rust
Swift 不仅支持基本的枚举类型,还可以是元组:
enum Product
{
case Car(String, Int)
case Phone(String, String)
}
Rust 中的枚举也有类似用法:
enum Shape {
Circle { center: Point, radius: f64 },
Retangle { top_left: Point, bottom_right: Point }
}
用于 switch case 的 fallthrough: 类似 Golang 中的 fallthrough
当初学 C 语言的时候就觉得 switch case
设计成默认贯穿很坑爹:实际开发中多数是不需要贯穿的,这就导致代码中一大堆 break
。
现在 Swift 终于从 Golang 吸收过来这个特性,一般不需要处理 case
,少数需要贯穿的情况才加上 fallthrough
,二者用法也类似,代码就不贴了。
switch case 支持 where 语句:类似 Haskell 中 Pattern Guard 的 where 语句
Swift 中的 switch case
语句支持 where
条件判断:
let point = (-1, 2, 0)
switch point
{
case (let x, _, _):
println("x: \(x)")
fallthrough
case (x, y, z) where x * y * z == 0:
println("x: \(x), y: \(y), z: \(z)")
default:
println()
}
Haskell 中虽然没有 switch case
,但有类似的 Pattern Guard,而且也支持 where
语句:
funcBMI :: (RealFloat a) => a -> a -> String
funcBMI weight height
| bmi <= bmi_thin = "Thin."
| bmi >= bmi_fat = "Fat."
| otherwise = "Normal."
where bmi = weight/height^2
(bmi_thin,bmi_fat) = (18.5,30.0)
支持函数类型:类似 C++11 中的 std::function 类型
Swift 中可定义函数类型,并作为变量类型、参数类型、返回值类型:
//定义函数类型:
typealias FuncType = (Double, Double) -> Double
//函数类型的变量:
var funcDDD : FuncType
//函数类型的参数:
func printFuncResult(fun : FuncType, x : Double, y : Double) {
println(fun(x, y))
}
//函数类型的返回值:
func getFunc() -> FuncType {
return funcDDD
}
甚至还可以将函数类型结合闭包使用:
typealias FuncType = (Double, Double) -> Double
func printFuncResult(x : Double, y : Double, fun : FuncType) {
println("\(fun(x, y))")
}
//定义闭包:
let funPlus = { (x : Double, y : Double) -> Double in return x + y }
//将闭包作为函数类型的参数传入函数:
printFuncResult(0.1234, 5.6789, funPlus)
C++11 中的 std::function
也可定义一个函数指针,也有上述类似用法:
//定义函数类型指针变量:
std::function<double(double, double)> multiply;
//将变量声明为该类型的 Lambda:
multiply = [] (double x, double y) -> double {
return x * y;
};
//调用 Lambda:
std::cout << multiply(1.234, 5.6789) << std::endl;
支持运算符重载和定义新运算符:类似 C++ 中的运算符重载
Swift 中可用 prefix
重载前缀运算符:
prefix func - (p : Point) -> Point {
return Point(x : -p.x, y : -p.y)
}
也可用 postfix
重载后缀运算符:
postfix func ++ (p : Point) -> Point {
var px = p.x, py = p.y
return Point(x : ++px, y : ++py)
}
甚至可以使用 operator
定义新的运算符:
prefix operator %& {}
prefix func %& (p : Point) -> Point {
return Point(x : p.x % 8, y : p.y % 8)
}
C++ 中的运算符重载那是出了名的强大,甚至可以重载 IO 运算符,这里仅给出基本用法:
class Point {
public:
Point(int i1, int i2, int i3): x(i1), y(i2), z(i3){}
Point operator-();
Point operator++();
bool operator==(const Point &p);
Point operator+(const Point &p);
int operator[](size_t n);
private:
int x, y, z;
int values[3] = {x, y, z};
};
Point Point::operator-() {
return Point(-x, -y, -z);
}
Point Point::operator++() {
return Point(++x, ++y, ++z);
}
bool Point::operator==(const Point &p) {
return x == p.x && y == p.y && z == p.z;
}
Point Point::operator+(const Point &p) {
return Point(x + p.x, y + p.y, z + p.z);
}
int Point::operator[](size_t n) {
return values[n];
}
int main (int argc, char **argv) {
Point p1(1, 3, 5);
Point p2(2, 4, 6);
std::cout << (p1 == p2 ? "p1 == p2" : "p1 != p2") << std::endl;
Point p3 = -(p1 + p2);
//p3.operator++();
++p3;
std::cout << "p3: " << p3[0] << ", " << p3[1] << ", " << p3[2] << std::endl;
return 0;
}
函数参数可设置是否可变:类似 C++
C++ 中在函数前添加 const
即可声明为不可变,代码就不贴了。
Swift 中函数参数默认就是不可变的,如果要在函数内部修改参数的值,需要在参数前声明 var
关键字:
func printContactVar(var name: String, email : String) {
name = name.uppercaseString
printContact(name: name, email: email)
}
上面的 var
参数虽然能修改值,但作用域仅限于函数内部,如果想在函数调用结束后,在外部依然生效,还可将关键字改为 inout
。
函数参数可设置默认值:类似 C++
Swift 中定义函数时,可设置参数默认值:
func printContact(name : String = "Rinc", email : String = "i@RincLiu.com") {
println("\(name): \(email)")
}
C++ 也有类似特性:
void printContact(std::string name = "Rinc", std::string email = "i@RincLiu.com");
void printContact(std::string name, std::string email) {
std::cout << name << ": " << email << std::endl;
}
可通过 count 和 repeatedValue 初始化数组:类似 C++
Swift 版本:
var a = [Character](count: 10, repeatedValue: "X")
C++ 版本:
std::vector<char> v(10, 'x');
元组相关特性:类似 Golang
Swift 中可以通过 _
不取元组中的某个元素:
let people = ("Rinc", age : 25)
var (name, _) = people
还有用于集合数据的遍历:
var dic : Dictionary<String, Int> = ["Rinc" : 25, "Emma": 24]
for (k, _) in dic {
println("\(k)")
}
Golang 由于经常取回的数据集含有索引(连数组都有),所以这种用法更常见:
mp := map[string]float32{"C": 5, "GO": 4.5, "JAVA": 6}
for key, _ := range mp {
fmt.Println(key)
}
虽然 C++11、Rust 等语言也支持元组,但感觉大多简单用于函数多值返回,像上面这种用法貌似没见过;
其他特性
函数语句结束不加分号:Golang、Rust 等很多语言都支持;
var
动态类型声明:JavaScript、Golang 等语言都支持;函数多值返回:因为有了元组的支持,所以实现这个并不难,C++、Golang、Rust 等语言都支持;
闭包:这东西现在随着函数式编程的热门,几乎所有语言都提供了支持,也不多说了。
总结
总体来看 Swift 吸收的 C++ 特性最多,其次是 Golang 和 Hashekll,还有少量 Rust 特性。
当然,因为我毕竟没有接触过所有其他编程语言,而且以上提到的几种语言也并不是全部都很了解,所以仅仅是个人看法。
最后附上 所有 Swift 代码示例。