華為倉頡語言的函數初步

華為倉頡語言的函數初步

函數是一段完成特定任務的獨立代碼片段,可以通過函數名字來標識,這個名字可以被用來調用函數。

要特別注意,與C/C++、Python等語言不同,倉頡禁止參數重新賦值——函數參數均為不可變(immutable)變量,在函數定義內不能對其賦值。也就是說,倉頡函數參數與 let 聲明的變量一樣,只能讀取,不能再次賦值。例如:
func demo(x: Int64): Int64 {
x = x + 1 ? // ? 編譯錯誤
return x
}

如果需要修改,只能引入新的局部變量:
func demo(x: Int64): Int64 {
let y = x + 1 ? // ? 正確
return y
}

這說明,倉頡語言語言設計決策強化對不可變性和函數式編程范式的偏好。

函數定義

基本語法:倉頡語言使用func關鍵字來定義函數,后跟函數名、參數列表、可選的返回值類型以及函數體。其基本語法為:

func 函數名(參數列表): 返回值類型 {

??? // 函數體

??? return 返回值

}

參數列表:函數可以有 0 個或多個參數,參數分為非命名參數命名參數。非命名參數定義為p: T,如a: Int64;命名參數定義為p!: T,如a!: Int64,且只能為命名參數設置默認值,非命名參數不能設置默認值,參數列表中非命名參數只能定義在命名參數之前。

返回值類型:必須顯式寫出返回類型,除非返回Unit(可以省略)。

函數體定義在一對花括號內。函數體中定義了函數被調用時執行的操作,通常包含一系列的變量定義和表達式,也可以包含新的函數定義(即嵌套函數)。

函數定義示例

func add(a: Int64, b: Int64): Int64 {
return a + b
}

如果函數體是單個表達式,倉頡也支持隱式返回(省略 return 關鍵字):

func add(a: Int64, b: Int64): Int64 {
a + b ?// 最后一行表達式的值即為返回值
}

函數參數示例

非命名參數 (Non-named Parameters): 即普通參數,調用時按順序傳遞。非命名參數必須定義在命名參數之前。例如:

// 定義一個包含兩個非命名參數的函數
func multiply(a: Int64, b: Int64): Int64 {return a * b
}main() {// 調用函數(必須按順序傳遞參數,無需指定參數名)let result = multiply(3, 4)  // 正確調用,返回12println(result) 
}

命名參數 (Named Parameters): 在參數名后加 !,調用時需指定參數名,并可提供默認值,提高了代碼可讀性和靈活性。例如:

// 定義包含命名參數的函數(帶默認值)
func calculateTotal(price: Int64, quantity!: Int64 = 1, discount!: Float64 = 0.0): Int64 {
let total = Float64(price * quantity) ? ? ?// 先轉成 Float64
let discounted = total * (1.0 - discount) ? ?// 浮點運算
return Int64(discounted) ? ? ? ? ? ? ? ? ? ? // 再轉回整數
}

注意:

只能為命名參數設置默認值,不能為非命名參數設置默認值。

參數列表中可以同時定義非命名參數和命名參數,但是需要注意的是,非命名參數只能定義在命名參數之前,也就意味著命名參數之后不能再出現非命名參數。例如,下例中 add 函數的參數列表定義是不合法的:

func add(a!: Int64, b: Int64): Int64 { // 錯誤!
return a + b
}

調用函數規則

a.非命名參數調用

調用規則:必須按定義順序傳遞,不能指定參數名。

b.命名參數調用

必須用 參數名: 值 形式傳遞,順序可任意調整,且支持使用默認值。

c.混合參數調用(非命名 + 命名)

非命名參數必須先定義、先傳遞,命名參數在后,且命名參數必須指定參數名。

示例:

// 定義包含命名參數的函數(帶默認值)
func calculateTotal(price: Int64, quantity!: Int64 = 1, discount!: Float64 = 0.0): Int64 {let total = Float64(price * quantity)      // 先轉成 Float64let discounted = total * (1.0 - discount)    // 浮點運算return Int64(discounted)                     // 再轉回整數
}main() {// 調用方式1:只傳非命名參數(price),命名參數用默認值let total1 = calculateTotal(100)  // quantity默認1,discount默認0 → 結果100println(total1)// 調用方式2:指定部分命名參數(可調整順序)let total2 = calculateTotal(100, discount: 0.2, quantity: 2)  // 計算:100 * 2 * (1-0.2) = 160 → 結果160println(total2)// 調用方式3:顯式指定所有參數名let total3 = calculateTotal(200, quantity: 3, discount: 0.1)  // 計算:200 * 3 * 0.9 = 540 → 結果540println(total3)
}

返回值類型的說明

在 倉頡語言(Cangjie) 的正式規范中:

必須顯式寫出返回類型,當返回類型為 Unit 時可省略 -> Unit。

// ? 合法

func add(a: Int64, b: Int64): Int64 { a + b }

// ? 不合法- 函數體中有返回值的表達式時必須聲明返回類型

func add(a: Int64, b: Int64) { a + b }??

如果函數 沒有有意義的返回值(即返回 Unit),可以寫成 : Unit,也可以 直接省略 返回類型部分。

// 等價寫法

func log(msg: String) { println(msg) }??????? // 省略 : Unit

func log(msg: String): Unit { println(msg) }? // 顯式 : Unit

log("你好") //調用

單值返回和多值返回(Tuple)

單值返回示例:

// 返回單個 Int64
func square(n: Int64): Int64 {return n * n
}main(): Unit {let s = square(7)   println("square = ${s}")  //square = 49
}

多值返回(Tuple)示例:

// 返回一個二元組 (Int64, Int64)
func divmod(a: Int64, b: Int64): (Int64, Int64) {return (a / b, a % b)
}main(): Unit {let (q, r) = divmod(10, 3)   // q = 3, r = 1println("quotient = ${q}, remainder = ${r}")  //quotient = 3, remainder = 1
}

函數類型(Function Type)

倉頡編程語言中,函數是一等公民(first-class citizens),可以作為函數的參數或返回值,也可以賦值給變量。因此函數本身也有類型,稱之為函數類型。

函數類型由函數的參數類型和返回類型組成,參數類型和返回類型之間使用 -> 連接。參數類型使用圓括號 () 括起來,可以有 0 個或多個參數,如果參數超過一個,參數類型之間使用逗號(,)分隔。

函數類型是編程中一個比較抽象但極其強大的概念,是函數式編程范式(FP)的核心基石之一。

函數類型的語法非常直觀,遵循以下格式:

(參數1類型, 參數2類型, ...) -> 返回值類型

解釋一下:

? ?? 括號 ():里面放置函數的參數類型列表。如果沒有參數,就空著 ()。

? ?? 箭頭 ->:連接參數和返回值,讀作“返回”。

? ?? 返回值類型:在箭頭后面,指定函數返回的數據類型。

例如:

沒參數也要空括號:?() -> Unit

多個參數逗號隔:?(Int, String) -> Bool

返回元組括號包:?(Int, Int) -> (Int, Int)

函數類型既可以根據函數定義隱式存在,也可以由程序員在代碼中顯式地書寫出來。

1. 隱式的函數類型 (由函數定義產生)

例子:

// 【函數定義】

// 程序員寫的是具體的實現

func add(a: Int64, b: Int64): Int64 {

??? return a + b

}

// 【隱式的函數類型】

// 編譯器會自動識別出這個函數有一個類型:(Int64, Int64) -> Int64

// 這個類型是“依據函數定義存在的”,程序員沒有顯式寫出 `(Int64, Int64) -> Int64` 這幾個字。

2. 顯式的函數類型 (由程序員主動書寫)

你完全可以先寫出類型,再去找或定義一個符合該類型的函數(甚至用變量、lambda、函數值等)。

例子

// 1. 【顯式地用于變量聲明】

// 程序員主動寫下了類型注解 `: (Int64, Int64) -> Int64`

let myMathOperator: (Int64, Int64) -> Int64 = add // 將函數`add`賦值給變量

// 2. 【顯式地用于函數參數】

// 程序員定義了一個高階函數,它接受一個函數作為參數

// 參數 `operation` 的類型被顯式地定義為 `(Int64, Int64) -> Int64`

func calculate(operation: (Int64, Int64) -> Int64, x: Int64, y: Int64) -> Int64 {

??? return operation(x, y)

}

// 3. 【顯式地用于解決重載歧義】

func add(i: Int64, j: Int64) -> Int64 { i + j }

func add(i: Float64, j: Float64) -> Float64 { i + j }

// 這里直接寫 `add` 編譯器不知道選哪個,產生歧義

// let f = add // Error!

// 程序員通過【顯式地書寫類型】來告訴編譯器需要哪個函數

let f: (Int64, Int64) -> Int64 = add // OK

函數類型的核心用途

1. 聲明函數類型的變量

在倉頡中,聲明變量必須顯式或通過初始化值來表明類型。

// 定義一個函數
func add(a: Int64, b: Int64): Int64 {return a + b
}main() {// 正確聲明1: 顯式指定變量類型,再賦值函數let operation: (Int64, Int64) -> Int64 // 聲明一個函數類型的變量operation = add // 將函數賦值給變量// 正確聲明2: 聲明的同時初始化(類型由編譯器推斷)let anotherOperation = add // 編譯器能推斷出anotherOperation的類型是 (Int64, Int64) -> Int64//現在,operation或anotherOperation就代表了 add 函數let result = operation(5, 3)println(result) // 輸出 8let result2 = anotherOperation(5, 3)println(result2) // 輸出 8
}

2. 作為函數的參數(高階函數)

// 導入標準庫中的集合包
import std.collection.ArrayList // 使用ArrayList// 高階函數的參數類型使用函數類型 (Int64) -> String
// 輸入和輸出使用 ArrayList 類型
func processNumbers(numbers: ArrayList<Int64>, transform: (Int64) -> String): ArrayList<String> {// 創建一個新的 ArrayList 來存放結果let results = ArrayList<String>()// 遍歷輸入的 ArrayList - 使用 for-in 循環for(num in numbers) {// 調用傳入的 transform 函數處理每個元素let transformedValue = transform(num)// 將結果添加到新的集合中results.add(transformedValue)}return results
}// 處理行為的函數定義不變
func intToString(num: Int64): String {return "Number: ${num}"
}main() {// 使用 ArrayList 而不是原生數組let myNumbers = ArrayList<Int64>([1, 2, 3, 4, 5])// 調用方式完全一樣,傳遞函數名let resultList = processNumbers(myNumbers, intToString)// 遍歷結果 ArrayList - 使用 for-in 循環for (str in resultList) {println(str)}// 同樣可以使用 Lambda 表達式let squaredList = processNumbers(myNumbers, { n => "Squared: ${n * n}" })// 遍歷 squaredListfor (str in squaredList) {println(str)}
}

3. 作為函數的返回值

// 這個函數返回一個 () -> String 類型的函數
func getGreeter(prefix: String): () -> String {// 在內部定義一個函數,它捕獲了參數 `prefix`func greeter(): String {return "${prefix}, Hello!" }return greeter // 返回這個內部函數
}main() {// getGreeter 返回的是一個函數let casualGreet = getGreeter("Hi")let formalGreet = getGreeter("Good morning")// 調用返回的函數println(casualGreet()) // 輸出: Hi, Hello!println(formalGreet()) // 輸出: Good morning, Hello!
}

順便提示,先把函數定義好,再傳遞函數名。也可可以用Lambda 表達式(匿名函數),下面示例對比:

import std.collection.ArrayList // 使用ArrayList// 高階函數的參數類型使用函數類型 (Int64) -> String
// 輸入和輸出使用 ArrayList 類型
func processNumbers(numbers: ArrayList<Int64>, transform: (Int64) -> String): ArrayList<String> {// 創建一個新的 ArrayList 來存放結果let results = ArrayList<String>()// 遍歷輸入的 ArrayList - 使用 for-in 循環for(num in numbers) {// 調用傳入的 transform 函數處理每個元素let transformedValue = transform(num)// 將結果添加到新的集合中results.add(transformedValue)}return results
}main() {// 使用 ArrayList 而不是原生數組let myNumbers = ArrayList<Int64>([1, 2, 3, 4, 5])// ---------1.具名函數(普通寫法)------------    // 先寫一個具名函數func multiply10(num: Int64): String {return "Value is: ${num * 10}"}// 把函數名當參數傳進去let result = processNumbers(myNumbers, multiply10)    // 遍歷結果for(str in result) {println(str)}println("-----------")// ---------2.Lambda 表達式(匿名寫法)------------ // Lambda 表達式寫法let result2 = processNumbers(myNumbers, { num => "Value is: ${num * 10}" })    // 遍歷結果for(str in result2) {println(str)}
}

特別提示,可給函數類型的參數標記顯式名稱(僅用于標識,不影響類型匹配),且需統一寫或統一不寫,不能混合。

示例:

// 首先定義 showFruitPrice 函數
func showFruitPrice(name: String, price: Int64): Unit {println("Fruit: ${name}, Price: ${price}")
}main() {// 1. 全部寫名字 —— 合法let handler1: (name: String, price: Int64) -> Unit = showFruitPrice// 2. 全部不寫名字 —— 合法  let handler2: (String, Int64) -> Unit = showFruitPrice// 3. 混寫 —— 非法,編譯時報錯// let handler3: (name: String, Int64) -> Unit   // Error: 必須統一寫或統一不寫參數名// 調用函數handler1("apple", 5)   // 正確handler2("apple", 5)   // 同樣正確
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/920170.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/920170.shtml
英文地址,請注明出處:http://en.pswp.cn/news/920170.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

服務初始化

目錄 1.配置yum源 2. 更新系統與安裝必備工具 3. 網絡連接驗證 4. 配置主機名 5. 同步時間 6. 配置防火墻 (兩種方式) 6.1 iptables 6.2firewalld 1.配置yum源 1. 備份原有的源文件&#xff0c;以防萬一 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.…

ICBC_TDR_UShield2_Install.exe [ICBC UKEY]

流程&#xff1a;1&#xff09;插入U盾&#xff0c;記住檢測到U盾類型&#xff0c;需要根據這個下載驅動

在線提取維基百科Wikipedia文章頁面及離線批處理Wikipedia XML Dump文件

1. 在線提取維基百科Wikipedia文章 本項目提供一個增強型 Wikipedia 概念條目抓取與摘要清洗腳本&#xff1a;支持多級回退策略 (wikipedia 庫 →wikipediaapi → 直接網頁 / REST 搜索)、智能標題匹配(精確/模糊判定)、摘要質量校驗、內容結構化抽取、斷點續跑(結果緩存)、統…

安全合規:AC(上網行為安全)--下

五、SSL移動接入方案概述1、SSL VPN概述SSL VPN是一種遠程安全接入技術&#xff0c;因為采用SSL協議而得名。因為Web瀏覽器都內嵌支持SSL協議&#xff0c;使得SSL VPN可以做到“無客戶端”部署。SSL VPN一般采用插件系統來支持各種TCP和UDP的非Web應用&#xff0c;使得SSL VPN真…

【86頁PPT】特種車行業SAP解決方案(附下載方式)

篇幅所限&#xff0c;本文只提供部分資料內容&#xff0c;完整資料請看下面鏈接 https://download.csdn.net/download/2501_92808859/91716699 資料解讀&#xff1a;《【86頁PPT】特種車行業SAP解決方案》 ??詳細資料請看本解讀文章的最后內容?? 作為特種車行業信息化建…

【Kubernetes k8s】(兩萬字超詳細)Ubuntu-22.04搭建 k8s-1.30.1集群,開啟Dashboard-2.7.0、部署ingress-nginx-1.10.1

Ubuntu-22.04搭建 k8s-1.30.1集群&#xff0c;開啟Dashboard-v2.7.0&#xff08;以及Token不生成的問題&#xff09;、部署ingress-nginx-1.10.1 引言 最近在研究分布式計算&#xff0c;想將分布式計算都容器化&#xff0c;使用 k8s 來調度&#xff0c;所以從0開始學 k8s &…

podman啟動mongdb的container因為權限問題導致changing ownership和讀取storage.bson失敗的解決方法

用FROM mongo:8.0.8 為基礎鏡像&#xff0c;加了些初始化數據做的mongodb鏡像。用podman管理和backend&#xff0c;frontend組成一個簡單的BS架構。利用podman創建pod&#xff0c;3個鏡像同用一個空間&#xff0c;項目內部連接就可以統一用127.0.0.1加上端口進行通信了。 要使…

UE5基本打光(新手向)

在UE5中場景照明往往是構建沉浸式視覺體驗的關鍵環節與常見挑戰。學會如何打光可以為項目創建出更具表現力和藝術感的燈光效果。 1.以UE5建筑展示demo為例&#xff0c;首先刪除舊的光照&#xff0c;將光照相關配置放置新的場景Light中。這樣更適合多人分工。 光照子場景Light&…

PiscCode使用OpenCV和Python實現運動檢測與可視化

光流分析是計算機視覺中的重要技術&#xff0c;用于檢測視頻序列中物體的運動模式。本文將介紹如何使用OpenCV和Python實現一個實時的光流分析系統&#xff0c;該系統能夠檢測運動、生成熱力圖并提供詳細的統計分析。 技術概述 本系統基于Farneback稠密光流算法&#xff0c;能…

Day 36 復習日

浙大疏錦行 今天是復習日&#xff0c;神經網絡與機器學習最大的不同就是不止固定的三行代碼&#xff0c;需要自己定義一個模型&#xff0c;先繼承類的屬性&#xff0c;然后去寫自己的屬性&#xff0c;以及前向傳播方法&#xff0c;可以手動構建&#xff1a;中間層的數量、每一…

ES6/ES2015 - ES16/ES2025

ES6/ES2015 - ES16/ES2025 ECMAScript&#xff08;簡稱ES&#xff09;是JavaScript的官方標準&#xff0c;從2015年開始每年發布一個新版本。 版本一覽表年份版本主要新特性2015ES6/ES2015let/const、箭頭函數、Class、模板字符串、解構賦值、模塊、Promise2016ES7/ES2016指數運…

BIM 地鐵站可視化:智慧運維 “透視鏡”

圖撲 BIM 地鐵站可視化系統&#xff0c;以三維建模完整復刻車站空間&#xff0c;從出入口、站廳到設備層&#xff0c;管線走向、設施分布精準呈現。實時匯聚客流數據、空調等設備運行狀態&#xff0c;動態標記設備告警、空間占用情況。通過透明化模型&#xff0c;運維人員可直觀…

淘寶商品詳情頁數據接口設計與實現:從合規采集到高效解析

在電商數據分析、比價系統開發等場景中&#xff0c;商品詳情頁數據是核心基礎。本文將圍繞淘寶商品詳情頁數據接口的合規設計、高效采集與智能解析展開&#xff0c;提供一套可落地的技術方案&#xff0c;重點解決動態渲染、參數加密與數據結構化等關鍵問題。 一、接口設計原則…

HTML應用指南:利用GET請求獲取中國銀行人民幣存款利率數據

人民幣存款利率是影響居民儲蓄行為和企業資金配置的關鍵因素&#xff0c;也是宏觀經濟調控的重要工具。中國銀行根據中國人民銀行的指導政策&#xff0c;結合市場情況與自身經營策略&#xff0c;定期調整并公布人民幣存款利率標準。這些利率信息主要涵蓋活期存款、定期存款&…

RPS和QPS

簡介 這是系統設計中兩個最核心且容易混淆的性能指標。簡單來說&#xff1a; ? RPS 是 “每秒請求數”&#xff0c;是從客戶端或負載均衡器的視角看&#xff0c;服務器每秒接收到的請求數量。 ? QPS 是 “每秒查詢數”&#xff0c;通常是從數據庫或特定服務的視角看&…

如何將用戶反饋轉化為可執行需求

用戶反饋是企業優化產品、改進服務的重要依據。將用戶反饋轉化為可執行需求的核心在于通過系統化的流程對反饋進行收集、分析和分類&#xff0c;并結合企業的戰略目標與技術能力&#xff0c;制定出具體的執行方案。這一過程不僅要求企業深入理解用戶需求&#xff0c;還需要跨部…

ry-vue docker部署

目錄 整體架構概覽 創建 Docker 自定義網絡 Redis 部署&#xff08;緩存服務&#xff09; redis.conf修改 啟動 Redis 容器 測試 啟動 MySQL 容器 允許 root 用戶遠程訪問&#xff08;%&#xff09; 初始化數據庫&#xff08;可選&#xff09; RuoYi-Admin 后端服務部…

Redis之Keys命令和Scan命令

序言 網上看到的面試題&#xff1a;Redis有1億個key&#xff0c;其中10w個key是以某個固定的前綴開頭&#xff0c;如何將它們全部找出來&#xff1f;一般有兩種命令可以實現&#xff1a; Keys命令Scan命令 下面具體分析一下兩種命令 Keys命令 Keys pattern如下圖所示&…

【小沐學GIS】基于Godot繪制三維數字地球Earth(Godot)

&#x1f37a;三維數字地球GIS系列相關文章&#xff08;C&#xff09;如下&#x1f37a;&#xff1a;1【小沐學GIS】基于C繪制三維數字地球Earth&#xff08;OpenGL、glfw、glut&#xff09;第一期2【小沐學GIS】基于C繪制三維數字地球Earth&#xff08;OpenGL、glfw、glut&…

day62 Floyd 算法 A * 算法

Floyd 算法本題是經典的多源最短路問題.Floyd 算法對邊的權值正負沒有要求&#xff0c;都可以處理。Floyd算法核心思想是動態規劃。例如我們再求節點1 到 節點9 的最短距離&#xff0c;用二維數組來表示即&#xff1a;grid[1][9]&#xff0c;如果最短距離是10 &#xff0c;那就…