前言

本文将继续学习和研究仓颉函数


一、仓颉的函数

1.函数的命名变量和非命名变量

仓颉函数存在多个命名变量,在调用时顺序可以调换

public func  execFun1(a!:Int64,b!:Int64)
{
     println("1.${a},${b}")
}

public  func call()
{
      execFun1(b:200, a: 100)
}

2.函数的返回值

可以对函数的返回值类型做出要求,函数():返回值类型{   }

如果希望根据具体情况转换返回值类型,可以依靠之前学过的,所有类型的父类Any转换的关键字as进行类型的转换

public  func  execFun1(a:Any):Any
{
    a
}

public  func call()
{
      var result =  execFun1("100")

      if(result  is Int64)
      {
         let  a =  (result as  Int64).getOrThrow()
         println("${a}")
      }
      else{

        println("不能转换")
      }
}

3.函数重载

同名函数,参数不同是方法重载

实际调用哪个函数由调用时给出的参数控制

而当局部变量和全局变量名称重合时,就近原则,决定打印哪个数值

var   a = 100

public  func  show()
{
    println("result:${a}")
}
public func  show(a:Int64)
{
      var a1 = a
      a1 =200
      println("${a}")

}
public func call()
{
     show(10) //10
     show() //result:100
}

4.递归函数

递归函数就是函数调用自己

函数不断自身调用自己递归需要有出口,就是一个递归头
//1.调用阶段
//2.返回阶段

//1.调用阶段
//2.返回阶段
public func  fn(n:Int64):Int64{

    if(n==0)
    {
         1 // 递归出口   返回阶段,开始逐层返回
    }
    else{
         fn(n-1)*n

    }
}
public func call()
{
     println(fn(4)) //24
}

5.函数嵌套

注意没有规定返回值类型,因为不清楚返回值类型hsi'shen

public  func  Outer(){

    func  Inner()
    {

        println("这是内部函数Inner")

    }
    Inner
}

public  func call()
{
      let  fn  = Outer()
      fn()
}

二、面向函数式编程

1.面向函数思想

一个函数可以等于一个变量  一个函数可以赋值给一个变量

值得注意的是一般来说变量是有类型的,当把函数赋值给变量时,类型如何定义?

变量类型 (a1:Int64,b1:Int64)->Int64  【(函数参数)->函数返回值类型

当直接将函数赋值给变量时,根据自动类型推断,也可直接赋值

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

     a+b
}

public  func call()
{
   //面向函数式编程思想  一个函数可以等于一个变量  一个函数可以赋值给一个变量
   var f1 : (a1:Int64,b1:Int64)->Int64  = add 

    //f1是个函数变量
    println(f1(10,20))
    var  f2 = add  //自动类型推断

}

但当函数存在方法重载时自动类型推断失效,不能自动推断其类型

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

     a+b
}

public  func add(a:String,b:String):String{

     a+b
}

public  func call()
{
   //面向函数式编程思想  一个函数可以等于一个变量  一个函数可以赋值给一个变量
   var f1 : (a1:Int64,b1:Int64)->Int64  = add 

    //f1是个函数变量
    println(f1(10,20))

    //错误
   // var  f2 = add  //自动类型推断,不能自行推断

   //存在 (a1:Int64,b1:Int64)->Int64(a:String,b:String)->String两种类型可能性

}

仓颉的方法作为另外一个方法的参数进行传递

//函数可以作为方法的参数
public  func   add(a:Int64,b:Int64):Int64
{
      a+b
}

public  func   sub(a:Int64,b:Int64):Int64
{
      a-b
}

public  func   execFun1(a:Int64,b:Int64,op:(a:Int64,b:Int64)->Int64):Int64{
     op(a,b)
}

//op是个函数类型,指向哪个具体的实现方法,是由业务决定的
//参数和返回值类型保持一致即可
public  func  call()
{
      execFun1(10,20,sub)
      execFun1(10,20,add)
}
 

2.切面编程思想

切面编程思想就是就是切方法,在进入主方法之前,进行前置控制,通过切入的面来判断,是否可以执行主方法

切面分为五类:

前置切面:在方法执行之前执行的切入,权限控制

环绕切面:在方法执行的前后都执行的切入,缓存机制

异常通知:在方法抛出异常时执行,操作的图文

后置切面:在方法执行之后执行,不管方法是否发生异常,日志

最终切面:在方法正常返回后执行,总结

//前置切面
public func before(a: Int64): Bool {
    if (a > 18) {
        true
    } else {
        false
    }
}

//主方法
public func execAction() {
    println("你可以毕业")
}

public  func  execBank(){
    println("你可以去银行办理贷款")
}

public func execFun2(a: Int64, be: (a: Int64) -> Bool, action: () -> Unit) {
    //切面编程思想  切面就是切方法,在进入主方法之前,进行前置控制

    if (before(a)) {
        action()
    } else {
        println("你没有能力去执行")
    }
}

public func  call(){

    execFun2(22,before,execAction) //你可以毕业
    execFun2(18,before,execBank) //你没有能力去执行
}

以上代码是前置切面编程思想,通过前置控制,判断主方法是否执行

需要注意的是进行前置控制时,注意返回函数类型并给定出口,以使得在没有条件满足时调用,否则会出现报错


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

public func sub(a: Int64, b: Int64): Any {
    a - b
}

public func any(a: Int64, b: Int64): Any {
    println("不满足条件")
}

public func exec(op: String): (Int64, Int64) -> Any {
    if (op == "+") {
        return add  //return可以省略
    } else if (op == "-") {
        return sub
    }
    any //需要一个出口,没有条件满足时进行调用
}

public func call() {
    var result = exec("*")(100, 200)
    let re = (result as Int64).getOrThrow()
    println("${re}")
}

继承是 Son< :Father

函数类型赋值是var f1:(参数)-> 返回值类型= 方法名        要求是方法名和被赋值函数类型一致

3.lamdba表达式

lamdba就是匿名函数,所谓的匿名函数,就是没有名字的函数,就是函数的具体实现

var f1 : ( )->Unit = 没有名字的方法    (lamdba表达式)

分为两种调用赋值调用和立即调用

public  func  exec1()
{
    var f1: ()->Unit = { =>println("这是lamdba表达式")}
    f1()
}

public  func  exec2()
{
    
//立即调用

     { =>println("这是lamdba表达式")}() //需要加括号表示函数调用
}

public func  exec3(){

  var  f2:(Int64,Int64)->Int64 = {a:Int64,b:Int64 => a+b}
   println(f2(30,40))  

   //带参数的立即调用

   {a:Int64,b:Int64 =>println("${a+b}")}(10,20)     
}

lamdba表达式做为方法的参数

public  func  execOne(a:Int64,b:Int64,op:(Int64,Int64)->Int64):Int64{
    op(a,b)
}

public  func  call()
{
     execOne(10,20, {a,b =>a*b})

}

lamdba表达式做为方法体

public  func  op():(String,String)->String{

   {a,b=>a+b}
}
public func call()
{
   // lamdba函数表达式的参数和返回值类型和函数保持一致  
   println(op()("10","20")) //输出结果为1020
}

调用lamaba函数

public  func   Outer(flag:Bool):(Int64,Int64)->Int64
{
     if(true)
     {
         {a,b=>a+b}
     }
     else{
        {a,b=>a-b}
     }
}

三.枚举类型

仓颉类型:
1. 基本数据类型  值类型: Int、String、 struct类型
2. 引用类型  类,接口和枚举类型
枚举类型是个固定的取值类型

枚举类型关键字enum

枚举类型的值必须大写,值用竖线分割

枚举类型可以有实例函数和静态函数,不能有构造函数和属性设计器

public enum Famialy {
    | FATHER | MOTHER | SON // 枚举类型的值必须大写

    //枚举类型可以有实例函数和静态函数,不能有构造函数和属性设计器
    public func execFamialy() {
        println("枚举类型的函数")
    }
}

public func call() {
    var f = Famialy.FATHER
    f.execFamialy()
}
 

枚举类型和结构体都可以继承接口

public  interface   A{

}
//一个枚举类型可以继承一个接口
public enum  Values <: A{
    |FATHER|MONTHER
 
}

public struct  User <:A {

}

利用枚举函数进行模式匹配

public enum Fam {
    | FATHER | MONTHER | SON
}

public func execEnum1(v: Fam): Unit {
    match (v) {
        case FATHER => println("这是父亲")   //枚举类型进行模式匹配时,每个元素都需要匹配上
        case MONTHER => println("这是母亲")
        case SON => println("这是儿子")
    }
}

public func execEnum2(v: Fam): String {
    let result = match (v) {
        case FATHER => "父亲"
        case MONTHER => "母亲"
        case SON => "儿子"
    }
    result
}


总结

本文深入探讨了仓颉函数的多个方面,包括函数的命名变量与非命名变量、返回值类型、函数重载、递归函数、函数嵌套等。文章还介绍了面向函数式编程的思想,如函数作为变量赋值、切面编程思想、lambda表达式的使用等。此外,文中还涉及了枚举类型的基本概念及其在仓颉语言中的应用,包括枚举类型的定义、实例函数、静态函数以及枚举类型与接口的继承关系。通过这些内容,本文为读者提供了仓颉函数编程的全面视角,帮助理解其核心概念和高级特性。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐