Package: ldict
ldictA lazydict.This project was succeeded byhdict.Latest release|Current code|API documentationSee alsolaziness+identity+persistence (idict)OverviewAldictis adictwithstrkeys.Simple usage examplefromldictimportldicta=ldict(x=3)print(a)"""{"x": 3}"""b=ldict(y=5)print(b)"""{"y": 5}"""print(a>>b)"""{"x": 3,"y": 5}"""We consider that every value is generated by a process, starting from anemptyldict. The process is a sequence of
transformation steps done through the operator>>, which symbolizes a data flow. There are two types of steps:value insertion- represented by dict-like objectsfunction application- represented by ordinary python functionsAldictis completely defined by its key-value pairs so that
it can be converted from/to a built-in dict.Creating a ldict is not different from creating an ordinary dict. Optionally it can be created through the>>operator
used afterempty:Function application is done in the same way. The parameter names define the input fields, while the keys in the
returned dict define the output fields:Similarly, for anonymous functions:Finally, the result is only evaluated at request:Installation...as a standalone lib# Set up a virtualenv.python3-mvenvvenvsourcevenv/bin/activate# Install from PyPI...pipinstall--upgradepip
pipinstall-Uldict
pipinstall-Uldict[full]# use this for extra functionality (recommended)# ...or, install from updated source code.pipinstallgit+https://github.com/davips/ldict...from sourcegitclonehttps://github.com/davips/ldictcdldict
poetryinstallExamplesMerging two ldictsfromldictimportldicta=ldict(x=3)print(a)"""{"x": 3}"""b=ldict(y=5)print(b)"""{"y": 5}"""print(a>>b)"""{"x": 3,"y": 5}"""Lazily applying functions to ldictfromldictimportldicta=ldict(x=3)print(a)"""{"x": 3}"""a=a>>ldict(y=5)>>{"z":7}>>(lambdax,y,z:{"r":x**y//z})print(a)"""{"x": 3,"y": 5,"z": 7,"r": "→(x y z)"}"""print(a.r)"""34"""print(a)"""{"x": 3,"y": 5,"z": 7,"r": 34}"""Parameterized functions and samplingfromrandomimportRandomfromldictimportempty,let# A function provide input fields and, optionally, parameters.# For instance:# 'a' is sampled from an arithmetic progression# 'b' is sampled from a geometric progression# Here, the syntax for default parameter values is borrowed with a new meaning.deffun(x,y,a=[-100,-99,-98,...,100],b=[0.0001,0.001,0.01,...,100000000]):return{"z":a*x+b*y}defsimplefun(x,y):return{"z":x*y}# Creating an empty ldict. Alternatively: d = ldict().d=empty>>{}print(d)"""{}"""# Putting some values. Alternatively: d = ldict(x=5, y=7).d["x"]=5d["y"]=7print(d)"""{"x": 5,"y": 7}"""# Parameter values are uniformly sampled.d1=d>>simplefunprint(d1)print(d1.z)"""{"x": 5,"y": 7,"z": "→(x y)"}35"""d2=d>>simplefunprint(d2)print(d2.z)"""{"x": 5,"y": 7,"z": "→(x y)"}35"""# Parameter values can also be manually set.e=d>>let(fun,a=5,b=10)print(e.z)"""95"""# Not all parameters need to be set.e=d>>Random()>>let(fun,a=5)print("e =",e.z)"""e = 25.007"""# Each run will be a different sample for the missing parameters.e=e>>Random()>>let(fun,a=5)print("e =",e.z)"""e = 725.0"""# We can define the initial state of the random sampler.# It will be in effect from its location place onwards in the expression.e=d>>Random(0)>>let(fun,a=5)print(e.z)"""725.0"""# All runs will yield the same result,# if starting from the same random number generator seed.e=e>>Random(0)>>let(fun,a=[555,777])print("Let 'a' be a list:",e.z)"""Let 'a' be a list: 700003885.0"""# Reproducible different runs are achievable by using a single random number generator.e=e>>Random(0)>>let(fun,a=[5,25,125,...,10000])print("Let 'a' be a geometric progression:",e.z)"""Let 'a' be a geometric progression: 700003125.0"""rnd=Random(0)e=d>>rnd>>let(fun,a=5)print(e.z)e=d>>rnd>>let(fun,a=5)# Alternative syntax.print(e.z)"""725.0700000025.0"""# Output fields can be defined dynamically through parameter values.# Input fields can be defined dynamically through kwargs.copy=lambdasource=None,target=None,**kwargs:{target:kwargs[source]}d=empty>>{"x":5}d>>=let(copy,source="x",target="y")print(d)d.evaluate()print(d)"""{"x": 5,"y": "→(source target x)"}{"x": 5,"y": 5}"""Composition of sets of functionsfromrandomimportRandomfromldictimportempty# A multistep process can be defined without applying its functionsdefg(x,y,a=[1,2,3,...,10],b=[0.00001,0.0001,0.001,...,100000]):return{"z":a*x+b*y}defh(z,c=[1,2,3]):return{"z":c*z}# In the ldict framework 'data is function',# so the alias ø represents the 'empty data object' and the 'reflexive function' at the same time.# In other words: 'inserting nothing' has the same effect as 'doing nothing'.fun=empty>>g>>h# empty enable the cartesian product of the subsequent sets of functions within the expression.print(fun)"""«λ{} × λ»"""# An unnapplied function has its free parameters unsampled.# A compostition of functions results in an ordered set (Cartesian product of sets).# It is a set because the parameter values of the functions are still undefined.d={"x":5,"y":7}>>(Random(0)>>fun)print(d)"""{"x": 5,"y": 7,"z": "→(c z→(a b x y))"}"""print(d.z)"""105.0"""d={"x":5,"y":7}>>(Random(0)>>fun)print(d.z)"""105.0"""# Reproducible different runs by passing a stateful random number generator.rnd=Random(0)e=d>>rnd>>funprint(e.z)"""105.0"""e=d>>rnd>>funprint(e.z)"""14050.0"""# Repeating the same results.rnd=Random(0)e=d>>rnd>>funprint(e.z)"""105.0"""e=d>>rnd>>funprint(e.z)"""14050.0"""ConceptA ldict is like a common Python dict, with extra functionality and lazy. It is a mapping between string keys, called
fields, and any serializable (pickable protocol=5) object.GrantsThis work was partially supported by Fapesp under supervision of
Prof. André C. P. L. F. de Carvalho at CEPID-CeMEAI (Grants 2013/07375-0 – 2019/01735-0).