前言

  • 由于柯里化在业务层的应用较少,所以从 Swift 3.0 开始移除了柯里化的用法,但是 Swift 的很多底层特性是使用柯里化来表达的。

1、柯里化

1.1 柯里化简介

  • 柯里化(Currying),又称部分求值(Partial Evaluation),是一种函数式编程思想,就是把接受多个参数的函数转换成接收一个单一参数(最初函数的第一个参数)的函数,并且返回一个接受余下参数的新函数技术。

  • uncurried: 普通函数

    // 接收多个参数的函数(与类相关的函数,统称为方法,但是这里就直接说函数了,方便理解)
    func add(a: Int, b: Int, c: Int) -> Int { print("\(a) + \(b) + \(c)")
    return a + b + c
    }
  • curried: 柯里化函数

    // 柯里化函数,Swift 3.0 之前支持这样的语法,可以直接写
    func addCur(a: Int)(b: Int)(c: Int) -> Int { println("\(a) + \(b) + \(c)")
    return a + b + c
    }

1.2 如何定义柯里化函数

  • 定义柯里化函数

    func function name (parameters)(parameters) -> return type {
    
        statements
    }

1.3 柯里化函数实现原理

  • uncurried: 普通函数

    class Currying {
    
        // 接收多个参数的函数
    func add(a: Int, b: Int, c: Int) -> Int { print("\(a) + \(b) + \(c)")
    return a + b + c
    }
    }
  • 系统自带的柯里化函数

    class Currying {
    
        func addCur(a: Int)(b: Int)(c: Int) -> Int {
    
            print("\(a) + \(b) + \(c)")
    return a + b + c
    }
    }
  • 手动实现柯里化函数

    • 把上面的函数转换为柯里化函数,首先转成接收第一个参数 a,并且返回接收余下第一个参数 b 的新函数(采用闭包).

    • 这里为什么能使用参数 a、b、c ?

      • 利用闭包的值捕获特性,即使这些值作用域不在了,也可以捕获到他们的值。
      • 闭包会自动判断捕获的值是值拷贝还是值引用,如果修改了,就是值引用,否则值拷贝。
      • 注意只有在闭包中才可以,a、b、c 都在闭包中。
      class Currying {
      
          // (a: Int)                    : 参数
      // (b: Int) -> (c: Int) -> Int : 函数返回值(一个接收参数 b 的函数,并且这个函数又返回一个接收参数 c,
      // 返回值为 Int 类型的函数) // 定义一个接收参数 a,并且返回一个接收参数 b 的函数,并且这个函数又返回一个接收参数 c,返回值为 Int 类型的函数。
      func add(a: Int) -> (b: Int) -> (c: Int) -> Int { // 返回一个接收参数 b 的函数,并且这个函数又返回一个接收参数 c,返回值为 Int 类型的函数
      return { (b:Int) -> (c: Int) -> Int in // 返回一个接收余下第一个参数 c,并且返回结果为 Int 类型的函数
      return { (c: Int) -> Int in return a + b + c;
      }
      }
      }
      }

1.4 如何调用柯里化函数

  • 创建柯里化类的实例

    var curryInstance = Currying()
  • 手动实现的柯里化函数调用

    var result: Int = curryInstance.add(a: 10)(b: 20)(c: 30)
    • 可能很多人都是第一次看这样的调用,感觉有点不可思议。
    • 让我们回顾下 OC 创建对象 [[Person alloc] init],这种写法应该都见过吧,就是一下发送了两个消息,alloc 返回一个实例,再用实例调用 init 初始化,上面也是一样,一下调用多个函数,每次调用都会返回一个函数,然后再次调用这个返回的函数。
  • 手动实现的柯里化函数拆解调用

    • curryInstance.add(a: 10) 调用一个接收参数 a,并且返回一个接收参数 b 的函数,并且这个函数又返回一个接收参数 c,返回值为 Int 类型的函数。

      // functionB: 一个接收参数 b 的函数,并且这个函数又返回一个接收参数 c,返回值为 Int 类型的函数
      let functionB = curryInstance.add(a: 10)
    • functionB(b: 20) 调用一个接收参数 b 的函数,并且这个函数又返回一个接收参数 c,返回值为 Int 类型的函数。

      // functionC: 一个接收参数 c,返回值为 Int 类型的函数
      let functionC = functionB(b: 20)
    • functionC(c: 30) 调用一个接收参数 c,返回值为 Int 类型的函数。

      // result: 函数的返回值
      var result: Int = functionC(c: 30);
  • 系统的柯里化函数调用

    var result: Int = curryInstance.addCur(a: 10)(b: 20)(c: 30)
  • 系统的柯里化函数拆解调用

    • curryInstance.addCur(a: 10) 调用一个接收参数 a,并且返回一个接收参数 b 的函数,并且这个函数又返回一个接收参数 c,返回值为 Int 类型的函数。

      // Swift是强类型语言,这里没有报错,说明调用系统柯里化函数返回的类型和手动的 functionB 类型一致
      // functionB: 一个接收参数 b 的函数,并且这个函数又返回一个接收参数 c,返回值为 Int 类型的函数
      functionB = curryInstance.addCur(a: 10)
    • functionB(b: 20) 调用一个接收参数 b 的函数,并且这个函数又返回一个接收参数 c,返回值为 Int 类型的函数。

      // functionC: 一个接收参数c,返回值为Int类型的函数
      functionC = functionB(b: 20)
    • functionC(c: 30) 调用一个接收参数 c,返回值为 Int 类型的函数。

      // result: 函数的返回值
      result = functionC(c: 30) // 打印 60,60,60 说明手动实现的柯里化函数,和系统的一样。
      print("\(r), \(res), \(result)")

1.5 柯里化函数使用注意

  • 必须按照参数的定义顺序来调用柯里化函数,否则就会报错。

  • 柯里化函数的函数体只会执行一次,只会在调用完最后一个参数的时候执行柯里化函数体。

    • 以下调用 functionC(c: 30) 才会执行函数体,这个可以自己断点调试。

      // curried: 柯里化函数
      func addCur(a: Int)(b: Int)(c: Int) -> Int { println("\(a) + \(b) + \(c)")
      return a + b + c
      } // 创建柯里化类的实例
      var curryInstance = Currying() // 不会执行柯里化函数体
      functionB = curryInstance.addCur(a: 10) // 不会执行柯里化函数体
      functionC = functionB(b: 20) // 执行柯里化函数体
      result = functionC(c: 30)

1.6 柯里化函数的好处

  • 这里就需要了解函数式编程思想了,柯里化函数就是运用了函数式编程思想,推荐看这篇文章函数式编程初探

  • 特点

    • 1)只用 “表达式” (表达式: 单纯的运算过程,总是有返回值),不用 “语句” (语句: 执行某种操作,没有返回值)。
    • 2)不修改值,只返回新值。
  • 好处

    • 1)代码简洁。
    • 2)提高代码复用性。
    • 3)代码管理方便,相互之间不依赖,每个函数都是一个独立的模块,很容易进行单元测试。
    • 4)易于 “并发编程”,因为不修改变量的值,都是返回新值。

1.7 柯里化函数的实用性

  • 实用性一:复用性

    • 需求 1:地图类产品,很多界面都有相同的功能模块,比如搜索框。

      • 我们可以利用柯里化函数,来组装界面,把界面分成一个个小模块,这样其他界面有相同的模块,直接运用模块代码,去重新组装下就好了。
  • 实用性二:延迟性

    • 柯里化函数代码需要前面的方法调用完成之后,才会来到柯里化函数代码中。

    • 需求 2:阅读类产品,一个界面的显示,依赖于数据,需要加载完数据之后,才能判断界面显示。

      • 这时候也可以利用柯里化函数,来组装界面,把各个模块加载数据的方法抽出来,等全部加载完成,再去执行柯里化函数,柯里化函数主要实现界面的组装。
  • 举例说明

    // 组合接口
    // 为什么要定义接口,为了程序的扩展性,以后只需要在接口中添加对应的组合方法就好了。
    protocol CombineUI { func combine(top: () -> ())(bottom: () -> ())()
    } // 定义一个界面类,遵守组合接口
    class UI: CombineUI { func combine(top: () -> ())(bottom: () -> ())() { // 搭建顶部
    top() // 搭建底部
    bottom()
    }
    }

2、Swift 中实例方法的柯里化

  • Swift 中实例方法就是一个柯里化函数。

2.1 Swift 中实例方法的柯里化调用

  • Swift 中实例方法的柯里化调用

    • 示例结构体

      struct Example {
      
          var internalStr = ""
      
          func combine(externalStr: String) {
      print(internalStr + " " + externalStr)
      }
      }
    • 调用实例方法的常用格式

      let example = Example(internalStr: "hello")
      
      example.combine(externalStr: "word")            // hello word
    • 调用实例方法的柯里化格式

      let example = Example(internalStr: "hello")
      
      Example.combine(example)(externalStr: "word")   // hello word

Swift 柯里化的更多相关文章

  1. Swift # 柯里化函数

    前言 此次文章,讲述的是Swift的一个新特性(柯里化函数),可能很多iOS开发人员是第一次听这个词汇,包括我自己也是,自己也用了几天时间才总结出来,希望能帮助到各位咯,个人感觉偏向有开发经验的码友, ...

  2. swift 学习(二)基础知识 (函数,闭包,ARC,柯里化,反射)

    函数 func x(a:Int, b:Int)  {}   func x(a:Int, b:Int) -> Void {}  func x(a:Int, b:Int) ->(Int,Int ...

  3. Swift开发第四篇——柯里化

    本篇分为两部分: 一.柯里化的基本使用 二.柯里化的使用场景 一.柯里化的基本使用 柯里化(Currying):也就是把接受多个参数的方法变换成接受第一个参数的方法,并且返回接受余下的参数并且返回结果 ...

  4. Swift函数柯里化(Currying)简谈

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 下面简单说说Swift语言中的函数柯里化.简单的说就是把接收多 ...

  5. Swift 4.0 废弃的柯里化

    // 柯里化 // http://www.jianshu.com/p/6eaacadafa1a                               Swift 2.0 柯里化方法 (废弃) / ...

  6. JS 函数的柯里化与反柯里化

    ===================================== 函数的柯里化与反柯里化 ===================================== [这是一篇比较久之前的总 ...

  7. JS - 柯里化

    一:what's this? 柯里化: 是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术.其实,柯里化就是用闭包原理实现函数 ...

  8. JAVAScript柯里化、部分应用参数终极理解

    一.柯里化 在定义柯里化.部分应用参数的概念前,首先必须对闭包有深入的了解和定义,闭包一句话说清楚:函数返回值为函数. 柯里化的定义:将多参函数分解为按步骤接受单个参数的函数,如下代码: var mo ...

  9. 【转载】JS中bind方法与函数柯里化

    原生bind方法 不同于jQuery中的bind方法只是简单的绑定事件函数,原生js中bind()方法略复杂,该方法上在ES5中被引入,大概就是IE9+等现代浏览器都支持了(有关ES5各项特性的支持情 ...

随机推荐

  1. openstack学习-glance安装(三)

    glance在openstack负责镜像相关管理的,对外提供标准的api提供服务,glance有两个服务,一个是glance-api接受云系统镜像的创建.删除.读取请求.glance-registry ...

  2. springmvc中登录过滤器使用

      前言 一般的javaWeb项目中我们肯定会使用过滤器来实现对一些请求的过滤,通过过滤来实现对一些请求参数的设置和校验,比如我们比较熟悉的CharacterEncodingFilter就是一个字符编 ...

  3. Codeforces 269C Flawed Flow (看题解)

    我好菜啊啊啊.. 循环以下操作 1.从队列中取出一个顶点, 把哪些没有用过的边全部用当前方向. 2.看有没有点的入度和 == 出度和, 如果有将当前的点加入队列. 现在有一个问题就是, 有没有可能队列 ...

  4. 2015 Benelux Algorithm Programming Contest I- Interesting Integers

    题目大意:给你一个数字n(n<=1e9) ,让你求一个能包含这个数的斐波那契数列的第一项a 和第二项b,找出b最小的那个. 帮我复习了一下扩展欧几里得.... 思路:a,b,a+b,a+2b…… ...

  5. Kubernetes核心概念总结

    目录贴:Kubernetes学习系列 1.基础架构 1.1 Master Master节点上面主要由四个模块组成:APIServer.scheduler.controller manager.etcd ...

  6. hdu 3068 最长回文【manacher】(模板题)

    <题目链接> 最长回文 Problem Description 给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度.回文就是正反读都是一样的字符串,如ab ...

  7. Java并发程序设计(七)乐天派:无锁

    无锁 一.概述 无锁是处理并发的一种乐观策略,它会假设对资源的访问是没有冲突的.既然没有冲突自然不需要等待,所以所有的线程都可以在不停顿的状态下执行.那遇到冲突怎么办?接下来请看,无锁绝招“CAS”即 ...

  8. 使用TVTK库创建一个矩形视图

    from tvtk.api import tvtk # s=tvtk.ConeSource(height=,radius=) # print(s.center) #创建一个长方体数据源,并同时设置长宽 ...

  9. Python 线程和进程和协程总结

    Python 线程和进程和协程总结 线程和进程和协程 进程 进程是程序执行时的一个实例,是担当分配系统资源(CPU时间.内存等)的基本单位: 进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其 ...

  10. wampServer 安装 Redis 扩展

    REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统. Redis是一个开源的使用ANSI C语言编写.遵守B ...