swift之函数式编程(三)
文章来源于《Functional Programing in Swift》,本系列仅仅是观后概括的一些内容
Wrapping Core Image
上一篇文章我们介绍了 高阶函数并且展示了函数是如何作为参数传递给其他函数。在本章中,我们将展示如何使用高阶函数对已有的 面向对象的API 进行函数式包装。
Core Image 是一个非常强大的图形处理的框架,但有些时候 它的API的使用有点笨重。CoreImage的API是松散类型—— image filters are configured using key-value coding.这样容易在类型以及参数的名称发生错误,导致运行时错误。我们新的API将会是安全和模块化的,利用类型来确保没有这样的运行时错误。
The Filter Type
CIFilter 用于创建图像过滤器(filter),当你初始化一个CIFilter对象时,你总是会提供一个输入图像通过 kCIInputImageKey,然后通过kCIOutputImageKey来获得过滤后的结果。然后您还可以用这个结果作为下个过滤器的输入。
我们将试着封装这些键值对的具体细节并提供一个安全、强类型的API给我们的用户。
typealias Filter = CIImage -> CIImage
这是我们要构建的基本类型
Building Filters
既然我们已经定义了过滤器的基本类型,我们可以开始为特定的过滤器定义相关的函数,这些便利的函数的参数需要一个特定filter和返回一个Filter。
func myFilter(/* parameters */) -> Filter
注意这个返回的Filter类型,这也是一个函数。此后,这个将帮组我们组装多个过滤器来实现我们想要的图像效果。
To make our lives a bit easier,我们将扩展CIFilter类 通过convenience initializer以及一个计算属性来取回output image
typealias Parameters = Dictionary<String, AnyObject>
extension CIFilter {
convenience init(name: String, parameters: Parameters) {
self.init(name: name)
setDefaults()
for (key, value: AnyObject) in parameters {
setValue(value, forKey: key)
}
} var outputImage: CIImage {
return self.valueForKey(kCIOutputImageKey) as CIImage
}
}
我们的便利构造器首先会调用我们的指定构造器。这个计算属性outputImage提供了一个简单的方法从filter对象中去获取output image 。通过这种计算属性,使用我们的API的用户再也不需要去关心如何获取这样的操作。
Blur(模糊)
blur filter 只需要一个blur radius 作为它的参数
func blur(radius: Double) -> Filter {
return { image in
let parameters: Parameters = [
kCIInputRadiusKey: radius,
kCIInputImageKey: image
]
let filter = CIFilter(name: "CIGaussianBlur",parameters:parameters)
return filter.outputImage
}
}
blur function 返回一个 function,这个函数的参数是CIImage 类型的image,返回一个新的image。正因为如此,模糊函数的返回值符合我们之前定义的Filter类型
(typealias Filter = CIImage -> CIImage).这个例子我们是在原来已存在Core Image中的filter只是进行了轻包装。我们可以反复使用相同的模式来创建我们自己的过滤功能。
Color Overlay
我们定义一个过滤器:在图像上覆盖我们选定的颜色。在Core Image中默认没有这样的filter,但是我们可以,当然,这也是通过已存在的过滤器组成的。
这两个构建块是我们要用颜色生成器filter(CIConstantColorGenerator)和source-over compositing filter(CISourceOverCompositing)。
func colorGenerator(color: NSColor) -> Filter {
return { _ in
let parameters: Parameters = [kCIInputColorKey: color]
let filter = CIFilter(name:"CIConstantColorGenerator",parameters: parameters)
return filter.outputImage
}
}
这个跟之前我们定义的blur filter相像,但有一点不同:这个constant color generator filter跟 imput image没有联系,因此我们不需要image 这个参数。
func compositeSourceOver(overlay: CIImage) -> Filter {
return { image in
let parameters: Parameters = [
kCIInputBackgroundImageKey: image,
kCIInputImageKey: overlay
]
let filter = CIFilter(name: "CISourceOverCompositing",parameters: parameters)
let cropRect = image.extent()
return filter.outputImage.imageByCroppingToRect(cropRect) }
}
这里我们将output image的尺寸裁剪成input image的尺寸。这不是必须的,取决于这个我们过滤器的行为。然而,在这个例子中工作的非常好。
最后,我们将两个filter进行组合成color overlay filter:
func colorOverlay(color: NSColor) -> Filter {
return { image in
let overlay = colorGenerator(color)(image)
return compositeSourceOver(overlay)(image)
}
}
Composing Filters
现在我们使用我们定义的filter运用到实际的图片上:first we blur the image, and then we put a red overlay on top.
let url = NSURL(string: "http://tinyurl.com/m74sldb")
let image = CIImage(contentsOfURL: url) // Now we can apply both filters to these by chaining them together: let blurRadius = 5.0
let overlayColor = NSColor.redColor().colorWithAlphaComponent(0.2)
let blurredImage = blur(blurRadius)(image)
let overlaidImage = colorOverlay(overlayColor)(blurredImage)
Function Composition
当然上面的filter组合我们可以用一个语句:
let result = colorOverlay(overlayColor)(blur(blurRadius)(image))
但是,代码很快变得不可读。有一个比较好的办法就是自定义一个filter组合的操作:
func composeFilters(filter1: Filter, filter2: Filter) -> Filter {
return { img in filter2(filter1(img)) }
}
let myFilter1 = composeFilters(blur(blurRadius), colorOverlay(overlayColor))
let result1 = myFilter1(image)
我们可以更进一步,使得这个更可读,通过自定义操作符来组合
infix operator >>> { associativity left }
func >>> (filter1: Filter, filter2: Filter) -> Filter {
return { img in filter2(filter1(img)) }
}
let myFilter2 = blur(blurRadius) >>> colorOverlay(overlayColor)
let result2 = myFilter2(image)
当我们定义>>> 为左结合
Discussion
在这章中,我们会发现我们设计的API有这么几个优点:
1. safety----it is almost impossible to create runtime errors arising from undefined keys or failed casts
2. modularity ---it is easy to compose filters using the >>> operator.Doing so allows you to tease apart complex filters into smaller, simpler, reusable components. Additionally, composed filters have the exact same type as their building blocks, so you can use them interchangeably.
3. clarity ---- even if you have never used Core Image, you should be able to assemble simple filters using the functions we have defined. To access the results, you don’t need to know about special dictionary keys, such as kCIOutputImageKey, or worry about initializing certain keys, such as kCIInputImageKey or kCIInputRadiusKey. From the types alone, you can almost figure out how to use the API, even without further documentation
swift之函数式编程(三)的更多相关文章
- swift 之函数式编程(一)
1. 什么是函数式编程? 函数式编程是阿隆佐思想的在现实世界中的实现, 它将电脑运算视为数学上的函数计算,并且避免使用程序状态以及异变物件. 函数式编程的最重要基础是λ演算.而且λ演算的函數可以接受函 ...
- swift之函数式编程
函数式编程初探 最近初学swift,和OC比,发现语言更现代,也有了更多的特性.如何写好swift代码,也许,熟练使用新特性写出更优秀的代码,就是答案.今天先从大的方向谈谈swift中的编程范式-函数 ...
- swift之函数式编程(四)
文章内容来自<Functional Programing in Swift>,具体内容请到书中查阅 Map, Filter, Reduce Functions that take func ...
- swift之函数式编程(二)
本文的主要内容来自<Functional Programming in Swift>这本书,有点所谓的观后总结 在本书的Introduction章中: we will try to foc ...
- 最通俗易懂的方式让你理解 Swift 的函数式编程
函数式编程(Functional Programming)是相对于我们常用的面向对象和面向过程编程的另外一种开发思维方式,它更加强调以函数为中心.善用函数式编程思路,可以对我们的开发工作有很大的帮助和 ...
- swift之函数式编程(五)
文章内容来源于<Functional Programing in Swift>,详情请看原著 The Value of Immutability swift 对于控制值改变有一些机制.在这 ...
- js函数式编程(三)-compose和pointFree
compose即函数嵌套组合 组合compose在第一篇已经初见端倪,可以感受一下.compose函数的实现用闭包的方法.不完善实现如下: const compose = (f, g) => { ...
- Scala 中的函数式编程基础(三)
主要来自 Scala 语言发明人 Martin Odersky 教授的 Coursera 课程 <Functional Programming Principles in Scala>. ...
- 转:JavaScript函数式编程(三)
转:JavaScript函数式编程(三) 作者: Stark伟 这是完结篇了. 在第二篇文章里,我们介绍了 Maybe.Either.IO 等几种常见的 Functor,或许很多看完第二篇文章的人都会 ...
随机推荐
- python实例编写(2)--等待,一组对象,层级元素,frame对象处理
一.设置等待 #coding=utf-8 from selenium import webdriver from selenium.webdriver.support.ui import WebDri ...
- jmeter按比例执行业务场景
可用函数 __counter实现: 函数助手中 找到 __counter,如 ${__counter(false,num)},功能简介 ---- 参数为true,每个用户有自己的计数器 ---- 参数 ...
- JSP入门2
1. CRUD是Create(创建).Read(读取).Update(更新)和Delete(删除)的缩写,一般应用有这四项也就足够了. 我们这里的例子是对联系人信息进行CRUD操作. 2. javab ...
- 使用LayUI操作数据表格
接着 上一篇 继续完善我们的demo,这次我们加一个搜索按钮 搜索 在table标签的上方,加入这样一组html <div class="demoTable"> 搜索商 ...
- 机器学习-KNN分类器
1. K-近邻(k-Nearest Neighbors,KNN)的原理 通过测量不同特征值之间的距离来衡量相似度的方法进行分类. 2. KNN算法过程 训练样本集:样本集中每个特征值都已经做好类别 ...
- Python数据分析(二): Numpy技巧 (2/4)
numpy.pandas.matplotlib(+seaborn)是python数据分析/机器学习的基本工具. numpy的内容特别丰富,我这里只能介绍一下比较常见的方法和属性. 昨天晚上发了第一 ...
- SQL server2005学习笔记(一)数据库的基本知识、基本操作(分离、脱机、收缩、备份、还原、附加)和基本语法
在软件测试中,数据库是必备知识,假期闲里偷忙,整理了一点学习笔记,共同探讨. 阅读目录 基本知识 数据库发展史 数据库名词 SQL组成 基本操作 登录数据库操作 数据库远程连接操作 数据库分离操作 数 ...
- httpd网页身份认证
html { font-family: sans-serif } body { margin: 0 } article,aside,details,figcaption,figure,footer,h ...
- 简单Elixir游戏服设计- 游戏玩法介绍
抄以前的,做了点修改. 到目前为止,我们完成了玩家的数据和进程建模,现在介绍游戏玩法. 为什么我们还不做客户端接入.协议指定呢?为什么还没有网关和数据存储呢.在我接手的游戏, 这些通常已经定下来了,我 ...
- 初识Hibernate之关联映射(二)
上篇我们介绍了关联映射的几种形式,有单向多对一,单向一对多,还有双向一对多.本篇接着介绍有关关联映射的其他几种映射方式,主要有以下几种: 基于外键的单向一对一关联映射 基于主键的单向一对一关联映射 单 ...