Scala学习-函数和方法(thirty-three day)

接着上一篇学习scala:

调用函数和方法

      除了方法之外,Scala还提供函数。相比Java, 在Scala中使用数学函数(比如min或pow)更为简单:你不需 要从某个类调用它的静态方法。

sqrt(2) 11 将产出1.4142135623730951

pow(2, 4)  // 将产出 16.0

min(3,Pi)> // 将产出3.0

这些数学函数足在scala.math包屮定义的。你可以用如下语句进行引入,

import scala.math._     // 在Scala中,_ 字符是 *通配符”,类似Java中的*

使用以 scala.开头的包时,我们可以省去 sea la觔缀。例如 ,import math._等同于import scala.math._, 而math.sqrt(2)等同于scala.math.sqrt(2) 。

现阶段你只需要在引入特定包时使用 import包名. _ 即可。

Scala没有静态方法,不过它有个类似的特性,叫做单例对象( singleton object)

通常, 一 个类对应奋一个伴生对象( companion object), 其方法就跟java中的静态方法一样。举例来说,BigInt类的Biglnt伴生对象有一个生成指定位数的随机索数的方法probablePrime

BigInt. probablePrime (100, scala. util.Random)

      在REPL(read+eval(运算)+printf+loop)运行上述代码,你会得到类似1039447980491200275486540240713这样的数字。注意BigInt,probablePrime这样的调用和Java中的静态方法调用很相似。这里的Random是一一个 单例的随机数生成器对象,而该对象是在scala.util包中定义的。这是用单例对象比用类更好的为数不多的场景之-。在Java中,为每个随机数都构造出一一个新的java.util.Random对象是一个常见的错误。

      不带参数的Scala方法通常不使用圆括号。例如,StringOps类的API显示它有-一个distinct方法,不带(),其作用是获取字符串中不重复的字符。调用方法如下:

      "Hello" .distinct

      一般来讲, 没有参数且不改变当前对象的方法不带圆括号。

 

apply

在Scala中,我们通常都会使用类似函数调用的语法。举例来说,如果s是一个字符申,那么s(i)就是该字符串的第i个字符。( 在C++中,你会写s[i];而在Java中, 你会这样写: s.charAt()。 )在REPL中运行如下代码:
"Hello"(4) //将产出'o'          类似于charAt(4)
你可以把这种用法当做是()操作符的重载形式,它背后的实现原理是个名为apply的方法。举例来说,在StringOps类的文档中, 你会发现这样一个方法:
def apply(n:Int): Char 

也就是说,"Hello"(4)是如下 语句的简写:
"Hello" .apply(4)
如果你去看BigInt伴生对象的文档,就会看到让你将字符申或数字转换为BigInt对象的apply方法。举例来说,如下调用
BigInt ("1234567890")  是BigInt.apply ("1234567890") 的简写

这个语句产出一个新的BigIn对象,不需要使用new。例如:

      BigInc ("1234567890")*BigInt ("112358111321")

像这样使用伴生对象的apply方法是Scala中构建对象的常用手法。例如,Array(1, 4, 9,16)返回-一个数组,用的就是Array伴生对象的apply方法。

 Scaladoc相当于 Java中的Javedoc来浏览Java API

 

控制结构和函数

条件表达式

Scala的ifelse语法结构和Java或C++- -样。 不过,在Scala中ifelse表达式有值, 这个值就是跟在if或else之后的表达式的值。例如:
if(x>0)1else -1
上述表达式的值是1或-1,具体是哪- 个取决于x的值。你可以将ifelse表达式的值赋值给变量:
val s=if(x>0)1 else-1
这与如下语句的效果-样:
if(x>0)s=l else s=-1
不过,第一种写法更好,因为它可以用来初始化一个val. 而在第种写法当中,s必须是var。
Scala中的分号绝大多数情况下不是必需的

Java和C++有个?:操作符用于同样目的。如下表达式
x>0?1:-1//Java或C++
等同于Sala表达式if(x>0) 1 else -1。不过,你不能在?:表达式中插人语句。Scala的f/else将在Java和C++中分开的两个语法结构if/else和?:结合在了- -起。
在Scala,每个表达式都有一个类型。 举例来说,表达式if(x> 0) 1 else -1的类型是Int,因为两个分支的类型都是Int。混合类型表达式,比如:

      if (x> 0) "positive" else -1

      上述表达式的类型是两个分支类型的公共超类型。在本例中,其中一个分支是java.lang.String.而另一个分支是Int。它们的公共超类型叫做Any。

      如果else部分缺失了,比如:

      if(x>0)1

      那么有可能if语句没有输出值。但是在Scala中,每个表达式都应该有某种值。这个问题的解决方案是引人一个Unity (写做()。)不带else的这个if语句等同于

    if (x > 0) 1 else ()

      你可以把()当做是表示“无有用值"的占位符,将Unit当做Java或C++中的void。(从技术上讲,void没有值但是Unit有一一个表示 “无值"的值。如果你定要深究的话,这就好比空的钱包和里面有一张写着“没钱"的无面值钞票的钱包之间的区别。)

Scala没有switch语句,不过它有一个强大得 多的模式匹配机制。

      注意: REPL比起编译器来更加“近视"           它 在同一时间只能看到一行代码。举例来说,当你键入如下代码时:

      if(x>0) 1

      else if (x ==0) 0 else -1

      REPL会执行if (x> 0) 1,然后显示结果。之后它看到接下来的else关键字就会不知所措。

      如果你想在else前换行的话,用花括号:

if(x>0)  {1

     } else if (x ==0) 0 else -1

scala中的粘贴模式   :paste键入    ctrl+D 退出

语句终止
在Java和C++中,每个语句都以分号结束。而在Scala中与JavaScript和其他脚本语言类似一-行尾的位置不需要分号。 同样,在}、else以及 类似的位置也不必写分号,只要能够从上下文明确地判断出这里是语句的终止即可2
不过,如果你想在单行中写下多个语句,就需要将它们以分号隔开。例如:

if(n>0) {r=r*n ; n-=1)
我们需要用分号将r=r*n和n-=1隔开。由于有},在第二个语句之后并不需要写分号。
如果你在写较长的语句,需要分两行来写的话,就要确保第-行以- 一个 不能用做语句结尾的符号结尾。通常来说一个比较好的选择是操作符:

s=s0+(v-v0)*七+     //+告诉解析器这里不是语句的末尾
0.5.(a-a0)*t*t
在实际编码时,长表达式通常涉及函数或方法调用,如此一来你并不需要过分担心一在左括号 (之后,编译器直到看到匹配的)才会去推断某处是否为语句结尾。
正因如此,Scala程序员们更倾向于使用Kerighan & Richie风格的花括号:
if(n>0){
r=r*n

n-=1

}

使用分号也可以。

输人和输出
如果要打印一个值,我们用print或printin函数。后者在打印完内容后会追加个换行符。举例来说,
print ("Answer: ")

print1n(42)
与下面的代码输出的内容相同:
println("Answer:”+ 42) 
另外,还有一个带有C风格格式化字符串的printf函数:
printf("Hello,%s! You are %d years old .\n, "Fred", 42)

循环

var n = 0 ;
    while(n < 10){
        println(n)
        n +=1
    }


    //for
    for(i <- 1 to 10){
        println(i)
    }

    //使用while和for输出99表格
    var r =  1
    while(r < 15){
        var c = 1
        while(c <= r){
            printf("%dx%d=%d\t" , c , r , (c * r))
            if(c == r) println()
            c+=1
        }
        r +=1
    }

    //for循环
    for(i <- 1 to 9){
        for(j <- 1 to i){
            printf("%dx%d=%d\t" , j , i , (i * j))
            if(i == j)println()
        }
    }

    //until, 1 ~ 9
    val a = 1 until 10

Scala并没有提供break或continue语句来退出循环。那么如果需要break时我们该怎么做呢?有如下几个选项:

1.使用Boolean型的控制变量。

2.使用嵌套函数一你可 以从函数当中return。

3.使用Breaks对象中的break方法:(注意tab键)

      import scala. util.control .Breaks._

breakable {

      for (.... ){

      if (... )break;     //退出breakable块

      }

}

 将代码写到scala脚本文件中,在scala中执行

写一个a.scala    $scala>:load /home/a.scala

 

高级循环

for(i <- 1 to 9 ; j <- 1 to i){
        printf("%dx%d=%d\t" , j , i i * j)
        if(i == i)println()
    }

    //守卫条件for循环
    for(i <- 1 to 9 ; j <- 1 to i if( i == 2 * j)){
        printf("i=%d,j=%d\r\n" , i , j)
    }

    //yield ,产生新集合
    for(i <- 1 to 5) yield i * 2

如果for循环的循环体以yicld开始,则该循环会构造出一个集合,每次迭代生成集合中的一个值:
for (i<-1 to10) yield i % 3
//生成Vector(1, 2, 0,1, 2, 0,1,2,0, 1)这类循环叫做for推导式。


for推导式生成的集合与它的第个生成器是类型兼容的。

for (c <- "Hello"; i<- 0 to 1) yield(c + i).toChar
1/将生成"HIeflmlmop"
for (i <-0 to1; c <- "Hello") yield (C + i) .toChar
1/将生成Vector('H', 'e', "1', '1','o, 'I', 'f', 'm', 'm', 'p')

可以将生成器、守卫和定义包含在花括号当中,并可以以换行的方式而不是分号来隔开它们:

for{i<-1to3
from-=4-i
」<- from to 3 }

 

函数

Scala除了方法外还支持函数。方法对对象进行操作,函数不是。C++也有函数,不过在Java中我们只能用静态方法来模拟。
要定义函数,你需要给出函数的名称、参数和函数体,就像这样:
def abs(x: Double) = if (x>= 0)x else -X
你必须给出所有参数的类型。不过,只要函数不是递归的,你就不需要指定返回类型。Scala编译 器可以通过=符号右侧的表达式的类型推断出返回类型。
如果函数体需要多个表达式完成,可以用代码块。块中最后一个表达式的值就是函数的返回值。举例来说,下面的这个函数返回位于for循环之后的r的值。

阶乘
def fac(n: Int) = {
varr=1
for (i <- 1 to n) r = r * i

r}
在本例中我们并不需要用到return。我们也可以像Java或C+ +那样使用retum,来立即从某个函数中退出,不过在Scala中这种做法并不常见。
 

 

对于递归函数,我们必须指定返回类型。例如:
def fac(n: Int): Int=if (n<=0)1 else n* fac(n- 1)
如果没有返回类型,Scala编译器无法校验n * fac(n - 1)的类型是Int。

某些编程语言(如ML和Haskell )能够推断出递归函数的类型,用的是Hindley- MiIner算法。 不过,在面向对象的语言中这样做并不总是行得通。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值