• 白话 Scala 控制抽象


    在 《快学Scala》 一书中,控制抽象被描述为是一系列语句的聚集,是一种特殊的函数,因为它是本质上只是对一系列语句的封装,所以它理应:

    1. 没有参数输入

    2. 没有值返回

    教材中还给出了两段代码示例来解释控制抽象。但这两段代码对于 Scala 初学者来说,可能没那么好理解。这篇文章主要就针对教材中的示例作个白话解释。

    第 1 个例子

    def runInThread(block: () => Unit) {
        new Thread {
            override def run() {
                block()
            }
        }
    }

    上面这个例子的功能倒好理解,它就是一个将传入的代码块放到一个全新的子线程里去执行的功能。这种场景在日常工作过程中是一很常见的场景,但是笔者认为这个例子对于初学者理解 控制抽象 却不是一个合适的例子。因为,有很多初学者甚至还没来得学习 Scala 中的对象实例化及匿名内部类的知识,虽然在 Scala 中它长的与 Java 非常像,但对于猜测的知识而言,心里始终会有个芥蒂。本来是只想学习控制抽象知识点的,突然穿插一个可能是匿名内部类,可能是线程的知识点进来,会加重读者学习负担,甚至打击学习信心。因此我准备换一个示例代码,这个示例代码除了控制抽象的知识以外,其余的全是单纯逻辑代码,不会对读者对控制抽象的学习造成额外的负担。

    新的示例代码就定义一个类似 “购物” 的功能模块。封装一个 myShop 函数,这个函数开放给顾客调用,每次调用都表示一位顾客的购买行为。

    def myShop(block: () => Unit) {
        
        println("Welcome in!")
        block()
        println("Thanks for coming!")
        
      }

    好,接下来就开始我们的学习。

    首先,上面我们有提到:控制抽象是一系列语句的聚集。因此,在示例代码中,千万不要觉得 def myShop 就是控制抽象。block 作为 “语句块” 才应该是控制抽象

    函数 myShop 的参数的变量名称是 block ,它的名称也已经很直白了,说明这里就应该传 “一块” 代码进来。而它的参数则是一个匿名函数类型 () => Unit ,它没有输入参数,也没有值返回。随后便是函数体的声明,这种函数名后面直接接大括号来写函数体的方式在 Scala 中被称为 “过程” ,简单说就是声明这个函数( myShop 函数 )是一个没有返回值的函数。在给这个 myShop 函数传参时,直接将你需要的语句块一骨脑扔进去就好。

    《快学Scala》 中调用 runInThread 函数的方式是

    runInThread {
        () =>
            println("Hi")
            Thread.sleep(10000)
            println("Bye")
    }

    类似地,在我们这个 myShop 函数中,也可以有如下调用方式

    myShop {
        () =>
            println("I want a pencil")
            println("I want a book")
            println("I want your wechat")
    }

    myShop 例子的执行结果如下

    mm... 这种调用方式乍看上去感觉像是在写一个函数体一样,感觉怪怪的。但其实只要你了解了 Scala 中 圆括号 与 大括号 的作用以后,这段代码你看起来就没什么毛病的了。

    在 Scala 中,调用函数时,一般既可以使用圆括号来传参,也可以使用大括号来传参。不信?

    所以,上面不管是 runInThread 还是 myShop 的函数调用,都只是在传参而已。圆括号与大括号的区别在于可以传递的代码量而已。圆括号只能传递一条语句,而大括号可以传递多条语句。因此,对于我们的 myShop 的例子来说,假如某个人只想买一件商品,那么,完全可以使用以下的调用方式。同时,由于控制抽象函数不需要传参,所以一个 空的参数列表括号 在这也挺多余的,所以,函数声明和调用都可以简化一下。

    def myShop(block: => Unit) {
    
        println("Welcome in!")
        block
        println("Thanks for coming!")
    
    }
    
    def main(args: Array[String]){
        myShop( println("I wanna buy a condom") )
    }

    怎么样,这样看起来是不是顺眼多了? 它的执行结果当然也是没毛病的。

    当然,即使是只想买一件商品,使用大括号来调用也是没有问题的

    myShop{ println("I wanna buy a pen") }

    不管是 runInThread 函数还是我们的 myShop 函数,最重要的都是把传进来的代码块执行一次。有的同学可能会问:我为什么不直接单独封装我那一系列代码块,而非得复杂化成控制抽象来执行呢? 关于这点,我觉得如果您了解设计模式中的代理模式的话,可能就不会问出这个问题来了。附上一篇笔者多年前写的博文: 大白话设计模式之代理模式  

    第 2 个例子

    《快学 Scala 》 中第 2 个例子是一个应用到柯里化与递归调用的控制抽象示例

    def until (condition: => Boolean) (block: => Unit) {
        if(!condition) {
            block
            until(condition)(block)
        }
    }

    在有了上面第 1 个例子的理解以后,对于这个例子的函数定义,应该就没什么问题的了。而关于这个函数的调用则是

    var x = 10
    until (x == 0) {
         x -= 1
         println(x)
    }

    信这个函数调用,很多初学者都看的挺懵的,笔者也不例外。

    这个例子书中说是模拟一个 while 表达式出来,看它的调用方式,也确实和 while 表达式一样。但是我们的 until 函数明明是定义了两个参数的,这里只传一个真的合适吗?

    还是那句话:在理解了 Scala 中圆括号和大括号的作用以后,这段调用就显得各种没毛病了。这个调用中,确确实实是以柯里化的形式传了两个参数进去的。只不过第 1 个参数是以圆括号的形式来传递的,第 2 个参数由于是一系列的代码块,所以要用大括号的形式来传递

    假如,我们在这个例子中的控制抽象函数只有一条语句的话,until 的调用形式完全可以改成下面这种更明朗的格式

    var x = 10
    until (x == 0)( x -= 1)
    关注公众号 海量干货等你
  • 相关阅读:
    开发落网电台windows phone 8应用的计划(3)
    开发落网电台windows phone 8应用的计划(2)
    开发落网电台windows phone 8应用的计划(1)
    codeforces 111B Petya and Divisors
    Mac远程连接windows报错“证书或相关链无效,是否仍要连接到此计算机”的处理办法。
    Navicat连接报错:cannot load OCI DLL,126
    navicat 连接oracle数据库报错:ORA-28547:connection to server failed,probable Oracle Net admin error
    无法启动此程序因为计算机中丢失msvcr71
    CSS实现矩形按钮右边缘的中间有个往里凹的小半圆
    如何运行jar文件
  • 原文地址:https://www.cnblogs.com/sowhat1412/p/12734157.html
Copyright © 2020-2023  润新知