Tag coding
多執行緒(Concurrency)與平行處理(Parallelism)
進程(Process) 使用系統資源執行程式的實體 電腦中執行程式的實體 每個進程都互相獨立 Process 是執行緒的容器 Process 會佔用系統資源 多工作業系統中可以同時執行數個 Process,但單個 CPU 一次只能執行一個 Process 線程、執行緒(Thread) 同個 Process 裡有至少個 Thread 同個 Process 裡的 Thread 共享系統資源 在多執行緒(Multithreading)環境中若兩個以上的執行緒對同一個變數進行改動可能產生死結(Deadlock) 多執行緒(Concurrency) 在同一進程的多執行緒環境中,把工作拆成數個子集,利用不同的執行緒分別完成每個子集。 平行處理(Parallelism) 透過負載平衡機制把數個工作分配到不同的工作單元裡「同時」進行。 參考文章 Concurrency 與 Parallelism 的不同之處 Concurrency vs. Parallelism — A brief view
Go | 型別(Type)
Go 的型別(Type)可以讓編譯器知道兩樣資訊: 需要使用的記憶體大小 這些記憶體所代表的內容 以內建的型別為例: 型別 記憶體大小 內容 int64 8 bytes 整數 float32 4 bytes IEEE-754 浮點數 bool 1 byte true OR false 有一些型別所代表的內容會跟著 build 機器的不同架構有所差異,例如同樣的 int 在 64 位元電腦裡佔 8 bytes 但在 32 位元電腦裡只佔 4 bytes。 自定義型別 Go 容許自定義型別,最簡單的宣告方式為 struct type user struct { name string email string ext int isAdmin bool } 使用 var ravi user 可以宣告一個 ravi 的變數,代表一個 user 型別。宣告的當下,型別的欄位值會使用各欄位之型別的零值。
Go | Map
Go 的 map 是鍵值對的資料結構。 // 宣告一個空白 map colors := map[string]string{} // 加入或修改鍵值對 colors["Yello"] = "#cc8500" // 刪除鍵值(鍵不存在時並不會噴錯誤) delete(colors, "Red") // 宣告一個空 map var numbers map[int]string numbers[1] = "One" // panic: runtime error: assignment ot entry in nil map 檢查鍵是否存在,如果指定的鍵不存在,會回傳零值給 value value, exists := colors["Blue"] if exists { fmt.Println(value) } else { fmt.Println(value) // "" } 迭代 map numbers := map[int]string{ 1: "one", 2: "two", 3: "three", } for key, value := range numbers { fmt.
Go | 切片(Slice)
切片(Slice)是操作其指向之陣列(Array)的物件。 [指標|長度|最大長度] // 切片 | [1,2,3,4,5] // 陣列 切片的初始化 // 宣告一個長度與最大長度都是 5 的字串陣列 slice1 := make([]string, 5) // 宣告一個長度為 3 最大長度為 5 的整數陣列 slice2 := make([]int, 3, 5) 長度不能大於最大長度,不然編譯器會噴錯誤。一般來說,宣告切片的同時也會同時賦值: // 宣告一個長度與最大長度為 3 的切片 slice1 := []string{"One", "Two", "Three"} // 宣告一個長度與最大長度為 100 的切片 slice2 := []string{99: ""} 空切片(nil slice)與空白切片(empty slice) 不管是空切片還是空白切片,標準函式庫裡的 append 、 len 跟 cap 的使用行為都一樣。 空切片(nil slice) 在 Go 程式中,空切片很常出現,通常被當成不存在資料時的回傳值,例如 return nilSlice, error。 空切片的宣吿方式: var slice []int 空切片長成這樣: 位置 0 1 2 用途 指標 長度 最大長度 值 nil 0 0 空白切片(empty slice) 空白切片通常是用來表達零資料集合,例如查詢資料庫後回傳零筆資料。空白切片的指標指向空陣列,所以並不會另外佔用記憶體。
Go | 陣列(Array)
Go 的陣列是連續空間內儲存了固定數目之相同型別物件的容器。當使用 var array [5]int 宣告時,陣列內儲存的型別或者長度都不能再改變,如果需要更多的空間來儲存新的物件,需要宣告一個新的陣列後把原先的值搬過去新的陣列。 在 Go 語言中宣告新的變數且無賦值時,這些變數所指向的物件都會以其零值(Zero Value)初始化。有關於 Go 的零值,請參考這邊。常用的零值: 類型 零值 數值 0 布林值 false 字串 "" 初始化陣列的同時賦值 // 宣告一個 array1 變數的 int 陣列,長度為 5 且值依序分別為 1,2,3,4,5 array1 := [5]int{1,2,3,4,5} // 宣告一個 array2 變數的 int 陣列,長度為 5 且值依序分別為 0,1,2,0,0 array2 := [5]int{1: 1, 2: 2} // 宣告一個 array3 變數的 int 陣列,長度為取決於賦值內容 // 如以下 array3 的長度為 3 array3 := [.
InputStream 、 DataInputStream 與 BufferedInputStream
Java 有很多不同的 InputStream 類別,老是搞不清楚,InputStream、DataInputStream跟BufferedInputStream最近出現在我看的書的練習題裡,一些從 Java Doc (1.8) 文件中拉出來的相關連結: Closeable FilterInputStream InputStream DataInputStream BufferedInputStream InputStream是抽象類別,實踐了Closeable,所以可以當成try with resource裡的資源。類別方法裡定義了InputStream.nullInputStream()可以產生空的InputStream。另外也規範所有實作子類別必須提供public int read() throws IOException方法來回傳下一個 byte 的內容。 FilterInputStream在生成時吃進InputStream後把InputStream存起來,並且覆寫了所有InputStream裡的方法,大部分覆寫的方法都是把原來的操作轉到物件生成時丟進去的InputStream上,以供更細分的子類別使用,例如: public int read(byte b[], int off, int len) throws IOException { return in.read(b, off, len); } DataInputStream跟BufferedInputStream都進一步繼承FilterInputStream。DataInputStream主要是從InputStream直接讀入 Java 的 primitive data type。 BufferedInputStream在內部會產生一個緩衝陣列(buffer array)來支援mark跟reset方法,透過額外使用的緩衝空間來先讀入資料,以優化資料讀入的效能。
泛型 Java Functional Interface 的特規化
如果某個特殊型別的 Functional Interface 常常被使用時,有時候直接宣告型別會比較方便引用。可以透過兩種方式來做特規,介面與抽象類別: Generic Functional Interface @FunctionalInterface public interface TwoArgsProcessor<X> { X process(X arg1, X arg2); } 介面(Interface) @FunctionalInterface public interface TwoIntsProcessor extends TwoArgsProcessor<Integer> { } 抽象類別(Abstract Class) abstract class TwoIntsProcessorAbstract implements TwoArgsProcessor<Integer> { }
使用複合條件來判斷是否進入下一輪 Java 迴圈
在 Java 迴圈寫法中,假如我想要透過一個外部判斷來提前中斷這個迴圈,之前我習慣寫: public class Test { public static void main(String[] args) { int [] ints = {1,2,3,4,5}; boolean continueCond = true; for (int i=0; i<ints.length; i++) { continueCond = ints[i] < 4; if (!continueCond) break; System.out.println(ints[i]); } } } 但這樣寫其實有點冗,今天看到在 Functional Interfaces in Java 裡的一個範例寫法: public class Test { public static void main(String[] args) { int [] ints = {1,2,3,4,5}; boolean continueCond = true; for (int i=0; i<ints.length && continueCond; i++) { continueCond &= ints[i] < 4; System.
Windows 裡的類 homebrew 工具: scoop.sh
新工作的環境是 Windows 系統,所有的開發工具生態系都要重新摸索。剛開始發現可以用 Git for Windows 裝好後的 console 下平常習慣的 bash 指令後,努力想要自己寫一些 shell script 去自動化開發工具跟環境變數的初始化,簡單的情境下好似都還堪用。 可是如果要安裝的套件都要一個一個手刻 script 好像又有些白癡。 因緣際會下被提點去找其他平台上的類 Homebrew 方案,權衡系統權限低落等實際情況下,好像也只剩 scoop.sh 可以用了。 搞了一整天反覆試驗了一陣子,總算是把裝機清單給列了出來,放在這裡。雖然還是要手動輸入指令,但是可以統一管理這些套件,用指令反安裝等已經比之前的原始人狀態進步多了。 被 oh my zsh 慣壞眼睛的我,有幸發現可以用 concfg 稍微把 PowerShell 的顏色弄得順眼一點。所謂山不轉路轉,路不轉人轉,接下來還需要努力爬行。(握拳)
在 RSpec 裡測試 Rake Task
最近被 Rake Task 的測試設定搞得一頭霧水,簡單記錄一下測試 Rake Task 的測試設定,以及各種鬼打牆的血淚史: TL;DR 為了避免各種 task 載入、執行狀態等相互干擾導致 test case 會偶發性失敗,每一次執行 test case 時就去做「載入」、「卸載」會比較沒有鬼打牆狀況出現。 # spec_helper.rb config.before(:each, rake: true) do Rails.application.load_tasks end config.after(:each, rake: true) do Rake::Task.clear end 把 Rails.application.load_tasks 放在 before(:each) 確保每次載入 test case 前都有正確 load 到要測試的那隻 task ,並在 after(:each) 時用 Rake::Task.clear 去清空剛剛載入的 Rake Task。 透過 rake: true 這個 flag 可以避免其他不相關的單元測試也去載入 Rake Task。 透過每個 each 都做載入跟卸載 Rake Task 後,就可以在 test case 單純使用 Rake::Task['task_name'].invoke 來手動執行 Task 而不用另外去作載入或卸載。