最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go expr 通用表達式引擎的使用

 更新時間:2025年05月25日 10:17:44   作者:NPE~  
本文主要介紹了Go expr 通用表達式引擎的使用,支持變量、運算符和函數(shù),適用于動態(tài)規(guī)則管理,具有一定的參考價值,感興趣的可以了解一下

官方教程:https://expr-lang.org/docs/language-definition
官方Github:https://github.com/expr-lang/expr
文章所含代碼地址:https://github.com/ziyifast/ziyifast-code_instruction/tree/main/go-demo/go-expr

一、介紹

Expr表達式引擎是一個針對Go語言設計的動態(tài)配置解決方案,它以簡單的語法和強大的性能特性著稱。Expr表達式引擎的核心是安全、快速和直觀,很適合用于處理諸如訪問控制、數(shù)據(jù)過濾和資源管理等場景。在Go語言中應用Expr,可以極大地提升應用程序處理動態(tài)規(guī)則的能力。不同于其他語言的解釋器或腳本引擎,Expr采用了靜態(tài)類型檢查,并且生成字節(jié)碼來執(zhí)行,因此它能同時保證性能和安全性。

二、安裝

//通過go get直接安裝即可
go get github.com/expr-lang/expr

三、使用

基礎使用

①運行基本表達式

在下面例子中,表達式2 + 2被編譯成能運行的字節(jié)碼,然后執(zhí)行這段字節(jié)碼并輸出結果。
同時下面的例子不包含變量,因此也不用傳入環(huán)境。

package main

import (
        "fmt"
        "github.com/expr-lang/expr"
)

func main() {
        // 編譯一個基礎的加法表達式
        program, err := expr.Compile(`2 + 2`)
        if err != nil {
                panic(err)
        }

        // 運行編譯后的表達式,并沒有傳入環(huán)境,因為這里不需要使用任何變量
        output, err := expr.Run(program, nil)
        if err != nil {
                panic(err)
        }

        // 打印結果
        fmt.Println(output)  // 輸出 4
}

②運行變量表達式

下面我們創(chuàng)建一個包含變量的環(huán)境,編寫使用這些變量的表達式,編譯并運行這個表達式。
在下面例子中,環(huán)境env包含了變量apple和banana。表達式apple + banana在編譯時會從環(huán)境中推斷apple和banana的類型,并在運行時使用這些變量的值來評估表達式結果。

package main

import (
    "fmt"
    "github.com/expr-lang/expr"
)

func main() {
    // 創(chuàng)建一個包含變量的環(huán)境
    env := map[string]interface{}{
       "apple":  5,
       "banana": 10,
    }

    // 編譯一個使用環(huán)境中變量的表達式
    program, err := expr.Compile(`apple + banana`, expr.Env(env))
    if err != nil {
       panic(err)
    }

    // 運行表達式
    output, err := expr.Run(program, env)
    if err != nil {
       panic(err)
    }

    // 打印結果
    fmt.Println(output) // 輸出 15
}

語法介紹

下面主要是介紹 Expr 表達式引擎內置函數(shù)的一部分。通過這些功能強大的函數(shù),可以更加靈活和高效地處理數(shù)據(jù)和邏輯。更詳細的函數(shù)列表和使用說明查閱官方函數(shù)文檔。

官方函數(shù)文檔:https://expr-lang.org/docs/language-definition

①字面量和變量

Expr表達式引擎能夠處理常見的數(shù)據(jù)類型字面量,包括數(shù)字、字符串和布爾值。字面量是直接在代碼中寫出的數(shù)據(jù)值,比如42、"hello"和true都是字面量

(1)字面量:數(shù)字、字符串、布爾值

// (1) 數(shù)字
42      // 表示整數(shù) 42
3.14    // 表示浮點數(shù) 3.14


// (2) 字符串
"hello, world" // 雙引號包裹的字符串,支持轉義字符
`hello, world` // 反引號包裹的字符串,保持字符串格式不變,不支持轉義

// (3)布爾值
true   // 布爾真值
false  // 布爾假值

(2)變量:Expr允許在環(huán)境中定義變量,然后在表達式中引用這些變量

// (1)表達式定義變量
env := map[string]interface{}{
    "age": 25,
    "name": "Alice",
}

// (2)表達式中引用變量
age > 18  // 檢查age是否大于18
name == "Alice"  // 判斷name是否等于"Alice"

②運算符

Expr表達式引擎支持多種運算符,包含數(shù)學運算符、邏輯運算符、比較運算符及集合運算符等。

  • 數(shù)學和邏輯運算符

數(shù)學運算符包括加(+)、減(-)、乘(*)、除(/)和取模(%)。邏輯運算符包括邏輯與(&&)、邏輯或(||)和邏輯非(!)

2 + 2 // 計算結果為4
7 % 3 // 結果為1
!true // 結果為false
age >= 18 && name == "Alice" // 檢查age是否不小于18且name是否等于"Alice"
  • 比較運算符

比較運算符有相等(==)、不等(!=)、小于(<)、小于等于(<=)、大于(>)和大于等于(>=),用于比較兩個值

age == 25 // 檢查age是否等于25
age != 18 // 檢查age是否不等于18
age > 20  // 檢查age是否大于20
  • 集合運算符

Expr還提供了一些用于操作集合的運算符,如in用于檢查元素是否在集合中,集合可以是數(shù)組、切片或字典

"user" in ["user", "admin"]  // true,因為"user"在數(shù)組中
3 in {1: true, 2: false}     // false,因為3不是字典的鍵

還有一些高級的集合操作函數(shù),比如all、any、one和none,這些函數(shù)需要結合匿名函數(shù)(lambda)使用:

all(tweets, {.Len <= 240})  // 檢查所有tweets的Len字段是否都不超過240
any(tweets, {.Len > 200})   // 檢查是否存在tweets的Len字段超過200
  • 成員操作符

在Expr表達式語言中,成員操作符允許我們訪問Go語言中struct的屬性。這個特性讓Expr可以直接操作復雜數(shù)據(jù)結構,非常地靈活實用。

// (1) 定義結構體
type User struct {
    Name string
    Age  int
}

// (2)訪問結構體變量
env := map[string]interface{}{
    "user": User{Name: "Alice", Age: 25},
}

code := `user.Name`

program, err := expr.Compile(code, expr.Env(env))
if err != nil {
    panic(err)
}

output, err := expr.Run(program, env)
if err != nil {
    panic(err)
}

fmt.Println(output) // 輸出: Alice

在操作結構體變量時,我們通常會需要判斷對應字段值是否為空,這時就需要處理nil的情況:

在訪問屬性時,可能會遇到對象是nil的情況。Expr提供了安全的屬性訪問,即使在結構體或者嵌套屬性為nil的情況下,也不會拋出運行時panic錯誤。

方法一:使用?.操作符引用屬性,如果對象為nil則返回nil,而不會報錯。

author.User?.Name

// 等價于下面的表達式
author.User != nil ? author.User.Name : nil

方法二:??操作符,主要用于nil時,返回默認值

author.User?.Name ?? "Anonymous"

// 等價于下面表達式
author.User != nil ? author.User.Name : "Anonymous"

③函數(shù)

Expr支持內置函數(shù)和自定義函數(shù),使得表達式更加強大和靈活。

  • 內置函數(shù):內置函數(shù)像len、all、none、any等可以直接在表達式中使用
  • all:函數(shù) all 可以用來檢驗集合中的元素是否全部滿足給定的條件。它接受兩個參數(shù),第一個參數(shù)是集合,第二個參數(shù)是條件表達式。
// 檢查所有 tweets 的 Content 長度是否小于 240
code := `all(tweets, len(.Content) < 240)`
program, err := expr.Compile(code, expr.Env(env))
if err != nil {
    panic(err)
}
  • any:與 all 類似,any 函數(shù)用來檢測集合中是否有任一元素滿足條件。
// 檢查是否有任一 tweet 的 Content 長度大于 240
code := `any(tweets, len(.Content) > 240)`
  • none:用于檢查集合中沒有任何元素滿足條件。
// 確保沒有 tweets 是重復的
code := `none(tweets, .IsRepeated)`
// 內置函數(shù)示例
program, err := expr.Compile(`all(users, {.Age >= 18})`, expr.Env(env))
if err != nil {
    panic(err)
}

// 注意:這里env需要包含users變量,每個用戶都需要有Age屬性
output, err := expr.Run(program, env)
fmt.Print(output) // 如果env中所有用戶年齡都大于等于18,返回true
  • 自定義函數(shù):通過在環(huán)境映射env中傳遞函數(shù)定義來創(chuàng)建自定義函數(shù)

在Expr中使用函數(shù)時,我們可以讓代碼模塊化并在表達式中加入復雜邏輯。通過結合變量、運算符和函數(shù)。但需要注意,在構建Expr環(huán)境并運行表達式時,始終要確保類型安全。

// 自定義函數(shù)示例
env := map[string]interface{}{
    "greet": func(name string) string {
        return fmt.Sprintf("Hello, %s!", name)
    },
}

program, err := expr.Compile(`greet("World")`, expr.Env(env))
if err != nil {
    panic(err)
}

output, err := expr.Run(program, env)
fmt.Print(output) // 返回 Hello, World!

實際生產案例

比如我們現(xiàn)在有一個需求:電商平臺需要根據(jù)用戶屬性(會員等級、地域)和訂單信息(金額、商品類目),動態(tài)配置促銷活動的參與條件和折扣規(guī)則,無需修改代碼即可更新規(guī)則。

package main

import (
    "fmt"
    "log"
    "time"

    "github.com/expr-lang/expr"
    "github.com/expr-lang/expr/vm"
)

// 用戶信息
type User struct {
    ID       int
    Name     string
    Level    int    // 會員等級(1-普通, 2-黃金, 3-鉆石)
    Region   string // 用戶所在地區(qū)
    JoinTime time.Time
}

// 訂單信息
type Order struct {
    OrderID     string
    Amount      float64 // 訂單金額
    Category    string  // 商品類目(electronics, clothing, food)
    CreatedTime time.Time
}

// 促銷規(guī)則配置
type PromotionRule struct {
    Condition string  // Expr表達式,判斷是否滿足條件
    Discount  float64 // 折扣比例(0.9表示9折)
}

// 初始化規(guī)則引擎環(huán)境
func createEnv(user User, order Order) map[string]interface{} {
    return map[string]interface{}{
       "User":  user,
       "Order": order,
       "Now":   time.Now(), // 內置當前時間函數(shù)
       // 可添加其他輔助函數(shù),如字符串處理、數(shù)學計算等
    }
}

// 編譯促銷規(guī)則條件
func compileRule(rule string) (*vm.Program, error) {
    return expr.Compile(rule, expr.Env(createEnv(User{}, Order{})))
}

// 應用促銷規(guī)則
func ApplyPromotion(user User, order Order, rule PromotionRule) (bool, float64, error) {
    // 1. 編譯規(guī)則(生產環(huán)境需緩存編譯結果)
    program, err := compileRule(rule.Condition)
    if err != nil {
       return false, 0, fmt.Errorf("規(guī)則編譯失敗: %v", err)
    }

    // 2. 創(chuàng)建執(zhí)行環(huán)境
    env := createEnv(user, order)

    // 3. 執(zhí)行規(guī)則判斷
    output, err := expr.Run(program, env)
    if err != nil {
       return false, 0, fmt.Errorf("規(guī)則執(zhí)行失敗: %v", err)
    }

    // 4. 類型斷言判斷結果
    conditionMet, ok := output.(bool)
    if !ok {
       return false, 0, fmt.Errorf("規(guī)則必須返回布爾值")
    }

    // 5. 返回是否滿足條件及折扣
    return conditionMet, rule.Discount, nil
}

func main() {
    // 模擬用戶和訂單數(shù)據(jù)
    user := User{
       ID:       1001,
       Name:     "Alice",
       Level:    3,
       Region:   "CN",
       JoinTime: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
    }
    order := Order{
       OrderID:     "20231020001",
       Amount:      1500.00,
       Category:    "electronics",
       CreatedTime: time.Now(),
    }

    // 從數(shù)據(jù)庫/配置中心讀取促銷規(guī)則(示例)
    rules := []PromotionRule{
       {
          // 規(guī)則1:鉆石會員且訂單金額>1000,享85折
          Condition: `User.Level >= 3 && Order.Amount > 1000 && Order.Category == "electronics"`,
          Discount:  0.85,
       },
       {
          // 規(guī)則2:注冊超過2年的用戶,任意訂單享9折
          Condition: `Now.Sub(User.JoinTime).Hours() > 24*365*2`,
          Discount:  0.9,
       },
    }

    // 遍歷所有規(guī)則,應用最優(yōu)折扣
    bestDiscount := 1.0 // 默認無折扣
    for _, rule := range rules {
       valid, discount, err := ApplyPromotion(user, order, rule)
       if err != nil {
          log.Printf("規(guī)則應用錯誤: %v", err)
          continue
       }
       if valid && discount < bestDiscount {
          bestDiscount = discount
       }
    }

    // 計算最終價格
    finalPrice := order.Amount * bestDiscount
    fmt.Printf("原價: ¥%.2f\n", order.Amount)
    fmt.Printf("適用折扣: %.0f%%\n", (1-bestDiscount)*100)
    fmt.Printf("最終價格: ¥%.2f\n", finalPrice)
}

運行結果:

在這里插入圖片描述

適用場景

總結:規(guī)則變更頻繁且對吞吐要求不高 -> expr表達式,否則就直接上代碼

場景特征推薦方案理由
規(guī)則每天調整多次表達式引擎避免頻繁發(fā)版,提升業(yè)務敏捷性
規(guī)則復雜且嵌套業(yè)務對象直接代碼復雜邏輯更易維護,編譯器輔助類型檢查
需非技術人員配置規(guī)則(產品/運營)表達式引擎降低技術門檻,釋放開發(fā)資源
性能敏感(如:>10萬QPS)直接代碼避免表達式解析開銷影響吞吐量
多租戶定制規(guī)則表達式引擎各租戶獨立配置,互不影響

還是以上面的電商場景為例,讓大家感受expr的好處以及使用場景:
場景:電商促銷規(guī)則判斷
需求:根據(jù)用戶等級、訂單金額、商品類目動態(tài)調整折扣。

方案一:表達式引擎(expr)

// 規(guī)則配置(存儲于數(shù)據(jù)庫)
rules := []PromotionRule{
    {
        Condition: `User.Level >= 3 && Order.Amount > 1000 && Order.Category == "electronics"`,
        Discount:  0.85,
    },
}
// 動態(tài)執(zhí)行
valid, _ := ApplyPromotion(user, order, rule)

優(yōu)勢:

  • 運營人員可通過管理后臺隨時新增/修改規(guī)則,無需等待版本發(fā)布。
  • 支持A/B測試:為不同用戶組配置不同規(guī)則。

劣勢:

  • 需額外開發(fā)規(guī)則管理界面和測試工具。

方案二:直接代碼判斷

func IsPromotionValid(user User, order Order) bool {
    return user.Level >= 3 && 
           order.Amount > 1000 && 
           order.Category == "electronics"
}

優(yōu)勢:

  • 性能極高,適合每秒數(shù)十萬次調用的場景。
  • 邏輯變更通過代碼評審,降低錯誤風險。

劣勢:

  • 修改折扣條件需發(fā)版,無法快速響應市場活動。

到此這篇關于Go expr 通用表達式引擎的使用的文章就介紹到這了,更多相關Go expr 通用表達式引擎內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家! 

相關文章

  • Go語言LeetCode題解682棒球比賽

    Go語言LeetCode題解682棒球比賽

    這篇文章主要為大家介紹了Go語言LeetCode題解682棒球比賽示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-12-12
  • Go語言使用Goroutine并發(fā)打印的項目實踐

    Go語言使用Goroutine并發(fā)打印的項目實踐

    本文通過并發(fā)打印任務案例,講解Go語言Goroutine的使用、同步控制及高并發(fā)優(yōu)勢,文中通過示例代碼介紹的非常詳細,需要的朋友們下面隨著小編來一起學習學習吧
    2025-08-08
  • Go語言Mock單元測試的實現(xiàn)示例

    Go語言Mock單元測試的實現(xiàn)示例

    Go語言Mock單元測試通過模擬外部依賴實現(xiàn)隔離測試,解決傳統(tǒng)測試中的環(huán)境依賴、結果不穩(wěn)定和效率問題,感興趣的可以了解一下
    2025-11-11
  • golang中make和new的區(qū)別示例詳解

    golang中make和new的區(qū)別示例詳解

    Go 語言中的 new 和 make 一直是新手比較容易混淆的東西,咋一看很相似。不過解釋兩者之間的不同也非常容易,下面這篇文章主要介紹了golang中make和new的區(qū)別,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-08-08
  • 使用Go語言實現(xiàn)發(fā)送HTTP請求并給GET添加參數(shù)

    使用Go語言實現(xiàn)發(fā)送HTTP請求并給GET添加參數(shù)

    在開發(fā)Web應用程序時,我們經常需要向服務器發(fā)送HTTP請求,本文將介紹一下使用Go語言發(fā)送HTTP請求,并給GET請求添加參數(shù)的方法,感興趣的小伙伴可以了解一下
    2023-07-07
  • golang編程開發(fā)使用sort排序示例詳解

    golang編程開發(fā)使用sort排序示例詳解

    這篇文章主要為大家介紹了go語言編程開發(fā)使用sort來排序示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪
    2021-11-11
  • 淺談beego默認處理靜態(tài)文件性能低下的問題

    淺談beego默認處理靜態(tài)文件性能低下的問題

    下面小編就為大家?guī)硪黄獪\談beego默認處理靜態(tài)文件性能低下的問題。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-06-06
  • Go高級特性探究之HTTP錯誤處理詳解

    Go高級特性探究之HTTP錯誤處理詳解

    在Web應用程序中,HTTP錯誤處理是非常重要的,它關系到Web應用程序的穩(wěn)定性和可靠性,本文介紹如何在Go項目中處理HTTP錯誤,并提供相應的解決方案和實踐經驗,希望對Go語言Web應用程序的開發(fā)者有所幫助
    2023-06-06
  • go 如何修改postgresql的配置參數(shù)

    go 如何修改postgresql的配置參數(shù)

    這篇文章主要介紹了go 如何修改postgresql的配置參數(shù),本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2024-01-01
  • 從基礎到高階解析Go語言中數(shù)組的應用

    從基礎到高階解析Go語言中數(shù)組的應用

    在本文中,我們將從基礎概念、常規(guī)操作,到高級技巧和特殊操作,帶大家深入了解Go語言中數(shù)組的各個方面,感興趣的小伙伴可以跟隨小編一起學習一下
    2023-10-10

最新評論

祥云县| 兴安县| 塔河县| 苏尼特左旗| 马关县| 特克斯县| 右玉县| 沽源县| 四平市| 承德县| 霍城县| 麦盖提县| 秦安县| 平果县| 鲁山县| 新河县| 贡山| 姚安县| 长乐市| 四子王旗| 大竹县| 嘉兴市| 葵青区| 万年县| 五大连池市| 遂溪县| 睢宁县| 洛隆县| 商都县| 保靖县| 辽阳县| 华容县| 邯郸市| 漳浦县| 桃园市| 定远县| 贵定县| 阿坝| 北票市| 开封县| 简阳市|