令人惊叹的swift

原文链接

几年前,函数式编程突然火了起来,一篇10 Scala functional one liners became文章迅速火了起来,同时也涌现出很多类似的的文章(Haskell,Python,CoffeScript)

我不知道有多少人对这些代码段留下了深刻的影响,但是我觉得这些例子可以让初学者领略到函数式编程的魅力

每个元素乘以2

这个没什么可以说的,用map可以很容易实现

(1...1024).map{$0 * 2}
列表求和

这个例子同样很简单,用reduce和+操作符即可完成(加号操作符其实也是一个函数)

(1...1024).reduce(0,combine: +)

(PS:下面的的代码讲逐步展示swift作为一个现代语言所拥有的强大魔力)

判断是否包含字符串

我们来判断下一个句子是否包含特定的字符

let words = ["Swift","iOS","cocoa","OSX","tvOS"] 
let tweet = "This is an example tweet larking about Swift" 
let valid = !words.filter({tweet.containsString($0)}).isEmpty
 valid //true

@oisdk提供一个更加简洁的方法

words.contains(tweet.containsString)

还有更绝的

tweet.characters 
.split(" ")
.lazy 
.map(String.init) 
.contains(Set(words).contains)

(PS:有关swift中lazy很好解释的一个文章以及这个简而言之就是Lazy方法返回一个可以用来筛选或者映射的序列或者集合类型,并且不会产生任何中间数组,使用这些结果作为另一个处理程序的输入。
也就是Lazy Evaluation。不同于OC中的lazyload)

读取一个文件

通过一些自带的标准库直接把文件读取到一个数组中在其他语言中几乎是不可能的,但是我们可以用splitmap来简化这个工作。

let path = NSBundle.mainBundle().pathForResource("test", ofType: "txt") 
let lines = try? String(contentsOfFile: path!).characters.split{$0 == "\n"}.map(String.init) 
if let lines=lines { 
lines[0] 
lines[1]
lines[2] 
lines[3] 
 }
祝你生日快乐

我们讲会给你展示一首生日歌

let name = "uraimo" 
(1...4).map{print("Happy Birthday " + (($0 == 3) ? "dear \(name)":"to You"))}
过滤列表

在这个案例中我们被要求按照一定的条件来划分数组,我们将会扩展SequenceType这个协议来完成这个任务。

extension SequenceType{
 typealias Element = Self.Generator.Element
  func partitionBy(fu: (Element)->Bool)->([Element],[Element]){ 
  var first=[Element]()
  var second=[Element]() 
  for el in self { 
  if fu(el) { 
     first.append(el) 
  }else{ 
    second.append(el)
     } 
    } 
    return (first,second)
  } 
  } 
  let part = [82, 58, 76, 49, 88, 90].partitionBy{$0 < 60} 
  part // ([58, 49], [82, 76, 88, 90])

恩~这还不够震撼

extension SequenceType{ 
func anotherPartitionBy(fu: (Self.Generator.Element)->Bool)->([Self.Generator.Element],[Self.Generator.Element]){
 return (self.filter(fu),self.filter({!fu($0)})) } } 
 let part2 = [82, 58, 76, 49, 88, 90].anotherPartitionBy{$0 < 60}
  part2 
  // ([58, 49], [82, 76, 88, 90])

This is slightly better, but it traverses the sequence two times and trying to turn this into a one liner removing the enclosing function will get us something with too much duplicated stuff (the filtering function and the array that will be used in two places.

Can we build something that will transform the original sequence into a partition tuple using a single stream of data? Yes we can, using reduce.

var part3 = [82, 58, 76, 49, 88, 90]
.reduce( ([],[]), combine: {
 (a:([Int],[Int]),n:Int) -> ([Int],[Int]) in (n<60) ? (a.0+[n],a.1) : (a.0,a.1+[n]) }) 
 part3 // ([58, 49], [82, 76, 88, 90])

(这一段我不能很准确的描述,我个人理解是map,flatmapfilter这种链式操作会造成极大的性能损失,浪费了CPU周期,对集合不断的进行重复无意义的访问。但是在数据量巨大的情况下reduce性能反而只有map的百分之一(没错,就是百分之一),原因是某些情况下reduce会对底层序列的每个元素都产生一份 copy,相关文章链接)

查找最大值最小值

恩..想起来孔乙己中字有几种写法梗了


//Find the minimum of an array of Ints [10,-22,753,55,137,-1,-279,1034,77].sort().first [10,-22,753,55,137,-1,-279,1034,77].reduce(Int.max, combine: min) [10,-22,753,55,137,-1,-279,1034,77].minElement() //Find the maximum of an array of Ints 

[10,-22,753,55,137,-1,-279,1034,77].sort().last 

[10,-22,753,55,137,-1,-279,1034,77].reduce(Int.min, combine: max) [10,-22,753,55,137,-1,-279,1034,77].maxElement()
并行计算

一些语言允许用flatmap和map来产生一个简单透明的并行计算方式,但是swift还不能这么干,swift采用了基于C的GCD库 链接

(补充一个在gist看到的一个对于GCD一个很好的封装)

protocol ExcutableQueue {
    var queue: dispatch_queue_t { get }
}

extension ExcutableQueue {
    func execute(closure: () -> Void) {
        dispatch_async(queue, closure)
    }
}

enum Queue: ExcutableQueue {
    case Main
    case UserInteractive
    case UserInitiated
    case Utility
    case Background

    var queue: dispatch_queue_t {
        switch self {
        case .Main:
            return dispatch_get_main_queue()
        case .UserInteractive:
            return dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0)
        case .UserInitiated:
            return dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)
        case .Utility:
            return dispatch_get_global_queue(QOS_CLASS_UTILITY, 0)
        case .Background:
            return dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0)
        }
    }
}

enum SerialQueue: String, ExcutableQueue {
    case DownLoadImage = "myApp.SerialQueue.DownLoadImage"
    case UpLoadFile = "myApp.SerialQueue.UpLoadFile"

    var queue: dispatch_queue_t {
        return dispatch_queue_create(rawValue, DISPATCH_QUEUE_SERIAL)
    }
}
//包装后的GCD将会是这样,链式调用将会使得代码变得更加清楚
let url = NSURL(string: "http://image.jpg")!
    let data = NSData(contentsOfURL: url)!
    let image = UIImage(data: data)

    Queue.Main.execute {
        imageView.image = image
    }
}

素数筛

依稀好记得本科学C语言时候老师让我们用C语言写一个素数筛,那一个个循环好痛苦

要求:给定的n为集合上限,返回集合中的素数

var n = 50
var primes = Set(2...n)
(2...Int(sqrt(Double(n)))).forEach{primes.subtractInPlace((2*$0).stride(through:n, by:$0))}
primes.sort()

We use the outer range to iterate over the integers we want to check and for each one we calculate a sequence of multiples of those numbers using stride(through:Int by:Int). Those sequences are then substracted from a Set initialized with all the integers from 2 to n.

But as you can see, to actually remove the multiples we use an external mutable Set, introducing a side-effect.

To eliminate side-effects, as we should always try to do, we will first calculate all the subsequences, flatMap them in a single array of multiples and remove these integers from the original Set.
(PS:由于本人的辣鸡水平,这段只能再次丢上英文了,我实在不知道怎么用中文表达出来,其中这段英文中的关键词side-effect翻译为"副作用",指的是函数内部与外部互动(最典型的情况,就是修改全局变量的值),产生运算以外的其他结果。
函数式编程强调没有"副作用",意味着函数要保持独立,所有功能就是返回一个新的值,没有其他行为,尤其是不得修改外部变量的值。其余的只能自行脑补,我就不误导了)

var sameprimes = Set(2...n) sameprimes.subtractInPlace((2...Int(sqrt(Double(n)))) .flatMap{ (2*$0).stride(through:n, by:$0)}) 
sameprimes.sort()

这里有一个关于flatmap很好的解释[usage of flatMap to flatten nested arrays](usage of flatMap to flatten nested arrays),
中文文章(有点抽象),以及一个通俗易懂的文章还有一个用haskell解释的中文文章

用元组来交换数据
var a=1,b=2 
(a,b) = (b,a) 
a //2 b //1

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容

  • 辞工在家有过年的感觉。
    游离_阅读 157评论 0 0
  • 苏增者,鹿邑朱庄村人。时值壮年,胆大,性燥爽。 立秋时节,稼禾成熟,瓜果飘香。为防贼夜间偷摘棉花,搬小床至田边地...
    河南豫东文学阅读 617评论 0 1