前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python|函数式编程|公式约束器实现

Python|函数式编程|公式约束器实现

作者头像
朝闻君
发布2021-11-22 11:19:09
4980
发布2021-11-22 11:19:09
举报
文章被收录于专栏:用户9199536的专栏
[公式]
[公式]

这个公式很简单,写成函数的话,用最简单的一个return即可。然而,如果我想要让他推广,输入华氏度也能求出摄氏度,甚至更广,一个公式里,只要其他的n-1个变量已知,就能自动补全公式,该怎么做呢?

代码非原创,我只能对着大佬源码膜拜一番

运行范例

运行结果

在上述代码中,对celsius的改变使得华氏度也发生了改变。

我们首先划分代码的架构,一共三个模块

connector(连接器) 高级的变量,可以通过约束发出通知和接受通知。

由于全程使用FP,因此OOP中的方法目前使用字典key进行调用。如celsius['forget']意为celsius.forget。

contraint (约束) 公式的约束,可以对connector之间进行连接,连接之后通过约束就能对connector进行通知了。

converter (转换器)负责装配公式中的所有连接器和约束

Constraint Part

代码语言:javascript
复制
def make_ternary_constraint(a,b,c,ab,ca,cb):
   #三元约束
    def new_value():
        #通用计算公式,求出第三者连接器
        av,bv,cv=[connector['has_val']()for connector in(a,b,c)]
        if av and bv:
            c['set_val'](constraint,ab(a['val'],b['val']))
        elif av and cv:
            b['set_val'](constraint,ca(c['val'],a['val']))
        elif bv and cv:
            a['set_val'](constraint,cb(c['val'],b['val']))
    def forget_value():
        #对abc三个连接器均使用forget函数
        for connector in(a,b,c):
            connector['forget'](constraint)
    #constraint为调用dictionary,传递message给连接器
    constraint={'new_val':new_value,#表示连接到约束的连接器有了新的值
                'forget':forget_value}#表示连接到约束的连接器需要忘掉它的值
    for connector in(a,b,c):
        #令abc三个连接器均加入约束
        connector['connect'](constraint)
    #返回三元约束
    return constraint

在上述函数中,a,b,c是参与约束的三个连接器,ab,ca,cb则是操作符,意思是知道其中两者的情况下,使用对应的操作符可以得到第三者。

Constraint Method

new_value():

先通过has_val观察是否存在已知变量,如果已知变量为n-1个,就能求出未知变量。

forget_value()

调用连接器中的forget函数清空其值,此后forget会对所有参与的约束都发出清空通知(因为一个连接器可能参与多个约束,例如方程组),相当于链式反应。

此后,所有连接器都加入这个约束,最后返回。

代码语言:javascript
复制
from operator import add,sub

def adder(a,b,c):
    #加法公式的三元约束
    return make_ternary_constraint(a,b,c,add,sub,sub)

from operator import mul,truediv
def multiplier(a,b,c):
    #乘法公式的三元约束
    return make_ternary_constraint(a,b,c,mul,truediv,truediv)

通过上面的高阶函数,我们可以建立这样的约束工具不断地减少参数数目,从而实现更好的抽象层次划分。

Connector Part

代码语言:javascript
复制
#Connector part
def make_connector(name=None):
    #接受来自约束的消息,name为变量连接器的名称
    informant=None#存储此connector的用户设置约束
    constraints=[]#存储连接器所参与的所有约束(包含公式得出的匿名约束)
    def set_value(source,value):
        nonlocal informant
        val=connector['val']
        if val is None:
            #若不存在值,则设置其为value,informant设置为约束(通过用户设置的约束)
            informant,connector['val']=source,value
            if name is not None:
                print(name,'=',value)
            #对所有约束均调用new_value
            inform_all_except(source,'new_val',constraints)
        else:
            #若传入了不同的值,则发生矛盾
            if val!=value:
                print('Contradiction detected:',val,'vs',value)
    def forget_value(source):
        nonlocal informant
        if informant==source:
            #若informant和约束名称相同(即密码正确且此前未被forget),则将informant和值归0
            informant,connector['val']=None,None
            if name is not None:
                print(name,'is forgotten')
            #对所有约束调用forget_value
            inform_all_except(source,'forget',constraints)
    connector ={'val':None,#当前值
                'set_val':set_value,#表示source请求连接器将当前值设置为该值
                'forget':forget_value,#告诉连接器,source请求它忘掉当前值
                'has_val':lambda: connector['val'] is not None,#返回连接器是否已经有了一个值
                'connect':lambda source:constraints.append(source)}#告诉连接器参与新的约束source
    #连接器 字典 key:string literal, value: function
    return connector

def inform_all_except(source,message,constraints):
    #通知约束来改变所有连接器
    for c in constraints:
        if c!=source:
            c[message]()

在上述函数中,我们为一个连接器取名为name(或者匿名) Connector Method

set_val(source,value)

由某个来源(这个来源可以是任何东西,比如上文中的‘user’手动添加,也可以是约束本身自动添加),设置连接器变量的值,并且存储来源(目的是使得forget必须要由设置者来进行)

如果先前已经有值,则发生冲突不予改变。

如果先前为空,则调用new_value尝试对整个公式进行补全。

forget(source)

如果来源正确,那么就清空connector的值,并且向所有参与的约束都发出清空通知

has_val()

返回当前是否有值

connect(source)

连接器参与约束

Connector Data Member

informant

存储此connector的用户设置约束(如果是人工设置的话,只要是身份标识即可)

constraints

存储连接器所参与的所有约束(包含公式得出的匿名约束)

Normal Function

inform_all_except(source,message,constraints)

链式反应,发出信息,注意source,不能回发给自己

Converter Part

代码语言:javascript
复制
def make_converter(c,f):
    #装配连接器和约束条件
    u,v,w,x,y=[make_connector()for _ in range(5)]#五个匿名连接器,9*c=5*(f-32)
    multiplier(c,w,u)#c*9=u
    multiplier(v,x,u)#v*5=u
    adder(v,y,f)#v+32=f
    #根据此连接器,c,f可相互转换
    constant(w,9)#w:9
    constant(x,5)#x:5
    constant(y,32)#y:32

def constant(connector,value):
    #不施加约束,仅设置连接器的值
    constraint={}
    connector['set_val'](constraint,value)
    return constraint

我们这里把不施加约束({})的connector作为常量,因为这样在forget时就不受影响了。因此我们特意用了一个函数来设置。

[公式]
[公式]

这个公式需要翻译一下才能变成我们已经有的三元约束。(9和5分开可能是因为作者想增加一点难度)

代码语言:javascript
复制
    multiplier(c,w,u)#c*9=u
    multiplier(v,x,u)#v*5=u
[公式]
[公式]
[公式]
[公式]

这两个公式,实际上就是使得v代表9/5Celsius,从而让两个connector连接到一个上。

代码语言:javascript
复制
    adder(v,y,f)
[公式]
[公式]

这个公式实际上就是最后的公式。

通过这样几个约束,我们成功搭建了最后的公式。

总结

Converter 网络

通过构建的约束网络,我们使得C上的信息通过三层约束的传播到达F,反之亦然,这样子的架构比起单纯的函数映射提供了更多的便利性,同时改变公式的复杂度也只需要构造更大的网络即可。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 运行范例
  • Constraint Part
  • Connector Part
  • Converter Part
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档