1. 程式人生 > >【scala】2.控制結構和函數

【scala】2.控制結構和函數

元素 lazy n-1 var 高級 all 構造 序列 ash

  簡介
  
  在Java或者C++中,我們把表達式和語句看做兩種不同的東西。表達式有值,而語句執行動作。
  
  在Scala中,幾乎所有構造出來的語法結構都是有值的。這個特性使得程序更加的精簡,也更易讀。
  
  1、條件表達式
  
  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
  
  2、語句終止
  
  Scala的語句無需添加類似Java和C++的分號;表示結尾,編譯器會自動判斷。當然,如果單行下存在多個語句,那麽則需要用分號隔開前面的n-1個語句:
  
  if ( x > 1) { r = r * n; n = n -1 }
  
  3、塊表達式和賦值
  
  1、塊表達式
  
  { }表示一系列表達式,其結果也是一個表達式。塊中最後一個表達式的值就是塊的值。
  
  val distance = { val dx = x - x0; val dy = y - y0; sqrt(dx *dx + dy * dy)}
  
  可以看到,這樣的語法可以很幹凈的讓dx、dy等對外部不可見了。
  
  2、賦值語句
  
  一個以賦值語句結束的塊,返回的是Unit類型的值。因此,類似於這樣的操作可能和java中的不一樣
  
  x = y = 1
  
  顯然 x 的值為y = 1,即(),也是Unit類型。前面我們提到過一次性初始化的方式
  
  scala> val x, y = 1;
  
  x: Int = 1
  
  y: Int = 1
  
  4、輸入和輸出
  
  1、普通輸出
  
  scala> print("I love you.")
  
  I love you.
  
  scala> println("I love you too.")
  
  I love you too.
  
  換行
  
  2、字符串插值輸出
  
  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風格的格式化字符串的表達式。
  
  3、控制臺讀取
  
  可以使用scala.io.StdIn的readLine方法從控制臺讀取一行輸入。
  
  5、循環
  
  1、while
  
  和java的使用一致
  
  2、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
  
  6、高級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)
  
  7、函數
  
  註意與類的方法進行區分。
  
  在Java中函數只能用類的靜態方法來模擬。
  
  定義函數
  
  def abs(x:Double) = if(x >= 0) x else -x
  
  如果函數不是遞歸的,就不需要指定返回值類型。
  
  同樣,塊中的最後一個表達式的值就是函數的值。
  
  如果是遞歸函數,則需要定義返回值類型
  
  def fac(n: Int): Int = {...}
  
  8、默認參數和帶名參數
  
  調用某些函數時可以不必顯式的給出所有參數值,對於這些函數我們可以使用默認參數。
  
  def decorate(str: String, left: String = "[", right: String = "]"){...}
  
  // 調用
  
  decorate("hello")
  
  // 調用2
  
  decorate("hello","(",")")
  
  9、變長參數
  
  def sum(args: Int*) = {...}
  
  10、過程
  
  Scala對於不返回值的函數有特殊的表示法。如果函數體包含在花括號中但沒有前面的=,那麽返回類型就是Unit。這樣的函數被稱為過程。
  
  // 省略了=號
  
  def box(s: String) {...}
  
  當然,既然返回值是Unit類型,那麽過程也可以用如下的方法定義
  
  def box(s: String): Unit = {...}
  
  11、懶值
  
  當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的中間狀態。
  
  12、異常
  
  Scala和java不一樣,不支持“受檢異常”。
  
  throws表達式有特殊的類型Nothing。
  
  如果一個分支的類型是Nothing,那麽if/else表達式的類型就是另一個分支的類型。
  
  捕獲異常的語法采用的是模式匹配語法。
  
  L、練習
  
  1、一個數字如果為正數,則他的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
  
  2、一個空的塊表達式{}的值是什麽?類型是什麽?
  
  scala> var s = {}
  
  s: Unit = ()
  
  Unit表示無值,和其他語言中void等同。用作不返回任何結果的方法的結果類型。Unit只有一個實例值,寫成()。
  
  3、指出在scala中何種情況下賦值語句 x = y = 1是合法的。
  
  顯然,只需要有“需要x的值為()”的時候,這樣的需求是合法的。
  
  4、針對下列Java循環編寫一個Scala版的程序。
  
  for(int i = 10;i>=0;i--){
  
  System.out.println(i);
  
  }
  
  scala版:
  
  for(i <- 1 to 10 reverse) println(i)
  
  5、編寫一個過程countdown(n: Int),打印從n到0的數字。
  
  def answer5(n: Int): Unit ={
  
  for(i <- 0 to n reverse){
  
  print(i + " ")
  
  }
  
  }
  
  6、編寫一個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")
  
  }
  
  7、同樣是問題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")
  
  }
  
  8、編寫一個函數product(s: String),計算前面練習中提到的乘積。
  
  def product(str: www.yongshiyule178.com String): Long = {
  
  var res: Long = 1
  
  str.foreach(c =>www.zhenghongyule.com/ res *= c.toLong)
  
  println(s"$str count value is: " + res)
  
  res
  
  }
  
  9、把前一個練習中的函數改造為遞歸函數
  
  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( dasheng178.com"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
  
  10、編寫函數計算$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: www.yongshi123.cn 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[www.mytxyl1.com String]): Unit = {
  
  val ans = answer10(2,2)
  
  print(ans)
  
  // 4.0

【scala】2.控制結構和函數