def head(Fn, B): """ implement monadic ↑ """ if B.isScalar() or B.isEmptyVector(): return B if B.isVector(): return makeScalar(Fn(B.vectorToPy()), Fn(B.prototype())) return makeScalar(Fn(B.arrayToPy()), Fn(B.prototype()))
def pick(_, A, B): """ implement dyadic ⊃ """ assertNotArray(A) assertNotArray(B) if A.isEmptyVector(): return B assertNotScalar(B) if B.isVector(): IO = indexOrigin() try: for X in A.vectorToPy(): X = confirmInteger(X) assertTrue(X >= IO, "INDEX ERROR") if isinstance(B, aplQuantity): B = B.vectorToPy()[X - IO] else: assertError("RANK ERROR") except IndexError: assertError("INDEX ERROR") if isinstance(B, aplQuantity): return B return makeScalar((B, ), None)
def index(Fn, A, B): """ implement dyadic ⍳ """ assertNotArray(A) if B.isEmptyVector(): return makeEmptyVector(B.prototype()) if A.isEmptyVector(): Rpy = scalarIterator(indexOrigin(), B.elementCount(), B.expressionToGo) elif B.isArray(): Rpy = Fn(A.vectorToPy(), B.arrayToPy()) else: Rpy = Fn(A.vectorToPy(), B.vectorToPy()) if B.isArray(): return makeArray(Rpy, B.dimension()) if B.isVector(): return makeVector(Rpy, B.dimension()) return makeScalar(Rpy)
def encode(Fn, A, B): """ implement dyadic ⊤ """ if B.isEmptyVector(): # Simplistic return makeEmptyVector() if A.isEmptyVector(): # Simplistic return makeEmptyVector() if B.isArray(): if A.isScalarLike(): Rpy = [] Apy = A.vectorToPy() for Bpy in B.arrayToPy(): Rpy.append(Fn(Apy, Bpy).__next__()) return makeArray(Rpy, B.dimension()) assertError("WIP - RANK ERROR") if B.isVector(): assertTrue(B.tally() == 2, "WIP - LENGTH ERROR") if A.isArray(): assertError("WIP - RANK ERROR") if A.isVector(): assertTrue(A.tally() == 2, "WIP - LENGTH ERROR") Rpy, Apy = [], A.vectorToPy() for Bpy in B.vectorToPy(): Rpy += Fn(Apy, Bpy) Rpy = monadicTranspose(Rpy, (A.tally(), B.tally())) return makeArray(Rpy, (A.tally(), B.tally())) Rpy, Apy = [], A.vectorToPy() for Bpy in B.vectorToPy(): Rpy += Fn(Apy, Bpy) if A.isArray(): Rpy, Bpy = [], B.scalarToPy() for Ait in A.arrayByFirstAxis(): Rpy += Fn(Ait.vectorToPy(), Bpy) return makeArray(Rpy, A.dimension(), B.prototype()) if A.isVector(): Rpy = Fn(A.vectorToPy(), B.scalarToPy()) return makeVector(Rpy, A.tally()) Rpy = Fn(A.vectorToPy(), B.scalarToPy()) return makeScalar(Rpy)
def decode(Fn, A, B): """ implement dyadic ⊥ """ if B.isArray(): if A.isScalarLike(): Rpy = [] for Bit in B.arrayByFirstAxis(): Rpy.append(Fn(A.promoteScalarToVectorPy(), Bit)) return makeVector(Rpy, B.dimension()[0], B.prototype()) if A.isVector(): assertNotTrue(A.isEmptyVector(), "DOMAIN ERROR") assertTrue(A.tally() == B.dimension()[0], "LENGTH ERROR") Rpy = [] for Bit in B.arrayByFirstAxis(): Rpy.append(Fn(A.vectorToPy(), Bit)) return makeVector(Rpy, A.dimension(), B.prototype()) assertTrue(A.rank() == B.rank(), "WIP - RANK ERROR") assertTrue(A.dimension()[-1] == B.dimension()[0], "LENGTH ERROR") Rpy = [] for Ait in A.arrayByLastAxis(): for Bit in B.arrayByFirstAxis(): Rpy.append(Fn(Ait, Bit)) return makeArray(Rpy, A.dimension(), B.prototype()) if B.isVector(): if A.isEmptyVector() or B.isEmptyVector(): return makeScalar(0) return makeScalar(Fn(A.promoteScalarToVectorPy(), B.vectorToPy())) if A.isScalar(): assertNumeric(A) assertNumeric(B) return B return makeScalar(Fn(A.vectorToPy(), B.scalarIterator()))
def enclose(_, B): """ implement monadic ⊂ """ if B.isScalar(): return B return makeScalar((B, ), None)
def evaluate(expression, cio): """ evaluate an APL expression called from parse and routines parse calls so beware indirect recursion """ try: expr = expression.lstrip() if not expr: assertError("SYNTAX ERROR") lhs, expr = parse(expr, cio) if lhs == []: lhs = None elif len(lhs) > 1: lhs = makeVector(tuple(lhs), -1, None) elif isinstance(lhs[0], aplQuantity): lhs = lhs[0] else: lhs = makeScalar(lhs[0], None) if not expr: return lhs if cio.newStmt: if lhs is None and expr[0] == '⊣': cio.hushImplicit = True cio.newStmt = False operator = operatorFunction(expr[1:].lstrip()) if lhs is None else None if operator is None and lhs is None: function = monadicFunction(expr) else: identity, function = dyadicFunction(expr) if not operator is None: expr = expr[1:].lstrip() rhs = evaluate(expr[1:], cio) rhs.expressionToGo = expr if operator is None: result = function(rhs) if lhs is None else function(lhs, rhs) else: result = operator(function, identity, rhs) return result.resolve() if eagerEvaluation() else result except aplException as error: if not error.expr: error.expr = expr raise error
def maths(Fn, A, B): """ the basic recursive map for dyadic mathematical functions """ if B.isArray(): if A.isArray(): # Check ranks are equal Rpy = iterator.maths(maths, Fn, A, B) elif A.isVector(): # Check length is compatible Rpy = iterator.maths(maths, Fn, A.vectorIterator(), B) else: Rpy = iterator.maths(maths, Fn, A.scalarIterator(), B) return makeArray(Rpy, B.dimension(), B.prototype()) if A.isArray(): if B.isVector(): # Check length is compatible Rpy = iterator.maths(maths, Fn, A, B.vectorIterator()) else: Rpy = iterator.maths(maths, Fn, A, B.scalarIterator()) return makeArray(Rpy, A.dimension(), A.prototype()) if B.isVector(): if A.isVector(): if B.isEmptyVector(): assertTrue(A.isEmptyVector(), "LENGTH ERROR") Rpy = iterator.maths(maths, Fn, A, B) elif B.isEmptyVector(): return B else: Rpy = iterator.maths(maths, Fn, A.scalarIterator(), B) return makeVector(Rpy, B.dimension(), B.prototype()) if A.isVector(): if A.isEmptyVector(): return A else: Rpy = iterator.maths(maths, Fn, A, B.scalarIterator()) return makeVector(Rpy, A.dimension(), A.prototype()) return makeScalar(iterator.maths(maths, Fn, A, B))
def reduceFirst(Fn, A, B): """ the map for the ⌿ (reduce along first axis) operator """ if B.isScalar(): return B if B.isVector(): if B.isEmptyVector(): assertNotTrue(A is None, "DOMAIN ERROR") return makeScalar(A) Rpy = iterator.reduceVector(Fn, B) if Rpy.isScalar(): return Rpy return makeScalar((Rpy, ), makePrototype(Rpy)) assertNotArray(B, "WIP - RANK ERROR")
def maths(Fn, B): """ the basic recursive map for monadic mathematical functions """ if B.isArray(): return makeArray(iterator.maths(maths, Fn, B), B.dimension()) if B.isVector(): return makeVector(iterator.maths(maths, Fn, B), B.dimension(), B.prototype()) return makeScalar(iterator.maths(maths, Fn, B))
def partition(Fn, A, B): """ implement dyadic ⊂ """ assertNotArray(A) assertNotTrue(B.isScalar(), "RANK ERROR") if A.isEmptyVector() and B.isEmptyVector(): return B if A.isScalar(): Apy = confirmInteger(A.scalarToPy()) if Apy == 0: return makeEmptyVector() return makeScalar((B, ), B.prototype()) if B.isArray(): assertTrue(B.dimension() == (2, 2), "WIP - MATRIX ERROR") Apy = A.vectorToPy() assertTrue(len(Apy) == B.rank(), "LENGTH ERROR") Rpy = [] for Bit in B.arrayByLastAxis(): Rpy += makeScalar((Fn( A.vectorToPy(), Bit.vectorToPy(), ), B.prototype())) Dpy = list(B.dimension()) Dpy[-1] = 1 return makeArray(Rpy, Dpy, None) return makeVector(Fn(A.vectorToPy(), B.vectorToPy()), -1, B.prototype())
def __next__(self): try: X, Y = _nextPair(self._A, self._B) if isinstance(X, aplQuantity) and isinstance(Y, aplQuantity): return self._map(self._fn, X, Y) if isinstance(X, aplQuantity): return self._map(self._fn, X, makeScalar(Y)) if isinstance(Y, aplQuantity): return self._map(self._fn, makeScalar(X), Y) try: return self._fn(X, Y) except TypeError: assertError("DOMAIN ERROR") except aplException as error: if error.expr is None: error.expr = self._expresso raise error
def reduceVector(Fn, B): """ the iterator for the reduce operator when applied to a vector """ I = reverse(B).__iter__() Y = I.__next__() if not isinstance(Y, aplQuantity): Y = makeScalar(Y) try: while True: X = I.__next__() if not isinstance(X, aplQuantity): X = makeScalar(X) Y = Fn(X, Y).resolve() except StopIteration: pass return Y
def find(Fn, A, B): """ implement dyadic ∊ """ A.resolve() Apy = A.arrayToPy() if A.isArray() else A.vectorToPy() Bpy = B.arrayToPy() if B.isArray() else B.vectorToPy() Rpy = Fn(Apy, Bpy) if B.isArray(): return makeArray(Rpy, B.dimension(), B.prototype()) if B.isVector(): return makeVector(Rpy, B.dimension(), B.prototype()) return makeScalar(Rpy, B.prototype())
def scanLast(Fn, _, B): """ the map for the \ (scan along last axis) operator """ if B.isScalar(): return B if B.isVector(): if B.isEmptyVector(): return B if B.isScalarLike(): return makeScalar(B.scalarToPy()) Rpy = iterator.scanVector(Fn, B) return makeVector(Rpy, B.dimension(), B.prototype()) assertNotArray(B, "WIP - RANK ERROR")
def membership(Fn, A, B): """ implement dyadic ∊ """ if A.isEmptyVector(): return A Apy = A.arrayToPy() if A.isArray() else A.vectorToPy() Bpy = B.arrayToPy() if B.isArray() else B.vectorToPy() Rpy = Fn(Apy, Bpy) if A.isArray(): return makeArray(Rpy, A.dimension(), A.prototype()) if A.isVector(): return makeVector(Rpy, A.dimension(), A.prototype()) return makeScalar(Rpy, A.prototype())
def disclose(Fn, B): """ implement monadic ⊃ """ if B.isArray(): return makeArray(Fn(B.arrayToPy()), B.dimension(), B.prototype()) if B.isVector(): if B.isEmptyVector(): return B return makeVector(Fn(B.vectorToPy()), B.dimension(), B.prototype()) if B.isScalar(): Bpy = B.scalarToPy() if isinstance(Bpy, aplQuantity): return Bpy return makeScalar(Bpy, None)
def depth(_, B): """ implement monadic ≡ """ return makeScalar(B.depth())
def tally(_, B): """ implement monadic ≢ """ return makeScalar(B.tally())
def setEvaluationMode(mode): """ set the eager/lazy evaluation mode (from command line flags) """ return _EE.set(makeScalar(mode))
def setIndexOrigin(value): """ set the index origin (from command line flags) """ return _IO.set(makeScalar(value))
'↑': lambda B: mapper.head(iterator.head, B), '/': lambda B: assertError("SYNTAX ERROR"), '⌿': lambda B: assertError("SYNTAX ERROR"), '\\': lambda B: assertError("SYNTAX ERROR"), '⍀': lambda B: assertError("SYNTAX ERROR"), '⍋': lambda B: mapper.grade(False, B), '⍒': lambda B: mapper.grade(True, B), '⌷': lambda B: assertError("VALENCE ERROR"), # Miscellaneous '⍺': lambda B: assertError("VALENCE ERROR"), '⍕': _toBeImplemented, # monadic format '⍎': _toBeImplemented, # execute '⊤': lambda B: assertError("VALENCE ERROR"), '⊥': lambda B: assertError("VALENCE ERROR"), '⊣': lambda B: makeScalar(0), '⊢': lambda B: B, } # ------------------------------ def monadicFunction(symbol): """ return the monadic function given its APL symbol raises INVALID TOKEN if the symbol is not recognised """ try: return _MonadicFunctions[symbol[0]] except KeyError: assertError("INVALID TOKEN")
def noMatch(Fn, A, B): """ recursive implementation of dyadic ≢ """ return makeScalar(int(not _matchMap(Fn, A, B)))