登录 |  注册
首页 >  移动开发 >  Swift >  Swift函数式编程惰性计算

Swift函数式编程惰性计算

Swift函数式编程-惰性计算

Swift支持函数式编程,这一篇介绍Swift的惰性计算。

惰性计算

惰性计算是函数式编程语言的一个特性。在使用惰性计算时,表达式不在它被绑定到变量之后就立即求值,而是在该值被取用的时候求值。惰性计算有如下优点:

  • 首先,你可以用它们来创建无限序列这样一种数据类型。因为直到需要时才会计算值,这样就可以使用惰性集合模拟无限序列。

  • 第二,减少了存储空间。因为在真正需要时才会发生计算。所以,节约了不必要的存储空间。

  • 第三,减少计算量,产生更高效的代码。因为在真正需要时才会发生计算。例如,寻找数组中第一个符合某个条件的值。

在纯函数式编程语言,如Haskell中是默认进行惰性求值的。所以,Haskell被称为惰性语言。而与之相对的大多数编程语言如Java,C++ 的求值都是严格的,或者说是及早求值。Swift默认是严格求值的,也就是每一个表达式都需要求值,而不论这个表达式在实际中是否确实需要求值。但是,Swift作为支持多种范型的编程语言,也同时提供语法来支持惰性求值。

内建 lazy 函数

Swift中,如果需要惰性计算,就要显式地将一个序列转化为惰性序列。转化方法是使用Swift内建的lazy函数。它有四个重载实现。编译器会为你选择最正确的实现。

如果传入lazy的是Sequence(实现了SequenceType协议的类或者结构体),返回的会是LazySequence;如果传入一个Collection(实现了CollectionType协议的的类或者结构体),返回的会是LazyForwardCollectionLazyBidirectionalCollection, 或者LazyRandomAccessCollection。 下面是lazy函数的四个函数重载函数的函数原型:

func lazy<S: SequenceType>(s: S) -> LazySequence<S>
func lazy<S: CollectionType where S.Index: ForwardIndexType>(s: S) -> LazyForwardCollection<S>
func lazy<S: CollectionType where S.Index: BidirectionalIndexType>(s: S) -> LazyBidirectionalCollection<S>
func lazy<S: CollectionType where S.Index: RandomAccessIndexType>(s: S) -> LazyRandomAccessCollection<S>

如果,传入一个Array,返回的将是LazyRandomAccessCollection类型。LazyRandomAccessCollection是惰性集合。下面展示了一个将Array变为惰性序列的例子:

let r = 1...3
let seq = lazy(r).map {
    (i: Int) -> Int in
    println("mapping \(i)")
    return i * 2
}

for i in seq {
    println(i)
}

将获得如下结果:

mapping 1
2
mapping 2
4
mapping 3
6

这显示了seq是一个惰性序列。它的值只有在需要时才会真正发生计算。

Generator

Swift中,Generator是任何实现了GeneratorType协议的类或者结构体。Generator可以理解为一个序列生成器。GeneratorType协议要求定义一个名为Element的别名,并实现一个next方法。

GeneratorType协议实现如下:

protocol GeneratorType {
    typealias Element
    mutating func next() -> Element?
}

语句typealias Element要求实现这个协议的类必须定义一个名为Element的别名,这样一定程度上实现了泛型协议。协议同时要求实现next函数,其返回值是别名中定义的Element类型,next函数代表生成器要生成的下一个元素。

下面代码实现了一个菲波那契数列生成器:

class FibonacciGenerator : GeneratorType {

    var current = 0, nextValue = 1  

    typealias Element = Int

    func next() -> Element? {
        let ret = current
        current = nextValue
        nextValue = nextValue + ret
        return ret
    }
}

下面代码打印出10个菲波那契数列,以显示如何使用生成器:

var n = 10
var generator = FibonacciGenerator()
while n-- > 0 {
    println(generator.next()!)
}

Generator是Sequence和Collection的基础。

Sequence

Sequence是任何实现了SequenceType协议的类或者结构体。Sequence可以理解为一个序列。SequenceType协议要求定义一个名为Generator,类型为GeneratorType的别名,并要求实现一个返回生成器Generator的函数。

SequenceType协议如下:

protocol SequenceType : _Sequence_Type {
    typealias Generator : GeneratorType
    func generate() -> Generator
}

相似于GeneratorType协议,typealias Generator : GeneratorType要求实现这个协议的类必须定义一个名为Generator类型为GeneratorType的别名。协议同时要求实现一个名为generate的函数,其返回值为别名Generator定义的类型,这个类型应该实现了上文提到的GeneratorType协议。也就是说Sequence其实是包含一个生成Generator的函数的类。

下面代码使用上文中提到的菲波那契数列生成器,实现了一个菲波那契数列:

class FibonacciSequence: SequenceType
{
    typealias GeneratorType = FibonacciGenerator

    func generate() -> FibonacciGenerator {
        return FibonacciGenerator()
    }
}

下面代码打印了10个菲波那契数列,以显示如何使用该序列:

let fib = FibonacciSequence().generate()
for _ in 1..<10 {
    println(fib.next()!)
}

符合SequenceType的序列有可能成为惰性序列。成为惰性序列的方法是对其显示的调用lazy函数:

let r = lazy(stride(from: 1, to: 8, by: 2))

函数stride返回一个结构体StrideTo,这个结构体是Sequence。所以,lazy函数返回一个lazySequence对象。

Collection

Collection是实现了CollectionType协议的协议的类或者结构体。CollectionType协议继承了SequenceType协议。所以,Collection也都实现了SequenceType,它同时也是Sequence。

CollectionType协议如下:

protocol _CollectionType : _SequenceType {
    typealias Index : ForwardIndexType
    var startIndex: Index { get }
    var endIndex: Index { get }
    typealias _Element
    subscript (_i: Index) -> _Element { get }
}

protocol CollectionType : _CollectionType, SequenceType {
    subscript (position: Self.Index) -> Self.Generator.Element { get }
}

所以,CollectionType协议首先实现了SequenceType协议,并要求实现一个subscript方法以获取序列中每个位置的元素值。

Swift中,大量内置类如Dictionary,Array,Range,String都实现了CollectionType协议。所以,Swift大部分容器类都可以变为惰性序列。

// a will be a LazyRandomAccessCollection
// since arrays are random access
let a = lazy([1,2,3,4])

// s will be a LazyBidirectionalCollection
let s = lazy("hello")

总结

Swift里的集合数据结构默认是严格求值的。但是,Swift也提供了惰性语法,在需要惰性时,你需要显式声明。这为开发者在Swift中使用惰性提供了条件。

原文出处:http://lincode.github.io/Swift-Lazy
作者:LinGuo

上一篇: Swift函数式编程不变性
推荐文章
  • 在iOS开发中,要实现发送电子邮件的功能,可以使用MFMailComposeViewController类,这是Apple提供的一个内置框架——MessageUI的一部分。以下是在Swift中使用MFMailComposeViewController来发送邮件的基本步骤:实例步骤1、创建一个简单的V
  • iOS故事板(Storyboards)是Apple在iOS5中引入的一种可视化界面构建工具,它允许开发者在一个单一的文件中设计和管理整个应用程序的所有用户界面屏幕以及它们之间的导航关系。故事板通过InterfaceBuilder集成到Xcode开发环境中,为开发者提供了一种直观的方式来组织UI元素、
  • iOS自动布局(AutoLayout)是Apple在iOS6及以后版本引入的一种动态布局系统,用于处理用户界面元素在不同屏幕尺寸和方向下的自适应布局。通过自动布局,开发者可以创建灵活的、可伸缩的UI设计,确保应用在各种iPhone、iPad设备以及不同的屏幕旋转状态下都能保持良好的视觉效果和用户体验
  • ios(InternetworkingOperatingSystem-Cisco,缩写ios,也可写作IOS,区别于苹果系统iOS)iOS是由苹果公司(AppleInc.)开发的一种专有移动操作系统,它为苹果的多款移动设备提供用户界面和底层操作逻辑。最初随iPhone在2007年发布时被称为“iPh
  • 小程序中的组件也是由宿主环境提供的,开发者可以使用组件快速搭建出页面结构,官方把小程序里的组件分为了9大类,分别是视图容器基础内容表单组件导航组件媒体组件map地图组件canvas画布组件开放能力无障碍访问常见的视图容器类组件1.view普通视图区域类似于HTML中的div,是一个块级元素常用于实现
  • 一、下载并安装开发工具uniapp的开发工具为HbuilderX,下载地址为:去下载这里选择windows版本。windows版本下载完成之后会得到一个zip的压缩包文件,解压完成即可使用,是不需要安装的绿色版本。二、安装sass和scss插件打开HbuilderX,点击工具----插件安装,如图所
学习大纲