文章来源于《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)

当我们定义>>> 为左结合

Theoretical Background: Currying  柯里化函数

关于柯里化函数的相关知识,不多介绍,下面有相关链接:

swift的柯里化

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之函数式编程(三)的更多相关文章

  1. swift 之函数式编程(一)

    1. 什么是函数式编程? 函数式编程是阿隆佐思想的在现实世界中的实现, 它将电脑运算视为数学上的函数计算,并且避免使用程序状态以及异变物件. 函数式编程的最重要基础是λ演算.而且λ演算的函數可以接受函 ...

  2. swift之函数式编程

    函数式编程初探 最近初学swift,和OC比,发现语言更现代,也有了更多的特性.如何写好swift代码,也许,熟练使用新特性写出更优秀的代码,就是答案.今天先从大的方向谈谈swift中的编程范式-函数 ...

  3. swift之函数式编程(四)

    文章内容来自<Functional Programing in Swift>,具体内容请到书中查阅 Map, Filter, Reduce Functions that take func ...

  4. swift之函数式编程(二)

    本文的主要内容来自<Functional Programming in Swift>这本书,有点所谓的观后总结 在本书的Introduction章中: we will try to foc ...

  5. 最通俗易懂的方式让你理解 Swift 的函数式编程

    函数式编程(Functional Programming)是相对于我们常用的面向对象和面向过程编程的另外一种开发思维方式,它更加强调以函数为中心.善用函数式编程思路,可以对我们的开发工作有很大的帮助和 ...

  6. swift之函数式编程(五)

    文章内容来源于<Functional Programing in Swift>,详情请看原著 The Value of Immutability swift 对于控制值改变有一些机制.在这 ...

  7. js函数式编程(三)-compose和pointFree

    compose即函数嵌套组合 组合compose在第一篇已经初见端倪,可以感受一下.compose函数的实现用闭包的方法.不完善实现如下: const compose = (f, g) => { ...

  8. Scala 中的函数式编程基础(三)

    主要来自 Scala 语言发明人 Martin Odersky 教授的 Coursera 课程 <Functional Programming Principles in Scala>. ...

  9. 转:JavaScript函数式编程(三)

    转:JavaScript函数式编程(三) 作者: Stark伟 这是完结篇了. 在第二篇文章里,我们介绍了 Maybe.Either.IO 等几种常见的 Functor,或许很多看完第二篇文章的人都会 ...

随机推荐

  1. svn服务器配置与客户端的使用

    1, Apache Subversion 官网下载地址: http://subversion.apache.org/packages.html#windows 官网下载提供的一般都是最新版本的,如果想 ...

  2. springmvc05-Spring+Springmvc+Hibernate实现简单的用户管理系统

    1, 导入\spring-framework-3.2.4.RELEASE\libs\disk下所有包; hibernate-distribution-3.6.7.Final\lib\required下 ...

  3. Linux 命令练习

     ls命令 ls就是list的简写,目的是打印当前目录下的清单 格式 ls[选项][目录名] 常用参数 -a –all 列出目录下的所有文件,包括以 . 开头的隐含文件 -l 除了文件名之外,还将文件 ...

  4. Docker入门之三容器

    上一篇博客学习了下镜像,今天来学习容器.容器类似一个手机中的沙盒环境,用来运行app实例.和镜像一样也是对容器的创建.删除.导出等. 由于我买的参考书中的例子好多都是基于linux的,所以我将dock ...

  5. http content-type accept的区别

    1.Accept属于请求头, Content-Type属于实体头. Http报头分为通用报头,请求报头,响应报头和实体报头. 请求方的http报头结构:通用报头|请求报头|实体报头 响应方的http报 ...

  6. 《Go in action》读后记录:Go的并发与并行

    本文的主要内容是: 了解goroutine,使用它来运行程序 了解Go是如何检测并修正竞争状态的(解决资源互斥访问的方式) 了解并使用通道chan来同步goroutine 一.使用goroutine来 ...

  7. hdu 5937 -- Equation(搜索)

    题目链接 problem description Little Ruins is a studious boy, recently he learned addition operation! He ...

  8. vue学习之父组件与子组件之间的交互

    1.父组件数据传给子组件 父组件中的msgfather定义数据 在之组件中通过设置props来取得希望从父组件中获得的值 通过设置这两个属性就可以从父组件传数据到子组件 2.子组件传数据给父组件(这里 ...

  9. 关于Java里面File类创建txt文件重复???

    private JButton getOpenButton() { if (openButton == null) { openButton = new JButton(); openButton.s ...

  10. 创建文件DSN

    记录:为了将access中表格直接上传到SQL中,不用在SQL中进行再创建. 优点:不用经过系统来生成,直接手动产生.(主要是搜索到的资料不足以一下搞出来,抱着试试的心态,最后成功了!) 操作步骤: ...