ἓν οἶδα ὅτι οὐδὲν οἶδα ( ο φιλόσοφος Σωκράτης )
我知道自己一无所知(哲学家苏格拉底)
对于利用 MetaQuotes 语言 4/5 (MQL4/5) 开发 EA 的交易者而言,这是一个全新的主题。尝试在 MetaQuotes 网站上执行相关搜索时,我就意识到了这一点。有关该主题,还是一片空白。
每一位交易者都会创建属于自己的 EA 交易,且需要一套严谨的方法,来处理与编程及非常复杂的程序逻辑相关的各种问题。一天结束时,该程序应该会像任何标准与不可抗力情况下的发条装置一样自行运行。
但是,怎样才能实现兼容并蓄、无所不包呢?这个太困难了,正因如此,自动化控制系统才要求所有控制系统都要妥善编程,而且也只有利用自动机编程的相应编程技术,才能达到最佳效果。近些年来,与软件质量设置高要求的嵌入与实时系统编程技术的发展相关的领域,得到了极大的关注。
1991 年,俄罗斯作者 A.A. Shalyto(讲师、教授、工程理学博士、SPbSU ITMO 学院“编程”技术部主管)开发出了一个名为“自动机编程”的编程技术。见识一下简单的自动机编程或 SWITCH 技术,我觉得读者可能会感兴趣。它允许利用 MetaQuotes 语言,让 MTS 开发方便到无以复加的程度。而且,它会很好地融入到复杂决策制定体系当中。
所有问题提出者及软件开发者长久以来魂牵梦萦的一个梦想,必然是拥有一套针对问题(算法)的有计划的解决方案,以及与其完全一致的该算法的实施。但事情的发展,往往不像提出者和开发者所想的那样。各种算法都倾向于忽略对开发者实施而言很重要的内容,而程序文本本身与算法又没有多少相似之处。
由此,存在两种算法 - 一种是书面的(用于记录和记载设计解决方案),通常会表示某种特定的设计成果,而不是获取某给定成果中采用的方法;另一种则在开发者的心中(但亦以文本方式保存)。
程序文本的最终版本之后,通常还会尝试修改文档编制,许多事情由此再一次被忽略。这种情况下,程序逻辑很可能与算法逻辑有所区别,因此显出缺乏一致性。之所以说是“很可能”,因为没人会检查某人的程序文本。
如果程序很大,那么,只靠文本来检查它是否与算法一致是不可能的。实施的准确度,可以利用一个名为“testing”的流程进行检验。基本上,它会检查开发者如何把握算法(付诸书面)、在头脑中将其转换成另一种算法并作为一个程序输出。最终,开发者是与逻辑相关宝贵信息以及实施变成完全不相干之前编撰的相关内容的唯一持有人。
就算开发者会生病(或... 辞职),也不是这样。重点在于,基本程序逻辑会根据每一位开发者的智力和对于编程语言的知识掌握而各不相同。任何情况下,开发者都会引入和使用大量其认为合适的中间变量。而且,如果程序很大且逻辑上很复杂,就需要一位更资深的专家来查找错误缺陷(我在这里所指,并非操作系统缺陷或语言函数的不正确使用,而是逻辑的不当实施),并通过程序文本本身解决它。
退一步讲,开发者中的大多数也都不愿意在编程之前先写下算法(甚至连在纸上大概画画都不愿意),而这很可能是因为某些事情他们仍要边做边想。确实,为什么要把时间浪费在绘制一些矩形、菱形和箭头上呢?最好是立即开始编程,然后再于文档编制中布置一个在某种程度上类似或非常通用的算法。
每个人都已经习惯于此了 - 开发者这么做是因为这种方式更简单;而问题提出者则不一定具备所要求程度的编程技能,而且即使是有,他们也根本不能对开发者想出来的内容进行及时的更改。方便的编程环境亦有助于指定开发顺序的有效性。用于调试的高级工具以及变量的监测值,则让我们有希望检测到逻辑中的任何错误。
随着时间的流逝和项目最后期限的逼近,开发者会坐下来为某个给定的逻辑问题草拟一份“餐巾纸”解决方案,顺便说一下,仍需要实施,更不用说后面跟随大量相同(混乱)情景的测试期间被忽视的错误了。当前的情况就是这样。有没有什么解决方案,或者至少能够改善?感觉像是,在从一种按标准方式规定的算法迁移到该程序代码的过程中,丢掉了某些重要的东西。
“自动机编程”的作者提出了有关某程序理想逻辑部分的下述理念。程序的整体逻辑基于切换。简言之,任何控制算法(自动机)均可如下实施(这里并未过多考虑注释的意义,只是大概看一下结构)。
switch(int STATUS ) // Мulti-valued global state variable of the automaton. { case 0: // start // Checking arc and loop conditions (in order of priority), // transition (change of the value of the variable STATUS) // and execution of arc and loop actions (output function execution); // logging transitions and actions if the condition is met. 0 // Calling nested automata. // Execution of output functions in the state. break ; case 1: // Checking arc and loop conditions (in order of priority), // transition (change of the value of the variable STATUS) // and execution of arc and loop actions (output function execution); // logging transitions and actions if the condition is met. // Calling nested automata. // Execution of output functions in the state. break ; ********* ********* ********* case N-1: // Checking arc and loop conditions (in order of priority), // transition (change of the value of the variable STATUS) // and execution of arc and loop actions (output function execution); // logging transitions and actions if the condition is met. // Calling nested automata. // Execution of output functions in the state. break ; case N: // Checking arc and loop conditions (in order of priority), // transition (change of the value of the variable STATUS) // and execution of arc and loop actions (output function execution); // logging transitions and actions if the condition is met. // Calling nested automata. // Execution of output functions in the state. break ; }
不论开发技术如何,任何程序都拥有由其任何特定时间点的所有数据值确定的状态。大型应用程序中,可能会有数百乃至成千上万的变量和多个控制流。而这些变量的一个完整的集,则会描述出该应用程序在任何特定时间点的状态。
程序状态可以作为全部控制变量中的一系列(参与所有迁移情况的)精选值以一种更简单的方式进行处理。如果更改某控制变量的值,则意味着程序状态的变化,而程序状态的数量则由程序运行期间出现的控制变量值的最大可能组合数量确定。假设某程序中仅使用二进制控制变量(标志)。那么这种情况下,包含 n 个二进制控制变量的程序的状态数量范围就会在 n 到 2n 之间。
它可能是由开发者提供的,用于应对控制变量值的所有组合(本例中为 2n 种组合)。但是,更有可能的情况是,控制变量值的某些组合(最多为 2n-n)最终是未指定。那么,只要出现了意外的输入动作组合,此程序即可迁移到一个未指定状态。
下述事件中,它与某交易者 EA 的不活动有同等效果:
这种状态被称为 "unvisualized"(非可视化)。复杂性造成了枚举的困难,更难以接受的是,此程序的所有可能状态都会导致其不可靠……结构的复杂性,是构成安全陷阱的各种非可视化状态的源头。从内存保护故障到拓展程序新功能并生成各种性质的副作用,处于未指定状态下的程序行为各有不同。
大量的 PC 用户,很可能还有所有的软件开发者,都常常会碰到使用或开发过程中,某程序进入一种未指定状态的情况。
要消除程序中的这种未指定状态的可能性,早在设计阶段就要显式指定所有需要的状态,而且用于区分它们的只能是一个多值的控制变量。之后,有必要识别各状态之间的所有可能迁移,并以其不能“误入歧途”为原则开发一个程序。
要在程序行为的开发过程中达到严格要求,需要三个分量:
建议将基于 "state" 概念的一个有限自动机作为一个数学模型使用。自动机编程会为设计、实施、调试及归档之类的软件开发阶段提供支持。
其中的术语 'event'近些年来在编程中越来越常见,提议的方法基于 'state' 概念。将其与术语 'input action' (既可作为一个输入变量,亦可作为一个事件)结合后,即可引入术语 'automaton without output'。继后者之后,又会进一步引入术语 'output action' 以及(决定性有限) automaton 概念。基于此理念的编程领域由此被称为自动机编程,而各个开发过程则被称为自动机程序设计。
应用时,指定的方法在这方面很特殊,自动机由迁移图表示。为在各节点间进行区分,又引入 'state assignment' 术语。如果选择一个 'multi-valued state assignment' (多值状态分配),与选定变量可取值数量一致的那些状态,可以通过只采用一个变量的方式进行区分。有这一事实的存在,术语 'program observability' 才得以引入编程。
遵照提议方法的编程,会通过 'states' 而非 'variables' (标志)来执行,从而有助于更好地理解并指定问题及其分量。这种情况下的调试,则是依据自动机的记录完成的。
由于上述方案提议从迁移图转至带有正式且同构方法的程序代码,所以如果采用的是高级编程语言,按此作法来应用切换结构似乎更为合理。正因如此,才决定在引用自动机编程范式时采用术语 'SWITCH-technology'。
自动机方案的应用,已被进一步延伸到亦被称为 'reactive' (反应性)的事件驱动系统。反应系统会利用相关讯息,按照环境(相同的类中可以纳入一个 EA)设置的速度与环境进行交互。
利用自动机开发事件驱动系统得以实现,采用的是过程性方法,而显式状态编程亦由此得名。在此方法中,输出动作会被分配到迁移图(使用的是混合自动机 - Moore 与 Mealy 自动机)的弧、循环或节点。此法允许获取(作为相关输入动作反应的)动作序列的一种紧凑表示。
设定系统给定类的提议方案的逻辑更为集中化,因为它由事件事件处理程序中被移除,并生成一个由处理程序调用的互联自动机系统。该系统中自动机之间的交互,则可以通过状态数量的嵌套、调用及交换来实现。
该互联自动机系统会构成一个独立于系统的程序部分,而依赖于系统的部分则通过输入与输出动作函数、处理程序等构成。
给定方案还有一项关键特征,那就是应用时,自动机是以三位一体的方式使用:
后者允许控制自动机系统操作的准确度。日志记录基于开发的程序自动执行,而且可用于带有复杂程序逻辑的大规模问题。这种情况下,每一个日志都可被视为一个相关脚本。
日志允许监视运行中的程序,并阐明自动机并非“图片”,而是真实活动实体的事实 。建议自动机方法不仅用于创建控制系统,还可以用于控制对象的建模。
自动机编程的基本概念为 STATE (状态)。系统状态于任何特定时间 t0 的主要属性,是要将未来 (t > t0) 于过去 (t < t0) “分隔”开来,意思是当前状态包含有关系统过去的所有相关信息,而该信息在确定其对任何给定时间 t0 处生成的任何输入动作的反应方面是必不可少的。
使用术语 STATE 时无需了解历史数据。状态可被看作是一种特殊的特性,它非显式将当前时刻所有影响到实体反应的所有过去的输入动作组合起来。现在的反应仅取决于输入动作及当前状态。
'input action' 的概念也是自动机编程领域中的关键概念之一。最常见的输入动作为向量。根据意义和生成机制,其分量被划分为事件和输入变量。
有限状态集与构成一个不带输出的(有限)自动机的有限输入动作集的组合。该自动机通过按某种方式更改其当前状态以对输入动作作出反应。更改状态所依据的规则被称为自动机迁移函数。
自动机编程中所指的(有限)自动机,基本上就是 'automaton without output' (不带输出的自动机)与 'input action' (输入动作)的组合。该自动机对输入动作作出反应的方式,一是通过更改其状态,二是通过在输出处生成特定值。生成输出动作的规则即所谓的自动输出函数。
设计一个带有复杂行为的系统时,有必要将现有的控制对象、带有某特定操作集以及可能在外部(市场)环境中出现的某个给定事件集作为出发点。
实际情况中,设计更常见的情况是以控制对象和事件为前提:
此问题的初始数据,不仅是对目标系统行为的一种语言描述,还(或多或少)是从外部环境以及所有控制对象的超大量请求与命令来到系统的事件集的准确规格。
已构建一组控制状态。
控制对象的每一次请求都会被赋予一个相应的自动机输入变量,同时每个命令也都被赋予一个相应的输出变量。要用来确保所需系统行为的自动机是基于控制状态、事件、输入与输出变量构建的。
自动机程序的第一个特色,就是必须存在一个外层循环。基本上没有什么新内容;这里最主要的就是,此循环将是整个程序逻辑部分中唯一的循环!(即新进订单号。)
第二个特色源于第一个特色。任何自动机都包含一个由所有逻辑操作构成的切换结构(实际上,它就是由此构成的)。某自动机被调用时,此控件就会被传递至某个 'case' 标签,而且继相关动作之后,此自动机(子程序)操作直到下一次开始时才会完成。这些动作存在于检验迁移条件的过程中,如果特定条件被满足,就会调用相关的输出函数,且自动机状态也会被更改。
综上所述,主要结论就是,自动机的实施不仅简单,最重要的是,该程序无需众多的中间逻辑变量(标志,其于每一个自动机中的功能,均由一个多值状态变量提供)就可以实现。
这句最后陈述让人难以相信,因为我们已经习惯了直接采用大量的全局与局部变量(标志)。没有它们,我们该怎么办?!这些通常都是发出信号告诉程序某条件已被满足的标志。如开发者认为有必要,则此标志会被设定(为 TRUE),但之后(通常仅在此标志通过始终为 TRUE,开始产生目标效果之后)还要在程序的其它地方费力地重新设置为 FALSE。
有点耳熟,不是吗?现在来看示例:这里未使用任何其它变量;变化仅涉及到状态数量的值,而且只在某逻辑条件被满足时。它难道不是标志的一种有价值的替代吗?!
算法在创建程序逻辑部分的过程中扮演着重要角色。此处要牢记的关键词为“逻辑部分”。这种情况下,状态即一切的基础。另一个需要加上的词是“等待”。而且,以我看来,我们对“等待状态”已有一个相当充分的定义。在这种状态下,我们会等待输入动作的出现(属性、值或事件)。等待或长或短。换句话说,分稳定和不稳定的状态。
此状态的第一种属性即状态中有一组有限的输入动作在等待。任何算法(很明显,任何程序也)都具有输入与输出信息。输出动作可被划分为两类:变量(比如对象属性操作)与函数(比如应用程序启动函数、报告函数等的调用)。
此状态的第二个属性,是一组输出变量准确值的提供。如此则会揭示一种非常简单但极其重要的情形 - 任何给定时间的所有输出变量值均可确定,因为此算法(程序)在每一个时间点都处于一种特定状态。
状态的数据有限制,输出变量值的数量也是如此。用于记录迁移的函数被顺利地集成到了自动机函数当中,所以各状态之间的迁移顺序以及输出动作的传递也就始终可以确定了。
各功能的完整列表,请见章节 《2. 提议技术的功能》,而优势的完整列表请见章节 《3. 提议技术的优势》。此主题相关信息过多,本文无法一一述及。透彻地研究完 Anatoly Shalyto 撰写的所有研究文献之后,如有任何理论性问题,都可以私人形式发信到 shalyto@mail.ifmo.ru 直接与他联系。
而且,作为秉承其科学理念、又牢记我们目标与问题的使用者,下面我会进一步提供三个例子,讲解我的自动机编程技术实施。
7.1. 方便理解的示例
所谓状态,就是系统所在的一种模式。比如说,水存在于三种状态中:固态、液态或气态。而其从一个状态向另一个状态的迁移,是受到一个变量的影响 - 温度(恒定压力条件下)。
假设我们有一个基于时间的温度 (t) 图表(本例中是价格值):
int STATUS=0; // a global integer is by all means always a variable !!! STATUS is a multi-valued flag //----------------------------------------------------------------------------------------------// int start() // outer loop is a must { switch(STATUS) { case 0: //--- start state of the program if(T>0 && T<100) STATUS=1; if(T>=100) STATUS=2; if(T<=0) STATUS=3; break; case 1: //--- liquid // set of calculations or actions in this situation (repeating the 1st status -- a loop in automata-based programming) // // and calls of other nested automata A4, A5; if(T>=100 ) { STATUS=2; /* set of actions when transitioning, calls of other nested automata A2, A3;*/} if(T<0) { STATUS=3; /* set of actions when transitioning, calls of other nested automata A2, A3;*/} // logging transitions and actions when the condition is met. break; case 2: //--- gas // set of calculations or actions in this situation (repeating the 2nd status -- a loop in automata-based programming) // // and calls of other nested automata A4, A5; if(T>0 && T<100) { STATUS=1; /* set of actions when transitioning, calls of other nested automata A2, A3;*/} if(T<=0) { STATUS=3; /* set of actions when transitioning, calls of other nested automata A2, A3;*/} // logging transitions and actions when the condition is met. break; case 3: //--- solid // set of calculations or actions in this situation (repeating the 3rd status -- a loop in automata-based programming) // // and calls of other nested automata A4, A5; if(T>0 && T<100) {STATUS=1; /* set of actions when transitioning, calls of other nested automata A2, A3;*/} if(T>=100) {STATUS=2; /* set of actions when transitioning, calls of other nested automata A2, A3;*/} // logging transitions and actions when the condition is met. break; } return(0); }
通过添加压力参数 P 和新状态并引入图表中显示的一个复杂依赖关系,即可加大该程序的复杂性:
该自动机有 32 = 9 迁移条件,所以不会遗漏或忽略任何事情。编写指令和规则时,这种样式也非常方便!这里不允许有任何漏洞和规则回避 - 必须涉及到一连串事件变量的所有组合,并描述到所有情况。
自动机编程要求我们考虑到方方面面,即使一连串事件中的某些变量在其他情况下不会被想到。正因如此,它是检查规则、指令及控制系统一致性与完整性的主要工具。还有一条数学定理:
迁移图:N = 3 种状态,迁移与循环数量为 N2 = 9 (与箭头数量相等)。
如果示例中的变量数量不同,则:
据其显示,表中所有计算值均呈指数级增长,即设计是一个在选择主系统变量时要求完全性的复杂过程。
即便是只有两个参数,要描述到全部也非常困难!然而实际情况下,一切都要简单得多!根据逻辑和意义,有 50-95% 的迁移实际上不可能存在,而状态的数量也要减少 60-95%。对于逻辑和意义的这种分析,会在很大程度上降低描述所有迁移与状态的难度。
在更复杂的情况下,则要求针对某 EA 中所有已知的输入与输出数据,计算状态的最大数量。至于该问题的解决方案,可以通过应用组合学与组合、置换、排列和计数组合学公式找到。
7.2. 带延迟继电器
在 EA 中,继电器、触发器、寄存器、计数器、解码器、比较器以及其它非线性数字与模拟控制系统元件的编程都非常简便。
int status=0; // at the beginning of the program we globally assign //------------------------------------------------------------------// switch(status) { case 0: // start Y=x; if(x>xmax) {status=1;} if(x<xmin) {status=2;} break; case 1: //++++++++++++++++++++ if(x>xmax) Y=x; if(x<xmax) Y=xmin; if(x<=xmin) {status=2; Y=xmin;} break; case 2: //-------------------- if(x<xmin) Y=x; if(x>xmin) Y=xmax; if(x>=xmax) {status=1; Y=xmax;} break; }
继电器特性:
7.3. 9 种状态与一连串事件的 81 种变量的模板
Y 是从 1 到 9 自动机的当前输入状态。Y 值是由 EA 在给定子程序之外生成。MEGASTATUS 是 Y 的过去状态。
int MEGASTATUS=0; // at the beginning of the program we globally assign //---------------------------------------------------------------------// void A0(int Y) // automaton template { switch(MEGASTATUS) { case 0: // start MEGASTATUS=Y; break; case 1: // it was the past // it became current, repeating if(Y=1) { /*set of actions in this situation, calls of other nested automata A2, A3, ... */ } // Loop// // new current if(Y=2) { /* set of actions in this situation */ } if(Y=3) { /* set of actions in this situation */ } if(Y=4) { /* set of actions in this situation */ } if(Y=5) { /* set of actions in this situation */ } if(Y=6) { /* set of actions in this situation */ } if(Y=7) { /* set of actions in this situation */ } if(Y=8) { /* set of actions in this situation */ } if(Y=9) { /* set of actions in this situation */ } // logging transitions and actions when the condition is met. break; case 2: // it was the past // it has become current if(Y=1) { /* set of actions in this situation */ } if(Y=2) { /* set of actions in this situation */ } //Loop// if(Y=3) { /* set of actions in this situation */ } if(Y=4) { /* set of actions in this situation */ } if(Y=5) { /* set of actions in this situation */ } // e.g. if the transition from 2 to 6 is in essence impossible or does not exist, do not write anything if(Y=6) { /* set of actions in this situation */ } // the automaton will then be reduced but the automaton template shall be complete to count in everything if(Y=7) { /* set of actions in this situation */ } if(Y=8) { /* set of actions in this situation */ } if(Y=9) { /* set of actions in this situation */ } // logging transitions and actions when the condition is met. break; case 3: // it was the past // it has become current if(Y=1) { /* set of actions in this situation */ } if(Y=2) { /* set of actions in this situation */ } if(Y=3) { /* set of actions in this situation */ } //Loop// if(Y=4) { /* set of actions in this situation */ } if(Y=5) { /* set of actions in this situation */ } if(Y=6) { /* set of actions in this situation */ } if(Y=7) { /* set of actions in this situation */ } if(Y=8) { /* set of actions in this situation */ } if(Y=9) { /* set of actions in this situation */ } // logging transitions and actions when the condition is met. break; case 4: // it was the past // it has become current if(Y=1) { /* set of actions in this situation */ } if(Y=2) { /* set of actions in this situation */ } if(Y=3) { /* set of actions in this situation */ } if(Y=4) { /* set of actions in this situation */ } //Loop// if(Y=5) { /* set of actions in this situation */ } if(Y=6) { /* set of actions in this situation */ } if(Y=7) { /* set of actions in this situation */ } if(Y=8) { /* set of actions in this situation */ } if(Y=9) { /* set of actions in this situation */ } // logging transitions and actions when the condition is met. break; case 5: // it was the past // it has become current if(Y=1) { /* set of actions in this situation */ } if(Y=2) { /* set of actions in this situation */ } if(Y=3) { /* set of actions in this situation */ } if(Y=4) { /* set of actions in this situation */ } if(Y=5) { /* set of actions in this situation */ } //Loop// if(Y=6) { /* set of actions in this situation */ } if(Y=7) { /* set of actions in this situation */ } if(Y=8) { /* set of actions in this situation */ } if(Y=9) { /* set of actions in this situation */ } // logging transitions and actions when the condition is met. break; case 6: // it was the past // it has become current if(Y=1) { /* set of actions in this situation */ } if(Y=2) { /* set of actions in this situation */ } if(Y=3) { /* set of actions in this situation */ } if(Y=4) { /* set of actions in this situation */ } if(Y=5) { /* set of actions in this situation */ } if(Y=6) { /* set of actions in this situation */ } //Loop// if(Y=7) { /* set of actions in this situation */ } if(Y=8) { /* set of actions in this situation */ } if(Y=9) { /* set of actions in this situation */ } // logging transitions and actions when the condition is met. break; case 7: // it was the past //it has become current if(Y=1) { /* set of actions in this situation */ } if(Y=2) { /* set of actions in this situation */ } if(Y=3) { /* set of actions in this situation */ } if(Y=4) { /* set of actions in this situation */ } if(Y=5) { /* set of actions in this situation */ } if(Y=6) { /* set of actions in this situation */ } if(Y=7) { /* set of actions in this situation */ } //Loop// if(Y=8) { /* set of actions in this situation */ } if(Y=9) { /* set of actions in this situation */ } // logging transitions and actions when the condition is met. break; case 8: // it was the past // it has become current if(Y=1) { /* set of actions in this situation */ } if(Y=2) { /* set of actions in this situation */ } if(Y=3) { /* set of actions in this situation */ } if(Y=4) { /* set of actions in this situation */ } if(Y=5) { /* set of actions in this situation */ } if(Y=6) { /* set of actions in this situation */ } if(Y=7) { /* set of actions in this situation */ } if(Y=8) { /* set of actions in this situation */ } //Loop// if(Y=9) { /* set of actions in this situation */ } // logging transitions and actions when the condition is met. break; case 9: // it was the past // it has become current if(Y=1) { /* set of actions in this situation */ } if(Y=2) { /* set of actions in this situation */ } if(Y=3) { /* set of actions in this situation */ } if(Y=4) { /* set of actions in this situation */ } if(Y=5) { /* set of actions in this situation */ } if(Y=6) { /* set of actions in this situation */ } if(Y=7) { /* set of actions in this situation */ } if(Y=8) { /* set of actions in this situation */ } if(Y=9) { /* set of actions in this situation */ } //Loop// // logging transitions and actions when the condition is met. break; } MEGASTATUS=Y; }
7.4. 音频播放器自动机
我们来研究一款 简易的音频播放器。
该设备可以有 6 种状态:
此音频播放器控制系统表示为一个自动机。按下的按钮被视为对自动机有影响的事件。音轨、正在播放、显示控件等等之间的迁移,均为输出动作。
switch(STATUS) { case 0: //--- "Ready" if(Event == 3) { STATUS = 3; } //«>>» button pressed if(Event == 6) { STATUS = 1; } //Audio file not found if(Event == 1) { STATUS = 2; } //«PLAY» button pressed z1(); // Set the indicator to the initial state break; case 1: //--- "No Track" z6(); // Give the «No Track» message break; case 2: //--- "Playing" if(Event == 4) { STATUS = 4; } //«<<» button pressed if(Event == 5) { STATUS = 5; } //«PAUSE»( | | ) button pressed if(Event == 3) { STATUS = 3; } //«>>» button pressed if(Event == 2) { STATUS = 0; } //«STOP» button pressed z2(); // Playing break; case 3: //--- "Fast-Forward" z3(); // Next track { STATUS=2; } break; case 4: //--- "Rewind" z4(); // Previous track { STATUS=2; } break; case 5: //--- "Pause" if(Event == 5) { STATUS = 2; } //«PAUSE» button pressed if(Event == 1) { STATUS = 2; } //«PLAY» button pressed if(Event == 2) { STATUS = 0; } //«STOP» button pressed if(Event == 3) { STATUS = 3; } //«>>» button pressed if(Event == 4) { STATUS = 4; } //«<<» button pressed z5(); //Pause break; }
理论上,此自动机包含 36 个迁移变量,但其中只有 15 个是真实存在的,所有细节请见作者提供的相关描述。
如何准备和编写项目文档的相关完整信息,请见这里 http://project.ifmo.ru/books/3,而本文中,我只会摘一小段以飨众位:
我鼓励你们当中的所有人:
我希望自动机编程会:
如果所有的交易开发者都能遵循这种编程法,那么创建一种无亏损 EA 的目标就一定可以实现。在此第一篇文章中,我是试着为您呈现自动机设计领域内的一种全新的创新与研究渠道,以及作为新发明与新探索的一种推动因素的编程。
还有一件事 - 我完全同意此作者文章中的内容,而且觉得,以一种简明的方式提供给您很重要(全文请见 http://is.ifmo.ru/works/open_doc/):
实际编程中的中心问题,就是程序代码的理解问题。掌握源代码始终是有好处的,但问题在于,这往往还不够。而且,为了获取对于某种非普通程序的理解,通常还需要额外的文档编制。而这一需求,会随着代码量的增长呈指数级增长。
旨在复原开发者所做原始设计决定和理解程序的程序代码分析,是编程技术的两大重要分支,在没有足够的源代码来理解程序时,它们两个相互依存。
每一位参与过大型软件重构项目的人,始终都忘不了第一次看到一大堆编制不良(尽管不总是编写糟糕)源代码之后,油然而生的那种无助感和窘困纠结感。如果接触不到主要开发者,源代码的可用性基本上没什么用处。如果程序是用某种相对低阶的语言编写,再加上编制不良,那么所有的主要设计决策往往都分散于编程细节当中,需要重构。类似这种情况下,接口规格与架构描述之类的高阶文档编制的价值可能比源代码本身的价值更高。
意识到源代码不足以理解程序之后,即催生了将代码与某种高阶文档编制结合起来的尝试。
如果您错过了项目的早期阶段,其复杂性和工作量几乎会将您与源代码完全“隔绝”——倘若没有什么高阶文档编制。在没有最初操作该项目的开发者或者足够的、允许筛分相关架构决定的文档编制的情况下,要理解“史前”代码,很可能就是程序员所面临的最为艰难的挑战»。
虽然说缺少源代码不太妙,但其可用性也是同样的乏善可陈。“大团圆结局”式的生活,还缺什么呢?答案很简单 - 将程序文档编制作为分量之一纳入的一份详细且准确的设计文档编制中。
没有文档在手,桥梁、道路和摩天大楼通常都建不起来,但程序不是这样。
编程中出现过的情况,可以如下定义:“如果建筑师按照程序员编写程序的方法来建大楼,那么过路的第一只啄木鸟就能摧毁整个文明了。”
针对硬件发布大量详细且明确的设计文档,就可以在其发布多年之后,还能相对轻松地被一位资质平平的专业人员所理解和修改。但为什么软件中都没有此类文档,或是未按十分正规的方式编写,又或是需要高精尖的专家才能修改(如果找不到开发人员)呢?
很明显,这种情况可解释如下。首先,硬件的开发和制造是两种由不同组织执行的不同过程。因此,如果文档编制的质量差,开发工程师就会穷尽其余生之力在“工厂”中工作,这当然不是他希望的。而说到软件开发,情况就有所变化了。因为这种情况下,软件开发者与生产商通常都是一个人或一家公司,所以,不论文档列表如何,其内容通常都非常浅显。
第二,硬件是“硬”的,而软件是“软”的。它会让修改程序更加简单,但还是未能给出不一同发布设计文档的根据。众所周知,大多数程序员都近乎病态地不喜阅读,当然也就更不喜欢编制文档了。
有经验显示:几乎没有任何新程序员(甚至包括最聪明的),可以准备设计文档。而且,尽管他们中有很多人都学习和经历了漫长且复杂的数学课程,但对于他们撰写文档的逻辑性和严格性还是几乎没有任何作用。在通篇的文档(不论大小)中,他们可能针对一个相同的事项采用不同的标记,由此随心所欲地称呼、且字母不分大小写。比如,bulb 与 light bulb、lamp 或 Lamp 等。很难想像,当他们充分发挥其想像力时,会是怎样一副光景!
很明显,之所以会这样,就是因为编程时编译器标记不一致,而已撰写的设计文档却没有任何提示。
软件文档编制质量的问题,已逐渐成为了一个意义不断加大的社会问题。软件开发也越来越像是以利润为导向的娱乐业。一切事情都仓促完成,而不考虑产品未来会变成什么样。与娱乐业相似,编程亦按“利润和损失”来衡量一切,而不是“好与坏”。大多数情况下,出色的技术并非真正好的,而是有人肯掏腰包的技术。
项目的限制(不入文档)程度越高,作者就越不可或缺。撰写设计文档的百般不愿,很可能还与此有关。
遗憾的是,这一作风已蔓延到了重大系统软件的开发领域。这主要是由于大多数情况下,程序都是编写的,而不是设计的。“设计时,任何比 CRC 卡片或使用用例图复杂的技巧,都会被视为太过复杂而不被采用。如果任何既有的技术需要上报领导,进而可能导致程序员不能按期完成,那么他就会拒绝应用该技术。”
这会导致即便是“用户也不考虑软件中的一般性错误”的境况。
目前的普遍观点是:设计和正确的文档适合于大型的建筑,而不是软件。
综上所述,要注意的是,这种情况在过去(早期使用大型计算机时)的编程中没有过,程序的设计或是开发都要十分谨慎,因为如有错误,下一次尝试一般一天之内就会发生。由此,技术进展将我们引领到了一个不那么认真的编程环境中。
遗憾的是,A.A. Shalyto 所工作的研究所的网站上不能找到我们的问题和顾虑。他们有自己的问题和目标,对我们的概念和定义完全不熟悉、不知情,所以也没有我们主题相关的示例。
A.A. Shalyto 编著的主要书籍/教科书:
项目:
以及其它有趣的文章和项目: http://project.ifmo.ru/projects/, http://is.ifmo.ru/projects_en/ and http://is.ifmo.ru/articles_en/.
一个 魔方可能的不同事件数量为 (8!× 38−1) × (12! × 212−1)/2 = 43 252 003 274 489 856 000. 但此数字还未考虑到中心格也可以有不同的朝向。
由此,如果考虑中心面的朝向,事件的数量就会变大 2048 倍,即 88 580 102 706 155 225 088 000。
外汇市场和交易没有这么多的连续事件变量,但与其关联的问题却可以利用这种编程范式在 100-200 步内解决。这是真的!整个市场都在同 EA 不断地竞争。就像是在下 国际象棋 ,没有人知道对手接下来的出招(就像我们一样)。但也有令人敬畏的计算机程序,比如基于 α-β 剪枝算法设计的 里布卡(非常强大的国际象棋引擎)。
愿其它编程领域中他人的此类成果也能赋予您专注于工作的能量!虽然我们确实都清楚自己一无所知。
本社区仅针对特定人员开放
查看需注册登录并通过风险意识测评
5秒后跳转登录页面...
移动端课程