对一个程序猿来说,从一开始接触编程后会逐渐经历好几个不同的编程思想。包括过程式编程、面向对象编程、响应式编程、函数式编程、链式编程等等。
过程式编程
的特点是随着程序的编写逐步进行,写到哪儿运行到哪儿。
面向对象
的特点是万物皆对象。很著名的例子:把大象放进冰箱。
响应式编程
的特点是一方触发,多方响应。OC中的KVO、通知中心就是这种。触发者只负责触发,不理会结果。
函数式编程
的特点是将函数作为一等公民,当作参数和返回值使用。典型的如OC和Swift 中的 map函数、fiflt函数、reduce函数等。每个函数的处理结果给到下一个函数,最后的结果由自身函数调出。
链式编程
的特点是运用点语法将很多个函数串联起来。典型的例子是OC中的Masnary和Swift 中的Snpkit。
这篇文章主要介绍函数式编程和链式编程的实现
链式编程
链式编程的特点是使用点语法将对象的多个函数连起来调用。就像这样
obj.func1(par).func2(par).func3(par)
以一个例子来实现。假设我需要做一个字符串处理的扩展,其中包涵拼接字符
、删除最后一个字符
、修改最后一个字符
这三个功能。在OC和Swift 中,这些函数都有现成,但是我想它们能够联合起来调用。假设我已经给这三个函数分别去名字叫:addString
, removeLastString
,alterLastString
.而我想像下面这样调用:
NSString *str = @"abcdefg";
NSString *newStr = str. addString(@"123").removeLastString().add(@"555"). alter LastString("*")
NSLog(@"%@",newStr );
最后输出的结果是:abcdefg1255*
.这个应该很容易理解了。
接下来我分别使用OC和Swift来实现。
在OC中(链式编程)
新建一个工具类StringManegeTool
,这个类包涵了一个初始化的函数:initWithString
以及一个启动函数doSomething.并且包含了若干个block做为属性,以便使用点语法。
//
// StringManegeTool.h
// funTest
//
// Created by JD on 2017/11/4.
// Copyright © 2017年 JD. All rights reserved.
//
#import <Foundation/Foundation.h>
@class StringManegeTool;
typedef StringManegeTool* (^TESTBlock1)(NSString*);
typedef StringManegeTool* (^TESTBlock2)(void);
@interface StringManegeTool: NSObject
- (instancetype)initWithString:(NSString*)str;
- (NSString*)doSomething:(void (^)(StringManegeTool*))maker;
@property (nonatomic,copy) TESTBlock1 addString;
@property (nonatomic,copy) TESTBlock2 removeLastString;
@property (nonatomic,copy) TESTBlock1 alertLastString;
@end
.m文件中实现。在初始化方法中实现了block,并且在doSomthing中实现了调用block。
//
// StringManegeTool.m
// funTest
//
// Created by JD on 2017/11/4.
// Copyright © 2017年 JD. All rights reserved.
//
#import "StringManegeTool.h"
#import <Foundation/Foundation.h>
@interface StringManegeTool ()
@property(nonatomic,strong) NSMutableString* buffStr;
@end
@implementation StringManegeTool
- (instancetype)initWithString:(NSString*)str{
self = [super init];
if (self) {
self.buffStr = [[NSMutableString alloc] initWithString:str];
__weak StringManegeTool *weakSelf = self;
self.addString = ^StringManegeTool *(NSString *str) {
[weakSelf.buffStr appendString:str];
return weakSelf;
};
self.removeLastString = ^StringManegeTool *{
[weakSelf.buffStr deleteCharactersInRange:NSMakeRange(weakSelf.buffStr.length-1, 1)];
return weakSelf;
};
self.alertLastString = ^StringManegeTool *(NSString *str) {
[weakSelf.buffStr replaceCharactersInRange:NSMakeRange(weakSelf.buffStr.length-1, 1) withString:str];
return weakSelf;
};
}
return self;
}
- (NSString*)doSomething:(void (^)(StringManegeTool*))maker{
maker(self);
return self.buffStr;
}
@end
接下来我可以这样调用:
StringManegeTool *tool = [[StringManegeTool alloc] initWithString:@"abcdefg"];
NSString *newStr = [tool doSomething:^(StringManegeTool *make) {
make.addString(@"123").removeLastString().addString(@"555").alertLastString(@"*");
}];
NSLog(@"%@",newStr);
打印结果
//2017-11-04 15:36:08.495 funTest[1661:121049] abcdefg1255*
回忆一下Masnary。熟悉的味道!
再来看看Swift中如何实现链式编程
//
// StringManegeToolInSwift.swift
// funTest
//
// Created by JD on 2017/11/4.
// Copyright © 2017年 JD. All rights reserved.
//
import UIKit
class StringManegeToolInSwift: NSObject {
var bufStr:String = String()
convenience init(_ withString:String){
self.init();
self.bufStr = withString
}
func doSomething(_ make: (StringManegeToolInSwift)->())->String{
make(self)
return self.bufStr
}
func addString(_ string:String)->StringManegeToolInSwift{
self.bufStr.append(string)
return self
}
func remoLastString()-> StringManegeToolInSwift{
let end = self.bufStr.endIndex
let start = self.bufStr.index(before: end)
self.bufStr.removeSubrange(start..<end)
return self
}
func alertLastString(_ string:String) -> StringManegeToolInSwift{
let end = self.bufStr.endIndex
let start = self.bufStr.index(before: end)
self.bufStr.replaceSubrange(start..<end, with: string)
return self
}
/// 调用
class func actionDo(){
let tool = StringManegeToolInSwift.init("abcdefg")
let newString = tool.doSomething { (maker) in
maker.addString("123").remoLastString().addString("555").alertLastString("*")
}
print(newString)
}
//打印:
abcdefg1255*
}
说实话,Swift写起来要简单太多了。
总结一下:iOS中,链式编程的特点是调用返回值为自身的函数或者Block。再结合map等函数的映射转换,这样可以使得代码变的非常灵活。
函数式编程
我之前一直把函数式编程理解为高阶函数,其实这就是高阶函数。先看一个很典型的例子,照样,我们从CO开始。
NSArray<NSString*> *arr = @[@"252",@"55541",@"2295",@"21"];
arr = [arr sortedArrayUsingComparator:^NSComparisonResult(NSString* _Nonnull obj1, NSString* _Nonnull obj2) {
return obj1.length < obj2.length;
}];
NSLog(@"%@",arr);
打印:2017-11-04 16:41:22.846 funTest[2478:150288] (
252,
55541,
2295,
21
)
这是一个数组排序的函数。我们经常把在函数中使用函数作为参数称作高阶函数(这实际上就是函数式编程的一种特质)。上面的排序函数中,我们将两个数组内的元素比较的结果拿来使用给我本体函数,实现排序的目的。下面这个函数可能回更加复杂一点:
NSString *result = [self testWithStr:@"adcf" One:^BOOL(NSString *str) {
return [str isEqualToString:@"111"];
} two:^NSString *(BOOL abool) {
return abool?@"相等":@"不相等";
}];
NSLog(@"%@",result);
//打印:
2017-11-08 09:24:49.011487+0800 funTest[7487:1779986] 不相等
它是一个字符判断函数,在第一个参数中,我们输入想要比较的字符,在第一个block中进行比较并返回一个bool值,这个bool值将使用在第二个block的参数中,第二个block是根据bool值返回需要比较的两个字符是否相等的描述。
那具体是怎么实现的呢? 根据这个函数的特点,第一个block的调用结果将被用在第二个block上,对于第二个block来讲,他的参数本身就不是固定的,而是由第一个block决定,由此我们应该能想到,第二个block的参数其实就是第一个block才对。实现如下:
- (NSString *)testWithStr:(NSString *)str One: (BOOL(^)(NSString* str))one two:(NSString*(^)(BOOL abool))Two{
return Two(one(str));
}
如果是Swift,其实是及其相似的:
func testWith(Str:String, one:(String)->Bool,two:(Bool)->String) -> String {
return two(one(Str))
}
//调用
self.testWith(Str: "abc", one: { (str) -> Bool in
return str == "123"
}) { (abool) -> String in
return abool?"相等":"不相等"
}
这里的确是函数式编程, 但是我们发现其中多了东西:虽然这里将一些函数作为另一函数的参数,但是,这些函数的返回值和参数都具有一定的关联,没错,上面的函数中,block one
的返回值的类型恰恰是block two
的参数类型。他们有一些必然的联系。这就涉及到了响应式编程
的范畴,这里其实算是响应式函数编程
,不在本文的范畴之内,有兴趣的可以自行了解,本文不做介绍.
但是无可厚非的是,这里我们可以看到函数式编程的特点:将函数做为参数来使用,这就使得函数本身具备更大的灵活性,通过参数函数适应不同的应用场景。
将函数配合泛型,又能继续扩大函数的灵活性。下面修改一下上面的例子看看泛型中的函数式编程:
func testWith<T:Comparable>(_ obj:T, one:(T)->Bool,two:(Bool)->String) -> String {
return two(one(obj))
}
调用
let result = tool.testWith("abc", one: { (str) -> Bool in
return str == "123"
}) { (abool) -> String in
guard abool else{
return "不相等"
}
return "相等"
}
print(result)
let result2 = tool.testWith(5, one: { (int) -> Bool in
return int == 5
}) { (abool) -> String in
guard abool else{
return "不相等"
}
return "相等"
}
print(result2)
以上就是函数式编程结合泛型。
提一下我们经常听到的RAC,这是一个响应式函数编程,同时也会使用到泛型。 关于响应式编程, 我会在以后的文章中写出,同时将包含RAC的知识。