Functional Game
程式範例 :
小遊戲:猜硬幣正反面,並記錄”猜的次數”及”猜對次數”.”猜的次數”及”猜對次數”這兩個值會隨著玩的次數變動.所以這兩個值是 mutable 的,所以這邊的範例是使用 FP 的寫法來實作這小遊戲,並處理兩個 mutable 的值.
package com.simple.job
import scala.annotation.tailrec
import scala.util.Random
case class GameState(numFlips: Int, numCorrect: Int)
/**
* Created by daniel on 2018/1/16.
*/
object PlayFlipCoin {
def main(args: Array[String]): Unit = {
val r = Random
val s = GameState(0, 0)
mainLoop(s, r)
}
@tailrec
def mainLoop(gameState: GameState, random: Random) {
showPrompt()
val userInput = getUserInput()
// handle the result
userInput match {
case "H" | "T" => {
val coinTossResult = tossCoin(random)
val newNumFlips = gameState.numFlips + 1
if (userInput == coinTossResult) {
val newNumCorrect = gameState.numCorrect + 1
val newGameState = gameState.copy(numFlips = newNumFlips, numCorrect = newNumCorrect)
printGameState(printableFlipResult(coinTossResult), newGameState)
mainLoop(newGameState, random)
} else {
val newGameState = gameState.copy(numFlips = newNumFlips)
printGameState(printableFlipResult(coinTossResult), newGameState)
mainLoop(newGameState, random)
}
}
case _ => {
printGameOver()
printGameState(gameState)
// return out of the recursion here
}
}
}
def showPrompt(): Unit = { print("\n(h)eads, (t)ails, or (q)uit: ") }
def getUserInput(): String = readLine.trim.toUpperCase
def printableFlipResult(flip: String): String = flip match {
case "H" => "Heads"
case "T" => "Tails"
}
def printGameState(printableFlipResult: String, gameState: GameState): Unit = {
print(s"Flip was $printableFlipResult. ")
printGameState(gameState)
}
def printGameState(gameState: GameState): Unit = {
println(s"#Flips: ${gameState.numFlips}, #Correct: ${gameState.numCorrect}")
}
def printGameOver(): Unit = println("\n=== GAME OVER ===")
// returns "H" for heads, "T" for tails
def tossCoin(r: Random): String = {
val i = r.nextInt(2)
i match {
case 0 => "H"
case 1 => "T"
}
}
}
程式執行結果 :
(h)eads, (t)ails, or (q)uit: h
Flip was Heads. #Flips: 1, #Correct: 1
(h)eads, (t)ails, or (q)uit: T
Flip was Heads. #Flips: 2, #Correct: 1
(h)eads, (t)ails, or (q)uit: h
Flip was Heads. #Flips: 3, #Correct: 2
(h)eads, (t)ails, or (q)uit: q
=== GAME OVER ===
#Flips: 3, #Correct: 2
Process finished with exit code 0
需求
scala API or function could perform to print out detail result step by step as well as following request ?
List( 1, 2, 3, 4 ).reduce( (x,y) => x + y )
Step 1 : op( 1, 2 ) will be the first evaluation.
Start with 1, 2, that is
x is 1 and y is 2
Step 2: op( op( 1, 2 ), 3 ) - take the next element 3
Take the next element 3:
x is op(1,2) = 3 and y = 3
Step 3: op( op( op( 1, 2 ), 3 ), 4)
Take the next element 4:
x is op(op(1,2), 3 ) = op( 3,3 ) = 6 and y is 4
FP 版本
import scala.annotation.tailrec
def printOp(list: List[Int]): Int = {
@tailrec
def op(list: List[Int], currentSum: Int): Int = list match {
case x :: Nil => currentSum
case head :: seconde :: tail => {
val tempSum = head + seconde;
println(s"op($head,$seconde)=$tempSum")
op(tempSum :: tail , currentSum + tempSum)
}
}
op(list,0)
}
val list = List.range(1,5)
printOp(list)
Unit 版本
import scala.annotation.tailrec
def printOp(list: List[Int]): Unit = {
@tailrec
def op(list: List[Int], currentSum: Int): Unit = list match {
case x :: Nil => ()
case head :: seconde :: tail => {
val tempSum = head + seconde;
println(s"op($head,$seconde)=$tempSum")
op(tempSum :: tail , currentSum + tempSum)
}
}
op(list,0)
}
val list = List.range(1,10)
printOp(list)
scala> List( 1, 2, 3, 4 ).reduce( (x,y) => { println(s"op($x,$y)"); x+y} )
op(1,2)
op(3,3)
op(6,4)
res3: Int = 10
scala> List( 1, 2, 3, 4 ).reduce { (x,y) =>
val r = x + y
println(s"op($x,$y)=$r")
r
}
op(1,2)=3
op(3,3)=6
op(6,4)=10
res0: Int = 10
總結
- 在使用 for 與 recursion 時,也同時顯現了處理一般 data 與 stream-data 的差異,因為使用 for 會有個界線值比如說 list.size() 但 recursion 時會一直遞迴直到沒有資料了,而 stream-data 會一直傳輸很適合使用 recursion 來處理,java 的 iterable 也類似.