百姓网那道题

本文通过使用PLY模块解析带有AND、OR逻辑操作的查询表达式,并实现语法树的子集判断,解决复杂查询间的包含关系问题。

赖勇浩(http://laiyonghao.com

昨天 @smallfish 传了个网址(http://home.wangjianshuo.com/cn/20100505_ccceeieeaece.htm )给我,打开一看,是王建硕先生的博客,正好我前段时间经由阮一峰的博客看到过他的简介,就好奇他又写了啥。仔细一看,原来是出了一道公开的笔试题,蛮有意思的,就决心做做看。

一开始看到要支持 AND OR 等逻辑操作符,想来也要支持括号标明优先级了,就想到拿前几天 python-cn 社区介绍的 PLY 模块做个 parser。虽然是第一次用这个模块,但真的很好用,写 parser 用的时间很短,调试了几下,就能够支持比较复杂的表达式了,比如:

(age < 15 and sex == 0) or age > 30

整出来的语法树如下:
or
and
('<', 'age', 15)
('==', 'sex', 0)
('>', 'age', 30)

接下来在比较两棵树是否有子集关系的时候,遇到了问题。今天早上看到 qmigh(http://qmigh.blogspot.com/ )的想法,启发了我。不过他提出的方案是把查询表达式“规范化”,这一步是比较难做的,需要用到离散数学的一些知识,实现起来比较烦。我思考之后,发现“规范化”只是为了将语法树转成两层的或与树,想到这一点之后,就可以不进行“规范化”了,只要将与或树的特性应用到语法树中即可。具体的判断方案我在代码里以注释的形式写了出来。

本题的解决方案后面的知识,SeasonLee 有一篇文章写得清晰,可供参考:从离散数学到编译原理--百姓网编程题后序(http://www.cnblogs.com/SeasonLee/archive/2010/05/11/1731414.html

现在这个方案可以实现比较复杂的表达式判断,不过不支持 NOT,也不支持 != 号,只支持 AND、OR、>、< 和 ==,可以使用 () 指定优先级。其实代码不长,约有 100 行是后面测试用的一些代码。

经王建硕先生同意,我把自己的方案公布到博客上,欢迎大家讨论和指正。

1 # encoding:utf-8
2
3 try :
4 from ply import yacc
5 from ply import lex
6 except ImportError:
7 import sys
8 print "Please install PLY first. "
9 print r"You can download it from here: http://www.dabeaz.com/ply/ply-3.3.tar.gz "
10 sys.exit(1)
11
12 #############################################
13 #lex
14 #############################################
15
16 # 定义条件表达式的词法
17 tokens = ['FIELD ', 'NUMBER ', 'GT ', 'EQ ', 'LT ', 'AND ', 'OR ', 'LPAREN ', 'RPAREN ']
18
19 t_ignore = ' /t '
20 t_FIELD = r'(?!and)(?!or)[a-zA-Z_][a-zA-Z0-9_]* '
21 t_GT = r'> '
22 t_EQ = r'== '
23 t_LT = r'< '
24 t_AND = r'and '
25 t_OR = r'or '
26 t_LPAREN = r'/( '
27 t_RPAREN = r'/) '
28
29 def t_NUMBER (t):
30 r'/d+ '
31 t.value = int(t.value)
32 return t
33
34 def t_error (t):
35 raise TypeError("Unknow text '%s' "%t.value)
36
37 lex.lex()
38
39 #############################################
40 #yacc
41 #############################################
42
43 # 用 BNF 定义条件表达式的语法,支持 AND OR,以及 () 的嵌套
44 def p_expr4 (p):
45 '''
46 expr :
47 '''
48
49 def p_expr3 (p):
50 '''
51 expr : LPAREN expr RPAREN
52 '''
53 p[0] = p[2]
54
55 def p_expr2 (p):
56 '''
57 expr : condi
58 '''
59 p[0] = p[1]
60
61 def p_expr (p):
62 '''
63 expr : expr logic_op expr
64 '''
65 p[0] = (p[2], p[1], p[3])
66
67 def p_logic_op (p):
68 '''
69 logic_op : AND
70 logic_op : OR
71 '''
72 p[0] = p[1]
73
74 def p_condi (p):
75 '''condi : FIELD cmp_op NUMBER '''
76 p[0] = (p[2], p[1], p[3])
77
78 # for i in p:print i
79
80 def p_cmp_op (p):
81 '''
82 cmp_op : GT
83 cmp_op : EQ
84 cmp_op : LT
85 '''
86 p[0] = p[1]
87 #for i in p:print i
88
89 def p_error (p):
90 raise TypeError("unknow text at %s "%p.value)
91
92 yacc.yacc()
93
94 #############################################
95 #util
96 #############################################
97
98 # 以比较漂亮的形式打印语法树
99 INDENT = ' '
100 def print_tree (t, indent):
101 if not t:
102 print indent + str(t)
103 return
104 op, l, r = t
105 if op in ('and ', 'or '):
106 print indent + op
107 print_tree(l, indent + INDENT)
108 print_tree(r, indent + INDENT)
109 else :
110 print indent + str(t)
111
112 #############################################
113 #judge
114 #############################################
115 OP = ('> ', '< ', '== ')
116 AND = 'and '
117 OR = 'or '
118
119 # 对 age < 18 和 age < 10 这种原子条件表达式对行判断
120 # 首先要求字段名要相同,否则返回 false
121 # 然后是根据表达式的比较运算符进行分情况比较
122 def is_subset_atom_atom (left, right):
123 assert left and right
124 lop, ll, lr = left
125 rop, rl, rr = right
126 assert lop in OP and rop in OP
127 if ll != rl:
128 return False
129 if lop == rop == '> ':
130 return lr >= rr
131 elif lop == rop == '< ':
132 return lr <= rr
133 elif lop == rop == '== ':
134 return lr == rr
135 elif lop == '> ':
136 return False
137 elif lop == '< ':
138 return False
139 elif lop == '== ':
140 if rop == '> ':
141 return lr > rr
142 else : # '<'
143 return lr < rr
144 else :
145 raise RuntimeError('Error ')
146
147 # 比较 age < 10 和 age < 10 and sex == 0 这种形式的复合表达式
148 # 这种情况下要求左式必须是右式的两个子式的子集才返回为 true
149 def is_subset_atom_and (left, right):
150 assert left and right
151 rop, rl, rr = right
152 global is_subset
153 return is_subset(left, rl) and is_subset(left, rr)
154
155 # 比较 age < 10 和 age < 10 or sex == 0 这种形式的复杂表达式
156 # 这种情况下只需要左式是右式中的任一子式的子集即返回 true
157 def is_subset_atom_or (left, right):
158 assert left and right
159 rop, rl, rr = right
160 global is_subset
161 return is_subset(left, rl) or is_subset(left, rr)
162
163 # 比较 age < 10 and sex == 0 和 age < 10 这种复合表达式
164 # 要求左式的任一子式为右式的子集即返回 true
165 def is_subset_and_atom (left, right):
166 assert left and right
167 lop, ll, lr = left
168 global is_subset
169 return is_subset(ll, right) or is_subset(lr, right)
170
171 # 比较 age < 10 and sex == 0 和 age < 18 and sex == 1 这种复合表达式
172 # 要求左式的任一子式必须都是右式的*某一*子式的子集才返回 true
173 def is_subset_and_and (left, right):
174 assert left and right
175 lop, ll, lr = left
176 rop, rl, rr = right
177 global is_subset
178 lresult = is_subset(ll, rl) or is_subset(ll, rr)
179 rresult = is_subset(lr, rl) or is_subset(lr, rr)
180 return lresult and rresult
181
182 # 比较 age < 10 and sex == 0 和 age < 10 or sex == 1 这种复合表达式
183 # 要求整个左式必须是右式的某一子式为真才返回 true
184 def is_subset_and_or (left, right):
185 assert left and right
186 lop, ll, lr = left
187 rop, rl, rr = right
188 global is_subset
189 return is_subset(left, rl) or is_subset(left, rr)
190
191 # 比较 age < 10 or sex == 0 和 age < 10 这种复合表达式
192 # 要求左式的任一子式都必须是右式的子集才返回 true
193 def is_subset_or_atom (left, right):
194 assert left and right
195 lop, ll, lr = left
196 global is_subset
197 return is_subset(ll, right) and is_subset(lr, right)
198
199 # 比较 age < 10 or sex == 0 和 age < 10 and sex == 0 这种复合表达式
200 # 与 is_subset_or_atom 是一样的。
201 def is_subset_or_and (left, right):
202 return is_subset_or_atom(left, right)
203
204 # 比较 age < 10 or sex == 0 和 age < 10 or sex == 0 这种复合表达式
205 # 与 is_subset_or_atom 是一样的。
206 def is_subset_or_or (left, right):
207 return is_subset_or_atom(left, right)
208
209 # 根据左右式的操作符分派给上述的判断函数
210 def is_subset (left, right):
211 assert left and right
212 print 'left: ',left
213 print 'right: ',right
214 lop, ll, lr = left
215 rop, rl, rr = right
216 if lop in OP:
217 if rop in OP:
218 return is_subset_atom_atom(left, right)
219 elif rop == AND:
220 return is_subset_atom_and(left, right)
221 elif rop == OR:
222 return is_subset_atom_or(left, right)
223 raise RuntimeError('Error ')
224 elif lop == 'and ':
225 if rop in OP:
226 return is_subset_and_atom(left, right)
227 elif rop == AND:
228 return is_subset_and_and(left, right)
229 elif rop == OR:
230 return is_subset_and_or(left, right)
231 else :
232 raise RuntimeError('Error ')
233 elif lop == 'or ':
234 if rop in OP:
235 return is_subset_or_atom(left, right)
236 elif rop == AND:
237 return is_subset_or_and(left, right)
238 elif rop == OR:
239 return is_subset_or_or(left, right)
240 else :
241 raise RuntimeError('Error ')
242 else :
243 raise RuntimeError('Error ')
244
245 #############################################
246 #query
247 #############################################
248 class Query (object):
249 def __init__ (self, q):
250 self._q = q
251 self._tree = yacc.parse(q)
252 assert self._tree
253
254 def is_subset (self, other):
255 if not self._tree:
256 return False
257 if not other._tree:
258 return True
259 return is_subset(self._tree, other._tree)
260
261 #############################################
262 #test
263 #############################################
264 if __name__ == '__main__ ':
265 t0 = Query('age > 40 ') # 中年人
266 t1 = Query('age > 18 ') # 成年人
267 print t0.is_subset(t0)
268 print t0.is_subset(t1)
269 print t1.is_subset(t0)
270 print t1.is_subset(t1)
271 print '- '*30
272
273 t2 = Query('age > 18 and weight < 100 ') # 成年瘦子
274 t3 = Query('age > 18 or weight < 100 ') # 成年人,或体重小于 100
275 print t0.is_subset(t2)
276 print t0.is_subset(t3)
277
278 print t2.is_subset(t0)
279 print t2.is_subset(t3)
280
281 print t3.is_subset(t2)
282
283 r0 = Query('age > 30 and sex == 0 ')
284 r1 = Query('age > 40 and sex == 0 ')
285
286 print r0.is_subset(r1)
287 print r1.is_subset(r0)
288 print '= '*30
289
290 t0 = Query('(age < 15 and sex == 0) or age > 30 ')
291 t1 = Query('age < 7 ')
292 t2 = Query('age < 18 ')
293 print_tree(t0._tree, '')
294 print '* '*30
295 assert 't0 is subset of t0: 'and t0.is_subset(t0) == True
296 print '- '*30
297 assert 't0 is subset of t1: 'and t0.is_subset(t1) == False
298 print '- '*30
299 assert 't1 is subset of t0: 'and t1.is_subset(t0) == False
300 print '- '*30
301 assert 't2 is subset of t0: 'and t2.is_subset(t0) == False
302 print '- '*30
303
304 q0 = Query('age < 15 ')
305 q1 = Query('age > 30 ')
306 q2 = Query('age > 18 ')
307 q3 = Query('age > 40 ')
308 q4 = Query('age > 30 and sex == 0 ')
309
310
311 assert 'q0 is subset of q0: 'and q0.is_subset(q0) == True
312 print '- '*30
313 assert 'q0 is subset of q1: 'and q0.is_subset(q1) == False
314 print '- '*30
315 assert 'q0 is subset of q2: 'and q0.is_subset(q2) == False
316 print '- '*30
317 assert 'q0 is subset of q3: 'and q0.is_subset(q3) == False
318 print '- '*30
319 assert 'q0 is subset of q4: 'and q0.is_subset(q4) == False
320 print '- '*30
321 print
322
323 assert 'q1 is subset of q0: 'and q1.is_subset(q0) == False
324 print '- '*30
325 assert 'q1 is subset of q1: 'and q1.is_subset(q1) == True
326 print '- '*30
327 assert 'q1 is subset of q2: 'and q1.is_subset(q2) == True
328 print '- '*30
329 assert 'q1 is subset of q3: 'and q1.is_subset(q3) == False
330 print '- '*30
331 assert 'q1 is subset of q4: 'and q1.is_subset(q4) == False
332 print '- '*30
333 print
334
335 assert 'q2 is subset of q0: 'and q2.is_subset(q0) == False
336 print '- '*30
337 assert 'q2 is subset of q1: 'and q2.is_subset(q1) == False
338 print '- '*30
339 assert 'q2 is subset of q2: 'and q2.is_subset(q2) == True
340 print '- '*30
341 assert 'q2 is subset of q3: 'and q2.is_subset(q3) == False
342 print '- '*30
343 assert 'q2 is subset of q4: 'and q2.is_subset(q4) == False
344 print '- '*30
345 print
346
347 assert 'q3 is subset of q0: 'and q3.is_subset(q0) == False
348 print '- '*30
349 assert 'q3 is subset of q1: 'and q3.is_subset(q1) == True
350 print '- '*30
351 assert 'q3 is subset of q2: 'and q3.is_subset(q2) == True
352 print '- '*30
353 assert 'q3 is subset of q3: 'and q3.is_subset(q3) == True
354 print '- '*30
355 assert 'q3 is subset of q4: 'and q3.is_subset(q4) == False
356 print '- '*30
357 print
358
359 assert 'q4 is subset of q0: 'and q4.is_subset(q0) == False
360 print '- '*30
361 assert 'q4 is subset of q1: 'and q4.is_subset(q1) == True
362 print '- '*30
363 assert 'q4 is subset of q2: 'and q4.is_subset(q2) == True
364 print '- '*30
365 assert 'q4 is subset of q3: 'and q4.is_subset(q3) == False
366 print '- '*30
367 assert 'q4 is subset of q4: 'and q4.is_subset(q4) == True
368 print '- '*30
369
370

一、 内容概要 本资源提供了一个完整的“金属板材压弯成型”非线性仿真案例,基于ABAQUS/Explicit或Standard求解器完成。案例精确模拟了模具(凸模、凹模)与金属板材之间的接触、压合过程,直至板材发生塑性弯曲成型。 模型特点:包含完整的模具-工件装配体,定义了刚体约束、通用接触(或面面接触)及摩擦系数。 材料定义:金属板材采用弹塑性材料模型,定义了完整的屈服强度、塑性应变等真实应力-应变数据。 关键结果:提供了成型过程中的板材应力(Mises应力)、塑性应变(PE)、厚度变化​ 云图,以及模具受力(接触力)曲线,完整再现了压弯工艺的力学状态。 二、 适用人群 CAE工程师/工艺工程师:从事钣金冲压、模具设计、金属成型工艺分析与优化的专业人员。 高校师生:学习ABAQUS非线性分析、金属塑性成形理论,或从事相关课研究的硕士/博士生。 结构设计工程师:需要评估钣金件可制造性(DFM)或预测成型回弹的设计人员。 三、 使用场景及目标 学习目标: 掌握在ABAQUS中设置金属塑性成形仿真的全流程,包括材料定义、复杂接触设置、边界条件与载荷步。 学习如何调试和分析大变形、非线性接触问的收敛性技巧。 理解如何通过仿真预测成型缺陷(如减薄、破裂、回弹),并与理论或实验进行对比验证。 应用价值:本案例的建模方法与分析思路可直接应用于汽车覆盖件、电器外壳、结构件等钣金产品的冲压工艺开发与模具设计优化,减少试模成本。 四、 其他说明 资源包内包含参数化的INP文件、CAE模型文件、材料数据参考及一份简要的操作要点说明文档。INP文件便于用户直接修改关键参数(如压边力、摩擦系数、行程)进行自主研究。 建议使用ABAQUS 2022或更高版本打开。显式动力学分析(如用Explicit)对计算资源有一定要求。 本案例为教学与工程参考目的提供,用户可基于此框架进行拓展,应用于V型弯曲
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值