`
RednaxelaFX
  • 浏览: 3067365 次
  • 性别: Icon_minigender_1
  • 来自: 海外
社区版块
存档分类
最新评论

方法分派(method dispatch)的几个例子

阅读更多
昨天发的帖只是提到了单一分派(single-dispatch)和多分派(multiple-dispatch),没有说明它们到底是什么。这里简单解释一下方法分派的概念。

追加:请留意后续帖C# 4的方法动态分派逻辑变了……中的内容。本帖所描述的C# 4.0的行为只适用于VS2010CTP的版本,将不适用于C# 4.0正式版。

在程序设计语言中,许多时候同一个概念的操作或运算可能需要针对不同数量、不同类型的数据而做不同的处理。既然是“同一概念”,如果能用同样的名字来命名这个操作或运算的函数,会有助于程序代码清晰的表达出语义。但是函数的名字一样了,程序该如何判断应该选用同名函数的哪个版本就成了个问题,这里就需要在编译时由编译器来选择,或在运行时进行方法分派。
参数的数量、类型等信息组成了函数的signature。在不同语言中,函数的signature不仅可以包含参数的数量、类型,也可能包含参数的结构/模式,甚至可能包括返回类型的数量和类型;这超出了本文的范围,下面将简单的认为signature只是针对指参数的数量和类型而言。

在过程式语言中,使用同一个名字来命名signature不同的函数,称为函数重载(function overloading)。可惜C语言并不支持函数重载,这里就用只使用了过程式程序设计的语法结构的C++代码来说明:
#include <cstdio>

void foo( int i ) {
    printf( "foo( int )\n" );
}

void foo( int i, int j ) {
    printf( "foo( int, int )\n" );
}

void foo( double d ) {
    printf( "foo( double )\n" );
}

int main( ) {
    int i, j;
    foo( i );    // foo( int )
    foo( i, j ); // foo( int, int )

    double d;
    foo( d );    // foo( double )

    return 0;
}

函数重载是编译时概念。参数类型是由变量声明的类型所决定的。上面的代码中虽然没有给局部变量i、j和d赋值,编译器已经有足够信息来判断应该采用哪个版本的foo()。

在面向对象程序设计语言中,同一个继承链上的不同类型可以拥有signature相同的虚方法,表现出多态。观察以下Java代码:
class A {
    public void foo( int i ) {
        System.out.println( "A.foo( int )" );
    }
}

class B extends A {
    @Override
    public void foo( int i ) {
        System.out.println( "B.foo( int )" );
    }
}

public class Program {
    public static void main( String[ ] args ) {
        A b = new B( );
        b.foo( 0 ); // B.foo( int )
    }
}

Java中的成员方法(非静态方法)都是虚方法。这里的A.foo(int)与B.foo(int)就是同一继承链上signature相同的两个虚方法,B.foo(int)覆盖(override)A.foo(int)。从语义上说,在编译时无法判断一个虚方法调用到底应该采用继承链上signature相同的哪个版本,所以要留待运行时进行分派(dispatch)。上面的例子中,可以看到虽然局部变量b的类型是A,但b.foo(0)调用的是b所指向的对象的实际类型B上的foo(int)方法。
当然,在静态类型的面向对象程序设计语言中,函数仍然是可以重载的。所以上面的例子也可以有:
class B extends A {
    @Override
    public void foo( int i ) { }

    public void foo( int i, int j ) { }

    public void foo( double d ) { }
}

这里,只有B.foo(int)对A.foo(int)表现出运行时多态,B.foo(int,int)与B.foo(double)只是对B.foo(int)的重载。
注意到在单一分派静态类型的面向对象语言中,重载仍然是编译时概念:编译器只会根据静态变量的类型来判断选择哪个版本的重载,而不像运行时多态那样根据值的实际类型来判断。

那么单一分派(single-dispatch)是什么意思?
假如把上面的Java例子的类型声明用伪C来展开,可以变成类似这样:
假设B*能隐式转换到A*。
下面代码无法表示B*到A*的隐式转换,也不支持重载,所以只能是伪C了,凑合看看吧)
typedef struct {
    FOOPTR foo;
} A;

typedef struct {
    FOOPTR foo;
} B;

void foo( A* this, int i ) { }
void foo( B* this, int i ) { }

那么在调用的时候可以看作:
A* b = ( A* ) malloc( sizeof( B ) );
foo( b, 0 );

这里想表达的是,面向对象语言中经常会对函数调用的第一个参数做特殊处理,包括语法和语义都很特别。语法的特别之处在于实际上的第一个参数不用写在参数列表里,而是写在某种特殊符号之前(b.foo(0)的“.”),也就是所谓的隐含参数。语义的特别之处在于这第一个参数的称为方法调用的接收者(reciever);它的实际类型会参与到方法分派的判断中,而其余的参数要么只参与静态类型判断(单一分派+方法重载),要么也以实际类型参与到方法分派的判断(多分派)。

再看看昨天的帖里我举的例子。假设有下面的类型声明:
public class A { }
public class B : A { }

public class Foo {
    public virtual void Bar( A a ) { }
    public virtual void Bar( B b ) { }
}

public class Goo : Foo {
    public override void Bar( A a ) { }
    public override void Bar( B b ) { }
}

那么Bar(A)与Bar(B)之间的关系就是方法重载,而Foo.Bar(A)与Goo.Bar(A)之间的关系就是继承关系中的虚方法覆盖。然后在这样的代码中:
Foo goo = new Goo( );
A b = new B( );

变量goo与b的静态类型都与它们实际所指向的值的实际类型不同:

C#的方法分派是单一分派的,所以在以下调用中:
goo.Bar( b ); // Goo.Bar( A )

这里goo就是隐含的第一个参数,而b是第二个参数。
虽然变量goo的静态类型是Foo,但因为它指向的值得实际类型是Goo而且Bar()是虚函数,所以选用Goo上的Bar()。
对参数b的处理则不同。编译器只看到它的静态类型是A,所以在编译时就决定选用Bar(A)而不是Bar(B)。

在C# 4增加了“动态类型”之后,如果一个虚方法调用的接收者或者任意的参数的类型是dynamic,那么整个方法调用都无法在编译时判定到底应该选用哪个具体版本。所以,底层的运行时库会根据运行时接收者和每个参数的实际类型来进行方法分派。这个语义就与多分派的语义一样了。因此:
Foo goo = new Goo( );
dynamic b = new B( );
goo.Bar( b ); // Goo.Bar( B )

在这个goo.Bar(b)中,虽然只有b是dynamic类型的,但这会让编译器认为整个方法调用的分派都需要留到运行时来做。到运行时,首先知道goo的静态类型是Foo,再看b的实际类型——是B,然后看Foo上有没有名为Bar的方法是接受B类型的参数的,找到Foo.Bar(B);接着由于Foo.Bar(B)是虚方法,要再看goo的实际类型——是Goo,结果找到Goo.Bar(B),于是就对这个版本进行调用。这个判断过程是由所谓的C# runtime binder(Microsoft.CSharp.RuntimeBinder.RuntimeBinder)来完成的,是C# 4基于DLR实现的一个组件。

using System;

public class A { }
public class B : A { }

public class Foo {
    public virtual void Bar( A a1, A a2 ) {
        Console.WriteLine( "Foo.Bar( A, A )" );
    }

    public virtual void Bar( A a, B b ) {
        Console.WriteLine( "Foo.Bar( A, B )" );
    }

    public virtual void Bar( B b, A a ) {
        Console.WriteLine( "Foo.Bar( B, A )" );
    }

    public virtual void Bar( B b1, B b2 ) {
        Console.WriteLine( "Foo.Bar( B, B )" );
    }

    public void Baz( A a ) {
        Console.WriteLine( "Foo.Baz( A )" );
    }
}

public class Goo : Foo {
    public override void Bar( A a1, A a2 ) {
        Console.WriteLine( "Goo.Bar( A, A )" );
    }

    public override void Bar( A a, B b ) {
        Console.WriteLine( "Goo.Bar( A, B )" );
    }

    public override void Bar( B b, A a ) {
        Console.WriteLine( "Goo.Bar( B, A )" );
    }

    public override void Bar( B b1, B b2 ) {
        Console.WriteLine( "Goo.Bar( B, B )" );
    }

    public new void Baz( A a ) {
        Console.WriteLine( "Goo.Baz( A )" );
    }
}


static class Program {
    static void Main( string[ ] args ) {
        A a = new A( );
        A b = new B( );
        Foo goo = new Goo( );

        dynamic da = a;
        dynamic db = b;
        dynamic dgoo = goo;

        goo.Bar( a, b );  // Goo.Bar( A, A ), single-dispatch

        goo.Bar( da, b ); // Goo.Bar( A, B ), multi-dispatch
        goo.Bar( a, db ); // Goo.Bar( A, B ), multi-dispatch
        goo.Bar( b, db ); // Goo.Bar( B, B ), multi-dispatch
        dgoo.Bar( a, b ); // Goo.Bar( A, B ), multi-dispatch

        goo.Baz( b );  // Foo.Baz( A )
        dgoo.Baz( b ); // Goo.Baz( A )
    }
}

很明显,C# 4的虚方法调用中任意一个或多个参数是dynamic类型时,所有参数的实际类型都会参与到方法分派的判定中。
但留意一下最后的两个方法调用,goo.Baz(b)和dgoo.Baz(b):Foo.Baz(A)和Goo.Baz(A)不是虚方法。在C# 4中如果一个非虚方法的方法调用的接收者不是dynamic,那么到运行时选择方法重载仍然会使用接收者的静态类型来考虑。goo.Baz(b)选用的是Foo.Baz(A)就是这个情况。

在Python或者Ruby等变量没有类型,只有值有类型的动态类型语言中,由于无法指定参数的静态类型,也就无从说起“根据参数的静态类型选择方法的版本”。在这样的语言里,要根据参数类型做方法分派基本上只能在一个分派用方法里手工判断,然后再调用具体的版本:
class A
end

def foo(a)
  # declare the differnet specialized versions of the method
  foo_Fixnum = proc { puts 'Fixnum defaults to 0' }
  foo_A = proc { puts 'A defaults to nil' }
  foo_other = proc { puts 'whatever...' }

  # manually dispatch according to type
  # NOT something I would recommend doing in Ruby, though
  case
  when a.instance_of?(Fixnum)
    foo_Fixnum[]
  when a.instance_of?(A)
    foo_A[]
  else
    foo_other[]
  end
end

foo 1     # Fixnum defaults to 0
foo A.new # A defaults to nil
foo 'me'  # whatever...

这种手工的方法分派在Python和Ruby都有些第三方库封装起来了可以直接用,但关键是脚本引擎本身并不直接提供根据类型做方法分派的支持。这些语言里通常也没必要对类型来分派就是了……

虽然上面一直是在讨论以参数的数量和类型为signature的考虑,许多语言实际上还可以对参数的模式进行判断来做函数分派。看看这段OCaml/F#代码的函数定义:
let rec length = function
    []    -> 0
  | x::xs -> 1 + length xs;;

然后调用:
length [0;2;4;6];;

就得到了结果4。这里就是对参数做了模式匹配然后决定采用哪个版本的表达式来计算。当看到参数是空的表时,返回0;当看到参数是一个非空的表时,把表头元素称为x,余下的元素所构成的表称为xs,那么对xs递归调用length之后把结果加上1然后返回。很简单很方便。
1
1
分享到:
评论
2 楼 RednaxelaFX 2008-10-30  
cajon 写道

吼吼,明白了。这个就是你说的那个“一个表达式中任何一个值的类型是dynamic的时候整个表达式的类型都是dynamic……”就是这个意思吗?还是有更多的影响。75G实在太夸张了,就不自己尝试了,厚着脸皮问了

其实没那么大啦。FAQ说最小需要40G,我在虚拟机里看这个虚拟硬盘也才56G而已。还好我的新笔记本是250G的硬盘不然也吃不消……

所谓一个表达式中任意一个变量的类型是dynamic导致整个表达式的类型都是dynamic是这个意思:
dynamic i = 1; // i is dynamic
var j = 2;     // j is int
var k = i + j; // k is dynamic
int l = k;     // l is implicitly converted into int

注意到var跟dynamic有着本质的区别:var是在编译时根据声明语句中赋值符右侧的表达式类型来静态判定类型,而dynamic在编译时不关心类型是什么,等到运行的时候再判断。

问题是dynamic到底是什么?在编译的时候,其实dynamic就是打上了[Dynamic]标签的object而已。任何能赋值给object的东西都能赋值给声明为dynamic类型的变量。但到运行的时候,这些dynamic会先被替换会到变量所指向的值的实际类型,然后再执行。换句话说到运行的时候经过处理,上面的代码近似等价于:
int i = 1;
int j = 2;
int k = i + j;
int l = k;

这个“处理”就是DLR CallSite配合C# runtime binder完成的了。以后肯定有机会再说说这方面的内容的。
1 楼 cajon 2008-10-30  
吼吼,明白了。
这个就是你说的那个“一个表达式中任何一个值的类型是dynamic的时候整个表达式的类型都是dynamic……”就是这个意思吗?还是有更多的影响。

75G实在太夸张了,就不自己尝试了,厚着脸皮问了

相关推荐

    设计模式精解-GoF 23 种设计模式解析附 C++实现源码

    - **4.3 也谈double dispatch(双分派):: Visitor模式**:通过解释双分派的概念,进一步阐述了Visitor模式的工作原理及其优势。 - **4.4 为什么使用设计模式——从Singleton模式谈起**:通过分析Singleton模式的...

    毕业设计物联网实战项目基于Eclipse Theia开源框架开发的物联网在线编程IDE.zip

    【项目资源】: 物联网项目适用于从基础到高级的各种项目,特别是在性能要求较高的场景中,比如操作系统开发、嵌入式编程和底层系统编程。如果您是初学者,可以从简单的控制台程序开始练习;如果是进阶开发者,可以尝试涉及硬件或网络的项目。 【项目质量】: 所有源码都经过严格测试,可以直接运行。 功能在确认正常工作后才上传。 【适用人群】: 适用于希望学习不同技术领域的小白或进阶学习者。 可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】: 项目具有较高的学习借鉴价值,也可直接拿来修改复刻。 对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】: 有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 鼓励下载和使用,并欢迎大家互相学习,共同进步。 # 注意 1. 本资源仅用于开源学习和技术交流。不可商用等,一切后果由使用者承担。 2. 部分字体以及插图等来自网络,若是侵权请联系删除。

    Android毕设实战项目基于Android的医院挂号系统.zip

    【项目资源】: 适用于从基础到高级的各种项目,特别是在性能要求较高的场景中,比如操作系统开发、嵌入式编程和底层系统编程。如果您是初学者,可以从简单的控制台程序开始练习;如果是进阶开发者,可以尝试涉及硬件或网络的项目。 【项目质量】: 所有源码都经过严格测试,可以直接运行。 功能在确认正常工作后才上传。 【适用人群】: 适用于希望学习不同技术领域的小白或进阶学习者。 可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】: 项目具有较高的学习借鉴价值,也可直接拿来修改复刻。 对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】: 有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 鼓励下载和使用,并欢迎大家互相学习,共同进步。 # 注意 1. 本资源仅用于开源学习和技术交流。不可商用等,一切后果由使用者承担。 2. 部分字体以及插图等来自网络,若是侵权请联系删除。

    (源码)基于Python的KMeans和EM算法结合图像分割项目.zip

    # 基于Python的KMeans和EM算法结合图像分割项目 ## 项目简介 本项目结合KMeans聚类和EM(期望最大化)算法,实现对马赛克图像的精准分割。通过Gabor滤波器提取图像的多维特征,并利用KMeans进行初步聚类,随后使用EM算法优化聚类结果,最终生成高质量的分割图像。 ## 项目的主要特性和功能 1. 图像导入和预处理: 支持导入马赛克图像,并进行灰度化、滤波等预处理操作。 2. 特征提取: 使用Gabor滤波器提取图像的多维特征向量。 3. 聚类分析: 使用KMeans算法对图像进行初步聚类。 利用KMeans的聚类中心初始化EM算法,进一步优化聚类结果。 4. 图像生成和比较: 生成分割后的图像,并与原始图像进行比较,评估分割效果。 5. 数值比较: 通过计算特征向量之间的余弦相似度,量化分割效果的提升。 ## 安装使用步骤 ### 假设用户已经下载了项目的源码文件 1. 环境准备:

    HCIP第一次作业:静态路由综合实验

    HCIP第一次作业:静态路由综合实验

    毕设单片机实战项目基于stm32、esp8266和Android的智能家居系统-设备端.zip

    【项目资源】: 单片机项目适用于从基础到高级的各种项目,特别是在性能要求较高的场景中,比如操作系统开发、嵌入式编程和底层系统编程。如果您是初学者,可以从简单的控制台程序开始练习;如果是进阶开发者,可以尝试涉及硬件或网络的项目。 【项目质量】: 所有源码都经过严格测试,可以直接运行。 功能在确认正常工作后才上传。 【适用人群】: 适用于希望学习不同技术领域的小白或进阶学习者。 可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】: 项目具有较高的学习借鉴价值,也可直接拿来修改复刻。 对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】: 有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 鼓励下载和使用,并欢迎大家互相学习,共同进步。 # 注意 1. 本资源仅用于开源学习和技术交流。不可商用等,一切后果由使用者承担。 2. 部分字体以及插图等来自网络,若是侵权请联系删除。

    统计学基于Python的Johnson-SU分布参数计算与优化:数据拟合及弹性网络参数优化方法实现(复现论文或解答问题,含详细可运行代码及解释)

    内容概要:本文详细介绍了Johnson-SU分布的参数计算与优化过程,涵盖位置参数γ、形状参数δ、尺度参数ξ和伸缩参数λ的计算方法,并实现了相应的Python代码。文中首先导入必要的库并设置随机种子以确保结果的可复现性。接着,分别定义了四个参数的计算函数,其中位置参数γ通过加权平均值计算,形状参数δ基于局部均值和标准差的比值,尺度参数ξ结合峰度和绝对偏差,伸缩参数λ依据偏态系数。此外,还实现了Johnson-SU分布的概率密度函数(PDF),并使用负对数似然函数作为目标函数,采用L-BFGS-B算法进行参数优化。最后,通过弹性网络的贝叶斯优化展示了另一种参数优化方法。; 适合人群:具有Python编程基础,对统计学和机器学习有一定了解的研究人员或工程师。; 使用场景及目标:①需要对复杂数据分布进行建模和拟合的场景;②希望通过优化算法提升模型性能的研究项目;③学习如何实现和应用先进的统计分布及优化技术。; 阅读建议:由于涉及较多数学公式和编程实现,建议读者在阅读时结合相关数学知识,同时动手实践代码,以便更好地理解和掌握Johnson-SU分布及其优化方法。

    TSP问题的3种智能优化方法求解(研究生课程《智能优化算法》结课大作业).zip

    TSP问题的3种智能优化方法求解(研究生课程《智能优化算法》结课大作业).zip

    毕业设计物联网实战项目基于Rtthread和MQTT搭建的物联网网关.zip

    【项目资源】: 物联网项目适用于从基础到高级的各种项目,特别是在性能要求较高的场景中,比如操作系统开发、嵌入式编程和底层系统编程。如果您是初学者,可以从简单的控制台程序开始练习;如果是进阶开发者,可以尝试涉及硬件或网络的项目。 【项目质量】: 所有源码都经过严格测试,可以直接运行。 功能在确认正常工作后才上传。 【适用人群】: 适用于希望学习不同技术领域的小白或进阶学习者。 可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】: 项目具有较高的学习借鉴价值,也可直接拿来修改复刻。 对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】: 有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 鼓励下载和使用,并欢迎大家互相学习,共同进步。 # 注意 1. 本资源仅用于开源学习和技术交流。不可商用等,一切后果由使用者承担。 2. 部分字体以及插图等来自网络,若是侵权请联系删除。

    基于STM32F103C8T6的温湿度传感器(HAL库版),通过串口向电脑端反馈数据(附通过ESP8266-01s模块连接WIFI上传云平台的资料代码-固件库版本).zip

    【项目资源】: 单片机项目适用于从基础到高级的各种项目,特别是在性能要求较高的场景中,比如操作系统开发、嵌入式编程和底层系统编程。如果您是初学者,可以从简单的控制台程序开始练习;如果是进阶开发者,可以尝试涉及硬件或网络的项目。 【项目质量】: 所有源码都经过严格测试,可以直接运行。 功能在确认正常工作后才上传。 【适用人群】: 适用于希望学习不同技术领域的小白或进阶学习者。 可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】: 项目具有较高的学习借鉴价值,也可直接拿来修改复刻。 对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】: 有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 鼓励下载和使用,并欢迎大家互相学习,共同进步。 # 注意 1. 本资源仅用于开源学习和技术交流。不可商用等,一切后果由使用者承担。 2. 部分字体以及插图等来自网络,若是侵权请联系删除。

    自动发布Java项目(Tomcat)Shell脚本

    自动发布Java项目(Tomcat)Shell脚本

    (源码)基于webpack和Vue的前端项目构建方案.zip

    # 基于webpack和Vue的前端项目构建方案 ## 项目简介 本项目是基于webpack和Vue构建的前端项目方案,借助webpack强大的打包能力以及Vue的开发特性,可用于快速搭建现代化的前端应用。项目不仅完成了基本的webpack与Vue的集成配置,还在构建速度优化和代码规范性方面做了诸多配置。 ## 项目的主要特性和功能 1. 打包功能运用webpack进行模块打包,支持将scss转换为css,借助babel实现语法转换。 2. Vue开发支持集成Vue框架,能使用Vue单文件组件的开发模式。 3. 构建优化采用threadloader实现多进程打包,cacheloader缓存资源,极大提高构建速度开启热更新功能,开发更高效。 4. 错误处理与优化提供不同环境下的错误映射配置,便于定位错误利用webpackbundleanalyzer分析打包体积。

    Hands-On Large Language Models - Jay Alammar 袋鼠书 《动手学大语言模型》

    Hands-On Large Language Models - Jay Alammar 袋鼠书 《动手学大语言模型》PDF

    《基于YOLOv8的舞蹈动作分析系统》(包含源码、完整数据集、可视化界面、部署教程)简单部署即可运行。功能完善、操作简单,适合毕设或课程设计.zip

    资源内项目源码是来自个人的毕业设计,代码都测试ok,包含源码、数据集、可视化页面和部署说明,可产生核心指标曲线图、混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果、标签分布图。都是运行成功后才上传资源,毕设答辩评审绝对信服的保底85分以上,放心下载使用,拿来就能用。包含源码、数据集、可视化页面和部署说明一站式服务,拿来就能用的绝对好资源!!! 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、大作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.txt文件,仅供学习参考, 切勿用于商业用途。

    (源码)基于Arduino Feather M0和Raspberry Pi的传感器数据采集与监控系统.zip

    # 基于Arduino Feather M0和Raspberry Pi的传感器数据采集与监控系统 ## 项目简介 本项目是一个基于Arduino Feather M0和Raspberry Pi的传感器数据采集与监控系统。系统通过Arduino Feather M0采集传感器数据,并通过WiFi将数据传输到Raspberry Pi。Raspberry Pi运行BalenaOS,集成了MySQL、PHP、NGINX、Apache和Grafana等工具,用于数据的存储、处理和可视化。项目适用于环境监测、物联网设备监控等场景。 ## 项目的主要特性和功能 1. 传感器数据采集使用Arduino Feather M0和AM2315传感器采集温度和湿度数据。 2. WiFi数据传输Arduino Feather M0通过WiFi将采集到的数据传输到Raspberry Pi。

    《基于YOLOv8的音响设备识别系统》(包含源码、完整数据集、可视化界面、部署教程)简单部署即可运行。功能完善、操作简单,适合毕设或课程设计.zip

    资源内项目源码是来自个人的毕业设计,代码都测试ok,包含源码、数据集、可视化页面和部署说明,可产生核心指标曲线图、混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果、标签分布图。都是运行成功后才上传资源,毕设答辩评审绝对信服的保底85分以上,放心下载使用,拿来就能用。包含源码、数据集、可视化页面和部署说明一站式服务,拿来就能用的绝对好资源!!! 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、大作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.txt文件,仅供学习参考, 切勿用于商业用途。

    Android毕设实战项目这是一个android 图书管理系统.zip

    【项目资源】: 适用于从基础到高级的各种项目,特别是在性能要求较高的场景中,比如操作系统开发、嵌入式编程和底层系统编程。如果您是初学者,可以从简单的控制台程序开始练习;如果是进阶开发者,可以尝试涉及硬件或网络的项目。 【项目质量】: 所有源码都经过严格测试,可以直接运行。 功能在确认正常工作后才上传。 【适用人群】: 适用于希望学习不同技术领域的小白或进阶学习者。 可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】: 项目具有较高的学习借鉴价值,也可直接拿来修改复刻。 对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】: 有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 鼓励下载和使用,并欢迎大家互相学习,共同进步。 # 注意 1. 本资源仅用于开源学习和技术交流。不可商用等,一切后果由使用者承担。 2. 部分字体以及插图等来自网络,若是侵权请联系删除。

    毕业设计物联网实战项目基于智龙2.0开发板和窄带物联网模块BC95。操作系统为RTT2.1。.zip

    【项目资源】: 物联网项目适用于从基础到高级的各种项目,特别是在性能要求较高的场景中,比如操作系统开发、嵌入式编程和底层系统编程。如果您是初学者,可以从简单的控制台程序开始练习;如果是进阶开发者,可以尝试涉及硬件或网络的项目。 【项目质量】: 所有源码都经过严格测试,可以直接运行。 功能在确认正常工作后才上传。 【适用人群】: 适用于希望学习不同技术领域的小白或进阶学习者。 可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】: 项目具有较高的学习借鉴价值,也可直接拿来修改复刻。 对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】: 有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 鼓励下载和使用,并欢迎大家互相学习,共同进步。 # 注意 1. 本资源仅用于开源学习和技术交流。不可商用等,一切后果由使用者承担。 2. 部分字体以及插图等来自网络,若是侵权请联系删除。

    (源码)基于Arduino的WiFi按钮项目.zip

    # 基于Arduino的WiFi按钮项目 ## 一、项目简介 本项目是一个基于ESP8266芯片的Arduino项目,主要实现WiFi连接、电压检测、LED灯控制以及向服务器发送POST请求等功能。通过简单的按钮操作,可以实现与服务器通信并获取相关信息,同时能检测电池电压并提示用户。 ## 二、项目的主要特性和功能 1. WiFi连接项目能够自动连接到指定的WiFi网络。 2. 电压检测通过ADC(模数转换器)检测电池电压,并在电压低于阈值时发出警告。 3. LED灯控制通过控制LED灯的亮灭来提示用户不同的状态信息(如连接成功、电压低等)。 4. 服务器通信项目可以向指定的服务器发送POST请求并处理返回的HTTP响应。 ## 三、安装使用步骤 1. 环境准备确保已安装Arduino IDE和ESP8266插件。 2. 下载源码下载项目的源码文件并解压。 3. 打开项目在Arduino IDE中打开解压后的main.cpp文件。

    scipy-0.10.1-cp26-cp26mu-manylinux1_x86_64.whl

    该资源为scipy-0.10.1-cp26-cp26mu-manylinux1_x86_64.whl,欢迎下载使用哦!

Global site tag (gtag.js) - Google Analytics