iOS 中的链式编程、函数式编程入门

对一个程序猿来说,从一开始接触编程后会逐渐经历好几个不同的编程思想。包括过程式编程、面向对象编程、响应式编程、函数式编程、链式编程等等。

过程式编程的特点是随着程序的编写逐步进行,写到哪儿运行到哪儿。

面向对象的特点是万物皆对象。很著名的例子:把大象放进冰箱。

响应式编程的特点是一方触发,多方响应。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的知识。

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

推荐阅读更多精彩内容