• 一个基于约束传播的,玩具级微型计算语言的设计和简单实现


    这个程序就是做来玩和练习的,代码是玩具级别的,用的python,基本可以正常工作了。

    先介绍应用背景:

         在流体机械设计中,通常根据性能参数进行设计,算出其它变量,但问题是,在设计过程中,需要进行变量的手工调整,例如圆整,修正到某一范围,校核等等。

    其计算模式举例如下:

    1.定义变量,如输入压力Pin=0.98,输入温度Tin=27,输入流量Qvin=400,kv2,φ2r,b2,D2,u2,qin等等。。。

    2.根据某些物理公式,算出几个新的量,如转速 n=33.9*sqrt(kv22r*b2/D2*(u2^3)/qin)

    3.把n从8296.93圆整为整数8300,

    4.重新计算b2/D2=0.06455,校核可知0.02<0.06455<0.065,符合要求

    5.根据n计算出其它新的变量,修正,校核。。。

    。。。

    观察可以发现,这种计算模式,和《计算机程序的构造与解释》中提到的约束传播系统很像,如果把一个变量看作一个对象,那么,当它位于一个公式的左侧,例如n,也就意味着,右侧变量例如kv2更新时,应该给它发送一个消息,让它重新计算自己的值,当n更新后,如果它发现有别的变量依赖于自己,它应该发消息通知它们更新自己的值。

    还可以看出,这种依赖关系形成了一个图,例如应该有一条从kv2到n的边,把n称为kv2的订阅者。

    所以这种计算模式可以用约束传播系统建模,但是此处和书里的约束传播系统有差异:此处的约束传播系统是有向图,而书里是无向图,设计成有向图主要是为了简单,无向图的消息发送顺序是难以控制的,而且构造的时候公式中的每个变量都要持有其它对象的引用,太麻烦,有向图只需要在公式左侧的那个变量哪里保存公式右侧的每个变量的引用。

    形成有向图后,每个变量的状态设为invaild,这种状态下,不会向它的会订阅者发送更新消息,收到获取值的消息时报错。

    有向图中,还有一些源点,是最初给定值的变量。

    当某个变量被赋值时,它把自己设为新值,同时向它的订阅者发送更新消息。订阅者计算自己的新值,如果和旧值相同,就沉默;否则,更新自己,通知订阅者更新。

    so,想象一下,在你的面前,虚空之中漂浮着一张有向图, 由kv2--->n这样的一条条边练成,当一个点被赋予值,从这点荡出一圈圈涟漪,传到它的下一级,再从更新过的点荡出新的波纹,传开,传开。。。直到所有的点都收敛,水面恢复宁静。

    好了,说代码,每一个变量都要保存它的订阅者,它的表达式,注意到,数字1.1是一种变量,变量a是一种表达式,所以代码如下:

    View Code
      1 #!/usr/bin/env python
      2 # -*- coding: utf-8 -*-
      3 
      4 """
      5 变量is-a表达式
      6 数值is-a表达式
      7 故有如下继承关系
      8 
      9 通过env的符号表可以查到Expr的实例
     10 """
     11 __all__ = ['Expr','Var','Number']
     12 
     13 class Expr(object):
     14     op=""               #a function
     15     parameters=[]       #Expr list
     16     desc=""
     17 
     18     def __init__(self,op,paras,desc=""):
     19         self.op=op
     20         self.parameters=paras
     21         self.desc=desc
     22 
     23     def get_value(self):
     24         pl=[p.get_value() for p in self.parameters]
     25         return self.op(*pl)
     26 
     27     def set_desc(self,d):
     28         self.desc=d
     29 
     30     def dump(self):
     31         pas=[]
     32         if len(self.parameters):
     33             pas=[s.dump() for s in self.parameters]
     34         pas.insert(0, '('+self.op.__name__)
     35         return ' '.join(pas) + ')'
     36 
     37 
     38 class Number(Expr):
     39     value=0.0
     40     def __init__(self,v):
     41         self.value=v
     42 
     43     def get_value(self):
     44         return self.value
     45 
     46     def dump(self):
     47         return str(self.value)
     48 
     49     def update(self):
     50         pass
     51 
     52 class Var(Expr):
     53     name=""
     54     desc=""
     55     expr=None
     56     value=0.0
     57     subscribers=[]
     58     state="invaild"
     59 
     60     def __init__(self,name,desc=""):
     61         self.name=name
     62         self.desc=desc
     63         self.state="invaild"
     64 
     65     def broadcast(self):
     66         for s in self.subscribers:
     67             s.update()
     68 
     69     def update(self):
     70         self.state="normal"
     71         new_value=self.expr.get_value()
     72         if new_value == self.value:
     73             return
     74         self.value=new_value
     75         self.broadcast()
     76 
     77     def set_expr(self,exp):
     78         self.expr=exp
     79         if isinstance(exp,Number):
     80             self.update()
     81 
     82     def set_value(self,v):
     83         self.value=v
     84         self.state="normal"
     85         self.broadcast()
     86 
     87     def get_value(self):
     88         if self.state=="invaild":
     89             self.update()
     90         assert self.state=="normal"
     91         return self.value
     92 
     93     def subscribe(self,subs):
     94         for sub in subs:
     95             self.subscribers.append(sub)
     96 
     97     def dump(self):
     98         expr_d=""
     99         if self.expr:
    100             expr_d=' '+self.expr.dump()
    101         return str(self.name) +"="+str(self.value)+expr_d#+" "+self.desc
    102 
    103 
    104 def test():
    105     a=Var("a","变量a")
    106     b=Var("b","变量b")
    107 
    108 if __name__=="__main__":
    109     test()

    所有的变量当然是要保存到一个符号表(或称环境)里的,同时,这个环境里还要有加减乘除,sin,sqrt这样的基本运算的定义,pi,e这样的常数的定义,python的operator和math模块就够用了。

    代码如下:

    View Code
      1 #!/usr/bin/env python
      2 # -*- coding: utf-8 -*-
      3 import math
      4 import operator
      5 from expr import Var,Number,Expr
      6 from parser import Parser
      7 
      8 class CmdFilter(object):
      9     def __init__(self,env,input_file):
     10         self.env=env
     11         self.input_file=input_file
     12 
     13     def readline(self):
     14         while True:
     15             s=self.input_file.readline()
     16             if not s:
     17                 return s
     18             if self.env.filter_cmd(s):
     19                 return s
     20 
     21 class Env(object):
     22     """
     23     求值环境,提供变量符号表和函数符号表
     24     """
     25     symbol_table={} #存放变量
     26     expr_table=[]   #存放自由表达式
     27     function_table={}#存放函数,对外只读
     28     cmds=['dump','run']    #env先于parser处理掉一些命令,如dump
     29     parser=None
     30 
     31     def __init__(self):
     32         self.fill_function_table()
     33         self.fill_symbol_table()
     34         self.parser=Parser(self)
     35 
     36     def dump(self):
     37         print "-"*70,"\ndump all variables and expressions:"
     38         for k,v in self.symbol_table.items():
     39             print k+":",v.get_value()
     40         print "\nall checkings:"
     41         for e in self.expr_table:
     42             print e.get_value(),"=",e.dump()
     43         print "-"*70
     44 
     45     def run(self):
     46         for k,v in self.symbol_table.items():
     47             v.update()
     48 
     49     def fill_function_table(self):
     50         #算术运算符
     51         #1.+,-,*,/,^,=,(,)   算术运算符
     52         self.function_table['+']=operator.add
     53         self.function_table['-']=operator.sub
     54         self.function_table['*']=operator.mul
     55         self.function_table['/']=operator.div
     56         self.function_table['^']=operator.pow
     57         #逻辑运算符
     58         #2.==,>=,>,<=,<,!=   逻辑运算符
     59         self.function_table['==']=operator.eq
     60         self.function_table['>=']=operator.ge
     61         self.function_table['>']=operator.gt
     62         self.function_table['<=']=operator.le
     63         self.function_table['<']=operator.lt
     64         self.function_table['!=']=operator.ne
     65         self.function_table['sqrt']=math.sqrt
     66         self.function_table['sin']=math.sin
     67         self.function_table['cos']=math.cos
     68         self.function_table['tan']=math.tan
     69         self.function_table['asin']=math.asin
     70         self.function_table['acos']=math.acos
     71         self.function_table['atan']=math.atan
     72         self.function_table['exp']=math.exp
     73         self.function_table['pow']=math.pow
     74         self.function_table['factorial']=math.factorial
     75         self.function_table['fabs']=math.fabs
     76         self.function_table['ln']=math.log
     77         self.function_table['log10']=math.log10
     78 
     79 
     80     def fill_symbol_table(self):
     81         self.symbol_table['pi']=Number(math.pi)
     82         self.symbol_table['e'] =Number(math.e)
     83 
     84     def add_expr(self,e):
     85         if e:
     86             self.expr_table.append(e)
     87 
     88     def get_function(self,name):
     89         if self.function_table.has_key(name):
     90             return self.function_table[name]
     91         else:
     92             return None
     93 
     94     def get_variable(self,name):
     95         if self.symbol_table.has_key(name):
     96             return self.symbol_table[name]
     97         else:
     98             return None
     99 
    100     def set_variable(self,name,var):
    101         self.symbol_table[name]=var
    102 
    103     def filter_cmd(self,s):
    104         s=s.strip()
    105         if s in self.cmds:
    106             fun=getattr(self,s)
    107             fun()
    108             return None
    109         return s
    110 
    111     def exec_stream(self,in_file):
    112         input_file=CmdFilter(self,in_file)
    113         self.parser.parse(input_file)
    114 
    115 import sys
    116 def test():
    117     env=Env()
    118     env.exec_stream(sys.stdin)
    119 
    120 if __name__=="__main__":
    121     test()


    接下来,词法分析和语法分析,词法分析没有手写,也没有用flex那样的工具,直接用了一排正则表达式,挨个往下匹配,匹配上了就返回。

    严格来说,这个是不太好的,此处的词法分析原理上是不能和flex比的,flex里的多个正则表达式是合并到一个NFA里,再转化成一个DFA的,所以它的规则首先是最长优先,其次是长度相同时写在前面的优先,此处只有写在前面的优先,不太好。

    语法分析是递归下降文法分析。一行一行地分析,一行是一个token的list。

    View Code
      1 #!/usr/bin/env python
      2 # -*- coding: utf-8 -*-
      3 from math import *
      4 from operator import *
      5 import re
      6 from expr import Var,Number,Expr
      7 
      8 """  9 a=1+b+c^2
     10 c=2
     11 b=c+2
     12 这样的一行一行,分成
     13 ID ASSIGN ADD ID ADD EQ POW 2 EOL
     14 ID ASSIGN 2 EOL
     15 ID ASSIGN ID ADD 2 EOL
     16 这样的一行一行token,
     17 输出是一行一个token list的形式
     18 
     19 token分为如下几类:
     20 1.+,-,*,/,^,=,(,),,   算术运算符
     21 2.==,>=,>,<=,<,!=   逻辑运算符
     22 3.[0-9]+\.[0-9]*[Ee][+-]?[0-9]+  [0-9]+ 字面浮点数和整数
     23 4.[a-zA-Z_]+        变量或函数名称标识符
     24 5.[ \\t\\n]           忽略,或结束
     25 
     26 由于使用正则表达式直接匹配,所以和flex不同的是:
     27 无法确保当有多个匹配项时,最长优先,因此,只能利用先后顺序解决冲突,
     28 因而必须把==放在=前面。
     29 """
     30 logic_op_re=re.compile(r'==|>=|>|<=|<|!=')
     31 number_re  =re.compile(r'[+-]?[0-9]+\.?[0-9]*[Ee]?[+-]?[0-9]?') 
     32 arith_op_re=re.compile(r'\+|-|\*|/|\^|=|\(|\)|,')
     33 int_re     =re.compile(r'[0-9]+')
     34 id_re      =re.compile(r'[a-zA-Z_]+')
     35 blank_re   =re.compile(r'[\ |\t|\r]+')
     36 comment_re =re.compile(r'"([^"]*)"')
     37 other_re   =re.compile(r'.+')
     38 
     39 def scanner(line):
     40     result=[]
     41     while True:
     42         line=line.strip()
     43         if not line:
     44             return result
     45 
     46         m=re.match(logic_op_re,line)
     47         if m:
     48             result.append(('logic_op',m.group()))
     49             line=line[m.end():]
     50             continue
     51 
     52         m=re.match(number_re  ,line)
     53         if m:
     54             result.append(('number',float(m.group())))
     55             line=line[m.end():]
     56             continue
     57 
     58         m=re.match(arith_op_re,line)
     59         if m:
     60             result.append(('arith_op',m.group()))
     61             line=line[m.end():]
     62             continue
     63 
     64         m=re.match(int_re     ,line)
     65         if m:
     66             result.append(('number',float(m.group())))
     67             line=line[m.end():]
     68             continue
     69 
     70         m=re.match(id_re      ,line)
     71         if m:
     72             result.append(('id',m.group()))
     73             line=line[m.end():]
     74             continue
     75 
     76         m=re.match(comment_re ,line)
     77         if m:
     78             result.append(('comment',m.group()))
     79             line=line[m.end():]
     80             continue
     81 
     82         m=re.match(blank_re   ,line)
     83         if m:
     84             line=line[m.end():]
     85             continue
     86 
     87         m=re.match(other_re,line)
     88         if m:
     89             print "亲,看看你都塞了一堆什么进来呀?\""+m.group()+"\" 人家好伤心的呀!" 
     90             line=line[m.end():]
     91             return result
     92 
     93 class Parser(object):
     94     """ 文法分析: """
     95     input_file=None
     96     env=None
     97 
     98     def __init__(self,env):
     99         self.env=env
    100 
    101     def parse(self,input_file):
    102         """
    103         如入可以是sys.stdin,a file,a string
    104         要求实现readline()方法
    105         """
    106         self.input_file=input_file
    107         self.run()
    108 
    109     def run(self):
    110         while True:
    111             line=self.input_file.readline()
    112             if not line:
    113                 return
    114             tokens=scanner(line)
    115 
    116             #把字母名称的函数标示出来
    117             r=[]
    118             for t in tokens:
    119                 if t[0]=='id' and self.env.get_function(t[1]):
    120                     r.append(('function',t[1]))
    121                 else:
    122                     r.append(t)
    123 
    124             tokens=r
    125 
    126             #把comment提取出来
    127             comments=map(lambda x:x[1],
    128                          filter(lambda x:x[0]=="comment",tokens))
    129             comments=' '.join(comments)
    130             tokens=filter(lambda x:x[0]!="comment",tokens)
    131 
    132             #含有=的表达式是约束方程,其它的都是expr
    133             c=tokens.count( ('arith_op', '='))
    134             if c==0:
    135                 #没有约束到变量的表达式
    136                 e=self.parse_expr(tokens)
    137                 #e.set_desc(comments)
    138                 self.env.add_expr(e)
    139                 continue
    140 
    141             if c>1:
    142                 print "亲,赋值一次就够了,你肿么赋值了"+str(c)+"次涅?"
    143                 continue
    144 
    145             #c=1
    146             if len(tokens)<3 or tokens[0][0]!='id' or \
    147                tokens[1]!=('arith_op','='):
    148                 print "亲,你给我的表达式格式有问题偶~:"+line
    149                 continue
    150 
    151             var_name=tokens[0][1]
    152             var=self.env.get_variable(var_name) 
    153             if var is None:
    154                 var=Var(var_name,comments)
    155                 self.env.set_variable(var_name,var)
    156 
    157             e=self.parse_expr(tokens[2:])
    158             var.set_expr(e)
    159 
    160     def parse_expr(self,tokens):
    161         """
    162         token分为如下几类:
    163         1.+,-,*,/,^,=,(,),,   算术运算符
    164         2.==,>=,>,<=,<,!=   逻辑运算符
    165         3.[0-9]+\.[0-9]*[Ee][+-]?[0-9]+  [0-9]+ 字面浮点数和整数
    166         4.[a-zA-Z_]+        变量或函数名称标识符
    167         5.[ \\t\\n]           忽略,或结束
    168 
    169         BNF:
    170         expr=expr[==|>=|>|<=|<|!=]expr|expr
    171         expr=expr+expr | expr-expr
    172         expr=expr*expr | expr/expr
    173         expr=expr^expr
    174         expr=function(expr[,expr])
    175         expr=(expr)
    176         expr=<float>|<var>
    177         """
    178         if len(tokens):
    179             expr,rest=self.parse_logic_op(tokens)
    180             return expr
    181 
    182     #能处理就处理,不能处理原样返回。
    183     def parse_logic_op(self,tokens):
    184         left,rest=self.parse_add_sub_op(tokens)
    185         if not len(rest):
    186             return left,rest
    187 
    188         logic_op_list=["==",">=",">","<=","<","!="]
    189 
    190         if rest[0][1] not in logic_op_list:
    191             return left,rest
    192 
    193         op=self.env.get_function(rest[0][1])
    194         right,rest=self.parse_add_sub_op(rest[1:])
    195         return Expr(op,[left,right]),rest
    196 
    197     def parse_add_sub_op(self,tokens):
    198         left,rest=self.parse_mul_div_op(tokens)
    199         add_sub_op_list=["+","-"]
    200 
    201         while len(rest) and rest[0][1] in add_sub_op_list:
    202             op=self.env.get_function(rest[0][1])
    203             right,rest=self.parse_mul_div_op(rest[1:])
    204             left=Expr(op,[left,right])
    205 
    206         return left,rest
    207 
    208     def parse_mul_div_op(self,tokens):
    209         left,rest=self.parse_pow_op(tokens)
    210         mul_div_op_list=["*","/"]
    211 
    212         while len(rest) and rest[0][1] in mul_div_op_list:
    213             op=self.env.get_function(rest[0][1])
    214             right,rest=self.parse_pow_op(rest[1:])
    215             left=Expr(op,[left,right])
    216 
    217         return left,rest
    218 
    219     def parse_pow_op(self,tokens):
    220         left,rest=self.parse_function_op(tokens)
    221         pow_op_list=["^"]
    222 
    223         while len(rest) and (rest[0][1] in pow_op_list):
    224             op=self.env.get_function(rest[0][1])
    225             right,rest=self.parse_pow_op(rest[1:])
    226             left=Expr(op,[left,right])
    227         return left,rest
    228 
    229     def parse_function_op(self,tokens):
    230         if tokens[0][0] in ['number','id']:
    231             return self.parse_float_var_op(tokens)
    232         if tokens[0][1]=='(':
    233             return self.parse_parentheses_op(tokens)
    234 
    235         if tokens[0][0]!='function':
    236             return None,tokens
    237 
    238         op=self.env.get_function(tokens[0][1])
    239         if op and tokens[1][1]=='(':
    240                 paras=[]
    241                 tokens=tokens[2:]
    242                 left,tokens=self.parse_add_sub_op(tokens)
    243                 paras.append(left)
    244                 while tokens[0][1]==',':
    245                     left,tokens=self.parse_add_sub_op(tokens[1:])
    246                     paras.append(left)
    247                 if tokens[0][1]==')':
    248                     tokens=tokens[1:]
    249                 else:
    250                     print "bad syntax. tokens found ->",tokens
    251 
    252                 expr=Expr(op,paras)
    253                 return expr,tokens
    254         else:
    255             print "error bad syntax ->",tokens
    256         return None,tokens
    257 
    258     def parse_parentheses_op(self,tokens):
    259         if tokens[0][1]=='(':
    260             left,tokens=self.parse_logic_op(tokens[1:])
    261             if tokens[0][1]==')':
    262                 return left,tokens[1:]
    263             return left,tokens
    264         return None,tokens
    265 
    266     def parse_float_var_op(self,tokens):
    267         if tokens[0][0] == 'number':
    268             n=Number(tokens[0][1])
    269             return n,tokens[1:]
    270         if tokens[0][0] == 'id':
    271             var=self.env.get_variable(tokens[0][1]) 
    272             if not var:
    273                 var_name=tokens[0][1]
    274                 var=Var(var_name,'')
    275                 self.env.set_variable(var_name,var)
    276                 var=self.env.get_variable(tokens[0][1]) 
    277             return var,tokens[1:]
    278         return None,tokens
    279 
    280 
    281 import StringIO
    282 from env import *
    283 def test():
    284     s=""" a=1+(b+c)^2/23.1e-10 "变量a"
    285     c=2  "变量c" "c是个好变量"
    286     b=c+2 "变量b" "b也是个好变量" "这是为它写的第三条注释"
    287     a>c  "检查a是否大于c"
    288     a>=c  "检查a是否大于等于c"
    289     run
    290     dump
    291     c=3   "change c again."
    292     "注释也可以在前面" c^2>=sin(pow(a,b)+b)
    293     run
    294     dump
    295     """
    296     #for l in s.split('\n'):
    297     #    scanner(l)
    298     print "+"*80
    299     print '*'*70
    300     print s
    301     print '*'*70
    302     e=Env()
    303     i=StringIO.StringIO(s)
    304     e.exec_stream(i)
    305     print "+"*80
    306 
    307 if __name__=="__main__":
    308     test()

    最后是个main.py

     1 #!/usr/bin/env python
     2 # -*- coding: utf-8 -*-
     3 import sys
     4 from env import Env
     5 import StringIO
     6 
     7 e=Env()
     8 e.exec_stream(sys.stdin)
     9 
    10 s=""" x=-1"""
    11 i=StringIO.StringIO(s)
    12 e.exec_stream(i)



  • 相关阅读:
    [git]git的简单配置使用 (将你的代码上传到Github)
    学习进度报告【第六周】
    [错误解决]SpringMVC接收对象 中文乱码问题解决
    [架构]myeclipse配置SpringMVC 以及简单应用 教程
    [机器学习]AttributeError: module 'tensorflow' has no attribute 'ConfigProto' 报错解决方法
    [机器学习]RuntimeError: The Session graph is empty. Add operations to the graph before calling run(). 报错解决方法
    [python]机器学习 k-mean 聚类分析
    学习进度报告【第五周】
    学习进度报告【第四周】
    unity3d优化总结篇
  • 原文地址:https://www.cnblogs.com/windydays/p/2439829.html
Copyright © 2020-2023  润新知