作者:京東科技 楊拓
一、倉頡介紹
倉頡是一種通用編程語言,適用于各種應(yīng)用開發(fā),兼顧開發(fā)效率和運行性能,其優(yōu)勢如下:
1、高效編程:語法簡明高效,支持多種范式編程。如插值字符串、主構(gòu)造函數(shù)、Flow 表達(dá)式、match、if-let、while-let 和重導(dǎo)出等,減少冗余書寫,提升開發(fā)效率。
2、輕量運行時庫:標(biāo)準(zhǔn)庫中除 core 包外(libcangjie-std-core < 1MB),其他包都可以按需鏈接加載。
3、輕量用戶線程:倉頡使用用戶態(tài)線程模型,每個線程都是輕量級的執(zhí)行實體,擁有獨立的執(zhí)行上下文并且共享內(nèi)存。創(chuàng)建線程內(nèi)存開銷僅 8KB,切換線程不進(jìn)入內(nèi)核態(tài),線程棧支持動態(tài)擴縮容,耗時納秒級。
4、輕量對象:每對象僅額外消耗一個機器字長記錄類型信息;不需要計數(shù)字段;GC 使用轉(zhuǎn)發(fā)表而非轉(zhuǎn)發(fā)指針。
5、類型安全:作為靜態(tài)強類型語言,倉頡通過編譯時類型檢查識別錯誤,降低運行時風(fēng)險,且具備強大的類型推斷能力,減少類型標(biāo)注工作。
二、快速環(huán)境配置
1、下載
在鴻蒙開發(fā)者網(wǎng)站找到下載中心,可以看到DevEco倉頡插件,下載到本地。
2、安裝
打開DevEco Studio,進(jìn)入設(shè)置,在plugin標(biāo)簽下選擇從本地磁盤安裝插件,安裝后重啟IDE。
3、創(chuàng)建
操作與創(chuàng)建ArkTs工程一致。
三、特性介紹
1、Flow 表達(dá)式
Flow流表示對輸入進(jìn)行流式處理后得到輸出,在倉頡中流操作符有兩種:表示數(shù)據(jù)流向的中綴操作符 |> 和表示函數(shù)組合的中綴操作符 ~> 。
1.1、|>
表示對輸入數(shù)據(jù)做一系列的處理,可以使用 |> 來簡化。
語法形式就是e1 |> e2,其中 e2 是函數(shù)類型的表達(dá)式,e1 的類型是 e2 的參數(shù)類型的子類型.
示例1: let gen: Array = [] gen |> forEach{a: Int64 => println("${a}")} 示例2: func split(words: Array, separator: Rune): Array { words |> map { text => text.split(String(separator), removeEmpty: true) } |> flatten |> collectArray } //示例3: 處理 Unicode 字符串 func split_unicode(text: String, sep: String) { let indices = ArrayList() text.runes() |> enumerate |> fold(false) { state, e => let current = !sep.contains(e[1].toString()) if (state != current) { indices.append(e[0]) } current } |> { valid: Bool => if (valid) { indices.append(text.size) } } let runes = text.toRuneArray() let result = ArrayList() for (i in 0..indices.size:2) { result.append(String(runes[indices[i]..indices[i + 1]])) } return result }
1.2、~>
當(dāng)兩個單參函數(shù)進(jìn)行組合時可以使用~>。
//示例: func f(x: Int64): Float64 { Float64(x) } func g(x: Float64): Float64 { x } //等價于{ x: Int64 => g(f(x)) },會先對f求值,然后再對g求值,最后才會進(jìn)行函數(shù)的組合 var fg = f ~> g
建議以上兩個流操作符不要用于帶默認(rèn)值的命名參數(shù),因為配置了默認(rèn)值的命名參數(shù)必須給出命名實參才能用
2、變長參數(shù)
變長參數(shù)是各個語言都有的一種功能,但是在倉頡中當(dāng)形參最后一個非命名參數(shù)是 Array 類型時,就可以作為變長參數(shù)使用,不需要特殊的語法形式
func sum(arr: Array) { var total = 0 for (x in arr) { total += x } return total } main() { println(sum()) println(sum(1, 2, 3)) }
3、擴展
倉頡擴展提供了對類和接口進(jìn)行擴展的操作,不需要使用繼承或者裝飾器模式,就可以給類添加額外的屬性和方法。這種場景的優(yōu)勢在于不需要破壞被擴展類型的封裝性,就可以添加額外的功能。
可以添加的功能包括:
?添加成員函數(shù)
?添加操作符重載函數(shù)
?添加成員屬性
?實現(xiàn)接口
//示例1:為整型擴展兩個屬性 extend Int64 { public prop r: Index { get() { Index.Row(this) } } public prop c: Index { get() { Index.Col(this) } } } //調(diào)用 2.r //示例2:擴展靜態(tài)方法 extend Expression { static func fromTokens(tokens: List): Result { match (expressionFunc(tokens).map {t => t[0]}) { case Some(e) => Ok(e) case None => Err("Invalid Expression!") } } } //調(diào)用 Expression.fromTokens
4、if-let和while-let
倉頡中的條件控制和循環(huán)控制基本和ArkTs一致,除了將switch換為match外無區(qū)別,但是多了if-let和while-let兩個特性表達(dá)式。
4.1、if-let
if-let 表達(dá)式首先會對條件中 <- 右側(cè)的表達(dá)式進(jìn)行求值,如果此值能匹配 <- 左側(cè)的模式,則執(zhí)行 if 分支,否則執(zhí)行 else 分支。
main() { let result = Option.Some(2023) if (let Some(value) - result) { println("操作成功,返回值為:${value}") } else { println("操作失敗") } } 操作成功,返回值為:2023
倉頡沒有null判空,所以提供了option(T)來判空,它里面的取值就是some(v)和none兩種情況None為空,表示沒有賦值。some表示有賦值
4.2、while-let
while-let 表達(dá)式同if-let一樣,也是先會對條件中 <- 右側(cè)的表達(dá)式進(jìn)行求值,如果此值能匹配 <- 左側(cè)的模式,則執(zhí)行循環(huán)體,然后重復(fù)執(zhí)行此過程。如果模式匹配失敗,則結(jié)束循環(huán)。
public static func fromJson(r: JsonReader): FunctionCall { var temp_name: String = ""; var temp_arguments: String = ""; while (let Some(v) - r.peek()) { match(v) { case BeginObject =?> r.startObject(); while(r.peek() != EndObject) { let n = r.readName() match (n) { case "name" => temp_name = r.readValue(); case "arguments" => temp_arguments = r.readValue(); case unkow => println("unkow key ${unkow}"); } } r.endObject(); break; case _ => throw Exception("can't deserialize for FunctionCall"); } } return FunctionCall(temp_name, temp_arguments); }
5、線程
倉頡中創(chuàng)建一個線程非常簡單,只需要使用 spawn{} 即可開啟一個新的線程,{}里面就是在新線程中執(zhí)行的代碼,并且可以使用Future獲取線程執(zhí)行結(jié)果。
import std.sync.* import std.time.* //開啟一個線程 let future = spawn { task() } //執(zhí)行內(nèi)容 func task(): Int64 { for (_ in 0..M) { ..... } return n } //使用fut.get()可以等待線程執(zhí)行完成獲取線程執(zhí)行結(jié)果 future.get()
5.1、同步-原子操作
倉頡同樣也提供了多種同步機制來確保數(shù)據(jù)的安全,例如原子操作、互斥鎖和條件變量等
原子操作方面?zhèn)}頡提供整數(shù)類型、AtomicBool 類型和AtomicReference引用類型的原子操作來保證同步操作。
let num = AtomicInt64(0); let list = ArrayList>(); func testAtomic() { for (i in 0..100) { let fut = spawn { sleep(Duration.millisecond) num.fetchAdd(1) } list.append(fut) } for (f in list) { f.get() } let result = num.load() AppLog.info("result = ${result}")//輸出100 }
5.2、同步-可重入互斥鎖 ReentrantMutex
可重入互斥鎖可以保證在任意時刻最多只有一個線程執(zhí)行區(qū)塊的代碼,當(dāng)一個線程嘗試獲取被其他線程持有的鎖會被阻塞,直到別的線程釋放鎖才可以執(zhí)行區(qū)塊代碼。使用ReentrantMutex需要注意以下兩點:
1.在訪問共享數(shù)據(jù)之前,必須嘗試獲取鎖。
2.處理完共享數(shù)據(jù)后,必須進(jìn)行解鎖,以便其他線程可以獲得鎖。
import std.sync.* import std.time.* import std.collection.* var num: Int64 = 0; let list = ArrayList>(); let lock = ReentrantMutex() func task() { sleep(Duration.millisecond) lock.lock() num++ lock.unlock() } func testMutex() { let list = ArrayList>() for (i in 0..100) { let fut = spawn {task()} list.append(fut) } for (f in list) { f.get() } AppLog.info("result = ${num}") //輸出100 }
在日常使用中需要手動unlock相當(dāng)不方便,而且也有可能在異常情況下鎖無法釋放的問題,為了解決這些問題,倉頡又提供一個 synchronized 關(guān)鍵字,搭配 ReentrantMutex 一起使用。
具體使用方式就是在 synchronized 上加一個 ReentrantMutex 對象即可,然后將同步代碼寫在synchronized{}中。一個線程在進(jìn)入 synchronized 修飾的代碼塊之前,會自動獲取 ReentrantMutex 實例對應(yīng)的鎖,如果無法獲取鎖,則當(dāng)前線程被阻塞;而線程在退出 synchronized 修飾的代碼塊之前,會自動釋放該 ReentrantMutex 實例的鎖。
import std.sync.* import std.time.* import std.collection.* var num: Int64 = 0; let list = ArrayList>(); let lock = ReentrantMutex() func task() { sleep(Duration.millisecond) // lock.lock() //跟上面的示例一樣,省去了加,釋放鎖的操作 synchronized(lock) { num++ } // lock.unlock() } func testMutex() { let list = ArrayList>() for (i in 0..100) { let fut = spawn {task()} list.append(fut) } for (f in list) { f.get() } AppLog.info("result = ${num}") //輸出100 }
四、和ArkTs互操作
現(xiàn)在一般的場景是在已有ArkTs庫中使用倉頡,所以可以將倉頡代碼封裝為ArkTs庫,提供給外部使用。
原理就是互操作宏解析被注解修飾的倉頡代碼,會自動生成ArkTs聲明文件和互操作層代碼。
使用步驟:
1.在cj文件中,針對 class、interface 和函數(shù),使用 @Interop[ArkTS] 進(jìn)行修飾,被修飾的對象是希望被 ArkTS 調(diào)用的。
2.在 DevEco Studio 中的倉頡文件或者 module 名稱右鍵選擇“Generate Cangjie-ArkTS Interop API”,會在 cangjie 目錄下生成 ark_interop_api 的聲明文件。
3.ArkTS 側(cè)添加依賴并 import ark_interop_api 即可使用。
倉頡代碼:
import ohos.ark_interop.* import ohos.ark_interop_macro.* @Interop[ArkTS] public func sub(a: Int64, b: Int64): Int64 { return a - b } @Interop[ArkTS] public class CjDemo { public let name: String @Interop[ArkTS, Invisible] public var id: Float64 = 1.0 public init(str: String) { name = str } public func add(a: Int64, b: Int64): Int64 { return a + b } public func foo(): Float64 { return 1.0 } }
生成的代碼:
export declare class CjDemo { name: string add(a: number, b: number): number foo(): number } export declare interface CustomLib { sub(a: number, b: number): number CjDemo: {new (str: string): CjDemo} }
使用:
let cjLib : CustomLib = requireCJLib("libohos_app_cangjie_entry.so") as CustomLib console.log("result" + cjLib.sub(2, 1)) let class1: CjDemo = new cjLib.CjDemo("arkts call") console.log("result " + class1.add(5,1))
五、后續(xù)規(guī)劃
鴻蒙應(yīng)用開發(fā)官方目前提供兩種編程語言供選擇:ArkTs和倉頡。從當(dāng)前趨勢來看,這兩種語言是并行發(fā)展的,尚不存在某一方被替代的情況,因此對當(dāng)前的開發(fā)工作沒有影響,倉頡可以作為技術(shù)儲備加以學(xué)習(xí)和掌握。
在開發(fā)初期,我們?nèi)渴褂昧薃rkTs。然而在實際開發(fā)過程中,我們發(fā)現(xiàn)了一些痛點:
某些ArkTs的官方API存在性能問題,使得我們在進(jìn)行性能優(yōu)化時某些關(guān)鍵點較依賴系統(tǒng)發(fā)版。
ArkTs提供了TaskPool和Worker兩種線程調(diào)用方式,但編寫過程較為繁瑣,線程間的數(shù)據(jù)傳遞存在限制且有性能損耗。
我們計劃利用倉頡的優(yōu)勢來解決這些問題,以打造更為健壯的鴻蒙版京東金融應(yīng)用。
審核編輯 黃宇
-
編程語言
+關(guān)注
關(guān)注
10文章
1951瀏覽量
35027 -
倉頡
+關(guān)注
關(guān)注
0文章
16瀏覽量
64
發(fā)布評論請先 登錄
相關(guān)推薦
鴻蒙OpenHarmony【標(biāo)準(zhǔn)系統(tǒng)編寫“Hello World”程序】 (基于RK3568開發(fā)板)
![<b class='flag-5'>鴻蒙</b>OpenHarmony【標(biāo)準(zhǔn)系統(tǒng)編寫“<b class='flag-5'>Hello</b> <b class='flag-5'>World</b>”程序】 (基于RK3568開發(fā)板)](https://file1.elecfans.com/web2/M00/C5/CD/wKgZomYCdwyAIFf5AAB_7E1pFms943.jpg)
鴻蒙Harmony是如何影響Android工程師的呢?
【HarmonyOS HiSpark Wi-Fi IoT 套件試用連連載】我和鴻蒙的親密接觸----hello_world
【HarmonyOS HiSpark Wi-Fi IoT 套件試用連載】+打印hello world
鴻蒙系統(tǒng)和安卓系統(tǒng)下的京東有什么不一樣
鴻蒙的第一個世界版Hello World
![<b class='flag-5'>鴻蒙</b>的第一個世界版<b class='flag-5'>Hello</b> <b class='flag-5'>World</b>](https://file.elecfans.com/web1/M00/DB/70/o4YBAGAI4ceANpLkAAPuoBaeRhA092.png)
在現(xiàn)有安卓應(yīng)用的基礎(chǔ)上擴展鴻蒙的特性
![在現(xiàn)有安卓應(yīng)用的基礎(chǔ)上擴展<b class='flag-5'>鴻蒙</b>的<b class='flag-5'>特性</b>](https://file.elecfans.com/web2/M00/04/AC/poYBAGDbzAyALnG6AAANZlrygO4786.jpg)
如何在鴻蒙開發(fā)板上輸出Hello World
京東與華為鴻蒙合作 京東啟動鴻蒙原生應(yīng)用開發(fā)
京東金融APP的鴻蒙之旅系列專題 新特性篇:意圖框架接入
![<b class='flag-5'>京東</b><b class='flag-5'>金融</b><b class='flag-5'>APP</b>的<b class='flag-5'>鴻蒙</b><b class='flag-5'>之旅</b><b class='flag-5'>系列</b><b class='flag-5'>專題</b> 新<b class='flag-5'>特性</b><b class='flag-5'>篇</b>:意圖框架接入](https://file1.elecfans.com//web2/M00/0A/5B/wKgaomcM4AWAJr3XAANVx6gTdU0985.png)
京東金融APP的鴻蒙之旅系列專題 鴻蒙工程化:Hvigor構(gòu)建技術(shù)
![<b class='flag-5'>京東</b><b class='flag-5'>金融</b><b class='flag-5'>APP</b>的<b class='flag-5'>鴻蒙</b><b class='flag-5'>之旅</b><b class='flag-5'>系列</b><b class='flag-5'>專題</b> <b class='flag-5'>鴻蒙</b>工程化:Hvigor構(gòu)建技術(shù)](https://file1.elecfans.com//web2/M00/09/70/wKgZomcM4EKAOLPsAANVx6gTdU0957.png)
Taro鴻蒙技術(shù)內(nèi)幕系列(一):如何將React代碼跑在ArkUI上
![Taro<b class='flag-5'>鴻蒙</b>技術(shù)內(nèi)幕<b class='flag-5'>系列</b>(一):如何將React代碼跑在ArkUI上](https://file1.elecfans.com//web1/M00/F3/6C/wKgaoWcXQA-AX6UzABGMbiseADc620.png)
評論