今天的主題是 Arithmetic operators,就是讓我們來做一些數學運算吧!實作上是件很簡單的事,我們就只是把幾個變數拿來加減乘除並且印出結果,你一定想說阿不就那樣,但是如果你有 Follow 我先前的 day 0 跟 day 1,嘿嘿!你就會知道你會得到的絕對比你想像中的多啦!在這一篇,我們會深入地探討到底當你在這些語言寫出加減乘除的時候究竟背後是發生什麼事呢?讓我們看下去!
相當容易,就是一些加減乘除,把主餐的價格加上一些是主餐比例的小費和稅囉!(淺入深出?)
import scala.io.StdIn._
object Solution {
def main(args: Array[String]) = {
val mealCost = readDouble
val tipPercent = readInt
val taxPercent = readInt
val bill = mealCost * (1 + (tipPercent + taxPercent) / 100.toDouble)
val totalCost = math.round(bill).toInt
println(s"The total cost is $totalCost.")
}
}
100.toDouble
。首先先來說為什麼要 toDouble
,因為不這麼做, (tipPercent + taxPercent) / 100
會等於 0
(如果tipPercent + taxPercent
小於 100
,那麼因為整數相除最後在 Scala 只會留下整數的部分,如果沒有特別指定型態的話。那為什麼 100
會有 toDouble
這個方法可以用呢?因為在 Scala,任何東西都是 Object,包含 100
也是。 100
是 Int
這個 Class 的 instance,我們可以在這裡 找到 toDouble
這個 method。而 Scala 又提供了語法糖讓我們可以不用寫 toDouble()
而可以直接寫 toDouble
,只要 method 本身沒有任何參數。Int
本身還有很多 method,像是 +
, -
, *
, /
等等,所以當我們如果想要把 a
和 b
兩個整數相加,應該要寫成 a.+(b)
,但是這樣寫又跟平常我們人類的寫法好像有點出入,這時候就又再搭配 Scala 的一個語法糖:只要 method 只吃一個參數,那麼就可以用 Infix notation,也就是寫成 a + b
囉!最後我們看到在官方文件中,光 +
的定義就有好多,不同的是,參數的類型不同,這就是所謂的 method overloading 了。math
是 Scala 內置的library,裡面提供了許多跟數學運算相關的 method,像是本例子中的 round
就是方便我們進行四捨五入囉!其他還有很多可以參考這裡
s"..."
,這個叫做 String interpolation,能夠讓我們去 Create string。在這裡 $totalCost
就是去存取 totalCost
的值,所以假使 totalCost
是 100
,那麼這個 String 就會是 The total cost is 100
。更厲害的是,我們還可以直接在裡頭做運算,像是 s"1+1=${1+1}"
,只要用大括號括起來的地方,就可以放入表達式,最後表達式回傳的結果就會變成字串的一部分,而這個例子最後會得到 1+1=2
的字串。除了 s
之外,還有一些有趣的,像是 f
可以讓我們格式化字串,甚至你也可以自定義唷!有點複雜,有興趣的可以參考這裡囉!def solve(meal_cost, tip_percent, tax_percent):
tip = (meal_cost * tip_percent) / 100
tax = (meal_cost * tax_percent) / 100
totalCost = int(round(meal_cost + tip + tax))
print(totalCost)
meal_cost = float(input())
tip_percent = int(input())
tax_percent = int(input())
solve(meal_cost, tip_percent, tax_percent)
input()
並且 Cast 成相對應的 Type 後,丟到我們自定義的 Function solve
。在 Python 裡面,定義 Function 不像其他語言通常會有大括號。Python 是用縮排來代替大括號去闡述 Code block 的存在。round
是內建的函數,可以幫助我們對參數取四捨五入。__xxx__
,前後都有雙底線的,通常都是默默幫我們把一些事情給做掉的 Special methods,才造就 Python 如此簡潔啦!那如果假設我們今天自定義了一個 Class,我們也想要讓這個 Class 產生的兩個 instance (例如 a
和 b
),可以這樣操作 a + b
呢?那我們就要在 Class 裡頭去定義 __add__
這個 Function 啦!這樣我們就能夠自己決定,對於這個 Class 來說,所謂相加是做什麼事情。當然還有很多很多,例如 __sub__
(相減 -
), __ge__
(大於 >
)。有興趣可以參考這個列表 ,超多 magic 的啦!package main
import (
"fmt"
"os"
"bufio"
"strconv"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
meal_cost, _ := strconv.ParseFloat(scanner.Text(), 64)
scanner.Scan()
tip_percent, _ := strconv.ParseInt(scanner.Text(), 10, 64)
scanner.Scan()
tax_percent, _ := strconv.ParseInt(scanner.Text(), 10, 64)
var total_cost float64 = meal_cost + meal_cost * float64(tip_percent + tax_percent) / 100
fmt.Printf("The total cost is %.1f dollars.", total_cost)
}
Scanner
, strconv
等等的內容,所以如果你還不清楚這些用法的話,可以回 Day 1 的文章,裡頭有詳細的說明。這裡要注意的是 meal_cost * float64(tip_percent + tax_percent)
,如果我們沒有用 float64(..)
做 casting 的話,Compiler 會不爽,因為這兩個 Type 的值是不能直接相加的 (int64
跟 float64
)。Struct
(Golang 沒有 Class),要把兩個 Struct 的 instance "相加",就得自定義 Method 例如 Add
來處理,而不能直接用 +
了。use std::io::stdin;
fn solve(meal_cost: f64, tip_percent: i32, tax_percent: i32) -> f64 {
let result = meal_cost * ( 1.0 + ( tip_percent as f64 / 100.0 ) + ( tax_percent as f64 / 100.0 ));
result
}
fn main() {
let mut meal_cost_input = String::new();
let mut tip_percent_input = String::new();
let mut tax_percent_input = String::new();
stdin().read_line(&mut meal_cost_input)
.expect("Failed to read your input");
let meal_cost_input: f64 = match meal_cost_input.trim().parse(){
Ok(num) => num,
Err(_) => 0.0
};
stdin().read_line(&mut tip_percent_input)
.expect("Failed to read your input");
let tip_percent_input: i32 = match tip_percent_input.trim().parse(){
Ok(num) => num,
Err(_) => 0
};
stdin().read_line(&mut tax_percent_input)
.expect("Failed to read your input");
let tax_percent_input: i32 = match tax_percent_input.trim().parse(){
Ok(num) => num,
Err(_) => 0
};
let total_cost: f64 = solve(meal_cost_input, tip_percent_input, tax_percent_input);
println!("{}", total_cost);
}
-> f64
,這是在定義 Function 最後計算所得到的值 Type 是 f64
,而這個 Function 的結果就是最後一行的 result
。我們在這裡沒有看到 return
這個關鍵字,因為 Rust 是 Expression-based language,每個表達式在計算完後都會產生一個值,而在 Function 內通常就是把最後一行當作結果,而且不需要加上分號。當然如果需要 Early return 的時候,例如遇到錯誤就先 Return,還是可以用上 return 這個關鍵字,但是其實在 Rust 的世界,幾乎是很少看到 Return 的。可以參考 Wiki 對於 Expression based language 的說明 (Scala 也是唷)tip_percent as f64
,這是 Rust 的 Explicit conversion,就是 Casting 的效果啦!(這裡是把 i32
cast 成 f64
)+
,我們可以讓自定義的 Struct 也能夠去使用 +
,至於如何定義我們自定義的 Struct 的 +
的行為呢?以 "加" 來說,我們自定義的 Struct 必須要去實現 std::ops::Add
這個 Trait。什麼是 Trait?可以想像是 interface,定義了很多不含實作的方法。而我們自定義的 Struct 就要去 Implement Add
這個 Trait ,包含裡頭的 add
方法,這樣我們就可以使用 +
來 “相加” 我們自定義的 Struct 了,細節可以參考這裡,裡頭也定義了各式各樣可以拿來當作 Overloading 的 Trait 唷!今天主要是透過 Operator 的單元,探討了一下這些 Operator 在各語言被使用的背後發生了哪些事情,還有 Operator overloading 的部分。那麼我們明天見囉!