★Dart-5-理解库和隐私模型

目录

          • 1.定位和导入库
          • 2.隐藏具有库隐私的功能
          • 3.组织库源文件
          • 4.打包库
          • 5.脚本是可运行的库
          • 总结

在本章中,您将学习如何在Dart中创建和使用代码库,以及这些库如何与Dart的隐私模型相关,您可以使用该模型隐藏库的内部工作。库是可部署代码的最小单元,可以小到单个类或函数,也可以大到整个应用程序。在现实世界中,除了最普通的应用程序外,所有应用程序都应该将其代码拆分为多个库,因为这种设计促进了良好的松散耦合体系结构、可重用性和可测试性。通过构建一个简单的记录器框架,您可以将其导入到自己的代码中,在阅读本章时,您将了解这些特性。

当您构建代码包以供重用时,通常存在一些内部工作,您不希望第三方用户能够访问这些工作,除非通过已发布并商定的接口(例如某些类数据的内部状态)进行访问。在Dart中,您可以向您的团队成员或web用户发布代码库,其中只包含您希望使这些最终用户可见的部分。此设置允许在不影响最终用户的情况下更改库的内部结构。它与Java和C#中的不同,Java和C#具有不同的、以类为中心的隐私模型。在这些语言中,类内部可以更改,而不会影响最终用户。

为什么Dart没有以类为中心的隐私模型?
这是Dart特别受JavaScript和web开发影响的领域之一。在JavaScript中,没有隐私的概念,除非遵循某些约定,例如从其他函数返回闭包。因此,Dart隐私模型应该被认为是对JavaScript的改进,而不是将其与更传统的基于类的语言(如Java和C)进行比较。

Dart的可选类型允许您在用户与库交互时在代码中提供文档类型信息(documentary type),例如函数参数和返回类型或类属性,同时允许您仅使用库中您认为必要的类型信息。正如本书前面提到的,类型信息不会改变应用程序的运行方式,但它会为工具和其他开发人员提供文档。

1.定位和导入库

我们将创建一个名叫loglib的日志库,如下图:

库名称的目的是标识可重用的代码块。库名称必须为小写,多个单词之间用下划线分隔。库名称不需要与文件名相同,尽管按照惯例Dart文件也都是小写的,多个单词之间用下划线分隔。与Java不同,Dart中的文件名和文件夹结构之间没有关系。下图显示了一些可以和不能用作库名称的值。

library loglib;

debug(msg) => print("DEBUG:$msg");
warn(msg) => print("WARN:$msg");
info(msg) => print("INFO:$msg");

class Logger {
	log(msg) => print("LOG:$msg");
}


导入的顺序并不重要,因为所有导入都是在代码开始执行之前加载的,但它们必须出现在任何代码语句之前。

如果loglib要导入dart:html库,那么dart:html库将仅对loglib库可用。如果应用程序的其余部分也希望使用dart:html库,那么还需要在应用程序文件的其他位置指定另一条导入语句。对库而言导入语句是本地的。

库导入之间也允许循环引用。例如,库A可以导入库B,库B也可以导入库A,因为Dart库在应用程序开始运行之前已完全加载,所以Dart工具可以理解这种依赖关系。

Dart被设计为转换为JavaScript,这样它就可以在本机不支持Dart的浏览器中运行。JavaScript本身并不支持库:一个多库Dart应用程序在转换时变成一个单独的JavaScript文件,每个导入的库都在其自己的注释部分,并用库名称进行注释,如下所示。

上面Dart语言转换为JavaScript后,如下:

...snip...
// ********** Library loglib **************
// ********** Code for top level **************
function debug(msg) {
	return print$(("DEBUG: " + msg));
}
// ********** Library C:\DartInAction\PackList **************
// ********** Code for PackItem **************
// ********** Code for top level **************
function main() {
	debug("Starting building UI");
	...snip...

请注意,在JavaScript输出中,loglib部分中只存在debug(msg)函数,这是因为您还没有使用其他函数或类,所以它知道不要转换它们。

下面是使用loglib库的示例:

class PackItem {
	// ...snip...
	PackItem(this.itemText) {
		if (itemText.length == 0) {
			warn("User added an empty item");
		} else {
			info("User added an item $itemText");
		}
	}
	DivElement get uiElement {
		if (_uiElement == null) {
			_uiElement = new Element.tag("div");
			_uiElement.classes.add("item");
			_uiElement.text = this.itemText;
			_uiElement.onClick.listen( (event) => isPacked = !isPacked);
			_uiElement.onClick.listen( (event) => info("Item updated");
		}
		return _uiElement;
	}
	// ...snip...
}

使用loglib里面的Logger类:

import "../loglib/loglib.dart";
main() {
	debug("Started building UI");
	// ...snip building the UI
	var logger = new Logger();
	logger.log("Finished building UI");
	// ...snip...
}


使用库前缀防止名称冲突:
这里没有什么可以阻止另一个开发人员使用另一个库,该库还包含一个Logger类和一个顶级info()方法。这是使用导入前缀的地方。


使用as语句定义的前缀后,必须始终使用前缀引用该库中的任何类或函数。尽管可以在每个导入声明中始终使用前缀,但这样做可能会导致代码变得混乱,因为导入库中对每个类和方法的每个引用都需要使用前缀。实用的方法是最好的:仅当添加库前缀有助于可读性和/或防止命名冲突时才添加库前缀,而不是到处使用库前缀。

记住
■ library library_name; 语句必须是库中的第一个语句。
■ 库可以使用import “uri/to/lib.dart”;语句来导入其他库。
■ 库和导入语句必须出现在其他代码之前。
■ 可以使用库前缀来避免不同库之间的命名冲突。

2.隐藏具有库隐私的功能

目前,loglib日志库的所有函数和类都可供库用户使用。任何导入库的应用程序都不会隐藏任何内容。您的所有功能都是公开可用的。下面来讲解使某些项成为私有项,以便无法从库外部访问它们。

在构建功能库时,可能会有一些内部实现细节,您不想向该库的最终用户公开。
loglib库当前包含将数据输出到浏览器控制台的基本功能。假设您想向记录器库添加一个功能,将日志消息发送到某个服务器。您不希望库的外部用户直接调用此服务器日志代码;它需要由入口点函数在内部调用。如果您只是在库中声明类和函数,最终用户将可以访问它们;但幸运的是,Dart允许您通过在项目名称前加下划线(_)来声明项目为私有。

围绕命名约定构建语言功能?
下划线前缀是一种常见(但不一定是通用)的命名约定,用于表示隐私,特别是在没有内置隐私的语言(如JavaScript和Python)中。Dart将此命名约定作为一种语言特性,从而使其更进一步。

这个特性一直是Dart社区争论的话题,它可能是争论的焦点之一。一方面,您可以在开发人员开销很小的情况下获得隐私;在调用站点,您可以看到被调用的内容是私有的,这在您探索新库的内部时非常有用,因为您不需要查找声明。另一方面,它确实会影响可读性,并且可能会有如下代码:
var someValue = new _MyClass()._getValue()._doAction(_withProperty);

反对使用下划线前缀的另一个理由是,如果需要将某个内容从公共更改为私有(反之亦然),则必须在使用该前缀的任何地方重命名该前缀。该论点的另一方面是,如果您正在从私有重命名为公共,那么重命名将只在您的库中发生(如果它当前在您的库中是私有的,那么外部用户将不会使用它)。如果你正在从公有变为私有,那么还有更基本的问题(比如打破库用户的隐私)通过删除函数(而不仅仅是重命名)进行编码。

要记住的两条规则如下:
■ 库中的代码可以访问同一库中的任何其他代码。
■ 库外的代码只能访问该库中的非私有代码。

在loglib库中,您当前有一个Logger类。也许您想通过存储一个内部的_isEnabled属性来确定日志是启用还是禁用的:它的内部状态。使用同一库中的Logger类的其他类可以直接访问内部状态,但库的用户无法访问该内部状态。应用程序的其他部分应该不知道Logger类的工作原理,只知道它可以工作。下图说明了这种关系。

通过使用下划线前缀,您可以在库中构建丰富的功能,并确保只有库用户需要的功能通过定义良好且一致的类、方法和顶级函数接口公开。

通过getter和setter访问私有字段:
如果希望允许外部用户以只读方式访问_isEnabled属性,可以向类中添加公有getter。同样,当您添加公共setter时,该值变为可写。有趣的是,通过只提供一个getter或setter,拥有只读或只写值是完全有效的。

使用私有函数:
类中的私有函数也可以通过在方法名前加下划线来定义。
为了保持代码的可读性和可维护性,可以将代码块提取到同一类的私有方法中,该方法在库外部是不可见的。

私有类的一个困惑:
与在类中具有私有方法和私有属性的方式相同,也可以通过在类名前加下划线在库中创建私有类,如下而清单中的_ServerLogger类所示。私有类非常有用,因为它们只能在库中创建。不能使用new关键字从库外部创建私有类的新实例。
一个有趣的困惑是为什么私有类(只能在库中访问)可以具有公共方法和属性。当你在同一个图书馆时,财产是公共的还是私人的没有区别;如果类是私有的,如何从库外部引用它?下面的代码显示了如何在getServerLogger()函数中实现这一点,该函数返回私有类_ServerLogger的一个新实例。

library mixed_loglib;
class Logger {// Logger类是public的,可以被外面调用直接引用
	_ServerLogger getServerLogger() {// 函数可被外部调用返回一个_ServerLogger实例,但不能直接被外部引用
		return new _ServerLogger();
	}
}
// 私有的_ServerLogger类包含公共和私有属性;这对库没有任何影响,因为整个类都是私有的
class _ServerLogger {
	var serverName;
	var _serverIp;
}

尽管您可以直接访问库外部的私有类,但该库中的公共方法或函数可能会返回私有类的实例。应该避免这种模式,但Dart仍然通过可选的分型来处理它。

从库中返回公共类是有效的,但此类通常由公共隐式接口引用,而不是由其实现类名引用。将在下一章讨论这个想法。

只能将结果存储在动态的可选类型变量中:

Logger logger = new Logger();
var privateInstance = logger.getServerLogger();

即使不能按名称引用_ServerLogger类,但一旦有了它的实例,就可以在该私有实例上访问它的公共属性,而不会受到工具的抱怨。但是,您将无法获得自动完成帮助,因为您无法向工具提供类型信息。如果您试图访问privateInstance._serverIp属性,则会出现noSuchMethod错误,因为您试图从库外部访问私有属性。不过,访问privateInstance.serverName可以正常工作,因为它没有标记为private。除非与公共接口结合使用,否则以这种方式使用库的目的编写库应该被视为不好的做法,因为库的最终用户无法了解如何使用私有类(除了看源码)。

在库中使用很有函数:
库中的顶级函数也可以是公共函数和私有函数,方式与类相同。在私有函数前面加下划线将使其成为库的私有函数,这意味着可以从库中的任何位置访问它。当您希望在库中提供私有实用程序函数,但没有关联的数据,因此它们不保证成为类中的方法时,这可能很有用。

library loglib;

_logMsg(msg) {
	_ServerLogger serverLogger = new _ServerLogger();
	serverLogger.send(msg);
}
info(msg) => _logMsg("INFO $msg");
warn(msg) => _logMsg("DEBUG $msg");
debug(msg) => _logMsg("WARN $msg");

class _ServerLogger {
	// ...snip...
}
class Logger {
	log(msg) => _logMsg(msg);
}

记住
■ private “_”前缀可以应用于函数、类、方法和属性。
■ 标记为private的代码只能从同一库中访问。
■ 该库的外部用户可以访问未标记为私有的代码。

构建具有隐藏内部功能的可重用库是大多数应用程序的标准做法,Dart通过采用下划线约定并将其烘焙到语言中来实现此功能。

3.组织库源文件

尽管现在可以将应用程序拆分为可重用库,但库仍然可以由数千行代码组成。在单个库文件中跟踪所有这些代码可能会很尴尬。幸运的是,Dart提供了一种进一步划分库的方法:将库划分为源文件集合。

loglib库现在包含公共和私有类和函数的混合。如果您要添加更多功能,不久库文件将变得难以导航,即使使用这些工具也是如此。在团队中开发时,更大的问题是,如果您同时在同一个库中工作,对文件的任何重大重构都很容易给团队中的其他开发人员带来问题。

幸运的是,Dart允许您将库拆分为多个源文件。您的库的外部用户对此一无所知,而且无论它是由单个文件、100个文件(每个文件包含一个类或函数)还是类和函数的任意组合构成,对您的库的用户来说都没有区别。

在本节中,您将获取loglib.dart文件,该文件当前包含两个类和四个函数,如下图所示,并将其拆分为单独的源文件。

这些函数和类将分为两个独立的源文件,loglib.dart库文件将它们链接在一起。目标是最终得到总共三个文件,如下图所示。

这只是拆分库的一种方法。您可以将每个类和函数拆分为自己的文件,也可以将所有公共函数和类拆分为一个文件,将所有私有函数和类拆分为另一个文件。在库中,可能有多个功能单元,每个功能单元可能由几个类组成。作为最佳实践,应该将这些功能单元包装到单个源文件中。

Dart提供part关键字,允许您将代码拆分为库中的单独文件。它与library关键字在同一个文件中使用,并且需要提供组成库的其他源文件的相对路径:例如,part "functions.dart";。您可以为classes.dart和functions.dart创建新的空文本文件,并将类和函数剪切粘贴到其中。他们不需要额外的关键字。下面的列表显示了完整的functions.dart文件。

part of loglib;// 表明这个文件是loglib库的一部分

_logMsg(msg) {
	print(msg);
	_ServerLogger serverLogger = new _ServerLogger();
	serverLogger.send(msg);
}
info(msg) => _logMsg("INFO $msg");
warn(msg) => _logMsg("DEBUG $msg");
debug(msg) => _logMsg("WARN $msg");

只能在一个库的上下文中使用它,因为它本身什么也做不到。需要注意的是,part源文件是对代码的提取,本可以保留在原始库文件中,但为了方便开发人员,已被提取到单独的文件中。它与代码在类和函数的公共和私有可见性或转换为JavaScript方面的使用方式无关。


从外部库导入的任何类和函数,如import "dart:html"; 对属于loglib的所有part文件可用。因此,库中每个类和函数之间的关系保持不变,尽管它们是在源文件中组织的。

源文件限制
源文件使用part关键字时,应注意以下限制:
■ 使用part命令导入到库中的文件需要被视为原始库文件的一部分。也就是说,它们不能包含自己的任何语句。如果他们这样做了,就有可能打破library、import和part关键字的严格顺序。
■ 源文件只能属于应用程序中的单个库。loglib和webloglib不能同时使用part "classes.dart";
■ 类或函数必须存在于单个文件中。无法使一个类或函数跨越多个文件(没有像C#中的分部类)。
如果您认为part文件是同一逻辑库文件的一部分,那么这些限制是有意义的。不能让文件的一部分同时也是其他文件的一部分,也不能让库文件包含另一个库语句。在同一个文件中将一个类或函数拆分为两个独立的部分也是不可能的。

重要的是要记住,在不同的part文件中包含类和函数不会影响隐私。它们都被认为是在同一个库中,隐私存在于库级别。

记住
■ 单个库文件可以拆分为多个part文件。
■ 库的外部用户不知道库已被拆分。
■ Dart将库文件拆分为多个物理零件文件,就像它是单个库文件一样。

4.打包库

除了将功能封装到库中并使其可供第三方代码使用外,还可以像脚本一样直接运行库。

在Dart中,包是一个独立的应用程序或一个或多个库,放在一个具有版本号的单元中。软件包有两个用途:它们允许您的应用程序轻松导入其他人的软件包,并且允许您格式化自己的文件结构,从而允许第三方打包和导入您的代码。

pub工具内置于Dart编辑器中,也可从命令行获得,用于导入代码所依赖的包。这些包可以托管在web服务器、GitHub(或其他Git存储库)和pub.dartlang.org存储库中。如果您使用过其他包管理器,如Java的Maven或Node.js的npm,pub将执行类似的功能:它会自动下载应用程序的依赖项和任何嵌套的依赖项。Pub还管理版本控制冲突,包括突出显示嵌套依赖项中的冲突。

一个名为pubspec.yaml的文件位于源文件结构的根目录中,它包含所有重要信息,使pub能够查找和下载依赖项。它使用YAML文件格式:一种人类可读的标记语言,使用缩进定义节和子节。下面清单显示了一个示例pubspec.yaml文件。如果希望将包托管在pub.dartlang.org上,则名称字段是必需的,版本和说明字段也是必需的。其他字段是可选的,但重要的是dependencies,它告诉pub您的依赖项(如果您在核心Dart SDK之外没有依赖项,那么您可以省略dependencies字段)。

Pub通过使用约定而不是配置来工作,它需要为您的应用程序提供特定的布局。关键文件和文件夹如下图所示;幸运的是,当您创建新项目时,Dart编辑器会为您创建此结构。

要将各种依赖项拉入项目,需要使用pub install和pub update命令,这两个命令都存在于Dart编辑器菜单中。这些命令将依赖项的较新版本安装或拉入应用程序的结构中,并创建pubspec.lock文件。包被下载到缓存中,通常是主目录中的.pub cache/文件夹。pubspec.lock文件包含pub安装的不同依赖项的实际版本(在指定版本范围时非常有用)。此文件可以提交到源代码管理中,从而确保团队中的其他开发人员使用相同版本的任何依赖项。

安装依赖项后,可以在库代码中使用import关键字引用它们。例如,PackList应用程序可以使用loglib包,如这行代码所示:import "package:loglib/loglib.dart";

5.脚本是可运行的库

loglib库通过提供大量外部代码可以使用的类和函数,向外部用户提供日志函数。也可以直接运行库——记住Dart脚本只不过是一个包含顶级main()函数的.dart文件。

使用loglib库的一个示例是允许它replay从web服务器加载回开发人员控制台的一系列日志消息。您可以提供一个可公开访问的函数,如replay(url),该函数调用服务器并将每个返回的日志消息发送到现有的私有_logMsg()函数。

运行此新replay功能的一种方法是编写一个单独的应用程序,导入loglib库,然后调用replay()。为了调用单个函数,这似乎需要做很多工作。幸运的是,Dart提供了另一种选择。库还可以包含main()函数,而main()函数是Dart使库可运行所需的全部函数(main()函数是所有Dart脚本的入口点函数)。下面的清单显示了添加到loglib库的main()。

library loglib;
import "dart:html";
part "classes.dart";
part "functions.dart";
main() {
	replay("http://www.someserver.com/logMessages");
}
replay(url) {
	//snip... load msgsFromUrl list
	for (msg in msgsFromUrl) {
		_logMsg(msg);
	}
}

现在,您可以通过在关联的HTML中包含脚本标记,从HTML文件中使用此功能,例如:
<script type="application/dart" src="loglib.dart"></script>
这将在代码完全加载并准备运行后调用main()函数。

尽管将main()保留在库文件(即包含库语句的文件)中是最佳做法,但您可以将main()放在不同的部件文件中。请记住,部件文件中的函数或类与主库文件中的函数或类的性能完全相同,main()函数也完全相同。

这意味着,通过在顶部添加库声明,您创建的每个Dart应用程序也可以成为库。通过这种方式,将现有的Dart应用程序转换为库变得非常简单,通过使现有的Dart应用程序也可以作为库来运行,该库可以嵌入到其他应用程序中。通过向PackList应用程序添加库语句,您可以将其包含在其他应用程序的混搭中,每个应用程序都提供独立的功能,由一个共同的主题组合在一起(见下图)。

Dart具有内置的模块性和灵活性,无论您是从构建库还是应用程序开始,在两者之间切换都非常容易。

总结

Dart提供了一个用于组织和重用源代码的库结构。使用库文件和部件文件,您可以以对团队开发和第三方重用有意义的方式构造代码。

import语句允许应用程序和库以简单的方式导入其他库,同时通过使用库前缀避免命名冲突。基于库的隐私允许您共享库中的代码库、函数库和类库,这些代码库是使用您的库的代码的私有部分。通过添加main()函数,库也可以成为独立的应用程序,main()函数是任何Dart脚本的入口点。

记住
■ 库可以导入其他库。
■ 库也可以用作可运行脚本。
■ 库的源代码可以跨多个部件文件拆分。
■ 库中声明为私有的任何代码都可以从该库的任何其他部分访问。
■ 任何未声明为私有的代码也可以由使用库的代码使用。

现在,您已经了解了Dart,可以构建一个由多个库和文件组成的结构化应用程序,并且了解了Dart的隐私机制与库而不是类的关系,现在是时候深入了解Dart的类、接口和继承机制,以及它们如何适应可选类型的动态世界了。

热门文章

暂无图片
编程学习 ·

C语言二分查找详解

二分查找是一种知名度很高的查找算法&#xff0c;在对有序数列进行查找时效率远高于传统的顺序查找。 下面这张动图对比了二者的效率差距。 二分查找的基本思想就是通过把目标数和当前数列的中间数进行比较&#xff0c;从而确定目标数是在中间数的左边还是右边&#xff0c;将查…
暂无图片
编程学习 ·

GMX 命令分类列表

建模和计算操作命令&#xff1a; 1.1 . 创建拓扑与坐标文件 gmx editconf - 编辑模拟盒子以及写入子组(subgroups) gmx protonate - 结构质子化 gmx x2top - 根据坐标生成原始拓扑文件 gmx solvate - 体系溶剂化 gmx insert-molecules - 将分子插入已有空位 gmx genconf - 增加…
暂无图片
编程学习 ·

一文高效回顾研究生课程《数值分析》重点

数值分析这门课的本质就是用离散的已知点去估计整体&#xff0c;就是由黑盒子产生的结果去估计这个黑盒子。在数学里这个黑盒子就是一个函数嘛&#xff0c;这门课会介绍许多方法去利用离散点最大化地逼近这个函数&#xff0c;甚至它的导数、积分&#xff0c;甚至微分方程的解。…
暂无图片
编程学习 ·

在职阿里5年,一个28岁女软测工程师的心声

简单的先说一下&#xff0c;坐标杭州&#xff0c;14届本科毕业&#xff0c;算上年前在阿里巴巴的面试&#xff0c;一共有面试了有6家公司&#xff08;因为不想请假&#xff0c;因此只是每个晚上去其他公司面试&#xff0c;所以面试的公司比较少&#xff09; ​ 编辑切换为居中…
暂无图片
编程学习 ·

字符串左旋c语言

目录 题目&#xff1a; 解题思路&#xff1a; 第一步&#xff1a; 第二步&#xff1a; 第三步&#xff1a; 总代码&#xff1a; 题目&#xff1a; 实现一个函数&#xff0c;可以左旋字符串中的k个字符。 例如&#xff1a; ABCD左旋一个字符得到BCDA ABCD左旋两个字符…
暂无图片
编程学习 ·

设计模式--观察者模式笔记

模式的定义与特点 观察者&#xff08;Observer&#xff09;模式的定义&#xff1a;指多个对象间存在一对多的依赖关系&#xff0c;当一个对象的状态发生改变时&#xff0c;所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式&#xf…
暂无图片
编程学习 ·

睡觉突然身体动不了,什么是睡眠痽痪症

很多朋友可能有这样的体验&#xff0c;睡觉过程中突然意识清醒&#xff0c;身体却动弹不了。这时候感觉非常恐怖&#xff0c;希望旁边有一个人推自己一下。阳光以前也经常会碰到这样的情况&#xff0c;一年有一百多次&#xff0c;那时候很害怕晚上到来&#xff0c;睡觉了就会出…
暂无图片
编程学习 ·

深入理解C++智能指针——浅析MSVC源码

文章目录unique_ptrshared_ptr 与 weak_ptrstd::bad_weak_ptr 异常std::enable_shared_from_thisunique_ptr unique_ptr 是一个只移型别&#xff08;move-only type&#xff0c;只移型别还有std::mutex等&#xff09;。 结合一下工厂模式&#xff0c;看看其基本用法&#xff…
暂无图片
编程学习 ·

@TableField(exist = false)

TableField(exist false) //申明此字段不在数据库存在&#xff0c;但代码中需要用到它&#xff0c;通知Mybatis-plus在做写库操作是忽略它。,.
暂无图片
编程学习 ·

Java Web day15

第十二章文件上传和下载 一、如何实现文件上传 要实现Web开发中的文件上传功能&#xff0c;通常需要完成两步操作&#xff1a;一.是在Web页面中添加上传输入项&#xff1b;二是在Servlet中读取上传文件的数据&#xff0c;并保存到本地硬盘中。 需要使用一个Apache组织提供一个…
暂无图片
编程学习 ·

【51nod 2478】【单调栈】【前缀和】小b接水

小b接水题目解题思路Code51nod 2478 小b接水 题目 输入样例 12 0 1 0 2 1 0 1 3 2 1 2 1输出样例 6解题思路 可以发现最后能拦住水的都是向两边递减高度&#xff08;&#xff1f;&#xff09; 不管两个高积木之间的的积木是怎样乱七八糟的高度&#xff0c;最后能用来装水的…
暂无图片
编程学习 ·

花了大半天写了一个UVC扩展单元调试工具

基于DIRECTSHOW 实现的&#xff0c;用的是MFC VS2019. 详见&#xff1a;http://www.usbzh.com/article/detail-761.html 获取方法 加QQ群:952873936&#xff0c;然后在群文件\USB调试工具&测试软件\UVCXU-V1.0(UVC扩展单元调试工具-USB中文网官方版).exe USB中文网 USB中文…
暂无图片
编程学习 ·

贪心(一):区间问题、Huffman树

区间问题 例题一&#xff1a;区间选点 给定 N 个闭区间 [ai,bi]请你在数轴上选择尽量少的点&#xff0c;使得每个区间内至少包含一个选出的点。 输出选择的点的最小数量。 位于区间端点上的点也算作区间内。 输入格式 第一行包含整数 N&#xff0c;表示区间数。 接下来 …
暂无图片
编程学习 ·

C语言练习实例——费氏数列

目录 题目 解法 输出结果 题目 Fibonacci为1200年代的欧洲数学家&#xff0c;在他的着作中曾经提到&#xff1a;「若有一只免子每个月生一只小免子&#xff0c;一个月后小免子也开始生产。起初只有一只免子&#xff0c;一个月后就有两只免子&#xff0c;二个月后有三只免子…
暂无图片
编程学习 ·

Android开发(2): Android 资源

个人笔记整理 Android 资源 Android中的资源&#xff0c;一般分为两类&#xff1a; 系统内置资源&#xff1a;Android SDK中所提供的已经定义好的资源&#xff0c;用户可以直接拿来使用。 用户自定义资源&#xff1a;用户自己定义或引入的&#xff0c;只适用于当前应用的资源…
暂无图片
编程学习 ·

零基础如何在短时间内拿到算法offer

​算法工程师是利用算法处理事物的职业 算法&#xff08;Algorithm&#xff09;是一系列解决问题的清晰指令&#xff0c;也就是说&#xff0c;能够对一定规范的输入&#xff0c;在有限时间内获得所要求的输出。 如果一个算法有缺陷&#xff0c;或不适合于某个问题&#xff0c;执…
暂无图片
编程学习 ·

人工智能:知识图谱实战总结

人工智能python&#xff0c;NLP&#xff0c;知识图谱&#xff0c;机器学习&#xff0c;深度学习人工智能&#xff1a;知识图谱实战前言一、实体建模工具Protegepython&#xff0c;NLP&#xff0c;知识图谱&#xff0c;机器学习&#xff0c;深度学习 人工智能&#xff1a;知识图…
暂无图片
编程学习 ·

【无标题】

这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注…