- 浏览: 3056536 次
- 性别:
- 来自: 海外
文章分类
- 全部博客 (430)
- Programming Languages (23)
- Compiler (20)
- Virtual Machine (57)
- Garbage Collection (4)
- HotSpot VM (26)
- Mono (2)
- SSCLI Rotor (1)
- Harmony (0)
- DLR (19)
- Ruby (28)
- C# (38)
- F# (3)
- Haskell (0)
- Scheme (1)
- Regular Expression (5)
- Python (4)
- ECMAScript (2)
- JavaScript (18)
- ActionScript (7)
- Squirrel (2)
- C (6)
- C++ (10)
- D (2)
- .NET (13)
- Java (86)
- Scala (1)
- Groovy (3)
- Optimization (6)
- Data Structure and Algorithm (3)
- Books (4)
- WPF (1)
- Game Engines (7)
- 吉里吉里 (12)
- UML (1)
- Reverse Engineering (11)
- NSIS (4)
- Utilities (3)
- Design Patterns (1)
- Visual Studio (9)
- Windows 7 (3)
- x86 Assembler (1)
- Android (2)
- School Assignment / Test (6)
- Anti-virus (1)
- REST (1)
- Profiling (1)
- misc (39)
- NetOA (12)
- rant (6)
- anime (5)
- Links (12)
- CLR (7)
- GC (1)
- OpenJDK (2)
- JVM (4)
- KVM (0)
- Rhino (1)
- LINQ (2)
- JScript (0)
- Nashorn (0)
- Dalvik (1)
- DTrace (0)
- LLVM (0)
- MSIL (0)
最新评论
-
mldxs:
虽然很多还是看不懂,写的很好!
虚拟机随谈(一):解释器,树遍历解释器,基于栈与基于寄存器,大杂烩 -
HanyuKing:
Java的多维数组 -
funnyone:
Java 8的default method与method resolution -
ljs_nogard:
Xamarin workbook - .Net Core 中不 ...
LINQ的恶搞…… -
txm119161336:
allocatestlye1 顺序为 // Fields o ...
最近做的两次Java/JVM分享的概要
系列链接:
一个简单的语言的语法(一):用ANTLR描述语法
一个简单的语言的语法(二):ANTLR的重写规则
一个简单的语言的语法(三):做些小调整,并将生成目标换到CSharp2
为了后面的tree grammar更简洁,本篇对上一篇的树重写规则和一些语法细节做了些调整。并且,将生成的lexer和parser的源码目标换到了CSharp2,以便后面能使用一些.NET的库。
要使用CSharp2的目标,需要从官网下载相应的运行时库。当前的最新版是3.1.1,可以从这里获取。CSharp/CSharp2目标的详细情况,可以查阅官网上的文档。以上一篇的语法为基础,要换到CSharp2目标只要把几个嵌入动作里的System.out.println换成Console.WriteLine,把toStringTree换成ToStringTree,把clear换成Clear就可以了。编译的时候至少需要引用Antlr3.Runtime.dll。
那么除去更换生成目标带来的影响,这次做了些怎样的修改呢?
首先,语法做了些细微的调整。例如说,program规则从原本允许没有语句到现在要求至少有一条语句;blockStatement为空block写了条专门的分支;expressionStatement也添加了一个EXPR_STMT的虚构token为根节点,等等。
变化最大的还是variableDeclaration及相关规则。上一篇里这条规则的重写规则并不区分有初始化与无初始化、简单类型与数组类型的区别;本篇里则将这两个区别都明确的写在了重写规则里,以不同的虚构token来作为生成的树的根节点。这样,到写后面的tree grammar的时候,需要的lookahead数就可以减少。
ANTLR所生成的AST,以深度优先的方式遍历,可以看做一个一维的流:每一层父子关系都可以表示为:
root -> "down" -> element1 -> element2 -> ... -> elementN -> "up" -> ...
其中"down"和"up"是ANTLR插入的虚构token,用于指定树的层次。
这样,后面使用tree grammar来遍历AST时,实际上遍历的就是这样一个一维的流(CommonTreeNodeStream)。所以我们也可以把tree grammar看做是隐含了"down"和"up"虚构token的普通parser grammar。那么,tree grammar中需要的lookahead个数的分析,也就跟parser grammar的一样。
看看下面的例子。对于上一篇variableDeclaration的重写规则中出现的变量声明的类型,可以用这样的tree grammar来匹配:
树语法的^( ... )就隐含了"down"和"up"这两个虚构token。实际上这条规则匹配的是:
可以很清楚的看到"down"和"up"在规则中的位置。
在进入这条规则之后,需要多少个lookahead才足以判断应该选择哪条分支呢?
向前看一位:只能排除掉两个分支,还有两个,不够;
向前看两位:第二位是什么呢?四个分支里第二位都是"down"节点,对判断分支没帮助,还是不够用;
向前看三位:SIMPLE和ARRAY、INT和REAL都能分开了,足够。
那么对这条规则而言,需要3个lookahead。阅读ANTLR生成的源码,可以看到input.LA(3)这样的调用,表示向前看第三位的token。每多一个lookahead,生成的代码就得多以层嵌套的if-else,很是麻烦。
如果能调整一下parser这边生成的AST的结构,让tree grammar那边能写成:
那么这两条规则都只需要1个lookahead就足以判断分支了,比原本的写法要简单,也会稍微快一些。写了个Ruby脚本来检查生成的源码里用的lookahead的个数(*):
明白了这个道理,就应该尽量将重写规则中的各个根节点设计成能直接区分的。
实际上不只是树的语法,在编程语言的源码的语法设计上也是一样:最容易解析的语法是每条规则都以特殊的token开头的语法,例如说声明变量就以var关键字开头,声明函数就以function关键字开头等。这样能保证语法只需要1个lookahead。而类似C的语法对解析器来说实在算不上友善……|||
(*:ANTLR在遇到比较复杂的判断条件时不会直接在规则对应的方法里调用input.LA(n),而是会生成一个DFA类来计算应该走的分支。上面的Ruby脚本不检查这个状况。)
其次,所有虚构token都添加了一些信息在后面。例如说原本一元负号的规则是:
则UNARY_MINUS这个虚构token将不包含任何文字、位置信息。因为MINUS原本携带的位置信息已经丢失了,所以如果后续处理中需要知道这个表达式的位置就没办法得到。
改写为这样:
则使得UNARY_MINUS继承MINUS匹配时的文字、位置等属性,解决了前面的问题。
除此之外,原本写在program规则里的嵌入动作也去掉了。之前写在那里主要是为了在parser内输出AST的字符串表示,只是演示用。
修改后的完整语法如下:
同上一篇一样,也写一个启动lexer和parser的程序。这次是用C#来写:
因为使用了DOTTreeGenerator,编译时记得在引用Antlr3.Runtime.dll之外,还需要引用Antlr3.Utility.dll与StringTemplate.dll。
继续使用前两篇用过的Jerry代码为例子:
通过上面的程序,可以得到这样的AST:
(点击放大)
上面的程序生成的是.dot文件(输出到标准输出流上)。使用Graphviz的dot,将这个文件以
这样的命令来转换,就能得到PNG图像。
本篇就到这里。下一篇看看遍历AST用的基本tree grammar。
一个简单的语言的语法(一):用ANTLR描述语法
一个简单的语言的语法(二):ANTLR的重写规则
一个简单的语言的语法(三):做些小调整,并将生成目标换到CSharp2
为了后面的tree grammar更简洁,本篇对上一篇的树重写规则和一些语法细节做了些调整。并且,将生成的lexer和parser的源码目标换到了CSharp2,以便后面能使用一些.NET的库。
要使用CSharp2的目标,需要从官网下载相应的运行时库。当前的最新版是3.1.1,可以从这里获取。CSharp/CSharp2目标的详细情况,可以查阅官网上的文档。以上一篇的语法为基础,要换到CSharp2目标只要把几个嵌入动作里的System.out.println换成Console.WriteLine,把toStringTree换成ToStringTree,把clear换成Clear就可以了。编译的时候至少需要引用Antlr3.Runtime.dll。
那么除去更换生成目标带来的影响,这次做了些怎样的修改呢?
首先,语法做了些细微的调整。例如说,program规则从原本允许没有语句到现在要求至少有一条语句;blockStatement为空block写了条专门的分支;expressionStatement也添加了一个EXPR_STMT的虚构token为根节点,等等。
变化最大的还是variableDeclaration及相关规则。上一篇里这条规则的重写规则并不区分有初始化与无初始化、简单类型与数组类型的区别;本篇里则将这两个区别都明确的写在了重写规则里,以不同的虚构token来作为生成的树的根节点。这样,到写后面的tree grammar的时候,需要的lookahead数就可以减少。
ANTLR所生成的AST,以深度优先的方式遍历,可以看做一个一维的流:每一层父子关系都可以表示为:
root -> "down" -> element1 -> element2 -> ... -> elementN -> "up" -> ...
其中"down"和"up"是ANTLR插入的虚构token,用于指定树的层次。
这样,后面使用tree grammar来遍历AST时,实际上遍历的就是这样一个一维的流(CommonTreeNodeStream)。所以我们也可以把tree grammar看做是隐含了"down"和"up"虚构token的普通parser grammar。那么,tree grammar中需要的lookahead个数的分析,也就跟parser grammar的一样。
看看下面的例子。对于上一篇variableDeclaration的重写规则中出现的变量声明的类型,可以用这样的tree grammar来匹配:
type : ^( SIMPLE_TYPE INT ) | ^( SIMPLE_TYPE REAL ) | ^( ARRAY_TYPE INT Integer+ ) | ^( ARRAY_TYPE REAL Integer+ ) ;
树语法的^( ... )就隐含了"down"和"up"这两个虚构token。实际上这条规则匹配的是:
可以很清楚的看到"down"和"up"在规则中的位置。
在进入这条规则之后,需要多少个lookahead才足以判断应该选择哪条分支呢?
向前看一位:只能排除掉两个分支,还有两个,不够;
向前看两位:第二位是什么呢?四个分支里第二位都是"down"节点,对判断分支没帮助,还是不够用;
向前看三位:SIMPLE和ARRAY、INT和REAL都能分开了,足够。
那么对这条规则而言,需要3个lookahead。阅读ANTLR生成的源码,可以看到input.LA(3)这样的调用,表示向前看第三位的token。每多一个lookahead,生成的代码就得多以层嵌套的if-else,很是麻烦。
如果能调整一下parser这边生成的AST的结构,让tree grammar那边能写成:
simpleType : INT | REAL ; arrayType : ^( INT Integer+ ) | ^( REAL Integer+ ) ;
那么这两条规则都只需要1个lookahead就足以判断分支了,比原本的写法要简单,也会稍微快一些。写了个Ruby脚本来检查生成的源码里用的lookahead的个数(*):
def check_lookaheads(file) lookaheads = open file, 'r' do |f| ret = [] f.readlines.grep(/^\s+(.+\.la\((\d+)\).+)$/i) do ret << "#{$2}: #{$1}" end ret end end if __FILE__ == $0 la = check_lookaheads ARGV[0] || 'JerryParser.cs' puts 'Lookaheads:', la, '' puts "Non-LL(1)'s:", la.select { |l| ?1 != l[0] } end
明白了这个道理,就应该尽量将重写规则中的各个根节点设计成能直接区分的。
实际上不只是树的语法,在编程语言的源码的语法设计上也是一样:最容易解析的语法是每条规则都以特殊的token开头的语法,例如说声明变量就以var关键字开头,声明函数就以function关键字开头等。这样能保证语法只需要1个lookahead。而类似C的语法对解析器来说实在算不上友善……|||
(*:ANTLR在遇到比较复杂的判断条件时不会直接在规则对应的方法里调用input.LA(n),而是会生成一个DFA类来计算应该走的分支。上面的Ruby脚本不检查这个状况。)
其次,所有虚构token都添加了一些信息在后面。例如说原本一元负号的规则是:
MINUS primaryExpression -> ^( UNARY_MINUS primaryExpression )
则UNARY_MINUS这个虚构token将不包含任何文字、位置信息。因为MINUS原本携带的位置信息已经丢失了,所以如果后续处理中需要知道这个表达式的位置就没办法得到。
改写为这样:
MINUS primaryExpression -> ^( UNARY_MINUS[$MINUS] primaryExpression )
则使得UNARY_MINUS继承MINUS匹配时的文字、位置等属性,解决了前面的问题。
除此之外,原本写在program规则里的嵌入动作也去掉了。之前写在那里主要是为了在parser内输出AST的字符串表示,只是演示用。
修改后的完整语法如下:
grammar Jerry; options { language = CSharp2; output = AST; ASTLabelType = CommonTree; } tokens { // imaginary tokens SIMPLE_VAR_DECL; SIMPLE_VAR_DECL_INIT; ARRAY_VAR_DECL; ARRAY_VAR_DECL_INIT; ARRAY_LITERAL; SIMPLE_VAR_ACCESS; ARRAY_VAR_ACCESS; UNARY_MINUS; BLOCK; EMPTY_BLOCK; EXPR_STMT; } // parser rules program : statement+ EOF! ; statement : expressionStatement | variableDeclaration | blockStatement | ifStatement | whileStatement | breakStatement | readStatement | writeStatement ; expressionStatement : expression SEMICOLON -> ^( EXPR_STMT[$expression.start, "ExprStmt"] expression ) ; variableDeclaration : typeSpecifier ( id1=Identifier ( ( -> ^( SIMPLE_VAR_DECL[$id1, "VarDecl"] ^( typeSpecifier ) $id1 ) ) | ( EQ expression -> ^( SIMPLE_VAR_DECL_INIT[$id1, "VarDeclInit"] ^( typeSpecifier ) $id1 expression ) ) | ( ( LBRACK Integer RBRACK )+ -> ^( ARRAY_VAR_DECL[$id1, "VarDecl"] ^( typeSpecifier Integer+ ) $id1 ) ) | ( ( LBRACK Integer RBRACK )+ EQ arrayLiteral -> ^( ARRAY_VAR_DECL_INIT[$id1, "VarDeclInit"] ^( typeSpecifier Integer+ ) $id1 arrayLiteral ) ) ) ) ( COMMA id2=Identifier ( ( -> $variableDeclaration ^( SIMPLE_VAR_DECL[$id2, "VarDecl"] ^( typeSpecifier) $id2 ) ) | ( EQ exp=expression -> $variableDeclaration ^( SIMPLE_VAR_DECL_INIT[$id2, "VarDeclInit"] ^( typeSpecifier ) $id2 $exp ) ) | ( ( LBRACK dim1+=Integer RBRACK )+ -> $variableDeclaration ^( ARRAY_VAR_DECL[$id2, "VarDecl"] ^( typeSpecifier $dim1+ ) $id2 ) ) | ( ( LBRACK dim2+=Integer RBRACK )+ EQ al=arrayLiteral -> $variableDeclaration ^( ARRAY_VAR_DECL_INIT[$id2, "VarDeclInit"] ^( typeSpecifier $dim2+ ) $id2 $al ) ) ) { if (null != $dim1) $dim1.Clear(); if (null != $dim2) $dim2.Clear(); } )* SEMICOLON ; typeSpecifier : INT | REAL ; arrayLiteral : LBRACE arrayLiteralElement ( COMMA arrayLiteralElement )* RBRACE -> ^( ARRAY_LITERAL[$LBRACE, "Array"] arrayLiteralElement+ ) ; arrayLiteralElement : expression | arrayLiteral ; blockStatement : LBRACE statement+ RBRACE -> ^( BLOCK[$LBRACE, "Block"] statement+ ) | LBRACE RBRACE // empty block -> EMPTY_BLOCK[$LBRACE, "EmptyBlock"] ; ifStatement : IF^ LPAREN! expression RPAREN! statement ( ELSE! statement )? ; whileStatement : WHILE^ LPAREN! expression RPAREN! statement ; breakStatement : BREAK SEMICOLON! ; readStatement : READ^ variableAccess SEMICOLON! ; writeStatement : WRITE^ expression SEMICOLON! ; variableAccess : Identifier ( -> ^( SIMPLE_VAR_ACCESS[$Identifier, "VarAccess"] Identifier ) | ( LBRACK Integer RBRACK )+ -> ^( ARRAY_VAR_ACCESS[$Identifier, "VarAccess"] Identifier Integer+ ) ) ; expression : assignmentExpression | logicalOrExpression ; assignmentExpression : variableAccess EQ^ expression ; logicalOrExpression : logicalAndExpression ( OROR^ logicalAndExpression )* ; logicalAndExpression : relationalExpression ( ANDAND^ relationalExpression )* ; relationalExpression : additiveExpression ( relationalOperator^ additiveExpression )? | BANG^ relationalExpression ; additiveExpression : multiplicativeExpression ( additiveOperator^ multiplicativeExpression )* ; multiplicativeExpression : primaryExpression ( multiplicativeOperator^ primaryExpression )* ; primaryExpression : variableAccess | Integer | RealNumber | LPAREN! expression RPAREN! | MINUS primaryExpression -> ^( UNARY_MINUS[$MINUS] primaryExpression ) ; relationalOperator : LT | GT | EQEQ | LE | GE | NE ; additiveOperator : PLUS | MINUS ; multiplicativeOperator : MUL | DIV ; // lexer rules LPAREN : '(' ; RPAREN : ')' ; LBRACK : '[' ; RBRACK : ']' ; LBRACE : '{' ; RBRACE : '}' ; COMMA : ',' ; SEMICOLON : ';' ; PLUS : '+' ; MINUS : '-' ; MUL : '*' ; DIV : '/' ; EQEQ : '==' ; NE : '!=' ; LT : '<' ; LE : '<=' ; GT : '>' ; GE : '>=' ; BANG : '!' ; ANDAND : '&&' ; OROR : '||' ; EQ : '=' ; IF : 'if' ; ELSE : 'else' ; WHILE : 'while' ; BREAK : 'break' ; READ : 'read' ; WRITE : 'write' ; INT : 'int' ; REAL : 'real' ; Identifier : LetterOrUnderscore ( LetterOrUnderscore | Digit )* ; Integer : Digit+ ; RealNumber : Digit+ '.' Digit+ ; fragment Digit : '0'..'9' ; fragment LetterOrUnderscore : Letter | '_' ; fragment Letter : ( 'a'..'z' | 'A'..'Z' ) ; WS : ( ' ' | '\t' | '\r' | '\n' )+ { $channel = HIDDEN; } ; Comment : '/*' ( options { greedy = false; } : . )* '*/' { $channel = HIDDEN; } ; LineComment : '//' ~('\n'|'\r')* '\r'? '\n' { $channel = HIDDEN; } ;
同上一篇一样,也写一个启动lexer和parser的程序。这次是用C#来写:
using System; using System.IO; using Antlr.Runtime; // Antlr3.Runtime.dll using Antlr.Runtime.Tree; using Antlr.Utility.Tree; // Antlr3.Utility.dll sealed class TestJerryAst { static void PrintUsage( ) { Console.WriteLine( "Usage: TestJerryAst [-dot] <source file>" ); } static void Main( string[] args ) { bool generateDot = false; string srcFile; switch ( args.Length ) { case 0: PrintUsage( ); return; case 1: if ( !File.Exists( args[ 0 ] ) ) goto case 0; srcFile = args[ 0 ]; break; default: if ( "-dot" == args[ 0 ] ) { generateDot = true; if ( !File.Exists( args[ 1 ] ) ) goto case 0; srcFile = args[ 1 ]; } else { goto case 1; } break; } var input = new ANTLRReaderStream( File.OpenText( srcFile ) ); var lexer = new JerryLexer( input ); var tokens = new CommonTokenStream( lexer ); var parser = new JerryParser( tokens ); var programReturn = parser.program(); var ast = ( CommonTree ) programReturn.Tree; // Generate DOT diagram if -dot option is given if ( generateDot ) { var dotgen = new DOTTreeGenerator( ); var astDot = dotgen.ToDOT( ast ); Console.WriteLine( astDot ); } else { Console.WriteLine( ast.ToStringTree( ) ); } } }
因为使用了DOTTreeGenerator,编译时记得在引用Antlr3.Runtime.dll之外,还需要引用Antlr3.Utility.dll与StringTemplate.dll。
继续使用前两篇用过的Jerry代码为例子:
// line comment // declare variables with/without initializers int i = 1, j; int x = i + 2 * 3 - 4 / ( 6 - - 7 ); int array[2][3] = { { 0, 1, 2 }, { 3, 4, 6 } }; /* block comment */ while (i < 10) i = i + 1; while (!x > 0 && i < 10) { x = x - 1; if (i < 5) break; else read i; } write x - j;
通过上面的程序,可以得到这样的AST:
(点击放大)
上面的程序生成的是.dot文件(输出到标准输出流上)。使用Graphviz的dot,将这个文件以
dot JerrySample.dot -Tpng -o JerrySample.png
这样的命令来转换,就能得到PNG图像。
本篇就到这里。下一篇看看遍历AST用的基本tree grammar。
发表评论
-
Sun JDK1.4.2_28有TieredCompilation
2014-05-12 08:48 0原来以前Sun的JDK 1.4.2 update 28就已经有 ... -
IBM JVM notes (2014 ver)
2014-05-11 07:16 0Sovereign JIT http://publib.bou ... -
HotSpot Server Compiler与data-flow analysis
2014-01-07 17:41 0http://en.wikipedia.org/wiki/Da ... -
基于LLVM实现VM的JIT的一些痛点
2014-01-07 17:25 0同事Philip Reames Sanjoy Das http ... -
《自制编程语言》的一些笔记
2013-11-24 00:20 0http://kmaebashi.com/programmer ... -
对C语义的for循环的基本代码生成模式
2013-10-19 23:12 21891之前有同学在做龙书(第二版)题目,做到8.4的练习,跟我对答案 ... -
Nashorn各种笔记
2013-07-15 17:03 0http://bits.netbeans.org/netbea ... -
《深入理解Java虚拟机(第二版)》书评
2013-07-08 19:19 0值得推荐的中文Java虚拟机入门书 感谢作者赠与的样书,以下 ... -
豆列:从表到里学习JVM实现
2013-06-13 14:13 48425刚写了个学习JVM用的豆列跟大家分享。 豆列地址:http: ... -
Building Blocks of a JavaScript Engine
2013-05-23 00:49 0sketches of my new book "B ... -
读《JavaScript语言精髓与编程实践(第二版)》
2013-05-21 00:32 02008年逛书店的时候偶 ... -
添加一个bool C1LateInline参数?
2011-11-25 16:03 0之前我试过给Phi加exact_type不行,那如果像C2一样 ... -
别测空循环
2011-06-23 21:56 5271今天有朋友提到一个叫 ReflectASM的库,为Java环境 ... -
javac在编译创建内部类对象时生成的奇怪的getClass()调用是什么?
2011-06-14 22:17 4266有人问下面这段代码里,main()方法里的outer.new ... -
confluence property
2011-06-08 20:41 0http://en.wikipedia.org/wiki/Co ... -
JIT编译找不到类?
2011-05-09 22:28 5218今天开始Sun的老blog真的搬迁了,从blogs.sun.c ... -
几个简答题
2011-01-10 16:08 2465某题目 写道 龙书 写道In addition to a c ... -
循环中的字符串拼接的优化
2010-12-09 20:46 0public class StringConcatDemo { ... -
Velocity模板的编译
2010-11-15 14:49 0http://ecee.colorado.edu/ecen45 ... -
ANTLR里迭代子规则的一个注意点
2010-09-27 15:31 3614这几天在休假在家,有空的时候在用ANTLR 3.2来写D 2. ...
相关推荐
"基于Comsol的采空区阴燃现象研究:速度、氧气浓度、瓦斯浓度与温度分布的二维模型分析",comsol采空区阴燃。 速度,氧气浓度,瓦斯浓度及温度分布。 二维模型。 ,comsol; 采空区; 阴燃; 速度; 氧气浓度; 瓦斯浓度; 温度分布; 二维模型;,"COMSOL模拟采空区阴燃:速度、浓度与温度分布的二维模型研究"
安全驱动的边云数据协同策略研究.pdf
MATLAB代码实现电-气-热综合能源系统耦合优化调度模型:精细电网、气网与热网协同优化,保姆级注释参考文档详可查阅。,MATLAB代码:电-气-热综合能源系统耦合优化调度 关键词:综合能源系统 优化调度 电气热耦合 参考文档:自编文档,非常细致详细,可联系我查阅 仿真平台:MATLAB YALMIP+cplex gurobi 主要内容:代码主要做的是一个考虑电网、热网以及气网耦合调度的综合能源系统优化调度模型,考虑了电网与气网,电网与热网的耦合,算例系统中,电网部分为10机39节点的综合能源系统,气网部分为比利时20节点的配气网络,潮流部分电网是用了直流潮流,气网部分也进行了线性化的操作处理,代码质量非常高,保姆级的注释以及人性化的模块子程序,所有数据均有可靠来源 ,关键词:MATLAB代码; 电-气-热综合能源系统; 耦合优化调度; 电网; 热网; 气网; 潮流; 直流潮流; 线性化处理; 保姆级注释; 人性化模块子程序; 可靠数据来源。,MATLAB代码:电-气-热综合能源系统耦合优化调度模型(保姆级注释,数据来源可靠)
内容概要:本文详细探讨了人工智能(AI)对就业市场的深远影响及其发展趋势。首先介绍了到2027年,44%的工人核心技能将受技术变革尤其是AI影响的事实,并提及自动化可能取代部分工作的现象。其次指出虽然某些职位面临风险,但也带来了全新的职业机遇与现有角色改进的可能性,关键在于人类要学会借助AI释放自身潜力并培养软实力,以适应快速发展的科技需求。再者,强调终身学习理念下企业和教育培训须革新教学手段与评估机制,以便紧跟AI进化速率,为个体和社会持续注入新动力。最后提到了教育机构应当加快调整步伐以匹配技术变革的速度,并利用AI实现个性化的教育,进而提升学习者的适应能力和解决问题的能力。 适用人群:政策制定者、企业管理层、在职人员及教育工作者,还有广大学生群体均能从中获得启示。 使用场景及目标:面向关注未来职场动向及教育发展方向的专业人士,提供前瞻性思考角度,助力各界积极规划职业生涯路径或调整教育资源分配策略。 其他说明:本文综合多位行业领袖的观点展开讨论,旨在唤起社会各界共同思考AI带来的变革及对策,而非单方面渲染危机感。
2025最新空调与制冷作业考试题及答案.doc
2025最新初级电工证考试题及答案.docx
飞剪PLC控制系统——采用西门子S7-200SMART和触摸屏实现智能化操控及图纸详述,飞锯追剪程序,PLC和触摸屏采用西门子200smart,包含图纸,触摸屏程序和PLC程序。 ,核心关键词:飞锯追剪程序; 西门子200smart; PLC程序; 触摸屏程序; 图纸; 控制系统。,"西门子200smart飞锯追剪系统程序包:含图纸、PLC与触摸屏程序"
使用PyQt6制作的Python应用程序。
三相桥式整流电路双闭环控制策略:电压外环与电流内环协同优化研究,三相桥式整流电路双闭环控制 电流内环 电压外环(也有开环控制) 采用电压电流双闭环控制,在电压、电流控制电路中,电压单环控制易于设计和分析,但是响应速度慢,无限流功能。 而电流环能增强电路稳定性、响应速度快。 三相桥式全控整流电路由整流变压器、阴极相连接的晶闸管(VT1, VT3, VT5)、阳极相连接的晶闸管(VT4, VT6, VT2)、负载、触发器和同步环节组成(如图1),6个晶闸管依次相隔60°触发,将电源交流电整流为直流电。 matlab仿真模型(开闭环都有)控制效果良好,可写报告。 ,三相桥式整流电路;双闭环控制;电流内环;电压外环;开环控制;MATLAB仿真模型。,基于双闭环控制的电压电流三相整流技术分析与Matlab仿真实现
MATLAB四旋翼仿真PID控制:从入门到精通的手把手教学,含QAV方法、模型代码、Simulink布局思路及详细图文说明,MATLAB四旋翼仿真 PID控制,有完全对应的说明文档,专门为初级学习者提供。 不用问在不在,直接拿即可。 亮点: 拥有和模型完全对应的讲解文档,相当于手把手教学。 内容包括: 1.QAV详细方法 2.模型及代码 3.模型2(提供simulink排版布局思路) 4.相关图片 5.使用备注 ,核心关键词:MATLAB四旋翼仿真; PID控制; 完全对应说明文档; 初级学习者; QAV详细方法; 模型及代码; simulink排版布局思路; 相关图片; 使用备注。,"MATLAB四旋翼仿真教程:PID控制详解与手把手教学"
定子磁链控制下的直接转矩控制系统MATLAB仿真研究及结果分析报告,基于定子磁链控制的直接转矩控制系统 MATLAB SIMULINK仿真模型(2018b)及说明报告,仿真结果良好。 报告第一部分讨论异步电动机的理论基础和数学模型,第二部分介绍直接转矩控制的具体原理,第三部分对调速系统中所用到的脉宽调制技术CFPWM、SVPWM进行了介绍,第四部分介绍了MATLAB仿真模型的搭建过程,第五部分对仿真结果进行了展示及讨论。 ,关键词:定子磁链控制;直接转矩控制系统;MATLAB SIMULINK仿真模型;异步电动机理论基础;数学模型;直接转矩控制原理;脉宽调制技术CFPWM;SVPWM;仿真结果。,基于MATLAB的异步电机直接转矩控制仿真研究报告
2025中小学教师编制考试教育理论基础知识必刷题库及答案.pptx
Python游戏编程源码-糖果消消消.zip
三相PWM整流器双闭环控制:电压外环电流内环的SVPWM调制策略及其代码编写详解——动态稳态特性优越的技术参考。,三相PWM整流器双闭环控制,电压外环,电流内环,PLL。 采用SVPWM调制,代码编写。 动态和稳态特性较好,可提供参考资料 ,三相PWM整流器;双闭环控制;电压外环;电流内环;PLL调制;SVPWM调制;动态特性;稳态特性;参考资料,三相PWM整流器双闭环SVPWM调制策略:稳态与动态特性优化参考指南
永磁同步电机滑膜观测器参数识别与仿真研究:转动惯量、阻尼系数及负载转矩的Matlab Simulink仿真分析文章及文档说明,永磁同步电机 滑膜观测器参数识别Matlab simulink仿真 包括转动惯量 阻尼系数 负载转矩 波形很好 跟踪很稳 包含仿真文件说明文档以及文章 ,关键词:永磁同步电机;滑膜观测器;参数识别;Matlab simulink仿真;转动惯量;阻尼系数;负载转矩;波形质量;跟踪稳定性;仿真文件;说明文档;文章。,基于Matlab Simulink仿真的永磁同步电机滑膜观测器参数识别及性能分析
基于永磁涡流的电梯缓冲结构设计.pdf
Python自动化办公源码-28 Python爬虫爬取网站的指定文章
MATLAB下的安全强化学习:利用Constraint Enforcement块训练代理实现目标接近任务,MATLAB代码:安全 强化学习 关键词:safe RL 仿真平台:MATLAB 主要内容:此代码展示了如何使用 Constraint Enforcement 块来训练强化学习 (RL) 代理。 此块计算最接近受约束和动作边界的代理输出的动作的修改控制动作。 训练强化学习代理需要 Reinforcement Learning Toolbox 。 在此示例中,代理的目标是使绿球尽可能靠近红球不断变化的目标位置。 具体步骤为创建用于收集数据的环境和代理,学习约束函数,使用约束强制训练代理,在没有约束执行的情况下训练代理。 ,核心关键词:safe RL; MATLAB代码; Constraint Enforcement 块; 强化学习代理; 绿球; 红球目标位置; 数据收集环境; 约束函数; 约束强制训练; 无约束执行训练。,MATLAB中安全强化学习训练的约束强化代理实现
基于EtherCAT总线网络的锂电池激光制片机控制系统,融合欧姆龙NX系列与威伦通触摸屏的智能制造方案。,锂电池激光模切机 欧姆龙NX1P2-1140DT,威伦通触摸屏,搭载从机扩展机架控制,I输入输出IO模块模拟量模块读取控制卷径计算 汇川IS620N总线伺服驱动器7轴控制,总线纠偏器控制 全自动锂电池激光制片机,整机采用EtherCAT总线网络节点控制, 伺服凸轮同步运动,主轴虚轴控制应用,卷径计算,速度计算,放卷张力控制。 触摸屏设计伺服驱动器报警代码,MC总线报警代码,欧姆龙伺服报警代码 张力摆臂控制,PID控制,等等 触摸屏产量统计,触摸屏故障统计,触摸屏与PLC对接信息交互,触摸屏多账户使用,多产品配方程序,优秀的触摸屏模板。 NX在收放卷控制的设计 欧姆龙NX系列实际项目程序+威纶触摸屏程序+新能源锂电设备 涵盖威纶通人机,故障记录功能,st+梯形图+FB块,注释齐全。 ,"新能源锂电池激光模切机:欧姆龙NX与威纶通触摸屏的智能控制与信息交互系统"
2025装载机理论考试试题库(含答案).pptx