【scala】2.控制结构和函数

简介

在Java或者C++中,咱们把表达式和语句看作两种不一样的东西。表达式有值,而语句执行动做。java

在Scala中,几乎全部构造出来的语法结构都是有值的。这个特性使得程序更加的精简,也更易读。app

一、条件表达式

scala> val x = 1
x: Int = 1

scala> val res = if(x == 1) 1 else 0
res: Int = 1

scala> var res = if(x == 1) "hello" else 3
res: Any = hello

二、语句终止

Scala的语句无需添加相似Java和C++的分号;表示结尾,编译器会自动判断。固然,若是单行下存在多个语句,那么则须要用分号隔开前面的n-1个语句:框架

if ( x > 1) { r = r * n; n = n -1 }

三、块表达式和赋值

一、块表达式函数

{ }表示一系列表达式,其结果也是一个表达式。块中最后一个表达式的值就是块的值。this

val distance = { val dx = x - x0; val dy = y - y0; sqrt(dx *dx + dy * dy)}

能够看到,这样的语法能够很干净的让dx、dy等对外部不可见了。scala

二、赋值语句code

一个以赋值语句结束的块,返回的是Unit类型的值。所以,相似于这样的操做可能和java中的不同递归

x = y = 1

显然 x 的值为y = 1,即(),也是Unit类型。前面咱们提到过一次性初始化的方式element

scala> val x, y = 1;
x: Int = 1
y: Int = 1

四、输入和输出

一、普通输出文档

scala> print("I love you.")
I love you.
scala> println("I love you too.")
I love you too.
换行

二、字符串插值输出

scala> val name = "akuya"
name: String = akuya

scala> print(f"I love $name!")
I love akuya!

格式化的字符串是Scala类库定义的三个字符串插值器之一。经过不一样的前缀采起不一样的输出策略:

  • s: 字符串能够包含表达式但不能有格式化指令;
  • row:转义序列不会被求值。例如 raw"\n love." 其中的\n会原样输出
  • f:带有C风格的格式化字符串的表达式。

三、控制台读取

可使用scala.io.StdIn的readLine方法从控制台读取一行输入。

五、循环

一、while

和java的使用一致

二、for

和java的使用有所区别,其使用方式以下所示

for(i <- 表达式)

例如

scala> for(i <- 1 to 10) println(i)
1
2
3
...

能够看到中间的特殊符号 <- 表示让变量i遍历(<-)右边的表达式的全部值。至于这个遍历具体的执行方式,则取决于后面的表达式的类型。对于集合而言,他会让i依次取得区间中的每一个值。例如:

scala> for(i <- "abcde") print(s" $i")
 a b c d e

六、高级for循环

生成器>- 后面的表达式。

守卫:每一个生成器均可以带上守卫,一个以if开头的Boolean表达式

scala> for(i <- 1 to 3; j <- 1 to 3 if(i != j)) print(f"(i=$i,j=$j) ")
(i=1,j=2) (i=1,j=3) (i=2,j=1) (i=2,j=3) (i=3,j=1) (i=3,j=2)

注意在if以前没有分号,多个生成器之间须要分号

定义:在循环中对变量赋值,从而引入变量。

scala> for(i <- 1 to 3; j <- 1 to 3;home = i) print(f"(i=$i,j=$j,home=$home) ")
(i=1,j=1,home=1) (i=1,j=2,home=1) (i=1,j=3,home=1) (i=2,j=1,home=2) (i=2,j=2,home=2) (i=2,j=3,home=2) (i=3,j=1,home=3) (i=3,j=2,home=3) (i=3,j=3,home=3)

其中home = i就是一个定义。

yield:若是for循环的循环体以yield关键字开始,则该循环会构造出一个集合,每次迭代生成集合中的一个值:

scala> val vec = for (i <- 1 to 20) yield i % 2
vec: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0)

七、函数

注意与类的方法进行区分。

在Java中函数只能用类的静态方法来模拟。

定义函数

def abs(x:Double) = if(x >= 0) x else -x

若是函数不是递归的,就不须要指定返回值类型。

一样,块中的最后一个表达式的值就是函数的值。

若是是递归函数,则须要定义返回值类型

def fac(n: Int): Int = {...}

八、默认参数和带名参数

调用某些函数时能够没必要显式的给出全部参数值,对于这些函数咱们可使用默认参数。

def decorate(str: String, left: String = "[", right: String = "]"){...}

// 调用
decorate("hello")

// 调用2
decorate("hello","(",")")

九、变长参数

def sum(args: Int*) = {...}

十、过程

Scala对于不返回值的函数有特殊的表示法。若是函数体包含在花括号中但没有前面的=,那么返回类型就是Unit。这样的函数被称为过程

// 省略了=号
def box(s: String) {...}

固然,既然返回值是Unit类型,那么过程也能够用以下的方法定义

def box(s: String): Unit = {...}

十一、懒值

当val被生命为lazy时,他的初始化将会被推迟,直到咱们首次对他取值。很像Linq或者Spark RDD等许多数据处理框架的惰性原理

scala> lazy val words = scala.io.Source.fromFile("/noexist.file")
words: scala.io.BufferedSource = <lazy>

scala> words.toString
java.io.FileNotFoundException: \noexist.file (系统找不到指定的文件。)

能够看到,即使咱们一开始的words取的是一个不存在的文件,也没有当即报错,而是在咱们对words进行取值以后才出现了错误。

能够把懒值理解为val和def的中间状态。

十二、异常

Scala和java不同,不支持“受检异常”。

throws表达式有特殊的类型Nothing。

若是一个分支的类型是Nothing,那么if/else表达式的类型就是另外一个分支的类型。

捕获异常的语法采用的是模式匹配语法。

L、练习

一、一个数字若是为正数,则他的signum为1;若是是负数,则signum为-1,;若是是0,则signum为0.编写一个函数来计算这个值。

package com.zhaoyi.c2

object Practice2 {

  def signum(x: Int): Int = {
    if(x > 0){
      1
    } else if( x == 0){
      0
    }else{
      -1
    }
  }

  def main(args: Array[String]): Unit = {
    println("signum(100) = " + signum(100));
    println("signum(0) = " + signum(0));
    println("signum(-100) = " + signum(-100));

    // 或者使用系统函数
    println("use system signum(10) = " + BigInt(10).signum);
  }
}

输出结果:

signum(100) = 1
signum(0) = 0
signum(-100) = -1
use system signum(10) = 1

二、一个空的块表达式{}的值是什么?类型是什么?

scala> var s = {}
s: Unit = ()

Unit表示无值,和其余语言中void等同。用做不返回任何结果的方法的结果类型。Unit只有一个实例值,写成()。

三、指出在scala中何种状况下赋值语句 x = y = 1是合法的。

显然,只须要有“须要x的值为()”的时候,这样的需求是合法的。

四、针对下列Java循环编写一个Scala版的程序。

for(int i = 10;i>=0;i--){
       System.out.println(i);
}

scala版:

for(i <- 1 to 10 reverse) println(i)

五、编写一个过程countdown(n: Int),打印从n到0的数字。

def answer5(n: Int): Unit ={
    for(i <- 0 to n reverse){
      print(i + " ")
    }
  }

六、编写一个for循环,计算字符串中全部字母的Unicode代码的乘积。

def answer6(str: String): Long = {
    var res: Long = 1
    for(c <- str){
      res *= c.toLong
    }
    println(s"$str count value is: " + res)
    res
  }

  def main(args: Array[String]): Unit = {
    answer6("Hello")
  }

七、一样是问题6,但此次不容许使用循环语句。

查找到

def foreach(f: (A) ⇒ Unit): Unit
[use case] Applies a function f to all elements of this string.

所以能够考虑使用foreach方法计算。

def answer7(str: String): Long = {
    var res: Long = 1
    str.foreach(c => res *= c.toLong)
    println(s"$str count value is: " + res)
    res
  }

  def main(args: Array[String]): Unit = {
    answer7("Hello")
  }

八、编写一个函数product(s: String),计算前面练习中提到的乘积。

def product(str: String): Long = {
    var res: Long = 1
    str.foreach(c => res *= c.toLong)
    println(s"$str count value is: " + res)
    res
  }

九、把前一个练习中的函数改造为递归函数

def answer9(str: String): Long = {
    if(str.length == 1){
      str(0).toLong
    }else{
      // 选择第0个元素,返回除了第0个元素的其余元素
      str(0).toLong * answer9(str.drop(1))
    }
  }

  def main(args: Array[String]): Unit = {
    val ans = answer9("Hello")
    print(ans)
  }

其中Doc文档:

// 返回除了前n个节点的元素
def drop(n: Int): String
Selects all elements except first n ones.

// 并无用到此方法
def take(n: Int): String
Selects first n elements.

// 默认方法 apply
def apply(index: Int): Char
Return element at index n

十、编写函数计算$x^n$,其中n是整数,使用以下的递归定义

  • $x^n=y*y$,若是n是正偶数的话,这里的$y=x^{\frac{n}{2}}$;
  • $x^n=x*x^(n-1)$,若是n是正奇数的话;
  • $x^0=1$
  • $x^n=\frac{1}{x^{-n}}$,若是n是负数的话。
def answer10(x: Double, n: Int): Double = {
    if(n == 0) 1
    else if (n > 0 && n % 2 == 0) answer10(x,n/2) * answer10(x,n/2)
    else if (n > 0 && n % 2 == 1) x * answer10(x,n - 1)
    else 1 / answer10(x, -n)
  }

  def main(args: Array[String]): Unit = {
    val ans = answer10(2,2)
    print(ans)
    // 4.0
  }
相关文章
相关标签/搜索