首页 > 编程语言 >搓一个Pythonic list

搓一个Pythonic list

时间:2023-11-02 15:47:05浏览次数:26  
标签:__ Pythonic 一个 type list cast static data

  总所周知,Python语言当中的list是可以存储不同类型的元素的,对应到现代C++当中,可以用std::variant或者std::any实现类似的功能。而Python官方的实现当中用到了二级指针,不过抛开这些,我们也可以自己设计一个list的架构,实现多类型值的存储容器。

  下图是自己实现的list的架构,按照这个架构,我们来逐步分析代码。不过为了节省篇幅,我仅仅只实现了一部分的方法,比如append,但是这里我们着重的是容器的设计。

  我们自顶向下分析。list这个结构体是最终要实现的容器,里面包含了一个指向__list的指针,__list里面存着一系列的Node节点。除了指针,还有offset偏移量,记录当前__list指针ptr的偏移量,size是list的元素大小,而最后一个联合体u则为了实现多值存储而塞的一个成员。Node这边,含有一个void类型的指针,它可以指向任意元素的地址,待会我们会将它转换回对应的元素类型,从而获取其指向的值。type记录该指针指向的具体类型。

  以下对应了这三个结构体的实现。

struct Node {
	void *data = nullptr;
	int type;
};

struct __list {
	Node node;
};

struct list {
	__list *ptr;
	int offset{};
	int size;

	U u;

	list(int size) : size(size) {
		ptr = static_cast<__list *>(malloc(sizeof(__list) * (size + 1)));
	}

	list(const list& other) = default;
	
	~list() {
		ptr -= offset;
		free(ptr);
	}
}

  在分配内存的时候,要注意额外分配多一个空位,因为ptr是指向list最后元素的下一个位置。析构函数的时候也要记得将ptr回退到最开始的位置,不然会出现内存方面的问题。

  在类型方面,这里仅写了几种常用的类型,可以按照实际需要补充更多的类型上去。

enum {
	INT,
	UINT,
	CHAR,
	UCHAR,
	FLOAT,
	DOUBLE
};

  append函数,这里我没有使用泛型实现,而是使用了函数重载,觉得比较好写,以下是int类型的实现,其它类型同理,只需要稍微改改。

void append(uint& __data) {
		ptr->node.data = static_cast<void *>(&__data);
		ptr->node.type = UINT;

		if (offset + 1 <= size) {
			++ptr;
			++offset;
		}
		else 
			std::cout << "The list has achived it's capacity\n";
	}

  另外,还重载了[]运算符,这里就用到了前面所提到的union了,这里设定了返回值为union,这样可以比较巧妙的处理不同返回值的情况。

U operator[](int index) {
		auto it = ptr - offset + index;
		auto __data = it->node.data;
		int type = it->node.type;

		switch (type) {
			case INT: {
				u.intData = *(static_cast<int *>(__data));
				u.type = INT;
				break;
			}
			case UINT: {
				u.uintData = *static_cast<uint *>(__data);
				u.type = UINT;
				break;
			}
			case CHAR: {
				u.charData = *static_cast<char *>(__data);
				u.type = CHAR;
				break;
			}
			case UCHAR: {
				u.ucharData = *static_cast<u_char *>(__data);
				u.type = UCHAR;
				break;
			}
			case FLOAT: {
				u.floatData = *static_cast<float *>(__data);
				u.type = FLOAT;
				break;
			}
			case DOUBLE: {
				u.doubleData = *static_cast<double *>(__data);
				u.type = DOUBLE;
				break;
			}
			default: {
				assert(0);
			}
		}

		return u;
	}

  为了最终可以遍历元素并且输出出来,还需要对union进行重载一下。

struct U {
	union {
		int intData;
		uint uintData;
		char charData;
		u_char ucharData;
		float floatData;
		double doubleData;
	};

	// To figure out which type we're using
	int type;

	friend std::ostream& operator<<(std::ostream& os, const U& u) {
		int type = u.type;

		switch (type) {
			case INT: {
				os << u.intData;
				break;
			}
			case UINT: {
				os << u.uintData;
				break;
			}
			case CHAR: {
				os << u.charData;
				break;
			}
			case UCHAR: {
				os << u.ucharData;
				break;
			}
			case FLOAT: {
				os << u.floatData;
				break;
			}
			case DOUBLE: {
				os << u.doubleData;
				break;
			}
			default: {
				assert(0);
			}
		}

		return os;
	}
};

  (能用switch代替if else就尽量代替)

  到这里,所设计的list就差不多了,剩下的函数可以由读者来拓展。不过还有局限性,可以看看它怎么使用。

int main() {
	list lst{3};

	std::vector v{1, 2, 3};

	for (int i{}; i < v.size(); ++i)
		lst.append(v[i]);
	
	for (int i{}; i < lst.size; ++i)
		std::cout << lst[i] << ' ';
}

  由于没有写对右值数据的处理,所以只能先将想要存的数据存入另一个容器当中。我们再来测试一下。

int main() {
	list lst{3};

	int a = 1;
	double b = 1.1;
	char c = 'c';

	lst.append(a);
	lst.append(b);
	lst.append(c);

	for (int i{}; i < lst.size; ++i)
		std::cout << lst[i] << ' ';
}

  运行结果是1, 1.1, c,符合预期。

  以下是完整代码

#include <iostream>
#include <cstdlib>
#include <cassert>
#include <vector>
#include <type_traits>

enum {
	INT,
	UINT,
	CHAR,
	UCHAR,
	FLOAT,
	DOUBLE
};

struct U {
	union {
		int intData;
		uint uintData;
		char charData;
		u_char ucharData;
		float floatData;
		double doubleData;
	};

	// To figure out which type we're using
	int type;

	friend std::ostream& operator<<(std::ostream& os, const U& u) {
		int type = u.type;

		switch (type) {
			case INT: {
				os << u.intData;
				break;
			}
			case UINT: {
				os << u.uintData;
				break;
			}
			case CHAR: {
				os << u.charData;
				break;
			}
			case UCHAR: {
				os << u.ucharData;
				break;
			}
			case FLOAT: {
				os << u.floatData;
				break;
			}
			case DOUBLE: {
				os << u.doubleData;
				break;
			}
			default: {
				assert(0);
			}
		}

		return os;
	}
};

struct Node {
	void *data = nullptr;
	int type;
};

struct __list {
	Node node;
};

struct list {
	__list *ptr;
	int offset{};
	int size;

	U u;

	list(int size) : size(size) {
		ptr = static_cast<__list *>(malloc(sizeof(__list) * (size + 1)));
	}

	list(const list& other) = default;
	list& operator=(const list& other) = default;

	
	~list() {
		ptr -= offset;
		free(ptr);
	}

	void append(int& __data) {
		if (offset + 1 <= size) {
			ptr->node.data = static_cast<void *>(&__data);
			ptr->node.type = INT;
			++ptr;
			++offset;
		}
		else 
			std::cout << "The list has achived it's capacity\n";
	}



	void append(float& __data) {
		ptr->node.data = static_cast<void *>(&__data);
		ptr->node.type = FLOAT;

		if (offset + 1 <= size) {
			++ptr;
			++offset;
		}
		else 
			std::cout << "The list has achived it's capacity\n";
	}



	void append(double& __data) {
		ptr->node.data = static_cast<void *>(&__data);
		ptr->node.type = DOUBLE;

		if (offset + 1 <= size) {
			++ptr;
			++offset;
		}
		else 
			std::cout << "The list has achived it's capacity\n";
	}


	void append(char& __data) {
		ptr->node.data = static_cast<void *>(&__data);
		ptr->node.type = CHAR;

		if (offset + 1 <= size) {
			++ptr;
			++offset;
		}
		else 
			std::cout << "The list has achived it's capacity\n";
	}
	

	void append(u_char& __data) {
		ptr->node.data = static_cast<void *>(&__data);
		ptr->node.type = UCHAR;

		if (offset + 1 <= size) {
			++ptr;
			++offset;
		}
		else 
			std::cout << "The list has achived it's capacity\n";
	}

	void append(uint& __data) {
		ptr->node.data = static_cast<void *>(&__data);
		ptr->node.type = UINT;

		if (offset + 1 <= size) {
			++ptr;
			++offset;
		}
		else 
			std::cout << "The list has achived it's capacity\n";
	}

	U operator[](int index) {
		auto it = ptr - offset + index;
		auto __data = it->node.data;
		int type = it->node.type;

		switch (type) {
			case INT: {
				u.intData = *(static_cast<int *>(__data));
				u.type = INT;
				break;
			}
			case UINT: {
				u.uintData = *static_cast<uint *>(__data);
				u.type = UINT;
				break;
			}
			case CHAR: {
				u.charData = *static_cast<char *>(__data);
				u.type = CHAR;
				break;
			}
			case UCHAR: {
				u.ucharData = *static_cast<u_char *>(__data);
				u.type = UCHAR;
				break;
			}
			case FLOAT: {
				u.floatData = *static_cast<float *>(__data);
				u.type = FLOAT;
				break;
			}
			case DOUBLE: {
				u.doubleData = *static_cast<double *>(__data);
				u.type = DOUBLE;
				break;
			}
			default: {
				assert(0);
			}
		}

		return u;
	}

};

  到这里,一个Pythonic的list就成型了,剩下的其它函数实现方式也就大同小异。在设计list的时候,由于设计到指针,因此对于内存泄露方面需要比较谨慎。以上的实现仅仅涉及到了一级指针,Python官方实现是采用二级指针,感兴趣的话可以去学习学习别人是怎么实现的~

标签:__,Pythonic,一个,type,list,cast,static,data
From: https://www.cnblogs.com/ChebyshevTST/p/17805547.html

相关文章

  • 详解Java LinkedList
    LinkedList简介LinkedList是List接口的实现类,基于双向链表实现,继承自AbstractSequentialList类,同时也实现了Cloneable、Serializable接口。此外还实现了Queue和Deque接口,可以作为队列或双端队列使用。LinkedList的插入删除时间复杂度:在头部或尾部插入删除元素,只需要修改头节......
  • 分享一个项目:`learning_go_plan9_assembly`, 学习 golang plan9 汇编
    作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢!cnblogs博客zhihuGithub公众号:一本正经的瞎扯近期在学习golangplan9汇编,总算基本做到了手写汇编,并整理了很多笔记。plan9汇编的资料少,难学,难用。可能也有想学习汇编的人会遇到与我一样的问题。于是把笔记进......
  • 在使用docker-compose build一个faq服务Helpy 时报错
    Helpy时报错如下:ERROR:failedtosolve:process"/bin/sh-cbundleinstall--withouttestdevelopment"didnotcompletesuccessfully:exitcode:11ERROR:Service'helpy'failedtobuild:Buildfailed有两种解释这种报错1、修改dockerfile ruby:2.5,然后......
  • LuaHttp库写的一个简单的爬虫
    LuaHttp库是一个基于Lua语言的HTTP客户端库,可以用于爬取网站数据。与Python的Scrapy框架类似,LuaHttp库也可以实现网站数据的抓取,并且可以将抓取到的数据保存到数据库中。不过需要注意的是,LuaHttp库并不像Scrapy框架那样具有完整的爬虫框架功能,需要自己编写代码实现。同时,LuaHttp库......
  • 分享一个HTML页面适配方式:用户手动缩放
    <metaname="viewport"content="width=device-width,initial-scale=1.0">这个配置告诉浏览器自动将页面的宽度设置为设备的宽度(通常是屏幕宽度),并将初始缩放比例设置为1.0。这通常用于确保网页在移动设备上以完整的屏幕宽度显示,而不需要用户手动缩放或调整。<metaname="viewpo......
  • 不会代码,也能批量数据合并,使用Python开发一个图形交互界面
    不会代码,也能批量数据合并,使用Python开发一个图形交互界面大话数据分析​​京东物流经营分析岗​关注他 作为一名数据分析师,日报,周报,月报是少不了的,经常在整理周报或者月报的时候,需要将这周的数据或者该月的数据进行一个汇总,常规地做法是将每一天的数据......
  • Java踩坑之List的removeAll方法
    最近写个功能,需要用到差集,然后就想到了javaList中有一个removeAll方法,正好可以实现差集功能,可以直接调用。我们知道,apache的common-collections包下面得CollectionUtils.subtract()方法也可以对List作差集,为了比较两种方式差集的结果,见Java中CollectionUtils.subtract()......
  • 我们在开发第一个flutter小程序时需要注意什么
    Flutter这些年发展的很快,特别是在Google持续的加持下,FlutterSDK的版本号已经来到了3开头,也正式开始对Windows、macOS和Linux桌面环境提供支持。如果从Flutter特有的优势来看,我个人认为主要是它已经几乎和原生的性能表现没什么太大的差别,这一点是ReactNative和Vue......
  • springboot正常启动的时候,@Configuration的@Bean属于初始化就得加载的,当该springboot
      ......
  • smartdns 一个强大的dns 服务器
    参考架构 一个集成其他dns的参考玩法 集成示例基于docker-compose运行docker-compose.yamlversion:"3"services:pdnsadmin:image:powerdnsadmin/pda-legacy:0.3networks:dns:ipv4_address:172.16.238.9......