def getArity(self, fn): """ Function: - Gets the arity (number of inputs left to be specified) of a function or method (curried or uncurried) Requires: - `fn`: - Type: function | method - What: The function or method to get the arity of - Note: Class methods remove one arity to account for self Examples: ``` p.getArity(p.zip) #=> 2 curriedZip=p.curry(p.zip) ABCuriedZip=curriedZip(['a','b']) p.getArity(ABCuriedZip) #=> 1 ``` """ if isinstance(fn, curry_fn): return fn.__arity__ return curry_fn(fn).__arity__
def thunkify(self, fn): """ Function: - Creates a curried thunk out of a function - Evaluation of the thunk lazy and is delayed until called Requires: - `fn`: - Type: function | method - What: The function or method to thunkify - Note: Thunkified functions are automatically curried - Note: Class methods auto apply self during thunkify Notes: - Input functions are not thunkified in place - The returned function is a thunkified version of the input function - A curried function can be thunkified in place by calling fn.thunkify() Examples: ``` def add(a,b): return a+b addThunk=p.thunkify(add) add(1,2) #=> 3 addThunk(1,2) addThunk(1,2)() #=> 3 x=addThunk(1,2) x() #=> 3 ``` ``` @p.curry def add(a,b): return a+b add(1,2) #=> 3 add.thunkify() add(1,2) add(1,2)() #=> 3 ``` """ if not isinstance(fn, curry_fn): fn = curry_fn(fn) else: fn = fn() return fn.thunkify()
def map(self, fn, data): """ Function: - Maps a function over a list or a dictionary Requires: - `fn`: - Type: function | method - What: The function or method to map over the list or dictionary - Note: This function should have an arity of 1 - `data`: - Type: list | dict - What: The list or dict of items to map the function over Examples: ``` data=[1,2,3] p.map( fn=p.inc, data=data ) #=> [2,3,4] ``` ``` data={'a':1,'b':2,'c':3} p.map( fn=p.inc, data=data ) #=> {'a':2,'b':3,'c':4} ``` """ if not isinstance(fn, curry_fn): fn = curry_fn(fn) if fn.__arity__ != 1: self.exception('`map` `fn` must be unary (take one input)') if not isinstance(data, (list, dict)): self.exception('`map` `data` must be a list or a dict') if not len(data) > 0: self.exception( '`map` `data` has a length of 0 or is an empty dictionary, however it must have at least one element in it' ) if isinstance(data, dict): return {key: fn(value) for key, value in data.items()} else: return [fn(i) for i in data]
def accumulate(self, fn, initial_accumulator, data): """ Function: - Returns an accumulated list of items by iterating a function starting with an accumulator over a list Requires: - `fn`: - Type: function | method - What: The function or method to reduce - Note: This function should have an arity of 2 (take two inputs) - Note: The first input should take the accumulator value - Note: The second input should take the data value -`initial_accumulator`: - Type: any - What: The initial item to pass into the function when starting the accumulation process - `data`: - Type: list - What: The list of items to iterate over Example: ``` data=[1,2,3,4] p.accumulate( fn=p.add, initial_accumulator=0, data=data ) #=> [1,3,6,10] ``` """ if not isinstance(fn, curry_fn): fn = curry_fn(fn) if fn.__arity__ != 2: self.exception( '`reduce` `fn` must have an arity of 2 (take two inputs)') if not isinstance(data, (list)): self.exception('`reduce` `data` must be a list') if not len(data) > 0: self.exception( '`reduce` `data` has a length of 0, however it must have a length of at least 1' ) acc = initial_accumulator out = [] for i in data: acc = fn(acc, i) out.append(acc) return out
def curry(self, fn): """ Function: - Curries a function such that inputs can be added interatively Requires: - `fn`: - Type: function | method - What: The function or method to curry - Note: Class methods auto apply self during curry Notes: - Once curried, the function | method becomes a curry_fn object - The initial function is only called once all inputs are passed Examples: ``` curriedZip=p.curry(p.zip) curriedZip(['a','b'])([1,2]) #=> [['a',1],['b',2]] # Curried functions can be thunkified at any time # See also thunkify zipThunk=curriedZip.thunkify()(['a','b'])([1,2]) zipThunk() #=> [['a',1],['b',2]] ``` ``` def myFunction(a,b,c): return [a,b,c] curriedMyFn=p.curry(myFunction) curriedMyFn(1,2,3) #=> [1,2,3] curriedMyFn(1)(2,3) #=> [1,2,3] x=curriedMyFn(1)(2) x(3) #=> [1,2,3] x(4) #=> [1,2,4] ``` """ if not isinstance(fn, curry_fn): return curry_fn(fn) return fn
def adjust(self, index, fn, data): """ Function: - Adjusts an item in a list by applying a function to it Requires: - `index`: - Type: int - What: The 0 based index of the item in the list to adjust - Note: Indicies are accepted - Note: If the index is out of range, picks the (-)first / (+)last item - `fn`: - Type: function | method - What: The function to apply the index item to - Note: This is automatically curried - `data`: - Type: list - What: The list to adjust Example: ``` data=[1,5,9] p.adjust( index=1, fn=p.inc, data=data ) #=> [1,6,9] ``` """ if not isinstance(index, int): self.exception('`index` must be an int') if not isinstance(data, list): self.exception('`data` must be a list') if not isinstance(fn, curry_fn): fn = curry_fn(fn) index = self.clamp(-len(data), len(data) - 1, index) data[index] = fn(data[index]) return data
def flip(self, fn): """ Function: - Returns a new function equivalent to the supplied function except that the first two inputs are flipped Requires: - `fn`: - Type: function | method - What: The function or method to flip - Note: This function must have an arity of at least 2 (take two inputs) - Note: Only args are flipped, kwargs are passed as normal Notes: - Input functions are not flipped in place - The returned function is a flipped version of the input function - A curried function can be flipped in place by calling fn.flip() - A function can be flipped multiple times: - At each flip, the first and second inputs for the function as it is currently curried are switched - Flipping a function two times before adding an input will return the initial value Examples: ``` def concat(a,b,c,d): return str(a)+str(b)+str(c)+str(d) flip_concat=p.flip(concat) concat('fe-','fi-','fo-','fum') #=> 'fe-fi-fo-fum' flip_concat('fe-','fi-','fo-','fum') #=> 'fi-fe-fo-fum' ``` ``` @p.curry def concat(a,b,c,d): return str(a)+str(b)+str(c)+str(d) concat('fe-','fi-','fo-','fum') #=> 'fe-fi-fo-fum' concat.flip() concat('fe-','fi-','fo-','fum') #=> 'fi-fe-fo-fum' ``` ``` @p.curry def concat(a,b,c,d): return str(a)+str(b)+str(c)+str(d) a=p.flip(concat)('fi-') b=p.flip(a)('fo-') c=p.flip(b)('fum') c('fe-') #=> 'fe-fi-fo-fum' ``` ``` def concat(a,b,c,d): return str(a)+str(b)+str(c)+str(d) a=p.flip(concat)('fi-').flip()('fo-').flip()('fum') a('fe-') #=> 'fe-fi-fo-fum' ``` """ if not isinstance(fn, curry_fn): fn = curry_fn(fn) else: fn = fn() return fn.flip()