Scala进阶之路-Scala的基本语法

                               作者:尹正杰

版权声明:原创作品,谢绝转载!否则将追究法律责任。

一.函数式编程初体验Spark-Shell之WordCount

var arr=Array("hello","yinzhengjie","hello","world","yinzhengjie","big","data")            //声明一个数组

arr.map((_,)).groupBy(_._1).mapValues(_.length).toList.sortBy(-_._2)                  //使用Spark进行单词个数统计并进行降序

  使用CMD窗口操作如下:

 

二.变量定义以及条件表达式

1>.数据类型介绍

  答:Scala 和Java 相似,有7 种数值类型Byte、Char、Short、Int、Long、Float 和Double(无包装类型)和Boolean、Unit 类型.注意:Unit 表示无值,和其他语言中void 等同。用作不返回任何结果的方法的结果类型。Unit只有一个实例值,写成()。下图展示了Scala中的整体类层级图,其中Any位于最顶端,Nothing位于最底端。 

2>.变量定义

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Scala%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.basicGrammar object DefiningVariable {
def main(args: Array[String]): Unit = {
/**
* 变量的定义可以用关键字var和val修饰
* var修饰的变量值可以更改
* val修饰的变量值不可用改变,相当于Java中final修饰的变量
* 定义变量格式如下 :
* 方式一 : var | val 变量名称 : 类型 = 值
* 方式二 : var | val 变量名称 = 值
*
*/
val name:String = "尹正杰"
var age:Int = 26
val blog = "http://www.cnblogs.com/yinzhengjie/tag/Scala%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/"
// 输出我们上面定义的变量
println ("姓名 :" + name , "年龄 :" + age , "博客地址 :" + blog ) /**
* Unit数据类型相当于Java中void关键字,但是在scala它的表示形式是一对括号,即"()"。
* 由于我println返回值为空,因此我定义了一个变量res它拿到的返回值必然为空。
*/
val res:Unit=println("yinzhengjie")
println(res)
/**
* 文字'f'插值器允许创建一个格式化的字符串,类似于C语言中的printf。注意,如果你没有写文字'f'插值器的话,格式化字符串会原样输出哟
* 在使用'f'插值器时,所有变量引用都应该是printf 样式格式说明符,如%d,%i,%f 等。
*/
println (f"姓名 :$name%s 年龄 :$age, 博客地址 :$blog ") // 该行输出有换行
/**
* 's'允许在处理字符串时直接使用变量。
* 在println 语句中将String 变量($name)附加到普通字符串中。
*/
println (s"Name=$name , Age=$age , Url=$blog ")
/**
* 字符串插入器还可以处理任意表达式。
* 使用's'字符串插入器处理具有任意表达式"${10 * 10}"的字符串"10 x 10"的以下代码片段。任何表达式都可以嵌入到${}中。
*/
println (s"10 x 10 = ${10 * 10}") /**
* 扩展小知识一:
* 多个变量声明模式
*/
val (x,y,z) = (100,200,300)
println(s"x = ${x},y = ${y},z = ${z}") /**
* 扩展小知识二
* 抽取前两个元素依次赋值,目的只是关心a,b两个值,这样我们就可以直接输出a和b的值
*/
val Array(a,b,_*) = Array("A","B","C","D")
println(s"a = ${a},b = ${b}") }
} /*
以上代码输出结果如下 :
(姓名 :尹正杰,年龄 :26,博客地址 :http://www.cnblogs.com/yinzhengjie/tag/Scala%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/)
yinzhengjie
()
姓名 :尹正杰 年龄 :26, 博客地址 :http://www.cnblogs.com/yinzhengjie/tag/Scala%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
Name=尹正杰 , Age=26 , Url=http://www.cnblogs.com/yinzhengjie/tag/Scala%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
10 x 10 = 100
x = 100,y = 200,z = 300
a = A,b = B
*/

3>.条件表达式

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Scala%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.variable object ConditionalExpression {
def main(args: Array[String]): Unit = {
/**
* if 语句的使用
*/
val Name = "尹正杰"
if (Name == "尹正杰"){
println("欢迎使用Scala!")
}
/**
* if...else 语句的使用
*/
val sex = "boy"
val res = if (sex == "boy") "小哥哥" else "小姐姐" //这个和Python中的三元表达式很像哟!
println(res) /**
* if...else if ...else多分支语句
*/
val age:Int = 18
var Title = if (age > 38){ //注意:我们在定义Title变量是并没有指定数据类型,编译器会自动推测出返回值类型,如果上面都没有返回默认就是Unit哟!
"大叔"
}else if (age > 20){
"小哥哥"
}else{
"小鲜肉"
}
println(s"${Title}")
}
} /*
以上代码输出结果如下 :
欢迎使用Scala!
小哥哥
小鲜肉
*/

三.循环语句for及yield关键字

1>.遍历数组的几种方式

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Scala%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.variable object CircularStatement {
def main(args: Array[String]): Unit = {
//定义一个数组
val arr = Array(1,2,3,4,5)
//遍历数组的中的所有元素
for (item <- arr){
print(item + " ")
}
println("\n=======我是分割线==========")
//定义一个数组,用面的每一个元素代表arr数组中的角标,从而达到访问arr每一个元素的目的
val index = Array[Int](0,1,2,3,4)
for (item <- index){
print(arr(item) + "|")
}
println("\n=======我是分割线==========")
//以角标的方式会访问,注意“0 to 4”,会生成一个“(0,1,2,3,4)”的数组
for (item <- 0 to 4){
print(arr(item) + " ")
}
println("\n=======我是分割线==========")
//以角标的方式会访问,注意“0 until arr.length”,也会生成一个“(0,1,2,3,4)”的数组,因为arr.length的值为5
for (item <- 0 until arr.length){
print(arr(item) + "|")
}
println("\n=======我是分割线==========")
//取出数组中的偶数元素
for (item <- arr){
if (item % 2 == 0){
print(item + " ")
}
}
println("\n=======我是分割线==========")
//当然,上面的循环表达式也可以简写,如下:
for (item <- arr if item % 2 == 0){
print(item + " ")
}
}
} /*
以上代码执行结果如下 :
1 2 3 4 5
=======我是分割线==========
1|2|3|4|5|
=======我是分割线==========
1 2 3 4 5
=======我是分割线==========
1|2|3|4|5|
=======我是分割线==========
2 4
=======我是分割线==========
2 4
*/

2>.循环的嵌套方式

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Scala%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.variable object CircularStatement {
def main(args: Array[String]): Unit = {
for (i <-1 to 3){
for (j <- 1 to 3){
if (i != j){
print(10 * i + j + " ")
}
}
}
println("\n=======我是分割线==========")
//上面的for循环嵌套是很繁琐的,我们可以用一行搞定!
for(i <- 1 to 3;j <-1 to 3 if i != j){
print(10 * i + j + " ")
}
}
} /*
以上代码执行结果如下 :
12 13 21 23 31 32
=======我是分割线==========
12 13 21 23 31 32
*/

3>.yield关键字

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Scala%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.variable object CircularStatement {
def main(args: Array[String]): Unit = { val arr = Array(1,2,3,4,5) //yield关键字和Python很类似,它可以多次返回值,比如我们判断arr数组中存在的偶数值
var res = for(item <- arr if item % 2 == 0) yield item //遍历我们得到的数组
for(item <- 0 until res.length){
print(res(item) + " ")
} }
} /*
以上代码执行结果如下 :
2 4
*/

 4>.小试牛刀-打印九九乘法表

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Scala%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.basicGrammar object MultiplicationTable {
def main(args: Array[String]): Unit = {
for_while_99(9)
println("=======我是分割线==========")
while_while_99(10)
println("=======我是分割线==========")
for_for_99(9)
println("=======我是分割线==========")
senior_for(9)
} /**
* 使用高级for循环打印99乘法表,当然是在传递的参数为9的情况下
*/
def senior_for(arg:Int):Unit={
for(i<-1 to arg;j<-1 to i ){
print(s"${i} x ${j} = ${i * j}\t")
if (j == i) {
println()
}
}
} /**
* 使用for循环嵌套打印99乘法表,我们需要传入一个参数,可以实现任意乘法表
*/
def for_for_99(arg:Int):Unit={
for(i <- 1 to arg){
for (j <- 1 to i){
print(s"${i} x ${j} = ${i * j}\t")
if (j == i) {
println()
}
}
}
}
/**
* 使用while循环嵌套打印99乘法表,我们需要传入一个参数,可以实现任意乘法表
*/
def while_while_99(arg:Int): Unit ={
var i = 1
while (i < arg){
var j = 1
while (j <= i){
printf("%d x %d = %d\t",j,i,(j*i))
if (j == i){
println()
}
j+=1
}
i+=1
}
}
/**
* 使用while和for循环打印99乘法表,我们需要传入一个参数,可以实现任意乘法表
*/
def for_while_99(arg:Int):Unit = {
var i: Int = 1
for (i <- 1 to arg) {
var j = 1
while (j <= i) {
print(s"${i} x ${j} = ${i * j}\t")
if (j == i) {
println()
}
j += 1
}
}
}
} /*
以上代码执行结果如下 :
1 x 1 = 1
2 x 1 = 2 2 x 2 = 4
3 x 1 = 3 3 x 2 = 6 3 x 3 = 9
4 x 1 = 4 4 x 2 = 8 4 x 3 = 12 4 x 4 = 16
5 x 1 = 5 5 x 2 = 10 5 x 3 = 15 5 x 4 = 20 5 x 5 = 25
6 x 1 = 6 6 x 2 = 12 6 x 3 = 18 6 x 4 = 24 6 x 5 = 30 6 x 6 = 36
7 x 1 = 7 7 x 2 = 14 7 x 3 = 21 7 x 4 = 28 7 x 5 = 35 7 x 6 = 42 7 x 7 = 49
8 x 1 = 8 8 x 2 = 16 8 x 3 = 24 8 x 4 = 32 8 x 5 = 40 8 x 6 = 48 8 x 7 = 56 8 x 8 = 64
9 x 1 = 9 9 x 2 = 18 9 x 3 = 27 9 x 4 = 36 9 x 5 = 45 9 x 6 = 54 9 x 7 = 63 9 x 8 = 72 9 x 9 = 81
=======我是分割线==========
1 x 1 = 1
1 x 2 = 2 2 x 2 = 4
1 x 3 = 3 2 x 3 = 6 3 x 3 = 9
1 x 4 = 4 2 x 4 = 8 3 x 4 = 12 4 x 4 = 16
1 x 5 = 5 2 x 5 = 10 3 x 5 = 15 4 x 5 = 20 5 x 5 = 25
1 x 6 = 6 2 x 6 = 12 3 x 6 = 18 4 x 6 = 24 5 x 6 = 30 6 x 6 = 36
1 x 7 = 7 2 x 7 = 14 3 x 7 = 21 4 x 7 = 28 5 x 7 = 35 6 x 7 = 42 7 x 7 = 49
1 x 8 = 8 2 x 8 = 16 3 x 8 = 24 4 x 8 = 32 5 x 8 = 40 6 x 8 = 48 7 x 8 = 56 8 x 8 = 64
1 x 9 = 9 2 x 9 = 18 3 x 9 = 27 4 x 9 = 36 5 x 9 = 45 6 x 9 = 54 7 x 9 = 63 8 x 9 = 72 9 x 9 = 81
=======我是分割线==========
1 x 1 = 1
2 x 1 = 2 2 x 2 = 4
3 x 1 = 3 3 x 2 = 6 3 x 3 = 9
4 x 1 = 4 4 x 2 = 8 4 x 3 = 12 4 x 4 = 16
5 x 1 = 5 5 x 2 = 10 5 x 3 = 15 5 x 4 = 20 5 x 5 = 25
6 x 1 = 6 6 x 2 = 12 6 x 3 = 18 6 x 4 = 24 6 x 5 = 30 6 x 6 = 36
7 x 1 = 7 7 x 2 = 14 7 x 3 = 21 7 x 4 = 28 7 x 5 = 35 7 x 6 = 42 7 x 7 = 49
8 x 1 = 8 8 x 2 = 16 8 x 3 = 24 8 x 4 = 32 8 x 5 = 40 8 x 6 = 48 8 x 7 = 56 8 x 8 = 64
9 x 1 = 9 9 x 2 = 18 9 x 3 = 27 9 x 4 = 36 9 x 5 = 45 9 x 6 = 54 9 x 7 = 63 9 x 8 = 72 9 x 9 = 81
=======我是分割线==========
1 x 1 = 1
2 x 1 = 2 2 x 2 = 4
3 x 1 = 3 3 x 2 = 6 3 x 3 = 9
4 x 1 = 4 4 x 2 = 8 4 x 3 = 12 4 x 4 = 16
5 x 1 = 5 5 x 2 = 10 5 x 3 = 15 5 x 4 = 20 5 x 5 = 25
6 x 1 = 6 6 x 2 = 12 6 x 3 = 18 6 x 4 = 24 6 x 5 = 30 6 x 6 = 36
7 x 1 = 7 7 x 2 = 14 7 x 3 = 21 7 x 4 = 28 7 x 5 = 35 7 x 6 = 42 7 x 7 = 49
8 x 1 = 8 8 x 2 = 16 8 x 3 = 24 8 x 4 = 32 8 x 5 = 40 8 x 6 = 48 8 x 7 = 56 8 x 8 = 64
9 x 1 = 9 9 x 2 = 18 9 x 3 = 27 9 x 4 = 36 9 x 5 = 45 9 x 6 = 54 9 x 7 = 63 9 x 8 = 72 9 x 9 = 81
*/

四.运算符重载成方法

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Scala%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.variable object OperatorReloadingMethod {
def main(args: Array[String]): Unit = {
var res1 = 1 + 2
println(res1)
//上面的运算符“+”其实是运算符重载成方法,即".+"
var res2 = 1.+(2)
println(res2) val res3 = 1 to 10
println(res3)
//上面的运算符“to”其实也是运算符重载成方法,即".to"
val res4 = 1.to(10)
println(res4) }
} /*
以上代码输出结果如下 :
3
3
Range 1 to 10
Range 1 to 10
*/

五.Scala中定义方法和函数简介

1>.有参函数和无参函数以及方法转换成函数案例

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Scala%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.function object Method { /**
* 定义有参函数 :
* 定义个 sum 方法(用关键字def来标识方法), 该方法有 3 个参数(即a,b和c), 参数类型为整型, 方法的返回值为整型,
*/
def sum(a:Int, b: Int,c:Int): Int = {
//方法体是将形参列表的三个参数进行相加操作,相加的结果就是返回值的整形
a + b + c
} /**
* 定义无参函数 :
* 改方法没有任何参数, 也没有返回值。注意:如果方法没有括号"()" 调用时不能加"()"
*/
def sayHello() ={
println("I'm Yinzhengjie!")
} def main(args: Array[String]): Unit = {
//调用有参函数
var res = sum(100,200,300)
println(res) //调用无参函数,调用时可以省略括号"()", 也可以不省略。如果方法没有括号"()",调用时不能加"()"
sayHello() //方法可转换为函数,格式很简单,只需要在方法名后面加个空格和下划线即可。
var f1 = sum _
//调用我们将方法转换过来的函数
var res1 = f1(1,2,3)
println(res1)
}
} /**
* 以上代码执行结果如下 :
* 600
* I'm Yinzhengjie!
* 6
*/

2>.匿名函数的两种定义和调用方式

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Scala%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.function object Method { /**
* 函数定义方式一:
* f1 :
* 其中f1是对匿名函数签名的一个引用,我们可以通过f1去调用这个函数,
* (x : Int) :
* 其中x是变量的名称,而Int是变量的类型
* => :
* 表示的是函数标志
* x * 2 :
* 表示的具体的函数体,即也是最终的返回值哟
*/
val f1 = (x:Int) => x * 2 /**
* 函数定义方式而:
* f2 :
* 其中f2是对匿名函数签名的一个引用,我们可以通过f2去调用这个函数,
* (Int) :
* Int定义的是函数的参数类型,我这个定义了一个Int类型的参数,如果有多个用逗号分隔即可
* => :
* 表示的是函数标志
* Int :
* 表示的是返回值类型为Int
* (x) :
* 注意,这里的x实际上是形参,这个参数的类型就是前面我们定义的Int类型
* x * 2 :
* 表示的具体的函数体,即也是最终的返回值哟
*/
val f2 :(Int) => Int =(x) => x * 2 /**
* 下面为没有任何参数的匿名函数, 函数的返回值为String类型.
*
*/
val f3:() => String = () => "尹正杰" def main(args: Array[String]): Unit = {
//调用匿名函数f1
var res1 = f1(10)
println(res1) //调用匿名函数f2
var res2 = f1(20)
println(res2) //调用参数为空的匿名函数f3
val Name = f3();
println(Name) }
} /**
* 以上代码执行结果如下 :
* 20
* 40
* 尹正杰
*/

  想要了解更多关于函数知识点笔记请参考:https://www.cnblogs.com/yinzhengjie/p/9352798.html。

Scala进阶之路-Scala的基本语法的更多相关文章

  1. Scala进阶之路-Scala高级语法之隐式(implicit)详解

    Scala进阶之路-Scala高级语法之隐式(implicit)详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 我们调用别人的框架,发现少了一些方法,需要添加,但是让别人为你一 ...

  2. Scala进阶之路-Scala中的高级类型

    Scala进阶之路-Scala中的高级类型 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.类型(Type)与类(Class)的区别 在Java里,一直到jdk1.5之前,我们说 ...

  3. Scala进阶之路-Scala中的Ordered--Ordering

    Scala进阶之路-Scala中的Ordered--Ordering 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任.   说道对象的比较,在Java中大家最熟悉不过的就是实现类本身实 ...

  4. Scala进阶之路-Scala中的泛型介绍

    Scala进阶之路-Scala中的泛型介绍 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 通俗的讲,比如需要定义一个函数,函数的参数可以接受任意类型.我们不可能一一列举所有的参数类 ...

  5. Scala进阶之路-Scala特征类与unapply反向抽取

    Scala进阶之路-Scala特征类与unapply反向抽取 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Scala特征类分析 1>.Unit 答:用于定义返回值类型, ...

  6. Scala进阶之路-Scala中的枚举用法案例展示

    Scala进阶之路-Scala中的枚举用法案例展示 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. Scala中的枚举值和Java中的枚举值有点差别,不过使用起来也都差大同小异,我这 ...

  7. Scala进阶之路-Scala函数篇详解

    Scala进阶之路-Scala函数篇详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.传值调用和传名调用 /* @author :yinzhengjie Blog:http: ...

  8. Scala进阶之路-高级数据类型之集合的使用

    Scala进阶之路-高级数据类型之集合的使用 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. Scala 的集合有三大类:序列 Seq.集 Set.映射 Map,所有的集合都扩展自 ...

  9. Scala进阶之路-反射(reflect)技术详解

    Scala进阶之路-反射(reflect)技术详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. Scala中的反射技术和Java反射用法类似,我这里就不一一介绍反射是啥了,如果对 ...

随机推荐

  1. WinDbg命令三部曲

    WinDbg 命令三部曲:(一)WinDbg 命令手册 WinDbg 命令三部曲:(二)WinDbg SOS 扩展命令手册 WinDbg 命令三部曲:(三)WinDbg SOSEX 扩展命令手册

  2. SQL Server 全文搜索

    SQL Server 的全文搜索(Full-Text Search)是基于分词的文本检索功能,依赖于全文索引.全文索引不同于传统的平衡树(B-Tree)索引和列存储索引,它是由数据表构成的,称作倒转索 ...

  3. 【ORACLE】oracle打补丁

    -- 备份旧的opatch cd $ORACLE_HOME/ mv OPatch  OPatch_20180323_old -- 上传补丁工具和补丁包到oraclehome目录下,解压 unzip p ...

  4. NodeMCU学习(一) : 开始之前的准备

    安装Aduino开发环境 在官网中下载Arduino开发环境,或者在网盘中下载: 网盘地址: https://pan.baidu.com/s/1OjMhYgKOYW69YC2dEwFgyw: 提取码: ...

  5. pandas 初识(四)

    Pandas 和 sqlalchemy 配合实现分页查询 Mysql 并获取总条数 @api.route('/show', methods=["POST"]) def api_sh ...

  6. 【独家】K8S漏洞报告|近期bug fix解读&1.11主要bug fix汇总

    内容提要: 1. 高危漏洞CVE-2018-1002105深度解读 2. 11/19--12/11 bug fix汇总分析 3. 1.11重要bug fix解读 4. 1.9重要bug fix解读 在 ...

  7. 20135202闫佳歆--week5 分析system_call中断处理过程--实验及总结

    week 5 实验:分析system_call中断处理过程 一.使用gdb跟踪分析一个系统调用内核函数(上周选择那一个系统调用)--getpid 复习视频: 如何实现? - 更新menu代码到最新版 ...

  8. 《Linux内核分析》 之 计算机是如何工作的

    [李行之原创作品 转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000] <Linux内 ...

  9. Java单元测试框架 JUnit

    Java单元测试框架 JUnit JUnit是一个Java语言的单元测试框架.它由Kent Beck和Erich Gamma建立,逐渐成为源于KentBeck的sUnit的xUnit家族中为最成功的一 ...

  10. 四则运算APP最后阶段

    四则运算APP最后阶段 [开发环境]:eclipse [开发项目]:小学生四则运算APP [开发人员]:郑胜斌 http://www.cnblogs.com/zsb1/ 孔德颖 http://www. ...