内容简介:作者:Ole Begemann,译者:函数式编程语言的一个常用范式是把一个列表切分为头部(第一个元素)和尾部(其余元素)。在 Haskell 中,
作者:Ole Begemann, 原文链接 ,原文日期:2018-11-29
译者: WAMaker ;校对: numbbbbb , BigNerdCoding ;定稿: Forelax
函数式编程语言的一个常用范式是把一个列表切分为头部(第一个元素)和尾部(其余元素)。在 Haskell 中, x:xs 会匹配非空列表,将头部绑定给变量 x,尾部绑定给 xs。
Swift 不是一门函数式编程语言
。既没有内置的 List
类型,也没有集合的特定匹配语法。
集合(Collections)
尽管如此,将 Sequence
或 Collection
切分成头部和尾部偶尔很有用。对于集合来说这很容易:
extension Collection { var headAndTail: (head: Element, tail: SubSequence)? { guard let head = first else { return nil } return (head, dropFirst()) } } if let (firstLetter, remainder) = "Hello".headAndTail { // firstLetter: Character == "H" // remainder: Substring == "ello" }
序列(Sequence)
对于序列来说却很困难,因为它们可以是单向(single-pass)的:一些序列只能被迭代一次,迭代器会消耗其中的元素。以网络流为例,一旦你从缓冲区里读取了一个字节,操作系统便将它抛弃了。你无法让它重新来一遍。
一个可能的解决方案是 创建一个迭代器 读取第一个元素,并把当前的迭代器状态包裹进一个新的 AnySequence 实例:
extension Sequence { var headAndTail: (head: Element, tail: AnySequence<Element>)? { var iterator = makeIterator() guard let head = iterator.next() else { return nil } let tail = AnySequence { iterator } return (head, tail) } }
以上代码能够实现功能,但不是一个好的通用解决方案,尤其是对满足 Collection
的类型而言。将尾部包进 AnySequence
会是一个 性能杀手
,也不可以使用合适的集合类型 SubSequence
。
为了保护集合的 SubSequence
类型,最好给 Collection
和 Sequence
分别写扩展。(我们也将会看到,这是 Swift 5 所推崇的方案,这点会在后面谈到。)
保护 SubSequence 类型
我没有找到一个通用的方案,能够让尾部的 SubSequence
类型完好,也同时能让单向序列正常工作。很感谢 Dennis Vennink
能够找出一个解决方案并 分享给我
。下面是 他的代码
(我略微对格式进行了修改):
extension Sequence { var headAndTail: (head: Element, tail: SubSequence)? { var first: Element? = nil let tail = drop(while: { element in if first == nil { first = element return true } else { return false } }) guard let head = first else { return nil } return (head, tail) } }
Dennis 的窍门是调用 Sequence.drop(while:)
,为尾部保留了 SubSequence
类型,同时在 drop(while:)
内部“捕获”了第一个元素。干得漂亮!
Swift 5
上面的代码使用 Swift 4.2。在 Swift 5 中由于序列不再会有关联 SubSequence
类型,只存在于集合中( Swift Evolution proposal SE-0234
),以上代码会崩溃。
这个改变有很多优势,但同样意味着不可能有一种通用的方法能够让 SubSequence
同时对 Sequence
和 Collection
有效。
相对的,我们把那个简单的解决方案添加给 Collection
:
extension Collection { var headAndTail: (head: Element, tail: SubSequence)? { guard let head = first else { return nil } return (head, dropFirst()) } }
如果我们需要让 Sequence
拥有同样的功能,就需要添加一个独立的扩展,使用新的 DropWhileSequence
作为返回类型的尾部:
extension Sequence { var headAndTail: (head: Element, tail: DropWhileSequence<Self>)? { var first: Element? = nil let tail = drop(while: { element in if first == nil { first = element return true } else { return false } }) guard let head = first else { return nil } return (head, tail) } }
(实现和之前的代码一样,仅仅改变了返回的类型。)
为集合添加一种模式匹配结构作为一个 可行 的特性已经在论坛多次被 提及 。有了它,你可以像下面这样将一个有序集合解构成头部和尾部:
let numbers = 1...10 let [head, tail...] = numbers // head == 1 // tail == 2...10
在 switch
表达式中会很有用。
很遗憾我们被误导性的名字 Sequence
给束缚住了。要将 Collection.SubSequence
重命名成更合适的 Slice
会造成 严重的代码破坏
。
本文由 SwiftGG 翻译组翻译,已经获得作者翻译授权,最新文章请访问http://swift.gg。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- [译] 让 Apache Cassandra 尾部延迟减小 10 倍,已开源
- php – Laravel:如何使用尾部斜杠创建路由路由返回URL?
- Java 序列化反序列化对比
- python 序列化和反序列化
- json序列化和反序列化
- golang gencode 序列化/反序列化数据
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Design for Hackers
David Kadavy / Wiley / 2011-10-18 / USD 39.99
Discover the techniques behind beautiful design?by deconstructing designs to understand them The term ?hacker? has been redefined to consist of anyone who has an insatiable curiosity as to how thin......一起来看看 《Design for Hackers》 这本书的介绍吧!
XML 在线格式化
在线 XML 格式化压缩工具
Markdown 在线编辑器
Markdown 在线编辑器