對(duì)一個(gè)程序猿來說,從一開始接觸編程后會(huì)逐漸經(jīng)歷好幾個(gè)不同的編程思想紧憾。包括過程式編程到千、面向?qū)ο缶幊獭㈨憫?yīng)式編程赴穗、函數(shù)式編程、鏈?zhǔn)骄幊痰鹊取?/p>
過程式編程
的特點(diǎn)是隨著程序的編寫逐步進(jìn)行膀息,寫到哪兒運(yùn)行到哪兒般眉。
面向?qū)ο?/code>的特點(diǎn)是萬物皆對(duì)象。很著名的例子:把大象放進(jìn)冰箱潜支。
響應(yīng)式編程
的特點(diǎn)是一方觸發(fā)甸赃,多方響應(yīng)。OC中的KVO冗酿、通知中心就是這種埠对。觸發(fā)者只負(fù)責(zé)觸發(fā),不理會(huì)結(jié)果裁替。
函數(shù)式編程
的特點(diǎn)是將函數(shù)作為一等公民项玛,當(dāng)作參數(shù)和返回值使用。典型的如OC和Swift 中的 map函數(shù)弱判、fiflt函數(shù)襟沮、reduce函數(shù)等。每個(gè)函數(shù)的處理結(jié)果給到下一個(gè)函數(shù)昌腰,最后的結(jié)果由自身函數(shù)調(diào)出开伏。
鏈?zhǔn)骄幊?/code>的特點(diǎn)是運(yùn)用點(diǎn)語法將很多個(gè)函數(shù)串聯(lián)起來。典型的例子是OC中的Masnary和Swift 中的Snpkit遭商。
這篇文章主要介紹函數(shù)式編程和鏈?zhǔn)骄幊痰膶?shí)現(xiàn)
鏈?zhǔn)骄幊?/h4>
鏈?zhǔn)骄幊痰奶攸c(diǎn)是使用點(diǎn)語法將對(duì)象的多個(gè)函數(shù)連起來調(diào)用固灵。就像這樣
obj.func1(par).func2(par).func3(par)
以一個(gè)例子來實(shí)現(xiàn)。假設(shè)我需要做一個(gè)字符串處理的擴(kuò)展劫流,其中包涵拼接字符
巫玻、刪除最后一個(gè)字符
丛忆、修改最后一個(gè)字符
這三個(gè)功能。在OC和Swift 中大审,這些函數(shù)都有現(xiàn)成蘸际,但是我想它們能夠聯(lián)合起來調(diào)用。假設(shè)我已經(jīng)給這三個(gè)函數(shù)分別去名字叫:addString
, removeLastString
,alterLastString
.而我想像下面這樣調(diào)用:
NSString *str = @"abcdefg"徒扶;
NSString *newStr = str. addString(@"123").removeLastString().add(@"555"). alter LastString("*")
NSLog(@"%@",newStr )粮彤;
最后輸出的結(jié)果是:abcdefg1255*
.這個(gè)應(yīng)該很容易理解了。
接下來我分別使用OC和Swift來實(shí)現(xiàn)姜骡。
在OC中(鏈?zhǔn)骄幊蹋?/h5>
新建一個(gè)工具類StringManegeTool
导坟,這個(gè)類包涵了一個(gè)初始化的函數(shù):initWithString
以及一個(gè)啟動(dòng)函數(shù)doSomething.并且包含了若干個(gè)block做為屬性,以便使用點(diǎn)語法圈澈。
//
// 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文件中實(shí)現(xiàn)惫周。在初始化方法中實(shí)現(xiàn)了block,并且在doSomthing中實(shí)現(xiàn)了調(diào)用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
接下來我可以這樣調(diào)用:
StringManegeTool *tool = [[StringManegeTool alloc] initWithString:@"abcdefg"];
NSString *newStr = [tool doSomething:^(StringManegeTool *make) {
make.addString(@"123").removeLastString().addString(@"555").alertLastString(@"*");
}];
NSLog(@"%@",newStr);
打印結(jié)果
//2017-11-04 15:36:08.495 funTest[1661:121049] abcdefg1255*
回憶一下Masnary递递。熟悉的味道!
再來看看Swift中如何實(shí)現(xiàn)鏈?zhǔn)骄幊?/h5>
//
// 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
}
/// 調(diào)用
class func actionDo(){
let tool = StringManegeToolInSwift.init("abcdefg")
let newString = tool.doSomething { (maker) in
maker.addString("123").remoLastString().addString("555").alertLastString("*")
}
print(newString)
}
//打由睹础:
abcdefg1255*
}
//
// 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
}
/// 調(diào)用
class func actionDo(){
let tool = StringManegeToolInSwift.init("abcdefg")
let newString = tool.doSomething { (maker) in
maker.addString("123").remoLastString().addString("555").alertLastString("*")
}
print(newString)
}
//打由睹础:
abcdefg1255*
}
說實(shí)話登舞,Swift寫起來要簡(jiǎn)單太多了。
總結(jié)一下:iOS中悬荣,鏈?zhǔn)骄幊痰奶攸c(diǎn)是調(diào)用返回值為自身的函數(shù)或者Block菠秒。再結(jié)合map等函數(shù)的映射轉(zhuǎn)換,這樣可以使得代碼變的非常靈活氯迂。
函數(shù)式編程
我之前一直把函數(shù)式編程理解為高階函數(shù)践叠,其實(shí)這就是高階函數(shù)。先看一個(gè)很典型的例子嚼蚀,照樣禁灼,我們從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
)
這是一個(gè)數(shù)組排序的函數(shù)匾二。我們經(jīng)常把在函數(shù)中使用函數(shù)作為參數(shù)稱作高階函數(shù)(這實(shí)際上就是函數(shù)式編程的一種特質(zhì))。上面的排序函數(shù)中拳芙,我們將兩個(gè)數(shù)組內(nèi)的元素比較的結(jié)果拿來使用給我本體函數(shù)察藐,實(shí)現(xiàn)排序的目的。下面這個(gè)函數(shù)可能回更加復(fù)雜一點(diǎn):
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] 不相等
它是一個(gè)字符判斷函數(shù)分飞,在第一個(gè)參數(shù)中,我們輸入想要比較的字符睹限,在第一個(gè)block中進(jìn)行比較并返回一個(gè)bool值譬猫,這個(gè)bool值將使用在第二個(gè)block的參數(shù)中讯檐,第二個(gè)block是根據(jù)bool值返回需要比較的兩個(gè)字符是否相等的描述。
那具體是怎么實(shí)現(xiàn)的呢染服? 根據(jù)這個(gè)函數(shù)的特點(diǎn)别洪,第一個(gè)block的調(diào)用結(jié)果將被用在第二個(gè)block上,對(duì)于第二個(gè)block來講柳刮,他的參數(shù)本身就不是固定的挖垛,而是由第一個(gè)block決定,由此我們應(yīng)該能想到秉颗,第二個(gè)block的參數(shù)其實(shí)就是第一個(gè)block才對(duì)痢毒。實(shí)現(xiàn)如下:
- (NSString *)testWithStr:(NSString *)str One: (BOOL(^)(NSString* str))one two:(NSString*(^)(BOOL abool))Two{
return Two(one(str));
}
如果是Swift,其實(shí)是及其相似的:
func testWith(Str:String, one:(String)->Bool,two:(Bool)->String) -> String {
return two(one(Str))
}
//調(diào)用
self.testWith(Str: "abc", one: { (str) -> Bool in
return str == "123"
}) { (abool) -> String in
return abool?"相等":"不相等"
}
這里的確是函數(shù)式編程蚕甥, 但是我們發(fā)現(xiàn)其中多了東西:雖然這里將一些函數(shù)作為另一函數(shù)的參數(shù)哪替,但是,這些函數(shù)的返回值和參數(shù)都具有一定的關(guān)聯(lián)菇怀,沒錯(cuò)凭舶,上面的函數(shù)中,block one
的返回值的類型恰恰是block two
的參數(shù)類型爱沟。他們有一些必然的聯(lián)系库快。這就涉及到了響應(yīng)式編程
的范疇,這里其實(shí)算是響應(yīng)式函數(shù)編程
钥顽,不在本文的范疇之內(nèi),有興趣的可以自行了解靠汁,本文不做介紹.
但是無可厚非的是蜂大,這里我們可以看到函數(shù)式編程的特點(diǎn):將函數(shù)做為參數(shù)來使用,這就使得函數(shù)本身具備更大的靈活性蝶怔,通過參數(shù)函數(shù)適應(yīng)不同的應(yīng)用場(chǎng)景奶浦。
將函數(shù)配合泛型,又能繼續(xù)擴(kuò)大函數(shù)的靈活性踢星。下面修改一下上面的例子看看泛型中的函數(shù)式編程:
func testWith<T:Comparable>(_ obj:T, one:(T)->Bool,two:(Bool)->String) -> String {
return two(one(obj))
}
調(diào)用
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)
以上就是函數(shù)式編程結(jié)合泛型澳叉。
提一下我們經(jīng)常聽到的RAC,這是一個(gè)響應(yīng)式函數(shù)編程沐悦,同時(shí)也會(huì)使用到泛型成洗。 關(guān)于響應(yīng)式編程, 我會(huì)在以后的文章中寫出藏否,同時(shí)將包含RAC的知識(shí)瓶殃。