add scala
parent
e579248efa
commit
445dbbe270
|
@ -0,0 +1,30 @@
|
|||
## 1.java中传递变长参数
|
||||
在java中传递变长参数的方式为`...`,看个简单的实例
|
||||
|
||||
```
|
||||
public static void argsTest(String... args) {
|
||||
for(String each: args) {
|
||||
System.out.println(each);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
String s1 = "a", s2 = "b", s3 = "c";
|
||||
argsTest(s1, s2, s3);
|
||||
}
|
||||
```
|
||||
|
||||
## 2.scala中传递变长参数
|
||||
scala中也支持传递变长参数,而且比java中更简单,只需要在参数类型后面使用特殊符号"*"即可。同样看一个简单的例子
|
||||
|
||||
```
|
||||
def findMax(values: Int*) = {
|
||||
values.foldLeft(values(0)) {
|
||||
(x, y) => Math.max(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
def main(args: Array[String]): Unit = {
|
||||
println(findMax(1, 3, 5, 2, 4, 6))
|
||||
}
|
||||
```
|
|
@ -0,0 +1,134 @@
|
|||
项目中经常有发送http请求的需求,现在将java,python,scala中发送http请求的方法稍作记录,以备不时之需。
|
||||
|
||||
## 1.java版本
|
||||
java代码相对来说最为冗长。。。这也是java的一贯风格。。。
|
||||
|
||||
```
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
|
||||
public class HttpUtils {
|
||||
|
||||
protected static Logger LOGGER = LoggerFactory.getLogger(HttpUtils.class);
|
||||
|
||||
public static HttpClient httpClient = new DefaultHttpClient();
|
||||
|
||||
public static String getResponse(String url, int retryTimes) {
|
||||
for(int i = 0; i < retryTimes; i++) {
|
||||
LOGGER.info("get response with the request: {}", url);
|
||||
try {
|
||||
HttpResponse response = httpClient.execute(new HttpGet(url));
|
||||
HttpEntity entity = response.getEntity();
|
||||
if(response.getStatusLine().getStatusCode() == 200) {
|
||||
String res = genResponseContext(entity.getContent());
|
||||
if(StringUtils.isNotBlank(res)) return res;
|
||||
}
|
||||
|
||||
} catch (Exception ex) {
|
||||
System.out.println("get response with error");
|
||||
ex.printStackTrace();
|
||||
LOGGER.error("get response with error: \n", ex);
|
||||
try {
|
||||
Thread.sleep(1000 * (i + 1));
|
||||
} catch (InterruptedException interEx) {
|
||||
interEx.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
|
||||
public static String genResponseContext(InputStream is) {
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
reader = new BufferedReader(new InputStreamReader(is));
|
||||
String line;
|
||||
while( (line = reader.readLine()) != null) {
|
||||
sb.append(line).append("\n");
|
||||
}
|
||||
return sb.toString().trim();
|
||||
} catch (Exception ex) {
|
||||
LOGGER.error("get response error: {} \n", ex);
|
||||
return null;
|
||||
} finally {
|
||||
if (reader != null) {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
String url = "http://www.baidu.com";
|
||||
String result = getResponse(url, 2);
|
||||
System.out.println(result);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## 2.python版本
|
||||
python代码相对来说就简单很多了。。。
|
||||
|
||||
```
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8
|
||||
|
||||
import urllib2
|
||||
|
||||
|
||||
def send_http_request(url):
|
||||
request = urllib2.Request(url)
|
||||
response = urllib2.urlopen(request)
|
||||
data = response.read()
|
||||
print data
|
||||
|
||||
|
||||
send_http_request("http://www.baidu.com")
|
||||
```
|
||||
|
||||
## 3.scala版本
|
||||
scala也是JVM系,所以跟java版本有一点像,但简洁很多。
|
||||
|
||||
```
|
||||
import org.apache.http.client.methods.HttpGet
|
||||
import org.apache.http.impl.client.DefaultHttpClient
|
||||
|
||||
import scala.io.Source
|
||||
|
||||
object HttpUtils {
|
||||
|
||||
def send_http_request(url: String): Unit = {
|
||||
val httpclient = new DefaultHttpClient()
|
||||
try {
|
||||
val response = httpclient.execute(new HttpGet(url))
|
||||
val entity = response.getEntity
|
||||
val result = Source.fromInputStream(entity.getContent).getLines().mkString("\n")
|
||||
println(result)
|
||||
} catch {
|
||||
case ex: Exception => ex.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
def main(args: Array[String]): Unit = {
|
||||
val url = "http://www.baidu.com"
|
||||
send_http_request(url)
|
||||
}
|
||||
}
|
||||
|
||||
```
|
|
@ -0,0 +1,158 @@
|
|||
## 1.object
|
||||
object的特点是:
|
||||
1.可以拥有属性和方法,且默认都是"static"类型,可以直接用object名直接调用属性和方法,不需要通过new出来的对象(也不支持)。
|
||||
2.object里的main函数式应用程序的入口。
|
||||
3.object和class有很多和class相同的地方,可以extends父类或Trait,但object不可以extends object,即object无法作为父类。
|
||||
|
||||
## 2.class
|
||||
一个主构造器(函数),其他是辅助构造器
|
||||
辅助构造器的实现体里,必须引用(调用)主构造器
|
||||
主构造器的参数,也会成为类的属性
|
||||
辅助构造函数的名称都是this
|
||||
辅助构造函数中必须以一个其他辅助构造器或主构造器的调用开始。
|
||||
构造函数
|
||||
|
||||
生成的字段方法
|
||||
name:String 对象私有字段。如果没有方法使用name,则没有该字段。
|
||||
Private val/var name String 私有字段,私有getter/setter方法。
|
||||
Val/var name:String 私有字段,公有getter/setter方法
|
||||
|
||||
scala类中是没有static方法的,那么如何实现某个类既有普通方法又有静态方法?伴生对象就可以满足这个要求,伴生类和伴生对象可以相互访问彼此的私有成员。
|
||||
|
||||
```
|
||||
class TestClass(val id: Int) {
|
||||
|
||||
def this(id1: Int, id2: Int) {
|
||||
this(id1)
|
||||
}
|
||||
|
||||
def add2(a: Int, b: Int) = {
|
||||
a + b
|
||||
}
|
||||
}
|
||||
|
||||
object TestClass {
|
||||
|
||||
def apply(id: Int)= {
|
||||
println("-----------apply--------")
|
||||
new TestClass(id)
|
||||
}
|
||||
|
||||
def add(a: Int, b: Int): Int = {
|
||||
a + b
|
||||
}
|
||||
|
||||
def main(args: Array[String]): Unit = {
|
||||
val r1 = TestClass.add(1, 2)
|
||||
println(s"r1 is: $r1")
|
||||
|
||||
val t1 = new TestClass(1)
|
||||
val r2 = t1.add2(3, 4)
|
||||
println(s"r2 is: $r2")
|
||||
|
||||
val t2 = TestClass(100000)
|
||||
println(s"t2.id is: ${t2.id}")
|
||||
|
||||
val t3 = new TestClass(100, 200)
|
||||
println((s"t3.id is: ${t3.id}"))
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
输出结果为:
|
||||
|
||||
```
|
||||
r1 is: 3
|
||||
r2 is: 7
|
||||
-----------apply--------
|
||||
t2.id is: 100000
|
||||
t3.id is: 100
|
||||
```
|
||||
|
||||
## 3.trait
|
||||
Scala的Trait相当于Java里的Interface,但Trait不仅可以定义函数,还可以有函数体实现。实现关键词是extends,实现多个Trait用with。当extends的多个Trait里有相同函数时,子类必须重写该函数。
|
||||
|
||||
父trait里无函数体的函数,子类必须override
|
||||
重写父类里有函数体的函数,必须有关键词override
|
||||
trait里的变量,都是val类型
|
||||
在trait里定义的的变量,必须是val类型,如果变量没初始化,子类必须override
|
||||
|
||||
```
|
||||
trait TestTrait {
|
||||
def printfun()
|
||||
}
|
||||
|
||||
class TestTraitImp extends TestTrait {
|
||||
override def printfun(): Unit = {
|
||||
println("I'm TestTrait implement!")
|
||||
}
|
||||
}
|
||||
|
||||
object TestClass {
|
||||
def main(args: Array[String]): Unit = {
|
||||
val t4 = new TestTraitImp()
|
||||
t4.printfun()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
运行的结果为
|
||||
|
||||
```
|
||||
I'm TestTrait implement!
|
||||
```
|
||||
|
||||
## 4.scala类层级结构
|
||||
Scala里,每个类都继承自通用的名为Any的超类。因为所有的类都是Any的子类,所以定义在Any中的方法就是“共同的”方法:它们可以被任何对象调用。
|
||||
|
||||
Any有两个子类:AnyVal和AnyRef(相当于Java里的Object)。
|
||||
|
||||
AnyVal是Scala里每个内建值类的父类。有9个这样的值类:Byte、Short、Char、Int、Long、Float、Double、Boolean和Unit。其中的前8个都对应到Java的基本类型。这些值类都被定义为既是抽象的又是final的,不能使用new创造这些类的实例。Unit被用作不返回任何结果的方法的结果类型。Unit只有一个实例值,写成()。
|
||||
AnyRef类是Scala里所有引用类(reference class)的基类。它其实是Java平台上java.lang.Object类的别名。因此Java里写的类和Scala里写的都继承自AnyRef。
|
||||
scala.Null和scala.Nothing是用统一的方式处理Scala面向对象类型系统的某些“边界情况”的特殊类型。Null类是null引用对象的类型,它是每个引用类(继承自AnyRef的类)的子类。Null不兼容值类型。Nothing类型在Scala的类层级的最低端;它是任何其他类型的子类型。然而,根本没有这个类型的任何值。Nothing的一个用处是它标明了不正常的终止。
|
||||
|
||||
## 5.scala实现单例模式
|
||||
因为scala中没有static,所以我们用伴生对象来实现单例模式。
|
||||
|
||||
```
|
||||
class SingleModel {
|
||||
def printfunc() = {
|
||||
println("this is SingleModel!")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object SingleModel {
|
||||
private var instance: SingleModel = null
|
||||
|
||||
def getInstance() = {
|
||||
if (instance == null) {
|
||||
this.synchronized {
|
||||
if (instance == null) {
|
||||
instance = new SingleModel()
|
||||
}
|
||||
}
|
||||
}
|
||||
instance
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
客户端调用的代码如下
|
||||
|
||||
```
|
||||
object SingleModelClient {
|
||||
|
||||
def main(args: Array[String]): Unit = {
|
||||
val instance = SingleModel.getInstance()
|
||||
instance.printfunc()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
最后的输出为
|
||||
|
||||
```
|
||||
this is SingleModel!
|
||||
```
|
|
@ -0,0 +1,138 @@
|
|||
scala的集合中提供了三种排序的方式:sorted,sortWith,sortBy。那么这三种方式有什么不同呢?下面我们结合源码来分析一下
|
||||
|
||||
## 1.sorted
|
||||
先来看看scala中sorted的源码。
|
||||
|
||||
```
|
||||
def sorted[B >: A](implicit ord: Ordering[B]): Repr = {
|
||||
val len = this.length
|
||||
val arr = new ArraySeq[A](len)
|
||||
var i = 0
|
||||
for (x <- this.seq) {
|
||||
arr(i) = x
|
||||
i += 1
|
||||
}
|
||||
java.util.Arrays.sort(arr.array, ord.asInstanceOf[Ordering[Object]])
|
||||
val b = newBuilder
|
||||
b.sizeHint(len)
|
||||
for (x <- arr) b += x
|
||||
b.result
|
||||
}
|
||||
```
|
||||
|
||||
源码中有两点值得注意的地方:
|
||||
1.sorted方法中有个隐式参数ord: Ordering。
|
||||
2.sorted方法真正排序的逻辑是调用的java.util.Arrays.sort。
|
||||
|
||||
## 2.sortBy
|
||||
看看sortBy的源码,很简单。
|
||||
|
||||
```
|
||||
def sortBy[B](f: A => B)(implicit ord: Ordering[B]): Repr = sorted(ord on f)
|
||||
```
|
||||
|
||||
sortBy最后也是调用的sorted方法。不一样的地方在于,sortBy前面还需要提供一个属性。
|
||||
|
||||
## 3.sortWith
|
||||
sortWith的源码如下。
|
||||
|
||||
```
|
||||
def sortWith(lt: (A, A) => Boolean): Repr = sorted(Ordering fromLessThan lt)
|
||||
```
|
||||
|
||||
跟前面两个不同的是,sortWith需要传入一个比较函数用来比较!
|
||||
|
||||
## 4.实例
|
||||
理论部分说完了,下面来干货
|
||||
|
||||
```
|
||||
object ImplicitValue {
|
||||
|
||||
implicit val KeyOrdering = new Ordering[String] {
|
||||
override def compare(x: String, y: String) : Int = {
|
||||
y.compareTo(x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
首先定义了一个隐式比较器。
|
||||
|
||||
```
|
||||
def test1() = {
|
||||
import ImplicitValue.KeyOrdering
|
||||
val list = List( "a", "g", "F", "B", "c")
|
||||
val sortedList = list.sorted
|
||||
println(sortedList) // List(g, c, a, F, B)
|
||||
}
|
||||
```
|
||||
注意因为我们将隐式比较器import了进来,这个时候sorted排序的规则是按照我们自定义的比较器进行比较。在我们自定义的比较器中,定义的是按字符串逆序,所以最终的输出结果为字符串按从大到小的顺序排列!
|
||||
|
||||
|
||||
再来看看sortWith的用法。
|
||||
|
||||
```
|
||||
//忽略大小写排序
|
||||
def compareIngoreUpperCase(e1: String, e2: String) : Boolean = {
|
||||
e1.toLowerCase < e2.toLowerCase
|
||||
}
|
||||
|
||||
def test2() = {
|
||||
val list = List( "a", "g", "F", "B", "c")
|
||||
val sortWithList1 = list.sortWith(_ < _) // List(B, F, a, c, g)
|
||||
val sortwithList2 = list.sortWith((left, right) => left < right) //List(B, F, a, c, g)
|
||||
val sortwithList3 = list.sortWith(compareIngoreUpperCase) // List(a, B, c, F, g)
|
||||
println(sortWithList1)
|
||||
println(sortwithList2)
|
||||
println(sortwithList3)
|
||||
}
|
||||
```
|
||||
|
||||
本例中, sortWithList1与sortWithList2最终的结果是一致的,只不过写法不一样而已,都是按字符串从小到大的顺序排列。sortwithList3则是按照传入的compareIngoreUpperCase函数进行排序!
|
||||
|
||||
最后看看sortBy的代码
|
||||
|
||||
```
|
||||
def test3() = {
|
||||
val m = Map(
|
||||
-2 -> 5,
|
||||
2 -> 6,
|
||||
5 -> 9,
|
||||
1 -> 2,
|
||||
0 -> -16,
|
||||
-1 -> -4
|
||||
)
|
||||
//按key排序
|
||||
m.toList.sorted.foreach{
|
||||
case (key, value) =>
|
||||
println(key + ":" + value)
|
||||
}
|
||||
println
|
||||
|
||||
//按value排序
|
||||
m.toList.sortBy(_._2).foreach {
|
||||
case (key, value) =>
|
||||
println(key + ":" + value)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
最后的输出结果为:
|
||||
|
||||
```
|
||||
-2:5
|
||||
-1:-4
|
||||
0:-16
|
||||
1:2
|
||||
2:6
|
||||
5:9
|
||||
|
||||
0:-16
|
||||
-1:-4
|
||||
1:2
|
||||
-2:5
|
||||
2:6
|
||||
5:9
|
||||
|
||||
```
|
|
@ -0,0 +1,100 @@
|
|||
在别的编码语言中,break与continue两种控制语句是非常常见的用法,一般也有对应的关键字。但是在scala中,没有专门的break与continue关键字。那怎么在循环中实现break与continue功能呢?
|
||||
|
||||
## 1.实现break功能
|
||||
|
||||
```
|
||||
package com.xiaomi.leilei.test1
|
||||
|
||||
import scala.util.control.Breaks._
|
||||
|
||||
/**
|
||||
* Created by wanglei on 17/8/11.
|
||||
*/
|
||||
object breakdemo {
|
||||
|
||||
def breaktest() = {
|
||||
val arr = Array(1, 2, 3, 4, 5, 6)
|
||||
breakable {
|
||||
arr.foreach { x =>
|
||||
if (x > 3) break
|
||||
else println(x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def main(args: Array[String]): Unit = {
|
||||
breaktest()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
最后的输出结果为:
|
||||
|
||||
```
|
||||
1
|
||||
2
|
||||
3
|
||||
|
||||
```
|
||||
|
||||
## 2.实现continue功能
|
||||
|
||||
```
|
||||
package com.xiaomi.leilei.test1
|
||||
|
||||
import scala.util.control.Breaks._
|
||||
|
||||
/**
|
||||
* Created by wanglei on 17/8/11.
|
||||
*/
|
||||
object breakdemo {
|
||||
|
||||
def continuetest() = {
|
||||
val arr = Array(1, 2, 3, 4, 5, 6)
|
||||
arr.foreach {
|
||||
x => breakable {
|
||||
if(x == 4) break()
|
||||
else println(x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def main(args: Array[String]): Unit = {
|
||||
continuetest()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
通过上面的代码不难发现,实现break与continue功能,都需要`util.control.Breaks._`类的辅助。不同的是,将整个循环的逻辑放在breakable方法中,在需要真正跳出循环的时候使用break方法,这样达到了跳出整个循环的目的。而continue功能是将breakable放在循环内,这样可以实现结束本次循环的目的而不是结束整个循环。
|
||||
|
||||
## 3.Breaks的部分源码
|
||||
|
||||
```
|
||||
package scala
|
||||
package util.control
|
||||
|
||||
/** A class that can be instantiated for the break control abstraction.
|
||||
* Example usage:
|
||||
* {{{
|
||||
* val mybreaks = new Breaks
|
||||
* import mybreaks.{break, breakable}
|
||||
*
|
||||
* breakable {
|
||||
* for (...) {
|
||||
* if (...) break()
|
||||
* }
|
||||
* }
|
||||
* }}}
|
||||
* Calls to break from one instantiation of `Breaks` will never
|
||||
* target breakable objects of some other instantiation.
|
||||
*/
|
||||
```
|
||||
|
||||
如果翻译过来就是说:
|
||||
1.Breaks是一个可以实例化为中断控制抽象的类。
|
||||
2.Breaks的实例对象永远不用破坏要被中断对象的实例。
|
||||
3.注释中给出了明确的要实现break功能的代码样式。
|
|
@ -0,0 +1,109 @@
|
|||
## 1.apply方法
|
||||
当scala中类或者对象有一个主要用途的时候,apply方法就是一个很好地语法糖。
|
||||
|
||||
请看下面一个简单的例子:
|
||||
|
||||
```
|
||||
class Foo(foo: String) {
|
||||
}
|
||||
|
||||
object Foo {
|
||||
def apply(foo: String) : Foo = {
|
||||
new Foo(foo)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
定义了一个Foo类,并且在这个类中,有一个伴生对象Foo,里面定义了apply方法。有了这个apply方法以后,我们在调用这个Foo类的时候,用函数的方式来调用:
|
||||
|
||||
```
|
||||
object Client {
|
||||
|
||||
def main(args: Array[String]): Unit = {
|
||||
val foo = Foo("Hello")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
我们用`Foo("Hello")`的方式,就得到了一个Foo类型的对象,这一切就是apply方法的功劳。如果没有apply方法,我们将需要使用new关键字来得到Foo对象。
|
||||
|
||||
## 2.apply方法用来做工厂
|
||||
apply方法的最佳实践方式之一就是用来做工厂。比如在Scala的标准库中,许多集合类给我们提供了apply方法来创建集合:
|
||||
|
||||
```
|
||||
object Client {
|
||||
|
||||
def main(args: Array[String]): Unit = {
|
||||
val arr = new Array[Int](3)
|
||||
arr(0) = 0
|
||||
arr(1) = 1
|
||||
arr(2) = 2
|
||||
arr.foreach(x => print(x + " "))
|
||||
println()
|
||||
|
||||
val array = Array(1,2,3)
|
||||
array.foreach(x => print(x + " "))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
上面两种方式我们都可以用来创建Array。第一种方式是使用new关键字,这是传统的面向对象的方式。那么第二种方式是什么情况呢?如果我们在IDE里点进去,可以发现IDE会提示我们有一个apply方法。点进去看apply方法的源码:
|
||||
|
||||
```
|
||||
/** Creates an array of `Int` objects */
|
||||
// Subject to a compiler optimization in Cleanup, see above.
|
||||
def apply(x: Int, xs: Int*): Array[Int] = {
|
||||
val array = new Array[Int](xs.length + 1)
|
||||
array(0) = x
|
||||
var i = 1
|
||||
for (x <- xs.iterator) { array(i) = x; i += 1 }
|
||||
array
|
||||
}
|
||||
```
|
||||
|
||||
## 3.unapply方法
|
||||
从上面的例子不难看出,apply方法有点类似于java中的构造函数,接受构造参数变成一个对象。那么unapply方法就刚好相反,他是接受一个对象,从对象中提取出相应的值。
|
||||
|
||||
unapply方法主要用于模式匹配中。
|
||||
看个简单的例子:
|
||||
|
||||
```
|
||||
class Money(val value: Double, val country: String) {}
|
||||
|
||||
object Money {
|
||||
def apply(value: Double, country: String) : Money = new Money(value, country)
|
||||
|
||||
def unapply(money: Money): Option[(Double, String)] = {
|
||||
if(money == null) {
|
||||
None
|
||||
} else {
|
||||
Some(money.value, money.country)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
客户端实现:
|
||||
|
||||
```
|
||||
def testUnapply() = {
|
||||
val money = Money(10.1, "RMB")
|
||||
money match {
|
||||
case Money(num, "RMB") => println("RMB: " + num)
|
||||
case _ => println("Not RMB!")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
最后输出为:
|
||||
|
||||
```
|
||||
RMB: 10.1
|
||||
```
|
||||
|
||||
|
||||
所以下面那种创建数组的方式,其实是通过Array类的apply方法实现的。
|
||||
|
||||
## 参考文档:
|
||||
1.https://stackoverflow.com/questions/9737352/what-is-the-apply-function-in-scala/9738862#9738862
|
||||
2.https://twitter.github.io/scala_school/zh_cn/basics2.html
|
|
@ -0,0 +1,88 @@
|
|||
## 1.scala枚举原理
|
||||
严格来说,和其它语言不同,Scala 并没有枚举这种类型。Scala 中的枚举值只是Enumeration下的Value类的实例,而不是枚举自身的实例。Value类的实例主要依靠其构造方法的两个值:id与name来构建。其中,id是自增长,name如果不指定时默认使用的就是值的名字。所以不像其它语言,Scala 并不能任意定义构造方法来构造枚举。
|
||||
|
||||
```
|
||||
abstract class Enumeration (initial: Int) extends Serializable {
|
||||
thisenum =>
|
||||
|
||||
def this() = this(0)
|
||||
...
|
||||
protected final def Value(i: Int, name: String): Value = new Val(i, name)
|
||||
...
|
||||
}
|
||||
abstract class Value extends Ordered[Value] with Serializable {
|
||||
/** the id and bit location of this enumeration value */
|
||||
def id: Int
|
||||
/** a marker so we can tell whose values belong to whom come reflective-naming time */
|
||||
private[Enumeration] val outerEnum = thisenum
|
||||
|
||||
override def compare(that: Value): Int =
|
||||
if (this.id < that.id) -1
|
||||
else if (this.id == that.id) 0
|
||||
else 1
|
||||
override def equals(other: Any) = other match {
|
||||
case that: Enumeration#Value => (outerEnum eq that.outerEnum) && (id == that.id)
|
||||
case _ => false
|
||||
}
|
||||
override def hashCode: Int = id.##
|
||||
|
||||
/** Create a ValueSet which contains this value and another one */
|
||||
def + (v: Value) = ValueSet(this, v)
|
||||
}
|
||||
|
||||
/** A class implementing the [[scala.Enumeration.Value]] type. This class
|
||||
* can be overridden to change the enumeration's naming and integer
|
||||
* identification behaviour.
|
||||
*/
|
||||
@SerialVersionUID(0 - 3501153230598116017L)
|
||||
protected class Val(i: Int, name: String) extends Value with Serializable {
|
||||
def this(i: Int) = this(i, nextNameOrNull)
|
||||
def this(name: String) = this(nextId, name)
|
||||
def this() = this(nextId)
|
||||
...
|
||||
}
|
||||
```
|
||||
从上面的源码可以看出,真正的主构造方法为`Val(i: Int, name: String)`,下面的辅助构造方法有三个,分别可以只传id,只传name,或者任何参数不传。
|
||||
|
||||
## 2.例子
|
||||
以一个星期几的枚举为例,看看scala中的枚举如何使用。
|
||||
|
||||
```
|
||||
object Weekday extends Enumeration {
|
||||
|
||||
type Weekday = Value
|
||||
val Monday = Value(1)
|
||||
val Tuesday = Value(2, "tue")
|
||||
val Wednesday = Value(3)
|
||||
val Thursday = Value(4)
|
||||
val Friday = Value(5)
|
||||
val Saturday = Value(6)
|
||||
val Sunday = Value(7)
|
||||
val SS = Value
|
||||
|
||||
def main(args: Array[String]): Unit = {
|
||||
// 枚举值,Monday只传了id,name默认为枚举值
|
||||
val monday = Weekday.Monday
|
||||
println(monday)
|
||||
|
||||
// 通过name获得枚举值
|
||||
val tuesday = Weekday.withName("tue")
|
||||
println(tuesday)
|
||||
|
||||
// 通过id获得枚举值
|
||||
println(Weekday(6))
|
||||
|
||||
// 获得id
|
||||
println(Weekday.SS.id)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
输出的结果为
|
||||
|
||||
```
|
||||
Monday
|
||||
tue
|
||||
Saturday
|
||||
8
|
||||
```
|
|
@ -0,0 +1,57 @@
|
|||
## 1.什么是柯里化函数
|
||||
在scala相关的教程与参考文档里,经常会看到柯里化函数这个词。但是对于具体什么是柯里化函数,柯里化函数又有什么作用,其实可能很多同学都会有些疑惑。今天就跟大家来掰扯掰扯柯里化函数(Haskell Curry)。
|
||||
|
||||
首先看两个简单的函数:
|
||||
|
||||
```
|
||||
def add(x: Int, y: Int) = x + y
|
||||
|
||||
def addCurry(x: Int)(y: Int) = x + y
|
||||
```
|
||||
|
||||
以上两个函数实现的都是两个整数相加的功能。对于add方法,调用方式为`add(1,2)`。对于addCurry方法,调用的方式为`addCurry(1)(2)`。这种方式就叫做柯里化。说得更加简单粗暴一点,有多个参数列表,或者说多个小括号括起来的函数参数列表的函数就是柯里化函数。
|
||||
|
||||
## 2.为什么要使用柯里化
|
||||
看了前面的例子,很多人估计忍不住就要开喷了:你特么逗我呢?好好的给一个方法传两个参数不挺好么,干嘛搞这么复杂。
|
||||
我能理解大家的感受,我第一次看到这种做法心里也是有一万匹草泥马奔腾而过。不过我们要相信一句话,存在即合理,既然这么干,肯定是有合理的地方,暂且慢慢分析。
|
||||
|
||||
curry化最大的意义在于把多个参数的函数等价转化成多个单参数函数的级联,这样所有的函数就都统一了,方便做lambda演算。 在scala里,curry化对类型推演也有帮助,scala的类型推演是局部的,在同一个参数列表中后面的参数不能借助前面的参数类型进行推演,curry化以后,放在两个参数列表里,后面一个参数列表里的参数可以借助前面一个参数列表里的参数类型进行推演。
|
||||
|
||||
上面的说法比较书面化,用更加口语化的一点来描述:
|
||||
1.把多个参数转化为单参数函数的级联,达到了动态确定参数的目的。
|
||||
2.当某些参数不确定时,可以先保留一个存根。剩余的参数确定以后,就可以通过存根调用剩下的参数。
|
||||
3.通过类似于建造者模式(building),把一个大的东西的构造过程,切成一个个的小模块来逐步构造。举个最简单的例子,`Person.name("xxx").age(num).salary(count).phone(xxxx)`。
|
||||
|
||||
## 3.scala源码中的柯里化
|
||||
scala源码中存在大量的柯里化函数的应用,看几个简单的例子。
|
||||
|
||||
```
|
||||
def foldLeft[B](z: B)(f: (B, A) => B): B = {
|
||||
var acc = z
|
||||
var these = this
|
||||
while (!these.isEmpty) {
|
||||
acc = f(acc, these.head)
|
||||
these = these.tail
|
||||
}
|
||||
acc
|
||||
}
|
||||
```
|
||||
|
||||
从foldLeft的方法原型里很容易就看出来,这是就是典型的柯里化函数的应用。foldLeft有两个参数,一个为参入的初始值z,类型为B。另一个为函数f,f有两个参数,一个类型与传入的初始值相同,另一个为集合本身的类型A,最后方法返回的类型为B。
|
||||
|
||||
```
|
||||
def foldtest() = {
|
||||
val list = List(1, 2, 3)
|
||||
val strResleft = list.foldLeft("res:")((x: String, y:Int) => x + y)
|
||||
val strResRight = list.foldRight("res:")((y: Int, x: String) => x + y)
|
||||
println(strResleft)
|
||||
println(strResRight)
|
||||
}
|
||||
```
|
||||
|
||||
函数的输出:
|
||||
|
||||
```
|
||||
res:123
|
||||
res:321
|
||||
```
|
|
@ -0,0 +1,147 @@
|
|||
在前面的文章里,我们讲了在java中如何利用泛型实现数值类型加法。具体可以参考博文 http://blog.csdn.net/bitcarmanlee/article/details/78733637。
|
||||
那么在scala中,我们怎么实现上面的需求呢?
|
||||
|
||||
## 1.用 <: 模拟extends关键字行不通
|
||||
如果按照在java中的处理思路,我们可以这么尝试一下:
|
||||
|
||||
```
|
||||
def numberAdd[T <: Number](t1 : T, t2 : T) = {
|
||||
t1.doubleValue() + t2.doubleValue()
|
||||
}
|
||||
|
||||
@Test
|
||||
def numberaddtest() = {
|
||||
val (t1, t2) = (1, 2)
|
||||
numberAdd(t1, t2)
|
||||
}
|
||||
```
|
||||
|
||||
在scala中,`<:`表示类型的上限,与java中的extends含义一样。我们试图将上面的test方法run起来,发现IDE中报错:
|
||||
|
||||
```
|
||||
Error:(26, 19) type mismatch;
|
||||
found : Int
|
||||
required: T
|
||||
numberAdd(t1, t2)
|
||||
Error:(26, 9) inferred type arguments [Int] do not conform to method numberAdd's type parameter bounds [T <: Number]
|
||||
numberAdd(t1, t2)
|
||||
Error:(26, 23) type mismatch;
|
||||
found : Int
|
||||
required: T
|
||||
numberAdd(t1, t2)
|
||||
```
|
||||
|
||||
很明显,上面的方法是行不通的。
|
||||
|
||||
## 2.使用Numeric[T]
|
||||
为什么上面的方法行不通呢?因为Scala的数字类型并不都共享一个超类,所以我们不能使用T <: Number。相反,要使之能工作,Scala的math库对适当的类型T 定义了一个隐含的Numeric[T],我们可以使用他来完成类似的功能。
|
||||
|
||||
首先上可以运行的代码:
|
||||
|
||||
```
|
||||
def add[T](x: T, y: T)(implicit num: Numeric[T]) = {
|
||||
val result = num.plus(x, y)
|
||||
result
|
||||
}
|
||||
@Test
|
||||
def testAdd() = {
|
||||
val int1 = 1
|
||||
val int2 = 2
|
||||
println("int sum is: " + add(int1, int2))
|
||||
|
||||
val long1 = 100L
|
||||
val long2 = 200L
|
||||
println("long sum is: " + add(long1, long2))
|
||||
|
||||
val f1 = 1.0f
|
||||
val f2 = 2.0f
|
||||
println("float sum is: " + add(f1, f2))
|
||||
|
||||
val d1 = 1.0
|
||||
val d2 = 2.0
|
||||
println("double sum is: " + add(d1, d2))
|
||||
}
|
||||
```
|
||||
|
||||
将上面的test方法run起来,可以得到如下输出:
|
||||
|
||||
```
|
||||
int sum is: 3
|
||||
long sum is: 300
|
||||
float sum is: 3.0
|
||||
double sum is: 3.0
|
||||
```
|
||||
|
||||
## 3.Numeric[T]的用法
|
||||
|
||||
Numeric[T]在scala中源码如下:
|
||||
|
||||
```
|
||||
type Numeric[T] = scala.math.Numeric[T]
|
||||
val Numeric = scala.math.Numeric
|
||||
```
|
||||
|
||||
当然,我们也可以通过implicitly方法,用context bound(上下文绑定)的方式让上面的代码更简单:
|
||||
|
||||
```
|
||||
def add2[T: Numeric](x: T, y: T) = {
|
||||
implicitly[Numeric[T]].plus(x, y)
|
||||
}
|
||||
```
|
||||
|
||||
其中,implicitly方法在scala中的定义如下:
|
||||
|
||||
```
|
||||
@inline def implicitly[T](implicit e: T) = e // for summoning implicit values from the nether world
|
||||
```
|
||||
|
||||
implicitly 主要是在当前作用域查找指定类型,例如以下的例子:
|
||||
|
||||
```
|
||||
@Test
|
||||
def testimplicit() = {
|
||||
implicit val x = 1
|
||||
implicit val x1 = 2.0
|
||||
val y = implicitly[Int]
|
||||
val z = implicitly[Double]
|
||||
println(y + "\t" + z)
|
||||
}
|
||||
```
|
||||
|
||||
将test方法run起来以后,输出如下:
|
||||
|
||||
```
|
||||
1 2.0
|
||||
```
|
||||
|
||||
## 4.数值集合求和
|
||||
搞定了单个的数值求和,那么数值集合的求和自然就变得容易了:
|
||||
|
||||
```
|
||||
def add4[T: Numeric](x : Array[T]) = {
|
||||
var sum = 0.0
|
||||
for(each <- x) {
|
||||
sum += implicitly[Numeric[T]].toDouble(each)
|
||||
}
|
||||
println(sum)
|
||||
}
|
||||
|
||||
@Test
|
||||
def test() = {
|
||||
val array = Array(1, 2, 3)
|
||||
add4(array)
|
||||
}
|
||||
```
|
||||
|
||||
代码输出:
|
||||
|
||||
```
|
||||
6.0
|
||||
```
|
||||
|
||||
## 参考内容:
|
||||
1.https://stackoverflow.com/questions/4373070/how-do-i-get-an-instance-of-the-type-class-associated-with-a-context-bound
|
||||
2.https://stackoverflow.com/questions/2982276/what-is-a-context-bound-in-scala
|
||||
3.http://www.jianshu.com/p/1d119c937015 Scala中的Implicit详解
|
||||
4.https://vimsky.com/article/1562.html scala常见问题整理
|
||||
5.https://fangjian0423.github.io/2015/06/07/scala-generic/ scala泛型
|
|
@ -0,0 +1,108 @@
|
|||
## 1.简介
|
||||
scopt是github上有人开发的一个用来解析参数的轻量级小工具,对于日常解析参数的场景基本够用,项目中使用也比较多,现在稍微记录一下用法。
|
||||
|
||||
## 2.依赖
|
||||
在pom.xml文件中加入以下依赖
|
||||
|
||||
```
|
||||
<dependency>
|
||||
<groupId>com.github.scopt</groupId>
|
||||
<artifactId>scopt_2.10</artifactId>
|
||||
<version>3.2.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
||||
## 3.例子
|
||||
直接上代码先
|
||||
|
||||
```
|
||||
object ScoptTest {
|
||||
|
||||
case class Params(
|
||||
input: String = "input",
|
||||
output: String = "output",
|
||||
index: Int = 0,
|
||||
partitionnum: Int = 2,
|
||||
debug: Boolean = false)
|
||||
|
||||
|
||||
val parser = new scopt.OptionParser[Params]("test") {
|
||||
head("this is a test for scopt params")
|
||||
|
||||
opt[String]('i', "input").required().action {
|
||||
(x, c) => c.copy(input = x)
|
||||
}.text("default of input is input")
|
||||
|
||||
opt[String]('o', "output").required().action {
|
||||
(x, c) => c.copy(output = x)
|
||||
}.text("default of output is output")
|
||||
|
||||
opt[Int]("index").optional().action {
|
||||
(x, c) => c.copy(index = x)
|
||||
}.text("default of index is 0")
|
||||
|
||||
opt[Int]("partitionnum").optional().action {
|
||||
(x, c) => c.copy(partitionnum = x)
|
||||
}.text("default of partitionnum is 2")
|
||||
|
||||
opt[Boolean]("debug").optional().action {
|
||||
(x, c) => c.copy(debug = x)
|
||||
}.text("default of debug is false")
|
||||
}
|
||||
|
||||
def init(args: Array[String]) = {
|
||||
val params = parser.parse(args, Params()).get
|
||||
println(params.input)
|
||||
println(params.output)
|
||||
println(params.index)
|
||||
println(params.partitionnum)
|
||||
println(params.debug)
|
||||
}
|
||||
|
||||
def main(args: Array[String]): Unit = {
|
||||
init(args)
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
首先我们先写一个case class,里面加入各种我们需要的参数。
|
||||
然后生成一个parser对象,解析传入的参数即可。
|
||||
|
||||
|
||||
将上面的代码run起来,如果不传入input与output这两必须参数,会有如下提示
|
||||
|
||||
```
|
||||
Error: Missing option --input
|
||||
Error: Missing option --output
|
||||
this is a test for scopt params
|
||||
Usage: test [options]
|
||||
|
||||
-i <value> | --input <value>
|
||||
default of input is input
|
||||
-o <value> | --output <value>
|
||||
default of output is output
|
||||
--index <value>
|
||||
default of index is 0
|
||||
--partitionnum <value>
|
||||
default of partitionnum is 2
|
||||
--debug <value>
|
||||
default of debug is false
|
||||
```
|
||||
|
||||
传入如下参数
|
||||
|
||||
```
|
||||
-i input1 -o output2
|
||||
```
|
||||
|
||||
代码运行的结果为
|
||||
|
||||
```
|
||||
input1
|
||||
output2
|
||||
0
|
||||
2
|
||||
false
|
||||
```
|
Loading…
Reference in New Issue