文章目录
- 一、第1关:方法
- [S 三角形 ](#S 三角形)
- 二、第2关:Scala函数以及函数调用
一、第1关:方法
任务描述
本关任务:根据三角形的三边长 a、b、c,返回三角形的面积。
任意三角形面积公式-海伦公式
已知三角形三边a、b、c,则
S
三角形
p(p−a)(p−b)(p−c)
p=
2
1
(a+b+c)
相关知识
为了完成本关任务,你需要掌握:
什么是方法;
方法的使用。
方法
定义函数最常用的方式是作为某个对象的成员。这种函数被称为方法(method)。换句话来说在类中定义的函数即是方法。
定义方法的基本格式是:
def 方法名称 (参数列表):返回值类型 = 方法体
如果你不写等于号和方法主体,那么方法会被隐式声明为抽象(abstract),包含它的类型也将是一个抽象类型。
方法定义由一个 def 关键字开始,接着为该方法的名称,紧接着是可选的参数列表(意味着可写可不写),一个冒号 : 和方法的返回类型,一个等于号 = ,最后是方法的主体。
接着我们来看几个定义方法的示例:
参数列表为空,返回类型为 Unit 的方法:
def f1() : Unit = {
println("f1")
}
返回 Unit 类型的函数,但是没有显式指定返回类型。(当然也可以返回非 Unit 类型的值):
def f2() = {
println("f2")
}
如果参数列表有多个时,可以使用逗号连接,当用 return 进行返回时,方法中必须明确指定返回值类型:
def addInt( a:Int, b:Int ) : Int = {
var sum:Int = 0
sum = a + b
return sum
}
返回值类型有多种可能,此时也可以省略 Unit:
def f3(content: String) = {
if(content.length >= 3)
content + "喵喵喵~"
else
3
递归方法必须明确指定方法返回值类型:
def factorial(n: Int): Int = {
if(n <= 0)
1
else
n * factorial(n - 1)
}
方法的最简形式:
def m = 100
方法的调用
Scala 提供了多种不同的方法调用方式:
直接通过方法名(参数列表)调用:
functionName( 参数列表 )
如果方法使用了实例的对象来调用,我们可以使用类似 Java 的格式 (使用 . 号):
[instance.]functionName( 参数列表 )
这里我们主要介绍第一种:
示例:
object Test {
def main(args: Array[String]) {
println( "Returned Value : " + addInt(5,7) );
}
def addInt( a:Int, b:Int ) : Int = {
var sum:Int = 0
sum = a + b
return sum
}
}
执行结果:
Returned Value : 12
注意:当方法没有参数列表或参数列表为空时,调用该方法时可以省略方法后的括号。
编程要求
仔细阅读右侧编辑区内给出的代码框架及注释,在 Begin-End 间编写程序代码,定义一个 area 方法,该方法具有三个参数,类型都是 Int 型,分别代表三角形的三条边长,返回值类型为 Double;要求根据三角形的三边长,返回三角形的面积。
测试说明
平台将使用测试集运行你编写的程序代码,若全部的运行结果正确,则通关。
例:
测试输入:
3
4
5
预期输出:
三角形的面积为:6.00
实验代码:
python
import scala.io.StdIn
object Triangle {
/********** Begin *********/
def area(a: Int, b: Int, c: Int): Double = {
val s = (a + b + c) / 2.0
math.sqrt(s * (s - a) * (s - b) * (s - c))
}
/********** End *********/
def main(args: Array[String]): Unit = {
val a: Int = StdIn.readInt()
val b: Int = StdIn.readInt()
val c: Int = StdIn.readInt()
printf("三角形的面积为:%.2f", area(a, b, c))
}
}
二、第2关:Scala函数以及函数调用
任务描述
本关任务:计算小明请完 5 顿饭之后还剩多少钱。
相关知识
为了完成本关任务,你需要掌握:
什么是函数;
如何调用函数。
函数定义
Scala 作为支持函数式编程的语言,Scala 函数式编程是 Scala 的重中之重,高级函数也是其独特的一个特性。首先,函数/变量同是一等公民,函数与变量同等地位,函数的定义可以单独定义,可以不依赖于类、接口或者 object,可以独立存在、独立使用,并且可以赋值给变量。
我们上一关所学习的方法就是函数的一种形式。接下来我们来看函数的定义:
(函数的参数列表)=>{函数体内容}
由于该函数需要被别人调用,所以要用一个变量来引用。
val 变量名称 =(函数的参数列表)=>{函数体内容}
示例:
// 将 x 的值加 1
val increase = (x:Int) => x+1
=> 表示该函数将左侧的内容(任何整数 x)转换成右侧的内容(x+1)。
因此,这是一个将任何整数 x 映射成 x+1 的函数。
复杂的定义形式:
val 变量名称:(输入参数类型) => 返回值类型 =(输入参数的引用)=>{函数体内容}
示例:
// 计算 x 和 y 的和
val add: (Int, Int) => Int = (x:Int, y:Int)=> x+y
如果你想要在函数中包含多于 1 条语句,可以将函数体用花括号括起来,每条语句占一行,组成一个代码块(block)。跟方法一样,当函数值被调用时,所有的语句都会被执行,并且该函数的返回值就是对最后一个表达式求值的结果。
参数列表对于方法是可选的,但是对于函数是强制的。
方法可以没有参数列表,参数列表也可以为空。但是函数必须有参数列表(参数列表可以为空)。
而函数可以直接赋值给变量,可以让函数很方便的传递。
函数调用
传值调用和传名调用
Scala 的解释器在解析函数参数(function arguments)时有两种方式:
传值调用(call-by-value):先计算参数表达式的值(reduce the arguments),再应用到函数内部;
传名调用(call-by-name):将未计算的参数表达式直接应用到函数内部。
object Test {
def main(args: Array[String]): Unit = {
callByValue(something())
println("------------------------------")
callByName(something())
}
def something() = {
println("calling something")
}
def callByValue(x: Int) = {
println("x1=" + x)
println("x2=" + x)
}
def callByName(x: => Int) = {
println("x1=" + x)
println("x2=" + x)
}
}
执行结果:
calling something
x1=1
x2=1
calling something
x1=1
calling something
x2=1
从执行结果上来看,我们可以得出:
在进入函数内部前,传值调用方式就已经将参数表达式的值计算完毕,而传名调用是在函数内部进行参数表达式的值计算的。
这就造成了一种现象,每次使用传名调用时,解释器都会计算一次表达式的值。
指定函数参数名调用
一般情况下函数调用参数,就按照函数定义时的参数顺序一个个传递。但是我们也可以通过指定函数参数名,并且不需要按照顺序向函数传递参数。
object Test {
def main(args: Array[String]) {
printInt(7,5); // 默认按参数顺序调用
println("------------------------------")
printInt(b=5, a=7); // 指定函数参数名调用
}
def printInt( a:Int, b:Int ) = {
println("Value of a : " + a );
println("Value of b : " + b );
}
}
执行结果:
Value of a : 7
Value of b : 5
Value of a : 7
Value of b : 5
注意:当参数列表为空时,直接写方法名可以调用方法,而函数名只是代表函数对象本身。因为保存函数字面量的变量(又称为函数名或者函数值)本身就是实现了 FunctionN 特质的类的对象,要调用对象的 apply 方法,就需要使用obj()的语法。所以函数名后面加括号才是调用函数。如下:
如果我们想要直接操纵方法的话,可以使用下划线(_)将方法转换为函数。
object TestMap {
def ttt(f:Int => Int):Unit = {
val r = f(10)
println®
}
val f0 = (x : Int) => x * x
//定义了一个方法
def m0(x:Int) : Int = {
//传递进来的参数乘以10
x * 10
}
//将方法转换成函数,利用了神奇的下滑线
val f1 = m0 _
def main(args: Array[String]): Unit = {
ttt(f0)
//通过m0 _将方法转化成函数
ttt(m0 _)
// 如果直接传递的是方法名称,scala相当于是把方法转成了函数
ttt(m0)
}
}
编程要求
仔细阅读右侧编辑区内给出的代码框架及注释,在 Begin-End 间编写程序代码,计算小明请完 5 顿饭之后还剩多少钱。具体要求如下:
变量 money 定义了小明的钱数,初始值为 1000 元;
eat 方法中定义了每请一顿饭消耗多少元;
balance 方法实现计算小明还剩多少元的功能;
定义一个 printMoneyByValue 方法,该方法传名调用 balance 函数,类型为 Int 的参数,打印连续五次调用所给参数后,小明还剩多少元;
定义一个 printMoneyByName 方法,该方法传值调用 balance 函数,类型为 Int 的参数,打印连续五次调用所给参数后,小明还剩多少元;
输出格式:"你的余额现在为:xx",xx 为剩余钱数。
测试说明
平台将使用测试集运行你编写的程序代码,若全部的运行结果正确,则通关。
例:
测试输入:无
预期输出:
你的余额现在为:950
你的余额现在为:650
实验代码:
python
object Test {
var money = 1000
// 吃一顿消费50
def eat(): Unit = {
money = money - 50;
}
// 余额
def balance(): Int = {
eat()
money
}
/********** Begin *********/
def printMoneyByValue(balanceFunc: => Int): Unit = {
val currentMoney = balanceFunc
println(s"你的余额现在为:$currentMoney")
}
def printMoneyByName(mealCost: Int): Unit = {
for (_ <- 1 to 5)
{
eat()
}
val remainingMoney = money
println(s"你的余额现在为:$remainingMoney")
}
/********** End *********/
def main(args: Array[String]): Unit = {
printMoneyByValue(balance())
println("-------------------")
printMoneyByName(balance())
}
}