• Scala函数特性


    通常情况下,函数的參数是传值參数;即參数的值在它被传递给函数之前被确定。可是,假设我们须要编写一个接收參数不希望立即计算。直到调用函数内的表达式才进行真正的计算的函数

    对于这样的情况。Scala提供按名称參数调用函数。

    演示样例代码例如以下:


     

    结果:


     

    在代码中,假设定义函数的时候,传入參数不是传入的值,而是传入的參数名称(如代码中使用t: => Long而不是t: Long)。在调用该函数时,不会马上运行和參数有关的计算,而是到參数真正使用到的时候才进行计算。

     

    结果说明:主函数调用delayed函数后。并不马上运行參数(time()函数的结果),而是跳过。直接运行delayed函数的第一行,到第二行真正使用到t时,才运行time()函数获取t的值,故有上述结果。

     

    假设将t: => Long改成t: Long。则依照值传递进行计算。结果例如以下:


    此时主函数调用了delayed函数后就直接先运行time()函数获取t的值

     

    还有一个演示样例代码例如以下:


    这里调用了一个log函数,传入的參数中有1/0。按理说应该会报异常。但实际结果却是代码顺利的运行完毕。为什么呢?这是由于我们在定义log函数的时候使用了“按名称传递參数”。仅仅有到实际运行參数相关的计算时才会检查是否有异常。而代码中if(logEnable) println(msg)这一行代码实际根本就不会运行,自然也就不会存在检查异常的问题了

     

    假设将msg: =>String改为msg: String。则运行代码就会报错


    由于在运行到log(MSG +1 / 0)这一句时就直接计算了1/0,自然就会报错了

     

    使用“按名称传递參数”方式的长处是:1.降低不必要的计算。 2.降低异常

    在正常的函数调用中。调用參数在调用函数中是按其定义时的參数顺序进行一一匹配。

    假设须要按不同的顺序传递參数。就要使用到scala的一种函数特性——命名參数。

    命名參数用法非常easy,即在调用函数时,指定參数名并进行赋值。演示样例代码例如以下:


     

    结果:


     

    从代码和结果中就能够看出使用命名參数的长处:在须要时能够随意的指定函数中某个參数的值,而不必将此參数之前的參数都赋值一遍。


    scalajava一样。在定义函数的时候支持接收可变长參数列表。即最后一个參数的能够被反复。

    演示样例代码例如以下:


     

    结果:


    在此代码中我们定义函数printInfo接收变长參数列表。其最后一个參数names能够依据实际情况进行传參(这里我们传了3个实參)。

    注意。函数可变參数仅仅能是该函数的最后一个參数(否则不能识别參数长度,这个应该非常好理解)。

    printInfo函数被声明的參数类型names: String*实际是数组[字符串]

    scala中,函数是“头等公民”,差点儿全部的操作都是以函数形式进行。相同的,可以在变量中存放函数(听上去非常奇妙吧)。示比例如以下:


    本段代码将ceil函数赋值给fun变量,ceil后面的_表名这是一个函数,而不是碰巧忘记给它传參。

     

    scala中。不须要为每一个函数命名,这样的没有命名的函数叫做匿名函数

    怎样进行匿名函数的定义呢?示比例如以下:

    (x:Double) => 3 * x

    这就是一个匿名函数。

    就好像在scala中可以把函数赋值给变量一样,我们可以把匿名函数赋值给变量:

    valtriple = (x: Double) => 3 * x

    这种方式跟使用def定义函数一样:

    deftriple(x: Double) = 3 * x

    可是优点就是可以不给函数命名,就能直接将它传递给还有一个函数。这样的方法在使用mapfilter等函数时很经常使用:



    因为scala有能够将函数作为參数传递给函数的特性,故从中引出了一种简单的封装模式——借贷模式。代码示比例如以下:


    在此代码中,我们将BufferedWriter进行了封装。仅仅要调用autoWrite函数,传入适当的參数,就能够完毕字符串写入文件,并且不须要关闭BufferedWriter。

    使用借贷模式是对系统资源操作的封装,为了防止资源不被安全释放。其次用户不用考虑资源来自何处,怎样归还等问题,仅仅须要使用即可了。


    带函数參数的函数因为是一个接受函数參数的函数,故被称为高阶函数。像之前讲到的map()函数就是高阶函数。例如以下例所看到的:


    上述代码中,apply函数接受一个函数f作为參数,接受一个Int类型的參数,进行f(v)运算,在以下又给出了f详细的定义(layout函数)。

     

    相同的。高阶函数也能够产出还有一个函数(即返回结果为一个函数,而不是某个值或对象)。例如以下例所看到的:


    这里函数rectangle的输出是一个计算矩形周长的函数,矩形长已固定。

     

     

    在高阶函数中,常常将仅仅须要运行一次的函数定义为匿名函数作为參数传递给高阶函数。就好像map()filter()等高阶函数中常常能看到使用了匿名函数作为參数。匿名函数在这里有一个特性可以帮助我们写出更easy阅读的函数——參数判断

    正常情况下。我们使用匿名函数的方式例如以下:


    即在map函数中定义匿名函数(a: Double) => a * 3,可是因为map函数知道你传入的是一个类型为(Double)=> Double类型的函数,故能够简化为以下的代码:


    而且假设匿名函数仅仅有一个參数。则能够省略()。继续简化:


    在此基础上,假设參数在=>右边仅仅出现了一次。则能够用_替换它:



    柯里化是指将原来接收两个參数的函数变成接收一个參数的函数的过程,新的函数返回一个以原有第二个參数作为參数的函数。是不是有种被绕晕了的感觉,先别急,先看一个演示样例:


     

    结果:


     

    这里能够看出,柯里化函数与多个參数的函数具有同样的功能,这中间有一个“应用部分函数”。或者叫“偏应用函数”,这个函数multipleOf4表示固定了两个參数中的一个,部分提供了函数mul所须要的參数。而不是所有提供。柯里化函数在理解上比較偏向于这种逻辑。

    那么。为什么要使用柯里化呢?首先,柯里化能够让我们构造出更像原生语句提供的功能的代码(就像我们在上面说的那样)。第二点也是更重要的一点,就是函数柯里化后,參数相对独立了,这样就能够对函数的某个參数单独提供很多其它的类型判断信息。例如以下例所看到的:


    这里的corresponds函数就是柯里化函数,其定义例如以下:


    使用柯里化后,该函数柯里化后单独对第二个參数进行了更具体的功能定义。丰富了函数的功能



  • 相关阅读:
    类型“System.Windows.Markup.IUriContext”在未被引用的程序集中定义 解决办法
    c# 根据文件流查看文件真实格式
    WPF ListBoxItem 使用Command命令添加双击事件
    MVVM 在使用 ItemsSource 之前,项集合必须为空
    WPF 自定义TextBox,可控制键盘输入内容
    百万数据如何在前端快速流畅显示?
    NodeJS npm 包装包失败的解决方案
    node.js express 4.x 安装指南(Express不是内部或外部命令解决方案)
    IIS8 不能在此路径中使用此配置节。如果在父级别上锁定了该节
    Npoi操作excel
  • 原文地址:https://www.cnblogs.com/jzssuanfa/p/7391484.html
Copyright © 2020-2023  润新知