2019年9月14日 星期六

Excel VBA 五 String、日期、Function、Sub

※String

Dim str As String
str = "abcdefg"
Cells(1, "A").Value = Now
Cells(1, "B").Value = Left(str, 4) 'abcd
Cells(2, "B").Value = Right(str, 4) 'defg
Cells(3, "B").Value = Mid(str, 4, 2) 'de
Cells(4, "B").Value = Len(str) '7
Cells(5, "B").Value = InStr(str, "cd") '3

※VBA 的 index 是從 1 開始的



※日期

Dim d1 As Date
Dim d2 As Date

d1 = DateValue("Jan 21, 1999")
Cells(1, "A").Value = d1 '1999/1/21
Cells(2, "A").Value = Year(d1) '1999
Cells(3, "A").Value = Month(d1) '1
Cells(4, "A").Value = Day(d1) '21

d2 = TimeValue("17:25:37")  '等同 TimeValue("5:25:37 pm")
Cells(5, "A").Value = Hour(d2) '17
Cells(6, "A").Value = Minute(d2) '25
Cells(7, "A").Value = Second(d2) '37


.增加
Dim d As Date
d = DateValue("Jan 21, 1999")
d = DateAdd("m", -1, d)
MsgBox (d)

※第一個參數如下:
yyyy - 年
q - 季度
m - 月
y - 当年的第几天
d - 日
w - 当周的第几天
ww - 周
h - 小时
n - 分钟
s - 秒


 ※Function 和 Sub

Sub hello() '我是註解
  'MsgBox 3 * fun1()
  'MsgBox fun2(3, 2)
  'Call s1
  's1
  Call s2(3, 2)
  s2 3, 2
End Sub

Function fun1() As Integer
  fun1 = 2
End Function

Function fun2(a As Integer, b As Integer) As Integer
  fun2 = a * b
End Function

Sub s1()
  MsgBox fun2(3, 2)
End Sub

Sub s2(a As Integer, b As Integer)
  MsgBox fun2(a, b)
End Sub

※Function 有沒有回傳值都可以,Sub 不能有回傳值

※呼叫時,Sub 可用 Call,如果不想用 Call,後面一定不能有圓括號

2019年9月13日 星期五

telegram BotAPI

安裝好 telegram 後,開啟 https://core.telegram.org/bots 文檔

開啟你的 telegram,然後點紅框的連結,即可加入 BotFather
指令查詢:輸入 /start 或 /help 會出現可以打得指令
/newbot 新增一個 bot

出現 Alright, a new bot. How are we going to call it? Please choose a name for your bot. 後
為你的 bot 取一個名字

取好後,會出現
Good. Now let's choose a username for your bot. It must end in `bot`. Like this, for example: TetrisBot or tetris_bot.

再取一個使用者名稱,一定要 bot 結尾
這個地方我試了好幾個名稱都不行

不行的訊息:
Sorry, this username is already taken. Please try something different.

成功後,會出現 Done! Congratulations on your new bot. You will find it at 開頭的
同時 telegram 也會增加一個 bot
Use this token to access the HTTP API 是取得的 token




可以使用 /token 和 /revoke 重新產生 token 和註消 token

如果按了清螢幕,所以看不到 token 了,還可以用 /mybots
然後點擊你取的 username,再點擊 API Token 即可

開啟群組:

/setjoingroups
@你的 username
然後開啟或關閉
這個功能能讓你的 bot 加入其他的群組
然後在 xxxbot 隨便打幾個字,然後新建群組後,將 xxxbot 加進去就可以對這個群發送訊息了




https://api.telegram.org/bot{你的 token}/getUpdates 可以取得你向你的 bot 發訊息的 json 內容
其中 id 表示此群的 id,如果有加入群組,每個群組的 id 會不一樣

加入群組:
創一個群組後,將機器人加入,並發訊息,然後在上面的網址 getUpdates 裡會有 id


使用 java 11 發送訊息:

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class TestTelegram {
    private final String TELEGRAM_TOKEN = "你的 token";
    private final String CHAT_ID = "你的群組 id 或機器人 id";
    private final String TEXT = "要發送的訊息";

    public static void main(String[] args) throws Exception {
        TestTelegram telegram = new TestTelegram();
        HttpClient client = HttpClient.newBuilder().build();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(
                        "https://api.telegram.org/bot" + telegram.TELEGRAM_TOKEN +
                                "/sendMessage")
                )
                .header("Content-Type", "application/x-www-form-urlencoded")
                .POST(HttpRequest.BodyPublishers.ofString("chat_id=" + telegram.CHAT_ID + "&text=" + telegram.TEXT))
                .build();

        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.statusCode());
    }
}




使用 apache 發送訊息:
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost("https://api.telegram.org/bot" + 得到的 token +
"/sendMessage");

httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded");
List<NameValuePair> urlParameters = new ArrayList<>();
urlParameters.add(new BasicNameValuePair("chat_id", CHAT_ID));
urlParameters.add(new BasicNameValuePair("text", TEXT));
httpPost.setEntity(new UrlEncodedFormEntity(urlParameters));

CloseableHttpResponse execute = httpClient.execute(httpPost);
execute.close();

2019年9月11日 星期三

Excel VBA 四 宣告變數、陣列、If、迴圈、Select Case

※宣告變數

Dim x As Integer
'x = "d"
x = 1 + 2
MsgBox (7 + x)
MsgBox ("x=" & x)

※雖然不用宣告也可以執行,但如果不宣告,註解那行是可以執行的

※類型
Byte (1):無符號,0-255
Integer (2)
Long (4)
Double (8)
Boolean (2)
Decimal (14)
String

Date (8)
Currency (8)
Single (4)
Object (4)
Variant (根据分配确定)

數字預設是 Integer



※陣列

Dim a(6 To 10) As String
a(6) = "a"
a(8) = "b"
a(10) = "c"
MsgBox a(6) 'a
MsgBox a(7) '

※如果不在範圍內 (6-10),如 a(5) 會報 Subscript out of range

.二維陣列

Dim b(1 To 3, 2 To 4) As String
b(1, 2) = "kkk"
MsgBox b(1, 2) 'kkk


※If

Dim i As Byte
i = 7
If i = 1 Then
  MsgBox (1)
ElseIf i = 2 Then
  MsgBox (2)
ElseIf i = 7 Then
  MsgBox (7)
Else
  MsgBox ("hahaha")
End If

※不等於用 <>


Dim i As Byte
i = 7
If i = 0 Then
  MsgBox (0)
ElseIf i >= 1 & i <= 10 Or i = 100 Then
  MsgBox ("1~10 or 100")
Else
  MsgBox ("hahaha")
End If

※& 和 Or 可以更進一步的判斷


※迴圈

※Do ~ Loop

Dim i As Integer
'i = 10
Do While i < 10
  i = i + 1
Loop
MsgBox (i)

※至少會跑一次


※For ~ Next

Dim i As Integer
Dim sum As Long

For i = 1 To 10 'Step 1
  sum = sum + i
Next i
MsgBox (sum)

※Step 預設就是 1 了


For i = 1 To 9
  For j = 1 To 9
    Cells(i, j).Value = i & "x" & j & "=" & i * j
  Next j
Next i

※可以嵌套


※For Each ~ Next

For i = 1 To 5 Step 1
    Cells(i, "a").Value = i * 9
Next i

For Each j In Range("A1: A5")
  If j = 36 Then
    GoTo xx
    'Exit For
  End If
  MsgBox (j)
  xx:
Next j

※Exit For 就是 break

※GoTo 到一個標籤,模擬 continue



※Select Case

n = -9
Dim str As String

Select Case n
  Case 1
    n = "a"
  Case 2, 3
    n = "b"
  Case 4 To 6
    n = "c"
  Case Is > 6
    n = "d"
  Case Else
    n = "other"
  End Select
MsgBox (n)

2019年9月10日 星期二

Excel VBA 三 選取、複製、清除內容、總數、常用方法

※選取

Set r = Range("C4: E5")
r.Value = 999
r.Select
'r.Rows(1).Select
'r.Columns(1).Select


分別為 Select、Rows(1).Select、Columns(1).Select
R 橫 C 直

※複製

Set r = Range("C4: E5")
r.Value = 888

'方法一
'r.Select
'Selection.Copy

'Range("A7").Select
'ActiveSheet.Paste

'方法二
Range("A7:C8").Value = r.Value

推薦用方法二比較簡潔有力


※清除內容

Range("A7:C8").ClearContents
'Range("A7:C8").Value = ""



※總數

Set r = Range("C4: E5")
r.Value = 888
Cells(1, "A").Value = r.Count '6
Cells(2, "A").Value = r.Rows.Count '2
Cells(3, "A").Value = r.Columns.Count '3


※常用方法

Cells(4, "A").Value = "abc"
Cells(4, "A").Interior.Color = vbYellow '儲存格背景顏色
Cells(4, "A").Font.Color = RGB(100, 200, 255) '字體顏色
Cells(4, "A").Font.Bold = True '粗體
Cells(4, "A").Font.Italic = True '斜體
Cells(4, "A").Font.Underline = True '底線
Cells(4, "A").Font.Size = 20 '字體大小
Cells(4, "A").ColumnWidth = 20 '儲存格寬度
Cells(4, "A").EntireColumn.AutoFit '自動調整適合的儲存格寬度
'Cells(4, "A").ClearContents '清除儲存格內容
'Cells(4, "A").ClearFormats '清除儲存格非內容的狀態 (背景色、字體色、字體大小…等)

Excel VBA 二 插入按鈕、Cell、Range



1.Insert 有很多的圖形可以控制,這裡以左上角的按鈕為例




2.選完第一張圖的按鈕後,在 Excel 工作區隨便畫一個長方形,如此圖的上半部分
滑鼠放開後,就會看到下半部分
然後名稱預設是 CommandButton1,想改名可按上圖的下半部分操作



3.第一和第二張圖都有個 View Code 可到這個畫面來,然後打程式碼
Private Sub CommandButton1_Click()
  Cells(2, "B").Value = "haha"
  MsgBox ("insert" & vbNewLine & "success")
End Sub
打完按三角按鈕即可執行



4.此時按鈕就可以按了,但發現按右鍵沒反應,此時可以發現 Properties 是灰色的
按下橘框的 Design Mode,會發現 Properties 又可以用了且按右鍵也可以了


※Cell、Range

Cells(1, "A").Value = 123  '第二個參數用數字也可以;1就是A,2就是B,依此類推
Cells(2, "B").Value = "haha"

Range("C3").Value = "I'm C3"
Range("C4: E5").Value = "c4-e5"
Range("C4: E5, A7: B13, G1").Value = "yeah"

以上是針對當前工作表,如果想控制其他的工作表可用如下三個方法:

Worksheets("Sheet1").Range("A2").Value = "A7"
Worksheets(1).Range("A3").Value = "A3" 'Worksheets.Count
Sheet1.Range("A4").Value = "A4"


如上圖右邊,Sheet1 和 Sheet2 都是 Excel 幫我們定好的,後面的括號是別名,會對應到上圖左邊下面的名稱
注意 Sheet2 的別名是 Sheet1

第一種是用別名的方式
第二種是以上圖左邊下面去算的,最左邊的 xxx 是1,Sheet1 是 2,但滑鼠按著其中一個去換,是可以換的,這時順序會改變,所以這種方法不推薦
第三種是用 Excel 幫我們定好的名稱
使用 Worksheets.Count 可以知道有幾張工作表

※Range 的另外一種用法

Range("xxx"),裡面的 xxx 可以自己定義,方法如下:


1.這次使用的不是 Developer 了,是 Formulas



2.Scope 是工作表的別名,橘框按了以後視窗會變小,這時在 Excel 拉需要的範圍即可,如果要多個範圍,就按 Ctrl



3.在左上角打上自己取得名稱,就會反白範圍



4.這個畫面可以編輯和刪除,但 Scope 不能改

Excel VBA 一 環境


1.在 File/Options (檔案/選項) 裡會出現如上的畫面



2.勾了第一張圖的 Developer 後,就有活頁標籤可以使用,選 Visual Basic 寫程式



3.大小寫要正確才行,打完後按橘框的按鈕可測試

Sub hello() '我是註解
  MsgBox ("hello, VBA")
End Sub

註解的按鈕和「"」是同一個

如果整行都要註解,可用 Rem xxx

MsgBox 的圖括號可省略




4.選左邊的程式,然後按 Run 即可
Macro Name 是搜尋,有可能有很多巨集
一個Sub ~ End Sub 就是一個巨集,可以在同一個地方寫很多巨集



5.存檔要存成 xlsm 才行,下次再進來選 Macros (巨集),會出現上一張圖的畫面,選 Edit (編輯) 就會出現打程式的地方了

官方文檔連結
可參考的 API

2019年9月6日 星期五

Scala 2.x 五 泛型、隱式轉換

※泛型類別、泛型方法

def genericMethod[T](t: T, list: List[T]): Unit = {}

class Chicken[T] extends Bird {
  def genericMethod(t: T, list: List[T]): Unit = {}
}

※寫在類別名稱或方法名稱後面



※傳參

class Animal{}
class Bird extends Animal{}
class Chicken extends Bird{}


def main(args: Array[String]): Unit = {
  val listA = List[Animal](new Animal)
  val listB = List[Bird](new Bird)
  val listC = List[Chicken](new Chicken)
  xxx(listA, listB, listC)
}

def xxx(a: List[Animal], b: List[Bird], c: List[Chicken]): Unit = {
  // ooo(a)
  ooo(b)
  ooo(c)
}

def ooo[T](list: List[Bird]) {}



※賦值

val listA = ListBuffer[Animal](new Animal)
val listB = ListBuffer[Bird](new Bird)
val listC = ListBuffer[Chicken](new Chicken)

val a = new Animal()
val b = new Bird()
val c = new Chicken()

listA += a
listA += b
listA += c

//    listB += a
listB += b
listB += c

//    listC += a
//    listC += b
listC += c


※以上都和 java 一樣

※沒有通配符「?」




※<:、>:


<::類似 java 的 extends
>::類似 java 的 super
.只能跟著自定義名稱,如 T

val listA = ListBuffer[Animal](new Animal)
    val listB = ListBuffer[Bird](new Bird)
    val listC = ListBuffer[Chicken](new Chicken)
    ooo(listA)
    ooo(listB)
//    ooo(listC)


def ooo[T >: Bird](listBuffer: ListBuffer[T]) = {}



※+T、-T

如果只有 T,沒有辦法將子類轉成父類或父類轉成子類

class GenericTest[T](t: T){} // +T、-T

class Animal{}
class Bird extends Animal{}
class Chicken extends Bird{}



.+T:表示子類可轉成父類

val a = new GenericTest[Animal](new Animal())
val b = new GenericTest[Bird](new Bird())
val c = new GenericTest[Chicken](new Chicken())

val d:GenericTest[Animal]  = b
val f:GenericTest[Bird]  = c
val e:GenericTest[Animal]  = c


.-T:表示父類可轉成子類

val a = new GenericTest[Animal](new Animal())
val b = new GenericTest[Bird](new Bird())
val c = new GenericTest[Chicken](new Chicken())

val d:GenericTest[Chicken]  = b
val f:GenericTest[Bird]  = a
val e:GenericTest[Chicken]  = a



※隱式轉換


.隱式方法

def printStr(data: String): Unit = println(data)

//  implicit def int2Str(i: Int):String = i + "xxx"
implicit def intToStr(j: Int):String = j.toString
// -------------------
printStr("abc")
printStr(123)

※用 java 的 overloading 也可以做到轉換的功能,但這裡說的是 scala 的隱式轉換

※如果沒有 implicit 的方法時,給 int 會編譯錯誤

※printStr 要的是 String 參數,但給的是 int,所以會去找參數是 int,回傳是 String 的隱式方法,如果有兩個(含)以上會報錯


再一個例子:
class Animal(name: String) {
  private var iname: String = ""

  def sname = name
  def sname_=(name: String): Unit = {
    this.iname = name
  }
}

class Bird(name: String) {
  private var iname: String = ""

  def sname = name
  def sname_=(name: String): Unit = {
    this.iname = name
  }
}

※兩個類別,然後給 getter/setter 方法

def printNameOfAnimal(animal: Animal): Unit = println(animal.sname)

implicit def birdToAnimal(b: Bird): Animal = {
new Animal("b" + b.sname)
}
// -------------------
printNameOfAnimal(new Animal("chicken"))
printNameOfAnimal(new Bird("chicken"))



.隱式值

object ScalaTest {
  implicit val xxx = new Animal("qoo")

  def main(args: Array[String]): Unit = {
    xxx("x")
  }

  def xxx(name: String)(implicit a: Animal): Unit = {
    println(a.sname)
  }

  class Animal(name: String) {
    private var iname: String = ""

    def sname = name
    def sname_=(name: String): Unit = {
      this.iname = name
    }
  }
}

※有了隱式值了以後,呼叫 xxx 方法可以不用參數,當然 xxx 方法也要有 implicit 關鍵字

※如果值在其他的類別,就需要 import


.隱式參數

def printStr(implicit data: String, num: Int): Unit = println(data, num)
// def printStr[T <% String](data: String, num: Int): Unit = println(data, num)
implicit def intToStr(j: Int):String = j.toString

printStr("abc", 7)
printStr(123, 7)

※這裡的隱式參數,雖然不寫也能找得到

※還可以用隱式視圖「<%」,但 IDE 提示我已經 deprecated 了,用隱式參數取代



.隱式類別

def main(args: Array[String]): Unit = {
  println(new Animal("xxx").show())
  println("qoo".show())
}

implicit class Animal(name: String) {
  def show(): String = {
    name
  }
}

※第二個 println 原本是不行的,但因為有隱式類別所以可以用

※隱式類別只能有一個參數,太多太少都不行

2019年9月5日 星期四

Scala 2.x 四 集合

※List、Set、Map 宣告

println(List(1, 1, 2, 2, null))
println(Set(1, 1, 2, 2, null))
println(Map("a" -> 1, "b" -> null)) // key 不能 null,value 可以;但如果有宣告泛型都不能是 null

限定某個類型,類似 java 的泛型
val value:List[Int] = List(1, 1, 2, 2, null)
以上都是不可變的,預設都是 scala.collection.immutable,可變的在 scala.collection.mutable
Set 和 Map都有同名的類別,但 List 的可變類別是 ListBuffer


※List

var xxx = 1 :: (2 :: (3 :: Nil)) // List(1,2,3),最後一定要寫 Nil
xxx.foreach(n => println(n))
println(xxx.head) // 1
println(xxx.tail) // List(2, 3), 返回第一個以外的元素
println(xxx.isEmpty) // false


var xxx = List(1, 2, 3)
var ooo = List(4, 5, 6)
var zzz = xxx.concat(ooo) // xxx ::: ooo
zzz.foreach(n => println(n)) // 1-6

※::: 等同於 concat

※可變的 List

import scala.collection.mutable.ListBuffer

val listBuffer = ListBuffer(1,2,3)
listBuffer += 4 // unit.addOne(4)
println(listBuffer)


※Set

import scala.collection.mutable.Set
val mutableSet:Set[String] = Set("a", "b", "c")
println(mutableSet.getClass.getName) // scala.collection.mutable.HashSet


mutableSet.add("d") // a b c d
mutableSet.remove("b") // a c d
mutableSet += "e" // a c d e
mutableSet -= "a" // c d e

※這裡用的是可變的


※整合兩個集合 (List、Set 、Map 混合都可以)

val s1 = Set("a", "b", "c")
val s2 = Set("d", "e")
val s3= s1 ++ s2
println(s3)


※最大值、最小值

val n = Set(78,44,88,115)
println(n.min)
println(n.max)



※Map

import scala.collection.mutable.Map


val map: Map[String, Int] = Map("a" -> 1, "b" -> 2)
map += ("c" -> 88)
map.keys.foreach(k => println(k + "-" + map(k)))


※整合兩個集合(List、Set、Map 混合都可以)

val map1: Map[String, Int] = Map("a" -> 1, "b" -> 2)
val map2: Map[String, Int] = Map("c" -> 3, "d" -> 4)
val map3 = map1 ++ map2
println(map3)



※Tuple

裡面可以放入不同類型,可以重複,但都是不可變的,可以回傳多個值 (1~22)
也就是 List,但值可以是多種型態

val t = ("x", true, 3): Tuple3[String, Boolean, Int]
println(t._1) // x
println(t._2) // true
println(t._3) // 3


// 解構賦值
val (s, b, i) = t
println(s) // x
println(b)// true
println(i) // 3

※scala 內鍵從 Tuple1~Tuple22,數字表示參數數量


// 匹配

val list = List(("a", 1), ("b", 2), ("c", 3), ("d", 4))
list.foreach(t => {
  t match {
case ("a", v) => println("a=" + v)
case (k, 2) => println(k + "=2")
// case k if k._1 == "c" => println(s"${k._1}=${k._2}")
case k => if(k._1 == "c") println(s"${k._1}=${k._2}")
case _ => println(".............")
  }
})

※如果匹配到多個會以第一個為主

※不用也不可寫 break 和 continue,只會判斷一個,但下面有個例外

※=> 前面還可以寫 if,再做一次判斷,圓括號可省略

※如果 if 寫在 => 前面,當沒有匹配到時,會再去 _ 的判斷式裡,如果沒有寫 _ 判斷式,會報 scala.MatchError;但 if 寫在 => 後面,沒判斷到就 continue 了

// for 迴圈

val list = List(("a", 1), ("b", 2), ("c", 3), ("d", 4))
for ((x, y) <- list) {
  println(x + y) // a1 b2 c3 d4
}

2019年9月4日 星期三

Scala 2.x 三 Function

※Function

類似 java 的 lambda
val showNum = () => 9
println(showNum()) // 9

val add = (x: Int) => x + 1
println(add(7))

val add2 = (x: Int, y: Int) => x + y
println(add2(1, 2)) // 3

※分別為 0~2 個參數


※傳 Function

val n = Seq(9, 7, 3)
val func = (x: Int) => x * 2

println(n.map(func))
println(n.map((x: Int) => x * 2))
println(n.map((x) => x * 2))
println(n.map(x => x * 2))
println(n.map(_ * 2))

※以上 5 種寫法都是一樣的



※接收 Function

def plusOne1(list: List[Int]): List[Int] =
list.map(n => n + 1)

def substractOne1(list: List[Int]): List[Int] =
list.map(n => n - 1)

def multyDouble1(list: List[Int]): List[Int] =
list.map(n => n * 2)


def plusOne2(list: List[Int]): List[Int] =
changeNumber(list, n => n + 1)

def substractOne2(list: List[Int]): List[Int] =
changeNumber(list, n => n - 1)

def multyDouble2(list: List[Int]): List[Int] =
changeNumber(list, n => n * 2)

/*private*/ def changeNumber(list: List[Int], calc: Int => Int): List[Int] =
list.map(calc)


val list = List(1,2,3);
println(plusOne1(list))
println(substractOne1(list))
println(multyDouble1(list))
println(changeNumber(list, n => n * 9))

※原本的 3 個 Xxx1 方法,回傳時,只有算法不同,所以可以提出變成公用的 changeNumber



※回傳 Function

def urlBuilder(ssl: Boolean = false, domainName: String): (String, Int) => String = {
  val protocol = if (ssl) "https://" else "http://"
  (endpoint: String, pageNum: Int) => s"$protocol$domainName/$endpoint?page=$pageNum"
}

val getURL = urlBuilder(ssl = true, domainName = "www.google.com")
println(getURL("queryList", 10))

※這是修改官方的例子,回傳接收兩個參數,String 和 Int,並回傳 String

2019年9月3日 星期二

Scala 2.x 二 Trait、Object、正則

※Trait

trait 類似 java 的 interface

trait Animal {
  def setName(name: String): Unit
  def getName(): Unit = print("taipei zoo")
}

class Cat extends Animal {
  override def setName(name: String): Unit = {
    println(name)
  }
}

val c = new Cat()
c.setName("tiger")
c.getName()

※也可以直接在 trait 裡實作


※加泛型

trait Add[I1,I2,S] {
  def calc(p1: I1, p2: I2): S
}

class Operator extends Add[Int, Int, Int] {
  override def calc(p1: Int, p2: Int): Int = p1 + p2
}

val op = new Operator()
print(op.calc(9,7))


※多繼承

abstract class A {
  val msg: String
}
class B extends A {
  override val msg = "xxx"
}
trait C1 extends A
trait C2

class D extends B with C1 with C2

※和 java 一樣,extends 還是只能繼承一個,實作可多個,但關鍵字變成 with 了



※Object

類似 java 的 static

object Singleton {
private var count = 0
def instance(): Int = {
  count += 1
  count
}
}

val c1: Int = Singleton.instance()
println(c1) // 1
val c2: Int = Singleton.instance()
println(c2) // 2



※正則


// val regex = "\\d+".r
val regex = new Regex("\\d+")
println(regex.matches("123"))

regex.findFirstMatchIn("123") match {
  case Some(_) => println("good")
  case None => println("bad")
}

※字串加 .r 可以變成正則

Scala 2.x 一 基本常識、迴圈、Method、類別、Case

scala 是基於 java 的,所以 java 要先安裝好
scala 安裝完並設定環境變數後 (我安裝時已自動加了環境變數),在命令提示字元有 scala 可以用即可
IntelliJ -> File/Settings/Plugins,搜尋 scala 並安裝好重啟 Intellij
新增專案後,在專案按右鍵 Add Framework Support,選中 scala,這樣按右鍵就有新增 scala 的選項了

這系列文章使用的是 2.13.0 版做測試

※main 方法

object ScalaTest {
  def main(args: Array[String]): Unit = {
  println("xxx")
  }
}

※args 為參數名稱

※檔名為 ScalaTest.scala


※基本常識

.Value and Variable:
val 為常數,一開始一定要賦值,和 js 一樣
val i: Int = 1,: Int 是確定它的型態

var 宣告變數如可以改變用這個

沒有前|後置遞增|減
沒有 static
沒有 switch,但有 case



※型態的繼承

Nothing -> Byte、Short、Int、Long、Float、Double、Char、Boolean、Unit -> AnyVal -> Any
Nothing -> Null -> List、Option、CustomClass -> AnyRef(java.lang.Object) -> Any

※ java 的八種型態全有,但 Int 和 Char 取名不同,然後多一種 Unit,相當於 java 的 void

※型態轉換規則

1.Byte -> Short -> Int -> Long -> Float -> Double
2.Char -> Int
3.不可強轉

val x: Int = 123456
val y: Float = x
// val z: Long = y
// val z: Long = (Long)y
val z: Double = y

※註解的兩行會編譯錯譯,沒有什麼強轉的


※宣告陣列

// 不定長度
println(Array(1, 2, 3).apply(0))
println(Array("a", "b", "c").apply(0))

// 固定長度
var arr = new Array[String](3)
println(arr(0))
arr(0) = "s"
println(arr(0))

// 多維陣列
var arr3 = Array.ofDim[String](2, 3, 4) // 內鍵到五維
arr3(0)(0)(0) = "000"
println(arr3(0)(0)(0))
println(arr3.length) // 2
println(arr3(0).length) // 3
println(arr3(0)(0).length) // 4

// 固定範圍陣列
val ints = Array.range(5, 10)
for(n <- ints){
  println(n) // 5-9
}

val ints2 = Array.range(5, 17, 3)
for(n <- ints2){
  println(n) // 5 8 11 14
}


※迴圈

while、do...while、for,只有 for 和 java 不一樣,只有 java 的增強型 for 迴圈,但中間不是 :,是 <-,還多出一種迴圈 叫 until

val value = List(1,2,3)
// for(v <- value){
// for(v <- 1 to 10){
for(v <- 1 until 10){ // 不包含10
  println(v)
}

.多層迴圈

scala 的多層 for 迴圈可以一行就搞定,當然寫多行也是可以

for(i <- 1 to 9; j <- 1 to 9){
  println(i + "x" + j + "=" + i * j)
}


for(i <- 1 to 3; j <- 1 to 3; k <- 1 to 3 if j != 2){
  println(i + "-" + j + "-" + k)
}

※如上上兩層和三層的迴圈,後面還可以加 if


.儲存迴圈的值

var xxx = for{i <- 1 to 10 if i != 3; if i < 8}
  yield i

for(x <- xxx) {
  println(x) // 1 2 4 5 6 7
}


var xxx = for(i <- 1 to 10; j <- 1 to 3 if i != 3; if i < 8)
  yield (i,j)

for(x <- xxx) {
  println(x)
}

※不可以有實體 (花括號)


.中斷與繼續

break 和 continue,但 scala 沒有 continue

var loop = new Breaks;
for(v <- 1 until 10){
  if(v == 7) loop.break()
  println(v)
}


var loop = new Breaks;
for(v <- 1 until 10){
  loop.breakable {
if (v == 7) loop.break()
println(v)
  }
}

※new Breaks 後,break() 就是 break;breakable 就是 continue



※Method

格式為:def 方法名(方法參數): 回傳值 = {內容}

def showNum() = 9
println(showNum()) // 9

def showNum:Int = 9
println(showNum) // 9

def add(x: Int): Int = x + 1
println(add(7)) // 8

def add2(x: Int, y: Int):Int = x + y
println(add2(1, 2)) // 3

def add2(x: Int)(y: Int):Int = x + y
println(add2(1)(2)) // 3

※()可以用多個,但用多個時,呼叫也要用多個



※方法裡的方法


java 不允許方法裡再放方法,但 scala 可以

def accu(start:Int, end: Int): Int = {
if(start >= end) return 0
var vstart = start - 1

def accu2(acc: Int, end: Int): Int = {
  //println("acc=" + acc + ", vstart=" + vstart)
  if (vstart == end)
return acc
  else
vstart += 1
accu2(acc + vstart, end)
}
accu2(0, end)
}
println(accu(0, 10))

※加總的程式


※類別

object ScalaTest {
  def main(args: Array[String]): Unit = {
    val a = new A
    val b = new B()
    val c = new C
    val d1 = new D("k", 1)
    println(d1.toString)
    d1.customMethod(99)

    val d2 = new D("k")
    println(d2.toString)

    val e = new E(value=1)
    println(e.toString)
  }

  class B() {}
  class C
  class D(var key:String, value:Int = 0) {
    override def toString: String = s"($key, $value)"

    def customMethod(xx: Int): Unit = {
      println(xx)
    }
  }
  class E(var key:String="xxx", value:Int) {
    override def toString: String = s"($key, $value)"
  }
}
class A {}

※也有內部類別,類別和建構子整合在一起,如果是空的參數,可以省略圓括號

※建構子可以有預設值,這樣就有 overloading 了

※使用 s"($xxx)" 可取得參數值

※class E,第一個建構子有預設值,如果第一個參數給 null,就真的抓到 null 了

※建構子中,有 val、var 時,new 完才可看見,如果不寫就是 private



※訪問修飾子

public、protected、private
以 java 來對比的話,沒有預設不寫的修飾子
protected 只有子類別可訪問,同包不行,這點和 java 不同

.作用域
可以在修飾子後面用方括號包起一個類別
如 private[.ooo.Xxx] :表示除了 Xxx 這個類別外,其他都是 private 的




※setter/getter

class Xxx {
  private var pid: Int = 0

  def id = pid
  def id_= (id: Int): Unit = {
this.pid = id
  }

  // def setId(id: Int): Unit = {
  //   this.id = id
  // }
  //

  // def getId() :Int = {
  //   this.id
  // }

  private def add(x: Int): Int = x + 1
}

val x = new Xxx()
x.id = 999
println(x.id)

※註解的部分是 java 的寫法,也是可以用

※setter 宣告方法:
1.宣告一個外面看不見的變數 (pid)
2.宣告一個外面看的見的變數 (id),這樣外面才可以用這個名稱,
3.然後再宣告一個方法,是 2 的後面加 _=,在 scala 表示 setter

※getter 就是變數名稱 (pid)

※沒有特別寫修飾子,預設是 public,但這個字不能寫,會編譯錯誤



※case

類似 java 的 switch

def xxx(str: String):Int = str match {
case "a" => 1
case "b" => 2
// case _ => 3
}
println(xxx("c"))

※匹配 _ 就是當什麼都匹配不到時用的,此例會報錯,必須將註解打開

※不需要也不能寫 break


※case class

在 scala 可以將 case 用在 class

case class Animal(id: Int, name: String)

val a1 = Animal(1, "dragon")
val a2 = Animal(2, "dragon")
val a3 = Animal(1, "dragon")
val a4 = new Animal(1, "dragon") // 可省略 new
println(a1 == a2) // false
println(a1 == a3) // true
println(a1 == a4) // true


.迴圈判斷

val list = List(
Animal(1, "pig"),
Animal(2, "dog"),
Animal(3, "chicken")
)

for(l <- list) {
l match {
case Animal(1, "pig") => println("I'm pig")
case Animal(2, "dog") => println("I'm dog")
case _ => println("Who am I")
}
}

2019年9月2日 星期一

java 11 功能

※HttpClient

org.apache.http.client.HttpClient.HttpClient;
java.net.HttpURLConnection;
java.net.http.HttpClient;

apache 也有 HttpClient,但聽說效能沒 HttpURLConnection 好,所以就越來越少人用了,但這一版新增了替代的抽象類別,名字也叫 HttpClient,可以支援 http 2.0 的,至於寫法,API 已有提供範例


※Optional 增加 1 個方法

.isEmpty
Optional<String> op = Optional.ofNullable(null);
if (op.isEmpty()) {
System.out.println("空的");
}



※String 新增 6 個方法

.strip、stripLeading、stripTrailing
System.out.println(" \t \n \r".strip().length()); // 0,去掉空格(全半形)
String s = " a\t \n \ra  ";
System.out.println(s.stripLeading().length()); // 9,去掉前空格(全半形)
System.out.println(s.stripTrailing().length()); // 8,去掉後空格(全半形)

.isBlank
System.out.println("".isBlank()); // true
System.out.println(" \t \n \r".isBlank()); // false

.repeat、lines
System.out.println("abc".repeat(3)); // abcabcabc
System.out.println("asdf\r\nzxcv\r\n".lines().count()); // 2
System.out.println("asdf\r\nzxcv\r\nqwer".lines().count()); // 3
lines() 回傳 Stream<String>

2019年9月1日 星期日

java 10 功能

※var

var i = 11;
只能寫在區域變數且一定要賦非 null 的值
var j = new int[]{1,2,3}; // new int[] 不能省略
可以寫在 lambda 的參數,但 lambda 本來就可以省略,多此一舉
可以寫在迴圈,新舊都可以
不能寫在方法的參數和回傳值


※Optional 增加 1 個方法

.orElseThrow
Integer integer = Optional.ofNullable(1).orElseThrow(NullPointerException::new);

如果是 null 就會報 orElseThrow 裡寫的 exception



※List.copyOf

List<Integer> list1 = List.of(1, 2, 3);
List<Integer> copyList1 = List.copyOf(list1);
System.out.println(list1 == copyList1); // true

List<Integer> list2 = Stream.of(1, 2, 3).collect(Collectors.toList());
List<Integer> copyList2 = List.copyOf(list2);
System.out.println(list2 == copyList2); // false

※此方法只會複製不可變的,所以如果本來就是不可變的,底層會判斷能不能 instanceof成 AbstractImmutableList,傳回來會不一樣

※copyOf 是淺複製

class Animal {
    private int id;

// getter/setter

    public Animal(int id) {
        this.id = id;
    }
}

List<Animal> animals = List.of(new Animal(1), new Animal(2), new Animal(3));
List<Animal> copyAnimals = List.copyOf(animals);
System.out.println(animals == copyAnimals); // true

animals.forEach(a -> System.out.println(a.getId())); // 1 2 3
animals.get(0).setId(3);
animals.forEach(a -> System.out.println(a.getId())); // 3 2 3
copyAnimals.forEach(a -> System.out.println(a.getId())); // 3 2 3
System.out.println(animals == copyAnimals); // true

Java 9 功能

※Module



新建一個專案,然後增加兩個 module,如上圖,Module1 和 Module2
在 Module1 增加 class M1;Module2 增加 class M2
M1 想用 M2 預設是沒辦法的,所以在兩個 module 的 src 按右鍵增加 module-info

1.M2 要給別人用,所以要匯出,都是以 package 為單位
module Module2 {
    exports xxx.ooo;
}

2.M1 要用別人的,所以要要求使用,如果要用 java 內鍵的,按下 content assist 會有提示
module Module1 {
    requires Module2;
}

3.此時還是會編譯錯誤,還得如下使用:

4.好文章


※介面可用 private 方法


※String 和 AbstractStringBuilder 

從 char[] 改成 byte[],AbstractStringBuilder 是 StringBuffer 和 StringBuilder 的抽象父類別


※jshell

java 的 shell,在安裝目錄的 bin 裡,每次要測簡單的東西,都要寫 class,然後在寫個 main 方法是很累人的,所以直接在裡面打即可
1.除了 java.lang 外,打 /imports 是預設有 import 的
2.不用 try catch
3.屬性和方法會後者蓋前者



※省略泛型和增強 try-with-resource

new Comparable<>(){
@Override
public int compareTo(Object o) {
return 0;
}
};

※在 java 7 就可以省略 <> 裡的東西了,但匿名類別不行,在這一版可以了


InputStreamReader isr = new InputStreamReader(System.in);
try(isr) {

} catch(Exception e) {

}

※在 java 7 就可以有 try () 了,但宣告都要在裡面才行,在這一版可以寫在外面,裡面寫變數名稱即可

※不能改 try 裡的東西,裡面是 final 的



※不可變的可讀集合

List.of(1, 2, 3, 4, 5);
Set.of(1, 1, 2, 2, 3);
Map.of("a", 1, "b", 2);
Map.ofEntries(Map.entry("c", 3), Map.entry("d", 4));



※Optional 增加 3 個方法

.stream
List<Integer> list = List.of(1, 2, 3);
Optional<List<Integer>> oList = Optional.ofNullable(list);
Stream<List<Integer>> stream = oList.stream();

※回傳的和 Optional 的泛型一樣

.or
Optional<Integer> op = Optional.ofNullable(null);
op = op.or(() -> Optional.of(7));
System.out.println(op.get());

※如果不是 null 就回傳,否則就回傳 7


.ifPresentOrElse
Optional<Integer> op = Optional.ofNullable(null);
op.ifPresentOrElse(x -> System.out.println("x=" + x), () -> System.out.println("hahaha"));

※回傳 x 或 hahaha


※Stream 增加 4 個方法

Stream.iterate(0, n -> n < 10, n -> ++n).forEach(System.out::println);

※這是個 overloading 方法,java 8 只有一個,要加 limit 才不會變成無限流,現在有了中間參數是 Predicate,可以不用 limit 了


Stream.of(1, 2, 6, 7, 4, 3).takeWhile(n -> n < 6).forEach(System.out::println); // 1 2
Stream.of(1, 2, 6, 7, 4, 3).dropWhile(n -> n < 6).forEach(System.out::println); // 6 7 4 3
takeWhile:從第一個開始判斷,如果條件成立( < 6),就抓,但只要條件不成立,馬上返回 (雖然後面也有條件成立的,但不管)

dropWhile:takeWhile 的相反,從第一個開始判斷,如果條件成立就刪除,但只要條件不成立,馬上返回 (雖然後面也有條件成立的,但不管)

Stream.of(null, null).count(); // 2 這個不是新增的,如果有兩個以上 (包括兩個) 的元素,不會報錯,但如果只有一個 null,會報空指針,所以可以用新方法 ofNullable,但回傳的是 0
Stream.ofNullable(null).count(); // 0



※ElementType 增加 MODULE

使用在 module-info 裡,例子可看這篇最下面