勇闖新世界︰ 《 pyDatalog 》【專題】之約束編程‧二

雖說《 Datalog tutorial 》用兩段文字

‧ Variables and expressions

‧ Loops

來說明『變元』與『表達式』的用法。然而並不容易掌握什麼是『合法的』 valid 『表達式』?尤其是以 pyDatalog 語法中的數值『比較大小』運算有『不相等』 != 的比較,卻沒有『相等』比較這一件事情為甚。若是一定需要,只能夠用『>=&<=』來代替。考之以《 pyDatalog.pyEngine 》原始碼,得到如下說明︰

# PRIMITIVES

“””

A primitive predicate, also called a built-in predicate, is implemented by code. Assertions about a primitive predicate are ignored, as the code takes precedence. Use the make_pred function to access a primitive by name.

The behavior of a primitive predicate is defined by adding a function
to the predicate’s prim field. The function takes a literal and a subgoal. The typical primitive derives a set of facts from the literal, and for each derived fact f, reports the result by invoking fact(subgoal, f).

The equals primitive defined below is protected from garage collection because the primitive is bound to a local variable. A primitive not stored in a Lua data structure can be protected by entering it into the predicate database used by assert and retract. For primitives created from C, protection may be provided by entering the predicate into the Lua registry.

Use the add_iter_prim function to add a primitive predicate that can defined by an iterator which when given a literal, generates a sequence of answers, each answer being an array of strings or numbers.

“””

# Other parts of the Datalog system depend on the equality primitive,
# so carefully consider any modifications to it.

,看來除非通盤閱讀原始碼作修改,目前只好接受現狀的了!

再者『變元』與『述詞』之『值域』必須是『有限的』,因此用『述詞』表達時,必須注意要確認『變元』之『論域』是有限的,否則很容易發生越『界』 bound 的問題︰

 

#
>>> pyDatalog.create_terms('V變元, 論域')

>>> 論域(V變元) <= (V變元 >= 0) 
論域(V變元) <= >=(V變元,'0')
>>> 論域(3)
[()]
>>> 論域(-1)
[]

#出界錯誤
>>> print(論域(V變元))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.2/dist-packages/pyDatalog/pyParser.py", line 134, in __str__
    return util.cast_to_str(self.__unicode__())
  File "/usr/local/lib/python3.2/dist-packages/pyDatalog/pyParser.py", line 604, in __unicode__
    return LazyListOfList.__unicode__(self)
  File "/usr/local/lib/python3.2/dist-packages/pyDatalog/pyParser.py", line 118, in __unicode__
    if self.data in (True, [], [()]): return util.unicode_type(self._data)
  File "/usr/local/lib/python3.2/dist-packages/pyDatalog/pyParser.py", line 93, in data
    self.todo.ask()
  File "/usr/local/lib/python3.2/dist-packages/pyDatalog/pyParser.py", line 577, in ask
    self._data = Body(self.pre_calculations, self).ask()
  File "/usr/local/lib/python3.2/dist-packages/pyDatalog/pyParser.py", line 693, in ask
    self._data = literal.lua.ask()
  File "pyDatalog\pyEngine.py", line 511, in pyDatalog.pyEngine.Literal.ask (pyDatalog/pyEngine.c:18499)
  File "pyDatalog\pyEngine.py", line 734, in pyDatalog.pyEngine.Subgoal.search (pyDatalog/pyEngine.c:25418)
  File "pyDatalog\pyEngine.py", line 1123, in pyDatalog.pyEngine.compare_primitive (pyDatalog/pyEngine.c:35911)
pyDatalog.util.DatalogError: Error: left hand side of comparison must be bound: >=/2
in line None of None

# 注意!pyDatalog 只有清除所有述詞的指令。如果不清除『述詞』,將會以『邏輯或』 or 的方式作『積累』。
>>> pyDatalog.clear()

>>> 論域(V變元) <= (V變元 >= 0) & (V變元 <= 5) 
論域(V變元) <= >=(V變元,'0')&<=(V變元,'5')
>>> 論域(3)
[()]
>>> 論域(6)
[]

# V變元未必是整數,因此並非『有限的』。
>>> print(論域(V變元))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.2/dist-packages/pyDatalog/pyParser.py", line 134, in __str__
    return util.cast_to_str(self.__unicode__())
  File "/usr/local/lib/python3.2/dist-packages/pyDatalog/pyParser.py", line 604, in __unicode__
    return LazyListOfList.__unicode__(self)
  File "/usr/local/lib/python3.2/dist-packages/pyDatalog/pyParser.py", line 118, in __unicode__
    if self.data in (True, [], [()]): return util.unicode_type(self._data)
  File "/usr/local/lib/python3.2/dist-packages/pyDatalog/pyParser.py", line 93, in data
    self.todo.ask()
  File "/usr/local/lib/python3.2/dist-packages/pyDatalog/pyParser.py", line 577, in ask
    self._data = Body(self.pre_calculations, self).ask()
  File "/usr/local/lib/python3.2/dist-packages/pyDatalog/pyParser.py", line 693, in ask
    self._data = literal.lua.ask()
  File "pyDatalog\pyEngine.py", line 511, in pyDatalog.pyEngine.Literal.ask (pyDatalog/pyEngine.c:18499)
  File "pyDatalog\pyEngine.py", line 734, in pyDatalog.pyEngine.Subgoal.search (pyDatalog/pyEngine.c:25418)
  File "pyDatalog\pyEngine.py", line 1123, in pyDatalog.pyEngine.compare_primitive (pyDatalog/pyEngine.c:35911)
pyDatalog.util.DatalogError: Error: left hand side of comparison must be bound: >=/2
in line None of None
>>> 

 

其次『變元』的一時『賦值』只是它的『值域』中的『某一值』,變元數值間之『相依性』,務須理解清楚 pyDatalog 的『語義』︰

 

>>> pyDatalog.create_terms('X變元, 論域,Y變元 ')
>>> 論域(X變元, Y變元) <= X變元.in_(range(1,10)) & Y變元.in_(range(X變元.v()))
論域(X變元,Y變元) <= _pyD_in(X變元,'['1', '2', '3', '4', '

# 僅表示 5 在 range(1,10) 之中。『Y變元.in_(range(X變元.v()))』裡 
# X變元 的值未必是 5。
>>> 論域(5, Y變元)
[(3,), (0,), (2,), (1,)]

# 注意快取記憶效應
>>> 論域(8, Y變元)
[(3,), (0,), (2,), (1,)]

# 注意次序性
>>> 論域(20, Y變元)
[]

# 對照例子
>>> pyDatalog.create_terms('論域乙')

>>> 論域乙(X變元, Y變元) <= X變元.in_(range(1,10)) & Y變元.in_(range(10)) & (Y變元 <= X變元)
論域乙(X變元,Y變元) <= _pyD_in(X變元,'['1', '2', '3', '4', 

>>> 論域乙(5, Y變元)
[(5,), (0,), (1,), (2,), (3,), (4,)]

>>> 論域乙(8, Y變元)
[(3,), (2,), (1,), (0,), (8,), (7,), (6,), (5,), (4,)]

>>> 論域乙(20, Y變元)
[]
>>> 

 

如此再借著『語法圖』

pyDatalog表達式

pyDatalog比較運算

或許一番練習之後,足以解釋與說明下面程式的想法和執行結果︰

 

※注意表達式括號 ( ) 使用的必要性。

pi@raspberrypi ~ $ python3
Python 3.2.3 (default, Mar  1 2013, 11:53:50) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from pyDatalog import pyDatalog

>>> pyDatalog.create_terms('X被除數, Y除數, Q商, R餘數, 被除數空間, 除數空間')

>>> 被除數空間(X被除數) <= (X被除數.in_(range(100)))
被除數空間(X被除數) <= _pyD_in(X被除數,'['0', '1', '2', '3', 

>>> 被除數空間(X被除數) .data
[(47,), (97,), (28,), (94,), (9,), (75,), (6,), (56,), (53,), (34,), (31,), (81,), (12,), (78,), (59,), (40,), (37,), (18,), (84,), (15,), (65,), (62,), (43,), (24,), (90,), (21,), (87,), (2,), (68,), (49,), (46,), (96,), (27,), (93,), (8,), (74,), (5,), (71,), (52,), (33,), (99,), (30,), (80,), (11,), (77,), (58,), (55,), (36,), (17,), (83,), (14,), (64,), (61,), (42,), (39,), (89,), (20,), (86,), (1,), (67,), (48,), (45,), (26,), (92,), (23,), (73,), (4,), (70,), (51,), (32,), (98,), (29,), (95,), (10,), (76,), (7,), (57,), (54,), (35,), (16,), (82,), (13,), (79,), (60,), (41,), (38,), (88,), (19,), (85,), (0,), (66,), (63,), (44,), (25,), (91,), (22,), (72,), (3,), (69,), (50,)]

>>> 除數空間(X被除數, Y除數) <= 被除數空間(X被除數) & (Y除數.in_(range(1, 100))) & (Y除數 <= X被除數)
除數空間(X被除數,Y除數) <= 被除數空間(X被除數)&_pyD_in(Y除數,'['1', '

>>> 除數空間(20, Y除數)
[(13,), (3,), (12,), (2,), (15,), (5,), (14,), (4,), (17,), (7,), (16,), (6,), (19,), (9,), (18,), (8,), (11,), (20,), (1,), (10,)]

>>> len(除數空間(20, Y除數).data)
20

>>> pyDatalog.create_terms('除法等式')

>>> def 餘數(a, b):
...     return a % b
... 

>>> pyDatalog.create_terms('餘數')

>>> 除法等式(X被除數,Y除數, Q商, R餘數) <= 除數空間(X被除數, Y除數) & (Q商 == (X被除數 // Y除數)) & (R餘數 == 餘數(X被除數,Y除數))
除法等式(X被除數,Y除數,Q商,R餘數) <= 除數空間(X被除數,Y除數)&==(Q商,(X被除

>>> 除法等式(20,Y除數, Q商, R餘數)
[(18, 1, 2), (14, 1, 6), (7, 2, 6), (8, 2, 4), (16, 1, 4), (15, 1, 5), (2, 10, 0), (9, 2, 2), (1, 20, 0), (12, 1, 8), (19, 1, 1), (4, 5, 0), (13, 1, 7), (17, 1, 3), (5, 4, 0), (3, 6, 2), (6, 3, 2), (20, 1, 0), (11, 1, 9), (10, 2, 0)]

>>> 除法等式(20,Y除數, Q商, 0)
[(4, 5), (1, 20), (5, 4), (2, 10), (10, 2), (20, 1)]
>>> 
>>> pyDatalog.create_terms('因數, F因子')

>>> 因數(X被除數, F因子) <= 除法等式(X被除數, F因子, Q商, 0)
因數(X被除數,F因子) <= 除法等式(X被除數,F因子,Q商,'0')

>>> 因數(20, F因子)
[(10,), (5,), (4,), (1,), (20,), (2,)]

>>> 因數(X被除數, F因子) <= 除法等式(X被除數, Y除數, F因子, 0)
因數(X被除數,F因子) <= 除法等式(X被除數,Y除數,F因子,'0')

>>> 因數(20, F因子)
[(20,), (1,), (4,), (5,), (10,), (2,)]
>>> 

>>> pyDatalog.create_terms('因式分解除法')

>>> import math
>>> pyDatalog.create_terms('math')

>>> 因式分解除法(X被除數,Y除數, Q商, R餘數) <= 除數空間(X被除數, Y除數) & (Y除數 <= (math.sqrt(X被除數) + 1)) & (Q商 == (X被除數 // Y除數)) & (R餘數 == 餘數(X 被除數,Y除數))
因式分解除法(X被除數,Y除數,Q商,R餘數) <= 除數空間(X被除數,Y除數)&<=(Y除數,(

>>> 因式分解除法(20,Y除數, Q商, R餘數)
[(1, 20, 0), (4, 5, 0), (3, 6, 2), (5, 4, 0), (2, 10, 0)]

>>> 因式分解除法(20,Y除數, Q商, 0)
[(5, 4), (4, 5), (1, 20), (2, 10)]

>>> pyDatalog.create_terms('因數一')

>>> 因數一(X被除數, F因子) <= 因式分解除法(X被除數, F因子, Q商, 0)
因數一(X被除數,F因子) <= 因式分解除法(X被除數,F因子,Q商,'0')

>>> 因數一(20, F因子)
[(4,), (1,), (2,), (5,)]

>>> 因數一(X被除數, F因子) <= 因式分解除法(X被除數, Y除數, F因子, 0)
因數一(X被除數,F因子) <= 因式分解除法(X被除數,Y除數,F因子,'0')

>>> 因數一(20, F因子)
[(4,), (5,), (10,), (2,), (20,), (1,)]
>>>