Godot 4 源码分析 - 代码自动补全流程

news2024/12/29 11:08:46

使用Godot 4过程中,有一点比较吸引我:代码自动补全

用RAD开发时,代码自动补全功能一直被吐槽,主要是速度慢

但我看Godot 4中的Script编写过程中,代码补全很快,这个可以研究一下。

研究代码可找到,代码补全触发过程

1) CodeTextEditor中创建时钟code_complete_timer,其timeout超时信号绑定_code_complete_timer_timeout函数。超时量缺省值为0.3,单位应该是秒。

	code_complete_timer = memnew(Timer);
	add_child(code_complete_timer);
	code_complete_timer->set_one_shot(true);
	code_complete_timer->set_wait_time(EDITOR_GET("text_editor/completion/code_complete_delay"));
    ...
    code_complete_timer->connect("timeout", callable_mp(this, &CodeTextEditor::_code_complete_timer_timeout));

2) 编辑器中文本变化时,触发CodeTextEditor::_text_changed,时钟code_complete_timer开始计时。

void CodeTextEditor::_text_changed() {
	if (text_editor->is_insert_text_operation()) {
		code_complete_timer_line = text_editor->get_caret_line();
		code_complete_timer->start();
	}

	idle->start();

	if (find_replace_bar) {
		find_replace_bar->needs_to_count_results = true;
	}
}

而_line_col_changed函数(绑定caret_changed事件)会停止时钟。

void CodeTextEditor::_line_col_changed() {
	if (!code_complete_timer->is_stopped() && code_complete_timer_line != text_editor->get_caret_line()) {
		code_complete_timer->stop();
	}

	String line = text_editor->get_line(text_editor->get_caret_line());

	int positional_column = 0;
	for (int i = 0; i < text_editor->get_caret_column(); i++) {
		if (line[i] == '\t') {
			positional_column += text_editor->get_indent_size(); //tab size
		} else {
			positional_column += 1;
		}
	}

	StringBuilder sb;
	sb.append(itos(text_editor->get_caret_line() + 1).lpad(4));
	sb.append(" : ");
	sb.append(itos(positional_column + 1).lpad(3));

	line_and_col_txt->set_text(sb.as_string());

	if (find_replace_bar) {
		if (!find_replace_bar->line_col_changed_for_result) {
			find_replace_bar->needs_to_count_results = true;
		}

		find_replace_bar->line_col_changed_for_result = false;
	}
}

3)时钟启动后在超时期内未被中止,则会触发timeout信号,调用_code_complete_timer_timeout

void CodeTextEditor::_code_complete_timer_timeout() {
	if (!is_visible_in_tree()) {
		return;
	}
	text_editor->request_code_completion();
}

在CodeEdit::request_code_completion函数中发出信号code_completion_requested

void CodeEdit::request_code_completion(bool p_force) {
	if (GDVIRTUAL_CALL(_request_code_completion, p_force)) {
		return;
	}

	/* Don't re-query if all existing options are quoted types, eg path, signal. */
	bool ignored = code_completion_active && !code_completion_options.is_empty();
	if (ignored) {
		ScriptLanguage::CodeCompletionKind kind = ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT;
		const ScriptLanguage::CodeCompletionOption *previous_option = nullptr;
		for (int i = 0; i < code_completion_options.size(); i++) {
			const ScriptLanguage::CodeCompletionOption &current_option = code_completion_options[i];
			if (!previous_option) {
				previous_option = &current_option;
				kind = current_option.kind;
			}
			if (previous_option->kind != current_option.kind) {
				ignored = false;
				break;
			}
		}
		ignored = ignored && (kind == ScriptLanguage::CODE_COMPLETION_KIND_FILE_PATH || kind == ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH || kind == ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL);
	}

	if (ignored) {
		return;
	}

	if (p_force) {
		emit_signal(SNAME("code_completion_requested"));
		return;
	}

	String line = get_line(get_caret_line());
	int ofs = CLAMP(get_caret_column(), 0, line.length());

	if (ofs > 0 && (is_in_string(get_caret_line(), ofs) != -1 || !is_symbol(line[ofs - 1]) || code_completion_prefixes.has(line[ofs - 1]))) {
		emit_signal(SNAME("code_completion_requested"));
	} else if (ofs > 1 && line[ofs - 1] == ' ' && code_completion_prefixes.has(line[ofs - 2])) {
		emit_signal(SNAME("code_completion_requested"));
	}
}

信号code_completion_requested绑定函数_complete_request,其中调用代理函数code_complete_func进行处理

void CodeTextEditor::_complete_request() {
	List<ScriptLanguage::CodeCompletionOption> entries;
	String ctext = text_editor->get_text_for_code_completion();
	_code_complete_script(ctext, &entries);
	bool forced = false;
	if (code_complete_func) {
		code_complete_func(code_complete_ud, ctext, &entries, forced);
	}
	if (entries.size() == 0) {
		return;
	}

	for (const ScriptLanguage::CodeCompletionOption &e : entries) {
		Color font_color = completion_font_color;
		if (e.insert_text.begins_with("\"") || e.insert_text.begins_with("\'")) {
			font_color = completion_string_color;
		} else if (e.insert_text.begins_with("#") || e.insert_text.begins_with("//")) {
			font_color = completion_comment_color;
		}
		text_editor->add_code_completion_option((CodeEdit::CodeCompletionKind)e.kind, e.display, e.insert_text, font_color, _get_completion_icon(e), e.default_value);
	}
	text_editor->update_code_completion_options(forced);
}

对于Script编辑器而言,code_complete_func指向ScriptTextEditor::_code_complete_scripts,本质上是调用GDScriptLanguage::complete_code函数

void ScriptTextEditor::_code_complete_script(const String &p_code, List<ScriptLanguage::CodeCompletionOption> *r_options, bool &r_force) {
	if (color_panel->is_visible()) {
		return;
	}
	Node *base = get_tree()->get_edited_scene_root();
	if (base) {
		base = _find_node_for_script(base, base, script);
	}
	String hint;
	Error err = script->get_language()->complete_code(p_code, script->get_path(), base, r_options, r_force, hint);

	r_options->sort_custom_inplace<CodeCompletionOptionCompare>();

	if (err == OK) {
		code_editor->get_text_editor()->set_code_hint(hint);
	}
}

在GDScriptLanguage::complete_code中,进行代码解析、语义分析,取得满足条件的候选代码

::Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptLanguage::CodeCompletionOption> *r_options, bool &r_forced, String &r_call_hint) {
	const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\"";

	GDScriptParser parser;
	GDScriptAnalyzer analyzer(&parser);

	parser.parse(p_code, p_path, true);
	analyzer.analyze();

	r_forced = false;
	HashMap<String, ScriptLanguage::CodeCompletionOption> options;

	GDScriptParser::CompletionContext completion_context = parser.get_completion_context();
	completion_context.base = p_owner;
	bool is_function = false;

	switch (completion_context.type) {
		case GDScriptParser::COMPLETION_NONE:
			break;
		case GDScriptParser::COMPLETION_ANNOTATION: {
			List<MethodInfo> annotations;
			parser.get_annotation_list(&annotations);
			for (const MethodInfo &E : annotations) {
				ScriptLanguage::CodeCompletionOption option(E.name.substr(1), ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
				if (E.arguments.size() > 0) {
					option.insert_text += "(";
				}
				options.insert(option.display, option);
			}
			r_forced = true;
		} break;
		case GDScriptParser::COMPLETION_ANNOTATION_ARGUMENTS: {
			if (completion_context.node == nullptr || completion_context.node->type != GDScriptParser::Node::ANNOTATION) {
				break;
			}
			const GDScriptParser::AnnotationNode *annotation = static_cast<const GDScriptParser::AnnotationNode *>(completion_context.node);
			_find_annotation_arguments(annotation, completion_context.current_argument, quote_style, options);
			r_forced = true;
		} break;
		case GDScriptParser::COMPLETION_BUILT_IN_TYPE_CONSTANT_OR_STATIC_METHOD: {
			// Constants.
			{
				List<StringName> constants;
				Variant::get_constants_for_type(completion_context.builtin_type, &constants);
				for (const StringName &E : constants) {
					ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT);
					bool valid = false;
					Variant default_value = Variant::get_constant_value(completion_context.builtin_type, E, &valid);
					if (valid) {
						option.default_value = default_value;
					}
					options.insert(option.display, option);
				}
			}
			// Methods.
			{
				List<StringName> methods;
				Variant::get_builtin_method_list(completion_context.builtin_type, &methods);
				for (const StringName &E : methods) {
					if (Variant::is_builtin_method_static(completion_context.builtin_type, E)) {
						ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);
						if (Variant::get_builtin_method_argument_count(completion_context.builtin_type, E) > 0 || Variant::is_builtin_method_vararg(completion_context.builtin_type, E)) {
							option.insert_text += "(";
						} else {
							option.insert_text += "()";
						}
						options.insert(option.display, option);
					}
				}
			}
		} break;
		case GDScriptParser::COMPLETION_INHERIT_TYPE: {
			_list_available_types(true, completion_context, options);
			r_forced = true;
		} break;
		case GDScriptParser::COMPLETION_TYPE_NAME_OR_VOID: {
			ScriptLanguage::CodeCompletionOption option("void", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
			options.insert(option.display, option);
		}
			[[fallthrough]];
		case GDScriptParser::COMPLETION_TYPE_NAME: {
			_list_available_types(false, completion_context, options);
			r_forced = true;
		} break;
		case GDScriptParser::COMPLETION_PROPERTY_DECLARATION_OR_TYPE: {
			_list_available_types(false, completion_context, options);
			ScriptLanguage::CodeCompletionOption get("get", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
			options.insert(get.display, get);
			ScriptLanguage::CodeCompletionOption set("set", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
			options.insert(set.display, set);
			r_forced = true;
		} break;
		case GDScriptParser::COMPLETION_PROPERTY_DECLARATION: {
			ScriptLanguage::CodeCompletionOption get("get", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
			options.insert(get.display, get);
			ScriptLanguage::CodeCompletionOption set("set", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
			options.insert(set.display, set);
			r_forced = true;
		} break;
		case GDScriptParser::COMPLETION_PROPERTY_METHOD: {
			if (!completion_context.current_class) {
				break;
			}
			for (int i = 0; i < completion_context.current_class->members.size(); i++) {
				const GDScriptParser::ClassNode::Member &member = completion_context.current_class->members[i];
				if (member.type != GDScriptParser::ClassNode::Member::FUNCTION) {
					continue;
				}
				if (member.function->is_static) {
					continue;
				}
				ScriptLanguage::CodeCompletionOption option(member.function->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);
				options.insert(option.display, option);
			}
			r_forced = true;
		} break;
		case GDScriptParser::COMPLETION_ASSIGN: {
			GDScriptCompletionIdentifier type;
			if (!completion_context.node || completion_context.node->type != GDScriptParser::Node::ASSIGNMENT) {
				break;
			}
			if (!_guess_expression_type(completion_context, static_cast<const GDScriptParser::AssignmentNode *>(completion_context.node)->assignee, type)) {
				_find_identifiers(completion_context, false, options, 0);
				r_forced = true;
				break;
			}

			if (!type.enumeration.is_empty()) {
				_find_enumeration_candidates(completion_context, type.enumeration, options);
				r_forced = options.size() > 0;
			} else {
				_find_identifiers(completion_context, false, options, 0);
				r_forced = true;
			}
		} break;
		case GDScriptParser::COMPLETION_METHOD:
			is_function = true;
			[[fallthrough]];
		case GDScriptParser::COMPLETION_IDENTIFIER: {
			_find_identifiers(completion_context, is_function, options, 0);
		} break;
		case GDScriptParser::COMPLETION_ATTRIBUTE_METHOD:
			is_function = true;
			[[fallthrough]];
		case GDScriptParser::COMPLETION_ATTRIBUTE: {
			r_forced = true;
			const GDScriptParser::SubscriptNode *attr = static_cast<const GDScriptParser::SubscriptNode *>(completion_context.node);
			if (attr->base) {
				GDScriptCompletionIdentifier base;
				bool found_type = _get_subscript_type(completion_context, attr, base.type);
				if (!found_type && !_guess_expression_type(completion_context, attr->base, base)) {
					break;
				}

				_find_identifiers_in_base(base, is_function, options, 0);
			}
		} break;
		case GDScriptParser::COMPLETION_SUBSCRIPT: {
			const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(completion_context.node);
			GDScriptCompletionIdentifier base;
			if (!_guess_expression_type(completion_context, subscript->base, base)) {
				break;
			}

			_find_identifiers_in_base(base, false, options, 0);
		} break;
		case GDScriptParser::COMPLETION_TYPE_ATTRIBUTE: {
			if (!completion_context.current_class) {
				break;
			}
			const GDScriptParser::TypeNode *type = static_cast<const GDScriptParser::TypeNode *>(completion_context.node);
			bool found = true;
			GDScriptCompletionIdentifier base;
			base.type.kind = GDScriptParser::DataType::CLASS;
			base.type.type_source = GDScriptParser::DataType::INFERRED;
			base.type.is_constant = true;
			base.type.class_type = completion_context.current_class;
			base.value = completion_context.base;

			for (int i = 0; i < completion_context.current_argument; i++) {
				GDScriptCompletionIdentifier ci;
				if (!_guess_identifier_type_from_base(completion_context, base, type->type_chain[i]->name, ci)) {
					found = false;
					break;
				}
				base = ci;
			}

			// TODO: Improve this to only list types.
			if (found) {
				_find_identifiers_in_base(base, false, options, 0);
			}
			r_forced = true;
		} break;
		case GDScriptParser::COMPLETION_RESOURCE_PATH: {
			if (EDITOR_GET("text_editor/completion/complete_file_paths")) {
				_get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), options);
				r_forced = true;
			}
		} break;
		case GDScriptParser::COMPLETION_CALL_ARGUMENTS: {
			if (!completion_context.node) {
				break;
			}
			_find_call_arguments(completion_context, completion_context.node, completion_context.current_argument, options, r_forced, r_call_hint);
		} break;
		case GDScriptParser::COMPLETION_OVERRIDE_METHOD: {
			GDScriptParser::DataType native_type = completion_context.current_class->base_type;
			while (native_type.is_set() && native_type.kind != GDScriptParser::DataType::NATIVE) {
				switch (native_type.kind) {
					case GDScriptParser::DataType::CLASS: {
						native_type = native_type.class_type->base_type;
					} break;
					default: {
						native_type.kind = GDScriptParser::DataType::UNRESOLVED;
					} break;
				}
			}

			if (!native_type.is_set()) {
				break;
			}

			StringName class_name = native_type.native_type;
			if (!ClassDB::class_exists(class_name)) {
				break;
			}

			bool use_type_hint = EditorSettings::get_singleton()->get_setting("text_editor/completion/add_type_hints").operator bool();

			List<MethodInfo> virtual_methods;
			ClassDB::get_virtual_methods(class_name, &virtual_methods);
			for (const MethodInfo &mi : virtual_methods) {
				String method_hint = mi.name;
				if (method_hint.contains(":")) {
					method_hint = method_hint.get_slice(":", 0);
				}
				method_hint += "(";

				if (mi.arguments.size()) {
					for (int i = 0; i < mi.arguments.size(); i++) {
						if (i > 0) {
							method_hint += ", ";
						}
						String arg = mi.arguments[i].name;
						if (arg.contains(":")) {
							arg = arg.substr(0, arg.find(":"));
						}
						method_hint += arg;
						if (use_type_hint && mi.arguments[i].type != Variant::NIL) {
							method_hint += ": ";
							if (mi.arguments[i].type == Variant::OBJECT && mi.arguments[i].class_name != StringName()) {
								method_hint += mi.arguments[i].class_name.operator String();
							} else {
								method_hint += Variant::get_type_name(mi.arguments[i].type);
							}
						}
					}
				}
				method_hint += ")";
				if (use_type_hint && (mi.return_val.type != Variant::NIL || !(mi.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT))) {
					method_hint += " -> ";
					if (mi.return_val.type == Variant::NIL) {
						method_hint += "void";
					} else if (mi.return_val.type == Variant::OBJECT && mi.return_val.class_name != StringName()) {
						method_hint += mi.return_val.class_name.operator String();
					} else {
						method_hint += Variant::get_type_name(mi.return_val.type);
					}
				}
				method_hint += ":";

				ScriptLanguage::CodeCompletionOption option(method_hint, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);
				options.insert(option.display, option);
			}
		} break;
		case GDScriptParser::COMPLETION_GET_NODE: {
			// Handles the `$Node/Path` or `$"Some NodePath"` syntax specifically.
			if (p_owner) {
				List<String> opts;
				p_owner->get_argument_options("get_node", 0, &opts);

				for (const String &E : opts) {
					r_forced = true;
					String opt = E.strip_edges();
					if (opt.is_quoted()) {
						// Remove quotes so that we can handle user preferred quote style,
						// or handle NodePaths which are valid identifiers and don't need quotes.
						opt = opt.unquote();
					}
					// The path needs quotes if it's not a valid identifier (with an exception
					// for "/" as path separator, which also doesn't require quotes).
					if (!opt.replace("/", "_").is_valid_identifier()) {
						// Ignore quote_style and just use double quotes for paths with apostrophes.
						// Double quotes don't need to be checked because they're not valid in node and property names.
						opt = opt.quote(opt.contains("'") ? "\"" : quote_style); // Handle user preference.
					}
					ScriptLanguage::CodeCompletionOption option(opt, ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH);
					options.insert(option.display, option);
				}

				// Get autoloads.
				for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) {
					String path = "/root/" + E.key;
					ScriptLanguage::CodeCompletionOption option(path.quote(quote_style), ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH);
					options.insert(option.display, option);
				}
			}
		} break;
		case GDScriptParser::COMPLETION_SUPER_METHOD: {
			if (!completion_context.current_class) {
				break;
			}
			_find_identifiers_in_class(completion_context.current_class, true, false, true, options, 0);
		} break;
	}

	for (const KeyValue<String, ScriptLanguage::CodeCompletionOption> &E : options) {
		r_options->push_back(E.value);
	}

	return OK;
}

再排序后

r_options->sort_custom_inplace<CodeCompletionOptionCompare>();

之后加入到编辑器的代码补全选项集合中

	for (const ScriptLanguage::CodeCompletionOption &e : entries) {
		Color font_color = completion_font_color;
		if (e.insert_text.begins_with("\"") || e.insert_text.begins_with("\'")) {
			font_color = completion_string_color;
		} else if (e.insert_text.begins_with("#") || e.insert_text.begins_with("//")) {
			font_color = completion_comment_color;
		}
		text_editor->add_code_completion_option((CodeEdit::CodeCompletionKind)e.kind, e.display, e.insert_text, font_color, _get_completion_icon(e), e.default_value);
	}
	text_editor->update_code_completion_options(forced);

根据显示逻辑,出现代码提示界面

 所以,Godot代码补全逻辑主要关注其处理流程、代码解析、语义分析、候选选项处理、显示

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/674257.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux国产操作系统,UCA-系统工程师学习必备技能,使用dpkg管理软件包、apt命令、内网获取依赖包及源码安装

目录 ​编辑 1.使用dpkg管理软件包 2.apt命令 3.内网获取依赖包 4.源码安装 1.使用dpkg管理软件包 第一种方法当然可以上网搜索软件安装包&#xff0c;下载然后解压成软件。 第二种也就是我接下来要介绍的&#xff0c;dpkg 命令&#xff0c;dpkg 全称叫做debian package…

Linux系统编程:进程的管理和创建

目录 一. 什么是进程 二. Linux对进程的管理方法 2.1 PCB描述进程 2.2 进程的组织 2.3 进程的查看 三. 子进程的创建 3.1 子进程创建函数fork的使用 3.2 子进程创建的原理 四. 总结 一. 什么是进程 进程&#xff08;process&#xff09;是指计算机中已经存在并运行的…

动态规划 DP (一)

1.动态规划&#xff08;Dynamic Programming&#xff0c;简称DP&#xff09; 维基百科的定义说的很清楚&#xff1a; 动态规划不能解决所有的问题&#xff0c; 只能应用于有最优子结构的问题。例如背包问题、最长公共子序列问题、最短路径问题等。 最优子结构&#xff1a;局部…

Vue3通信方式之defineProps、defineEmits、useAttrs、插件mitt和v-model

目录 1、使用defineProps2、使用defineEmits接受自定义事件2.1原生DOM事件2.2自定义事件 3、全局事件总线&#xff08;插件mitt&#xff09;4、v-model5、useAttrs 1、使用defineProps props可以实现父子组件通信,在vue3中我们可以通过defineProps获取父组件传递的数据。且在组…

Opencv医学图片分割-以血管、胼胝体MR等分割为例

用到环境 1、pycharm community edition 2022.3.2 2、Python 3.10 后面应该会传代码到资源&#xff0c;比较需要的可以私信我。 总体设计 图1 扩展实验二“医学图像分割”流程图 ## 具体步骤 1. 导入OpenCV和NumPy库 2. 定义阈值分割方法threshold_segmentation&#xff0c;接…

spring框架-概述(spring特性、生命周期)(一)

文章目录 什么是springspring重要特性spring生命周期知识扩展 什么是spring Spring框架是一个开放源代码的J2EE应用程序框架&#xff0c;由Rod Johnson发起&#xff0c;是针对bean的生命周期进行管理的轻量级容器 ,是为了解决企业级编程开发中的复杂性&#xff0c;实现敏捷开发…

[进阶]网络通信:TCP通信-支持与多个客户端同时通信

目前我们开发的服务端程序&#xff0c;是否可以支持与多个客户端同时通信&#xff1f; &#xfeff;不可以的。&#xfeff;因为服务端现在只有一个主线程&#xff0c;只能处理一个客户端的消息。 代码演示如下&#xff1a; 客户端&#xff1a; public class Client {public…

SpringBoot 异常处理的最佳实践

SpringBoot 异常处理的最佳实践 在 Web 开发中&#xff0c;异常处理是非常重要的一环。在 SpringBoot 框架中&#xff0c;异常处理方式有很多种&#xff0c;但是如何选择最佳实践呢&#xff1f;本文将介绍 SpringBoot 异常处理的最佳实践&#xff0c;并附带代码示例。 异常处理…

flutter - 编写 阿里云-金融级实名认证插件

项目中有实名认证的需求&#xff0c;用户上传身份证反正面&#xff0c;进行人脸核验&#xff0c;后台集成的是阿里云的金融级实名认证SDK&#xff0c;巧合的是阿里云没有packages 需要自己造轮子。 废话不多少&#xff0c;直接上代码&#xff1a; 新建项目 ProjectType Plugin…

网站图片优化技巧及最佳实践

&#x1f482; 个人网站:【海拥】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 目录 前言使用适当的图片格式…

0017-TIPS-pawnyable : eBPF

原文 BPFの導入 検証器とJITコンパイラ eBPFのバグの悪用 题目下载 BPF介绍 BPF 在介绍eBPF之前&#xff0c;先介绍其前身BPF。 随着时代的发展&#xff0c;BPF的用途越来越广泛&#xff0c;扩展也越来越多。在重大更改后的BPF有时被称为eBPF&#xff08;扩展BPF&#xff09…

路漫漫其修远兮,吾将上下而求索

路漫漫其修远兮&#xff0c;吾将上下而求索 一、坚定信念二、持之以恒地努力三、谦虚和学习的态度四、上下而求索也要遵循道德底线和原则五、建立合适的人际关系和互助机制六、坚定自己的信仰和信念七、个人经验与体会 路漫漫其修远兮&#xff0c;吾将上下而求索——这句话不仅…

python期末上机题:

1、编写Python程序&#xff0c;创建类Temperature&#xff0c;其包含成员变量degree&#xff08;表示温度&#xff09;以及实例方法ToHuaShiDu()和ToSheShiDu&#xff0c;并编写测试代码。 程序运行示例&#xff1a; 请输入摄氏温度&#xff1a;30 摄氏温度 30.0&#xff0…

栈和队列(一)

文章目录 顺序表&#xff0c;链表的有点和缺点链表顺序表 栈和队列栈的实现栈的应用&#xff08;括号匹配问题&#xff09; 顺序表&#xff0c;链表的有点和缺点 链表 优点&#xff1a; 1、任意位置插入删除&#xff0c;时间复杂度位O(1) 2、按需申请释放空间 缺点&#xff1a…

Prompt Engineering 面面观

作者&#xff1a;紫气东来 项目地址&#xff1a;https://zhuanlan.zhihu.com/p/632369186 一、概述 提示工程&#xff08;Prompt Engineering&#xff09;&#xff0c;也称为 In-Context Prompting&#xff0c;是指在不更新模型权重的情况下如何与 LLM 交互以引导其行为以获得…

Latex长表格

示例一&#xff1a; 输出一个长表格的示例。 代码&#xff1a; \documentclass[jou,apacite]{apa6} \usepackage{multirow} \usepackage{array} \newcolumntype{P}[1]{>{\centering\arraybackslash}p{#1}} \usepackage{longtable}%\usepackage{showframe} % to visualize…

极致呈现系列之:Echarts漏斗图的流光溢彩

目录 什么是漏斗图漏斗图的特点及应用场景漏斗图的特点漏斗图常见的的应用场景&#xff1a; Echarts中漏斗的常用属性Vue3中创建漏斗图美化漏斗图样式 在数据分析和可视化中&#xff0c;我们经常需要比较不同阶段的数据比例或流程的渐进筛选过程。漏斗图作为一种专门用于展示这…

Qt使用技巧--定义Private类

如果查看Qt的源码&#xff0c;会发现很多类都会有一个***Private类。这是Qt用于封装私有操作的一种设计模式。 给出一个继承自QObject的Private类具体的实现&#xff1a; MyClass.h #include "QObject"class MyClassPrivate; class MyClass: public QObject{ Q_OB…

LangChain让LLM连接更多能力

随着LLM&#xff08;Large language models &#xff09;的发展&#xff0c;不仅仅出现了很多新的应用&#xff0c;一些开发框架也发展很快&#xff0c;典型的就是本文介绍的项目——LangChain&#xff0c;目前LangChain几乎一天一个版本&#xff0c;几个月时间Star数目已经49k…

<C++> C++11 Lambda表达式

C11 Lambda表达式 1.C98中的一个例子 在C98中&#xff0c;如果想要对一个数据集合中的元素进行排序&#xff0c;可以使用std::sort方法。 #include <algorithm> #include <functional> int main() {int array[] {4, 1, 8, 5, 3, 7, 0, 9, 2, 6};// 默认按照小于…