def _toBeImplemented(_, __): """ placeholder for functions not yet implemented raises FUNCTION NOT YET IMPLEMENTED """ assertError("FUNCTION NOT YET IMPLEMENTED")
def workspaceVariable(name, quantity=None): """ set or get the value of a workspace variable by name lookup if a value is passed in, it is assigned to the variable and the value is returned a new variable is created if appropriate """ if quantity is None: try: WV = _WorkspaceVariables[name] except KeyError: assertError("UNKNOWN VARIABLE") else: quantity = quantity.resolve() try: WV = _WorkspaceVariables[name] WV.set(quantity) except KeyError: WV = _workspaceVariable(quantity) _WorkspaceVariables[name] = WV return WV.get()
def tally(self): """ 1 if a scalar, its length if a vector not resolved so do not use internally """ if isinstance(self._dimension, tuple): if self._dimension[0] < 0: assertError("tally: needs more thought") if not isinstance(self._value, tuple): self._value = tuple(self._value) self._dimension[0] = len(self._value) ## wrong if self._dimension[0] == 0: self._value = self._prototype return self._dimension[0] if isinstance(self._dimension, int): if self._dimension < 0: if not isinstance(self._value, tuple): self._value = tuple(self._value) self._dimension = len(self._value) if self._dimension == 0: self._value = self._prototype return self._dimension return 1
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 log(A, B): """ A ⍟ B """ try: if A == 10: return math.log10(B) # Python 3.3 and later # if A == 2: return math.log2(B) return math.log(B, A) except ValueError: if fuzzyEquals(A, B): return 1.0 if fuzzyEquals(A, 0): return 0.0 if fuzzyEquals(B, 1): return 0.0 except ZeroDivisionError: if fuzzyEquals(A, B): return 1.0 assertError("DOMAIN ERROR")
def makeVector(value, length=-1, prototype=(0,)): """ make an APL vector quantity from a Python list """ if length is None: length = 1 if not isinstance(value, (tuple, lookAhead)): if length < 0 or prototype is None: value = lookAhead(value, 1) if isinstance(value, lookAhead): if length < 0: if value.buffered() == 0: length = 0 if prototype is None: if value.buffered() == 0: assertError("ASSERTION ERROR: makeVector()") prototype = (makePrototype(value.peek()),) elif isinstance(value, list): value = tuple(value) if length == 0: return makeEmptyVector(prototype) if not isinstance(value, (tuple, lookAhead)): value = lookAhead(value, 1) return aplQuantity(value, length, None)
def _nextPair(A, B): """ utility routine to fetch the next pair of values from the iterators """ OK = 0 try: X = A.__next__() OK += 1 except StopIteration: if isinstance(B, aplIterator): raise StopIteration try: Y = B.__next__() OK += 1 except StopIteration: if isinstance(A, aplIterator): raise StopIteration if OK == 2: return X, Y if OK == 0: raise StopIteration assertError("LENGTH ERROR")
def __next__(self): Y = self._B.__next__() if isinstance(Y, aplQuantity): assertError("WIP - LENGTH ERROR") return Y
def _nextPairWithInteger(A, B): """ utility routine to fetch the next pair of values from the iterators The first of the pair must be an integer """ OK = 0 try: X = confirmInteger(A.__next__()) OK += 1 except StopIteration: if isinstance(B, aplIterator): raise StopIteration try: Y = B.__next__() OK += 1 except StopIteration: if isinstance(A, aplIterator): raise StopIteration if OK == 2: return X, Y if OK == 0: raise StopIteration assertError("LENGTH ERROR")
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 roll(B): """ ? B """ try: return random.randint(1, B) except ValueError: assertError("DOMAIN ERROR")
def log(B): """ ⍟ B """ try: return math.log(B) except ValueError: assertError("DOMAIN ERROR")
def arrayByFirstAxis(self): """ return an APL array wrapped in a First Axis iterator """ if self.isArray(): return firstAxisIterator(self._value, self.dimension()[-1]) assertError("ASSERTION ERROR: aplQuantity.arrayByFirstAxis()")
def arrayToPy(self): """ return the Python sequence (or a promise thereof) that represents an array """ if self.isArray(): return self._value assertError("ASSERTION ERROR: aplQuantity.arrayToPy()")
def scalarToPy(self): """ return Python scalar (a single number or character) """ if self.isArray(): assertError("ASSERTION ERROR: aplQuantity.scalarToPy()") return self._value.__iter__().__next__()
def reciprocal(B): """ ÷ B """ try: return operator.truediv(1.0, B) except ZeroDivisionError: assertError("DOMAIN ERROR")
def exp(A, B): """ A * B """ try: return math.pow(A, B) except ValueError: assertError("DOMAIN ERROR")
def dimension(self): """ the dimension(s) of the quantity """ if isinstance(self._dimension, tuple): if self._dimension[-1] < 0: assertError("DIMENSION ERROR: aplQuantity.dimension()") return self._dimension
def __init__(self, B, L): self._B = tuple(B) # the data self._L = L # the last axis row length self._O = 0 # current last axis offset aka first axis row self._R = None # inferred first axis row count if L <= 0: assertError("lastAxisIterator: sheep dip")
def __init__(self, B, D): self._B = B # the data self._RC = D[0] # row count (first axis vector length) self._CC = D[-1] # column count (last axis vector length) self._RO = 0 # row offset (current row) self._CO = 0 # column offset (current column) if self._CC <= 0: assertError("monadicTranspose: sheep dip")
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 systemCommand(name, arguments, cio): """ invoke a system command the rest of the command line may be ignored """ try: _SystemCommands[name.upper()](arguments.lstrip(), cio) except KeyError: assertError("UNKNOWN SYSTEM COMMAND")
def confirmReal(B): """ the real-domain value of B """ try: return float(B) except TypeError: pass assertError("DOMAIN ERROR")
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 vectorToPy(self): """ return Python sequence (or a promise thereof) """ if self.isArray(): assertError("ASSERTION ERROR: aplQuantity.vectorToPy()") if self.isEmptyVector(): return () return self._value
def promoteScalarToVectorPy(self): """ return a scalar as a scalar iterator but a vector as a sequence (promise) """ if self.isScalarLike(): return self.scalarIterator() if self.isVector(): return self.vectorToPy() assertError("ASSERTION ERROR: aplQuantity.promoteScalarToVectorPy()")
def python(self): """ return the Python value (which could be a promise) """ if self.isScalar(): return self.scalarToPy() if self.isVector(): return self.vectorToPy() assertError("ASSERTION ERROR: aplQuantity.python()")
def divide(A, B): """ A ÷ B """ try: return operator.truediv(A, B) except ZeroDivisionError: if fuzzyEquals(A, 0) and fuzzyEquals(B, 0): return 1 assertError("DOMAIN ERROR")
def deal(A, B): """ A ? B """ A = confirmInteger(A) B = confirmInteger(B) try: return tuple(random.sample(range(1, B + 1), A)) except ValueError: assertError("DOMAIN ERROR")
def binomial(A, B): """ A ! B """ try: if isinstance(A, int) and isinstance(B, int): return int(mpmath.binomial(B, A)) return float(mpmath.binomial(B, A)) except ValueError: assertError("DOMAIN ERROR")