設計模式(十五)解釋器

来源:https://www.cnblogs.com/WinterSir/archive/2023/11/28/17506255.html
-Advertisement-
Play Games

一、定義 定義一個語言的文法,並且建立一個解釋器來解釋該語言中的句子,這裡的“語言”是指使用規定格式和語法的代碼。解釋器模式是一種行為型模式。 二、描述 解釋器模式是一種使用頻率相對較低但學習難度較大的設計模式,它主要用於描述如何使用面向對象語言構成一個簡單的語言解釋器,包含以下四個角色: 1、Ab ...


一、定義

定義一個語言的文法,並且建立一個解釋器來解釋該語言中的句子,這裡的“語言”是指使用規定格式和語法的代碼。解釋器模式是一種行為型模式。

二、描述

解釋器模式是一種使用頻率相對較低但學習難度較大的設計模式,它主要用於描述如何使用面向對象語言構成一個簡單的語言解釋器,包含以下四個角色:
1、AbstractExpression(抽象表達式)在抽象表達式中聲明瞭抽象的解釋操作,它是所有終結符表達式和非終結表達式的公共父類。
2、TerminalExpression(終結符表達式):TerminalExpression(終結符表達式):終結符表達式是抽象表達式的子類,它實現了與文法中的終結符相關聯的解釋操作,在句子中的每一個終結符都是該類的一個實例。通常,在一個解釋器模式中只有少數幾個終結符表達式類,它們的實例可以通過非終結符表達式組成較為複雜的句子。
3、NonterminalExpression(非終結符表達式):非終結符表達式也是抽象表達式的子類,它實現了文法中非終結符的解釋操作,由於在非終結符表達式中可以包含終結符表達式,也可以繼續包含非終結符表達式,因此其解釋操作一般通過遞歸的方式來完成。
4、Context(環境類):環境類又稱為上下文類,它用於存儲解釋器之外的一些全局信息,通常臨時存儲了需要解釋的語句。

三、例子

X公司開發了一套簡單的基於字元界面的格式化指令,可以根據輸入的指令在字元界面輸出一些格式化內容,例如輸入“LOOP 2 PRINT 楊過 SPACE SPACE PRINT 小龍女 BREAK END PRINT 郭靖 SPACE SPACE PRINT 黃蓉”,將輸出以下結果:其中,關鍵詞LOOP表示迴圈,後面的數字表示迴圈次數;PRINT表示列印,後面的字元串表示列印的內容;SPACE表示空格;BREAK表示換行;END表示迴圈結束。每一個關鍵詞對應一條指令,電腦程式將根據關鍵詞執行相應的處理操作。
Context:環境類

/// <summary>
/// 環境類:用於存儲和操作需要解釋的語句,
/// 在本實例中每一個需要解釋的單詞都可以稱為一個動作標記(ActionToker)或命令
/// </summary>
public class Context
{
    private int index = -1;
    private string[] tokens;
    private string currentToken;

    public Context(string text)
    {
        text = text.Replace("  ", " ");
        tokens = text.Split(' ');
        NextToken();
    }

    // 獲取下一個標記
    public string NextToken()
    {
        if (index < tokens.Length - 1)
        {
            currentToken = tokens[++index];
        }
        else
        {
            currentToken = null;
        }

        return currentToken;
    }

    // 返回當前的標記
    public string GetCurrentToken()
    {
        return currentToken;
    }

    // 跳過一個標記
    public void SkipToken(string token)
    {
        if (!token.Equals(currentToken, StringComparison.OrdinalIgnoreCase))
        {
            Console.WriteLine("錯誤提示:{0} 解釋錯誤!", currentToken);
        }

        NextToken();
    }

    // 如果當前的標記是一個數字,則返回對應的數值
    public int GetCurrentNumber()
    {
        int number = 0;
        try
        {
            // 將字元串轉換為整數
            number = Convert.ToInt32(currentToken);
        }
        catch (Exception ex)
        {
            Console.WriteLine("錯誤提示:{0}", ex.Message);
        }

        return number;
    }
}

Node:抽象節點類,充當抽象表達式

public abstract class Node
{
    // 聲明一個方法用於解釋語句
    public abstract void Interpret(Context context);
    // 聲明一個方法用於執行標記對應的命令
    public abstract void Execute();
}

ExpressionNode、CommandNode、LoopCommandNode:表達式節點類、語句命令節點類、迴圈命令類,充當非終結符表達式

public class ExpressionNode : Node
{
    // 用於存儲多條命令的集合
    private IList<Node> nodeList = new List<Node>();

    public override void Interpret(Context context)
    {
        // 迴圈處理Context中的標記
        while (true)
        {
            // 如果已經沒有任何標記,則退出解釋
            if (context.GetCurrentToken() == null)
            {
                break;
            }
            // 如果標記為END,則不解釋END並結束本次解釋過程,可以繼續之後的解釋
            else if (context.GetCurrentToken().Equals("END", StringComparison.OrdinalIgnoreCase))
            {
                context.SkipToken("END");
                break;
            }
            // 如果為其它標記,則解釋標記並加入命令集合
            else
            {
                Node node = new CommandNode();
                node.Interpret(context);
                nodeList.Add(node);
            }
        }
    }

    // 迴圈執行命令集合中的每一條指令
    public override void Execute()
    {
        foreach (var node in nodeList)
        {
            node.Execute();
        }
    }
}

public class CommandNode : Node
{
    private Node node;

    public override void Interpret(Context context)
    {
        // 處理LOOP指令
        if (context.GetCurrentToken().Equals("LOOP", StringComparison.OrdinalIgnoreCase))
        {
            node = new LoopCommand();
            node.Interpret(context);
        }
        // 處理其他指令
        else
        {
            node = new PrimitiveCommand();
            node.Interpret(context);
        }
    }

    public override void Execute()
    {
        node.Execute();
    }
}

public class LoopCommand : Node
{
    // 迴圈次數
    private int number;
    // 迴圈語句中的表達式
    private Node commandNode;

    public override void Interpret(Context context)
    {
        context.SkipToken("LOOP");
        number = context.GetCurrentNumber();
        context.NextToken();
        // 迴圈語句中的表達式
        commandNode = new ExpressionNode();
        commandNode.Interpret(context);
    }

    public override void Execute()
    {
        for (int i = 0; i < number; i++)
        {
            commandNode.Execute();
        }
    }
}

PrimitiveCommandNode:基本命令類,充當終結符表達式

public class PrimitiveCommand : Node
{
    private string name;
    private string text;

    public override void Interpret(Context context)
    {
        name = context.GetCurrentToken();
        context.SkipToken(name);

        if (!name.Equals("PRINT", StringComparison.OrdinalIgnoreCase) 
            && !name.Equals("BREAK", StringComparison.OrdinalIgnoreCase)
            && !name.Equals("SPACE", StringComparison.OrdinalIgnoreCase))
        {
            Console.WriteLine("非法命令!");
        }

        if (name.Equals("PRINT", StringComparison.OrdinalIgnoreCase))
        {
            text = context.GetCurrentToken();
            context.NextToken();
        }
    }

    public override void Execute()
    {
        if (name.Equals("PRINT", StringComparison.OrdinalIgnoreCase))
        {
            Console.Write(text);
        }
        else if (name.Equals("SPACE", StringComparison.OrdinalIgnoreCase))
        {
            Console.Write(" ");
        }
        else if (name.Equals("BREAK", StringComparison.OrdinalIgnoreCase))
        {
            Console.Write("\r\n");
        }
    }
}

Program:客戶端測試類

string instruction = "LOOP 2 PRINT 楊過 SPACE SPACE PRINT 小龍女 BREAK END PRINT 郭靖 SPACE SPACE PRINT 黃蓉";
Context context = new Context(instruction);

Node node = new ExpressionNode();
node.Interpret(context);

Console.WriteLine("源指令 : {0}", instruction);
Console.WriteLine("解釋後 : ");

node.Execute();
Console.ReadLine();

四、總結

1、優點

(1)解釋器模式易於改變和擴展文法。由於在解釋器模式中使用類來表示語言的文法規則,因此可以通過繼承等機制來改變或擴展文法。
(2)在解釋器模式中,每一條文法規則都可以表示為一個類,因此可以方便地實現一個簡單的語言。
(3)實現文法較為容易。在抽象語法樹中每一個表達式結點類的實現方式都是相似的,這些類的代碼編寫都不會特別複雜,還可以通過一些工具自動生成結點類代碼。
(4)增加新的解釋表達式較為方便。如果用戶需要增加新的解釋表達式只需要對應增加一個新的終結符表達式或非終結符表達式類,原有表達式類代碼無須修改,符合開閉原則。

2、缺點

(1)解釋器模式對於複雜文法難以維護。在解釋器模式中,每一條規則至少需要定義-個類,因此如果一個語言包含太多的文法規則,類的個數將會急劇增加,從而導致系統難以管理和維護,此時可以考慮使用語法分析程式等方式來取代解釋器模式。
(2)其執行效率較低。由於在解釋器模式中使用了大量的迴圈和遞歸調用,因此在解釋較為複雜的句子時其速度很慢,而且代碼的調試過程也比較麻煩。

測試簽名
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 不要問自己需要什麼樣的人生,而要問自己想要成為什麼樣的人。 我們從前面的學習知道一個 React 組件不僅僅只包含 DOM 結構的,還應該樣式和 Javascript 邏輯的。這裡我們認識邏輯構造之事件處理。 1. React 事件處理 這裡列舉了在 React 中事件的幾種綁定處理方式: impo ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 仿貝殼地圖畫圈找房功能實現(高德地圖) 前言 在最近租房時,看到貝殼找房上線了一個地圖畫圈找房的功能,感覺很是新奇。接觸地圖開發也有很長一段時間了,以前大部分都是基於web pc端開發,所以一般遇到這種圈選,繪製多邊形圓形都是直接基於官方 ...
  • 項目代碼同步至碼雲 weiz-vue3-template 要求代碼規範,主要是為了提高多人協同和代碼維護效率,結合到此項目,具體工作就是為項目配置 eslint 和 prettier。 editorconfig 安裝 EditorConfig for VS Code 插件,根目錄下新建 .edito ...
  • 迴圈練習 1、列印100以內7的倍數 // 需要驗證的是1-100之間的數字 迴圈計數器正好可以表示 // i初始值設置為1 正好可以表示出需要驗證的數字 for (var i = 1; i <= 100; i++) { if (i % 7 == 0) { console.log(i) } } 2、 ...
  • 簡介 nodemon用來監視node.js應用程式中的任何更改並自動重啟服務,非常適合用在開發環境中。以前,我們開發一個node後端服務時,每次更改文件,均需重啟一下,服務才能生效。這使我們的開發效率降低了很多。nodemon的出現,可以隨時監聽文件的變更,自動重啟服務,我們開發時只需關註代碼即可, ...
  • 最近,相信大家一定被這麼個動效給刷屏了: 以至於,基於這個效果的二次創作層出不窮,眼花繚亂。 基於跨視窗通信的彈彈球: 基於跨視窗通信的 Flippy Bird: 我也嘗試製作了一個跨 Tab 視窗的 CSS 動畫聯動,效果如下: 代碼不多,核心代碼 200 行,感興趣的可以戳這裡:Github - ...
  • 專欄分享:vue2源碼專欄,vue3源碼專欄,vue router源碼專欄,玩具項目專欄,硬核💪推薦🙌 歡迎各位ITer關註點贊收藏🌸🌸🌸 Vue3中響應數據核心是 reactive , reactive 的實現是由 proxy 加 effect 組合,上一章節我們利用 proxy 實現了 ...
  • 自增自減運算符 1、基本使用 內置提供 ++、--運算符 是用於將變數本身進行加1或者減1操作 // 1、基本使用 var i = 10; i++;//等價於語句 i+=1 console.log(i);//11 var m = 10; m--; console.log(m) 2、前置與後置的區別 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...