From: Tim Vieira Date: Mon, 1 Jul 2013 21:12:53 +0000 (-0400) Subject: added `mean=` aggregator. X-Git-Url: https://hydra-www.ietfng.org/gitweb/?a=commitdiff_plain;h=212942f7e8732abc952b34df1a13384bf5b0153d;p=dyna2 added `mean=` aggregator. `set=` and `bag=` aggregators return dyna lists. So we can use the results in other rules. make sure we show changes when we `retract_rule` when we show changes we print a header > a += 1. Changes ======= a = 1. Also, pycall translates python and dyna lists. Fixed formatting of lists, strings are double quoted. Fixed `query [A|_] is [1,2,3]` -- we weren't substituting `A`. --- diff --git a/src/Dyna/Backend/Python/Backend.hs b/src/Dyna/Backend/Python/Backend.hs index 8645c6b..d5e2f9b 100644 --- a/src/Dyna/Backend/Python/Backend.hs +++ b/src/Dyna/Backend/Python/Backend.hs @@ -54,7 +54,8 @@ aggrs = S.fromList , "and=" , "or=" , "&=" , "|=" , ":-" , "=" - , "majority=" , "set=" , "bag=" + , "majority=" , "mean=" + , "set=" , "bag=" , ":=" , "dict=" ] diff --git a/src/Dyna/Backend/Python/defn.py b/src/Dyna/Backend/Python/defn.py index a67c4d2..6a6bd89 100644 --- a/src/Dyna/Backend/Python/defn.py +++ b/src/Dyna/Backend/Python/defn.py @@ -120,8 +120,17 @@ class DictEquals(BAggregator): class majority_equals(BAggregator): def fold(self): - [(k,_)] = self.most_common(1) - return k + [(k,c)] = self.most_common(1) + if c > 0: + return k + +class mean_equals(BAggregator): + def fold(self): + # TODO: support negative multiplicity or throw an error + s = [k*m for k, m in self.iteritems() if m > 0] + if len(s): + n = sum(m for _, m in self.iteritems() if m > 0) + return reduce(operator.add, s) / n class max_equals(BAggregator): def fold(self): @@ -173,13 +182,15 @@ class b_or_equals(BAggregator): class set_equals(BAggregator): def fold(self): + from stdlib import todynalist s = {x for x, m in self.iteritems() if m > 0} if len(s): - return s + return todynalist(s) class bag_equals(BAggregator): def fold(self): - return Counter(self) + from stdlib import todynalist + return todynalist(Counter(self).elements()) # map names to functions @@ -196,6 +207,7 @@ defs = { 'majority=': majority_equals, 'set=': set_equals, 'bag=': bag_equals, + 'mean=': mean_equals, } def aggregator(name): diff --git a/src/Dyna/Backend/Python/repl.py b/src/Dyna/Backend/Python/repl.py index fccbf9e..d88525f 100644 --- a/src/Dyna/Backend/Python/repl.py +++ b/src/Dyna/Backend/Python/repl.py @@ -87,9 +87,12 @@ class REPL(cmd.Cmd, object): except ValueError: print 'Please specify an integer. Type `help retract_rule` to read more.' else: - if self.interp.retract_rule(idx) is None: + changes = self.interp.retract_rule(idx) + if changes is None: print 'List available by typing `rules`' print + else: + self._changed(changes) def do_exit(self, _): """ @@ -201,9 +204,7 @@ class REPL(cmd.Cmd, object): print 'No results.' return for val, bindings in results: - #if not bindings: - # print ' ', _repr(val) - print ' ', _repr(val), 'where', drepr(dict(bindings)) + print _repr(val), 'where', drepr(dict(bindings)) print def do_query(self, q): @@ -269,9 +270,13 @@ class REPL(cmd.Cmd, object): def _changed(self, changed): if not changed: return - print '=============' + + print + print 'Changes' + print '=======' for x, v in sorted(changed.items()): print '%s = %s.' % (x, _repr(v)) + print # def _changed_subscriptions(self, changed): # diff --git a/src/Dyna/Backend/Python/stdlib.py b/src/Dyna/Backend/Python/stdlib.py index a96ffd5..7b1d8da 100644 --- a/src/Dyna/Backend/Python/stdlib.py +++ b/src/Dyna/Backend/Python/stdlib.py @@ -11,17 +11,29 @@ except ImportError: # XXX: should probably issue a warning return _random() * (b - a) + a def split(s, delim='\s+'): - return _todynalist(re.split(delim, s)) + return todynalist(re.split(delim, s)) def pycall(name, *args): """ Temporary foreign function interface - call Python functions from dyna! """ + args = tuple(topython(x) for x in args) x = eval(name)(*args) - if isinstance(x, list): - return _todynalist(x) + return todyna(x) + +def todyna(x): + if isinstance(x, (list, tuple)): + return todynalist(x) + return x + +def topython(x): + if isinstance(x, (Cons, Nil)): + return x.aslist return x +def todynalist(x): + return _todynalist(list(x)) + def _todynalist(x): if not x: return Nil diff --git a/src/Dyna/Backend/Python/term.py b/src/Dyna/Backend/Python/term.py index c0bf6da..9d093dc 100644 --- a/src/Dyna/Backend/Python/term.py +++ b/src/Dyna/Backend/Python/term.py @@ -47,43 +47,39 @@ class Term(object): __add__ = __sub__ = __mul__ = notimplemented -from term import Term class Cons(Term): + def __init__(self, head, tail): self.head = head self.tail = tail assert isinstance(tail, (Cons, _Nil)), tail Term.__init__(self, 'cons/2', (head, tail)) self.aggregator = Aggregator() - def tolist(self): - return [self.head] + self.tail.tolist() - def __repr__(self): - return repr(self.tolist()) - - def __iter__(self): -# return iter([(x,(x,),x) for x in self.tolist()]) - for a in self.tolist(): + self.aslist = [self.head] + self.tail.aslist - if not isinstance(a, Term): - yield a, (None,), a + def __repr__(self): + return '[%s]' % (', '.join(map(_repr, self.aslist))) - else: - yield a, (None,), a +# def __iter__(self): +# for a in self.aslist: +# if not isinstance(a, Term): +# yield a, (None,), a +# else: +# yield a, (None,), a def __eq__(self, other): try: - return self.tolist() == other.tolist() + return self.aslist == other.aslist except AttributeError: return False + class _Nil(Term): def __init__(self): Term.__init__(self, 'nil/0', ()) self.aggregator = Aggregator() - - def tolist(self): - return [] + self.aslist = [] def __repr__(self): return '[]' diff --git a/src/Dyna/Backend/Python/utils.py b/src/Dyna/Backend/Python/utils.py index ccf50bd..d66b007 100644 --- a/src/Dyna/Backend/Python/utils.py +++ b/src/Dyna/Backend/Python/utils.py @@ -81,12 +81,12 @@ def dynac(f, out, anf=None, compiler_args=()): def lexer(term): return re.findall('"[^"]*"' # string '|[a-z][a-zA-Z_0-9]*' # functor - '|[A-Z][a-zA-Z0-9_]*' # variable - '|[(), ]+' # parens and comma + '|[A-Z_][a-zA-Z0-9_]*' # variable + '|[(), \[\]|]+' # parens and comma '|[^(), ]+', term) # everything else -def subst(term, v, show_vars=False): +def subst(term, v): """ >>> subst('f("asdf",*g(1,X, Y), X+1)', {'X': 1234}) 'f("asdf",*g(1,1234, Y), 1234+1)' @@ -99,10 +99,6 @@ def subst(term, v, show_vars=False): """ assert isinstance(v, dict) - - if show_vars: - return ''.join((x + (red % ('=' + _repr(v[x]))) if x in v else x) for x in lexer(term)) - return ''.join((_repr(v[x]) if x in v else x) for x in lexer(term)) diff --git a/test/repl/aggregator-conflict.expect b/test/repl/aggregator-conflict.expect index cebfc6e..1bc2e60 100644 --- a/test/repl/aggregator-conflict.expect +++ b/test/repl/aggregator-conflict.expect @@ -1,5 +1,8 @@ -> > ============= +> > +Changes +======= a = 1. + > DynaCompilerError: Encountered error in input program: Conflicting aggregators; rule diff --git a/test/repl/late-aggregator-assignment.expect b/test/repl/late-aggregator-assignment.expect index 7f69603..6321d06 100644 --- a/test/repl/late-aggregator-assignment.expect +++ b/test/repl/late-aggregator-assignment.expect @@ -3,17 +3,23 @@ Rules ===== 0: a += b * c. -> ============= +> +Changes +======= b = 2. + > Rules ===== 0: a += b * c. 1: b := 2. -> ============= +> +Changes +======= a = 6. c = 3. + > Rules ===== diff --git a/test/repl/retract-rule.expect b/test/repl/retract-rule.expect index abb16d1..2e477ba 100644 --- a/test/repl/retract-rule.expect +++ b/test/repl/retract-rule.expect @@ -1,9 +1,18 @@ -> > ============= +> > +Changes +======= a = 1. -> ============= + +> +Changes +======= b = 1. -> ============= + +> +Changes +======= a = 2. + > Rules ===== @@ -17,7 +26,17 @@ Solution a = 2. b = 1. -> > > +> +Changes +======= +a = 1. + +> +Changes +======= +b = null. + +> Solution ======== a = 1.