aardio教程三) 元表、元方法

来源:https://www.cnblogs.com/kanadeblisst/p/18084708
-Advertisement-
Play Games

前言 還有個迭代器,基礎語法基本已經說完了,後面想到啥再補充,之後的教程會從以下方面來講: 基礎庫的使用,比如string、table等 基礎控制項的使用,比如listview、tab等 aardio和Python交互,比如給Python寫個界面 自帶的範常式序 我寫的一些小程式 當然,我的理解也是很 ...


前言

還有個迭代器,基礎語法基本已經說完了,後面想到啥再補充,之後的教程會從以下方面來講:

  • 基礎庫的使用,比如string、table等
  • 基礎控制項的使用,比如listview、tab等
  • aardio和Python交互,比如給Python寫個界面
  • 自帶的範常式序
  • 我寫的一些小程式

當然,我的理解也是很基礎的,特別是在界面設計上,我都是用的預設控制項的預設設置,不會去自定義控制項內容。要想做出特別炫酷的程式,你還得依賴其他語言和工具的基礎。例如用HTML和CSS來實現界面。

元表、元方法

參考文檔:

  • https://bbs.aardio.com/doc/reference/libraries/kernel/table/meta.html
  • https://bbs.aardio.com/doc/reference/the%20language/operator/overloading.html

主要是用於重載運算符和內置函數的行為。

表可以定義另一個表作為元表,然後在元表裡定義元方法來定義操作符或內置函數操作表的一些行為。這種類似於Python的魔法方法,在Python中使用__eq__定義==的行為,而在aardio中用_eq元方法來定義==的行為。

初級使用例子

舉個例子,python中print會調用對象的__str____repr__來列印一個對象,而aardio也是調用tostring來列印一個對象,但表預設並沒有定義_tostring元方法,導致列印出來的內容是table: 03B2E3A8的格式

我們可以通過給表定義_tostring元方法,來使io.print或者console.log正常顯示表

import console; 
io.open()
var tab = {
	a=1;
	b=2;
	@{
		_tostring = function(...) {
		    // 元方法里不能調用觸發元方法的函數,比如_tostring里不能調用tostring
		    // _get元方法可以通過[[k]]運算符來避開元方法,通過.和[]會觸發_get,而[[]]不會
			return table.tostring(owner);
		}
	}
}
io.print("沒有定義元方法" , {});
io.print("定義了元方法" , tab);
console.pause(true);

輸出如下:

沒有定義元方法  table: 03A8E2F8
定義了元方法    {
a=1;
b=2
}

運算符重載

這個就不細說了,應該很容易理解。

io.open(); tab = { x=10 ; y=20 };
tab2 = { x=12 ; y=22 }
//c = tab + tab2; //這樣肯定會出錯,因為 table預設是不能相加的

//創建一個元素,元表中的__add函數重載加運算符。
tab@ = {
	_add = function(b) { 
		return owner.x + b.x
	};
}

c = tab + tab2; //這時候會調用重載的操作符 tab@._add(tab2)
io.print( c ) //顯示22

入門使用例子

還有一個很常用的元方法是_get_set,是定義訪問對象屬性時觸發的。利用這個可以讓代碼量少很多,看起來邏輯也更清晰。

這裡舉個實際例子,我在封裝sunny的時候,遇到個很累人的事。sunny的dll導出函數,返回值有些是指針,你需要手動給他轉成字元串,而且還需要手動釋放這個指針指向的記憶體,也就是說你調用一次導出函數,就得寫至少三行代碼(調用、轉字元串和釋放)。

那麼,有沒有一種方法,定義完這個導出函數,在使用的時候就調用函數釋放記憶體,並轉成字元串返回,而不用我每次都手動釋放和轉字元串。

先定義request類,現在只需要給它定義一個messageId屬性和_meta元方法:

namespace sunny;

class request{
	ctor(messageId){
		this.messageId = messageId;
	}
	@_meta;
	
}

@後面跟的是元表的名稱,你可以將元表定義在名字空間里,這樣看起來代碼更舒服。下麵在類的名字空間里定義dll方法和元表.

namespace request{
    //釋放指針的函數
	Free = ::SunnyDLL.api("Free","void(pointer p)");
	// 下麵的函數第一個參數都是messageId
    DelRequestHeader = ::SunnyDLL.api("DelRequestHeader","void(int id,str h)");
    GetRequestBodyLen = ::SunnyDLL.api("GetRequestBodyLen","int(int id)");
	GetRequestBody = ::SunnyDLL.api("GetRequestBody","pointer(int id)");
	// 定義一個中間方法
    // name是要調用的導出函數,messageId則是導出函數的第一個參數
	xcall = function(name, messageId, len){
		var func = self[name];
		if(!func) error("不支持的函數!");
		function proxyFunc(...){
			var v = func(messageId, ...);
			var result;
			if(type(v) == type.pointer){
				if(len) result = ..raw.tostring(v,1,len);
				else result = ..raw.tostring(v);
				Free(v);
			}else{
				result = v;
			}
			return result;
		}
		
		return proxyFunc;
	}
	// 定義元表
	_meta = {
		_get = function(k){
			return xcall(k, owner.messageId);
		}
		
	}
}

這個代碼初看可能有點費勁,我們拆解著來看。

首先前面幾行只是定義了四個dll的導出函數,然後下麵定義了_meta這個表。

而_meta里只定義了一個元方法_get,它的作用是當你訪問對象的屬性時會觸發這個方法,然後給你返回值。比如我先實例化一個request對象

r = request(111111);
// 當訪問r.GetRequestBody時,這個對象沒有GetRequestBody屬性,所以會觸發_get元方法
// 得到的返回值就是 返回它的返回值也就是`xcall("GetRequestBody", owner.messageId)`.
console.log(r.GetRequestBody)

這裡的owner就是指r這個對象。然後定義了xcall這個函數,它裡面又定義了一個函數proxyFunc,並將它作為返回值,這種被稱為閉包。先分析下xcall方法

// 這裡的self指的是當前名字空間,也就是request,name則是需要調用的方法名,例如是GetRequestBody
// 這裡func的值就等於GetRequestBody,也就是::SunnyDLL.api("GetRequestBody","pointer(int id)");
var func = self[name];
// 如果func是null的話,說明當前名字空間下沒有這個函數,也就不是我們定義的sunny導出函數
if(!func) error("不支持的函數!");
// 定義了proxyFunc函數,`xcall(k, owner.messageId)`返回的值就是proxyFunc函數,這裡的三個點表示傳入任意個參數,類似於Python中的*args
function proxyFunc(...){
    // 調用GetRequestBody(messageId, ...)
	var v = func(messageId, ...);
	// 定義返回結果
	var result;
	// 如果結果是指針的話
	if(type(v) == type.pointer){
	    // 就把它轉為字元串,二進位數據需要指定長度,不然就是到\0結束
		if(len) result = ..raw.tostring(v,1,len);
		else result = ..raw.tostring(v);
		// 調用導出函數釋放記憶體
		Free(v);
	}else{
	    // 如果是其他類型數據就直接返回,比如數值或null
		result = v;
	}
	return result;
}

這樣一番折騰,起了什麼效果呢,看一下下麵兩段代碼,如果不利用元方法的話,你使用dll導出函數得這麼寫

// 導入request名字空間
improt request;
// 調用名字空間下的函數
var messageId = 111111
var pResult = request.GetRequestBody(messageId);
// 將指針轉為字元串
var result = raw.tostring(pResult,1);
// 釋放記憶體
request.Free(pResult);
// 再使用其他導出函數也需要重覆寫這幾行代碼

看著就幾行代碼,但是你想想調用一個函數都得寫好幾行,如果調用多次呢。而定義了xcall和_meta之後,只需要這樣寫代碼:

improt request;
var messageId = 111111;
var req = request(messageId);
var result = req.GetRequestBody();
// 後面調用都只需要用req.方法名()調用,不需要管raw.tostring和Free了

因為req是可以復用的,所以我調用任何導出函數都只需要寫一行代碼,使用sunny庫的代碼也變得更簡潔易懂了。

官方例子

給表創建一個代理,監控表屬性的訪問和設置:

// 創建一個代理,為另一個table對象創建一個替身以監控對這個對象的訪問
function table.createProxy(tab) {
    var real = tab;//在閉包中保存被代理的數據表tab
    var _meta = {
        _get = function(k){
            io.print(k+"被讀了");
            return real[k];
        };
        _set = function (k,v){
            io.print(k+"被修改值為"+v)
            real[k]=v; //刪除這句代碼就創建了一個只讀表
        }
    }
    var proxy = {@_meta};//創建一個代理表
    
    return proxy; //你要訪問真正的表?先問過我吧,我是他的經紀人!!!
}

//下麵是使用示例

tab = {x=12;y=15};
proxy = table.createProxy(tab);//創建一個代理表,以管理對tab的存取訪問

io.open();
c = proxy.x; //顯示 "x被讀了"
proxy.y = 19; //顯示 "y被修改值為19"
io.print(proxy.y); //顯示 "y被讀了" 然後顯示19

所有的元方法

元方法/屬性 函數定義 Python中的魔法方法 說明
_weak 用不到
_type 屬性 type(obj)函數的行為
_readonly 屬性 等於false,_開頭的成員也不是只讀屬性
_defined 感覺沒啥用
_keys 屬性 可用於table.keys等函數動態獲取對象的鍵名列表(例如動態生成鍵值對的外部JS對象可使用這個元方法返回成員名字列表
_startIndex 屬性 用於table.eachIndex等函數動態指定數組的開始下標。
_get function(k,ownerCall) __getattr____getitem__ 如果讀取表中不存在的鍵會觸發_get元方法並返回值
_set function(k,v) __setattr____setitem__ 當你給表的一個缺少的鍵賦值時會觸發_set元方法
_tostring function(...) __str____repr__ tostring(obj, ...)
_tonumber function() tonumber(obj)
_json function() web.json.stringify(obj),可返回一個可被轉化為json的值。或者返回一個字元串和true
_toComObject 用於自定義一個表對象如何轉換為 COM 對象,可定義為函數,也可以直接定義為對象
_eq function(b) __eq____ne__ ==!=,比較對象時,兩個對象的_eq必須是同一個
_le function(b) __le____ge__ <=>=
_lt function(b) __lt____gt__ <>
_add function(b) __add__ +
_sub function(b) __sub__ -
_mul function(b) __mul__ *
_div function(b) __truediv__ /
_lshift function(b) __lshift__ << 左移
_rshift function(b) __rshift__ >> 右移
_mod function(b) __mod__ % 取模
_pow function(b) __pow__ **冪運算
_unm function() __neg__ - 負號
_len function() __len__ #取長運算符,Python中則為len函數
_concat function(b) ++ 連接運算符
_call function(...) __call__ 對象當函數來調用

屬性元表

不僅可以給對象定義元表,也可以給對象的屬性定義一個元表,有點類似於Python中的property,可以控制屬性修改和獲取的行為。

如果要看例子的話,可以在aardio的目錄全局搜下@_metaProperty

以使用最多的屬性text為例,基本每個控制項都有一個text屬性,你可以很方便的通過.text獲取和修改空間顯示的文字。

其實不用屬性元表也能實現這個效果,代碼如下:

import console; 
class staticText{
	getText = function(){
		..console.log("獲取到界面文本內容")
	};
	setText = function(v){
		..console.log("將文本("+v+")顯示到界面控制項上")
	}
	@_meta;
}

namespace staticText{
    _meta = {
        _get = function(k){
        	if(k == "text"){
        	    return owner.getText();
        	}
        };
        _set = function(k,v){
        	if(k == "text"){
        	    return owner.setText(v);
        	}
        }    
    }
}

s = staticText()
console.log(s.text);
s.text = "修改文本";
console.pause(true);

但是如果屬性多了的話,就需要一堆的if來判斷屬性,所以aardio作者就引入了metaProperty這個功能。這樣寫的代碼看起來更簡潔和清晰,用法如下:

import console; 
import util.metaProperty;

class staticText{
	getText = function(){
		..console.log("獲取到界面文本內容")
	};
	setText = function(v){
		..console.log("將文本("+v+")顯示到界面控制項上")
	}
	@_metaProperty;
}

namespace staticText{
    _metaProperty = ..util.metaProperty(
        text = {
            _get = function(){
            	return owner.getText();
            };
            _set = function(v){
            	return owner.setText(v);
            }
        };
        // 可以寫其他屬性
    );
    // 可以列印下_metaProperty看看
    ..console.dump(_metaProperty);
}

s = staticText()
console.log(s.text);
s.text = "修改文本";
console.pause(true);

本文由博客一文多發平臺 OpenWrite 發佈!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • C++ 簡介 什麼是 C++? C++ 是一種跨平臺的編程語言,可用於創建高性能應用程式。 C++ 是由 Bjarne Stroustrup 開發的,作為 C 語言的擴展。 C++ 為程式員提供了對系統資源和記憶體的高級控制。 該語言在 2011 年、2014 年、2017 年和 2020 年進行了 ...
  • https://leetcode.cn/problems/product-of-array-except-self/description/?envType=study-plan-v2&envId=top-interview-150 問題在於不使用除法並且空間複雜度為O(1),當第一次從頭開始遍歷時 ...
  • 車輛出險報告查詢功能一直以來都是車主們關註的重點,畢竟瞭解一輛車的出險、理賠和事故記錄對於購車和保險的選擇都有著重要影響。而今天,我將向大家介紹一款實用的API介面,通過提供車輛出險報告查詢的功能,幫助車主們快速瞭解車輛的歷史記錄。 這個API介面的使用非常簡單方便,只需要提供車輛的VIN碼和行駛證 ...
  • 你好,這裡是codetrend專欄“SpringCloud2023實戰”。歡迎點擊關註查看往期文章。 註冊中心在前文提到有很多選型,在這裡以Spring Cloud Zookeeper為例說明註冊中心的集成和使用。 選擇Spring Cloud Zookeeper作為註冊中心原因如下: 依賴更少,只 ...
  • 參考指南 fastjson:我一路向北,離開有你的季節 | 素十八 (su18.org) Java 反序列化漏洞始末(3)— fastjson - 淺藍 's blog (b1ue.cn) 梅子酒の筆記本 (meizjm3i.github.io) fastjson基礎 早期版本的 fastjson ...
  • 就在昨晚,Java 22正式發佈!該版本提供了 12 項功能增強,其中包括 7 項預覽功能和 1 項孵化器功能。它們涵蓋了對 Java 語言、API、性能以及 JDK 中包含的工具的改進。 下麵就來一起學習一下該版本都更新了哪些新特性! Unnamed Variables & Patterns - ...
  • 基於Js和Java+MyBatis實現xlsx\xls文檔的導入下載、導出 背景: ​ 實現xlsx\xls文檔的導入、導出 ​ 導入效果: ​ 導出效果: 導出效果圖 1、導入、下載 1.1、前臺 <div style="margin-left: 15px"> <input type="file" ...
  • 本文介紹基於Python中ArcPy模塊,讀取Excel表格數據並生成帶有屬性表的矢量要素圖層,同時配置該圖層的坐標系的方法~ ...
一周排行
    -Advertisement-
    Play Games
  • 基於.NET Framework 4.8 開發的深度學習模型部署測試平臺,提供了YOLO框架的主流系列模型,包括YOLOv8~v9,以及其系列下的Det、Seg、Pose、Obb、Cls等應用場景,同時支持圖像與視頻檢測。模型部署引擎使用的是OpenVINO™、TensorRT、ONNX runti... ...
  • 十年沉澱,重啟開發之路 十年前,我沉浸在開發的海洋中,每日與代碼為伍,與演算法共舞。那時的我,滿懷激情,對技術的追求近乎狂熱。然而,隨著歲月的流逝,生活的忙碌逐漸占據了我的大部分時間,讓我無暇顧及技術的沉澱與積累。 十年間,我經歷了職業生涯的起伏和變遷。從初出茅廬的菜鳥到逐漸嶄露頭角的開發者,我見證了 ...
  • C# 是一種簡單、現代、面向對象和類型安全的編程語言。.NET 是由 Microsoft 創建的開發平臺,平臺包含了語言規範、工具、運行,支持開發各種應用,如Web、移動、桌面等。.NET框架有多個實現,如.NET Framework、.NET Core(及後續的.NET 5+版本),以及社區版本M... ...
  • 前言 本文介紹瞭如何使用三菱提供的MX Component插件實現對三菱PLC軟元件數據的讀寫,記錄了使用電腦模擬,模擬PLC,直至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1. PLC開發編程環境GX Works2,GX Works2下載鏈接 https:// ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • 1、jQuery介紹 jQuery是什麼 jQuery是一個快速、簡潔的JavaScript框架,是繼Prototype之後又一個優秀的JavaScript代碼庫(或JavaScript框架)。jQuery設計的宗旨是“write Less,Do More”,即倡導寫更少的代碼,做更多的事情。它封裝 ...
  • 前言 之前的文章把js引擎(aardio封裝庫) 微軟開源的js引擎(ChakraCore))寫好了,這篇文章整點js代碼來測一下bug。測試網站:https://fanyi.youdao.com/index.html#/ 逆向思路 逆向思路可以看有道翻譯js逆向(MD5加密,AES加密)附完整源碼 ...
  • 引言 現代的操作系統(Windows,Linux,Mac OS)等都可以同時打開多個軟體(任務),這些軟體在我們的感知上是同時運行的,例如我們可以一邊瀏覽網頁,一邊聽音樂。而CPU執行代碼同一時間只能執行一條,但即使我們的電腦是單核CPU也可以同時運行多個任務,如下圖所示,這是因為我們的 CPU 的 ...
  • 掌握使用Python進行文本英文統計的基本方法,並瞭解如何進一步優化和擴展這些方法,以應對更複雜的文本分析任務。 ...
  • 背景 Redis多數據源常見的場景: 分區數據處理:當數據量增長時,單個Redis實例可能無法處理所有的數據。通過使用多個Redis數據源,可以將數據分區存儲在不同的實例中,使得數據處理更加高效。 多租戶應用程式:對於多租戶應用程式,每個租戶可以擁有自己的Redis數據源,以確保數據隔離和安全性。 ...