当前位置: 首页 > 科技观察

编程语言中的索引签名是什么?

时间:2023-03-20 22:28:30 科技观察

1。背景最近参与了KusionStack内置领域语言——KCL配置语言编译器的开发。语言语法包括“索引签名”的概念。在参与社区讨论的时候,发现很多小伙伴都不明白这个“索引签名”是什么东西,于是想了想,发现只知道长什么样子,却不知道“索引签名”的完整定义,所以我决定写一篇文章来梳理一下“索引签名”到底是什么。2.见名知义首先,索引签名的思想并不神秘,也不新鲜。你应该在早期的Windows开发中看到过类似的编程规范:bool(BOOL)以b开头bIsParentbyte(BYTE)以bybyFlagshort(int)开头nnStepCountlong(LONG)以l开头lSumchar(CHAR)以c开头cCount看看就好可以通过名称知道变量和类成员的类型,提高代码类型的可读性。但是这种约定在C++语言中还没有落定。如果该语言能够支持将以b开头的成员定义为BOOL类型的特性,那就太好了——这实际上是索引签名的简单目的。先从字面意思说起,“索引签名”包含两部分:“索引”和“签名”。1.索引(index)从开发者的角度来看,索引类似于C语言中的指针。它可以指向特定的事物,例如箭头。由于某些原因,我们可能无法直接访问这个东西,或者这个东西和其他东西混在一起,直接访问这个东西可能需要很长时间才能在很多其他东西中找到。所以,我们用索引来指向这个东西,可以理解为我们在需要的东西上绑一个线程,留下一个线程,不管什么时候要用到这个东西,都不需要从一个开始要在一堆东西中找到它,你只需要拿起这个线程并跟随它找到这个特定的东西。这个线程就是“index索引”,很明显,一个线程是不允许forks绑定两个的,所以通常大家默认一个东西的“index”是不会指向另一个东西的。因此,在发展过程中,“索引索引”的主要使用场景是“在一堆东西中寻找特定的东西”。例如:最常见的数据结构——数组,就是“索引”的绝佳案例。在数组中,索引是一个整数,它是每个元素在数组中的位置信息。通过位置可以快速定位到某个阵元。inta[3]=[1,2,3];//使用索引0,可以快速找到1、2、3这三个数中最前面的元素asserta[0]=1;asserta[1]=2;asserta[3]=3;除了数组之外,另一种使用索引的数据结构就是我们常见的Hash表,但是在一些编程语言中,hash表中的索引被称为key。hashtablea;//"Jack"可以看作是一个索引,以名字string作为索引,//不考虑重名,指向一个结构体实例Person("Jack").a.put("Jack",newPerson("Jack"))a.put("Tom",newPerson("Tom"))a.put("Lee",newPerson("Lee"))作为举个例子,很多编程语言中存在的结构体struct或者class也是用到了索引的思想。//可以看做是String和Integer的集合//如果没有索引,我们只知道Person里面有两个属性,//一种是name的String,//一种是age的Integer。structPerson{name:String,age:Integer,}Personp=newPerson(name:"Jack",age:10);//我们可以很容易的通过索引name得到Person的名字Jack。assertp.name=="Jack"//通过indexage我们可以很容易的得到Person10的年龄assertp.age==10总结一下,index可以看做是一个指针,没有具体的格式constraint,只要能唯一指向一个事物,就不能有歧义,即不能同时指向A和指向B。或者索引也可以看做是一个方法,以索引值作为参数,返回索引指向的东西。注意:这个概念不包括一些特殊情况。比如有的应用场景可能需要同时指向A和B索引。在这里,我们讨论大多数一般情况。2.签名(Signature)在编程语言领域,Signature这个词不仅用在IndexSignature中,在很多常见的编程语言中也有Signature的概念。比如C++中的类型签名:charc;doubled;//他的签名是(int)(char,double)intretVal=(*fPtr)(c,d);通过上面的类型签名,虽然我们不知道这个函数指针以后可能指向的函数的具体定义,但是通过这个签名,我们可以看出这个指针指向的函数是如何被使用的。它以char和double为输入参数,返回值为int,并且这个签名也对以后指针指向的函数做了约束,它只能指向一个以char和double为输入参数并返回的函数一个整数。类似的概念也存在于Rust语言中。在Rust中,我们可以直接使用一个函数,其签名如下://add方法的签名fn(i32,i32)->i32fnadd(left:i32,right:i32)->i32{left+right}//sub方法签名fn(i32,i32)->i32fnsub(left:i32,right:i32)->i32{left-right}//通过方法签名,我们可以为具有类似结构的方法提供工厂。fnselect(name:&str)->fn(i32,i32)->i32{匹配名称{"add"=>add,"sub"=>sub,_=>unimplemented!(),}}fnmain(){letfun=select("add");println!("{}+{}={}",1,2,fun(1,2));}我们再看看Java中的类型签名:可以看到,核心思想和类型是一样的C/C++/Rust中的签名。通过描述一个方法的传入参数和返回值的类型,概述了如何使用一个方法,而无需关心方法的具体实现。Python/Golang也有类型签名的概念,核心思想都是一样的,这里不再赘述。通过了解这些编程语言的类型签名,我们知道签名(Signature)实际上描述的是与类型(Type)相同的事物,而类型(Type)所描述的事物是某些属性的集合。可以认为它们的类型(Type)是相同的;而签名(Signature)可以看作是由多种类型(Type)组成的复合类型。示例:int32a=0;//a的类型是int32。可以看到上面变量a的类型是int32。只要一听到int32,就会条件反射的想到一些属性,比如:32位,整型,范围等,int32就是这些属性的总称,下次遇到变量b,只要由于其属性符合int32的属性,我们可以将它们归为一类,即它们都是int32类型(type)的变量。然而,在一门编程语言的类型系统中,不仅有变量,还有一个非常重要的事物——方法。intadd(inta,intb){returna+b;}现在,我们需要一些东西来描述上面方法的类型,也就是我们需要一些东西来区分什么样的方法与add属于同一类方法。姓名?恐怕不是,因为下面这两个同名的方法用起来感觉完全不一样。//添加两个数字intadd(inta,intb){returna+b;}//合并两个数组int[]add(inta[],intb[]){returna.append(b);因此,大佬们在设计语言的时候,决定用返回值和参数列表的类型(type)的组合来定义一个方法的类型,即://两个数相加//typeFor(int,int)->intintadd(inta,intb){returna+b;}//两个数组合并//typeis(int[],int[])->int[]int[]add(inta[],intb[]){returna.append(b);}而签名(Signature)可以理解为多种类型(type)组合而成的复合类型。这个签名用来描述方法的类型,可以称为方法签名(Method/FunctionSignature)。所以,现在,通过类推,你可以猜出索引签名是什么了。前面说了,索引可以看做是一个方法,输入一个值,返回它指向的东西。3.索引签名(IndexSignature)上面说过,索引可以看成是一个指针或者方法,而签名(Signature)可以理解为多种类型(type)组合而成的复合类型,索引签名(IndexSignature)描述的是索引的类型。写到这里心里有些疑惑。索引签名不就是索引的类型吗?为什么要用复合类型描述索引?一个普通的类型(type)就不能描述索引的类型吗?[0]的类型不是索引Integer吗?hash.get("name")的索引类型不就是String吗?这个问题源于对索引理解的偏差,a[0]的索引不为0,它的索引为0->a[0]``,即输入0,返回[0]`。hash.get("name")索引不是"name",它的索引是"name"->"Jack",输入"name"返回"Jack"。写到这里,使用过各种编程语言的朋友心里应该会有这样的感觉,可能或多或少接触过索引签名,只是当时并不关心它叫什么。之所以这么说是因为我在写这个的时候,想到了我在开发java的时候用到的hashmap:<字符串,字符串>();Sites.put("one","Google");Sites.put("二","Runoob");sites.put("三","淘宝");sites.put("四","知乎");System.out.println(站点);}}上面代码的第七行,HashMapsites=newHashMap(),可以理解为一个索引签名,它定义了这个索引类型HashMap结构,就是输入一个字符串,返回一个字符串。数组的索引签名也类似,只是编译器自动帮我们省略了数组的索引签名,即输入类型必须是int,不需要我们手动写。//显式索引签名:Arraya=[0,1,2]int[]a=[0,1,2];//显式索引签名:Arraya=["0","1","2"]String[]a=["0","1"];3、一些语言中的索引签名索引签名的思想由来已久,甚至可以追溯到早期在这些年程序员为了程序的可读性而定下的编程协议中,当我们规定整数变量的名称必须以i开头,我们实际上是在定义指向整数索引的签名。inti_user_id=10;//以i开头的整数,定义以开头的字符串indexsignaturefloatf_user_weight=120.3;//以f开头的浮点数,定义以开头的字符串的索引签名但是,这个协议可能不是每个人都遵守的。当索引的名称成为编程元素的一部分并且可以动态操作时,使用索引签名作为协议就不是很合适了。//当有可以动态添加索引的编程元素时。consta={"name":"Jack"}//你和你的朋友约定年龄的索引是“age”。//他在某处add("age",10).a.add("age",10);//你在某个地方,需要这个年龄。a.get("age");//如果索引签名是编程规范,则不强制。//你的小伙伴只是手一滑,闭上了眼睛,即使犯错也没有看到警告。a.add("aeg",10);//那么只能看到空指针异常。NullPointerException:“年龄”未定义。因此,为了提高程序的稳定性,避免这种不必要的风险,一些通用编程语言(如:TypeScript)和领域语言(如:KCL、CUE)开始使用索引签名作为该语言的特性暴露给开发者,旨在提供编程过程中的安全性和稳定性,减少上述问题的影响。1.TypeScript索引签名在TS中,我们可以这样定义一个对象:constsalary1={baseSalary:100_000,yearlyBonus:20_000};根据我们上面对索引的描述,我们知道这个对象有两个索引,它们的类型,即索引签名,应该是相同的,即它们是同一类型的索引。constsalary1={baseSalary:100_000,//索引1:输入“baseSalary”,返回100_000yearlyBonus:20_000//索引2:输入“yearlyBonus”,返回20_000};TS提供了一个特性,允许开发者编写这种类型的索引签名:interfaceNumbersNames{[key:string]:string//索引的类型是输入String并返回String}constnames:NumbersNames={'1':'one','2':'two','3':'three',//etc...'5':'five'//错误:这个索引的输入类型是int,类型不是匹配。};2。CUE索引签名CUE支持在索引签名中写入正则表达式,支持对索引名称的校验。a:{foo:string//索引foo的返回值为字符串类型。[=~"^i"]:int//以i开头的索引,返回值都是int。[=~"^b"]:bool//以b开头的索引返回bool。...string//所有其他索引返回值都是字符串。}b:a&{i3:3//索引i3以i开头,返回值为3,类型为int。bar:true//索引bar以b开头,返回值true类型为bool。other:"astring"//其他索引的返回值类型都是字符串。}3。KCL索引签名KCL索引签名的形式为[:]:,语义上表示结构中所有属性的key只能是类型,value只能是oftype类型:索引签名的```处的类型只能是str、int、float,不能是union类型可以是KCL的任何合法数据类型,包括schema类型和union类型表示任何KCL合法标识符,可以省略。一般与支票结合使用。基本用法schema定义方法:schemaMap:[str]:str注意使用索引签名的schema默认是放宽的。索引签名只能在模式中定义一次。Advancedusagetypesignaturewritingdefaultvalue:schemaMap:[str]:str={"default_key":"default_value"}混合schema定义,强制schemakey,valuetype:schemaPerson:name:strage:int的所有属性#错误,与[str]:str语义冲突,[str]:str#schema的所有属性的值只能是string类型。schema属性和索引签名可以同时在schema中定义,通常用于表示schema中的附加属性类型约束,除schema之外定义的所有属性的强制键和值类型。schemaPerson:name:strage?:int[...str]:str#表示除了name和age,其余schema属性必须是string类型,属性的值也必须是string类型。属性名与check一起使用:schemaData:[dataName:str]:strcheck:dataNamein["Alice","Bob","John"]data=Data{Alice:"10"Bob:"12"Jonn:"8"#errorJonnnotin["Alice","Bob","John"]}注意:KCL索引签名目前不支持联合类型和文字类型。注意:索引签名目前不支持value值的校验,只支持类型校验。注意:索引签名目前不支持像CUE["$b^"]这样的常规检查,因为它们属于运行时检查,不属于类型系统。前后运行时阶段的类型检查不容易结合起来,所以暂时不支持。4.小结本文简单介绍了索引签名。通过梳理索引和签名的概念,对比一些通用编程语言和领域语言使用的签名的思想,对索引签名的一般外观进行了概括描述。希望能帮助大家更容易理解索引签名的概念。文章内容仅为作者个人对索引签名的理解。如有不妥或不妥之处,请指正。