1.Lua设计与实现--Table篇
2.大文件处理(上传,下载)思考
3.Underscore源码分析
4.c# list中查找元素是否存在,如果存在返回索引,下面的代码为什么报错?
Lua设计与实现--Table篇
本文系《Lua设计与实现》系列文章第四篇,聚焦于Lua的表(table)结构,基于该书第4章内容与Lua 5.3源码进行总结。sdl源码使用尽管书中的示例基于Lua 5.1,本文将尽量保持与书中的代码逻辑一致,以供读者参考。
表设计的核心理念在于集多功能于一身,简化开发者对类型的关注。Lua通过表这一结构,实现了将数据存储、索引、映射等多种功能集成,使得开发者能够在表上进行操作,同时保持语言简洁性和灵活性。表的实现巧妙地结合了数组和哈希表,提供高效的数据访问与管理。
表的vnc修改源码数据结构分为两部分:实现容器的数组和哈希表。数组用于存储键值对,哈希表则用于快速查找。数组部分允许基于整型键进行高效访问,而哈希表则提供非整型键的快速查找机制。数组和哈希表之间的平衡与转换,是表实现的关键。
表的重要操作包括查询、新增元素和迭代访问。查询操作区分整型键与非整型键,前者直接从数组访问,后者通过哈希表查找。新增元素时,核心步骤是新增键,通过luaH_newkey函数实现。函数中包含rehash操作,以动态调整数组和哈希表的大小,保持性能优化。rehash操作包括遍历数组和哈希表,更新使用计数,git源码工具计算并调整数组与哈希表大小,确保空间利用效率。
迭代操作主要通过ipairs和pairs函数实现。这两个函数在虚拟机内部创建临时变量,通过调用luaH_next函数进行迭代访问。该函数根据findindex函数定位表的数组或哈希表部分,以数组或哈希表的分布决定访问路径,优化遍历效率。
大文件处理(上传,下载)思考
文件处理一直都是前端人的心头病,如何控制好文件大小,文件太大上传不了,文件下载时间太长,tcp直接给断开了等效果为了方便大家有意义的学习,这里就先放效果图,如果不满足直接返回就行,不浪费大家的时间。
文件上传文件上传实现,分片上传,alz算法源码暂停上传,恢复上传,文件合并等
文件下载为了方便测试,我上传了1个1g的大文件拿来下载,前端用的是流的方式来保存文件的,具体的可以看这个apiTransformStream
正文本项目的地址是:/post/
requestIdleCallback有不明白的可以看这里:/post/
接下来咋们来计算文件的hash,计算文件的hash需要使用spark-md5这个库,
全量计算文件hashexportasyncfunctioncalcHashSync(file:File){ //对文件进行分片,每一块文件都是分为2MB,这里可以自己来控制constsize=2**;letchunks:any[]=[];letcur=0;while(cur<file.size){ chunks.push({ file:file.slice(cur,cur+size)});cur+=size;}//可以拿到当前计算到第几块文件的进度lethashProgress=0returnnewPromise(resolve=>{ constspark=newSparkMD5.ArrayBuffer();letcount=0;constloadNext=(index:number)=>{ constreader=newFileReader();reader.readAsArrayBuffer(chunks[index].file);reader.onload=e=>{ //累加器不能依赖index,count++;//增量计算md5spark.append(e.target?.resultasArrayBuffer);if(count===chunks.length){ //通知主线程,计算结束hashProgress=;resolve({ hashValue:spark.end(),progress:hashProgress});}else{ //每个区块计算结束,通知进度即可hashProgress+=/chunks.length//计算下一个loadNext(count);}};};//启动loadNext(0);});}全量计算文件hash,在文件小的时候计算是很快的,但是在文件大的情况下,计算文件的hash就会非常慢,并且影响主进程哦
抽样计算文件hash抽样就是取文件的一部分来继续,原理如下:
/***抽样计算hash值大概是1G文件花费1S的时间**采用抽样hash的方式来计算hash*我们在计算hash的时候,将超大文件以2M进行分割获得到另一个chunks数组,*第一个元素(chunks[0])和最后一个元素(chunks[-1])我们全要了*其他的女神漫画源码元素(chunks[1,2,3,4....])我们再次进行一个分割,这个时候的分割是一个超小的大小比如2kb,我们取*每一个元素的头部,尾部,中间的2kb。*最终将它们组成一个新的文件,我们全量计算这个新的文件的hash值。*@paramfile{ File}*@returns*/exportasyncfunctioncalcHashSample(file:File){ returnnewPromise(resolve=>{ constspark=newSparkMD5.ArrayBuffer();constreader=newFileReader();//文件大小constsize=file.size;letoffset=2**;letchunks=[file.slice(0,offset)];//前面2mb的数据letcur=offset;while(cur<size){ //最后一块全部加进来if(cur+offset>=size){ chunks.push(file.slice(cur,cur+offset));}else{ //中间的前中后去两个字节constmid=cur+offset/2;constend=cur+offset;chunks.push(file.slice(cur,cur+2));chunks.push(file.slice(mid,mid+2));chunks.push(file.slice(end-2,end));}//前取两个字节cur+=offset;}//拼接reader.readAsArrayBuffer(newBlob(chunks));//最后Kreader.onload=e=>{ spark.append(e.target?.resultasArrayBuffer);resolve({ hashValue:spark.end(),progress:});};});}这个设计是不是发现挺灵活的,真是个人才哇
在这两个的基础上,咋们还可以分别使用web-worker和requestIdleCallback来实现,源代码在hereヾ(≧▽≦*)o
这里把我电脑配置说一下,公司给我分的电脑配置比较lower,8g内存的老机器。计算(3.3g文件的)hash的结果如下:
结果很显然,全量无论怎么弄,都是比抽样的更慢。
文件分片的方式这里可能大家会说,文件分片方式不就是等分吗,其实还可以根据网速上传的速度来实时调整分片的大小哦!
consthandleUpload1=async(file:File)=>{ if(!file)return;constfileSize=file.sizeletoffset=2**letcur=0letcount=0//每一刻的大小需要保存起来,方便后台合并constchunksSize=[0,2**]constobj=awaitcalcHashSample(file)as{ hashValue:string};fileHash.value=obj.hashValue;//todo判断文件是否存在存在则不需要上传,也就是秒传while(cur<fileSize){ constchunk=file.slice(cur,cur+offset)cur+=offsetconstchunkName=fileHash.value+"-"+count;constform=newFormData();form.append("chunk",chunk);form.append("hash",chunkName);form.append("filename",file.name);form.append("fileHash",fileHash.value);form.append("size",chunk.size.toString());letstart=newDate().getTime()//todo上传单个碎片constnow=newDate().getTime()consttime=((now-start)/).toFixed(4)letrate=Number(time)///速率有最大和最小可以考虑更平滑的过滤比如1/tanif(rate<0.5)rate=0.5if(rate>2)rate=2offset=parseInt((offset/rate).toString())chunksSize.push(offset)count++}//todo可以发送合并操作了}ATTENTION!!!?如果是这样上传的文件碎片,如果中途断开是无法续传的(每一刻的网速都是不一样的),除非每一次上传都把chunksSize(分片的数组)保存起来哦
控制/post/Underscore源码分析
JavaScript,作为最被低估的编程语言之一,自从Node.js的出现,全端开发(All Stack/Full Stack)概念日渐兴起,现今,其地位不可小觑。JavaScript实质上是一种类C语言,对于具备C语言基础的学习者,理解JavaScript代码大体上较为容易,然而,作为脚本语言,JavaScript的灵活性远超C语言,这在一定程度上给学习者带来了一定的困难。
集合是JavaScript中一种重要的概念,下面我们就来看看其中的几个迭代方法。
首先,集合中的迭代方法包括`_.each`和`_.forEach`,这两个方法在功能上基本一致,主要用于对集合进行遍历。它们接受三个参数:集合、迭代函数和执行环境。其中,`_.each`和`_.forEach`在ES6中为数组添加了原生的`forEach`方法,但后者更灵活,能够应用于所有集合。
`_.each`和`_.forEach`在遍历时会根据集合的类型(类数组或对象)调用不同的实现。如若集合有`Length`属性且为数字且在0至`MAX_ARRAY_INDEX`之间,则判定为类数组,否则视为对象集合。在遍历过程中,`_.each`和`_.forEach`会根据集合的特性使用合适的迭代方式。
在处理集合时,`_.map`和`_.reduce`方法的实现原理类似,`_.map`用于获取集合中元素的映射结果,而`_.reduce`则用于逐元素执行函数并逐步聚合结果。
此外,`_.find`函数与`Array.some()`具有相似性,不同之处在于`_.find`返回第一个使迭代结果为真的元素,而`Array.some()`则返回一个布尔值。`_.find`和`_.detect`函数基于`_.findIndex`和`_.findLastIndex`实现,它们分别在正序和反序的情况下查找满足条件的元素。
在处理集合时,`_.max`方法用于寻找集合中的最大值,通过循环比较集合中的所有项,最终返回最大值。`_.toArray`则负责将各种类型的集合转换为数组,确保数据的格式统一。对于数组、类数组对象、普通对象以及null或undefined的情况,`_.toArray`分别采用了不同的处理方式,确保了转换过程的灵活性与准确性。
至于集合转换为数组的问题,JavaScript中的数据类型多样,理解它们之间的区别对于开发者来说至关重要。然而,`_.toArray`函数的设计似乎更侧重于处理特定类型的数据,而不仅仅基于JavaScript的基本数据类型。在实际应用中,开发者需要根据具体场景灵活运用这些工具,以实现高效、准确的数据处理。
c# list中查找元素是否存在,如果存在返回索引,下面的代码为什么报错?
class Program{ static void Main(string[] args) { List<db> ls = new List<db> { }; ls.Add(new db() { id = 1, Name = "张三" }); ls.Add(new db() { id = 1, Name = "李四" }); ls.Add(new db() { id = 2, Name = "王五" }); string str = "李四"; Console.WriteLine(ls.FindIndex(x => x.Name == str)); //不存在:返回-1,存在:返回位置。 Console.ReadKey(); }}class db{ public int id; public string Name;}一. 基本概念
代码就是程序员用开发工具所支持的语言写出来的源文件,是一组由字符、符号或信号码元以离散形式表示信息的明确的规则体系。代码设计的原则包括唯一确定性、标准化和通用性、可扩充性与稳定性、便于识别与记忆、力求短小与格式统一以及容易修改等。
二. 作用
源代码主要功用有如下2种作用:
生成目标代码,即计算机可以识别的代码。
对软件进行说明,即对软件的编写进行说明。为数不少的初学者,甚至少数有经验的程序员都忽视软件说明的编写,因为这部分虽然不会在生成的程序中直接显示,也不参与编译。但是说明对软件的学习、分享、维护和软件复用都有巨大的好处。
需要指出的是,源代码的修改不能改变已经生成的目标代码。如果需要目标代码做出相应的修改,必须重新编译。